diff --git a/package.json b/package.json index ef6e5ba67..d5dbd47a6 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 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" + "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 c6a112cac..2981c6c73 100644 --- a/packages/ethers-viem-compat/package.json +++ b/packages/ethers-viem-compat/package.json @@ -25,8 +25,10 @@ "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" + "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/.eslintignore b/packages/sdk-viem/.eslintignore new file mode 100644 index 000000000..99ead7177 --- /dev/null +++ b/packages/sdk-viem/.eslintignore @@ -0,0 +1,5 @@ +dist/** +node_modules/** +coverage/** +src/lib/abi +docs/** diff --git a/packages/sdk-viem/.eslintrc b/packages/sdk-viem/.eslintrc new file mode 100644 index 000000000..bde91889b --- /dev/null +++ b/packages/sdk-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/sdk-viem/.prettierignore b/packages/sdk-viem/.prettierignore new file mode 100644 index 000000000..ee20e44d9 --- /dev/null +++ b/packages/sdk-viem/.prettierignore @@ -0,0 +1,5 @@ +build/** +cache/** +dist/** +src/lib/abi/** +.nyc_output diff --git a/packages/sdk-viem/.prettierrc.js b/packages/sdk-viem/.prettierrc.js new file mode 100644 index 000000000..008736fae --- /dev/null +++ b/packages/sdk-viem/.prettierrc.js @@ -0,0 +1,5 @@ +const baseConfig = require('../../.prettierrc.js') + +module.exports = { + ...baseConfig, +} diff --git a/packages/sdk-viem/package.json b/packages/sdk-viem/package.json new file mode 100644 index 000000000..c24ad8dd9 --- /dev/null +++ b/packages/sdk-viem/package.json @@ -0,0 +1,42 @@ +{ + "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: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", + "clean": "rm -rf node_modules && rm -rf dist" + }, + "dependencies": { + "@arbitrum/sdk": "workspace:*", + "@offchainlabs/ethers-viem-compat": "workspace:*" + }, + "peerDependencies": { + "ethers": "^5.0.0", + "viem": "^2.0.0" + } +} diff --git a/packages/sdk-viem/src/actions.ts b/packages/sdk-viem/src/actions.ts new file mode 100644 index 000000000..1a0503ac8 --- /dev/null +++ b/packages/sdk-viem/src/actions.ts @@ -0,0 +1,223 @@ +import { + EthBridger, + ParentToChildMessageStatus, + ParentTransactionReceipt, +} from '@arbitrum/sdk' +import { + publicClientToProvider, + viemTransactionReceiptToEthersTransactionReceipt, +} from '@offchainlabs/ethers-viem-compat' +import { BigNumber } from 'ethers' +import { + Account, + Address, + Hash, + PublicClient, + TransactionRequest, + WalletClient, +} from 'viem' + +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 +} + +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 Address, + value: BigNumber.from(request.txRequest.value).toBigInt(), + data: request.txRequest.data as Address, + } +} + +export 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') +} + +export 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, + }) +} + +export 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/sdk-viem/src/createArbitrumClient.ts b/packages/sdk-viem/src/createArbitrumClient.ts new file mode 100644 index 000000000..a4551a7ca --- /dev/null +++ b/packages/sdk-viem/src/createArbitrumClient.ts @@ -0,0 +1,57 @@ +import { + Chain, + PublicClient, + WalletClient, + createPublicClient, + http, +} from 'viem' +import { + ArbitrumParentWalletActions, + arbitrumParentWalletActions, +} from './actions' + +export type ArbitrumClients = { + parentPublicClient: PublicClient + childPublicClient: PublicClient + 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]), + }) + + const extendedParentWalletClient = parentWalletClient.extend( + arbitrumParentWalletActions(parentPublicClient, childPublicClient) + ) + + return { + parentPublicClient, + childPublicClient, + parentWalletClient: extendedParentWalletClient, + childWalletClient, + } +} diff --git a/packages/sdk-viem/src/index.ts b/packages/sdk-viem/src/index.ts new file mode 100644 index 000000000..297983871 --- /dev/null +++ b/packages/sdk-viem/src/index.ts @@ -0,0 +1,2 @@ +export * from './actions' +export * from './createArbitrumClient' diff --git a/packages/sdk-viem/tests/customFeeTokenTestHelpers.ts b/packages/sdk-viem/tests/customFeeTokenTestHelpers.ts new file mode 100644 index 000000000..af1a8773f --- /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 new file mode 100644 index 000000000..736d0aed0 --- /dev/null +++ b/packages/sdk-viem/tests/deposit.test.ts @@ -0,0 +1,89 @@ +import { registerCustomArbitrumNetwork } from '@arbitrum/sdk' +import { + approveParentCustomFeeToken, + fundParentCustomFeeToken, + isArbitrumNetworkWithCustomFeeToken, +} from '@arbitrum/sdk/tests/integration/custom-fee-token/customFeeTokenTestHelpers' +import { fundParentSigner } from '@arbitrum/sdk/tests/integration/testHelpers' +import { expect } from 'chai' +import { parseEther } from 'viem' +import { + approveCustomFeeTokenWithViem, + getAmountInEnvironmentDecimals, + normalizeBalanceDiffByDecimals, +} from './customFeeTokenTestHelpers' +import { testSetup } from './testSetup' + +describe('deposit', function () { + this.timeout(300000) + + let setup: Awaited> + + before(async function () { + setup = await testSetup() + registerCustomArbitrumNetwork(setup.childChain) + }) + + beforeEach(async function () { + await fundParentSigner(setup.parentSigner) + if (isArbitrumNetworkWithCustomFeeToken()) { + await fundParentCustomFeeToken(setup.parentAccount.address) + await approveParentCustomFeeToken(setup.parentSigner) + } + }) + + it('deposits ETH from parent to child using deposit action', async function () { + const [depositAmount, tokenDecimals] = await getAmountInEnvironmentDecimals( + '0.01' + ) + + const { childPublicClient, parentWalletClient } = setup + + const initialBalance = await childPublicClient.getBalance({ + address: setup.parentAccount.address, + }) + + if (isArbitrumNetworkWithCustomFeeToken()) { + await approveCustomFeeTokenWithViem({ + parentAccount: setup.parentAccount, + parentWalletClient, + chain: setup.localEthChain, + }) + } + + const result = await parentWalletClient.depositEth({ + amount: depositAmount, + account: setup.parentAccount, + }) + + expect(result.status).to.equal('success') + + const finalBalance = await childPublicClient.getBalance({ + address: setup.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 depositAmount = parseEther('999999999') + + const { parentWalletClient } = setup + + try { + await parentWalletClient.depositEth({ + amount: depositAmount, + account: setup.parentAccount, + }) + expect.fail('Should have thrown an error') + } catch (error) { + expect(error).to.exist + } + }) +}) diff --git a/packages/sdk-viem/tests/testSetup.ts b/packages/sdk-viem/tests/testSetup.ts new file mode 100644 index 000000000..ed5811547 --- /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-viem/tsconfig.json b/packages/sdk-viem/tsconfig.json new file mode 100644 index 000000000..70de820d0 --- /dev/null +++ b/packages/sdk-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"] +} diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 0e4780757..7e9f374a1 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/packages/sdk/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts b/packages/sdk/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts index b84a8289b..0e4b097b4 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 597b0b511..677b08063 100644 --- a/packages/sdk/tests/testSetup.ts +++ b/packages/sdk/tests/testSetup.ts @@ -85,6 +85,7 @@ export const testSetup = async (): Promise<{ inboxTools: InboxTools parentDeployer: Signer childDeployer: Signer + seed: Wallet }> => { const ethProvider = new JsonRpcProvider(config.ethUrl) const arbProvider = new JsonRpcProvider(config.arbUrl) @@ -136,6 +137,7 @@ export const testSetup = async (): Promise<{ inboxTools, parentDeployer, childDeployer, + seed, } } diff --git a/yarn.lock b/yarn.lock index 8258d4c9e..28c65d941 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"