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: Configure Fast Withdrawals using Safe #177

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
79 changes: 79 additions & 0 deletions examples/setup-fast-withdrawal/1-create_multisig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { createPublicClient, http, isAddress } from 'viem';
import {
createSafePrepareTransactionRequest,
} from '@arbitrum/orbit-sdk';
import { getParentChainFromId } from '@arbitrum/orbit-sdk/utils';
import { config } from 'dotenv';
import { propose } from './common.js';

config();

//check environment variables
if (typeof process.env.OWNER_1_ADDRESS_PRIVATE_KEY === 'undefined') {
throw new Error(`Please provide the "OWNER_1_ADDRESS_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.SAFE_ADDRESS === 'undefined') {
throw new Error(`Please provide the "SAFE_ADDRESS" environment variable`);
}

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

const rollupOwnerSafeAddress = process.env.SAFE_ADDRESS as `0x${string}`;
// // set the parent chain and create a public client for it
const parentChainId = Number(process.env.PARENT_CHAIN_ID);
const parentChain = getParentChainFromId(parentChainId);
const parentChainPublicClient = createPublicClient({
chain: parentChain,
transport: http(process.env.RPC),
});
// 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.`,
);
}

async function main() {
//
// 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: rollupOwnerSafeAddress,
owners: fcValidators,
threshold: safeWalletThreshold,
saltNonce: BigInt(Date.now())
});
propose(txRequest.to as string, txRequest.data as string, rollupOwnerSafeAddress);
//execute the transaction
//https://help.safe.global/en/articles/40834-verify-safe-creation
//in the executed transaction find `ProxyCreation` event
//Data singleton : <address> is what you're looing for
}

main();
117 changes: 117 additions & 0 deletions examples/setup-fast-withdrawal/2-add_validators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { createPublicClient, http, isAddress, Address } from 'viem';
import {
createRollupFetchTransactionHash,
createRollupPrepareTransactionReceipt,
rollupAdminLogicPublicActions,
} from '@arbitrum/orbit-sdk';
import { getParentChainFromId } from '@arbitrum/orbit-sdk/utils';
import { config } from 'dotenv';
import { propose } from './common.js';


config();

//check environment variables
if (typeof process.env.OWNER_1_ADDRESS_PRIVATE_KEY === 'undefined') {
throw new Error(`Please provide the "OWNER_1_ADDRESS_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.SAFE_ADDRESS === 'undefined') {
throw new Error(`Please provide the "SAFE_ADDRESS" environment variable`);
}

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

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

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

if (typeof process.env.RPC === 'undefined') {
throw new Error(`Please provide an "RPC" endpoint with unlimited eth_getLogs range`);
}

const rollupOwnerSafeAddress = process.env.SAFE_ADDRESS as `0x${string}`;
const safeAddress = process.env.FC_VALIDATORS_SAFE_ADDRESS as `0x${string}`;
const rollupAddress = process.env.ROLLUP_ADDRESS as Address;
// // set the parent chain and create a public client for it
const parentChainId = Number(process.env.PARENT_CHAIN_ID);
const parentChain = getParentChainFromId(parentChainId);
const parentChainPublicClient = createPublicClient({
chain: parentChain,
transport: http(process.env.RPC),
}).extend(
rollupAdminLogicPublicActions({
rollup: rollupAddress,
}),
);

// 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.`,
);
}

async function main() {
console.log('Add the new Safe address (created in step 1) as a validator');
fcValidators.push(safeAddress);

console.log('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 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: rollupOwnerSafeAddress,
});
propose(setValidatorTransactionRequest.to as string, setValidatorTransactionRequest.data as string, rollupOwnerSafeAddress);
}

main();
90 changes: 90 additions & 0 deletions examples/setup-fast-withdrawal/3-set-any-trust-fast-confirmer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { createPublicClient, http, Address, parseAbi } from 'viem';
import {
createRollupFetchTransactionHash,
createRollupPrepareTransactionReceipt,
rollupAdminLogicPublicActions,
setAnyTrustFastConfirmerPrepareTransactionRequest,
} from '@arbitrum/orbit-sdk';
import { getParentChainFromId } from '@arbitrum/orbit-sdk/utils';
import { config } from 'dotenv';
import { propose } from './common.js';


config();

//check environment variables
if (typeof process.env.OWNER_1_ADDRESS_PRIVATE_KEY === 'undefined') {
throw new Error(`Please provide the "OWNER_1_ADDRESS_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.SAFE_ADDRESS === 'undefined') {
throw new Error(`Please provide the "SAFE_ADDRESS" environment variable`);
}

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

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

if (typeof process.env.RPC === 'undefined') {
throw new Error(`Please provide an "RPC" endpoint with unlimited eth_getLogs range`);
}

const rollupOwnerSafeAddress = process.env.SAFE_ADDRESS as `0x${string}`;
const safeAddress = process.env.FC_VALIDATORS_SAFE_ADDRESS as `0x${string}`;
const rollupAddress = process.env.ROLLUP_ADDRESS as Address;
// // set the parent chain and create a public client for it
const parentChainId = Number(process.env.PARENT_CHAIN_ID);
const parentChain = getParentChainFromId(parentChainId);
const parentChainPublicClient = createPublicClient({
chain: parentChain,
transport: http(process.env.RPC),
}).extend(
rollupAdminLogicPublicActions({
rollup: rollupAddress,
}),
);

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

if (currentAnyTrustFastConfirmer.toLowerCase() !== safeAddress.toLowerCase()) {
console.log('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;
const setAnyTrustFastConfirmerTransactionRequest =
await setAnyTrustFastConfirmerPrepareTransactionRequest({
publicClient: parentChainPublicClient,
account: rollupOwnerSafeAddress,
rollup: rollupAddress,
upgradeExecutor: upgradeExecutorAddress,
fastConfirmer: safeAddress,
});
propose(setAnyTrustFastConfirmerTransactionRequest.to as string, setAnyTrustFastConfirmerTransactionRequest.data as string, rollupOwnerSafeAddress);

} else {
console.log(
`AnyTrust fast confirmer is already configured to ${currentAnyTrustFastConfirmer}. Skipping.`,
);
}
}

main();
Loading
Loading