Skip to content

Commit

Permalink
feat: enable factory address overrides (#56)
Browse files Browse the repository at this point in the history
  • Loading branch information
spsjvc authored Feb 27, 2024
1 parent 52e2f96 commit b22ad2a
Show file tree
Hide file tree
Showing 13 changed files with 233 additions and 114 deletions.
1 change: 0 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,3 @@ import { parseEther } from 'viem';
export const createRollupDefaultRetryablesFees = parseEther('0.125');

export const createTokenBridgeDefaultRetryablesFees = parseEther('0.02');
export const createTokenBridgeDefaultGasLimit = 5_000_000n;
18 changes: 12 additions & 6 deletions src/createRollupEnoughCustomFeeTokenAllowance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@ import { validParentChainId } from './types/ParentChain';
import { fetchAllowance } from './utils/erc20';
import { createRollupDefaultRetryablesFees } from './constants';

export type CreateRollupEnoughCustomFeeTokenAllowanceParams = {
nativeToken: Address;
account: Address;
publicClient: PublicClient;
};
import { Prettify } from './types/utils';
import { WithRollupCreatorAddressOverride } from './types/createRollupTypes';

export type CreateRollupEnoughCustomFeeTokenAllowanceParams = Prettify<
WithRollupCreatorAddressOverride<{
nativeToken: Address;
account: Address;
publicClient: PublicClient;
}>
>;

export async function createRollupEnoughCustomFeeTokenAllowance({
nativeToken,
account,
publicClient,
rollupCreatorAddressOverride,
}: CreateRollupEnoughCustomFeeTokenAllowanceParams) {
const chainId = publicClient.chain?.id;

Expand All @@ -25,7 +31,7 @@ export async function createRollupEnoughCustomFeeTokenAllowance({
const allowance = await fetchAllowance({
address: nativeToken,
owner: account,
spender: rollupCreator.address[chainId],
spender: rollupCreatorAddressOverride ?? rollupCreator.address[chainId],
publicClient,
});

Expand Down
20 changes: 13 additions & 7 deletions src/createRollupPrepareCustomFeeTokenApprovalTransactionRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,24 @@ import { validParentChainId } from './types/ParentChain';
import { rollupCreator } from './contracts';
import { createRollupDefaultRetryablesFees } from './constants';

export type CreateRollupPrepareCustomFeeTokenApprovalTransactionRequestParams = {
amount?: bigint;
nativeToken: Address;
account: Address;
publicClient: PublicClient;
};
import { Prettify } from './types/utils';
import { WithRollupCreatorAddressOverride } from './types/createRollupTypes';

export type CreateRollupPrepareCustomFeeTokenApprovalTransactionRequestParams = Prettify<
WithRollupCreatorAddressOverride<{
amount?: bigint;
nativeToken: Address;
account: Address;
publicClient: PublicClient;
}>
>;

export async function createRollupPrepareCustomFeeTokenApprovalTransactionRequest({
amount = createRollupDefaultRetryablesFees,
nativeToken,
account,
publicClient,
rollupCreatorAddressOverride,
}: CreateRollupPrepareCustomFeeTokenApprovalTransactionRequestParams) {
const chainId = publicClient.chain?.id;

Expand All @@ -27,7 +33,7 @@ export async function createRollupPrepareCustomFeeTokenApprovalTransactionReques
const request = await approvePrepareTransactionRequest({
address: nativeToken,
owner: account,
spender: rollupCreator.address[chainId],
spender: rollupCreatorAddressOverride ?? rollupCreator.address[chainId],
amount,
publicClient,
});
Expand Down
40 changes: 33 additions & 7 deletions src/createRollupPrepareTransactionRequest.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Address, PublicClient, encodeFunctionData, zeroAddress } from 'viem';

import { CreateRollupFunctionInputs, CreateRollupParams } from './types/createRollupTypes';
import { defaults } from './createRollupDefaults';
import { createRollupGetCallValue } from './createRollupGetCallValue';
import { createRollupGetMaxDataSize } from './createRollupGetMaxDataSize';
Expand All @@ -10,6 +9,14 @@ import { isCustomFeeTokenAddress } from './utils/isCustomFeeTokenAddress';
import { ChainConfig } from './types/ChainConfig';
import { isAnyTrustChainConfig } from './utils/isAnyTrustChainConfig';
import { fetchDecimals } from './utils/erc20';
import { TransactionRequestGasOverrides, applyPercentIncrease } from './utils/gasOverrides';

import { Prettify } from './types/utils';
import {
CreateRollupFunctionInputs,
CreateRollupParams,
WithRollupCreatorAddressOverride,
} from './types/createRollupTypes';

function createRollupEncodeFunctionData(args: CreateRollupFunctionInputs) {
return encodeFunctionData({
Expand All @@ -19,15 +26,22 @@ function createRollupEncodeFunctionData(args: CreateRollupFunctionInputs) {
});
}

export type CreateRollupPrepareTransactionRequestParams = Prettify<
WithRollupCreatorAddressOverride<{
params: CreateRollupParams;
account: Address;
publicClient: PublicClient;
gasOverrides?: TransactionRequestGasOverrides;
}>
>;

export async function createRollupPrepareTransactionRequest({
params,
account,
publicClient,
}: {
params: CreateRollupParams;
account: Address;
publicClient: PublicClient;
}) {
gasOverrides,
rollupCreatorAddressOverride,
}: CreateRollupPrepareTransactionRequestParams) {
const chainId = publicClient.chain?.id;

if (!validParentChainId(chainId)) {
Expand Down Expand Up @@ -65,11 +79,23 @@ export async function createRollupPrepareTransactionRequest({

const request = await publicClient.prepareTransactionRequest({
chain: publicClient.chain,
to: rollupCreator.address[chainId],
to: rollupCreatorAddressOverride ?? rollupCreator.address[chainId],
data: createRollupEncodeFunctionData([paramsWithDefaults]),
value: createRollupGetCallValue(paramsWithDefaults),
account,
// if the base gas limit override was provided, hardcode gas to 0 to skip estimation
// we'll set the actual value in the code below
gas: typeof gasOverrides?.gasLimit?.base !== 'undefined' ? 0n : undefined,
});

// potential gas overrides (gas limit)
if (gasOverrides && gasOverrides.gasLimit) {
request.gas = applyPercentIncrease({
// the ! is here because we should let it error in case we don't have the estimated gas
base: gasOverrides.gasLimit.base ?? request.gas!,
percentIncrease: gasOverrides.gasLimit.percentIncrease,
});
}

return { ...request, chainId };
}
71 changes: 66 additions & 5 deletions src/createRollupPrepareTransactionRequest.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { generateChainId } from './utils';
import { prepareChainConfig } from './prepareChainConfig';
import { createRollupPrepareConfig } from './createRollupPrepareConfig';
import { createRollupPrepareTransactionRequest } from './createRollupPrepareTransactionRequest';
import { rollupCreator } from './contracts';

import { getNitroTestnodePrivateKeyAccounts } from './testHelpers';

Expand Down Expand Up @@ -137,11 +138,6 @@ it(`fails to prepare transaction request if "params.nativeToken" is custom and c
});

it(`fails to prepare transaction request if "params.nativeToken" doesn't use 18 decimals`, async () => {
const arbitrumSepoliaPublicClient = createPublicClient({
chain: arbitrumSepolia,
transport: http(),
});

// generate a random chain id
const chainId = generateChainId();

Expand Down Expand Up @@ -172,3 +168,68 @@ it(`fails to prepare transaction request if "params.nativeToken" doesn't use 18
`"params.nativeToken" can only be configured with a token that uses 18 decimals.`,
);
});

it(`successfully prepares a transaction request with the default rollup creator and a gas limit override`, async () => {
// generate a random chain id
const chainId = generateChainId();

// create the chain config
const chainConfig = prepareChainConfig({
chainId,
arbitrum: { InitialChainOwner: deployer.address, DataAvailabilityCommittee: true },
});

const txRequest = await createRollupPrepareTransactionRequest({
params: {
config: createRollupPrepareConfig({
chainId: BigInt(chainId),
owner: deployer.address,
chainConfig,
}),
batchPoster: deployer.address,
validators: [deployer.address],
},
account: deployer.address,
publicClient,
gasOverrides: { gasLimit: { base: 1_000n } },
});

expect(txRequest.account).toEqual(deployer.address);
expect(txRequest.from).toEqual(deployer.address);
expect(txRequest.to).toEqual(rollupCreator.address[arbitrumSepolia.id]);
expect(txRequest.chainId).toEqual(arbitrumSepolia.id);
expect(txRequest.gas).toEqual(1_000n);
});

it(`successfully prepares a transaction request with a custom rollup creator and a gas limit override`, async () => {
// generate a random chain id
const chainId = generateChainId();

// create the chain config
const chainConfig = prepareChainConfig({
chainId,
arbitrum: { InitialChainOwner: deployer.address, DataAvailabilityCommittee: true },
});

const txRequest = await createRollupPrepareTransactionRequest({
params: {
config: createRollupPrepareConfig({
chainId: BigInt(chainId),
owner: deployer.address,
chainConfig,
}),
batchPoster: deployer.address,
validators: [deployer.address],
},
account: deployer.address,
publicClient,
gasOverrides: { gasLimit: { base: 1_000n, percentIncrease: 20n } },
rollupCreatorAddressOverride: '0x31421C442c422BD16aef6ae44D3b11F404eeaBd9',
});

expect(txRequest.account).toEqual(deployer.address);
expect(txRequest.from).toEqual(deployer.address);
expect(txRequest.to).toEqual('0x31421C442c422BD16aef6ae44D3b11F404eeaBd9');
expect(txRequest.chainId).toEqual(arbitrumSepolia.id);
expect(txRequest.gas).toEqual(1_200n);
});
63 changes: 14 additions & 49 deletions src/createTokenBridge.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import { getNitroTestnodePrivateKeyAccounts } from './testHelpers';
import { createTokenBridgePrepareTransactionRequest } from './createTokenBridgePrepareTransactionRequest';
import { createTokenBridgePrepareTransactionReceipt } from './createTokenBridgePrepareTransactionReceipt';
import { deployTokenBridgeCreator } from './createTokenBridge-testHelpers';
import { createTokenBridgeEnoughCustomFeeTokenAllowance } from './createTokenBridgeEnoughCustomFeeTokenAllowance';
import {
CreateTokenBridgeEnoughCustomFeeTokenAllowanceParams,
createTokenBridgeEnoughCustomFeeTokenAllowance,
} from './createTokenBridgeEnoughCustomFeeTokenAllowance';
import { createTokenBridgePrepareCustomFeeTokenApprovalTransactionRequest } from './createTokenBridgePrepareCustomFeeTokenApprovalTransactionRequest';
import { erc20 } from './contracts';

Expand Down Expand Up @@ -110,14 +113,12 @@ it(`successfully deploys token bridge contracts through token bridge creator`, a
base: 4_000_000_000_000n,
},
},
tokenBridgeCreatorAddressOverride: tokenBridgeCreator,
});

// update the transaction request to use the fresh token bridge creator
const txRequestToFreshTokenBridgeCreator = { ...txRequest, to: tokenBridgeCreator };

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

// get the transaction receipt after waiting for the transaction to complete
Expand Down Expand Up @@ -197,59 +198,25 @@ it(`successfully deploys token bridge contracts with a custom fee token through
expect(fundTxReceipt.status).toEqual('success');

// -----------------------------
// 2. check the new token bridge creator allowance
// NOTE: We will estimate the token bridge creation through the canonical TokenBridgeCreator
// and we will later change the destination address to the new deployed TokenBridgeCreator.
// Thus, we need to give allowance to both TokenBridgeCreators
const allowanceParams = {
// 2. approve custom fee token to be spent by the TokenBridgeCreator
const allowanceParams: CreateTokenBridgeEnoughCustomFeeTokenAllowanceParams = {
nativeToken: testnodeInformation.l3NativeToken,
owner: l3RollupOwner.address,
publicClient: nitroTestnodeL2Client,
tokenBridgeCreatorAddressOverride: tokenBridgeCreator,
};

// 2.a. Approval for canonical TokenBridgeCreator (gas estimation is still done through it, so we need allowance)
if (!(await createTokenBridgeEnoughCustomFeeTokenAllowance(allowanceParams))) {
const approvalTxRequest =
await createTokenBridgePrepareCustomFeeTokenApprovalTransactionRequest(allowanceParams);

// sign and send the transaction
const approvalTxHash = await nitroTestnodeL2Client.sendRawTransaction({
serializedTransaction: await l3RollupOwner.signTransaction(approvalTxRequest),
});

// get the transaction receipt after waiting for the transaction to complete
const approvalTxReceipt = await nitroTestnodeL2Client.waitForTransactionReceipt({
hash: approvalTxHash,
});
expect(approvalTxReceipt.status).toEqual('success');
}

// 2.b. Approval for the new TokenBridgeCreator
const approvalForNewTokenBridgeCreatorTxRequest =
await createTokenBridgePrepareCustomFeeTokenApprovalTransactionRequest(allowanceParams);

// update the transaction request to use the fresh token bridge creator
approvalForNewTokenBridgeCreatorTxRequest.data = encodeFunctionData({
abi: erc20.abi,
functionName: 'approve',
args: [tokenBridgeCreator, maxInt256],
});
// also update the gas used since we estimated an update of the mapping of allowances (to the canonical TokenBridgeCreator),
// but we will now be adding a new element to that mapping (hence the extra cost of gas)
approvalForNewTokenBridgeCreatorTxRequest.gas =
approvalForNewTokenBridgeCreatorTxRequest.gas! * 2n;

// sign and send the transaction
const approvalForNewTokenBridgeCreatorTxHash = await nitroTestnodeL2Client.sendRawTransaction({
const approvalForTokenBridgeCreatorTxHash = await nitroTestnodeL2Client.sendRawTransaction({
serializedTransaction: await l3RollupOwner.signTransaction(
approvalForNewTokenBridgeCreatorTxRequest,
await createTokenBridgePrepareCustomFeeTokenApprovalTransactionRequest(allowanceParams),
),
});

// get the transaction receipt after waiting for the transaction to complete
const approvalForNewTokenBridgeCreatorTxReceipt =
await nitroTestnodeL2Client.waitForTransactionReceipt({
hash: approvalForNewTokenBridgeCreatorTxHash,
hash: approvalForTokenBridgeCreatorTxHash,
});
expect(approvalForNewTokenBridgeCreatorTxReceipt.status).toEqual('success');

Expand Down Expand Up @@ -282,14 +249,12 @@ it(`successfully deploys token bridge contracts with a custom fee token through
base: 4_000_000_000_000n,
},
},
tokenBridgeCreatorAddressOverride: tokenBridgeCreator,
});

// update the transaction request to use the fresh token bridge creator
const txRequestToFreshTokenBridgeCreator = { ...txRequest, to: tokenBridgeCreator };

// sign and send the transaction
const txHash = await nitroTestnodeL2Client.sendRawTransaction({
serializedTransaction: await l3RollupOwner.signTransaction(txRequestToFreshTokenBridgeCreator),
serializedTransaction: await l3RollupOwner.signTransaction(txRequest),
});

// get the transaction receipt after waiting for the transaction to complete
Expand Down
18 changes: 12 additions & 6 deletions src/createTokenBridgeEnoughCustomFeeTokenAllowance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@ import { validParentChainId } from './types/ParentChain';
import { fetchAllowance } from './utils/erc20';
import { createTokenBridgeDefaultRetryablesFees } from './constants';

export type CreateTokenBridgeEnoughCustomFeeTokenAllowanceParams = {
nativeToken: Address;
owner: Address;
publicClient: PublicClient;
};
import { Prettify } from './types/utils';
import { WithTokenBridgeCreatorAddressOverride } from './types/createTokenBridgeTypes';

export type CreateTokenBridgeEnoughCustomFeeTokenAllowanceParams = Prettify<
WithTokenBridgeCreatorAddressOverride<{
nativeToken: Address;
owner: Address;
publicClient: PublicClient;
}>
>;

export async function createTokenBridgeEnoughCustomFeeTokenAllowance({
nativeToken,
owner,
publicClient,
tokenBridgeCreatorAddressOverride,
}: CreateTokenBridgeEnoughCustomFeeTokenAllowanceParams) {
const chainId = publicClient.chain?.id;

Expand All @@ -25,7 +31,7 @@ export async function createTokenBridgeEnoughCustomFeeTokenAllowance({
const allowance = await fetchAllowance({
address: nativeToken,
owner,
spender: tokenBridgeCreator.address[chainId],
spender: tokenBridgeCreatorAddressOverride ?? tokenBridgeCreator.address[chainId],
publicClient,
});

Expand Down
Loading

0 comments on commit b22ad2a

Please sign in to comment.