Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add weth gateway registration #44

Merged
merged 72 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
ec5967d
everything before cleanup
spsjvc Dec 28, 2023
f653ec2
start cleaning up stuff
spsjvc Dec 28, 2023
b9b5cf0
clean up more
spsjvc Dec 28, 2023
67be35a
clean up more
spsjvc Dec 28, 2023
4aea262
rm unused
spsjvc Dec 28, 2023
be153ed
rm unused again
spsjvc Dec 28, 2023
d0a1eac
make parent configurable in test rollup
spsjvc Dec 28, 2023
b88c709
temp fix
spsjvc Dec 28, 2023
782eee3
wait for retryables
spsjvc Jan 9, 2024
44445ad
unused import
spsjvc Jan 9, 2024
3034c4c
move stuff around
spsjvc Jan 9, 2024
f5860e5
more ethers viem compat stuff
spsjvc Jan 9, 2024
cea16a8
return retryables
spsjvc Jan 9, 2024
4e256d6
Merge branch 'main' into feat-token-bridge-creator
spsjvc Jan 9, 2024
9a2de12
format
spsjvc Jan 9, 2024
7d565c1
bump token bridge contracts
spsjvc Jan 9, 2024
018179b
increase test timeout
spsjvc Jan 10, 2024
e7d0f69
fix test
spsjvc Jan 22, 2024
716249c
add helper to deploy token bridge creator
spsjvc Jan 22, 2024
c028ce7
cast to address
spsjvc Jan 22, 2024
f85aae5
wrap up test
spsjvc Jan 23, 2024
d462cde
remove unneeded code
spsjvc Jan 23, 2024
6f0561a
fix nitro-testnode ref
spsjvc Jan 23, 2024
e53c510
fix
spsjvc Jan 23, 2024
aa049d6
try
spsjvc Jan 23, 2024
c06ac5f
comment
spsjvc Jan 23, 2024
bd3deca
add logs
spsjvc Jan 23, 2024
1f67c88
load rollup and rollup owner
spsjvc Jan 23, 2024
1d97f85
Merge branch 'main' into feat-token-bridge-creator
TucksonDev Jan 24, 2024
f6d8a39
Clean up token bridge ABI and add createRollupFetchCoreContracts and …
TucksonDev Jan 24, 2024
efdb47e
Add feedback changes
TucksonDev Jan 25, 2024
fdccf15
Fix publicClientToProvider RPC bug
TucksonDev Jan 25, 2024
2e33364
Add approval methods and examples
TucksonDev Jan 25, 2024
cb9d564
Increase tests timeout
TucksonDev Jan 26, 2024
d6cea89
Test with simpler args for testnode on CI
TucksonDev Jan 29, 2024
73fb8c5
Add integration test for a token bridge on l3 with custom fee token
TucksonDev Jan 30, 2024
8582253
Add gas overriding options for creating a token bridge
TucksonDev Jan 30, 2024
6463345
Revert branch change for token-bridge-contracts
TucksonDev Jan 30, 2024
1b6ce9e
Bring back no-token-bridge flag
TucksonDev Jan 30, 2024
efb4b4d
Change branch of token-bridge-contracts
TucksonDev Jan 30, 2024
0369f82
Add comment back on CI file and add testnode command to README
TucksonDev Jan 30, 2024
87cb0e1
Merge branch 'main' into feat-token-bridge-creator
TucksonDev Jan 30, 2024
894b8a3
feat: add weth gateway registration
TucksonDev Feb 1, 2024
bba4fc3
Change gas limit percentage increase to enforced minimum gasLimit
TucksonDev Feb 1, 2024
704c3cf
Format
TucksonDev Feb 1, 2024
b3ff466
Adjust default value for token bridge creation
TucksonDev Feb 1, 2024
5447e56
Update example to create token bridge
TucksonDev Feb 1, 2024
8db15a0
Format
TucksonDev Feb 1, 2024
fc82da8
Merge branch 'feat-token-bridge-creator' into feat-add-weth-gateway-r…
TucksonDev Feb 1, 2024
2ef2e55
Added changes from review
TucksonDev Feb 7, 2024
3bfe7e2
Adjust testnode CI action flags
TucksonDev Feb 8, 2024
145063e
Change TokenBridgeCreator address on L1 testnode since the rollup own…
TucksonDev Feb 8, 2024
9511462
Merge branch 'main' into feat-token-bridge-creator
TucksonDev Feb 8, 2024
9547b8e
Adjustments from merge
TucksonDev Feb 8, 2024
ff94adc
Add fetch contracts in example and add check for retryables status
TucksonDev Feb 8, 2024
bd90710
Format
TucksonDev Feb 8, 2024
4f51836
Merge branch 'feat-token-bridge-creator' into feat-add-weth-gateway-r…
TucksonDev Feb 8, 2024
2cf9c96
Changes from merge
TucksonDev Feb 8, 2024
b739e1e
Format
TucksonDev Feb 8, 2024
cfe21f7
Smol adjustments
TucksonDev Feb 8, 2024
5da1247
Bump viem peerDependency
TucksonDev Feb 8, 2024
e1cc9a1
Merge branch 'feat-token-bridge-creator' into feat-add-weth-gateway-r…
TucksonDev Feb 8, 2024
bb123f9
Merge branch 'main' into feat-add-weth-gateway-registration
TucksonDev Feb 20, 2024
54e9f04
Changes from last merge
TucksonDev Feb 20, 2024
59ae2ae
Some cleanup
TucksonDev Feb 20, 2024
98a7f17
Format
TucksonDev Feb 20, 2024
f4b913c
Add orbit contract verification on example
TucksonDev Feb 20, 2024
3ed5f2e
Merge branch 'main' into feat-add-weth-gateway-registration
TucksonDev Feb 28, 2024
d428e9a
Use the new TokenBridgeCreator override to fix the failing integratio…
TucksonDev Feb 28, 2024
6addddd
Format
TucksonDev Feb 28, 2024
b4174fd
Remove extra retryables and add check for Orbit side
TucksonDev Feb 29, 2024
2c175a7
Format
TucksonDev Feb 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions examples/create-token-bridge-eth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { Chain, createPublicClient, http, defineChain } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { arbitrumSepolia } from 'viem/chains';
import {
createTokenBridgePrepareSetWethGatewayTransactionReceipt,
createTokenBridgePrepareSetWethGatewayTransactionRequest,
createTokenBridgePrepareTransactionReceipt,
createTokenBridgePrepareTransactionRequest,
} from '@arbitrum/orbit-sdk';
Expand Down Expand Up @@ -90,12 +92,76 @@ async function main() {
console.log(
`Transaction hash for second retryable is ${orbitChainRetryableReceipts[1].transactionHash}`,
);
if (orbitChainRetryableReceipts[0].status !== 'success') {
throw new Error(
`First retryable status is not success: ${orbitChainRetryableReceipts[0].status}. Aborting...`,
);
}
if (orbitChainRetryableReceipts[1].status !== 'success') {
throw new Error(
`Second retryable status is not success: ${orbitChainRetryableReceipts[1].status}. Aborting...`,
);
}

// fetching the TokenBridge contracts
const tokenBridgeContracts = await txReceipt.getTokenBridgeContracts({
parentChainPublicClient,
});
console.log(`TokenBridge contracts:`, tokenBridgeContracts);

// verifying L2 contract existence
const orbitChainRouterBytecode = await orbitChainPublicClient.getBytecode({
address: tokenBridgeContracts.orbitChainContracts.router,
});

if (!orbitChainRouterBytecode || orbitChainRouterBytecode == '0x') {
throw new Error(
`TokenBridge deployment seems to have failed since orbit chain contracts do not have code`,
);
}

// set weth gateway
const setWethGatewayTxRequest = await createTokenBridgePrepareSetWethGatewayTransactionRequest({
rollup: process.env.ROLLUP_ADDRESS as `0x${string}`,
parentChainPublicClient,
orbitChainPublicClient,
account: rollupOwner.address,
retryableGasOverrides: {
gasLimit: {
percentIncrease: 200n,
},
},
});

// sign and send the transaction
const setWethGatewayTxHash = await parentChainPublicClient.sendRawTransaction({
serializedTransaction: await rollupOwner.signTransaction(setWethGatewayTxRequest),
});

// get the transaction receipt after waiting for the transaction to complete
const setWethGatewayTxReceipt = createTokenBridgePrepareSetWethGatewayTransactionReceipt(
await parentChainPublicClient.waitForTransactionReceipt({ hash: setWethGatewayTxHash }),
);

console.log(
`Weth gateway set in ${getBlockExplorerUrl(parentChain)}/tx/${
setWethGatewayTxReceipt.transactionHash
}`,
);

// Wait for retryables to execute
const orbitChainSetWethGatewayRetryableReceipt = await setWethGatewayTxReceipt.waitForRetryables({
orbitPublicClient: orbitChainPublicClient,
});
console.log(`Retryables executed`);
console.log(
`Transaction hash for retryable is ${orbitChainSetWethGatewayRetryableReceipt[0].transactionHash}`,
);
if (orbitChainSetWethGatewayRetryableReceipt[0].status !== 'success') {
throw new Error(
`Retryable status is not success: ${orbitChainSetWethGatewayRetryableReceipt[0].status}. Aborting...`,
);
}
}

main();
35 changes: 35 additions & 0 deletions src/createRollupFetchCoreContracts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Address, PublicClient } from 'viem';
import { validParentChainId } from './types/ParentChain';
import { CoreContracts } from './types/CoreContracts';
import { createRollupFetchTransactionHash } from './createRollupFetchTransactionHash';
import { createRollupPrepareTransactionReceipt } from './createRollupPrepareTransactionReceipt';

export type CreateRollupFetchCoreContractsParams = {
rollup: Address;
publicClient: PublicClient;
};

export async function createRollupFetchCoreContracts({
rollup,
publicClient,
}: CreateRollupFetchCoreContractsParams): Promise<CoreContracts> {
const chainId = publicClient.chain?.id;

if (!validParentChainId(chainId)) {
throw new Error('chainId is undefined');
}

// getting core contract addresses
const transactionHash = await createRollupFetchTransactionHash({
rollup,
publicClient,
});

const transactionReceipt = createRollupPrepareTransactionReceipt(
await publicClient.waitForTransactionReceipt({
hash: transactionHash,
}),
);

return transactionReceipt.getCoreContracts();
}
32 changes: 32 additions & 0 deletions src/createTokenBridge-ethers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,38 @@ const getEstimateForDeployingFactory = async (
return deployFactoryGasParams;
};

export const getEstimateForSettingGateway = async (
l1ChainOwnerAddress: Address,
l1UpgradeExecutorAddress: Address,
l1GatewayRouterAddress: Address,
setGatewaysCalldata: `0x${string}`,
l1Provider: ethers.providers.Provider,
l2Provider: ethers.providers.Provider,
) => {
//// run retryable estimate for setting a token gateway in the router
const l1ToL2MsgGasEstimate = new L1ToL2MessageGasEstimator(l2Provider);

const setGatewaysGasParams = await l1ToL2MsgGasEstimate.estimateAll(
{
from: l1UpgradeExecutorAddress,
to: l1GatewayRouterAddress,
l2CallValue: BigNumber.from(0),
excessFeeRefundAddress: l1ChainOwnerAddress,
callValueRefundAddress: l1ChainOwnerAddress,
data: setGatewaysCalldata,
},
await getBaseFee(l1Provider),
l1Provider,
);

return {
gasLimit: setGatewaysGasParams.gasLimit.toBigInt(),
maxFeePerGas: setGatewaysGasParams.maxFeePerGas.toBigInt(),
maxSubmissionCost: setGatewaysGasParams.maxSubmissionCost.toBigInt(),
deposit: setGatewaysGasParams.deposit.toBigInt(),
};
};

const registerNewNetwork = async (
l1Provider: JsonRpcProvider,
l2Provider: JsonRpcProvider,
Expand Down
63 changes: 58 additions & 5 deletions src/createTokenBridge.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import {
createPublicClient,
encodeFunctionData,
http,
maxInt256,
parseEther,
zeroAddress,
parseAbi,
} from 'viem';
import { execSync } from 'node:child_process';

Expand All @@ -14,12 +14,11 @@ import { getNitroTestnodePrivateKeyAccounts } from './testHelpers';
import { createTokenBridgePrepareTransactionRequest } from './createTokenBridgePrepareTransactionRequest';
import { createTokenBridgePrepareTransactionReceipt } from './createTokenBridgePrepareTransactionReceipt';
import { deployTokenBridgeCreator } from './createTokenBridge-testHelpers';
import {
CreateTokenBridgeEnoughCustomFeeTokenAllowanceParams,
createTokenBridgeEnoughCustomFeeTokenAllowance,
} from './createTokenBridgeEnoughCustomFeeTokenAllowance';
import { CreateTokenBridgeEnoughCustomFeeTokenAllowanceParams } from './createTokenBridgeEnoughCustomFeeTokenAllowance';
import { createTokenBridgePrepareCustomFeeTokenApprovalTransactionRequest } from './createTokenBridgePrepareCustomFeeTokenApprovalTransactionRequest';
import { erc20 } from './contracts';
import { createTokenBridgePrepareSetWethGatewayTransactionRequest } from './createTokenBridgePrepareSetWethGatewayTransactionRequest';
import { createTokenBridgePrepareSetWethGatewayTransactionReceipt } from './createTokenBridgePrepareSetWethGatewayTransactionReceipt';

type TestnodeInformation = {
rollup: `0x${string}`;
Expand Down Expand Up @@ -161,6 +160,60 @@ it(`successfully deploys token bridge contracts through token bridge creator`, a
expect(tokenBridgeContracts.orbitChainContracts.beaconProxyFactory).not.toEqual(zeroAddress);
expect(tokenBridgeContracts.orbitChainContracts.upgradeExecutor).not.toEqual(zeroAddress);
expect(tokenBridgeContracts.orbitChainContracts.multicall).not.toEqual(zeroAddress);

// set weth gateway
const setWethGatewayTxRequest = await createTokenBridgePrepareSetWethGatewayTransactionRequest({
TucksonDev marked this conversation as resolved.
Show resolved Hide resolved
rollup: testnodeInformation.rollup,
parentChainPublicClient: nitroTestnodeL1Client,
orbitChainPublicClient: nitroTestnodeL2Client,
account: l2RollupOwner.address,
retryableGasOverrides: {
gasLimit: {
base: 100_000n,
},
},
tokenBridgeCreatorAddressOverride: tokenBridgeCreator,
});

// sign and send the transaction
const setWethGatewayTxHash = await nitroTestnodeL1Client.sendRawTransaction({
serializedTransaction: await l2RollupOwner.signTransaction(setWethGatewayTxRequest),
});

// get the transaction receipt after waiting for the transaction to complete
const setWethGatewayTxReceipt = createTokenBridgePrepareSetWethGatewayTransactionReceipt(
await nitroTestnodeL1Client.waitForTransactionReceipt({ hash: setWethGatewayTxHash }),
);

// checking retryables execution
const orbitChainSetGatewayRetryableReceipt = await setWethGatewayTxReceipt.waitForRetryables({
orbitPublicClient: nitroTestnodeL2Client,
});
expect(orbitChainSetGatewayRetryableReceipt).toHaveLength(1);
expect(orbitChainSetGatewayRetryableReceipt[0].status).toEqual('success');

// verify weth gateway (parent chain)
const registeredWethGatewayOnParentChain = await nitroTestnodeL1Client.readContract({
address: tokenBridgeContracts.parentChainContracts.router,
abi: parseAbi(['function l1TokenToGateway(address) view returns (address)']),
functionName: 'l1TokenToGateway',
args: [tokenBridgeContracts.parentChainContracts.weth],
});
expect(registeredWethGatewayOnParentChain).toEqual(
tokenBridgeContracts.parentChainContracts.wethGateway,
);

// verify weth gateway (orbit chain)
// Note: we pass the address of the token on the parent chain when asking for the registered gateway on the orbit chain
const registeredWethGatewayOnOrbitChain = await nitroTestnodeL2Client.readContract({
address: tokenBridgeContracts.orbitChainContracts.router,
abi: parseAbi(['function l1TokenToGateway(address) view returns (address)']),
functionName: 'l1TokenToGateway',
args: [tokenBridgeContracts.parentChainContracts.weth],
});
expect(registeredWethGatewayOnOrbitChain).toEqual(
tokenBridgeContracts.orbitChainContracts.wethGateway,
);
});

it(`successfully deploys token bridge contracts with a custom fee token through token bridge creator`, async () => {
Expand Down
56 changes: 56 additions & 0 deletions src/createTokenBridgePrepareSetWethGatewayTransactionReceipt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { PublicClient, TransactionReceipt } from 'viem';
import { L1ToL2MessageStatus, L1TransactionReceipt } from '@arbitrum/sdk';
import { TransactionReceipt as EthersTransactionReceipt } from '@ethersproject/abstract-provider';

import { publicClientToProvider } from './ethers-compat/publicClientToProvider';
import { viemTransactionReceiptToEthersTransactionReceipt } from './ethers-compat/viemTransactionReceiptToEthersTransactionReceipt';
import { ethersTransactionReceiptToViemTransactionReceipt } from './ethers-compat/ethersTransactionReceiptToViemTransactionReceipt';

type RedeemedRetryableTicket = {
status: L1ToL2MessageStatus.REDEEMED;
l2TxReceipt: EthersTransactionReceipt;
};

export type WaitForRetryablesParameters = {
orbitPublicClient: PublicClient;
};

export type WaitForRetryablesResult = [TransactionReceipt];

export type CreateTokenBridgeSetWethGatewayTransactionReceipt = TransactionReceipt & {
waitForRetryables(params: WaitForRetryablesParameters): Promise<WaitForRetryablesResult>;
};

export function createTokenBridgePrepareSetWethGatewayTransactionReceipt(
txReceipt: TransactionReceipt,
): CreateTokenBridgeSetWethGatewayTransactionReceipt {
return {
...txReceipt,
waitForRetryables: async function ({
orbitPublicClient,
}: WaitForRetryablesParameters): Promise<WaitForRetryablesResult> {
const ethersTxReceipt = viemTransactionReceiptToEthersTransactionReceipt(txReceipt);
const parentChainTxReceipt = new L1TransactionReceipt(ethersTxReceipt);
const orbitProvider = publicClientToProvider(orbitPublicClient);
const messages = await parentChainTxReceipt.getL1ToL2Messages(orbitProvider);
const messagesResults = await Promise.all(messages.map((message) => message.waitForStatus()));

if (messagesResults.length !== 1) {
throw Error(`Unexpected number of retryable tickets: ${messagesResults.length}`);
}

if (messagesResults[0].status !== L1ToL2MessageStatus.REDEEMED) {
throw Error(`Unexpected status for retryable ticket: ${messages[0].retryableCreationId}`);
}

return (
// these type casts are both fine as we already checked everything above
(messagesResults as unknown as [RedeemedRetryableTicket])
//
.map((result) =>
ethersTransactionReceiptToViemTransactionReceipt(result.l2TxReceipt),
) as WaitForRetryablesResult
);
},
};
}
Loading
Loading