Skip to content

Commit 76a38ff

Browse files
committed
test: deploy token bridge creator contracts on L2
1 parent 006a190 commit 76a38ff

File tree

5 files changed

+188
-45
lines changed

5 files changed

+188
-45
lines changed

.github/workflows/build-test.yml

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,9 +269,54 @@ jobs:
269269
tags: |
270270
${{ env.NITRO_CONTRACTS_GHCR_IMAGE }}
271271
272+
publish-token-bridge-contracts-image:
273+
name: Publish Token Bridge Contracts Image
274+
runs-on: ubuntu-latest
275+
permissions:
276+
contents: read
277+
packages: write
278+
steps:
279+
- name: Checkout
280+
uses: actions/checkout@v4
281+
282+
- name: Set token bridge contracts image
283+
run: |
284+
echo "TOKEN_BRIDGE_CONTRACTS_GHCR_IMAGE=ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/chain-sdk-token-bridge-contracts:v1.2.2" >> "$GITHUB_ENV"
285+
286+
- name: Set up Docker Buildx
287+
uses: docker/setup-buildx-action@v3
288+
289+
- name: Log in to GHCR
290+
uses: docker/login-action@v3
291+
with:
292+
registry: ghcr.io
293+
username: ${{ github.actor }}
294+
password: ${{ secrets.GITHUB_TOKEN }}
295+
296+
- name: Check token bridge contracts image
297+
id: check-image
298+
run: |
299+
if docker manifest inspect "${TOKEN_BRIDGE_CONTRACTS_GHCR_IMAGE}" >/dev/null 2>&1; then
300+
echo "exists=true" >> "$GITHUB_OUTPUT"
301+
else
302+
echo "exists=false" >> "$GITHUB_OUTPUT"
303+
fi
304+
305+
- name: Build and push token bridge contracts image
306+
if: steps.check-image.outputs.exists != 'true'
307+
uses: docker/build-push-action@v6
308+
with:
309+
context: ./token-bridge-contracts
310+
file: ./token-bridge-contracts/Dockerfile
311+
push: true
312+
tags: |
313+
${{ env.TOKEN_BRIDGE_CONTRACTS_GHCR_IMAGE }}
314+
272315
test-integration-anvil:
273316
name: Test (Integration Anvil)
274-
needs: publish-nitro-contracts-image
317+
needs:
318+
- publish-nitro-contracts-image
319+
- publish-token-bridge-contracts-image
275320
runs-on: ubuntu-latest
276321
permissions:
277322
contents: read
@@ -283,6 +328,7 @@ jobs:
283328
- name: Set nitro contracts image
284329
run: |
285330
echo "NITRO_CONTRACTS_GHCR_IMAGE=ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/chain-sdk-nitro-contracts:v3.2.0-2f747c7" >> "$GITHUB_ENV"
331+
echo "TOKEN_BRIDGE_CONTRACTS_GHCR_IMAGE=ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/chain-sdk-token-bridge-contracts:v1.2.2" >> "$GITHUB_ENV"
286332
287333
- name: Log in to GHCR
288334
uses: docker/login-action@v3
@@ -294,6 +340,9 @@ jobs:
294340
- name: Load nitro contracts image
295341
run: docker pull "${NITRO_CONTRACTS_GHCR_IMAGE}"
296342

343+
- name: Load token bridge contracts image
344+
run: docker pull "${TOKEN_BRIDGE_CONTRACTS_GHCR_IMAGE}"
345+
297346
- name: Setup pnpm
298347
uses: pnpm/action-setup@v4
299348
with:

src/integrationTestHelpers/anvilHarness.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import {
4848
createAccount,
4949
deployContract,
5050
deployRollupCreator,
51+
deployTokenBridgeCreator,
5152
ensureCreate2Factory,
5253
fundL2Deployer,
5354
refreshForkTime,
@@ -455,6 +456,22 @@ export async function setupAnvilTestStack(): Promise<AnvilTestStack> {
455456

456457
console.log('L2 rollup creator deployed\n');
457458

459+
console.log('Deploying L2 token bridge creator...');
460+
const l2TokenBridgeCreator = await deployTokenBridgeCreator(
461+
{
462+
networkName: dockerNetworkName,
463+
rpcUrl: `http://${l2ContainerName}:8449`,
464+
deployerPrivateKey: harnessDeployer.privateKey,
465+
wethAddress: customGasToken.address as Address,
466+
},
467+
{
468+
publicClient: l2Client,
469+
walletClient: blockAdvancerWalletClient,
470+
account: blockAdvancerAccount,
471+
},
472+
);
473+
console.log('L2 token bridge creator deployed\n');
474+
458475
await (
459476
await customGasToken.deposit({
460477
value: parseEther('10'),
@@ -475,7 +492,7 @@ export async function setupAnvilTestStack(): Promise<AnvilTestStack> {
475492
testnet: true,
476493
contracts: {
477494
rollupCreator: { address: l2RollupCreator },
478-
tokenBridgeCreator: { address: harnessDeployer.address },
495+
tokenBridgeCreator: { address: l2TokenBridgeCreator },
479496
weth: { address: customGasToken.address as Address },
480497
},
481498
});

src/integrationTestHelpers/anvilHarnessHelpers.ts

Lines changed: 37 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,13 @@ import { ethers } from 'ethers';
1414

1515
import { arbOwnerABI, arbOwnerAddress } from '../contracts/ArbOwner';
1616
import { testConstants } from './constants';
17-
import { dockerAsync, getNitroContractsImage } from './dockerHelpers';
17+
import {
18+
dockerAsync,
19+
getNitroContractsImage,
20+
getRollupCreatorDockerArgs,
21+
getTokenBridgeContractsImage,
22+
getTokenBridgeCreatorDockerArgs,
23+
} from './dockerHelpers';
1824
import type { PrivateKeyAccountWithPrivateKey } from '../testHelpers';
1925

2026
export type ContractArtifact = {
@@ -41,6 +47,14 @@ type DeployRollupCreatorParams = {
4147
chainId: number;
4248
};
4349

50+
type DeployTokenBridgeCreatorParams = {
51+
networkName: string;
52+
rpcUrl: string;
53+
deployerPrivateKey: `0x${string}`;
54+
wethAddress: Address;
55+
gasLimitForL2FactoryDeployment?: bigint;
56+
};
57+
4458
function sleep(ms: number) {
4559
return new Promise((resolve) => setTimeout(resolve, ms));
4660
}
@@ -68,41 +82,6 @@ async function getRequiredRetryableFunding(
6882
};
6983
}
7084

71-
function getRollupCreatorDockerArgs(
72-
params: Omit<DeployRollupCreatorParams, 'blockAdvancer'>,
73-
nitroContractsImage: string,
74-
) {
75-
return [
76-
'run',
77-
'--rm',
78-
'--network',
79-
params.networkName,
80-
'-e',
81-
`CUSTOM_RPC_URL=${params.rpcUrl}`,
82-
'-e',
83-
`CUSTOM_PRIVKEY=${params.deployerPrivateKey}`,
84-
'-e',
85-
`CUSTOM_CHAINID=${params.chainId}`,
86-
'-e',
87-
`FACTORY_OWNER=${params.factoryOwner}`,
88-
'-e',
89-
`MAX_DATA_SIZE=${params.maxDataSize}`,
90-
'-e',
91-
`POLLING_INTERVAL=${testConstants.NITRO_DEPLOY_POLLING_INTERVAL_MS}`,
92-
'-e',
93-
'DISABLE_VERIFICATION=true',
94-
'-e',
95-
'IGNORE_MAX_DATA_SIZE_WARNING=true',
96-
nitroContractsImage,
97-
'hardhat',
98-
'run',
99-
'--no-compile',
100-
'scripts/deployment.ts',
101-
'--network',
102-
'custom',
103-
];
104-
}
105-
10685
function parseRollupCreatorAddress(stdout: string): Address {
10786
const match = stdout.match(/\* RollupCreator created at address: (0x[0-9a-fA-F]{40})/);
10887
if (!match) {
@@ -112,6 +91,17 @@ function parseRollupCreatorAddress(stdout: string): Address {
11291
return match[1] as Address;
11392
}
11493

94+
function parseTokenBridgeCreatorAddress(stdout: string): Address {
95+
const match = stdout.match(/L1TokenBridgeCreator:\s*(0x[0-9a-fA-F]{40})/);
96+
if (!match) {
97+
throw new Error(
98+
`Failed to parse TokenBridgeCreator address from token bridge deploy output:\n${stdout}`,
99+
);
100+
}
101+
102+
return match[1] as Address;
103+
}
104+
115105
export function createAccount(privateKey?: Hex): PrivateKeyAccountWithPrivateKey {
116106
const normalizedPrivateKey = privateKey ?? generatePrivateKey();
117107
return {
@@ -345,7 +335,6 @@ export async function deployRollupCreator(
345335
blockAdvancer: BlockAdvancer,
346336
): Promise<Address> {
347337
const nitroContractsImage = getNitroContractsImage();
348-
const dockerRunStartedAt = Date.now();
349338
const stdout = await withBackgroundBlockAdvancing(blockAdvancer, () =>
350339
dockerAsync(
351340
getRollupCreatorDockerArgs(
@@ -361,11 +350,17 @@ export async function deployRollupCreator(
361350
),
362351
),
363352
);
364-
console.log(
365-
`[${new Date().toISOString()}] rollup creator docker run finished after ${
366-
Date.now() - dockerRunStartedAt
367-
}ms`,
368-
);
369353

370354
return parseRollupCreatorAddress(stdout);
371355
}
356+
357+
export async function deployTokenBridgeCreator(
358+
params: DeployTokenBridgeCreatorParams,
359+
blockAdvancer: BlockAdvancer,
360+
): Promise<Address> {
361+
const tokenBridgeContractsImage = getTokenBridgeContractsImage();
362+
const stdout = await withBackgroundBlockAdvancing(blockAdvancer, () =>
363+
dockerAsync(getTokenBridgeCreatorDockerArgs(params, tokenBridgeContractsImage)),
364+
);
365+
return parseTokenBridgeCreatorAddress(stdout);
366+
}

src/integrationTestHelpers/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ export const testConstants = {
55
DEFAULT_ANVIL_IMAGE: 'ghcr.io/foundry-rs/foundry:v1.3.1',
66
DEFAULT_NITRO_IMAGE: 'offchainlabs/nitro-node:v3.9.5-66e42c4',
77
DEFAULT_NITRO_CONTRACTS_IMAGE: 'ghcr.io/offchainlabs/chain-sdk-nitro-contracts:v3.2.0-2f747c7',
8+
DEFAULT_TOKEN_BRIDGE_CONTRACTS_IMAGE:
9+
'ghcr.io/offchainlabs/chain-sdk-token-bridge-contracts:v1.2.5',
810
DEPLOYER_PRIVATE_KEY:
911
'0x490d84b7602e4b470af4f86a3ad095607a8bb5a4fa8ba148f41fcfd236b4fdf5' as Address,
1012
DEFAULT_L2_CHAIN_ID: 421_337,

src/integrationTestHelpers/dockerHelpers.ts

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { execFile, execFileSync } from 'node:child_process';
22
import { rmSync } from 'node:fs';
33
import { join } from 'node:path';
44

5-
import { createPublicClient, http } from 'viem';
5+
import { createPublicClient, http, Address } from 'viem';
66

77
import { testConstants } from './constants';
88

@@ -50,6 +50,86 @@ export function getNitroContractsImage(): string {
5050
}
5151
}
5252

53+
export function getTokenBridgeContractsImage(): string {
54+
const image =
55+
process.env.TOKEN_BRIDGE_CONTRACTS_GHCR_IMAGE ??
56+
testConstants.DEFAULT_TOKEN_BRIDGE_CONTRACTS_IMAGE;
57+
try {
58+
docker(['image', 'inspect', image]);
59+
return image;
60+
} catch {
61+
const tokenBridgeContractsDir = join(process.cwd(), 'token-bridge-contracts');
62+
const dockerfilePath = join(tokenBridgeContractsDir, 'Dockerfile');
63+
docker(['build', '-f', dockerfilePath, '-t', image, tokenBridgeContractsDir]);
64+
65+
return image;
66+
}
67+
}
68+
69+
export function getRollupCreatorDockerArgs(params: {
70+
networkName: string;
71+
rpcUrl: string;
72+
deployerPrivateKey: `0x${string}`;
73+
factoryOwner: Address;
74+
maxDataSize: number;
75+
chainId: number;
76+
}, nitroContractsImage: string) {
77+
return [
78+
'run',
79+
'--rm',
80+
'--network',
81+
params.networkName,
82+
'-e',
83+
`CUSTOM_RPC_URL=${params.rpcUrl}`,
84+
'-e',
85+
`CUSTOM_PRIVKEY=${params.deployerPrivateKey}`,
86+
'-e',
87+
`CUSTOM_CHAINID=${params.chainId}`,
88+
'-e',
89+
`FACTORY_OWNER=${params.factoryOwner}`,
90+
'-e',
91+
`MAX_DATA_SIZE=${params.maxDataSize}`,
92+
'-e',
93+
`POLLING_INTERVAL=${testConstants.NITRO_DEPLOY_POLLING_INTERVAL_MS}`,
94+
'-e',
95+
'DISABLE_VERIFICATION=true',
96+
'-e',
97+
'IGNORE_MAX_DATA_SIZE_WARNING=true',
98+
nitroContractsImage,
99+
'hardhat',
100+
'run',
101+
'--no-compile',
102+
'scripts/deployment.ts',
103+
'--network',
104+
'custom',
105+
];
106+
}
107+
108+
export function getTokenBridgeCreatorDockerArgs(params: {
109+
networkName: string;
110+
rpcUrl: string;
111+
deployerPrivateKey: `0x${string}`;
112+
wethAddress: Address;
113+
gasLimitForL2FactoryDeployment?: bigint;
114+
}, tokenBridgeContractsImage: string) {
115+
return [
116+
'run',
117+
'--rm',
118+
'--network',
119+
params.networkName,
120+
'-e',
121+
`BASECHAIN_RPC=${params.rpcUrl}`,
122+
'-e',
123+
`BASECHAIN_DEPLOYER_KEY=${params.deployerPrivateKey}`,
124+
'-e',
125+
`BASECHAIN_WETH=${params.wethAddress}`,
126+
'-e',
127+
`GAS_LIMIT_FOR_L2_FACTORY_DEPLOYMENT=${params.gasLimitForL2FactoryDeployment ?? 10_000_000n}`,
128+
tokenBridgeContractsImage,
129+
'deploy:token-bridge-creator',
130+
];
131+
}
132+
53133
export function createDockerNetwork(networkName: string) {
54134
docker(['network', 'create', networkName]);
55135
}

0 commit comments

Comments
 (0)