Skip to content

Commit

Permalink
feat: add weth gateway registration
Browse files Browse the repository at this point in the history
  • Loading branch information
TucksonDev committed Feb 1, 2024
1 parent 87cb0e1 commit 894b8a3
Show file tree
Hide file tree
Showing 8 changed files with 399 additions and 10 deletions.
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();
}
55 changes: 55 additions & 0 deletions src/createTokenBridge-ethers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ const IInbox__factory = NamedFactoryInstance(IInbox);
import IERC20Bridge from '@arbitrum/nitro-contracts/build/contracts/src/bridge/IERC20Bridge.sol/IERC20Bridge.json';
const IERC20Bridge__factory = NamedFactoryInstance(IERC20Bridge);
import IERC20 from '@arbitrum/nitro-contracts/build/contracts/@openzeppelin/contracts/token/ERC20/IERC20.sol/IERC20.json';
import { TransactionRequestGasOverrides } from './types/TransactionRequestGasOverrides';
import { GasOverrides } from '@arbitrum/sdk/dist/lib/message/L1ToL2MessageGasEstimator';

const IERC20__factory = NamedFactoryInstance(IERC20);

Expand Down Expand Up @@ -320,6 +322,59 @@ export 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,
gasOverrides?: TransactionRequestGasOverrides,
) => {
//// run retryable estimate for setting a token gateway in the router
const l1ToL2MsgGasEstimate = new L1ToL2MessageGasEstimator(l2Provider);

// applying gas overrides
const gasOverridesForEstimation: GasOverrides = {};
if (gasOverrides && gasOverrides.gasLimit) {
gasOverridesForEstimation.gasLimit = {
min: undefined,
percentIncrease: undefined,
};

if (gasOverrides.gasLimit.minimum) {
gasOverridesForEstimation.gasLimit.min = BigNumber.from(gasOverrides.gasLimit.minimum);
}

if (gasOverrides.gasLimit.percentIncrease) {
gasOverridesForEstimation.gasLimit.percentIncrease = BigNumber.from(
gasOverrides.gasLimit.percentIncrease,
);
}
}

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

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

export const getSigner = (provider: JsonRpcProvider, key?: string) => {
if (!key && !provider) throw new Error('Provide at least one of key or provider.');
if (key) return new Wallet(key).connect(provider);
Expand Down
48 changes: 48 additions & 0 deletions src/createTokenBridge.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
encodeFunctionData,
http,
maxInt256,
parseAbi,
parseEther,
zeroAddress,
} from 'viem';
Expand All @@ -17,6 +18,8 @@ import { deployTokenBridgeCreator } from './createTokenBridge-testHelpers';
import { createTokenBridgeEnoughCustomFeeTokenAllowance } 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 @@ -149,6 +152,46 @@ it(`successfully deploys token bridge contracts through token bridge creator`, a
expect(tokenBridgeContracts.childChainContracts.beaconProxyFactory).not.toEqual(zeroAddress);
expect(tokenBridgeContracts.childChainContracts.upgradeExecutor).not.toEqual(zeroAddress);
expect(tokenBridgeContracts.childChainContracts.multicall).not.toEqual(zeroAddress);

// set weth gateway
// -----------------------------
const setWethGatewayTxRequest = await createTokenBridgePrepareSetWethGatewayTransactionRequest({
rollup: testnodeInformation.rollup,
parentChainPublicClient: nitroTestnodeL1Client,
childChainPublicClient: nitroTestnodeL2Client,
account: l2RollupOwner.address,
gasOverrides: {
gasLimit: {
percentIncrease: 200n,
},
},
});

// 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 }),
);

function waitForRetryablesOfSetWethGateway() {
return setWethGatewayTxReceipt.waitForRetryables({ orbitPublicClient: nitroTestnodeL2Client });
}

expect(setWethGatewayTxReceipt.status).toEqual('success');
await expect(waitForRetryablesOfSetWethGateway()).resolves.toHaveLength(1);

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

it(`successfully deploys token bridge contracts with a custom fee token through token bridge creator`, async () => {
Expand Down Expand Up @@ -305,4 +348,9 @@ it(`successfully deploys token bridge contracts with a custom fee token through
expect(tokenBridgeContracts.childChainContracts.beaconProxyFactory).not.toEqual(zeroAddress);
expect(tokenBridgeContracts.childChainContracts.upgradeExecutor).not.toEqual(zeroAddress);
expect(tokenBridgeContracts.childChainContracts.multicall).not.toEqual(zeroAddress);

// verify weth gateway and token contracts
expect(tokenBridgeContracts.parentChainContracts.wethGateway).toEqual(zeroAddress);
expect(tokenBridgeContracts.childChainContracts.wethGateway).toEqual(zeroAddress);
expect(tokenBridgeContracts.childChainContracts.weth).toEqual(zeroAddress);
});
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, 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 l1TxReceipt = new L1TransactionReceipt(ethersTxReceipt);
const orbitProvider = publicClientToProvider(orbitPublicClient);
const messages = await l1TxReceipt.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, RedeemedRetryableTicket])
//
.map((result) =>
ethersTransactionReceiptToViemTransactionReceipt(result.l2TxReceipt),
) as WaitForRetryablesResult
);
},
};
}
Loading

0 comments on commit 894b8a3

Please sign in to comment.