From db77df88565098addb25cc300b9004f6945b48af Mon Sep 17 00:00:00 2001 From: hanzel98 Date: Thu, 10 Jul 2025 08:50:51 -0600 Subject: [PATCH 1/4] chore: Add citrea and ink chains --- .../script/validate-contract-deployments.ts | 52 ++++++++++++++++++- packages/delegation-deployments/src/index.ts | 6 +++ .../validate-contract-deployments.test.ts | 19 +++---- 3 files changed, 63 insertions(+), 14 deletions(-) diff --git a/packages/delegation-deployments/script/validate-contract-deployments.ts b/packages/delegation-deployments/script/validate-contract-deployments.ts index c212f5a5..f47e8b06 100644 --- a/packages/delegation-deployments/script/validate-contract-deployments.ts +++ b/packages/delegation-deployments/script/validate-contract-deployments.ts @@ -87,18 +87,66 @@ const monadTestnetChain: Chain = { }, }; -const chains = { +const citreaTestnetChain: Chain = { + id: 5115, + name: 'Citrea Testnet', + rpcUrls: { + default: { + http: ['https://rpc.testnet.citrea.xyz'], + }, + }, + nativeCurrency: { + name: 'cBTC', + symbol: 'cBTC', + decimals: 18, + }, +}; + +const inkMainnetChain: Chain = { + id: 57073, + name: 'Ink Mainnet', + rpcUrls: { + default: { + http: ['https://rpc-qnd.inkonchain.com'], + }, + }, + nativeCurrency: { + name: 'Ether', + symbol: 'ETH', + decimals: 18, + }, +}; + +const inkSepoliaChain: Chain = { + id: 763373, + name: 'Ink Sepolia', + rpcUrls: { + default: { + http: ['https://ink-sepolia.drpc.org'], + }, + }, + nativeCurrency: { + name: 'Ether', + symbol: 'ETH', + decimals: 18, + }, +}; + +export const chains = { ...allChains, megaEthTestNet: megaEthTestNetChain, berachainMainnet: berachainMainnetChain, bepoliaTestnet: bepoliaTestnetChain, unichain: unichainChain, monadTestnet: monadTestnetChain, + citreaTestnet: citreaTestnetChain, + inkMainnet: inkMainnetChain, + inkSepolia: inkSepoliaChain, } as any as { [key: string]: Chain }; // The default rpc urls for these chains are not reliable, so we override them // This may be a game of cat and mouse, so a better solution may be needed. -const rpcUrlOverrides = { +export const rpcUrlOverrides = { [chains.mainnet.id]: 'https://eth.merkle.io', [chains.bsc.id]: 'https://bsc-dataseed1.binance.org/', [chains.bscTestnet.id]: 'https://bsc-testnet-rpc.publicnode.com', diff --git a/packages/delegation-deployments/src/index.ts b/packages/delegation-deployments/src/index.ts index a544af5d..3edbcb57 100644 --- a/packages/delegation-deployments/src/index.ts +++ b/packages/delegation-deployments/src/index.ts @@ -13,12 +13,14 @@ export const CHAIN_ID = { polygon: 137, base: 8453, arbitrum: 42161, + inkMainnet: 57073, linea: 59144, berachain: 80094, unichain: 130, arbitrumNova: 42170, // Testnets bscTestnet: 97, + citreaTestnet: 5115, megaEthTestnet: 6342, chiado: 10200, lineaSepolia: 59141, @@ -29,6 +31,7 @@ export const CHAIN_ID = { optimismSepolia: 11155420, unichainSepolia: 1301, polygonAmoy: 80002, + inkSepolia: 763373, monadTestnet: 10143, // decommissioned lineaGoerli: 59140, @@ -79,6 +82,7 @@ export const DELEGATOR_CONTRACTS: DeployedContracts = { [CHAIN_ID.bsc]: deployments_1_3_0, [CHAIN_ID.optimism]: deployments_1_3_0, [CHAIN_ID.arbitrum]: deployments_1_3_0, + [CHAIN_ID.inkMainnet]: deployments_1_3_0, [CHAIN_ID.linea]: deployments_1_3_0, [CHAIN_ID.base]: deployments_1_3_0, [CHAIN_ID.gnosis]: deployments_1_3_0, @@ -87,6 +91,7 @@ export const DELEGATOR_CONTRACTS: DeployedContracts = { [CHAIN_ID.arbitrumNova]: deployments_1_3_0, // Testnets [CHAIN_ID.sepolia]: deployments_1_3_0, + [CHAIN_ID.citreaTestnet]: deployments_1_3_0, [CHAIN_ID.lineaSepolia]: deployments_1_3_0, [CHAIN_ID.baseSepolia]: deployments_1_3_0, [CHAIN_ID.megaEthTestnet]: deployments_1_3_0, @@ -97,6 +102,7 @@ export const DELEGATOR_CONTRACTS: DeployedContracts = { [CHAIN_ID.berachainBepolia]: deployments_1_3_0, [CHAIN_ID.unichainSepolia]: deployments_1_3_0, [CHAIN_ID.polygonAmoy]: deployments_1_3_0, + [CHAIN_ID.inkSepolia]: deployments_1_3_0, [CHAIN_ID.monadTestnet]: deployments_1_3_0, }, }; diff --git a/packages/delegation-deployments/test/validate-contract-deployments.test.ts b/packages/delegation-deployments/test/validate-contract-deployments.test.ts index e2c83c72..99ad93a1 100644 --- a/packages/delegation-deployments/test/validate-contract-deployments.test.ts +++ b/packages/delegation-deployments/test/validate-contract-deployments.test.ts @@ -1,9 +1,11 @@ import { createPublicClient, http } from 'viem'; -import * as chains from 'viem/chains'; -import type { Chain } from 'viem/chains'; import { DELEGATOR_CONTRACTS } from '../src/index'; import { compareVersions } from 'compare-versions'; +import { + chains, + rpcUrlOverrides, +} from '../script/validate-contract-deployments'; /* This test validates that the DeleGator contracts are deployed on the specified chains, as specified in the @metamask-private/delegation-deployments package. @@ -12,13 +14,6 @@ import { compareVersions } from 'compare-versions'; code is found at the expected address for each contract. */ -// The default rpc urls for these chains are not reliable, so we override them -// This may be a game of cat and mouse, so a better solution may be needed. -const rpcUrlOverrides = { - [chains.mainnet.id]: 'https://eth.merkle.io', - [chains.bsc.id]: 'https://bsc-dataseed1.binance.org/', -}; - const latestVersion = Object.keys(DELEGATOR_CONTRACTS).reduce( (acc, version) => { if (compareVersions(version, acc) === 1) { @@ -35,12 +30,12 @@ const chainIds = Object.keys(latestContracts); let hasFailed = false; -const allChains = chainIds.map(async (chainIdAsString) => { +const allChainValidations = chainIds.map(async (chainIdAsString) => { const chainId = parseInt(chainIdAsString); const contracts = latestContracts[chainIdAsString]; const transport = http(rpcUrlOverrides[chainId]); - const chain = (chains as any as Chain[]).find((c) => c.id === chainId); + const chain = Object.values(chains).find((c) => c.id === chainId); if (!chain) { throw new Error(`Chain ${chainId} not found`); @@ -71,7 +66,7 @@ const allChains = chainIds.map(async (chainIdAsString) => { console.log(`${chain.name} succeeded`); }); -Promise.all(allChains).then(() => { +Promise.all(allChainValidations).then(() => { if (hasFailed) { process.exitCode = 1; console.error('Failed to validate contract deployments'); From 067b3b69979202557a8b7f8c048391a82ddf1639 Mon Sep 17 00:00:00 2001 From: hanzel98 Date: Thu, 10 Jul 2025 10:30:32 -0600 Subject: [PATCH 2/4] chore: adds new tx explorers and infura rpcs --- packages/delegation-toolkit/src/blockchain.ts | 64 +++++++++++++++---- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/packages/delegation-toolkit/src/blockchain.ts b/packages/delegation-toolkit/src/blockchain.ts index cbdd5d96..b2993bb6 100644 --- a/packages/delegation-toolkit/src/blockchain.ts +++ b/packages/delegation-toolkit/src/blockchain.ts @@ -1,14 +1,27 @@ import { CHAIN_ID } from '@metamask/delegation-deployments'; const CHAIN_EXPLORERS = { + [CHAIN_ID.arbitrum]: 'https://arbiscan.io', + [CHAIN_ID.arbitrumNova]: 'https://nova.arbiscan.io', + [CHAIN_ID.arbitrumSepolia]: 'https://sepolia.arbiscan.io', + [CHAIN_ID.base]: 'https://basescan.org', + [CHAIN_ID.baseSepolia]: 'https://sepolia.basescan.org', + [CHAIN_ID.berachain]: 'https://berascan.com', + [CHAIN_ID.berachainBepolia]: 'https://testnet.berascan.com', + [CHAIN_ID.bsc]: 'https://bscscan.com', + [CHAIN_ID.bscTestnet]: 'https://testnet.bscscan.com', + [CHAIN_ID.gnosis]: 'https://gnosisscan.io', + [CHAIN_ID.linea]: 'https://lineascan.build', + [CHAIN_ID.lineaSepolia]: 'https://sepolia.lineascan.build', [CHAIN_ID.mainnet]: 'https://etherscan.io', + [CHAIN_ID.monadTestnet]: 'https://testnet.monadscan.com', [CHAIN_ID.optimism]: 'https://optimistic.etherscan.io', + [CHAIN_ID.optimismSepolia]: 'https://sepolia-optimism.etherscan.io', [CHAIN_ID.polygon]: 'https://polygonscan.com', - [CHAIN_ID.base]: 'https://basescan.org', - [CHAIN_ID.arbitrum]: 'https://arbiscan.io', - [CHAIN_ID.linea]: 'https://lineascan.build', + [CHAIN_ID.polygonAmoy]: 'https://amoy.polygonscan.com', [CHAIN_ID.sepolia]: 'https://sepolia.etherscan.io', - [CHAIN_ID.lineaSepolia]: 'https://sepolia.lineascan.build', + [CHAIN_ID.unichain]: 'https://uniscan.xyz', + [CHAIN_ID.unichainSepolia]: 'https://sepolia.uniscan.xyz', }; const getExplorerLink = (chainId: number) => { @@ -32,25 +45,48 @@ export const getExplorerTransactionLink = (chainId: number, hash: string) => { export const shortenHash = (address: string | undefined) => address ? `${address.slice(0, 6)}...` : ''; -export const getInfuraRpcUrl = (chainId: number, apiKey?: string) => { - if (!apiKey) { - throw new Error('Infura API key is required'); - } +/** + * Returns the Infura RPC URL for a given chainId and API key. + * @param chainId - The chain ID. + * @param apiKey - Infura API key. + * @returns The Infura RPC URL. + * @throws If the API key is missing or the chain is unsupported. + */ +export const getInfuraRpcUrl = (chainId: number, apiKey?: string): string => { + if (!apiKey) throw new Error('Infura API key is required'); switch (chainId) { + case CHAIN_ID.arbitrum: + return `https://arbitrum-mainnet.infura.io/v3/${apiKey}`; + case CHAIN_ID.arbitrumSepolia: + return `https://arbitrum-sepolia.infura.io/v3/${apiKey}`; + case CHAIN_ID.base: + return `https://base-mainnet.infura.io/v3/${apiKey}`; + case CHAIN_ID.baseSepolia: + return `https://base-sepolia.infura.io/v3/${apiKey}`; + case CHAIN_ID.bsc: + return `https://bsc-mainnet.infura.io/v3/${apiKey}`; + case CHAIN_ID.bscTestnet: + return `https://bsc-testnet.infura.io/v3/${apiKey}`; + case CHAIN_ID.linea: + return `https://linea-mainnet.infura.io/v3/${apiKey}`; + case CHAIN_ID.lineaSepolia: + return `https://linea-sepolia.infura.io/v3/${apiKey}`; case CHAIN_ID.mainnet: return `https://mainnet.infura.io/v3/${apiKey}`; case CHAIN_ID.optimism: return `https://optimism-mainnet.infura.io/v3/${apiKey}`; + case CHAIN_ID.optimismSepolia: + return `https://optimism-sepolia.infura.io/v3/${apiKey}`; case CHAIN_ID.polygon: return `https://polygon-mainnet.infura.io/v3/${apiKey}`; - case CHAIN_ID.arbitrum: - return `https://arbitrum-mainnet.infura.io/v3/${apiKey}`; - case CHAIN_ID.linea: - return `https://linea-mainnet.infura.io/v3/${apiKey}`; + case CHAIN_ID.polygonAmoy: + return `https://polygon-amoy.infura.io/v3/${apiKey}`; case CHAIN_ID.sepolia: return `https://sepolia.infura.io/v3/${apiKey}`; - case CHAIN_ID.lineaSepolia: - return `https://linea-sepolia.infura.io/v3/${apiKey}`; + case CHAIN_ID.unichain: + return `https://unichain-mainnet.infura.io/v3/${apiKey}`; + case CHAIN_ID.unichainSepolia: + return `https://unichain-sepolia.infura.io/v3/${apiKey}`; default: throw new Error(`Unsupported chainId: ${chainId}`); } From 231a0050349350b04b676d3847c333242f8e73a7 Mon Sep 17 00:00:00 2001 From: hanzel98 Date: Thu, 10 Jul 2025 11:00:56 -0600 Subject: [PATCH 3/4] chore: yarn lint if curly --- packages/delegation-toolkit/src/blockchain.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/delegation-toolkit/src/blockchain.ts b/packages/delegation-toolkit/src/blockchain.ts index b2993bb6..8cbb37f9 100644 --- a/packages/delegation-toolkit/src/blockchain.ts +++ b/packages/delegation-toolkit/src/blockchain.ts @@ -53,7 +53,9 @@ export const shortenHash = (address: string | undefined) => * @throws If the API key is missing or the chain is unsupported. */ export const getInfuraRpcUrl = (chainId: number, apiKey?: string): string => { - if (!apiKey) throw new Error('Infura API key is required'); + if (!apiKey) { + throw new Error('Infura API key is required'); + } switch (chainId) { case CHAIN_ID.arbitrum: return `https://arbitrum-mainnet.infura.io/v3/${apiKey}`; From 4537ca45b719bf94f232a5ff06afe5f24ed0247f Mon Sep 17 00:00:00 2001 From: hanzel98 Date: Mon, 28 Jul 2025 10:02:18 -0600 Subject: [PATCH 4/4] chore: deleted old files --- .../validate-contract-deployments.test.ts | 76 --------------- packages/delegation-toolkit/src/blockchain.ts | 95 ------------------- 2 files changed, 171 deletions(-) delete mode 100644 packages/delegation-deployments/test/validate-contract-deployments.test.ts delete mode 100644 packages/delegation-toolkit/src/blockchain.ts diff --git a/packages/delegation-deployments/test/validate-contract-deployments.test.ts b/packages/delegation-deployments/test/validate-contract-deployments.test.ts deleted file mode 100644 index 99ad93a1..00000000 --- a/packages/delegation-deployments/test/validate-contract-deployments.test.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { createPublicClient, http } from 'viem'; - -import { DELEGATOR_CONTRACTS } from '../src/index'; -import { compareVersions } from 'compare-versions'; -import { - chains, - rpcUrlOverrides, -} from '../script/validate-contract-deployments'; -/* - This test validates that the DeleGator contracts are deployed on the specified chains, - as specified in the @metamask-private/delegation-deployments package. - - It does this by getting the DeleGatorEnvironment for each chain and then ensuring that - code is found at the expected address for each contract. -*/ - -const latestVersion = Object.keys(DELEGATOR_CONTRACTS).reduce( - (acc, version) => { - if (compareVersions(version, acc) === 1) { - return version; - } - return acc; - }, - '0.0.0', -); - -const latestContracts = DELEGATOR_CONTRACTS[latestVersion]; - -const chainIds = Object.keys(latestContracts); - -let hasFailed = false; - -const allChainValidations = chainIds.map(async (chainIdAsString) => { - const chainId = parseInt(chainIdAsString); - const contracts = latestContracts[chainIdAsString]; - - const transport = http(rpcUrlOverrides[chainId]); - const chain = Object.values(chains).find((c) => c.id === chainId); - - if (!chain) { - throw new Error(`Chain ${chainId} not found`); - } - - const publicClient = createPublicClient({ - chain, - transport, - }); - - const contractNames = Object.keys(contracts); - - const allContracts = contractNames.map(async (contractName) => { - const contractAddress = contracts[contractName]; - - const code = await publicClient.getCode({ address: contractAddress }); - - if (code === '0x') { - console.error( - `${chain.name}: ${contractName} is not deployed at ${contractAddress}`, - ); - hasFailed = true; - } - }); - - await Promise.all(allContracts); - - console.log(`${chain.name} succeeded`); -}); - -Promise.all(allChainValidations).then(() => { - if (hasFailed) { - process.exitCode = 1; - console.error('Failed to validate contract deployments'); - } else { - console.log('Successfully validated contract deployments'); - } -}); diff --git a/packages/delegation-toolkit/src/blockchain.ts b/packages/delegation-toolkit/src/blockchain.ts deleted file mode 100644 index 8cbb37f9..00000000 --- a/packages/delegation-toolkit/src/blockchain.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { CHAIN_ID } from '@metamask/delegation-deployments'; - -const CHAIN_EXPLORERS = { - [CHAIN_ID.arbitrum]: 'https://arbiscan.io', - [CHAIN_ID.arbitrumNova]: 'https://nova.arbiscan.io', - [CHAIN_ID.arbitrumSepolia]: 'https://sepolia.arbiscan.io', - [CHAIN_ID.base]: 'https://basescan.org', - [CHAIN_ID.baseSepolia]: 'https://sepolia.basescan.org', - [CHAIN_ID.berachain]: 'https://berascan.com', - [CHAIN_ID.berachainBepolia]: 'https://testnet.berascan.com', - [CHAIN_ID.bsc]: 'https://bscscan.com', - [CHAIN_ID.bscTestnet]: 'https://testnet.bscscan.com', - [CHAIN_ID.gnosis]: 'https://gnosisscan.io', - [CHAIN_ID.linea]: 'https://lineascan.build', - [CHAIN_ID.lineaSepolia]: 'https://sepolia.lineascan.build', - [CHAIN_ID.mainnet]: 'https://etherscan.io', - [CHAIN_ID.monadTestnet]: 'https://testnet.monadscan.com', - [CHAIN_ID.optimism]: 'https://optimistic.etherscan.io', - [CHAIN_ID.optimismSepolia]: 'https://sepolia-optimism.etherscan.io', - [CHAIN_ID.polygon]: 'https://polygonscan.com', - [CHAIN_ID.polygonAmoy]: 'https://amoy.polygonscan.com', - [CHAIN_ID.sepolia]: 'https://sepolia.etherscan.io', - [CHAIN_ID.unichain]: 'https://uniscan.xyz', - [CHAIN_ID.unichainSepolia]: 'https://sepolia.uniscan.xyz', -}; - -const getExplorerLink = (chainId: number) => { - const url = CHAIN_EXPLORERS[chainId]; - if (!url) { - throw new Error(`Unsupported chainId: ${chainId}`); - } - return url; -}; - -export const getExplorerAddressLink = (chainId: number, address: string) => { - const prefix = getExplorerLink(chainId); - return `${prefix}/address/${address}`; -}; - -export const getExplorerTransactionLink = (chainId: number, hash: string) => { - const prefix = getExplorerLink(chainId); - return `${prefix}/tx/${hash}`; -}; - -export const shortenHash = (address: string | undefined) => - address ? `${address.slice(0, 6)}...` : ''; - -/** - * Returns the Infura RPC URL for a given chainId and API key. - * @param chainId - The chain ID. - * @param apiKey - Infura API key. - * @returns The Infura RPC URL. - * @throws If the API key is missing or the chain is unsupported. - */ -export const getInfuraRpcUrl = (chainId: number, apiKey?: string): string => { - if (!apiKey) { - throw new Error('Infura API key is required'); - } - switch (chainId) { - case CHAIN_ID.arbitrum: - return `https://arbitrum-mainnet.infura.io/v3/${apiKey}`; - case CHAIN_ID.arbitrumSepolia: - return `https://arbitrum-sepolia.infura.io/v3/${apiKey}`; - case CHAIN_ID.base: - return `https://base-mainnet.infura.io/v3/${apiKey}`; - case CHAIN_ID.baseSepolia: - return `https://base-sepolia.infura.io/v3/${apiKey}`; - case CHAIN_ID.bsc: - return `https://bsc-mainnet.infura.io/v3/${apiKey}`; - case CHAIN_ID.bscTestnet: - return `https://bsc-testnet.infura.io/v3/${apiKey}`; - case CHAIN_ID.linea: - return `https://linea-mainnet.infura.io/v3/${apiKey}`; - case CHAIN_ID.lineaSepolia: - return `https://linea-sepolia.infura.io/v3/${apiKey}`; - case CHAIN_ID.mainnet: - return `https://mainnet.infura.io/v3/${apiKey}`; - case CHAIN_ID.optimism: - return `https://optimism-mainnet.infura.io/v3/${apiKey}`; - case CHAIN_ID.optimismSepolia: - return `https://optimism-sepolia.infura.io/v3/${apiKey}`; - case CHAIN_ID.polygon: - return `https://polygon-mainnet.infura.io/v3/${apiKey}`; - case CHAIN_ID.polygonAmoy: - return `https://polygon-amoy.infura.io/v3/${apiKey}`; - case CHAIN_ID.sepolia: - return `https://sepolia.infura.io/v3/${apiKey}`; - case CHAIN_ID.unichain: - return `https://unichain-mainnet.infura.io/v3/${apiKey}`; - case CHAIN_ID.unichainSepolia: - return `https://unichain-sepolia.infura.io/v3/${apiKey}`; - default: - throw new Error(`Unsupported chainId: ${chainId}`); - } -};