diff --git a/examples/setup-fast-withdrawal/.env.example b/examples/setup-fast-withdrawal-eoa/.env.example similarity index 100% rename from examples/setup-fast-withdrawal/.env.example rename to examples/setup-fast-withdrawal-eoa/.env.example diff --git a/examples/setup-fast-withdrawal/README.md b/examples/setup-fast-withdrawal-eoa/README.md similarity index 99% rename from examples/setup-fast-withdrawal/README.md rename to examples/setup-fast-withdrawal-eoa/README.md index d7e8663c..baba9ad5 100644 --- a/examples/setup-fast-withdrawal/README.md +++ b/examples/setup-fast-withdrawal-eoa/README.md @@ -52,3 +52,4 @@ You need to set the following environment variables in an .env file: ```bash yarn dev ``` + diff --git a/examples/setup-fast-withdrawal/index.ts b/examples/setup-fast-withdrawal-eoa/index.ts similarity index 100% rename from examples/setup-fast-withdrawal/index.ts rename to examples/setup-fast-withdrawal-eoa/index.ts diff --git a/examples/setup-fast-withdrawal/package.json b/examples/setup-fast-withdrawal-eoa/package.json similarity index 100% rename from examples/setup-fast-withdrawal/package.json rename to examples/setup-fast-withdrawal-eoa/package.json diff --git a/examples/setup-fast-withdrawal/tsconfig.json b/examples/setup-fast-withdrawal-eoa/tsconfig.json similarity index 100% rename from examples/setup-fast-withdrawal/tsconfig.json rename to examples/setup-fast-withdrawal-eoa/tsconfig.json diff --git a/examples/setup-fast-withdrawal-multisig/.env.example b/examples/setup-fast-withdrawal-multisig/.env.example new file mode 100644 index 00000000..aea0e8b2 --- /dev/null +++ b/examples/setup-fast-withdrawal-multisig/.env.example @@ -0,0 +1,57 @@ +#step 1 +#signer of the Safe multisignature contract that can propose a transaction +OWNER_1_ADDRESS_PRIVATE_KEY= +#parent chain id - 1 etheruem, 42161 arbitrum one +PARENT_CHAIN_ID= +#Safe multisignature contract address +SAFE_ADDRESS= +#stakers and validators that are whitelisted on the Rollup contract +FC_VALIDATORS='["0x1234567890123456789012345678901234567890"]' + +#step 2 +#Rollup contract address +ROLLUP_ADDRESS= +#Safe address that was created on step 1 +FC_VALIDATORS_SAFE_ADDRESS= +#RPC url +RPC= +#signer of the Safe multisignature contract that can propose a transaction +OWNER_1_ADDRESS_PRIVATE_KEY= +#parent chain id - 1 etheruem, 42161 arbitrum one +PARENT_CHAIN_ID= +#Safe multisignature contract address +SAFE_ADDRESS= +#stakers and validators that are whitelisted on the Rollup contract +FC_VALIDATORS='["0x1234567890123456789012345678901234567890"]' + +#step 3 +#Rollup contract address +ROLLUP_ADDRESS= +#add Safe address that was created on step 1 as fast-confirmer (setAnyTrustFastConfirmerPrepareTransactionRequest - fastConfirmer: safeAddress) +FC_VALIDATORS_SAFE_ADDRESS= +#RPC url +RPC= +#signer of the Safe multisignature contract that can propose a transaction +OWNER_1_ADDRESS_PRIVATE_KEY= +#parent chain id - 1 etheruem, 42161 arbitrum one +PARENT_CHAIN_ID= +#Safe multisignature contract address +SAFE_ADDRESS= +#stakers and validators that are whitelisted on the Rollup contract +FC_VALIDATORS='["0x1234567890123456789012345678901234567890"]' + +#step 4 +#Rollup contract address +ROLLUP_ADDRESS= +#Safe address that was created on step 1 +FC_VALIDATORS_SAFE_ADDRESS= +#RPC url +RPC= +#signer of the Safe multisignature contract that can propose a transaction +OWNER_1_ADDRESS_PRIVATE_KEY= +#parent chain id - 1 etheruem, 42161 arbitrum one +PARENT_CHAIN_ID= +#Safe multisignature contract address +SAFE_ADDRESS= +#default is 75 (15minutes) +MINIMUM_ASSERTION_PERIOD=1 diff --git a/examples/setup-fast-withdrawal-multisig/1-create_multisig.ts b/examples/setup-fast-withdrawal-multisig/1-create_multisig.ts new file mode 100644 index 00000000..a30d7334 --- /dev/null +++ b/examples/setup-fast-withdrawal-multisig/1-create_multisig.ts @@ -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 proxy :
is what you're looing for +} + +main(); diff --git a/examples/setup-fast-withdrawal-multisig/2-add_validators.ts b/examples/setup-fast-withdrawal-multisig/2-add_validators.ts new file mode 100644 index 00000000..30af8b25 --- /dev/null +++ b/examples/setup-fast-withdrawal-multisig/2-add_validators.ts @@ -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(); diff --git a/examples/setup-fast-withdrawal-multisig/3-set-any-trust-fast-confirmer.ts b/examples/setup-fast-withdrawal-multisig/3-set-any-trust-fast-confirmer.ts new file mode 100644 index 00000000..63ebb297 --- /dev/null +++ b/examples/setup-fast-withdrawal-multisig/3-set-any-trust-fast-confirmer.ts @@ -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(); diff --git a/examples/setup-fast-withdrawal-multisig/4-configure-minimum-assertion-period.ts b/examples/setup-fast-withdrawal-multisig/4-configure-minimum-assertion-period.ts new file mode 100644 index 00000000..1514e17c --- /dev/null +++ b/examples/setup-fast-withdrawal-multisig/4-configure-minimum-assertion-period.ts @@ -0,0 +1,148 @@ +import { createPublicClient, http, Address } from 'viem'; +import { + createRollupFetchTransactionHash, + createRollupPrepareTransactionReceipt, + rollupAdminLogicPublicActions, +} from '@arbitrum/orbit-sdk'; +import { base, baseSepolia } from '@arbitrum/orbit-sdk/chains'; +import { getParentChainFromId } from '@arbitrum/orbit-sdk/utils'; +import { config } from 'dotenv'; +import { propose } from './common.js'; + + +config(); + +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.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.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`); +} + +if (typeof process.env.MINIMUM_ASSERTION_PERIOD === 'undefined') { + console.log('MinimumAssertionPeriod set to 75') +} + +if (typeof process.env.FC_VALIDATORS_SAFE_ADDRESS === 'undefined') { + throw new Error(`Please provide the "FC_VALIDATORS_SAFE_ADDRESS" environment variable (run step 1)`); +} + +const minimumAssertionPeriod = BigInt(process.env.MINIMUM_ASSERTION_PERIOD || 75); +const rollupOwnerSafeAddress = process.env.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() { + // + // 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; + + 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: rollupOwnerSafeAddress, + }); + propose(setMinimumAssertionPeriodTransactionRequest.to as string, setMinimumAssertionPeriodTransactionRequest.data as string, rollupOwnerSafeAddress); + console.log('Transaction proposed'); + } 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.1.2'); + 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.1.2'); + console.log('Add the following parameters:'); + console.log(`--node.staker.enable-fast-confirmation=true`); + console.log(`--node.staker.make-assertion-interval=${timeDelay}`); + console.log(''); + + // Final recommendation + console.log('Finally, restart your batch poster and validator nodes.'); +} + +main(); diff --git a/examples/setup-fast-withdrawal-multisig/README.md b/examples/setup-fast-withdrawal-multisig/README.md new file mode 100644 index 00000000..87a001f7 --- /dev/null +++ b/examples/setup-fast-withdrawal-multisig/README.md @@ -0,0 +1,26 @@ + + +## Multisig ownership +Beware: At least one of the signers needs to be an EOA account so that it can propose transactions through this script. +If you want to use this script then you need to make sure the owner of the Rollup has been transfered to a Multisig contract and it has execution rights on L1 Executor Contract. + +1. Build this example: yarn dev +2. This step will create a new Safe on the parent chain and add fast confirmation validators as owners. +``` +node ./dist/1-create_multisig.js +``` +3. The validators list is expanded with the Safe created with step 1 (1-create_multisig). That's why you need to provide FC_VALIDATORS_SAFE_ADDRESS. +``` +node ./dist/2-add_validators.js +``` + +4. We also add Safe as `fast confirmer`. +``` +node ./dist/3-set-any-trust-fast-confirmer.js +``` + + +5. Configure minimum assertion period. The default is 75 (~15 minutes) +``` +node ./dist/4-configure-minimum-assertion-period.js +``` diff --git a/examples/setup-fast-withdrawal-multisig/common.ts b/examples/setup-fast-withdrawal-multisig/common.ts new file mode 100644 index 00000000..d5ee67a7 --- /dev/null +++ b/examples/setup-fast-withdrawal-multisig/common.ts @@ -0,0 +1,53 @@ +import { createPublicClient, http } from 'viem'; +import { getParentChainFromId, sanitizePrivateKey } from '@arbitrum/orbit-sdk/utils'; +import SafeApiKit from '@safe-global/api-kit' +import Safe from '@safe-global/protocol-kit' +import { privateKeyToAccount } from 'viem/accounts'; +import { + MetaTransactionData, + OperationType +} from '@safe-global/safe-core-sdk-types' + +export async function propose(to: string, data: string, rollupOwnerSafeAddress: string): Promise { + const parentChainId = Number(process.env.PARENT_CHAIN_ID); + const parentChain = getParentChainFromId(parentChainId); + let h = http() + if (typeof process.env.RPC !== 'undefined') { + h = http(process.env.RPC) + } + const parentChainPublicClient = createPublicClient({ + chain: parentChain, + transport: h, + }); + + const safeTransactionData: MetaTransactionData = { + to: to as `0x${string}`, + value: '0', + data: data as `0x${string}`, + operation: OperationType.Call + } + const protocolKitOwner1 = await Safe.default.init({ + provider: parentChainPublicClient.transport, + signer: process.env.OWNER_1_ADDRESS_PRIVATE_KEY as `${string}`, + safeAddress: process.env.SAFE_ADDRESS as `0x${string}`, + }) + const safeTransaction = await protocolKitOwner1.createTransaction({ + transactions: [safeTransactionData] + }) + // Propose transaction to the service + const chainId = BigInt(String(process.env.PARENT_CHAIN_ID)); + const apiKit = new SafeApiKit.default({ + chainId: chainId, // set the correct chainId + }) + const safeTxHash = await protocolKitOwner1.getTransactionHash(safeTransaction) + const signature = await protocolKitOwner1.signHash(safeTxHash) + const senderAddress = privateKeyToAccount(sanitizePrivateKey(process.env.OWNER_1_ADDRESS_PRIVATE_KEY as `${string}`)); + await apiKit.proposeTransaction({ + safeAddress: rollupOwnerSafeAddress, + safeTransactionData: safeTransaction.data, + safeTxHash, + senderAddress: senderAddress.address, + senderSignature: signature.data + }) + console.log('Transaction proposed.') +} diff --git a/examples/setup-fast-withdrawal-multisig/package.json b/examples/setup-fast-withdrawal-multisig/package.json new file mode 100644 index 00000000..1377971b --- /dev/null +++ b/examples/setup-fast-withdrawal-multisig/package.json @@ -0,0 +1,15 @@ +{ + "name": "setup-fast-withdrawal", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "tsc --outDir dist" + }, + "devDependencies": { + "@types/node": "^20.9.0", + "typescript": "^5.2.2", + "@safe-global/api-kit": "^2.4.5", + "@safe-global/safe-core-sdk-types": "^5.1.0" + } +} diff --git a/examples/setup-fast-withdrawal-multisig/tsconfig.json b/examples/setup-fast-withdrawal-multisig/tsconfig.json new file mode 100644 index 00000000..7101af0f --- /dev/null +++ b/examples/setup-fast-withdrawal-multisig/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.base.json", + "include": ["./**/*"], + "compilerOptions": { + "moduleResolution": "NodeNext", + "module": "NodeNext", + } +} diff --git a/src/createSafePrepareTransactionRequest.ts b/src/createSafePrepareTransactionRequest.ts index f8b3d406..0684ec46 100644 --- a/src/createSafePrepareTransactionRequest.ts +++ b/src/createSafePrepareTransactionRequest.ts @@ -77,7 +77,7 @@ export const SafeProxyFactoryAbi = [ */ export type CreateSafePrepareTransactionRequestParams = { publicClient: PublicClient; - account: PrivateKeyAccount; + account: Address; owners: Address[]; threshold: number; saltNonce?: bigint; @@ -92,7 +92,7 @@ export type CreateSafePrepareTransactionRequestParams = { publicClient: PublicClient; - account: PrivateKeyAccount; + account: Address; rollup: Address; upgradeExecutor: Address; fastConfirmer: Address; @@ -30,7 +30,7 @@ export type SetAnyTrustFastConfirmerPrepareTransactionRequestParams< * * @param {SetAnyTrustFastConfirmerPrepareTransactionRequestParams} setAnyTrustFastConfirmerPrepareTransactionRequestParams {@link SetAnyTrustFastConfirmerPrepareTransactionRequestParams} * @param {PublicClient} setAnyTrustFastConfirmerPrepareTransactionRequestParams.publicClient - A Viem Public Client - * @param {PrivateKeyAccount} setAnyTrustFastConfirmerPrepareTransactionRequestParams.account - The private key of the chain owner or an account with the executor role in the UpgradeExecutor + * @param {PrivateKeyAccount | `0x${string}`} setAnyTrustFastConfirmerPrepareTransactionRequestParams.account - The private key of the deployer of the new Safe or the address of the Safe * @param {Address} setAnyTrustFastConfirmerPrepareTransactionRequestParams.rollup - Address of the Rollup contract * @param {Address} setAnyTrustFastConfirmerPrepareTransactionRequestParams.upgradeExecutor - Address of the UpgradeExecutor contract * @param {Address} setAnyTrustFastConfirmerPrepareTransactionRequestParams.fastConfirmer - Address of the fast confirmer validator (usually a Safe multisig) diff --git a/yarn.lock b/yarn.lock index d5c76a35..819d6c3e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -808,7 +808,17 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz#5d694d345ce36b6ecf657349e03eb87297e68da4" integrity sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g== -"@safe-global/protocol-kit@^4.0.2": +"@safe-global/api-kit@^2.4.5": + version "2.4.5" + resolved "https://registry.yarnpkg.com/@safe-global/api-kit/-/api-kit-2.4.5.tgz#e2fb5d9b0949c8f8726e4e9f3343031ff38802b5" + integrity sha512-kMV6snDLSuoXLpEHKDbbURBda8iaMMDZp19uDj4d34smIUIPLE6XHpjXR+1S6i+GkaJH/kdIrCe6v4/ip/BjpQ== + dependencies: + "@safe-global/protocol-kit" "^4.1.0" + "@safe-global/safe-core-sdk-types" "^5.1.0" + ethers "^6.13.1" + node-fetch "^2.7.0" + +"@safe-global/protocol-kit@4.0.2", "@safe-global/protocol-kit@^4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@safe-global/protocol-kit/-/protocol-kit-4.0.2.tgz#5555e65359eeb5d210608aaa1ab889df15c14150" integrity sha512-csmBR22XY0Sgx2Q6WSdRiAPj5UlR3FxrMeoAqUbV7kCzT7SVXBwrsRqLjiW2+B59Dgcxs6fR8aLjl7maweBPXw== @@ -821,6 +831,20 @@ ethers "^6.13.1" semver "^7.6.2" +"@safe-global/protocol-kit@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@safe-global/protocol-kit/-/protocol-kit-4.1.0.tgz#8ab41e179c559840f0cd6b6ae296438dabe1793f" + integrity sha512-WAGXEn6UvKGlEYNqcWUasLZ4240sVWBg8T2SsfHoTs8Im0x2i48CNNZ5Mw9x+oKqhWs/Q9frNG6JcycN19LDRw== + dependencies: + "@noble/hashes" "^1.3.3" + "@safe-global/safe-core-sdk-types" "^5.1.0" + "@safe-global/safe-deployments" "^1.37.3" + "@safe-global/safe-modules-deployments" "^2.2.1" + abitype "^1.0.2" + ethereumjs-util "^7.1.5" + ethers "^6.13.1" + semver "^7.6.2" + "@safe-global/safe-core-sdk-types@^5.0.2": version "5.0.2" resolved "https://registry.yarnpkg.com/@safe-global/safe-core-sdk-types/-/safe-core-sdk-types-5.0.2.tgz#9552f5793581333c81676986b3eb19697e1c6627" @@ -828,6 +852,13 @@ dependencies: abitype "^1.0.2" +"@safe-global/safe-core-sdk-types@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@safe-global/safe-core-sdk-types/-/safe-core-sdk-types-5.1.0.tgz#af8d877b9af231242d023c7182f78ff4223bc3f4" + integrity sha512-UzXR4zWmVzux25FcIm4H049QhZZpVpIBL5HE+V0p5gHpArZROL+t24fZmsKUf403CtBxIJM5zZSVQL0nFJi+IQ== + dependencies: + abitype "^1.0.2" + "@safe-global/safe-deployments@^1.37.0": version "1.37.1" resolved "https://registry.yarnpkg.com/@safe-global/safe-deployments/-/safe-deployments-1.37.1.tgz#d8293c421b1b7445899aec720061be26bb3c4976" @@ -835,6 +866,18 @@ dependencies: semver "^7.6.2" +"@safe-global/safe-deployments@^1.37.3": + version "1.37.3" + resolved "https://registry.yarnpkg.com/@safe-global/safe-deployments/-/safe-deployments-1.37.3.tgz#ded9fa6bb04f0e8972c00c481badcf513d590b0b" + integrity sha512-EtbiOJVGe697+GcbHtfo75NYpp+hTlIIBqL2ETPLGoQBHoxo9HWbGX/6ZkVxsZv/NN4nKawyMi+MvpUkH9VXGg== + dependencies: + semver "^7.6.2" + +"@safe-global/safe-modules-deployments@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@safe-global/safe-modules-deployments/-/safe-modules-deployments-2.2.1.tgz#a8b88f2afc6ec04fed09968fe1e4990ed975c86e" + integrity sha512-H0XpusyXVcsTuRsQSq0FoBKqRfhZH87/1DrFEmXXPXmI3fJkvxq3KpTaTTqzcqoIe/J+erwVZQUYNfL68EcvAQ== + "@scure/base@~1.1.0", "@scure/base@~1.1.2": version "1.1.3" resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.3.tgz#8584115565228290a6c6c4961973e0903bb3df2f" @@ -2667,6 +2710,13 @@ node-domexception@^1.0.0: resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== +node-fetch@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + node-fetch@^3.3.0: version "3.3.2" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b" @@ -3450,6 +3500,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + ts-morph@^21.0.1: version "21.0.1" resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-21.0.1.tgz#712302a0f6e9dbf1aa8d9cf33a4386c4b18c2006" @@ -3645,6 +3700,19 @@ web-streams-polyfill@^3.0.3: resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"