From 9eed4b16503ce19d36eb00b969798ad39041ce9b Mon Sep 17 00:00:00 2001 From: Doug Lance <4741454+douglance@users.noreply.github.com> Date: Mon, 30 Dec 2024 13:11:54 -0500 Subject: [PATCH 1/7] feat: adds sdk-viem withdraws --- packages/sdk-viem/src/actions.ts | 192 +++++++++++++++--- packages/sdk-viem/src/createArbitrumClient.ts | 10 +- packages/sdk-viem/tests/deposit.test.ts | 2 +- packages/sdk-viem/tests/helpers.ts | 65 ++++++ packages/sdk-viem/tests/withdraw.test.ts | 151 ++++++++++++++ 5 files changed, 388 insertions(+), 32 deletions(-) create mode 100644 packages/sdk-viem/tests/helpers.ts create mode 100644 packages/sdk-viem/tests/withdraw.test.ts diff --git a/packages/sdk-viem/src/actions.ts b/packages/sdk-viem/src/actions.ts index 70541a4fe..1c5753c91 100644 --- a/packages/sdk-viem/src/actions.ts +++ b/packages/sdk-viem/src/actions.ts @@ -1,4 +1,6 @@ import { + ChildToParentMessageStatus, + ChildTransactionReceipt, EthBridger, ParentToChildMessageStatus, ParentTransactionReceipt, @@ -17,14 +19,10 @@ import { WalletClient, } from 'viem' -export type PrepareDepositEthParameters = { - amount: bigint - account: Account | Address -} - -const DEFAULT_CONFIRMATIONS = 1 -const DEFAULT_TIMEOUT = 1000 * 60 * 5 // 5 minutes +export const DEFAULT_CONFIRMATIONS = 1 +export const DEFAULT_TIMEOUT = 1000 * 60 * 5 // 5 minutes +// Cross-chain transaction types export type WaitForCrossChainTxParameters = { hash: Hash timeout?: number @@ -45,9 +43,13 @@ export type CrossChainTransactionStatus = { hash: Hash } -export type DepositEthParameters = { +// Deposit types +export type PrepareDepositEthParameters = { amount: bigint account: Account | Address +} + +export type DepositEthParameters = PrepareDepositEthParameters & { confirmations?: number timeout?: number } @@ -58,6 +60,32 @@ export type ArbitrumDepositActions = { ) => Promise } +// Withdraw types +export type PrepareWithdrawEthParameters = { + amount: bigint + destinationAddress: Address + account: Account | Address +} + +export type WithdrawEthParameters = PrepareWithdrawEthParameters & { + confirmations?: number + timeout?: number +} + +export type ArbitrumChildWalletActions = { + prepareWithdrawEthTransaction: ( + params: PrepareWithdrawEthParameters + ) => Promise<{ + request: TransactionRequest + l1GasEstimate: bigint + }> + + withdrawEth: ( + params: WithdrawEthParameters + ) => Promise +} + +// Parent wallet types export type ArbitrumParentWalletActions = { waitForCrossChainTransaction: ( params: WaitForCrossChainTxParameters @@ -72,25 +100,8 @@ export type ArbitrumParentWalletActions = { ) => 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( +// Cross-chain transaction functions +export async function waitForCrossChainTransaction( parentClient: PublicClient, childClient: PublicClient, { @@ -150,7 +161,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, @@ -164,6 +175,7 @@ async function sendCrossChainTransaction( ...request, chain: walletClient.chain, account: walletClient.account as Account, + kzg: undefined, }) return waitForCrossChainTransaction(parentClient, childClient, { @@ -173,7 +185,26 @@ async function sendCrossChainTransaction( }) } -async function depositEth( +// Deposit functions +export 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}`, + } +} + +export async function depositEth( parentClient: PublicClient, childClient: PublicClient, walletClient: WalletClient, @@ -196,6 +227,97 @@ async function depositEth( }) } +// Withdraw functions +export async function prepareWithdrawEthTransaction( + client: PublicClient, + { amount, destinationAddress, account }: PrepareWithdrawEthParameters +): Promise<{ + request: TransactionRequest + l1GasEstimate: bigint +}> { + const provider = publicClientToProvider(client) + const ethBridger = await EthBridger.fromProvider(provider) + const request = await ethBridger.getWithdrawalRequest({ + amount: BigNumber.from(amount), + destinationAddress, + from: typeof account === 'string' ? account : account.address, + }) + + const l1GasEstimate = await request.estimateParentGasLimit(provider) + + return { + request: { + to: request.txRequest.to as `0x${string}`, + value: BigNumber.from(request.txRequest.value).toBigInt(), + data: request.txRequest.data as `0x${string}`, + }, + l1GasEstimate: l1GasEstimate.toBigInt(), + } +} + +export async function withdrawEth( + parentClient: PublicClient, + childClient: PublicClient, + walletClient: WalletClient, + { + amount, + destinationAddress, + account, + confirmations = DEFAULT_CONFIRMATIONS, + }: WithdrawEthParameters +): Promise { + const { request } = await prepareWithdrawEthTransaction(childClient, { + amount, + destinationAddress, + account, + }) + + const hash = await walletClient.sendTransaction({ + ...request, + chain: walletClient.chain, + account: walletClient.account as Account, + kzg: undefined, + }) + + const childProvider = publicClientToProvider(childClient) + const parentProvider = publicClientToProvider(parentClient) + + const viemReceipt = await childClient.waitForTransactionReceipt({ + hash, + confirmations, + }) + + const ethersReceipt = + viemTransactionReceiptToEthersTransactionReceipt(viemReceipt) + + const childReceipt = new ChildTransactionReceipt(ethersReceipt) + + const messages = await childReceipt.getChildToParentMessages(parentProvider) + if (messages.length === 0) { + return { + status: 'failed', + complete: false, + hash, + message: undefined, + childTxReceipt: undefined, + } + } + + const message = messages[0] + const messageStatus = await message.status(childProvider) + + // For withdrawals, return early since it needs to wait for challenge period + const isUnconfirmed = messageStatus === ChildToParentMessageStatus.UNCONFIRMED + return { + status: isUnconfirmed ? 'success' : 'failed', + complete: false, // Not complete until executed after challenge period + message, + childTxReceipt: ethersReceipt, + hash, + } +} + +// Client action creators export function arbitrumParentClientActions() { return (client: PublicClient): ArbitrumDepositActions => ({ prepareDepositEthTransaction: params => @@ -221,3 +343,15 @@ export function arbitrumParentWalletActions( depositEth(parentClient, childClient, walletClient, params), }) } + +export function arbitrumChildWalletActions( + parentClient: PublicClient, + childClient: PublicClient +) { + return (walletClient: WalletClient): ArbitrumChildWalletActions => ({ + prepareWithdrawEthTransaction: (params: PrepareWithdrawEthParameters) => + prepareWithdrawEthTransaction(childClient, params), + withdrawEth: (params: WithdrawEthParameters) => + withdrawEth(parentClient, childClient, walletClient, params), + }) +} diff --git a/packages/sdk-viem/src/createArbitrumClient.ts b/packages/sdk-viem/src/createArbitrumClient.ts index eb30876ed..24c8034ae 100644 --- a/packages/sdk-viem/src/createArbitrumClient.ts +++ b/packages/sdk-viem/src/createArbitrumClient.ts @@ -6,8 +6,10 @@ import { http, } from 'viem' import { + ArbitrumChildWalletActions, ArbitrumDepositActions, ArbitrumParentWalletActions, + arbitrumChildWalletActions, arbitrumParentClientActions, arbitrumParentWalletActions, } from './actions' @@ -16,7 +18,7 @@ export type ArbitrumClients = { parentPublicClient: PublicClient childPublicClient: PublicClient & ArbitrumDepositActions parentWalletClient: WalletClient & ArbitrumParentWalletActions - childWalletClient?: WalletClient + childWalletClient?: WalletClient & ArbitrumChildWalletActions } export type CreateArbitrumClientParams = { @@ -50,10 +52,14 @@ export function createArbitrumClient({ arbitrumParentWalletActions(parentPublicClient, childPublicClient) ) + const extendedChildWalletClient = childWalletClient?.extend( + arbitrumChildWalletActions(parentPublicClient, childPublicClient) + ) + return { parentPublicClient, childPublicClient, parentWalletClient: extendedParentWalletClient, - childWalletClient, + childWalletClient: extendedChildWalletClient, } } diff --git a/packages/sdk-viem/tests/deposit.test.ts b/packages/sdk-viem/tests/deposit.test.ts index b790a4217..87d9e3926 100644 --- a/packages/sdk-viem/tests/deposit.test.ts +++ b/packages/sdk-viem/tests/deposit.test.ts @@ -14,7 +14,7 @@ import { createWalletClient, http, parseEther, type Chain } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { createArbitrumClient } from '../src/createArbitrumClient' -describe('deposit', function () { +describe.skip('deposit', function () { this.timeout(300000) let localEthChain: Chain diff --git a/packages/sdk-viem/tests/helpers.ts b/packages/sdk-viem/tests/helpers.ts new file mode 100644 index 000000000..65e8a5275 --- /dev/null +++ b/packages/sdk-viem/tests/helpers.ts @@ -0,0 +1,65 @@ +import { + ChildToParentMessage, + ChildToParentMessageStatus, + ChildTransactionReceipt, +} from '@arbitrum/sdk' +import { config } from '@arbitrum/sdk/tests/testSetup' +import { + publicClientToProvider, + viemTransactionReceiptToEthersTransactionReceipt, +} from '@offchainlabs/ethers-viem-compat' +import { Wallet } from 'ethers' +import { Hash, PublicClient, TransactionReceipt } from 'viem' + +/** + * Test utility to execute a withdrawal after it's been confirmed. + */ +export async function executeConfirmedWithdrawal( + viemReceipt: TransactionReceipt, + childClient: PublicClient, + parentClient: PublicClient, + confirmations = 1 +): Promise<{ status: boolean; hash: Hash }> { + const childProvider = publicClientToProvider(childClient) + const parentProvider = publicClientToProvider(parentClient) + + const ethersReceipt = + viemTransactionReceiptToEthersTransactionReceipt(viemReceipt) + + const childReceipt = new ChildTransactionReceipt(ethersReceipt) + + const messages = await childReceipt.getChildToParentMessages(parentProvider) + if (messages.length === 0) { + throw new Error('No messages found in receipt') + } + + const message = messages[0] + + // Wait for message to be ready to execute + await message.waitUntilReadyToExecute(childProvider) + + // Check if message has been confirmed + const status = await message.status(childProvider) + if (status !== ChildToParentMessageStatus.CONFIRMED) { + throw new Error('Message not confirmed after waiting') + } + + // Create a writer to execute the message + const parentSigner = new Wallet(`0x${config.ethKey}`, parentProvider) + const events = childReceipt.getChildToParentEvents() + const messageWriter = ChildToParentMessage.fromEvent(parentSigner, events[0]) + + // Execute the message + const execTx = await messageWriter.execute(childProvider) + const execHash = execTx.hash + + const execReceipt = await parentClient.waitForTransactionReceipt({ + hash: execHash as `0x${string}`, + confirmations, + }) + + return { + status: Boolean(execReceipt.status), + hash: execHash as Hash, + } +} diff --git a/packages/sdk-viem/tests/withdraw.test.ts b/packages/sdk-viem/tests/withdraw.test.ts new file mode 100644 index 000000000..d1baae1fe --- /dev/null +++ b/packages/sdk-viem/tests/withdraw.test.ts @@ -0,0 +1,151 @@ +import { registerCustomArbitrumNetwork } from '@arbitrum/sdk' +import { + approveCustomFeeTokenWithViem, + approveParentCustomFeeToken, + fundParentCustomFeeToken, + getAmountInEnvironmentDecimals, + isArbitrumNetworkWithCustomFeeToken, +} 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 { executeConfirmedWithdrawal } from './helpers' + +describe('withdraw', 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('withdraws ETH from child to parent using withdraw action', async function () { + const parentAccount = privateKeyToAccount(`0x${config.ethKey}`) + const [withdrawAmount] = 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 { parentPublicClient, childPublicClient, childWalletClient } = + createArbitrumClient({ + parentChain: localEthChain, + childChain: localArbChain, + parentWalletClient: baseParentWalletClient, + childWalletClient: baseChildWalletClient, + }) + + const initialParentBalance = await parentPublicClient.getBalance({ + address: parentAccount.address, + }) + + const initialChildBalance = await childPublicClient.getBalance({ + address: parentAccount.address, + }) + + if (isArbitrumNetworkWithCustomFeeToken()) { + await approveCustomFeeTokenWithViem({ + parentAccount, + parentWalletClient: baseParentWalletClient, + chain: localEthChain, + }) + } + + // Start withdrawal + const result = await childWalletClient!.withdrawEth({ + amount: withdrawAmount, + destinationAddress: parentAccount.address, + account: parentAccount, + }) + + expect(result.status).to.equal('success') + expect(result.complete).to.equal(false) + + const receipt = await childPublicClient.waitForTransactionReceipt({ + hash: result.hash, + }) + + const { status } = await executeConfirmedWithdrawal( + receipt, + childPublicClient, + parentPublicClient + ) + + expect(status).to.be.true + + const finalParentBalance = await parentPublicClient.getBalance({ + address: parentAccount.address, + }) + + const finalChildBalance = await childPublicClient.getBalance({ + address: parentAccount.address, + }) + + // Check that balance decreased on child chain + expect(finalChildBalance < initialChildBalance).to.be.true + + // Check that balance increased on parent chain + expect(finalParentBalance > initialParentBalance).to.be.true + }) + + it('handles withdrawal failure gracefully', async function () { + const parentAccount = privateKeyToAccount(`0x${config.ethKey}`) + const withdrawAmount = 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 { childWalletClient } = createArbitrumClient({ + parentChain: localEthChain, + childChain: localArbChain, + parentWalletClient: baseParentWalletClient, + childWalletClient: baseChildWalletClient, + }) + + try { + await childWalletClient!.withdrawEth({ + amount: withdrawAmount, + destinationAddress: parentAccount.address, + account: parentAccount, + }) + expect.fail('Should have thrown an error') + } catch (error) { + expect(error).to.exist + } + }) +}) From a0a9dc4afcab181f2847452a9164022e755dd6de Mon Sep 17 00:00:00 2001 From: Doug Lance <4741454+douglance@users.noreply.github.com> Date: Tue, 31 Dec 2024 15:26:30 -0500 Subject: [PATCH 2/7] update tests --- packages/sdk-viem/tests/deposit.test.ts | 2 +- packages/sdk-viem/tests/withdraw.test.ts | 26 +++++++++++++++++------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/packages/sdk-viem/tests/deposit.test.ts b/packages/sdk-viem/tests/deposit.test.ts index 87d9e3926..b790a4217 100644 --- a/packages/sdk-viem/tests/deposit.test.ts +++ b/packages/sdk-viem/tests/deposit.test.ts @@ -14,7 +14,7 @@ import { createWalletClient, http, parseEther, type Chain } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { createArbitrumClient } from '../src/createArbitrumClient' -describe.skip('deposit', function () { +describe('deposit', function () { this.timeout(300000) let localEthChain: Chain diff --git a/packages/sdk-viem/tests/withdraw.test.ts b/packages/sdk-viem/tests/withdraw.test.ts index d1baae1fe..99a37eb75 100644 --- a/packages/sdk-viem/tests/withdraw.test.ts +++ b/packages/sdk-viem/tests/withdraw.test.ts @@ -5,6 +5,7 @@ import { 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' @@ -39,7 +40,8 @@ describe('withdraw', function () { it('withdraws ETH from child to parent using withdraw action', async function () { const parentAccount = privateKeyToAccount(`0x${config.ethKey}`) - const [withdrawAmount] = await getAmountInEnvironmentDecimals('0.01') + const [withdrawAmount, tokenDecimals] = + await getAmountInEnvironmentDecimals('0.01') const baseParentWalletClient = createWalletClient({ account: parentAccount, @@ -62,11 +64,11 @@ describe('withdraw', function () { }) const initialParentBalance = await parentPublicClient.getBalance({ - address: parentAccount.address, + address: parentAccount.address as `0x${string}`, }) const initialChildBalance = await childPublicClient.getBalance({ - address: parentAccount.address, + address: parentAccount.address as `0x${string}`, }) if (isArbitrumNetworkWithCustomFeeToken()) { @@ -100,18 +102,28 @@ describe('withdraw', function () { expect(status).to.be.true const finalParentBalance = await parentPublicClient.getBalance({ - address: parentAccount.address, + address: parentAccount.address as `0x${string}`, }) const finalChildBalance = await childPublicClient.getBalance({ - address: parentAccount.address, + address: parentAccount.address as `0x${string}`, }) // Check that balance decreased on child chain - expect(finalChildBalance < initialChildBalance).to.be.true + const childBalanceDiff = finalChildBalance - initialChildBalance + const normalizedChildBalanceDiff = normalizeBalanceDiffByDecimals( + BigInt(childBalanceDiff), + tokenDecimals + ) + expect(normalizedChildBalanceDiff < BigInt(0)).to.be.true // Check that balance increased on parent chain - expect(finalParentBalance > initialParentBalance).to.be.true + const parentBalanceDiff = finalParentBalance - initialParentBalance + const normalizedParentBalanceDiff = normalizeBalanceDiffByDecimals( + BigInt(parentBalanceDiff), + tokenDecimals + ) + expect(normalizedParentBalanceDiff >= withdrawAmount).to.be.true }) it('handles withdrawal failure gracefully', async function () { From 4bdf69adcd5cd58e8af6ebcdb7b813edb7faf4ef Mon Sep 17 00:00:00 2001 From: Doug Lance <4741454+douglance@users.noreply.github.com> Date: Tue, 31 Dec 2024 16:24:27 -0500 Subject: [PATCH 3/7] fix test --- packages/sdk-viem/tests/withdraw.test.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/sdk-viem/tests/withdraw.test.ts b/packages/sdk-viem/tests/withdraw.test.ts index 99a37eb75..aa3809755 100644 --- a/packages/sdk-viem/tests/withdraw.test.ts +++ b/packages/sdk-viem/tests/withdraw.test.ts @@ -117,13 +117,18 @@ describe('withdraw', function () { ) expect(normalizedChildBalanceDiff < BigInt(0)).to.be.true - // Check that balance increased on parent chain const parentBalanceDiff = finalParentBalance - initialParentBalance const normalizedParentBalanceDiff = normalizeBalanceDiffByDecimals( BigInt(parentBalanceDiff), tokenDecimals ) - expect(normalizedParentBalanceDiff >= withdrawAmount).to.be.true + + if (isArbitrumNetworkWithCustomFeeToken()) { + const maxExpectedDecrease = -withdrawAmount * BigInt(2) + expect(normalizedParentBalanceDiff >= maxExpectedDecrease).to.be.true + } else { + expect(normalizedParentBalanceDiff >= withdrawAmount).to.be.true + } }) it('handles withdrawal failure gracefully', async function () { From 781e1f4ebf7180d221b4ce89491ddfdff5611ac1 Mon Sep 17 00:00:00 2001 From: Doug Lance <4741454+douglance@users.noreply.github.com> Date: Mon, 13 Jan 2025 16:33:45 -0500 Subject: [PATCH 4/7] fix types --- packages/sdk-viem/src/createArbitrumClient.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/sdk-viem/src/createArbitrumClient.ts b/packages/sdk-viem/src/createArbitrumClient.ts index 469bc3562..437e475c0 100644 --- a/packages/sdk-viem/src/createArbitrumClient.ts +++ b/packages/sdk-viem/src/createArbitrumClient.ts @@ -6,15 +6,17 @@ import { http, } from 'viem' import { + ArbitrumChildWalletActions, arbitrumChildWalletActions, + ArbitrumParentWalletActions, arbitrumParentWalletActions, } from './actions' export type ArbitrumClients = { parentPublicClient: PublicClient childPublicClient: PublicClient - parentWalletClient: WalletClient & typeof arbitrumParentWalletActions - childWalletClient?: WalletClient & typeof arbitrumChildWalletActions + parentWalletClient: WalletClient & ArbitrumParentWalletActions + childWalletClient?: WalletClient & ArbitrumChildWalletActions } export type CreateArbitrumClientParams = { From e04cab65226080192c5a6ec53c1523eefc3690b1 Mon Sep 17 00:00:00 2001 From: Doug Lance <4741454+douglance@users.noreply.github.com> Date: Mon, 13 Jan 2025 16:42:26 -0500 Subject: [PATCH 5/7] migrate to simpler system --- packages/sdk-viem/tests/testSetup.ts | 13 ++++- packages/sdk-viem/tests/withdraw.test.ts | 69 ++++++------------------ 2 files changed, 26 insertions(+), 56 deletions(-) diff --git a/packages/sdk-viem/tests/testSetup.ts b/packages/sdk-viem/tests/testSetup.ts index ed5811547..c84e515f3 100644 --- a/packages/sdk-viem/tests/testSetup.ts +++ b/packages/sdk-viem/tests/testSetup.ts @@ -13,8 +13,10 @@ export type ViemTestSetup = { localEthChain: Chain localArbChain: Chain parentAccount: ReturnType - childPublicClient: ArbitrumClients['childPublicClient'] + parentPublicClient: ArbitrumClients['parentPublicClient'] parentWalletClient: ArbitrumClients['parentWalletClient'] + childPublicClient: ArbitrumClients['childPublicClient'] + childWalletClient: ArbitrumClients['childWalletClient'] childChain: Awaited>['childChain'] parentSigner: Awaited>['parentSigner'] } @@ -76,7 +78,12 @@ export async function testSetup(): Promise { transport: http(config.arbUrl), }) - const { childPublicClient, parentWalletClient } = createArbitrumClient({ + const { + childPublicClient, + childWalletClient, + parentWalletClient, + parentPublicClient, + } = createArbitrumClient({ parentChain: localEthChain, childChain: localArbChain, parentWalletClient: baseParentWalletClient, @@ -89,7 +96,9 @@ export async function testSetup(): Promise { localArbChain, parentAccount, childPublicClient, + childWalletClient, parentWalletClient, + parentPublicClient, } } diff --git a/packages/sdk-viem/tests/withdraw.test.ts b/packages/sdk-viem/tests/withdraw.test.ts index aa3809755..4ae020324 100644 --- a/packages/sdk-viem/tests/withdraw.test.ts +++ b/packages/sdk-viem/tests/withdraw.test.ts @@ -1,4 +1,3 @@ -import { registerCustomArbitrumNetwork } from '@arbitrum/sdk' import { approveCustomFeeTokenWithViem, approveParentCustomFeeToken, @@ -8,61 +7,41 @@ import { 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 { executeConfirmedWithdrawal } from './helpers' +import { testSetup } from './testSetup' describe('withdraw', 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('withdraws ETH from child to parent using withdraw action', async function () { - const parentAccount = privateKeyToAccount(`0x${config.ethKey}`) + const { + parentAccount, + childPublicClient, + childWalletClient, + parentWalletClient, + parentPublicClient, + localEthChain, + } = setup + const [withdrawAmount, 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 { parentPublicClient, childPublicClient, childWalletClient } = - createArbitrumClient({ - parentChain: localEthChain, - childChain: localArbChain, - parentWalletClient: baseParentWalletClient, - childWalletClient: baseChildWalletClient, - }) - const initialParentBalance = await parentPublicClient.getBalance({ address: parentAccount.address as `0x${string}`, }) @@ -74,7 +53,7 @@ describe('withdraw', function () { if (isArbitrumNetworkWithCustomFeeToken()) { await approveCustomFeeTokenWithViem({ parentAccount, - parentWalletClient: baseParentWalletClient, + parentWalletClient, chain: localEthChain, }) } @@ -132,27 +111,9 @@ describe('withdraw', function () { }) it('handles withdrawal failure gracefully', async function () { - const parentAccount = privateKeyToAccount(`0x${config.ethKey}`) - const withdrawAmount = parseEther('999999999') + const { parentAccount, childWalletClient } = setup - const baseParentWalletClient = createWalletClient({ - account: parentAccount, - chain: localEthChain, - transport: http(config.ethUrl), - }) - - const baseChildWalletClient = createWalletClient({ - account: parentAccount, - chain: localArbChain, - transport: http(config.arbUrl), - }) - - const { childWalletClient } = createArbitrumClient({ - parentChain: localEthChain, - childChain: localArbChain, - parentWalletClient: baseParentWalletClient, - childWalletClient: baseChildWalletClient, - }) + const withdrawAmount = parseEther('999999999') try { await childWalletClient!.withdrawEth({ From c15ce3f57662fa6b9afd84d3c64be83edeca4e78 Mon Sep 17 00:00:00 2001 From: Doug Lance <4741454+douglance@users.noreply.github.com> Date: Mon, 13 Jan 2025 16:50:42 -0500 Subject: [PATCH 6/7] fix lint --- packages/sdk-viem/src/actions.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/sdk-viem/src/actions.ts b/packages/sdk-viem/src/actions.ts index 1e258f87c..a59e35a60 100644 --- a/packages/sdk-viem/src/actions.ts +++ b/packages/sdk-viem/src/actions.ts @@ -100,7 +100,6 @@ export type ArbitrumParentWalletActions = { ) => Promise } - export async function waitForCrossChainTransaction( parentClient: PublicClient, childClient: PublicClient, From fa623c28f7438d0eeff910aa46333bd9e23212cd Mon Sep 17 00:00:00 2001 From: Doug Lance <4741454+douglance@users.noreply.github.com> Date: Mon, 13 Jan 2025 16:58:02 -0500 Subject: [PATCH 7/7] fix test --- packages/sdk-viem/tests/testSetup.ts | 2 +- packages/sdk-viem/tests/withdraw.test.ts | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/sdk-viem/tests/testSetup.ts b/packages/sdk-viem/tests/testSetup.ts index c84e515f3..5f7a64d23 100644 --- a/packages/sdk-viem/tests/testSetup.ts +++ b/packages/sdk-viem/tests/testSetup.ts @@ -19,7 +19,7 @@ export type ViemTestSetup = { childWalletClient: ArbitrumClients['childWalletClient'] childChain: Awaited>['childChain'] parentSigner: Awaited>['parentSigner'] -} +} & Awaited> function generateViemChain( networkData: { diff --git a/packages/sdk-viem/tests/withdraw.test.ts b/packages/sdk-viem/tests/withdraw.test.ts index 4ae020324..b476eef63 100644 --- a/packages/sdk-viem/tests/withdraw.test.ts +++ b/packages/sdk-viem/tests/withdraw.test.ts @@ -6,7 +6,10 @@ import { isArbitrumNetworkWithCustomFeeToken, normalizeBalanceDiffByDecimals, } from '@arbitrum/sdk/tests/integration/custom-fee-token/customFeeTokenTestHelpers' -import { fundParentSigner } from '@arbitrum/sdk/tests/integration/testHelpers' +import { + fundChildSigner, + fundParentSigner, +} from '@arbitrum/sdk/tests/integration/testHelpers' import { expect } from 'chai' import { parseEther } from 'viem' import { executeConfirmedWithdrawal } from './helpers' @@ -23,6 +26,8 @@ describe('withdraw', function () { beforeEach(async function () { await fundParentSigner(setup.parentSigner) + await fundChildSigner(setup.childSigner) + if (isArbitrumNetworkWithCustomFeeToken()) { await fundParentCustomFeeToken(setup.parentAccount.address) await approveParentCustomFeeToken(setup.parentSigner)