Skip to content

Commit

Permalink
adds depositEth and sendCrossChainTx
Browse files Browse the repository at this point in the history
  • Loading branch information
douglance committed Nov 22, 2024
1 parent d9f2e92 commit bdd936b
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 21 deletions.
91 changes: 88 additions & 3 deletions src/experimental/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,34 @@ export type PrepareDepositEthParameters = {
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 = {
Expand All @@ -43,6 +60,14 @@ export type ArbitrumParentWalletActions = {
waitForCrossChainTransaction: (
params: WaitForCrossChainTxParameters
) => Promise<CrossChainTransactionStatus>

sendCrossChainTransaction: (
params: SendCrossChainTransactionParameters
) => Promise<CrossChainTransactionStatus>

depositEth: (
params: DepositEthParameters
) => Promise<CrossChainTransactionStatus>
}

async function prepareDepositEthTransaction(
Expand All @@ -66,11 +91,14 @@ async function prepareDepositEthTransaction(
async function waitForCrossChainTransaction(
parentClient: PublicClient,
childClient: PublicClient,
{ hash, timeout, confirmations }: WaitForCrossChainTxParameters
{
hash,
confirmations = DEFAULT_CONFIRMATIONS,
timeout = DEFAULT_TIMEOUT,
}: WaitForCrossChainTxParameters
): Promise<CrossChainTransactionStatus> {
const childProvider = transformPublicClientToProvider(childClient)

// Wait for the transaction to be mined and get the receipt
const viemReceipt = await parentClient.waitForTransactionReceipt({
hash,
confirmations,
Expand All @@ -90,6 +118,7 @@ async function waitForCrossChainTransaction(
complete: Boolean(result),
message: ethDeposits[0],
childTxReceipt: result,
hash,
}
}
} catch (e) {
Expand All @@ -109,6 +138,7 @@ async function waitForCrossChainTransaction(
complete: result.status === ParentToChildMessageStatus.REDEEMED,
message: messages[0],
childTxReceipt: result,
hash,
}
}
} catch (e) {
Expand All @@ -118,7 +148,53 @@ async function waitForCrossChainTransaction(
throw new Error('No cross chain message found in transaction')
}

export function arbitrumDepositActions() {
async function sendCrossChainTransaction(
parentClient: PublicClient,
childClient: PublicClient,
walletClient: WalletClient,
{
request,
confirmations = DEFAULT_CONFIRMATIONS,
timeout = DEFAULT_TIMEOUT,
}: SendCrossChainTransactionParameters
): Promise<CrossChainTransactionStatus> {
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<CrossChainTransactionStatus> {
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),
Expand All @@ -132,5 +208,14 @@ export function arbitrumParentWalletActions(
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),
})
}
4 changes: 2 additions & 2 deletions src/experimental/createArbitrumClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
import {
ArbitrumDepositActions,
ArbitrumParentWalletActions,
arbitrumDepositActions,
arbitrumParentClientActions,
arbitrumParentWalletActions,
} from './actions'

Expand Down Expand Up @@ -44,7 +44,7 @@ export function createArbitrumClient({
const childPublicClient = createPublicClient({
chain: childChain,
transport: http(childRpcUrl || childChain.rpcUrls.default.http[0]),
}).extend(arbitrumDepositActions())
}).extend(arbitrumParentClientActions())

const extendedParentWalletClient = parentWalletClient.extend(
arbitrumParentWalletActions(parentPublicClient, childPublicClient)
Expand Down
54 changes: 38 additions & 16 deletions tests/integration/arbitrumDepositActions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,8 @@ describe('arbitrumDepositActions', function () {
}
})

it('deposits ETH from parent to child and waits for completion', async function () {
const parentAccount = privateKeyToAccount(
`0x${config.ethKey}` as `0x${string}`
)
it('deposits ETH from parent to child using deposit action', async function () {
const parentAccount = privateKeyToAccount(`0x${config.ethKey}`)
const depositAmount = parseEther('0.01')

const baseParentWalletClient = createWalletClient({
Expand All @@ -55,23 +53,12 @@ describe('arbitrumDepositActions', function () {
address: parentAccount.address,
})

const request = await childPublicClient.prepareDepositEthTransaction({
const result = await parentWalletClient.depositEth({
amount: depositAmount,
account: parentAccount,
})

const hash = await parentWalletClient.sendTransaction({
...request,
chain: localEthChain,
account: parentAccount,
})

const result = await parentWalletClient.waitForCrossChainTransaction({
hash,
})

expect(result.status).to.equal('success')
expect(result.complete).to.be.true

const finalBalance = await childPublicClient.getBalance({
address: parentAccount.address,
Expand All @@ -80,4 +67,39 @@ describe('arbitrumDepositActions', function () {
const balanceDiff = finalBalance - initialBalance
expect(balanceDiff).to.equal(depositAmount)
})

it('handles deposit failure gracefully', async function () {
const parentAccount = privateKeyToAccount(`0x${config.ethKey}`)
// Use an amount too large to cause a failure
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
}
})
})

0 comments on commit bdd936b

Please sign in to comment.