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: create token bridge #34

Merged
merged 57 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
57 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
b3ff466
Adjust default value for token bridge creation
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
5da1247
Bump viem peerDependency
TucksonDev Feb 8, 2024
596c5b3
Refactor retryable gas overrides
TucksonDev Feb 12, 2024
f23b328
Restoring doubling submission cost in ethers wrapper
TucksonDev Feb 12, 2024
2aae25d
Merge branch 'main' into feat-token-bridge-creator
TucksonDev Feb 12, 2024
e3c1545
Smol fix from merge
TucksonDev Feb 12, 2024
0ede075
Reformat retryable gas overrides
TucksonDev Feb 12, 2024
7868069
Remove unused import
TucksonDev Feb 12, 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
5 changes: 3 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
ARBISCAN_API_KEY=
PRIVATE_KEY=0xb6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659

NITRO_TESTNODE_DEPLOYER_PRIVATE_KEY=0xb6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659
NITRO_TESTNODE_L2_ROLLUP_OWNER_PRIVATE_KEY=0xdc04c5399f82306ec4b4d654a342f40e2e0620fe39950d967e1e574b32d4dd36
NITRO_TESTNODE_L3_ROLLUP_OWNER_PRIVATE_KEY=0xecdf21cb41c65afb51f91df408b7656e2c8739a5877f2814add0afd780cc210e
9 changes: 4 additions & 5 deletions .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,13 @@ jobs:
uses: OffchainLabs/actions/run-nitro-test-node@main
with:
nitro-testnode-ref: release
# don't deploy the regular token bridge using arbitrum-sdk, as it will be deployed through the TokenBridgeCreator
no-token-bridge: true
# Use simple mode
no-simple: false
TucksonDev marked this conversation as resolved.
Show resolved Hide resolved
# RollupCreator on L1 is deployed by default
# RollupCreator on L2 is deployed with --l3node
l3-node: true
# TokenBridgeCreator on L1 is deployed with --tokenbridge
# TokenBridgeCreator on L2 is deployed with --l3-token-bridge
args: --tokenbridge --l3-token-bridge
# L3 node with custom fee token when using --l3-fee-token
args: --l3-fee-token

- name: Copy .env
run: cp ./.env.example ./.env
Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@ Make sure you are using Node v18 or greater.
yarn add @arbitrum/orbit-sdk viem
```

## Run integration tests

Clone the branch `main` of [nitro-testnode](https://github.com/OffchainLabs/nitro-testnode), and run the testnode using the following arguments:

```bash
./test-node.bash --init --tokenbridge --l3node --l3-fee-token --l3-token-bridge
```

Then, run the integration tests:

```bash
yarn test:integration
```

## Examples

See [examples](./examples).
13 changes: 13 additions & 0 deletions examples/create-token-bridge-custom-fee-token/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Address of the Rollup contract
ROLLUP_ADDRESS=
TucksonDev marked this conversation as resolved.
Show resolved Hide resolved

# Private key of the account with executor privileges in the UpgradeExecutor admin contract for the chain
# (usually, the deployer of the chain)
ROLLUP_OWNER_PRIVATE_KEY=

# Custom fee token address on the parent chain
CUSTOM_FEE_TOKEN_ADDRESS=

# Orbit chain configuration
ORBIT_CHAIN_ID=
ORBIT_CHAIN_RPC=
128 changes: 128 additions & 0 deletions examples/create-token-bridge-custom-fee-token/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { Chain, createPublicClient, http, Address, defineChain } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { arbitrumSepolia } from 'viem/chains';
import {
createTokenBridgeEnoughCustomFeeTokenAllowance,
createTokenBridgePrepareCustomFeeTokenApprovalTransactionRequest,
createTokenBridgePrepareTransactionRequest,
createTokenBridgePrepareTransactionReceipt,
} from '@arbitrum/orbit-sdk';
import { config } from 'dotenv';
config();

function sanitizePrivateKey(privateKey: string): `0x${string}` {
if (!privateKey.startsWith('0x')) {
return `0x${privateKey}`;
}

return privateKey as `0x${string}`;
}

function getBlockExplorerUrl(chain: Chain) {
return chain.blockExplorers?.default.url;
}

if (typeof process.env.ROLLUP_ADDRESS === 'undefined') {
throw new Error(`Please provide the "ROLLUP_ADDRESS" environment variable`);
}

if (typeof process.env.ROLLUP_OWNER_PRIVATE_KEY === 'undefined') {
throw new Error(`Please provide the "ROLLUP_OWNER_PRIVATE_KEY" environment variable`);
}

if (typeof process.env.ORBIT_CHAIN_ID === 'undefined') {
throw new Error(`Please provide the "ORBIT_CHAIN_ID" environment variable`);
}

if (typeof process.env.ORBIT_CHAIN_RPC === 'undefined') {
throw new Error(`Please provide the "ORBIT_CHAIN_RPC" environment variable`);
}

if (typeof process.env.CUSTOM_FEE_TOKEN_ADDRESS === 'undefined') {
throw new Error(`Please provide the "CUSTOM_FEE_TOKEN_ADDRESS" environment variable`);
}

// set the parent chain and create a public client for it
const parentChain = arbitrumSepolia;
const parentChainPublicClient = createPublicClient({
chain: parentChain,
transport: http(),
});

// define chain config for the orbit chain
const orbitChain = defineChain({
id: Number(process.env.ORBIT_CHAIN_ID),
network: 'Orbit chain',
name: 'orbit',
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
rpcUrls: {
default: {
http: [process.env.ORBIT_CHAIN_RPC],
},
public: {
http: [process.env.ORBIT_CHAIN_RPC],
},
},
testnet: true,
});
const orbitChainPublicClient = createPublicClient({ chain: orbitChain, transport: http() });

// load the rollup owner account
const rollupOwner = privateKeyToAccount(sanitizePrivateKey(process.env.ROLLUP_OWNER_PRIVATE_KEY));

async function main() {
// set the custom fee token
const nativeToken: Address = process.env.CUSTOM_FEE_TOKEN_ADDRESS as `0x${string}`;

// prepare transaction to approve custom fee token spend
const allowanceParams = {
nativeToken,
owner: rollupOwner.address,
publicClient: parentChainPublicClient,
};
if (!(await createTokenBridgeEnoughCustomFeeTokenAllowance(allowanceParams))) {
const approvalTxRequest =
await createTokenBridgePrepareCustomFeeTokenApprovalTransactionRequest(allowanceParams);

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

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

console.log(
`Tokens approved in ${getBlockExplorerUrl(parentChain)}/tx/${
approvalTxReceipt.transactionHash
}`,
);
}

// prepare the transaction for deploying the core contracts
const txRequest = await createTokenBridgePrepareTransactionRequest({
params: {
rollup: process.env.ROLLUP_ADDRESS as `0x${string}`,
rollupOwner: rollupOwner.address,
},
parentChainPublicClient,
orbitChainPublicClient,
account: rollupOwner.address,
});

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

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

TucksonDev marked this conversation as resolved.
Show resolved Hide resolved
console.log(`Deployed in ${getBlockExplorerUrl(parentChain)}/tx/${txReceipt.transactionHash}`);
}

main();
13 changes: 13 additions & 0 deletions examples/create-token-bridge-custom-fee-token/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "create-token-bridge-custom-fee-token",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "tsc --outDir dist && node ./dist/index.js"
},
"devDependencies": {
"@types/node": "^20.9.0",
"typescript": "^5.2.2"
}
}
4 changes: 4 additions & 0 deletions examples/create-token-bridge-custom-fee-token/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "../tsconfig.base.json",
"include": ["./**/*"]
}
10 changes: 10 additions & 0 deletions examples/create-token-bridge-eth/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Address of the Rollup contract
ROLLUP_ADDRESS=

# Private key of the account with executor privileges in the UpgradeExecutor admin contract for the chain
# (usually, the deployer of the chain)
ROLLUP_OWNER_PRIVATE_KEY=

# Orbit chain configuration
ORBIT_CHAIN_ID=
ORBIT_CHAIN_RPC=
89 changes: 89 additions & 0 deletions examples/create-token-bridge-eth/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { Chain, createPublicClient, http, defineChain } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { arbitrumSepolia } from 'viem/chains';
import {
createTokenBridgePrepareTransactionReceipt,
createTokenBridgePrepareTransactionRequest,
} from '@arbitrum/orbit-sdk';
import { config } from 'dotenv';
config();

function sanitizePrivateKey(privateKey: string): `0x${string}` {
if (!privateKey.startsWith('0x')) {
return `0x${privateKey}`;
}

return privateKey as `0x${string}`;
}

function getBlockExplorerUrl(chain: Chain) {
return chain.blockExplorers?.default.url;
}

if (typeof process.env.ROLLUP_ADDRESS === 'undefined') {
throw new Error(`Please provide the "ROLLUP_ADDRESS" environment variable`);
}

if (typeof process.env.ROLLUP_OWNER_PRIVATE_KEY === 'undefined') {
throw new Error(`Please provide the "ROLLUP_OWNER_PRIVATE_KEY" environment variable`);
}

if (typeof process.env.ORBIT_CHAIN_ID === 'undefined') {
throw new Error(`Please provide the "ORBIT_CHAIN_ID" environment variable`);
}

if (typeof process.env.ORBIT_CHAIN_RPC === 'undefined') {
throw new Error(`Please provide the "ORBIT_CHAIN_RPC" environment variable`);
}

// set the parent chain and create a public client for it
const parentChain = arbitrumSepolia;
const parentChainPublicClient = createPublicClient({ chain: parentChain, transport: http() });

// define chain config for the orbit chain
const orbitChain = defineChain({
id: Number(process.env.ORBIT_CHAIN_ID),
network: 'Orbit chain',
name: 'orbit',
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
rpcUrls: {
default: {
http: [process.env.ORBIT_CHAIN_RPC],
},
public: {
http: [process.env.ORBIT_CHAIN_RPC],
},
},
testnet: true,
});
const orbitChainPublicClient = createPublicClient({ chain: orbitChain, transport: http() });

// load the rollup owner account
const rollupOwner = privateKeyToAccount(sanitizePrivateKey(process.env.ROLLUP_OWNER_PRIVATE_KEY));

async function main() {
// prepare the transaction for creating the token bridge
const txRequest = await createTokenBridgePrepareTransactionRequest({
params: {
rollup: process.env.ROLLUP_ADDRESS as `0x${string}`,
rollupOwner: rollupOwner.address,
},
parentChainPublicClient,
orbitChainPublicClient,
account: rollupOwner.address,
});

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

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

console.log(`Deployed in ${getBlockExplorerUrl(parentChain)}/tx/${txReceipt.transactionHash}`);
TucksonDev marked this conversation as resolved.
Show resolved Hide resolved
}

main();
13 changes: 13 additions & 0 deletions examples/create-token-bridge-eth/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "create-token-bridge-eth",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "tsc --outDir dist && node ./dist/index.js"
},
"devDependencies": {
"@types/node": "^20.9.0",
"typescript": "^5.2.2"
}
}
4 changes: 4 additions & 0 deletions examples/create-token-bridge-eth/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "../tsconfig.base.json",
"include": ["./**/*"]
}
27 changes: 25 additions & 2 deletions src/chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,29 @@ const nitroTestnodeL2 = defineChain({
testnet: true,
});

export const chains = [sepolia, arbitrumSepolia, nitroTestnodeL1, nitroTestnodeL2] as const;
// the L3 deployed as part of the nitro-testnode framework uses a custom fee token
const nitroTestnodeL3 = defineChain({
id: 333333,
network: 'Nitro Testnode L3',
name: 'nitro-testnode-l3',
nativeCurrency: { name: 'AppTestToken', symbol: 'APP', decimals: 18 },
rpcUrls: {
default: {
http: ['http://127.0.0.1:3347'],
},
public: {
http: ['http://127.0.0.1:3347'],
},
},
testnet: true,
});

export const chains = [
sepolia,
arbitrumSepolia,
nitroTestnodeL1,
nitroTestnodeL2,
nitroTestnodeL3,
] as const;

export { sepolia, arbitrumSepolia, nitroTestnodeL1, nitroTestnodeL2 };
export { sepolia, arbitrumSepolia, nitroTestnodeL1, nitroTestnodeL2, nitroTestnodeL3 };
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { parseEther } from 'viem';

export const deterministicFactoriesDeploymentEstimatedFees = parseEther(String('0.125'));
export const createTokenBridgeDefaultRetryablesFees = parseEther('0.02');
export const createTokenBridgeDefaultGasLimit = 5_000_000n;
Loading
Loading