Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion apps/tangle-cloud/src/app/cloudWagmiConfig.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import anvilLocal from '@tangle-network/dapp-config/chains/evm/customChains/anvilLocal';
import tempoModerato from '@tangle-network/dapp-config/chains/evm/customChains/tempoModerato';
import { getDefaultConfig } from 'connectkit';
import { baseSepolia } from 'viem/chains';
import { createConfig, http } from 'wagmi';
Expand All @@ -21,7 +22,7 @@ const ENABLE_FAMILY_WALLET =
process.env?.NEXT_PUBLIC_ENABLE_FAMILY_WALLET === 'true')) ||
false;

const chains = [anvilLocal, baseSepolia] as const;
const chains = [anvilLocal, baseSepolia, tempoModerato] as const;

/**
* Wallet config matches the Tangle staking dapp — ConnectKit's
Expand All @@ -42,6 +43,9 @@ export const cloudWagmiConfig = createConfig(
timeout: 4_000,
}),
[baseSepolia.id]: http('https://sepolia.base.org', { timeout: 8_000 }),
[tempoModerato.id]: http('https://rpc.moderato.tempo.xyz', {
timeout: 8_000,
}),
},
}),
);
24 changes: 24 additions & 0 deletions apps/tangle-cloud/src/constants/networks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { getBrowserLocalServiceUrl } from '@tangle-network/tangle-shared-ui/util
export enum NetworkId {
ANVIL_LOCAL = 6,
BASE_SEPOLIA = 8,
TEMPO_MODERATO = 9,
}

export type Network = {
Expand Down Expand Up @@ -51,7 +52,30 @@ export const BASE_SEPOLIA_NETWORK: Network = {
isEvm ? `https://sepolia.basescan.org/tx/${txHash}` : null,
};

// Tempo Moderato hosts the live tnt-core 0.19 deployment. Its native gas asset
// is a USD stablecoin (see the viem chain def), but `tokenSymbol` here is the
// staking-token label used by the network switcher, so it stays `tTNT` like the
// other testnets.
export const TEMPO_MODERATO_NETWORK: Network = {
id: NetworkId.TEMPO_MODERATO,
evmChainId: 42431,
name: 'Tempo Moderato',
tokenSymbol: 'tTNT',
nodeType: 'standalone',
wsRpcEndpoints: [],
httpRpcEndpoints: ['https://rpc.moderato.tempo.xyz'],
polkadotJsDashboardUrl: '',
evmExplorerUrl: 'https://explore.tempo.xyz',
createExplorerAccountUrl: (address) =>
/^0x[a-fA-F0-9]{40}$/.test(address)
? `https://explore.tempo.xyz/address/${address}`
: null,
createExplorerTxUrl: (isEvm, txHash) =>
isEvm ? `https://explore.tempo.xyz/tx/${txHash}` : null,
};

export const TANGLE_CLOUD_NETWORKS = [
ANVIL_LOCAL_NETWORK,
BASE_SEPOLIA_NETWORK,
TEMPO_MODERATO_NETWORK,
];
Original file line number Diff line number Diff line change
Expand Up @@ -314,11 +314,10 @@ const OperatorExitPanel: FC<Props> = ({

{/* Current Exit Status */}
{/*
TODO(v019): `useCanScheduleExit` fail-closes to `{ canExit: false }` on
v019 chains (tnt-core 0.19 removed the `canScheduleExit` view), so the
Schedule-Exit button below is always gated off there and only the
yellow "unavailable" reason renders. Wire getExitStatus-based
eligibility in the hook to re-enable scheduling on v019.
On v019 chains (tnt-core 0.19 removed the `canScheduleExit` view),
`useCanScheduleExit` derives eligibility from `getExitStatus`, so the
Schedule-Exit button below re-enables when the operator is not already
in the exit queue.
*/}
{exitStatus === ExitStatus.None && (
<div className="space-y-3">
Expand Down
1 change: 1 addition & 0 deletions apps/tangle-dapp/src/constants/networks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ export const NETWORK_FEATURE_MAP: Record<NetworkId, NetworkFeature[]> = {
[NetworkId.ANVIL_LOCAL]: [],
[NetworkId.BASE]: [],
[NetworkId.BASE_SEPOLIA]: [],
[NetworkId.TEMPO_MODERATO]: [],
};
34 changes: 34 additions & 0 deletions libs/dapp-config/src/chains/evm/customChains/tempoModerato.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { EVMChainId } from '@tangle-network/dapp-types';
import { defineChain } from 'viem';

/**
* Tempo Moderato testnet — the chain running the live tnt-core 0.19 deployment.
*
* Unlike every other testnet the dapp wires (which pay gas in ETH), Tempo pays
* gas in a USD stablecoin. viem's `nativeCurrency` still models it as the
* 18-decimal base asset; only the name/symbol change. `decimals: 18` matches the
* other testnet defs so fee/balance formatting stays consistent.
*/
const tempoModerato = defineChain({
id: EVMChainId.TempoModerato,
name: 'Tempo Moderato',
nativeCurrency: {
name: 'USD',
symbol: 'USD',
decimals: 18,
},
rpcUrls: {
default: {
http: ['https://rpc.moderato.tempo.xyz'],
},
},
blockExplorers: {
default: {
name: 'Tempo Explorer',
url: 'https://explore.tempo.xyz',
},
},
testnet: true,
});

export default tempoModerato;
12 changes: 12 additions & 0 deletions libs/dapp-config/src/chains/evm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ import {
} from 'viem/chains';
import type { ChainConfig } from '../chain-config.interface';
import anvilLocal from './customChains/anvilLocal';
import tempoModerato from './customChains/tempoModerato';

// Primary chains for development and production
export const wagmiChains = [
anvilLocal, // Local development (Anvil on port 8545)
base, // Base mainnet
baseSepolia, // Base testnet
tempoModerato, // Tempo Moderato testnet (live tnt-core 0.19 deployment)
mainnet, // Ethereum mainnet (for native staking)
{
...holesky,
Expand Down Expand Up @@ -200,6 +202,16 @@ export const chainsConfig = {
displayName: 'Base Sepolia',
} satisfies ChainConfig,

[PresetTypedChainId.TempoModerato]: {
...tempoModerato,
chainType: ChainType.EVM,
// Tempo hosts Tangle's own tnt-core 0.19 deployment; there is no dedicated
// `tempo` ChainGroup, and `tangle` is the closest semantic bucket.
group: 'tangle',
tag: 'test',
displayName: 'Tempo Moderato',
} satisfies ChainConfig,

[PresetTypedChainId.AnvilLocal]: {
...anvilLocal,
chainType: ChainType.EVM,
Expand Down
45 changes: 42 additions & 3 deletions libs/dapp-config/src/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,38 @@ export const BASE_SEPOLIA_CONTRACTS: ContractAddresses = {
blueprintAuditors: '0xf4428bbbfa9207b4b91ca5fe8c1d401fd8b1e8e8', // deployed 2026-05-22 via tnt-core/script/UpgradeFlowAddon.s.sol
};

// Tempo Moderato testnet addresses — live tnt-core 0.19 deployment.
//
// Field mapping (Tempo deploy name -> `ContractAddresses` field):
// tangle -> tangle
// staking/restaking -> multiAssetDelegation
// statusRegistry -> operatorStatusRegistry
// rewardVaults -> rewardVaults
// inflationPool -> inflationPool
//
// The Tempo deploy also exposes contracts that have no field on the shared
// `ContractAddresses` type, so they are intentionally NOT surfaced here (adding
// them would force a field on every other chain's config). Recorded for
// traceability:
// tntToken 0x64fb7ffae82c1682574e9edb4e2f4e377132f671
// serviceFeeDistributor 0x39d16ff4e0bce0e6b00a4a49be80c2b7ad2fb4b4
// streamingPaymentManager address(0) — not deployed in this release
//
// Fields with no Tempo address (masterBlueprintServiceManager, credits,
// liquidDelegationFactory, blueprintAuditors) are the zero address; the dapp
// already treats zero-address entries as "not available on this chain".
export const TEMPO_CONTRACTS: ContractAddresses = {
tangle: '0xff137b9c879c47c28ce389e84501925438ab4cda',
multiAssetDelegation: '0x9484d07899b98384f1d66bd5b2659f3ed346f89e',
masterBlueprintServiceManager: '0x0000000000000000000000000000000000000000', // not deployed in this release
operatorStatusRegistry: '0xaedaffd260e21b41cb9926370e32d14bef812b48',
rewardVaults: '0x9a1615fdcaaf2f659d78eb0d72fd3759480af7c4',
inflationPool: '0x3602b76c625464895205466c645feff0d911836f',
credits: '0x0000000000000000000000000000000000000000', // not deployed in this release
liquidDelegationFactory: '0x0000000000000000000000000000000000000000', // not deployed in this release
blueprintAuditors: '0x0000000000000000000000000000000000000000', // pending deployment; dapp falls back to the JSON registry
};

// Base mainnet addresses (to be updated after deployment)
export const BASE_MAINNET_CONTRACTS: ContractAddresses = {
tangle: '0x0000000000000000000000000000000000000000',
Expand Down Expand Up @@ -173,9 +205,14 @@ export const getTntCoreRevisionByChainId = (
chainId: number,
): TntCoreRevision => {
switch (chainId) {
// Every currently-wired chain runs pre-0.18 contracts (Base Sepolia is
// pre-#193; local anvil is post-#193 but pre-#194, which for every
// surface the dapp branches on behaves like `legacy`).
// Tempo Moderato runs the live tnt-core 0.19 deployment, so it must select
// the v019 read paths (shorter binary-version / attestation / slash tuples,
// `getExitStatus`-derived exit eligibility). Every other wired chain runs
// pre-0.18 contracts (Base Sepolia is pre-#193; local anvil is post-#193 but
// pre-#194, which for every surface the dapp branches on behaves like
// `legacy`).
case 42431: // Tempo Moderato
return 'v019';
default:
return 'legacy';
}
Expand All @@ -188,6 +225,8 @@ export const getContractsByChainId = (chainId: number): ContractAddresses => {
return LOCAL_CONTRACTS;
case 84532: // Base Sepolia
return BASE_SEPOLIA_CONTRACTS;
case 42431: // Tempo Moderato (live tnt-core 0.19 deployment)
return TEMPO_CONTRACTS;
case 8453: // Base Mainnet
return BASE_MAINNET_CONTRACTS;
case 421614: // Arbitrum Sepolia
Expand Down
5 changes: 5 additions & 0 deletions libs/dapp-types/src/ChainId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ export enum PresetTypedChainId {
EVMChainId.ArbitrumSepolia,
),

TempoModerato = calculateTypedChainId(
ChainType.EVM,
EVMChainId.TempoModerato,
),

AnvilLocal = calculateTypedChainId(ChainType.EVM, EVMChainId.AnvilLocal),

BSC = calculateTypedChainId(ChainType.EVM, EVMChainId.BSC),
Expand Down
4 changes: 4 additions & 0 deletions libs/dapp-types/src/EVMChainId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ enum EVMChainId {
// Arbitrum testnets
ArbitrumSepolia = 421614,

// Tempo testnets — tnt-core 0.19 lives here (Tempo pays gas in a USD
// stablecoin, not ETH; see the custom chain def for the nativeCurrency).
TempoModerato = 42431,

// Local development
AnvilLocal = 31337,

Expand Down
5 changes: 3 additions & 2 deletions libs/tangle-shared-ui/src/blueprintApps/trustScore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ export interface Attestation {
attester: `0x${string}`;
reportHash: `0x${string}`;
// tnt-core 0.19 dropped `reportUri` from the on-chain `Attestation` struct;
// it is event-sourced from `BinaryVersionAttested`. `null` on v019 chains
// until the indexer wiring lands (follow-up); a string on legacy/v018 chains.
// it is event-sourced from `BinaryVersionAttested`. On v019 `fetchAttestations`
// backfills it from the attest log (joined by attester); `null` only when no
// matching log is found. A string on legacy/v018 chains.
reportUri: string | null;
kind: AttestationKind;
severityFound: number;
Expand Down
11 changes: 7 additions & 4 deletions libs/tangle-shared-ui/src/data/blueprints/binaryVersion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ export interface BinaryVersion {
versionId: bigint;
sha256Hash: `0x${string}`;
// tnt-core 0.19 dropped `binaryUri` from the on-chain `BinaryVersion` struct;
// it is event-sourced from `BinaryVersionPublished`. `null` on v019 chains
// until the indexer wiring lands (follow-up); a non-empty string on
// legacy/v018 chains that still store it in the returned tuple.
// it is event-sourced from `BinaryVersionPublished`. The normalizer leaves it
// `null` (the tuple has no URI slot); on v019 the fetchers in
// `useBinaryVersions` backfill it from the publish log. `null` only when no
// publish event is found. A non-empty string on legacy/v018 chains that still
// store it in the returned tuple.
binaryUri: string | null;
attestationHash: `0x${string}`;
publishedAt: bigint;
Expand Down Expand Up @@ -68,7 +70,8 @@ export const normalizeBinaryVersion = (raw: {
});

// `reportUri` is absent from the 0.19 attestation struct returndata; missing →
// `null` (event-sourced from `BinaryVersionAttested.reportUri` in a follow-up).
// `null` here. On v019 `fetchAttestations` backfills it from the
// `BinaryVersionAttested.reportUri` log, joined by attester address.
export const normalizeAttestation = (raw: {
attester: Address;
reportHash: `0x${string}`;
Expand Down
Loading
Loading