From c88bc2ea3298b066890f14ba9b0ec9e8b3ea7072 Mon Sep 17 00:00:00 2001 From: Doug Lance <4741454+douglance@users.noreply.github.com> Date: Thu, 26 Dec 2024 11:36:32 -0500 Subject: [PATCH 01/10] feat: initialize viem package --- .../customFeeTokenTestHelpers.ts | 71 +++++- packages/sdk/tests/testSetup.ts | 44 ++++ packages/viem/.eslintignore | 5 + packages/viem/.eslintrc | 8 + packages/viem/.prettierignore | 5 + packages/viem/.prettierrc.js | 5 + packages/viem/package.json | 40 ++++ packages/viem/src/actions.ts | 223 ++++++++++++++++++ packages/viem/src/createArbitrumClient.ts | 59 +++++ packages/viem/src/index.ts | 2 + packages/viem/tests/deposit.test.ts | 130 ++++++++++ packages/viem/tsconfig.json | 11 + 12 files changed, 602 insertions(+), 1 deletion(-) create mode 100644 packages/viem/.eslintignore create mode 100644 packages/viem/.eslintrc create mode 100644 packages/viem/.prettierignore create mode 100644 packages/viem/.prettierrc.js create mode 100644 packages/viem/package.json create mode 100644 packages/viem/src/actions.ts create mode 100644 packages/viem/src/createArbitrumClient.ts create mode 100644 packages/viem/src/index.ts create mode 100644 packages/viem/tests/deposit.test.ts create mode 100644 packages/viem/tsconfig.json diff --git a/packages/sdk/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts b/packages/sdk/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts index b84a8289b5..0e4b097b46 100644 --- a/packages/sdk/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts +++ b/packages/sdk/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts @@ -1,5 +1,14 @@ import { StaticJsonRpcProvider } from '@ethersproject/providers' import { Signer, Wallet, ethers, utils } from 'ethers' +import { + Account, + parseEther, + parseUnits, + type Hex, + type WalletClient, + type Chain, + formatUnits, +} from 'viem' import { testSetup as _testSetup, @@ -58,7 +67,7 @@ export async function fundParentCustomFeeToken( const tx = await tokenContract.transfer( address, - utils.parseUnits('10', decimals) + utils.parseUnits('1000', decimals) ) await tx.wait() } @@ -109,3 +118,63 @@ export async function fundChildCustomFeeToken(childSigner: Signer) { }) await tx.wait() } + +export async function getAmountInEnvironmentDecimals( + amount: string +): Promise<[bigint, number]> { + if (isArbitrumNetworkWithCustomFeeToken()) { + const tokenDecimals = await getNativeTokenDecimals({ + parentProvider: ethProvider(), + childNetwork: localNetworks().l3Network!, + }) + return [parseUnits(amount, tokenDecimals), tokenDecimals] + } + return [parseEther(amount), 18] // ETH decimals +} + +export function normalizeBalanceDiffByDecimals( + balanceDiff: bigint, + tokenDecimals: number +): bigint { + // Convert to 18 decimals (ETH standard) for comparison + if (tokenDecimals === 18) return balanceDiff + + // Convert to decimal string with proper precision + const formattedDiff = formatUnits(balanceDiff, 18) + // Parse back with target decimals + return parseUnits(formattedDiff, tokenDecimals) +} + +export async function approveCustomFeeTokenWithViem({ + parentAccount, + parentWalletClient, + chain, +}: { + parentAccount: { address: string } + parentWalletClient: WalletClient + chain: Chain +}) { + if (!isArbitrumNetworkWithCustomFeeToken()) return + + const networks = localNetworks() + const inbox = networks.l3Network!.ethBridge.inbox + + const currentAllowance = await getParentCustomFeeTokenAllowance( + parentAccount.address, + inbox + ) + + // Only approve if allowance is insufficient + if (currentAllowance.lt(ethers.constants.MaxUint256)) { + const ethBridger = await EthBridger.fromProvider(arbProvider()) + const approveRequest = ethBridger.getApproveGasTokenRequest() + await parentWalletClient.sendTransaction({ + to: approveRequest.to as Hex, + data: approveRequest.data as Hex, + account: parentAccount as Account, + chain, + value: BigInt(0), + kzg: undefined, + }) + } +} diff --git a/packages/sdk/tests/testSetup.ts b/packages/sdk/tests/testSetup.ts index 597b0b511e..ac5d067eac 100644 --- a/packages/sdk/tests/testSetup.ts +++ b/packages/sdk/tests/testSetup.ts @@ -40,6 +40,7 @@ import { isArbitrumNetworkWithCustomFeeToken, } from './integration/custom-fee-token/customFeeTokenTestHelpers' import { fundParentSigner } from './integration/testHelpers' +import { Chain } from 'viem' loadEnv() @@ -85,6 +86,8 @@ export const testSetup = async (): Promise<{ inboxTools: InboxTools parentDeployer: Signer childDeployer: Signer + localEthChain: Chain + localArbChain: Chain }> => { const ethProvider = new JsonRpcProvider(config.ethUrl) const arbProvider = new JsonRpcProvider(config.arbUrl) @@ -113,6 +116,23 @@ export const testSetup = async (): Promise<{ assertArbitrumNetworkHasTokenBridge(setChildChain) + // Generate Viem chains using the network data we already have + const localEthChain = generateViemChain( + { + chainId: setChildChain.parentChainId, + name: 'EthLocal', + }, + config.ethUrl + ) + + const localArbChain = generateViemChain( + { + chainId: setChildChain.chainId, + name: setChildChain.name, + }, + config.arbUrl + ) + const erc20Bridger = new Erc20Bridger(setChildChain) const adminErc20Bridger = new AdminErc20Bridger(setChildChain) const ethBridger = new EthBridger(setChildChain) @@ -136,6 +156,8 @@ export const testSetup = async (): Promise<{ inboxTools, parentDeployer, childDeployer, + localEthChain, + localArbChain, } } @@ -153,3 +175,25 @@ export function getLocalNetworksFromFile(): { return { l2Network: localL2, l3Network: localL3 } } + +function generateViemChain( + networkData: { + chainId: number + name: string + }, + rpcUrl: string +): Chain { + return { + id: networkData.chainId, + name: networkData.name, + nativeCurrency: { + decimals: 18, + name: 'Ether', + symbol: 'ETH', + }, + rpcUrls: { + default: { http: [rpcUrl] }, + public: { http: [rpcUrl] }, + }, + } as const +} diff --git a/packages/viem/.eslintignore b/packages/viem/.eslintignore new file mode 100644 index 0000000000..99ead71778 --- /dev/null +++ b/packages/viem/.eslintignore @@ -0,0 +1,5 @@ +dist/** +node_modules/** +coverage/** +src/lib/abi +docs/** diff --git a/packages/viem/.eslintrc b/packages/viem/.eslintrc new file mode 100644 index 0000000000..bde91889ba --- /dev/null +++ b/packages/viem/.eslintrc @@ -0,0 +1,8 @@ +{ + "root": false, + "extends": ["../../.eslintrc.js"], + "parserOptions": { + "files": ["src/**/*.ts", "src/**/*.js"] + }, + "ignorePatterns": ["dist/**/*", "node_modules/**/*"] +} \ No newline at end of file diff --git a/packages/viem/.prettierignore b/packages/viem/.prettierignore new file mode 100644 index 0000000000..ee20e44d90 --- /dev/null +++ b/packages/viem/.prettierignore @@ -0,0 +1,5 @@ +build/** +cache/** +dist/** +src/lib/abi/** +.nyc_output diff --git a/packages/viem/.prettierrc.js b/packages/viem/.prettierrc.js new file mode 100644 index 0000000000..008736fae8 --- /dev/null +++ b/packages/viem/.prettierrc.js @@ -0,0 +1,5 @@ +const baseConfig = require('../../.prettierrc.js') + +module.exports = { + ...baseConfig, +} diff --git a/packages/viem/package.json b/packages/viem/package.json new file mode 100644 index 0000000000..b200680eaf --- /dev/null +++ b/packages/viem/package.json @@ -0,0 +1,40 @@ +{ + "name": "@arbitrum/sdk-viem", + "version": "0.0.1", + "description": "Typescript library client-side interactions with Arbitrum using viem", + "author": "Offchain Labs, Inc.", + "license": "Apache-2.0", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "repository": { + "type": "git", + "url": "git+https://github.com/offchainlabs/arbitrum-sdk.git" + }, + "engines": { + "node": ">=v11", + "npm": "please-use-yarn", + "yarn": ">= 1.0.0" + }, + "bugs": { + "url": "https://github.com/offchainlabs/arbitrum-sdk/issues" + }, + "homepage": "https://offchainlabs.com", + "files": [ + "dist/**/*" + ], + "scripts": { + "build": "rm -rf dist && tsc -p tsconfig.json", + "gen:network": "yarn workspace @arbitrum/sdk gen:network", + "test:integration": "mocha -r ts-node/register 'tests/**/*.test.ts'", + "lint": "eslint .", + "format": "prettier './**/*.{js,json,md,ts,yml}' '!./src/lib/abi' --write && yarn run lint --fix" + }, + "dependencies": { + "@arbitrum/sdk": "4.0.2", + "@arbitrum/viem-compatibility": "0.0.1" + }, + "peerDependencies": { + "ethers": "^5.0.0", + "viem": "^2.0.0" + } +} diff --git a/packages/viem/src/actions.ts b/packages/viem/src/actions.ts new file mode 100644 index 0000000000..4c9f492f1a --- /dev/null +++ b/packages/viem/src/actions.ts @@ -0,0 +1,223 @@ +import { EthBridger } from '../../sdk/src' +import { + ParentToChildMessageStatus, + ParentTransactionReceipt, +} from '../../sdk/src' +import { BigNumber } from 'ethers' +import { + Account, + Address, + Hash, + PublicClient, + TransactionRequest, + WalletClient, +} from 'viem' +import { + publicClientToProvider, + viemTransactionReceiptToEthersTransactionReceipt, +} from '@arbitrum/viem-compatibility' + +export type PrepareDepositEthParameters = { + amount: bigint + account: Account | Address +} + +const DEFAULT_CONFIRMATIONS = 1 +const DEFAULT_TIMEOUT = 1000 * 60 * 5 // 5 minutes + +export type WaitForCrossChainTxParameters = { + hash: Hash + timeout?: number + confirmations?: number +} + +export type SendCrossChainTransactionParameters = { + request: TransactionRequest + timeout?: number + confirmations?: number +} + +export type CrossChainTransactionStatus = { + status: 'success' | 'failed' + complete: boolean + message?: unknown + childTxReceipt?: unknown + hash: Hash +} + +export type DepositEthParameters = { + amount: bigint + account: Account | Address + confirmations?: number + timeout?: number +} + +export type ArbitrumDepositActions = { + prepareDepositEthTransaction: ( + params: PrepareDepositEthParameters + ) => Promise +} + +export type ArbitrumParentWalletActions = { + waitForCrossChainTransaction: ( + params: WaitForCrossChainTxParameters + ) => Promise + + sendCrossChainTransaction: ( + params: SendCrossChainTransactionParameters + ) => Promise + + depositEth: ( + params: DepositEthParameters + ) => Promise +} + +async function prepareDepositEthTransaction( + client: PublicClient, + { amount, account }: PrepareDepositEthParameters +): Promise { + const provider = publicClientToProvider(client) + const ethBridger = await EthBridger.fromProvider(provider) + const request = await ethBridger.getDepositRequest({ + amount: BigNumber.from(amount), + from: typeof account === 'string' ? account : account.address, + }) + + return { + to: request.txRequest.to as `0x${string}`, + value: BigNumber.from(request.txRequest.value).toBigInt(), + data: request.txRequest.data as `0x${string}`, + } +} + +async function waitForCrossChainTransaction( + parentClient: PublicClient, + childClient: PublicClient, + { + hash, + confirmations = DEFAULT_CONFIRMATIONS, + timeout = DEFAULT_TIMEOUT, + }: WaitForCrossChainTxParameters +): Promise { + const childProvider = publicClientToProvider(childClient) + + const viemReceipt = await parentClient.waitForTransactionReceipt({ + hash, + confirmations, + }) + + const ethersReceipt = + viemTransactionReceiptToEthersTransactionReceipt(viemReceipt) + const parentReceipt = new ParentTransactionReceipt(ethersReceipt) + + // Try to get eth deposits first + try { + const ethDeposits = await parentReceipt.getEthDeposits(childProvider) + if (ethDeposits.length > 0) { + const result = await ethDeposits[0].wait(confirmations, timeout) + return { + status: result ? 'success' : 'failed', + complete: Boolean(result), + message: ethDeposits[0], + childTxReceipt: result, + hash, + } + } + } catch (e) { + // Not an eth deposit, continue to check for other message types + } + + // Check for other cross chain messages + try { + const messages = await parentReceipt.getParentToChildMessages(childProvider) + if (messages.length > 0) { + const result = await messages[0].waitForStatus(confirmations, timeout) + return { + status: + result.status === ParentToChildMessageStatus.REDEEMED + ? 'success' + : 'failed', + complete: result.status === ParentToChildMessageStatus.REDEEMED, + message: messages[0], + childTxReceipt: result, + hash, + } + } + } catch (e) { + // Not a cross chain message + } + + throw new Error('No cross chain message found in transaction') +} + +async function sendCrossChainTransaction( + parentClient: PublicClient, + childClient: PublicClient, + walletClient: WalletClient, + { + request, + confirmations = DEFAULT_CONFIRMATIONS, + timeout = DEFAULT_TIMEOUT, + }: SendCrossChainTransactionParameters +): Promise { + const hash = await walletClient.sendTransaction({ + ...request, + chain: walletClient.chain, + account: walletClient.account as Account, + }) + + return waitForCrossChainTransaction(parentClient, childClient, { + hash, + confirmations, + timeout, + }) +} + +async function depositEth( + parentClient: PublicClient, + childClient: PublicClient, + walletClient: WalletClient, + { + amount, + account, + confirmations = DEFAULT_CONFIRMATIONS, + timeout = DEFAULT_TIMEOUT, + }: DepositEthParameters +): Promise { + const request = await prepareDepositEthTransaction(childClient, { + amount, + account, + }) + + return sendCrossChainTransaction(parentClient, childClient, walletClient, { + request, + confirmations, + timeout, + }) +} + +export function arbitrumParentClientActions() { + return (client: PublicClient): ArbitrumDepositActions => ({ + prepareDepositEthTransaction: params => + prepareDepositEthTransaction(client, params), + }) +} + +export function arbitrumParentWalletActions( + parentClient: PublicClient, + childClient: PublicClient +) { + return (walletClient: WalletClient): ArbitrumParentWalletActions => ({ + waitForCrossChainTransaction: (params: WaitForCrossChainTxParameters) => + waitForCrossChainTransaction(parentClient, childClient, params), + sendCrossChainTransaction: (params: SendCrossChainTransactionParameters) => + sendCrossChainTransaction( + parentClient, + childClient, + walletClient, + params + ), + depositEth: (params: DepositEthParameters) => + depositEth(parentClient, childClient, walletClient, params), + }) +} diff --git a/packages/viem/src/createArbitrumClient.ts b/packages/viem/src/createArbitrumClient.ts new file mode 100644 index 0000000000..eb30876ed1 --- /dev/null +++ b/packages/viem/src/createArbitrumClient.ts @@ -0,0 +1,59 @@ +import { + Chain, + PublicClient, + WalletClient, + createPublicClient, + http, +} from 'viem' +import { + ArbitrumDepositActions, + ArbitrumParentWalletActions, + arbitrumParentClientActions, + arbitrumParentWalletActions, +} from './actions' + +export type ArbitrumClients = { + parentPublicClient: PublicClient + childPublicClient: PublicClient & ArbitrumDepositActions + parentWalletClient: WalletClient & ArbitrumParentWalletActions + childWalletClient?: WalletClient +} + +export type CreateArbitrumClientParams = { + parentChain: Chain + childChain: Chain + parentRpcUrl?: string + childRpcUrl?: string + parentWalletClient: WalletClient + childWalletClient?: WalletClient +} + +export function createArbitrumClient({ + parentChain, + childChain, + parentRpcUrl, + childRpcUrl, + parentWalletClient, + childWalletClient, +}: CreateArbitrumClientParams): ArbitrumClients { + const parentPublicClient = createPublicClient({ + chain: parentChain, + transport: http(parentRpcUrl || parentChain.rpcUrls.default.http[0]), + }) + + const childPublicClient = createPublicClient({ + chain: childChain, + transport: http(childRpcUrl || childChain.rpcUrls.default.http[0]), + }).extend(arbitrumParentClientActions()) + + const extendedParentWalletClient = parentWalletClient.extend( + arbitrumParentWalletActions(parentPublicClient, childPublicClient) + ) + + return { + parentPublicClient, + childPublicClient, + parentWalletClient: extendedParentWalletClient, + childWalletClient, + } +} diff --git a/packages/viem/src/index.ts b/packages/viem/src/index.ts new file mode 100644 index 0000000000..2979838711 --- /dev/null +++ b/packages/viem/src/index.ts @@ -0,0 +1,2 @@ +export * from './actions' +export * from './createArbitrumClient' diff --git a/packages/viem/tests/deposit.test.ts b/packages/viem/tests/deposit.test.ts new file mode 100644 index 0000000000..1a826489ab --- /dev/null +++ b/packages/viem/tests/deposit.test.ts @@ -0,0 +1,130 @@ +import { expect } from 'chai' +import { createWalletClient, http, parseEther, type Chain } from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import { config, testSetup } from '../../sdk/tests/testSetup' +import { createArbitrumClient } from '../src/createArbitrumClient' +import { fundParentSigner } from '../../sdk/tests/integration/testHelpers' +import { registerCustomArbitrumNetwork } from '../../sdk/src/lib/dataEntities/networks' +import { + approveParentCustomFeeToken, + fundParentCustomFeeToken, + isArbitrumNetworkWithCustomFeeToken, + getAmountInEnvironmentDecimals, + normalizeBalanceDiffByDecimals, + approveCustomFeeTokenWithViem, +} from '../../sdk/tests/integration/custom-fee-token/customFeeTokenTestHelpers' + +describe('deposit', function () { + this.timeout(300000) + + let localEthChain: Chain + let localArbChain: Chain + let setup: Awaited> + + before(async function () { + setup = await testSetup() + localEthChain = setup.localEthChain + localArbChain = setup.localArbChain + registerCustomArbitrumNetwork(setup.childChain) + }) + + beforeEach(async function () { + const parentAccount = privateKeyToAccount(`0x${config.ethKey}`) + await fundParentSigner(setup.parentSigner) + if (isArbitrumNetworkWithCustomFeeToken()) { + await fundParentCustomFeeToken(parentAccount.address) + await approveParentCustomFeeToken(setup.parentSigner) + } + }) + + it('deposits ETH from parent to child using deposit action', async function () { + const parentAccount = privateKeyToAccount(`0x${config.ethKey}`) + const [depositAmount, tokenDecimals] = await getAmountInEnvironmentDecimals( + '0.01' + ) + + const baseParentWalletClient = createWalletClient({ + account: parentAccount, + chain: localEthChain, + transport: http(config.ethUrl), + }) + + const baseChildWalletClient = createWalletClient({ + account: parentAccount, + chain: localArbChain, + transport: http(config.arbUrl), + }) + + const { childPublicClient, parentWalletClient } = createArbitrumClient({ + parentChain: localEthChain, + childChain: localArbChain, + parentWalletClient: baseParentWalletClient, + childWalletClient: baseChildWalletClient, + }) + + const initialBalance = await childPublicClient.getBalance({ + address: parentAccount.address, + }) + + if (isArbitrumNetworkWithCustomFeeToken()) { + await approveCustomFeeTokenWithViem({ + parentAccount, + parentWalletClient, + chain: localEthChain, + }) + } + + const result = await parentWalletClient.depositEth({ + amount: depositAmount, + account: parentAccount, + }) + + expect(result.status).to.equal('success') + + const finalBalance = await childPublicClient.getBalance({ + address: parentAccount.address, + }) + + const balanceDiff = finalBalance - initialBalance + const normalizedBalanceDiff = normalizeBalanceDiffByDecimals( + BigInt(balanceDiff), + tokenDecimals + ) + + expect(normalizedBalanceDiff.toString()).to.equal(depositAmount.toString()) + }) + + it('handles deposit failure gracefully', async function () { + const parentAccount = privateKeyToAccount(`0x${config.ethKey}`) + const depositAmount = parseEther('999999999') + + const baseParentWalletClient = createWalletClient({ + account: parentAccount, + chain: localEthChain, + transport: http(config.ethUrl), + }) + + const baseChildWalletClient = createWalletClient({ + account: parentAccount, + chain: localArbChain, + transport: http(config.arbUrl), + }) + + const { parentWalletClient } = createArbitrumClient({ + parentChain: localEthChain, + childChain: localArbChain, + parentWalletClient: baseParentWalletClient, + childWalletClient: baseChildWalletClient, + }) + + try { + await parentWalletClient.depositEth({ + amount: depositAmount, + account: parentAccount, + }) + expect.fail('Should have thrown an error') + } catch (error) { + expect(error).to.exist + } + }) +}) diff --git a/packages/viem/tsconfig.json b/packages/viem/tsconfig.json new file mode 100644 index 0000000000..70de820d0e --- /dev/null +++ b/packages/viem/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "target": "ES2020", + "rootDir": "./src", + "outDir": "./dist", + "skipLibCheck": true + }, + "include": ["src/**/*.ts", "src/**/*.d.ts"], + "exclude": ["node_modules", "dist"] +} From a10e420e72c0e7d7b70740dc7833b4ae3f2637b3 Mon Sep 17 00:00:00 2001 From: Doug Lance <4741454+douglance@users.noreply.github.com> Date: Thu, 26 Dec 2024 12:56:49 -0500 Subject: [PATCH 02/10] update package name --- packages/viem/package.json | 2 +- packages/viem/src/actions.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/viem/package.json b/packages/viem/package.json index b200680eaf..f0eb9b784d 100644 --- a/packages/viem/package.json +++ b/packages/viem/package.json @@ -31,7 +31,7 @@ }, "dependencies": { "@arbitrum/sdk": "4.0.2", - "@arbitrum/viem-compatibility": "0.0.1" + "@offchainlabs/ethers-viem-compat": "0.0.1" }, "peerDependencies": { "ethers": "^5.0.0", diff --git a/packages/viem/src/actions.ts b/packages/viem/src/actions.ts index 4c9f492f1a..cb3ae10feb 100644 --- a/packages/viem/src/actions.ts +++ b/packages/viem/src/actions.ts @@ -15,7 +15,7 @@ import { import { publicClientToProvider, viemTransactionReceiptToEthersTransactionReceipt, -} from '@arbitrum/viem-compatibility' +} from '@offchainlabs/ethers-viem-compat' export type PrepareDepositEthParameters = { amount: bigint From 757ac90ce679327806b0441d646694f6ec828dde Mon Sep 17 00:00:00 2001 From: Doug Lance <4741454+douglance@users.noreply.github.com> Date: Thu, 26 Dec 2024 13:12:17 -0500 Subject: [PATCH 03/10] fixes issue with statefulness --- packages/viem/src/actions.ts | 12 ++++++------ packages/viem/tests/deposit.test.ts | 20 ++++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/viem/src/actions.ts b/packages/viem/src/actions.ts index cb3ae10feb..70541a4fee 100644 --- a/packages/viem/src/actions.ts +++ b/packages/viem/src/actions.ts @@ -1,8 +1,12 @@ -import { EthBridger } from '../../sdk/src' import { + EthBridger, ParentToChildMessageStatus, ParentTransactionReceipt, -} from '../../sdk/src' +} from '@arbitrum/sdk' +import { + publicClientToProvider, + viemTransactionReceiptToEthersTransactionReceipt, +} from '@offchainlabs/ethers-viem-compat' import { BigNumber } from 'ethers' import { Account, @@ -12,10 +16,6 @@ import { TransactionRequest, WalletClient, } from 'viem' -import { - publicClientToProvider, - viemTransactionReceiptToEthersTransactionReceipt, -} from '@offchainlabs/ethers-viem-compat' export type PrepareDepositEthParameters = { amount: bigint diff --git a/packages/viem/tests/deposit.test.ts b/packages/viem/tests/deposit.test.ts index 1a826489ab..b790a42170 100644 --- a/packages/viem/tests/deposit.test.ts +++ b/packages/viem/tests/deposit.test.ts @@ -1,18 +1,18 @@ -import { expect } from 'chai' -import { createWalletClient, http, parseEther, type Chain } from 'viem' -import { privateKeyToAccount } from 'viem/accounts' -import { config, testSetup } from '../../sdk/tests/testSetup' -import { createArbitrumClient } from '../src/createArbitrumClient' -import { fundParentSigner } from '../../sdk/tests/integration/testHelpers' -import { registerCustomArbitrumNetwork } from '../../sdk/src/lib/dataEntities/networks' +import { registerCustomArbitrumNetwork } from '@arbitrum/sdk' import { + approveCustomFeeTokenWithViem, approveParentCustomFeeToken, fundParentCustomFeeToken, - isArbitrumNetworkWithCustomFeeToken, getAmountInEnvironmentDecimals, + isArbitrumNetworkWithCustomFeeToken, normalizeBalanceDiffByDecimals, - approveCustomFeeTokenWithViem, -} from '../../sdk/tests/integration/custom-fee-token/customFeeTokenTestHelpers' +} from '@arbitrum/sdk/tests/integration/custom-fee-token/customFeeTokenTestHelpers' +import { fundParentSigner } from '@arbitrum/sdk/tests/integration/testHelpers' +import { config, testSetup } from '@arbitrum/sdk/tests/testSetup' +import { expect } from 'chai' +import { createWalletClient, http, parseEther, type Chain } from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import { createArbitrumClient } from '../src/createArbitrumClient' describe('deposit', function () { this.timeout(300000) From a7c2d6db9677065687336d270892d85552a00879 Mon Sep 17 00:00:00 2001 From: Doug Lance <4741454+douglance@users.noreply.github.com> Date: Thu, 26 Dec 2024 13:13:16 -0500 Subject: [PATCH 04/10] change name --- packages/{viem => sdk-viem}/.eslintignore | 0 packages/{viem => sdk-viem}/.eslintrc | 0 packages/{viem => sdk-viem}/.prettierignore | 0 packages/{viem => sdk-viem}/.prettierrc.js | 0 packages/{viem => sdk-viem}/package.json | 0 packages/{viem => sdk-viem}/src/actions.ts | 0 packages/{viem => sdk-viem}/src/createArbitrumClient.ts | 0 packages/{viem => sdk-viem}/src/index.ts | 0 packages/{viem => sdk-viem}/tests/deposit.test.ts | 0 packages/{viem => sdk-viem}/tsconfig.json | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename packages/{viem => sdk-viem}/.eslintignore (100%) rename packages/{viem => sdk-viem}/.eslintrc (100%) rename packages/{viem => sdk-viem}/.prettierignore (100%) rename packages/{viem => sdk-viem}/.prettierrc.js (100%) rename packages/{viem => sdk-viem}/package.json (100%) rename packages/{viem => sdk-viem}/src/actions.ts (100%) rename packages/{viem => sdk-viem}/src/createArbitrumClient.ts (100%) rename packages/{viem => sdk-viem}/src/index.ts (100%) rename packages/{viem => sdk-viem}/tests/deposit.test.ts (100%) rename packages/{viem => sdk-viem}/tsconfig.json (100%) diff --git a/packages/viem/.eslintignore b/packages/sdk-viem/.eslintignore similarity index 100% rename from packages/viem/.eslintignore rename to packages/sdk-viem/.eslintignore diff --git a/packages/viem/.eslintrc b/packages/sdk-viem/.eslintrc similarity index 100% rename from packages/viem/.eslintrc rename to packages/sdk-viem/.eslintrc diff --git a/packages/viem/.prettierignore b/packages/sdk-viem/.prettierignore similarity index 100% rename from packages/viem/.prettierignore rename to packages/sdk-viem/.prettierignore diff --git a/packages/viem/.prettierrc.js b/packages/sdk-viem/.prettierrc.js similarity index 100% rename from packages/viem/.prettierrc.js rename to packages/sdk-viem/.prettierrc.js diff --git a/packages/viem/package.json b/packages/sdk-viem/package.json similarity index 100% rename from packages/viem/package.json rename to packages/sdk-viem/package.json diff --git a/packages/viem/src/actions.ts b/packages/sdk-viem/src/actions.ts similarity index 100% rename from packages/viem/src/actions.ts rename to packages/sdk-viem/src/actions.ts diff --git a/packages/viem/src/createArbitrumClient.ts b/packages/sdk-viem/src/createArbitrumClient.ts similarity index 100% rename from packages/viem/src/createArbitrumClient.ts rename to packages/sdk-viem/src/createArbitrumClient.ts diff --git a/packages/viem/src/index.ts b/packages/sdk-viem/src/index.ts similarity index 100% rename from packages/viem/src/index.ts rename to packages/sdk-viem/src/index.ts diff --git a/packages/viem/tests/deposit.test.ts b/packages/sdk-viem/tests/deposit.test.ts similarity index 100% rename from packages/viem/tests/deposit.test.ts rename to packages/sdk-viem/tests/deposit.test.ts diff --git a/packages/viem/tsconfig.json b/packages/sdk-viem/tsconfig.json similarity index 100% rename from packages/viem/tsconfig.json rename to packages/sdk-viem/tsconfig.json From 37afa5fd03f380d261dada9b321b0b7363385474 Mon Sep 17 00:00:00 2001 From: Doug Lance <4741454+douglance@users.noreply.github.com> Date: Thu, 26 Dec 2024 13:16:42 -0500 Subject: [PATCH 05/10] add no op --- packages/sdk-viem/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/sdk-viem/package.json b/packages/sdk-viem/package.json index f0eb9b784d..80acd11f90 100644 --- a/packages/sdk-viem/package.json +++ b/packages/sdk-viem/package.json @@ -25,6 +25,7 @@ "scripts": { "build": "rm -rf dist && tsc -p tsconfig.json", "gen:network": "yarn workspace @arbitrum/sdk gen:network", + "test:unit": "echo 'No unit tests for sdk-viem'", "test:integration": "mocha -r ts-node/register 'tests/**/*.test.ts'", "lint": "eslint .", "format": "prettier './**/*.{js,json,md,ts,yml}' '!./src/lib/abi' --write && yarn run lint --fix" From 5b8ea8b7f4b06b0397c1b96b7d2c099f9f29f4d3 Mon Sep 17 00:00:00 2001 From: Doug Lance <4741454+douglance@users.noreply.github.com> Date: Thu, 26 Dec 2024 13:43:19 -0500 Subject: [PATCH 06/10] update test runner --- package.json | 2 +- packages/ethers-viem-compat/package.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index ef6e5ba67a..85cd663f73 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "lint": "yarn workspaces run lint", "format": "yarn workspaces run format", "test:unit": "yarn workspaces run test:unit", - "test:integration": "yarn workspace @arbitrum/sdk test:integration", + "test:integration": "yarn workspaces run test:integration", "gen:abi": "yarn workspace @arbitrum/sdk gen:abi", "gen:network": "yarn workspace @arbitrum/sdk gen:network" }, diff --git a/packages/ethers-viem-compat/package.json b/packages/ethers-viem-compat/package.json index c6a112cac5..fbeba5904f 100644 --- a/packages/ethers-viem-compat/package.json +++ b/packages/ethers-viem-compat/package.json @@ -25,6 +25,7 @@ "scripts": { "build": "rm -rf dist && tsc -p tsconfig.json", "test:unit": "mocha -r ts-node/register 'tests/**/*.test.ts'", + "test:integration": "echo 'No integration tests for ethers-viem-compat'", "lint": "eslint .", "format": "prettier './**/*.{js,json,md,ts,yml}' '!./src/lib/abi' --write && yarn run lint --fix" }, From 1262a9eddf294a04adb8a63282a4e075841850d2 Mon Sep 17 00:00:00 2001 From: Doug Lance <4741454+douglance@users.noreply.github.com> Date: Tue, 31 Dec 2024 10:32:39 -0500 Subject: [PATCH 07/10] fix ci --- package.json | 5 +++-- packages/ethers-viem-compat/package.json | 3 ++- packages/sdk-viem/package.json | 7 ++++--- packages/sdk/package.json | 3 ++- yarn.lock | 16 ++++++++++++++++ 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 85cd663f73..d5dbd47a6b 100644 --- a/package.json +++ b/package.json @@ -2,13 +2,14 @@ "private": true, "scripts": { "audit:ci": "audit-ci --config ./audit-ci.jsonc", - "build": "yarn workspace @arbitrum/sdk build", + "build": "yarn workspaces run build", "lint": "yarn workspaces run lint", "format": "yarn workspaces run format", "test:unit": "yarn workspaces run test:unit", "test:integration": "yarn workspaces run test:integration", "gen:abi": "yarn workspace @arbitrum/sdk gen:abi", - "gen:network": "yarn workspace @arbitrum/sdk gen:network" + "gen:network": "yarn workspace @arbitrum/sdk gen:network", + "clean": "yarn workspaces run clean" }, "workspaces": { "packages": [ diff --git a/packages/ethers-viem-compat/package.json b/packages/ethers-viem-compat/package.json index fbeba5904f..2981c6c731 100644 --- a/packages/ethers-viem-compat/package.json +++ b/packages/ethers-viem-compat/package.json @@ -27,7 +27,8 @@ "test:unit": "mocha -r ts-node/register 'tests/**/*.test.ts'", "test:integration": "echo 'No integration tests for ethers-viem-compat'", "lint": "eslint .", - "format": "prettier './**/*.{js,json,md,ts,yml}' '!./src/lib/abi' --write && yarn run lint --fix" + "format": "prettier './**/*.{js,json,md,ts,yml}' '!./src/lib/abi' --write && yarn run lint --fix", + "clean": "rm -rf node_modules && rm -rf dist" }, "peerDependencies": { "ethers": "^5.0.0", diff --git a/packages/sdk-viem/package.json b/packages/sdk-viem/package.json index 80acd11f90..c24ad8dd9e 100644 --- a/packages/sdk-viem/package.json +++ b/packages/sdk-viem/package.json @@ -28,11 +28,12 @@ "test:unit": "echo 'No unit tests for sdk-viem'", "test:integration": "mocha -r ts-node/register 'tests/**/*.test.ts'", "lint": "eslint .", - "format": "prettier './**/*.{js,json,md,ts,yml}' '!./src/lib/abi' --write && yarn run lint --fix" + "format": "prettier './**/*.{js,json,md,ts,yml}' '!./src/lib/abi' --write && yarn run lint --fix", + "clean": "rm -rf node_modules && rm -rf dist" }, "dependencies": { - "@arbitrum/sdk": "4.0.2", - "@offchainlabs/ethers-viem-compat": "0.0.1" + "@arbitrum/sdk": "workspace:*", + "@offchainlabs/ethers-viem-compat": "workspace:*" }, "peerDependencies": { "ethers": "^5.0.0", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 0e47807575..7e9f374a1d 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -46,7 +46,8 @@ "setStandard": "ts-node scripts/setStandardGateways.ts", "setCustom": "ts-node scripts/setArbCustomGateways.ts", "cancelRetryable": "ts-node scripts/cancelRetryable.ts", - "bridgeStandardToken": "ts-node scripts/deployStandard.ts" + "bridgeStandardToken": "ts-node scripts/deployStandard.ts", + "clean": "rm -rf node_modules && rm -rf dist" }, "dependencies": { "@ethersproject/address": "^5.0.8", diff --git a/yarn.lock b/yarn.lock index 8258d4c9ec..28c65d9412 100644 --- a/yarn.lock +++ b/yarn.lock @@ -42,6 +42,17 @@ "@openzeppelin/contracts-upgradeable" "4.5.2" patch-package "^6.4.7" +"@arbitrum/sdk@workspace:*": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@arbitrum/sdk/-/sdk-4.0.2.tgz#23555858f49e2b237b94a65bd486c65edb7b1690" + integrity sha512-KkuXNwbG5c/hCT66EG2tFMHXxIDCvt9dxAIeykZYnW7KyEH5GNlRwaPzwo6MU0shHNc0qg6pZzy2XakJWuSw2Q== + dependencies: + "@ethersproject/address" "^5.0.8" + "@ethersproject/bignumber" "^5.1.1" + "@ethersproject/bytes" "^5.0.8" + async-mutex "^0.4.0" + ethers "^5.1.0" + "@arbitrum/token-bridge-contracts@1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@arbitrum/token-bridge-contracts/-/token-bridge-contracts-1.2.0.tgz#b1dc02e123393848d0d8e5c167028bafa0ac8229" @@ -1344,6 +1355,11 @@ resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.5.tgz#131b0da1b71680d5a01569f916ae878229d326d3" integrity sha512-A2gZAGB6kUvLx+kzM92HKuUF33F1FSe90L0TmkXkT2Hh0OKRpvWZURUSU2nghD2yC4DzfEZ3DftfeHGvZ2JTUw== +"@offchainlabs/ethers-viem-compat@workspace:*": + version "0.0.1" + resolved "https://registry.yarnpkg.com/@offchainlabs/ethers-viem-compat/-/ethers-viem-compat-0.0.1.tgz#928ecf333839a9cecb69a3440034909d114b1a8a" + integrity sha512-yzKdlXrbL+udhCWNcZWdMSU7xEF17fQVy5r/er4DLlSqyHI7AZEpH0YLHPaFEA2JrxI9KmR0tJBvMOCa5H0XqQ== + "@offchainlabs/l1-l3-teleport-contracts@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@offchainlabs/l1-l3-teleport-contracts/-/l1-l3-teleport-contracts-1.0.1.tgz#cdb4599f5714e123e52e5547d91c955fbfeff2c8" From 44b85bdb3ee4f44708abd92d4cee542b8cbc1a9a Mon Sep 17 00:00:00 2001 From: Doug Lance <4741454+douglance@users.noreply.github.com> Date: Thu, 9 Jan 2025 08:56:04 -0500 Subject: [PATCH 08/10] simplify system --- packages/sdk-viem/src/actions.ts | 12 +-- packages/sdk-viem/src/createArbitrumClient.ts | 8 +- .../tests/customFeeTokenTestHelpers.ts | 87 +++++++++++++++++ packages/sdk-viem/tests/deposit.test.ts | 75 ++++----------- packages/sdk-viem/tests/testSetup.ts | 96 +++++++++++++++++++ packages/sdk/tests/testSetup.ts | 46 +-------- 6 files changed, 210 insertions(+), 114 deletions(-) create mode 100644 packages/sdk-viem/tests/customFeeTokenTestHelpers.ts create mode 100644 packages/sdk-viem/tests/testSetup.ts diff --git a/packages/sdk-viem/src/actions.ts b/packages/sdk-viem/src/actions.ts index 70541a4fee..1a0503ac88 100644 --- a/packages/sdk-viem/src/actions.ts +++ b/packages/sdk-viem/src/actions.ts @@ -72,7 +72,7 @@ export type ArbitrumParentWalletActions = { ) => Promise } -async function prepareDepositEthTransaction( +export async function prepareDepositEthTransaction( client: PublicClient, { amount, account }: PrepareDepositEthParameters ): Promise { @@ -84,13 +84,13 @@ async function prepareDepositEthTransaction( }) return { - to: request.txRequest.to as `0x${string}`, + to: request.txRequest.to as Address, value: BigNumber.from(request.txRequest.value).toBigInt(), - data: request.txRequest.data as `0x${string}`, + data: request.txRequest.data as Address, } } -async function waitForCrossChainTransaction( +export async function waitForCrossChainTransaction( parentClient: PublicClient, childClient: PublicClient, { @@ -150,7 +150,7 @@ async function waitForCrossChainTransaction( throw new Error('No cross chain message found in transaction') } -async function sendCrossChainTransaction( +export async function sendCrossChainTransaction( parentClient: PublicClient, childClient: PublicClient, walletClient: WalletClient, @@ -173,7 +173,7 @@ async function sendCrossChainTransaction( }) } -async function depositEth( +export async function depositEth( parentClient: PublicClient, childClient: PublicClient, walletClient: WalletClient, diff --git a/packages/sdk-viem/src/createArbitrumClient.ts b/packages/sdk-viem/src/createArbitrumClient.ts index eb30876ed1..e8bd834eec 100644 --- a/packages/sdk-viem/src/createArbitrumClient.ts +++ b/packages/sdk-viem/src/createArbitrumClient.ts @@ -6,15 +6,13 @@ import { http, } from 'viem' import { - ArbitrumDepositActions, ArbitrumParentWalletActions, - arbitrumParentClientActions, - arbitrumParentWalletActions, + arbitrumParentWalletActions } from './actions' export type ArbitrumClients = { parentPublicClient: PublicClient - childPublicClient: PublicClient & ArbitrumDepositActions + childPublicClient: PublicClient parentWalletClient: WalletClient & ArbitrumParentWalletActions childWalletClient?: WalletClient } @@ -44,7 +42,7 @@ export function createArbitrumClient({ const childPublicClient = createPublicClient({ chain: childChain, transport: http(childRpcUrl || childChain.rpcUrls.default.http[0]), - }).extend(arbitrumParentClientActions()) + }) const extendedParentWalletClient = parentWalletClient.extend( arbitrumParentWalletActions(parentPublicClient, childPublicClient) diff --git a/packages/sdk-viem/tests/customFeeTokenTestHelpers.ts b/packages/sdk-viem/tests/customFeeTokenTestHelpers.ts new file mode 100644 index 0000000000..af1a8773fb --- /dev/null +++ b/packages/sdk-viem/tests/customFeeTokenTestHelpers.ts @@ -0,0 +1,87 @@ +import { ethers } from 'ethers' +import { + Account, + formatUnits, + parseEther, + parseUnits, + type Chain, + type Hex, + type WalletClient, +} from 'viem' + +import { + getParentCustomFeeTokenAllowance, + isArbitrumNetworkWithCustomFeeToken, +} from '@arbitrum/sdk/tests/integration/custom-fee-token/customFeeTokenTestHelpers' +import { EthBridger } from '@arbitrum/sdk/src' +import { getNativeTokenDecimals } from '@arbitrum/sdk/src/lib/utils/lib' +import { + testSetup as _testSetup, + config, + getLocalNetworksFromFile, +} from '@arbitrum/sdk/tests/testSetup' +import { StaticJsonRpcProvider } from '@ethersproject/providers' + +const ethProvider = () => new StaticJsonRpcProvider(config.ethUrl) +const arbProvider = () => new StaticJsonRpcProvider(config.arbUrl) +const localNetworks = () => getLocalNetworksFromFile() + +export async function getAmountInEnvironmentDecimals( + amount: string +): Promise<[bigint, number]> { + if (isArbitrumNetworkWithCustomFeeToken()) { + const tokenDecimals = await getNativeTokenDecimals({ + parentProvider: ethProvider(), + childNetwork: localNetworks().l3Network!, + }) + return [parseUnits(amount, tokenDecimals), tokenDecimals] + } + return [parseEther(amount), 18] // ETH decimals +} + +export function normalizeBalanceDiffByDecimals( + balanceDiff: bigint, + tokenDecimals: number +): bigint { + // Convert to 18 decimals (ETH standard) for comparison + if (tokenDecimals === 18) return balanceDiff + + // Convert to decimal string with proper precision + const formattedDiff = formatUnits(balanceDiff, 18) + // Parse back with target decimals + return parseUnits(formattedDiff, tokenDecimals) +} + +export async function approveCustomFeeTokenWithViem({ + parentAccount, + parentWalletClient, + chain, +}: { + parentAccount: { address: string } + parentWalletClient: WalletClient + chain: Chain +}) { + if (!isArbitrumNetworkWithCustomFeeToken()) return + + const networks = localNetworks() + const inbox = networks.l3Network!.ethBridge.inbox + + const currentAllowance = await getParentCustomFeeTokenAllowance( + parentAccount.address, + inbox + ) + + // Only approve if allowance is insufficient + if (currentAllowance.lt(ethers.constants.MaxUint256)) { + const ethBridger = await EthBridger.fromProvider(arbProvider()) + const approveRequest = ethBridger.getApproveGasTokenRequest() + await parentWalletClient.sendTransaction({ + to: approveRequest.to as Hex, + data: approveRequest.data as Hex, + account: parentAccount as Account, + chain, + value: BigInt(0), + kzg: undefined, + }) + } +} diff --git a/packages/sdk-viem/tests/deposit.test.ts b/packages/sdk-viem/tests/deposit.test.ts index b790a42170..9a3d1309ed 100644 --- a/packages/sdk-viem/tests/deposit.test.ts +++ b/packages/sdk-viem/tests/deposit.test.ts @@ -1,88 +1,63 @@ -import { registerCustomArbitrumNetwork } from '@arbitrum/sdk' import { - approveCustomFeeTokenWithViem, approveParentCustomFeeToken, fundParentCustomFeeToken, - getAmountInEnvironmentDecimals, isArbitrumNetworkWithCustomFeeToken, - normalizeBalanceDiffByDecimals, } from '@arbitrum/sdk/tests/integration/custom-fee-token/customFeeTokenTestHelpers' import { fundParentSigner } from '@arbitrum/sdk/tests/integration/testHelpers' -import { config, testSetup } from '@arbitrum/sdk/tests/testSetup' import { expect } from 'chai' -import { createWalletClient, http, parseEther, type Chain } from 'viem' -import { privateKeyToAccount } from 'viem/accounts' -import { createArbitrumClient } from '../src/createArbitrumClient' +import { parseEther } from 'viem' +import { + approveCustomFeeTokenWithViem, + getAmountInEnvironmentDecimals, + normalizeBalanceDiffByDecimals, +} from './customFeeTokenTestHelpers' +import { testSetup } from './testSetup' describe('deposit', function () { this.timeout(300000) - let localEthChain: Chain - let localArbChain: Chain let setup: Awaited> before(async function () { setup = await testSetup() - localEthChain = setup.localEthChain - localArbChain = setup.localArbChain - registerCustomArbitrumNetwork(setup.childChain) }) beforeEach(async function () { - const parentAccount = privateKeyToAccount(`0x${config.ethKey}`) await fundParentSigner(setup.parentSigner) if (isArbitrumNetworkWithCustomFeeToken()) { - await fundParentCustomFeeToken(parentAccount.address) + await fundParentCustomFeeToken(setup.parentAccount.address) await approveParentCustomFeeToken(setup.parentSigner) } }) it('deposits ETH from parent to child using deposit action', async function () { - const parentAccount = privateKeyToAccount(`0x${config.ethKey}`) const [depositAmount, tokenDecimals] = await getAmountInEnvironmentDecimals( '0.01' ) - const baseParentWalletClient = createWalletClient({ - account: parentAccount, - chain: localEthChain, - transport: http(config.ethUrl), - }) - - const baseChildWalletClient = createWalletClient({ - account: parentAccount, - chain: localArbChain, - transport: http(config.arbUrl), - }) - - const { childPublicClient, parentWalletClient } = createArbitrumClient({ - parentChain: localEthChain, - childChain: localArbChain, - parentWalletClient: baseParentWalletClient, - childWalletClient: baseChildWalletClient, - }) + const { childPublicClient, parentWalletClient } = setup const initialBalance = await childPublicClient.getBalance({ - address: parentAccount.address, + address: setup.parentAccount.address, }) if (isArbitrumNetworkWithCustomFeeToken()) { await approveCustomFeeTokenWithViem({ - parentAccount, + parentAccount: setup.parentAccount, parentWalletClient, - chain: localEthChain, + chain: setup.localEthChain, }) } const result = await parentWalletClient.depositEth({ amount: depositAmount, - account: parentAccount, + account: setup.parentAccount, }) expect(result.status).to.equal('success') const finalBalance = await childPublicClient.getBalance({ - address: parentAccount.address, + address: setup.parentAccount.address, }) const balanceDiff = finalBalance - initialBalance @@ -95,32 +70,14 @@ describe('deposit', function () { }) it('handles deposit failure gracefully', async function () { - const parentAccount = privateKeyToAccount(`0x${config.ethKey}`) const depositAmount = parseEther('999999999') - const baseParentWalletClient = createWalletClient({ - account: parentAccount, - chain: localEthChain, - transport: http(config.ethUrl), - }) - - const baseChildWalletClient = createWalletClient({ - account: parentAccount, - chain: localArbChain, - transport: http(config.arbUrl), - }) - - const { parentWalletClient } = createArbitrumClient({ - parentChain: localEthChain, - childChain: localArbChain, - parentWalletClient: baseParentWalletClient, - childWalletClient: baseChildWalletClient, - }) + const { parentWalletClient } = setup try { await parentWalletClient.depositEth({ amount: depositAmount, - account: parentAccount, + account: setup.parentAccount, }) expect.fail('Should have thrown an error') } catch (error) { diff --git a/packages/sdk-viem/tests/testSetup.ts b/packages/sdk-viem/tests/testSetup.ts new file mode 100644 index 0000000000..ed5811547a --- /dev/null +++ b/packages/sdk-viem/tests/testSetup.ts @@ -0,0 +1,96 @@ +import { + config, + testSetup as sdkTestSetup, +} from '@arbitrum/sdk/tests/testSetup' +import { Address, Chain, createWalletClient, http } from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import { + ArbitrumClients, + createArbitrumClient, +} from '../src/createArbitrumClient' + +export type ViemTestSetup = { + localEthChain: Chain + localArbChain: Chain + parentAccount: ReturnType + childPublicClient: ArbitrumClients['childPublicClient'] + parentWalletClient: ArbitrumClients['parentWalletClient'] + childChain: Awaited>['childChain'] + parentSigner: Awaited>['parentSigner'] +} + +function generateViemChain( + networkData: { + chainId: number + name: string + }, + rpcUrl: string +): Chain { + return { + id: networkData.chainId, + name: networkData.name, + nativeCurrency: { + decimals: 18, + name: 'Ether', + symbol: 'ETH', + }, + rpcUrls: { + default: { http: [rpcUrl] }, + public: { http: [rpcUrl] }, + }, + } as const +} + +export async function testSetup(): Promise { + const setup = await sdkTestSetup() + + const parentPrivateKey = setup.seed._signingKey().privateKey as Address + const parentAccount = privateKeyToAccount(parentPrivateKey) + + // Generate Viem chains using the network data we already have + const localEthChain = generateViemChain( + { + chainId: setup.childChain.parentChainId, + name: 'EthLocal', + }, + config.ethUrl + ) + + const localArbChain = generateViemChain( + { + chainId: setup.childChain.chainId, + name: setup.childChain.name, + }, + config.arbUrl + ) + + const baseParentWalletClient = createWalletClient({ + account: parentAccount, + chain: localEthChain, + transport: http(config.ethUrl), + }) + + const baseChildWalletClient = createWalletClient({ + account: parentAccount, + chain: localArbChain, + transport: http(config.arbUrl), + }) + + const { childPublicClient, parentWalletClient } = createArbitrumClient({ + parentChain: localEthChain, + childChain: localArbChain, + parentWalletClient: baseParentWalletClient, + childWalletClient: baseChildWalletClient, + }) + + return { + ...setup, + localEthChain, + localArbChain, + parentAccount, + childPublicClient, + parentWalletClient, + } +} + +export { config } diff --git a/packages/sdk/tests/testSetup.ts b/packages/sdk/tests/testSetup.ts index ac5d067eac..677b080637 100644 --- a/packages/sdk/tests/testSetup.ts +++ b/packages/sdk/tests/testSetup.ts @@ -40,7 +40,6 @@ import { isArbitrumNetworkWithCustomFeeToken, } from './integration/custom-fee-token/customFeeTokenTestHelpers' import { fundParentSigner } from './integration/testHelpers' -import { Chain } from 'viem' loadEnv() @@ -86,8 +85,7 @@ export const testSetup = async (): Promise<{ inboxTools: InboxTools parentDeployer: Signer childDeployer: Signer - localEthChain: Chain - localArbChain: Chain + seed: Wallet }> => { const ethProvider = new JsonRpcProvider(config.ethUrl) const arbProvider = new JsonRpcProvider(config.arbUrl) @@ -116,23 +114,6 @@ export const testSetup = async (): Promise<{ assertArbitrumNetworkHasTokenBridge(setChildChain) - // Generate Viem chains using the network data we already have - const localEthChain = generateViemChain( - { - chainId: setChildChain.parentChainId, - name: 'EthLocal', - }, - config.ethUrl - ) - - const localArbChain = generateViemChain( - { - chainId: setChildChain.chainId, - name: setChildChain.name, - }, - config.arbUrl - ) - const erc20Bridger = new Erc20Bridger(setChildChain) const adminErc20Bridger = new AdminErc20Bridger(setChildChain) const ethBridger = new EthBridger(setChildChain) @@ -156,8 +137,7 @@ export const testSetup = async (): Promise<{ inboxTools, parentDeployer, childDeployer, - localEthChain, - localArbChain, + seed, } } @@ -175,25 +155,3 @@ export function getLocalNetworksFromFile(): { return { l2Network: localL2, l3Network: localL3 } } - -function generateViemChain( - networkData: { - chainId: number - name: string - }, - rpcUrl: string -): Chain { - return { - id: networkData.chainId, - name: networkData.name, - nativeCurrency: { - decimals: 18, - name: 'Ether', - symbol: 'ETH', - }, - rpcUrls: { - default: { http: [rpcUrl] }, - public: { http: [rpcUrl] }, - }, - } as const -} From ad08638be342009e08f4892e0d79e992eed9d80e Mon Sep 17 00:00:00 2001 From: Doug Lance <4741454+douglance@users.noreply.github.com> Date: Thu, 9 Jan 2025 08:57:57 -0500 Subject: [PATCH 09/10] restore network register --- packages/sdk-viem/tests/deposit.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/sdk-viem/tests/deposit.test.ts b/packages/sdk-viem/tests/deposit.test.ts index 9a3d1309ed..90bc1ab37e 100644 --- a/packages/sdk-viem/tests/deposit.test.ts +++ b/packages/sdk-viem/tests/deposit.test.ts @@ -1,3 +1,4 @@ +import { registerCustomArbitrumNetwork } from '@arbitrum/sdk' import { approveParentCustomFeeToken, fundParentCustomFeeToken, @@ -20,6 +21,7 @@ describe('deposit', function () { before(async function () { setup = await testSetup() + registerCustomArbitrumNetwork(setup.childChain) }) beforeEach(async function () { @@ -33,7 +35,7 @@ describe('deposit', function () { it('deposits ETH from parent to child using deposit action', async function () { const [depositAmount, tokenDecimals] = await getAmountInEnvironmentDecimals( '0.01' - ) + ) const { childPublicClient, parentWalletClient } = setup From 3b25110be60892795beaf2b450cd553bfcf6b3f2 Mon Sep 17 00:00:00 2001 From: Doug Lance <4741454+douglance@users.noreply.github.com> Date: Thu, 9 Jan 2025 09:00:27 -0500 Subject: [PATCH 10/10] lint fix --- packages/sdk-viem/src/createArbitrumClient.ts | 2 +- packages/sdk-viem/tests/deposit.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/sdk-viem/src/createArbitrumClient.ts b/packages/sdk-viem/src/createArbitrumClient.ts index e8bd834eec..a4551a7ca5 100644 --- a/packages/sdk-viem/src/createArbitrumClient.ts +++ b/packages/sdk-viem/src/createArbitrumClient.ts @@ -7,7 +7,7 @@ import { } from 'viem' import { ArbitrumParentWalletActions, - arbitrumParentWalletActions + arbitrumParentWalletActions, } from './actions' export type ArbitrumClients = { diff --git a/packages/sdk-viem/tests/deposit.test.ts b/packages/sdk-viem/tests/deposit.test.ts index 90bc1ab37e..736d0aed09 100644 --- a/packages/sdk-viem/tests/deposit.test.ts +++ b/packages/sdk-viem/tests/deposit.test.ts @@ -35,7 +35,7 @@ describe('deposit', function () { it('deposits ETH from parent to child using deposit action', async function () { const [depositAmount, tokenDecimals] = await getAmountInEnvironmentDecimals( '0.01' - ) + ) const { childPublicClient, parentWalletClient } = setup