Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
51 changes: 50 additions & 1 deletion .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,54 @@ jobs:
tags: |
${{ env.NITRO_CONTRACTS_GHCR_IMAGE }}

publish-token-bridge-contracts-image:
name: Publish Token Bridge Contracts Image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set token bridge contracts image
run: |
echo "TOKEN_BRIDGE_CONTRACTS_GHCR_IMAGE=ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/chain-sdk-token-bridge-contracts:v1.2.2" >> "$GITHUB_ENV"

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Check token bridge contracts image
id: check-image
run: |
if docker manifest inspect "${TOKEN_BRIDGE_CONTRACTS_GHCR_IMAGE}" >/dev/null 2>&1; then
echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "exists=false" >> "$GITHUB_OUTPUT"
fi

- name: Build and push token bridge contracts image
if: steps.check-image.outputs.exists != 'true'
uses: docker/build-push-action@v6
with:
context: ./token-bridge-contracts
file: ./token-bridge-contracts/Dockerfile
push: true
tags: |
${{ env.TOKEN_BRIDGE_CONTRACTS_GHCR_IMAGE }}

test-integration-anvil:
name: Test (Integration Anvil)
needs: publish-nitro-contracts-image
needs:
- publish-nitro-contracts-image
- publish-token-bridge-contracts-image
runs-on: ubuntu-latest
permissions:
contents: read
Expand All @@ -283,6 +328,7 @@ jobs:
- name: Set nitro contracts image
run: |
echo "NITRO_CONTRACTS_GHCR_IMAGE=ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/chain-sdk-nitro-contracts:v3.2.0-2f747c7" >> "$GITHUB_ENV"
echo "TOKEN_BRIDGE_CONTRACTS_GHCR_IMAGE=ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/chain-sdk-token-bridge-contracts:v1.2.2" >> "$GITHUB_ENV"

- name: Log in to GHCR
uses: docker/login-action@v3
Expand All @@ -294,6 +340,9 @@ jobs:
- name: Load nitro contracts image
run: docker pull "${NITRO_CONTRACTS_GHCR_IMAGE}"

- name: Load token bridge contracts image
run: docker pull "${TOKEN_BRIDGE_CONTRACTS_GHCR_IMAGE}"

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
Expand Down
19 changes: 18 additions & 1 deletion src/integrationTestHelpers/anvilHarness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import {
createAccount,
deployContract,
deployRollupCreator,
deployTokenBridgeCreator,
ensureCreate2Factory,
fundL2Deployer,
refreshForkTime,
Expand Down Expand Up @@ -455,6 +456,22 @@ export async function setupAnvilTestStack(): Promise<AnvilTestStack> {

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

console.log('Deploying L2 token bridge creator...');
const l2TokenBridgeCreator = await deployTokenBridgeCreator(
{
networkName: dockerNetworkName,
rpcUrl: `http://${l2ContainerName}:8449`,
deployerPrivateKey: harnessDeployer.privateKey,
wethAddress: customGasToken.address as Address,
},
{
publicClient: l2Client,
walletClient: blockAdvancerWalletClient,
account: blockAdvancerAccount,
},
);
console.log('L2 token bridge creator deployed\n');

await (
await customGasToken.deposit({
value: parseEther('10'),
Expand All @@ -475,7 +492,7 @@ export async function setupAnvilTestStack(): Promise<AnvilTestStack> {
testnet: true,
contracts: {
rollupCreator: { address: l2RollupCreator },
tokenBridgeCreator: { address: harnessDeployer.address },
tokenBridgeCreator: { address: l2TokenBridgeCreator },
weth: { address: customGasToken.address as Address },
},
});
Expand Down
79 changes: 37 additions & 42 deletions src/integrationTestHelpers/anvilHarnessHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ import { ethers } from 'ethers';

import { arbOwnerABI, arbOwnerAddress } from '../contracts/ArbOwner';
import { testConstants } from './constants';
import { dockerAsync, getNitroContractsImage } from './dockerHelpers';
import {
dockerAsync,
getNitroContractsImage,
getRollupCreatorDockerArgs,
getTokenBridgeContractsImage,
getTokenBridgeCreatorDockerArgs,
} from './dockerHelpers';
import type { PrivateKeyAccountWithPrivateKey } from '../testHelpers';

export type ContractArtifact = {
Expand All @@ -41,6 +47,14 @@ type DeployRollupCreatorParams = {
chainId: number;
};

type DeployTokenBridgeCreatorParams = {
networkName: string;
rpcUrl: string;
deployerPrivateKey: `0x${string}`;
wethAddress: Address;
gasLimitForL2FactoryDeployment?: bigint;
};

function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
Expand Down Expand Up @@ -68,41 +82,6 @@ async function getRequiredRetryableFunding(
};
}

function getRollupCreatorDockerArgs(
params: Omit<DeployRollupCreatorParams, 'blockAdvancer'>,
nitroContractsImage: string,
) {
return [
'run',
'--rm',
'--network',
params.networkName,
'-e',
`CUSTOM_RPC_URL=${params.rpcUrl}`,
'-e',
`CUSTOM_PRIVKEY=${params.deployerPrivateKey}`,
'-e',
`CUSTOM_CHAINID=${params.chainId}`,
'-e',
`FACTORY_OWNER=${params.factoryOwner}`,
'-e',
`MAX_DATA_SIZE=${params.maxDataSize}`,
'-e',
`POLLING_INTERVAL=${testConstants.NITRO_DEPLOY_POLLING_INTERVAL_MS}`,
'-e',
'DISABLE_VERIFICATION=true',
'-e',
'IGNORE_MAX_DATA_SIZE_WARNING=true',
nitroContractsImage,
'hardhat',
'run',
'--no-compile',
'scripts/deployment.ts',
'--network',
'custom',
];
}

function parseRollupCreatorAddress(stdout: string): Address {
const match = stdout.match(/\* RollupCreator created at address: (0x[0-9a-fA-F]{40})/);
if (!match) {
Expand All @@ -112,6 +91,17 @@ function parseRollupCreatorAddress(stdout: string): Address {
return match[1] as Address;
}

function parseTokenBridgeCreatorAddress(stdout: string): Address {
const match = stdout.match(/L1TokenBridgeCreator:\s*(0x[0-9a-fA-F]{40})/);
if (!match) {
throw new Error(
`Failed to parse TokenBridgeCreator address from token bridge deploy output:\n${stdout}`,
);
}

return match[1] as Address;
}

export function createAccount(privateKey?: Hex): PrivateKeyAccountWithPrivateKey {
const normalizedPrivateKey = privateKey ?? generatePrivateKey();
return {
Expand Down Expand Up @@ -345,7 +335,6 @@ export async function deployRollupCreator(
blockAdvancer: BlockAdvancer,
): Promise<Address> {
const nitroContractsImage = getNitroContractsImage();
const dockerRunStartedAt = Date.now();
const stdout = await withBackgroundBlockAdvancing(blockAdvancer, () =>
dockerAsync(
getRollupCreatorDockerArgs(
Expand All @@ -361,11 +350,17 @@ export async function deployRollupCreator(
),
),
);
console.log(
`[${new Date().toISOString()}] rollup creator docker run finished after ${
Date.now() - dockerRunStartedAt
}ms`,
);

return parseRollupCreatorAddress(stdout);
}

export async function deployTokenBridgeCreator(
params: DeployTokenBridgeCreatorParams,
blockAdvancer: BlockAdvancer,
): Promise<Address> {
const tokenBridgeContractsImage = getTokenBridgeContractsImage();
const stdout = await withBackgroundBlockAdvancing(blockAdvancer, () =>
dockerAsync(getTokenBridgeCreatorDockerArgs(params, tokenBridgeContractsImage)),
);
return parseTokenBridgeCreatorAddress(stdout);
}
2 changes: 2 additions & 0 deletions src/integrationTestHelpers/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export const testConstants = {
DEFAULT_ANVIL_IMAGE: 'ghcr.io/foundry-rs/foundry:v1.3.1',
DEFAULT_NITRO_IMAGE: 'offchainlabs/nitro-node:v3.9.5-66e42c4',
DEFAULT_NITRO_CONTRACTS_IMAGE: 'ghcr.io/offchainlabs/chain-sdk-nitro-contracts:v3.2.0-2f747c7',
DEFAULT_TOKEN_BRIDGE_CONTRACTS_IMAGE:
'ghcr.io/offchainlabs/chain-sdk-token-bridge-contracts:v1.2.5',
DEPLOYER_PRIVATE_KEY:
'0x490d84b7602e4b470af4f86a3ad095607a8bb5a4fa8ba148f41fcfd236b4fdf5' as Address,
DEFAULT_L2_CHAIN_ID: 421_337,
Expand Down
88 changes: 87 additions & 1 deletion src/integrationTestHelpers/dockerHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { execFile, execFileSync } from 'node:child_process';
import { rmSync } from 'node:fs';
import { join } from 'node:path';

import { createPublicClient, http } from 'viem';
import { createPublicClient, http, Address } from 'viem';

import { testConstants } from './constants';

Expand Down Expand Up @@ -51,6 +51,92 @@ export function getNitroContractsImage(): string {
}
}

export function getTokenBridgeContractsImage(): string {
const image =
process.env.TOKEN_BRIDGE_CONTRACTS_GHCR_IMAGE ??
testConstants.DEFAULT_TOKEN_BRIDGE_CONTRACTS_IMAGE;
try {
docker(['image', 'inspect', image]);
return image;
} catch {
const tokenBridgeContractsDir = join(process.cwd(), 'token-bridge-contracts');
const dockerfilePath = join(tokenBridgeContractsDir, 'Dockerfile');
docker(['build', '-f', dockerfilePath, '-t', image, tokenBridgeContractsDir]);

return image;
}
}

export function getRollupCreatorDockerArgs(
params: {
networkName: string;
rpcUrl: string;
deployerPrivateKey: `0x${string}`;
factoryOwner: Address;
maxDataSize: number;
chainId: number;
},
nitroContractsImage: string,
) {
return [
'run',
'--rm',
'--network',
params.networkName,
'-e',
`CUSTOM_RPC_URL=${params.rpcUrl}`,
'-e',
`CUSTOM_PRIVKEY=${params.deployerPrivateKey}`,
'-e',
`CUSTOM_CHAINID=${params.chainId}`,
'-e',
`FACTORY_OWNER=${params.factoryOwner}`,
'-e',
`MAX_DATA_SIZE=${params.maxDataSize}`,
'-e',
`POLLING_INTERVAL=${testConstants.NITRO_DEPLOY_POLLING_INTERVAL_MS}`,
'-e',
'DISABLE_VERIFICATION=true',
'-e',
'IGNORE_MAX_DATA_SIZE_WARNING=true',
nitroContractsImage,
'hardhat',
'run',
'--no-compile',
'scripts/deployment.ts',
'--network',
'custom',
];
}

export function getTokenBridgeCreatorDockerArgs(
params: {
networkName: string;
rpcUrl: string;
deployerPrivateKey: `0x${string}`;
wethAddress: Address;
gasLimitForL2FactoryDeployment?: bigint;
},
tokenBridgeContractsImage: string,
) {
return [
'run',
'--rm',
'--network',
params.networkName,
'-e',
`BASECHAIN_RPC=${params.rpcUrl}`,
'-e',
`BASECHAIN_DEPLOYER_KEY=${params.deployerPrivateKey}`,
'-e',
`BASECHAIN_WETH=${params.wethAddress}`,
'-e',
`GAS_LIMIT_FOR_L2_FACTORY_DEPLOYMENT=${params.gasLimitForL2FactoryDeployment ?? 10_000_000n}`,
tokenBridgeContractsImage,
'deploy:token-bridge-creator',
];
}

export function createDockerNetwork(networkName: string) {
docker(['network', 'create', networkName]);
}
Expand Down
Loading