Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
542fa73
feat: fast-withdrawal setup script (#143)
TucksonDev Jul 15, 2024
a2e7af0
refactor: bump @arbitrum/sdk from 4.0.0-alpha.4 to 4.0.0 (#151)
spsjvc Jul 17, 2024
e34d989
chore: update yarn.lock
spsjvc Jul 19, 2024
1e5a32b
chore: bump version
spsjvc Jul 19, 2024
86486d7
ci: update node modules install action (#154)
spsjvc Aug 5, 2024
264c397
ci: ignore elliptic advisories (#156)
fionnachan Aug 6, 2024
2808ec1
fix: convert big number to hex string first (#158)
spsjvc Aug 7, 2024
886e0b2
docs: update readme
spsjvc Aug 7, 2024
6d8f9e0
chore: bump version
spsjvc Aug 7, 2024
4e12681
feat: handle generating abi from proxy (#159)
spsjvc Aug 8, 2024
19b5a52
feat: organize contracts by file and version (#160)
spsjvc Aug 8, 2024
85ded16
feat: add AEP fee routing utils with examples (#80)
TucksonDev Aug 9, 2024
7f072f0
test: update implementation address in assertion (#161)
spsjvc Aug 12, 2024
e52980a
chore: omit patch number from contract version
spsjvc Aug 12, 2024
8522dd8
feat: remove deprecated createRollupPrepareConfig (#163)
spsjvc Aug 13, 2024
3266be7
feat: add support for RollupCreator v2.1 (#153)
spsjvc Aug 13, 2024
414d08a
feat: add utils for wasm module root (#155)
spsjvc Aug 13, 2024
b52e6d7
feat: use consensus v31 wasm module root (#164)
spsjvc Aug 13, 2024
c65c346
chore: bump version
spsjvc Aug 14, 2024
45745e8
feat: add getConsensusVersion util (#166)
spsjvc Aug 14, 2024
011748c
feat: update wasm utils as consensus release utils (#167)
spsjvc Aug 14, 2024
9dc8568
chore: update token bridge contracts (#165)
spsjvc Aug 14, 2024
b4634ba
feat: reduce config options in prepareChainConfig (#169)
spsjvc Aug 14, 2024
9b33e2f
chore: update yarn.lock
spsjvc Aug 15, 2024
143d415
chore: bump version
spsjvc Aug 15, 2024
057a7f0
feat: add nitro contracts v2.1 abis (#168)
spsjvc Aug 15, 2024
5f2b6ff
fix: properly export contracts (#170)
spsjvc Aug 16, 2024
f82d01e
chore: bump version
spsjvc Aug 16, 2024
36fd6fe
fix: add fast confirmer as validator (#171)
TucksonDev Aug 16, 2024
ced5193
feat: add SequencerInbox getters (#114)
chrstph-dvx Aug 19, 2024
e14d272
ci: pin nitro testnode to release (#172)
spsjvc Aug 20, 2024
3586145
chore: ignore GHSA-952p-6rrq-rcjv advisory (#174)
spsjvc Aug 26, 2024
4b6abaf
fix: make createRollupPrepareTransaction work with v1.1 (#173)
spsjvc Aug 26, 2024
3170519
feat: add SequencerInbox setters (#117)
chrstph-dvx Aug 27, 2024
e937f3c
build: bump packages and remove advisories (#175)
fionnachan Aug 27, 2024
e100388
feat: export buildSetIsBatchPoster (#176)
chrstph-dvx Aug 27, 2024
4e61b48
chore: bump version
spsjvc Aug 28, 2024
95d0a78
Feat: Add v1 RollupAdminLogic getters
chrstph-dvx Jun 27, 2024
be07399
Fetch rollupAddress from sequencerInbox in getters
chrstph-dvx Jul 2, 2024
ab36d2d
Update cache key
chrstph-dvx Jul 10, 2024
c2e8a55
Feat: Add v1 ArbOwnerPublic getters
chrstph-dvx Jun 28, 2024
02e6fdf
Feat: Add v1 ArbGasInfo getters
chrstph-dvx Jun 28, 2024
d0925ab
Use proper case for getParentBaseFeeEstimate
chrstph-dvx Jul 1, 2024
fdc7501
Feat: Add v1 upgradeExecutor getters
chrstph-dvx Jun 28, 2024
81a09e5
Feat: Add v1 SequencerInbox setters
chrstph-dvx Jun 27, 2024
795894a
Feat: Add v1 RollupAdminLogic setters
chrstph-dvx Jun 27, 2024
2dfedf9
Fetch rollupAddress from sequencerInbox in setters
chrstph-dvx Jul 2, 2024
e5d1cb9
Feat: Add v1 ArbOwnerPublic setters
chrstph-dvx Jun 28, 2024
8a219dd
Rename setMaxTxGasLimit
chrstph-dvx Jul 1, 2024
3e8811c
Feat: Add v1 upgradeExecutor setters
chrstph-dvx Jun 28, 2024
5beddc0
Feat: Add v1 publicActionsChildChain
chrstph-dvx Jul 1, 2024
3c5a929
Update getParentBaseFeeEstimate unit test
chrstph-dvx Jul 1, 2024
a326949
Feat: Add v1 publicActions for parent chain
chrstph-dvx Jun 27, 2024
ef6ab80
Change chains for unit test
chrstph-dvx Jul 10, 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
Next Next commit
feat: fast-withdrawal setup script (#143)
  • Loading branch information
TucksonDev authored Jul 15, 2024
commit 542fa73e3f381e73612ba87f9d1a567a5d64c7ac
14 changes: 14 additions & 0 deletions examples/setup-fast-withdrawal/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Private key of an account with executor privileges in the UpgradeExecutor admin contract for the chain.
CHAIN_OWNER_PRIVATE_KEY=

# Parent chain id
PARENT_CHAIN_ID=421614

# Address of the Rollup contract
ROLLUP_ADDRESS=

# Fast-confirmation validators (comma-separated array)
FC_VALIDATORS=["0x1234567890123456789012345678901234567890", "0x1234567890123456789012345678901234567891"]

# Minimum number of blocks that have to pass in between assertions (measured in L1 blocks)
MINIMUM_ASSERTION_PERIOD=75
54 changes: 54 additions & 0 deletions examples/setup-fast-withdrawal/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Setup a fast-withdrawal committee for your AnyTrust Orbit chain

This example script shows how to setup a fast-withdrawal committee for your AnyTrust Orbit chain.

## Rationale

Optimistic rollups must sustain a multi-day challenge period to allow time for fraud proofs. This delays finality for users and apps, resulting in multi-day withdrawal times and cross-chain communication delays.

Fast Withdrawals is a new configuration allowing Orbit chains to achieve fast finality. Orbit chains with Fast Withdrawals will have their transactions processed by a committee of validators. Transactions with a unanimous vote across the committee will have their state transition immediately confirmed.

This will allow:

- Orbit chains can configure a fast confirmation frequency (any time up to 15 minutes)
- User withdrawals to are confirmed on the parent chain at frequencies up to ~15 minutes
- Enhanced cross-chain communication by allowing cross-chain apps to read finalized state up to the fast confirmation frequency

## How it works

This script performs the following operations:

1. Create a new n/n Safe wallet with the specified validators as signers
2. Add the specified validators to the Rollup validators whitelist
3. Set the new Safe wallet as the anytrustFastConfirmer in the Rollup contract
4. Set the new minimumAssertionPeriod if needed
5. Show how to configure the batch poster and validator nodes

## Variables needed

You need to set the following environment variables in an .env file:

- CHAIN_OWNER_PRIVATE_KEY: private key of the account with executor privileges in the UpgradeExecutor admin contract for the chain. It will be the deployer of the multisig Safe wallet.
- PARENT_CHAIN_ID: chainId of the parent chain.
- ROLLUP_ADDRESS: address of the Rollup contract.
- FC_VALIDATORS: array of fast-withdrawal validators. They will be added as signers to the multisig Safe wallet, and will be added to the Rollup's validator whitelist.
- MINIMUM_ASSERTION_PERIOD: optional parameter. Minimum number of blocks that have to pass in between assertions (measured in L1 blocks).

## Setup

1. Install dependencies

```bash
yarn install
```

2. Create .env file and add the env vars

```bash
cp .env.example .env
```

3. Run the example
```bash
yarn dev
```
317 changes: 317 additions & 0 deletions examples/setup-fast-withdrawal/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
import { Chain, createPublicClient, http, isAddress, Address, parseAbi } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import {
createRollupFetchTransactionHash,
createRollupPrepareTransactionReceipt,
createSafePrepareTransactionReceipt,
createSafePrepareTransactionRequest,
rollupAdminLogicPublicActions,
setAnyTrustFastConfirmerPrepareTransactionRequest,
} from '@arbitrum/orbit-sdk';
import { sanitizePrivateKey, getParentChainFromId } from '@arbitrum/orbit-sdk/utils';
import { base, baseSepolia } from '@arbitrum/orbit-sdk/chains';
import { config } from 'dotenv';
config();

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

function getTimeDelayFromNumberOfBlocks(chainId: number, blocks: bigint): string {
// For Arbitrum L2s built on top of Ethereum, or Arbitrum L3s built on top of an Arbitrum L2, `block.number` always returns the L1 block number.
// L1 blocks are produced every 12 seconds.
//
// For Arbitrum L3s built on top of an OP Stack L2, `block.number` will return the L2 block number.
// L2 blocks in OP Stack chains are produced every 2 seconds.
const seconds = Number(
chainId === base.id || chainId === baseSepolia.id ? blocks * 2n : blocks * 12n,
);

const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = seconds % 60;
return `${h}h${m}m${s}s`;
}

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

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

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

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

// rollup address
const rollupAddress = process.env.ROLLUP_ADDRESS as Address;

// set the parent chain and create a public client for it
const parentChain = getParentChainFromId(Number(process.env.PARENT_CHAIN_ID));
const parentChainPublicClient = createPublicClient({
chain: parentChain,
transport: http(),
}).extend(
rollupAdminLogicPublicActions({
rollup: rollupAddress,
}),
);

// load the deployer account
const safeOwner = privateKeyToAccount(sanitizePrivateKey(process.env.CHAIN_OWNER_PRIVATE_KEY));

// sanitize validator addresses
const fcValidators = JSON.parse(process.env.FC_VALIDATORS);
const safeWalletThreshold = fcValidators.length;
if (!fcValidators) {
throw new Error(`The "FC_VALIDATORS" environment variable must be a valid array`);
}

const sanitizedFcValidators = [
...new Set(
fcValidators.filter((validator: `0x${string}`) =>
isAddress(validator) ? validator : undefined,
),
),
];
if (sanitizedFcValidators.length !== safeWalletThreshold) {
throw new Error(
`Some of the addresses in the "FC_VALIDATORS" environment variable appear to not be valid or duplicated.`,
);
}

// minimum assertion period
// (not required, by setting a default)
const minimumAssertionPeriod = BigInt(process.env.MINIMUM_ASSERTION_PERIOD || 75);

async function main() {
//
// Step 0. Gather necessary data (UpgradeExecutor address)
//
const transactionHash = await createRollupFetchTransactionHash({
rollup: rollupAddress,
publicClient: parentChainPublicClient,
});
const transactionReceipt = createRollupPrepareTransactionReceipt(
await parentChainPublicClient.getTransactionReceipt({ hash: transactionHash }),
);
const coreContracts = transactionReceipt.getCoreContracts();
const upgradeExecutorAddress = coreContracts.upgradeExecutor;

//
// Step 1. Create Safe multisig
//
console.log(
`Step 1: Create a new ${safeWalletThreshold}/${safeWalletThreshold} Safe wallet with the following addresses as signers:`,
fcValidators,
);
console.log('---');

const txRequest = await createSafePrepareTransactionRequest({
publicClient: parentChainPublicClient,
account: safeOwner,
owners: fcValidators,
threshold: safeWalletThreshold,
saltNonce: BigInt(Date.now()),
});

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

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

// get the safe address
const safeAddress = txReceipt.getSafeContract();

console.log(
`Safe wallet (${safeAddress}) deployed in transaction ${getBlockExplorerUrl(parentChain)}/tx/${
txReceipt.transactionHash
}`,
);
console.log('');

//
// Step 2. Add validators to the Orbit chain rollup validator whitelist
//
console.log(
`Step 2: Adding the following validators to the Rollup validator whitelist:`,
fcValidators,
);
console.log('---');

// prepare set validator transaction request
const fcValidatorsStatus = Array(fcValidators.length).fill(true);
const setValidatorTransactionRequest =
await parentChainPublicClient.rollupAdminLogicPrepareTransactionRequest({
functionName: 'setValidator',
args: [
fcValidators, // validator address list
fcValidatorsStatus, // validator status list
],
upgradeExecutor: upgradeExecutorAddress,
account: safeOwner.address,
});

// sign and send the transaction
const setValidatorTransactionHash = await parentChainPublicClient.sendRawTransaction({
serializedTransaction: await safeOwner.signTransaction(setValidatorTransactionRequest),
});

// wait for transaction receipt
const setValidatorTransactionReceipt = await parentChainPublicClient.waitForTransactionReceipt({
hash: setValidatorTransactionHash,
});

console.log(
`New validators added in ${getBlockExplorerUrl(parentChain)}/tx/${
setValidatorTransactionReceipt.transactionHash
}`,
);
console.log('');

//
// Step 3. Configure the multisig address as the `anyTrustFastConfirmer` on the rollup contract using `setAnyTrustFastConfirmer`
//
console.log(
`Step 3: Configure the multisig address as the anyTrustFastConfirmer : ${safeAddress}`,
);
console.log('---');

// get current anyTrustFastConfirmer
const currentAnyTrustFastConfirmer = await parentChainPublicClient.readContract({
address: rollupAddress,
abi: parseAbi(['function anyTrustFastConfirmer() view returns (address)']),
functionName: 'anyTrustFastConfirmer',
});

if (currentAnyTrustFastConfirmer.toLowerCase() !== safeAddress.toLowerCase()) {
// prepare transaction
const setAnyTrustFastConfirmerTransactionRequest =
await setAnyTrustFastConfirmerPrepareTransactionRequest({
publicClient: parentChainPublicClient,
account: safeOwner,
rollup: rollupAddress,
upgradeExecutor: upgradeExecutorAddress,
fastConfirmer: safeAddress,
});

// sign and send the transaction
const setAnyTrustFastConfirmerTransactionHash =
await parentChainPublicClient.sendRawTransaction({
serializedTransaction: await safeOwner.signTransaction(
setAnyTrustFastConfirmerTransactionRequest,
),
});

// wait for transaction receipt
const setAnyTrustFastConfirmerTransactionReceipt =
await parentChainPublicClient.waitForTransactionReceipt({
hash: setAnyTrustFastConfirmerTransactionHash,
});

console.log(
`New AnyTrust fast confirmer set in ${getBlockExplorerUrl(parentChain)}/tx/${
setAnyTrustFastConfirmerTransactionReceipt.transactionHash
}`,
);
} else {
console.log(
`AnyTrust fast confirmer is already configured to ${currentAnyTrustFastConfirmer}. Skipping.`,
);
}
console.log('');

//
// Step 4. Configure `minimumAssertionPeriod` on the rollup contract using `setMinimumAssertionPeriod`
//
console.log(
`Step 4: Configure the minimumAssertionPeriod in the Rollup contract to: ${Number(
minimumAssertionPeriod,
)}`,
);
console.log('---');

// get current minimumAssertionPeriod
const currentMinimumAssertionPeriod = await parentChainPublicClient.rollupAdminLogicReadContract({
functionName: 'minimumAssertionPeriod',
});

if (currentMinimumAssertionPeriod !== minimumAssertionPeriod) {
// prepare setMinimumAssertionPeriod transaction request
const setMinimumAssertionPeriodTransactionRequest =
await parentChainPublicClient.rollupAdminLogicPrepareTransactionRequest({
functionName: 'setMinimumAssertionPeriod',
args: [minimumAssertionPeriod],
upgradeExecutor: upgradeExecutorAddress,
account: safeOwner.address,
});

// sign and send the transaction
const setMinimumAssertionPeriodTransactionHash =
await parentChainPublicClient.sendRawTransaction({
serializedTransaction: await safeOwner.signTransaction(
setMinimumAssertionPeriodTransactionRequest,
),
});

// wait for transaction receipt
const setMinimumAssertionPeriodTransactionReceipt =
await parentChainPublicClient.waitForTransactionReceipt({
hash: setMinimumAssertionPeriodTransactionHash,
});

console.log(
`Minimum assertion period set in ${getBlockExplorerUrl(parentChain)}/tx/${
setMinimumAssertionPeriodTransactionReceipt.transactionHash
}`,
);
} else {
console.log(
`Minimum assertion period is already configured to ${currentMinimumAssertionPeriod}. Skipping.`,
);
}
console.log('');

//
// Step 5. Show a confirmation message with the next steps
//
console.log(`Step 5: Configure your batch poster and your validator nodes`);
console.log(`---`);
console.log(
'Your chain is now configured with a fast-withdrawal committee. The following instructions show how to configure your batch poster and your validators to start using this feature.',
);
console.log('');

// Batch poster configuration
const timeDelay = getTimeDelayFromNumberOfBlocks(parentChain.id, minimumAssertionPeriod);
console.log('Your batch poster has to run at least nitro v3.x');
console.log('Add the following parameter:');
console.log(`--node.batch-poster.max-delay=${timeDelay}`);
console.log('');

// Validator configuration
console.log('Your validators have to run at least nitro v3.x');
console.log('Add the following parameters:');
console.log(`--node.staker.enable-fast-confirmation=true`);
console.log(`--node.staker.fast-confirm-safe-address=${safeAddress}`);
console.log(`--node.staker.make-assertion-interval=${timeDelay}`);
console.log('');

// Final recommendation
console.log('Finally, restart your batch poster and validator nodes.');
}

main();
Loading