diff --git a/examples/set-new-validators/index.ts b/examples/set-new-validators/index.ts index d8858e7b..d26f38b7 100644 --- a/examples/set-new-validators/index.ts +++ b/examples/set-new-validators/index.ts @@ -5,6 +5,8 @@ import { createRollupFetchTransactionHash, createRollupPrepareTransactionReceipt, rollupAdminLogicPublicActions, + // Uncomment it when you want to use getValidators() to get validator status + // getValidators, } from '@arbitrum/orbit-sdk'; import { sanitizePrivateKey } from '@arbitrum/orbit-sdk/utils'; import { config } from 'dotenv'; @@ -76,6 +78,17 @@ async function main() { `Before executing, the address ${newValidators[0]} status in validator list is ${beforeStatus}`, ); + /* + You can also use the following code to check validator status, it will return a list + of whitelist validators. + + console.log('Fetching current validator address list in the parent chain...'); + const beforeValidatorList = await getValidators(parentChainPublicClient, { + rollup: coreContracts.rollup, + }); + console.log(`Before executing, the validator list is ${beforeValidatorList.validators}`); + */ + // prepare set validator transaction request const setValidatorTransactionRequest = await parentChainPublicClient.rollupAdminLogicPrepareTransactionRequest({ diff --git a/examples/setup-fast-withdrawal/index.ts b/examples/setup-fast-withdrawal/index.ts index f8ad21e3..2ca146c6 100644 --- a/examples/setup-fast-withdrawal/index.ts +++ b/examples/setup-fast-withdrawal/index.ts @@ -301,16 +301,15 @@ async function main() { // Batch poster configuration const timeDelay = getTimeDelayFromNumberOfBlocks(parentChain.id, minimumAssertionPeriod); - console.log('Your batch poster has to run at least nitro v3.1.1'); + 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.1'); + 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.fast-confirm-safe-address=${safeAddress}`); console.log(`--node.staker.make-assertion-interval=${timeDelay}`); console.log(''); diff --git a/src/actions/buildInvalidateKeysetHash.ts b/src/actions/buildInvalidateKeysetHash.ts index 30b55039..fdc29550 100644 --- a/src/actions/buildInvalidateKeysetHash.ts +++ b/src/actions/buildInvalidateKeysetHash.ts @@ -28,10 +28,14 @@ export type BuildInvalidateKeysetHashReturnType = PrepareTransactionRequestRetur export async function buildInvalidateKeysetHash( client: PublicClient, - params: BuildInvalidateKeysetHashParameters, + { + account, + upgradeExecutor, + sequencerInbox: sequencerInboxAddress, + params, + }: BuildInvalidateKeysetHashParameters, ): Promise { const validatedPublicClient = validateParentChainPublicClient(client); - const { account, upgradeExecutor, sequencerInbox: sequencerInboxAddress, ...args } = params; const request = await client.prepareTransactionRequest({ chain: client.chain, @@ -39,9 +43,9 @@ export async function buildInvalidateKeysetHash( client: PublicClient, - params: BuildSetIsBatchPosterParameters & { enable: boolean }, + { + account, + upgradeExecutor, + sequencerInbox: sequencerInboxAddress, + params, + }: BuildSetIsBatchPosterParameters & { params: { enable: boolean } }, ): Promise { const validatedPublicClient = validateParentChainPublicClient(client); - const { account, upgradeExecutor, sequencerInbox: sequencerInboxAddress, ...args } = params; const request = await client.prepareTransactionRequest({ chain: client.chain, @@ -33,7 +37,7 @@ export async function buildSetIsBatchPoster( ...prepareUpgradeExecutorCallParameters({ to: sequencerInboxAddress, upgradeExecutor, - args: [args.batchPoster, args.enable], + args: [params.batchPoster, params.enable], abi: sequencerInboxABI, functionName: 'setIsBatchPoster', }), @@ -48,7 +52,10 @@ export async function buildEnableBatchPoster( ): Promise { return buildSetIsBatchPoster(client, { ...args, - enable: true, + params: { + ...args.params, + enable: true, + }, }); } @@ -58,6 +65,9 @@ export async function buildDisableBatchPoster( ): Promise { return buildSetIsBatchPoster(client, { ...args, - enable: false, + params: { + ...args.params, + enable: false, + }, }); } diff --git a/src/actions/buildSetMaxTimeVariation.ts b/src/actions/buildSetMaxTimeVariation.ts index aef110fa..3fad2176 100644 --- a/src/actions/buildSetMaxTimeVariation.ts +++ b/src/actions/buildSetMaxTimeVariation.ts @@ -24,10 +24,14 @@ export type BuildSetMaxTimeVariationReturnType = PrepareTransactionRequestReturn export async function buildSetMaxTimeVariation( client: PublicClient, - params: BuildSetMaxTimeVariationParameters, + { + account, + upgradeExecutor, + sequencerInbox: sequencerInboxAddress, + params, + }: BuildSetMaxTimeVariationParameters, ): Promise { const validatedPublicClient = validateParentChainPublicClient(client); - const { account, upgradeExecutor, sequencerInbox: sequencerInboxAddress, ...args } = params; const request = await client.prepareTransactionRequest({ chain: client.chain, @@ -35,7 +39,7 @@ export async function buildSetMaxTimeVariation ...prepareUpgradeExecutorCallParameters({ to: sequencerInboxAddress, upgradeExecutor, - args: [args], + args: [params], abi: sequencerInboxABI, functionName: 'setMaxTimeVariation', }), diff --git a/src/actions/buildSetValidKeyset.ts b/src/actions/buildSetValidKeyset.ts index c690a41c..666b1fda 100644 --- a/src/actions/buildSetValidKeyset.ts +++ b/src/actions/buildSetValidKeyset.ts @@ -28,10 +28,14 @@ export type BuildSetValidKeysetReturnType = PrepareTransactionRequestReturnTypeW export async function buildSetValidKeyset( client: PublicClient, - params: BuildSetValidKeysetParameters, + { + account, + upgradeExecutor, + sequencerInbox: sequencerInboxAddress, + params, + }: BuildSetValidKeysetParameters, ): Promise { const validatedPublicClient = validateParentChainPublicClient(client); - const { account, upgradeExecutor, sequencerInbox: sequencerInboxAddress, ...args } = params; const request = await client.prepareTransactionRequest({ chain: client.chain, @@ -39,7 +43,7 @@ export async function buildSetValidKeyset( ...prepareUpgradeExecutorCallParameters({ to: sequencerInboxAddress, upgradeExecutor, - args: [args.keyset], + args: [params.keyset], abi: sequencerInboxABI, functionName: 'setValidKeyset', }), diff --git a/src/actions/getMaxTimeVariation.ts b/src/actions/getMaxTimeVariation.ts index b0bfe82c..4924a757 100644 --- a/src/actions/getMaxTimeVariation.ts +++ b/src/actions/getMaxTimeVariation.ts @@ -17,12 +17,12 @@ export type GetMaxTimeVariationReturnType = { export async function getMaxTimeVariation( client: PublicClient, - args: GetMaxTimeVariationParameters, + { sequencerInbox }: GetMaxTimeVariationParameters, ): Promise { const [delayBlocks, futureBlocks, delaySeconds, futureSeconds] = await client.readContract({ abi: sequencerInboxABI, functionName: 'maxTimeVariation', - address: args.sequencerInbox, + address: sequencerInbox, }); return { delayBlocks, diff --git a/src/actions/isBatchPoster.ts b/src/actions/isBatchPoster.ts index f71b773f..39e24894 100644 --- a/src/actions/isBatchPoster.ts +++ b/src/actions/isBatchPoster.ts @@ -18,12 +18,12 @@ export type IsBatchPosterReturnType = ReadContractReturnType< export async function isBatchPoster( client: PublicClient, - args: IsBatchPosterParameters, + { sequencerInbox, params }: IsBatchPosterParameters, ): Promise { return client.readContract({ abi: sequencerInboxABI, functionName: 'isBatchPoster', - address: args.sequencerInbox, - args: [args.batchPoster], + address: sequencerInbox, + args: [params.batchPoster], }); } diff --git a/src/actions/isValidKeysetHash.ts b/src/actions/isValidKeysetHash.ts index 1a5e7007..3ef15828 100644 --- a/src/actions/isValidKeysetHash.ts +++ b/src/actions/isValidKeysetHash.ts @@ -19,12 +19,12 @@ export type IsValidKeysetHashReturnType = ReadContractReturnType< export async function isValidKeysetHash( client: PublicClient, - args: IsValidKeysetHashParameters, + { sequencerInbox, params }: IsValidKeysetHashParameters, ): Promise { return client.readContract({ abi: sequencerInboxABI, functionName: 'isValidKeysetHash', - address: args.sequencerInbox, - args: [args.keysetHash], + address: sequencerInbox, + args: [params.keysetHash], }); } diff --git a/src/actions/sequencerInbox.integration.test.ts b/src/actions/sequencerInbox.integration.test.ts new file mode 100644 index 00000000..56ad1074 --- /dev/null +++ b/src/actions/sequencerInbox.integration.test.ts @@ -0,0 +1,248 @@ +import { describe, it, expect } from 'vitest'; +import { + createRollupHelper, + getInformationFromTestnode, + getNitroTestnodePrivateKeyAccounts, +} from '../testHelpers'; +import { Hex, createPublicClient, http, zeroAddress } from 'viem'; +import { nitroTestnodeL2 } from '../chains'; +import { getMaxTimeVariation } from './getMaxTimeVariation'; +import { isBatchPoster } from './isBatchPoster'; +import { sequencerInboxABI } from '../contracts/SequencerInbox'; +import { isValidKeysetHash } from './isValidKeysetHash'; +import { buildSetValidKeyset } from './buildSetValidKeyset'; +import { buildSetMaxTimeVariation } from './buildSetMaxTimeVariation'; +import { buildDisableBatchPoster, buildEnableBatchPoster } from './buildSetIsBatchPoster'; +import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'; +import { buildInvalidateKeysetHash } from './buildInvalidateKeysetHash'; + +const { l3SequencerInbox, l3BatchPoster, l3UpgradeExecutor } = getInformationFromTestnode(); +const { l3TokenBridgeDeployer, deployer, l3RollupOwner } = getNitroTestnodePrivateKeyAccounts(); + +const client = createPublicClient({ + chain: nitroTestnodeL2, + transport: http(), +}); + +describe('max time variation management', () => { + const defaultMaxTimeVariation = { + delayBlocks: 5_760n, + delaySeconds: 86_400n, + futureBlocks: 12n, + futureSeconds: 3_600n, + }; + it('getMaxTimeVariation successfully fetches max time variation', async () => { + const result = await getMaxTimeVariation(client, { + sequencerInbox: l3SequencerInbox, + }); + expect(result).toEqual(defaultMaxTimeVariation); + }); + + it('buildSetMaxTimeVariation successfully set max time varation', async () => { + async function setMaxTimeVariation(changes: { + delayBlocks: bigint; + futureBlocks: bigint; + delaySeconds: bigint; + futureSeconds: bigint; + }) { + const transactionRequest = await buildSetMaxTimeVariation(client, { + sequencerInbox: l3SequencerInbox, + upgradeExecutor: l3UpgradeExecutor, + account: l3RollupOwner.address, + params: changes, + }); + const txHash = await client.sendRawTransaction({ + serializedTransaction: await l3RollupOwner.signTransaction(transactionRequest), + }); + + await client.waitForTransactionReceipt({ hash: txHash }); + } + + const changes = { + delayBlocks: 2_880n, + futureBlocks: 6n, + delaySeconds: 43_200n, + futureSeconds: 1_800n, + }; + await setMaxTimeVariation(changes); + const newResult = await getMaxTimeVariation(client, { + sequencerInbox: l3SequencerInbox, + }); + expect(newResult).toEqual(changes); + + await setMaxTimeVariation(defaultMaxTimeVariation); + const finalResult = await getMaxTimeVariation(client, { + sequencerInbox: l3SequencerInbox, + }); + expect(finalResult).toEqual(defaultMaxTimeVariation); + }); +}); + +describe('batch poster management', () => { + it('isBatchPoster successfully fetches whether or an address is a batch poster', async () => { + const isNotBatchPosterAddress = await isBatchPoster(client, { + sequencerInbox: l3SequencerInbox, + params: { + batchPoster: zeroAddress, + }, + }); + expect(isNotBatchPosterAddress).toBeFalsy(); + const isBatchPosterAddress = await isBatchPoster(client, { + sequencerInbox: l3SequencerInbox, + params: { + batchPoster: l3BatchPoster, + }, + }); + expect(isBatchPosterAddress).toBeTruthy(); + }); + + it('successfully enable or disable an address as batch poster', async () => { + const randomAddress = privateKeyToAccount(generatePrivateKey()).address; + const isRandomAddressBatchPoster = await isBatchPoster(client, { + sequencerInbox: l3SequencerInbox, + params: { + batchPoster: randomAddress, + }, + }); + expect(isRandomAddressBatchPoster).toBeFalsy(); + + const enableTransactionRequest = await buildEnableBatchPoster(client, { + sequencerInbox: l3SequencerInbox, + upgradeExecutor: l3UpgradeExecutor, + account: l3RollupOwner.address, + params: { + batchPoster: randomAddress, + }, + }); + const enableTxHash = await client.sendRawTransaction({ + serializedTransaction: await l3RollupOwner.signTransaction(enableTransactionRequest), + }); + await client.waitForTransactionReceipt({ hash: enableTxHash }); + const isRandomAddressBatchPosterAfterEnabling = await isBatchPoster(client, { + sequencerInbox: l3SequencerInbox, + params: { + batchPoster: randomAddress, + }, + }); + expect(isRandomAddressBatchPosterAfterEnabling).toBeTruthy(); + + const disableTransactionRequest = await buildDisableBatchPoster(client, { + sequencerInbox: l3SequencerInbox, + upgradeExecutor: l3UpgradeExecutor, + account: l3RollupOwner.address, + params: { + batchPoster: randomAddress, + }, + }); + const disableTxHash = await client.sendRawTransaction({ + serializedTransaction: await l3RollupOwner.signTransaction(disableTransactionRequest), + }); + await client.waitForTransactionReceipt({ hash: disableTxHash }); + const isRandomAddressBatchPosterAfterDisabling = await isBatchPoster(client, { + sequencerInbox: l3SequencerInbox, + params: { + batchPoster: randomAddress, + }, + }); + expect(isRandomAddressBatchPosterAfterDisabling).toBeFalsy(); + }); +}); + +describe('keyset management', () => { + const keysetBytes = + '0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000002560000000000000002000000000000000201216006dcb5e56764bb72e6a45e6deb301ca85d8c4315c1da2efa29927f2ac8fb25571ce31d2d603735fe03196f6d56bcbf9a1999a89a74d5369822c4445d676c15ed52e5008daa775dc9a839c99ff963a19946ac740579874dac4f639907ae1bc69f0c6694955b524d718ca445831c5375393773401f33725a79661379dddabd5fff28619dc070befd9ed73d699e5c236c1a163be58ba81002b6130709bc064af5d7ba947130b72056bf17263800f1a3ab2269c6a510ef8e7412fd56d1ef1b916a1306e3b1d9c82c099371bd9861582acaada3a16e9dfee5d0ebce61096598a82f112d0a935e8cab5c48d82e3104b0c7ba79157dad1a019a3e7f6ad077b8e6308b116fec0f58239622463c3631fa01e2b4272409215b8009422c16715dbede5909060121600835f995f2478f24892d050daa289f8b6b9c1b185bcd28532f88d610c2642a2dc6f3509740236d33c3e2d9136aab17f819c8c671293bba277717762e8d1c1f7bac9e17dd28d2939a959bb38e500f9c11c38cebbc426e2dea97c40175a655d17400ae6c75ff49e884c79469249e70953258854b64fa8445c585ad45dc6dc6975501c6af7cff7074202c687f8a7bf1a3ac192689755f232275b4c8421b1a5669e9b904c29a292cdf961b783a7c0b4ce736900de4d8c63c5f85a65cb44af34bef840acef84ab75f44c4c9137610b68107aff3bbdcc19119c7a927c115b7b9bfb27d85c500ee77d13ec5a97a3ae6bf51d3b70a5502e8416de7b5eb8e9feee376411ca35c8a7f3f597c7606578cf96a4715ce5a35cf48e39c0a1faa2dee22d74e681900000000000000000000'; + async function deployAnyTrustChainWithKeyset(keyset: Hex) { + const batchPosters = [deployer.address]; + const validators = [deployer.address]; + + const { createRollupInformation } = await createRollupHelper({ + deployer: l3TokenBridgeDeployer, + batchPosters, + validators, + nativeToken: zeroAddress, + client, + }); + + const { sequencerInbox, upgradeExecutor } = createRollupInformation.coreContracts; + const transactionRequest = await buildSetValidKeyset(client, { + sequencerInbox: sequencerInbox, + account: l3TokenBridgeDeployer.address, + upgradeExecutor, + params: { + keyset, + }, + }); + const transactionHash = await client.sendRawTransaction({ + serializedTransaction: await l3TokenBridgeDeployer.signTransaction(transactionRequest), + }); + await client.waitForTransactionReceipt({ hash: transactionHash }); + const logs = await client.getContractEvents({ + address: sequencerInbox, + abi: sequencerInboxABI, + eventName: 'SetValidKeyset', + }); + + const keysetHash = logs.find((log) => log.args.keysetBytes === keyset)?.args.keysetHash; + + return { + keysetHash, + sequencerInbox, + upgradeExecutor, + }; + } + + it('isValidKeysetHash successfully fetches whether a hash is a valid keyset hash', async () => { + const invalidKeysetHash = await isValidKeysetHash(client, { + sequencerInbox: l3SequencerInbox, + params: { + keysetHash: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + }); + expect(invalidKeysetHash).toBeFalsy(); + + const { sequencerInbox, keysetHash } = await deployAnyTrustChainWithKeyset(keysetBytes); + + const result = await isValidKeysetHash(client, { + sequencerInbox, + params: { + keysetHash: keysetHash!, + }, + }); + expect(result).toBeTruthy(); + }); + + it('successfully invalidate a keyset hash', async () => { + const { sequencerInbox, keysetHash, upgradeExecutor } = await deployAnyTrustChainWithKeyset( + keysetBytes, + ); + + const result = await isValidKeysetHash(client, { + sequencerInbox, + params: { + keysetHash: keysetHash!, + }, + }); + expect(result).toBeTruthy(); + + const transactionRequest = await buildInvalidateKeysetHash(client, { + sequencerInbox, + account: l3TokenBridgeDeployer.address, + upgradeExecutor, + params: { + keysetHash: keysetHash!, + }, + }); + const txHash = await client.sendRawTransaction({ + serializedTransaction: await l3TokenBridgeDeployer.signTransaction(transactionRequest), + }); + await client.waitForTransactionReceipt({ hash: txHash }); + + const resultAfterChange = await isValidKeysetHash(client, { + sequencerInbox, + params: { + keysetHash: keysetHash!, + }, + }); + expect(resultAfterChange).toBeFalsy(); + }); +}); diff --git a/src/createRollup.ts b/src/createRollup.ts index eeb20b37..fab36b79 100644 --- a/src/createRollup.ts +++ b/src/createRollup.ts @@ -8,7 +8,7 @@ import { } from './createRollupPrepareTransactionReceipt'; import { createRollupEnoughCustomFeeTokenAllowance } from './createRollupEnoughCustomFeeTokenAllowance'; import { createRollupPrepareCustomFeeTokenApprovalTransactionRequest } from './createRollupPrepareCustomFeeTokenApprovalTransactionRequest'; -import { getBlockExplorerUrl } from './utils/getters'; +import { getBlockExplorerUrl } from './utils/getBlockExplorerUrl'; import { CreateRollupTransaction, createRollupPrepareTransaction, diff --git a/src/createRollupEnoughCustomFeeTokenAllowance.ts b/src/createRollupEnoughCustomFeeTokenAllowance.ts index 1fa8e5e0..5ff4f2e4 100644 --- a/src/createRollupEnoughCustomFeeTokenAllowance.ts +++ b/src/createRollupEnoughCustomFeeTokenAllowance.ts @@ -1,7 +1,7 @@ import { Address, PublicClient, Transport, Chain } from 'viem'; import { fetchAllowance } from './utils/erc20'; -import { getRollupCreatorAddress } from './utils/getters'; +import { getRollupCreatorAddress } from './utils/getRollupCreatorAddress'; import { createRollupDefaultRetryablesFees } from './constants'; import { Prettify } from './types/utils'; diff --git a/src/createRollupPrepareCustomFeeTokenApprovalTransactionRequest.ts b/src/createRollupPrepareCustomFeeTokenApprovalTransactionRequest.ts index 6961882d..752d5433 100644 --- a/src/createRollupPrepareCustomFeeTokenApprovalTransactionRequest.ts +++ b/src/createRollupPrepareCustomFeeTokenApprovalTransactionRequest.ts @@ -2,7 +2,7 @@ import { Address, PublicClient, Transport, Chain } from 'viem'; import { approvePrepareTransactionRequest } from './utils/erc20'; import { validateParentChain } from './types/ParentChain'; -import { getRollupCreatorAddress } from './utils/getters'; +import { getRollupCreatorAddress } from './utils/getRollupCreatorAddress'; import { createRollupDefaultRetryablesFees } from './constants'; import { Prettify } from './types/utils'; diff --git a/src/createRollupPrepareTransactionRequest.ts b/src/createRollupPrepareTransactionRequest.ts index 76eee497..fa790d05 100644 --- a/src/createRollupPrepareTransactionRequest.ts +++ b/src/createRollupPrepareTransactionRequest.ts @@ -8,7 +8,7 @@ import { validateParentChain } from './types/ParentChain'; import { isCustomFeeTokenAddress } from './utils/isCustomFeeTokenAddress'; import { ChainConfig } from './types/ChainConfig'; import { isAnyTrustChainConfig } from './utils/isAnyTrustChainConfig'; -import { getRollupCreatorAddress } from './utils/getters'; +import { getRollupCreatorAddress } from './utils/getRollupCreatorAddress'; import { fetchDecimals } from './utils/erc20'; import { TransactionRequestGasOverrides, applyPercentIncrease } from './utils/gasOverrides'; diff --git a/src/createTokenBridge.ts b/src/createTokenBridge.ts index cc4291e7..25cea9ed 100644 --- a/src/createTokenBridge.ts +++ b/src/createTokenBridge.ts @@ -33,10 +33,7 @@ import { import { isCustomFeeTokenAddress } from './utils/isCustomFeeTokenAddress'; import { WithTokenBridgeCreatorAddressOverride } from './types/createTokenBridgeTypes'; import { TransactionRequestGasOverrides } from './utils/gasOverrides'; - -function getBlockExplorerUrl(chain: Chain | undefined) { - return chain?.blockExplorers?.default.url; -} +import { getBlockExplorerUrl } from './utils/getBlockExplorerUrl'; export type CreateTokenBridgeParams< TParentChain extends Chain | undefined, diff --git a/src/createTokenBridgeEnoughCustomFeeTokenAllowance.ts b/src/createTokenBridgeEnoughCustomFeeTokenAllowance.ts index 85daacbb..1d4fbb87 100644 --- a/src/createTokenBridgeEnoughCustomFeeTokenAllowance.ts +++ b/src/createTokenBridgeEnoughCustomFeeTokenAllowance.ts @@ -5,7 +5,7 @@ import { createTokenBridgeDefaultRetryablesFees } from './constants'; import { Prettify } from './types/utils'; import { WithTokenBridgeCreatorAddressOverride } from './types/createTokenBridgeTypes'; -import { getTokenBridgeCreatorAddress } from './utils/getters'; +import { getTokenBridgeCreatorAddress } from './utils/getTokenBridgeCreatorAddress'; export type CreateTokenBridgeEnoughCustomFeeTokenAllowanceParams = Prettify< diff --git a/src/createTokenBridgeFetchTokenBridgeContracts.ts b/src/createTokenBridgeFetchTokenBridgeContracts.ts index f4121be8..b1fff792 100644 --- a/src/createTokenBridgeFetchTokenBridgeContracts.ts +++ b/src/createTokenBridgeFetchTokenBridgeContracts.ts @@ -5,7 +5,7 @@ import { tokenBridgeCreatorABI } from './contracts/TokenBridgeCreator'; import { Prettify } from './types/utils'; import { WithTokenBridgeCreatorAddressOverride } from './types/createTokenBridgeTypes'; import { TokenBridgeContracts } from './types/TokenBridgeContracts'; -import { getTokenBridgeCreatorAddress } from './utils/getters'; +import { getTokenBridgeCreatorAddress } from './utils/getTokenBridgeCreatorAddress'; export type CreateTokenBridgeFetchTokenBridgeContractsParams = Prettify< diff --git a/src/createTokenBridgePrepareCustomFeeTokenApprovalTransactionRequest.ts b/src/createTokenBridgePrepareCustomFeeTokenApprovalTransactionRequest.ts index 814e8812..090258e7 100644 --- a/src/createTokenBridgePrepareCustomFeeTokenApprovalTransactionRequest.ts +++ b/src/createTokenBridgePrepareCustomFeeTokenApprovalTransactionRequest.ts @@ -5,7 +5,7 @@ import { approvePrepareTransactionRequest } from './utils/erc20'; import { Prettify } from './types/utils'; import { validateParentChain } from './types/ParentChain'; import { WithTokenBridgeCreatorAddressOverride } from './types/createTokenBridgeTypes'; -import { getTokenBridgeCreatorAddress } from './utils/getters'; +import { getTokenBridgeCreatorAddress } from './utils/getTokenBridgeCreatorAddress'; export type CreateTokenBridgePrepareCustomFeeTokenApprovalTransactionRequestParams< TChain extends Chain | undefined, diff --git a/src/createTokenBridgePrepareTransactionRequest.ts b/src/createTokenBridgePrepareTransactionRequest.ts index e357bf9f..b5c04f34 100644 --- a/src/createTokenBridgePrepareTransactionRequest.ts +++ b/src/createTokenBridgePrepareTransactionRequest.ts @@ -12,7 +12,7 @@ import { import { Prettify } from './types/utils'; import { WithTokenBridgeCreatorAddressOverride } from './types/createTokenBridgeTypes'; -import { getTokenBridgeCreatorAddress } from './utils/getters'; +import { getTokenBridgeCreatorAddress } from './utils/getTokenBridgeCreatorAddress'; export type TransactionRequestRetryableGasOverrides = { maxSubmissionCostForFactory?: GasOverrideOptions; diff --git a/src/package.json b/src/package.json index 316eca52..014cd073 100644 --- a/src/package.json +++ b/src/package.json @@ -1,7 +1,7 @@ { "name": "@arbitrum/orbit-sdk", "description": "TypeScript SDK for building Arbitrum Orbit chains", - "version": "0.19.0-beta.0", + "version": "0.19.0", "main": "./dist/index.js", "files": [ "./dist" diff --git a/src/types/Actions.ts b/src/types/Actions.ts index a1ea1cf7..9a89614f 100644 --- a/src/types/Actions.ts +++ b/src/types/Actions.ts @@ -1,6 +1,8 @@ import { Address, PrepareTransactionRequestReturnType } from 'viem'; import { Prettify } from './utils'; +type isEmptyObject = Args extends Record ? true : false; + /** * Actions require contract address, but as part of decorators, the address might have been passed already to the decorator. * @@ -9,16 +11,12 @@ import { Prettify } from './utils'; */ export type ActionParameters = Prettify< Curried extends false - ? Args & { [key in ContractName]: Address } - : Args extends Record - ? - | { - [key in ContractName]: Address; - } - | void - : Args & { - [key in ContractName]?: Address; - } + ? isEmptyObject extends true + ? { [key in ContractName]: Address } // Contract wasn't curried. Args is an empty object. Only requires the contract name + : { params: Args } & { [key in ContractName]: Address } // Contract wasn't curried. Args is not empty. Requires both params and contract name + : isEmptyObject extends true + ? { [key in ContractName]: Address } | void // Contract was curried. Args is empty. Only requires the contract name. Allows no parameters + : { params: Args } & { [key in ContractName]?: Address } // Contract was curried. Args is not empty. Requires params, contract name is optional >; export type WithAccount = Args & { diff --git a/src/utils/getArbOSVersion.unit.test.ts b/src/utils/getArbOSVersion.unit.test.ts index f3f8038b..a90aacee 100644 --- a/src/utils/getArbOSVersion.unit.test.ts +++ b/src/utils/getArbOSVersion.unit.test.ts @@ -1,21 +1,23 @@ import { it, expect } from 'vitest'; +import { createPublicClient, http } from 'viem'; +import { arbitrum as arbitrumOne, sepolia } from 'viem/chains'; import { getArbOSVersion } from './getArbOSVersion'; -import { createPublicClient, http } from 'viem'; -import { arbitrum, sepolia } from 'viem/chains'; -it('Returns the ArbOS version for arbitrum chain', async () => { - const arbProvider = createPublicClient({ - chain: arbitrum, +it('returns the ArbOS version of Arbitrum One', async () => { + const arbitrumOneClient = createPublicClient({ + chain: arbitrumOne, transport: http(), }); - expect(await getArbOSVersion(arbProvider)).toBe(20); + + expect(await getArbOSVersion(arbitrumOneClient)).toBe(31); }); -it('Throws if the provider is not an Arbitrum provider', async () => { - const sepoliaProvider = createPublicClient({ +it('throws if the chain is not an Arbitrum chain', async () => { + const sepoliaClient = createPublicClient({ chain: sepolia, transport: http(), }); - await expect(getArbOSVersion(sepoliaProvider)).rejects.toThrowError(); + + await expect(getArbOSVersion(sepoliaClient)).rejects.toThrowError(); }); diff --git a/src/utils/getBlockExplorerUrl.ts b/src/utils/getBlockExplorerUrl.ts new file mode 100644 index 00000000..11e45e43 --- /dev/null +++ b/src/utils/getBlockExplorerUrl.ts @@ -0,0 +1,5 @@ +import { Chain } from 'viem'; + +export function getBlockExplorerUrl(chain: Chain | undefined) { + return chain?.blockExplorers?.default.url; +} diff --git a/src/utils/getClientVersion.unit.test.ts b/src/utils/getClientVersion.unit.test.ts index 9d8eda71..2481b8ba 100644 --- a/src/utils/getClientVersion.unit.test.ts +++ b/src/utils/getClientVersion.unit.test.ts @@ -11,10 +11,10 @@ const arbitrumSepoliaPublicClient = createPublicClient({ it('fetches client version with public client', async () => { const clientVersion = await getClientVersion(arbitrumSepoliaPublicClient); - expect(clientVersion.startsWith('nitro/v3')).toBeTruthy(); + expect(clientVersion.startsWith('nitro/')).toBeTruthy(); }); it('fetches client version with rpc url', async () => { const clientVersion = await getClientVersion('https://sepolia-rollup.arbitrum.io/rpc'); - expect(clientVersion.startsWith('nitro/v3')).toBeTruthy(); + expect(clientVersion.startsWith('nitro/')).toBeTruthy(); }); diff --git a/src/utils/getRollupCreatorAddress.ts b/src/utils/getRollupCreatorAddress.ts new file mode 100644 index 00000000..c3515c38 --- /dev/null +++ b/src/utils/getRollupCreatorAddress.ts @@ -0,0 +1,16 @@ +import { Client, Transport, Chain } from 'viem'; + +import { rollupCreatorAddress } from '../contracts/RollupCreator'; +import { validateParentChain } from '../types/ParentChain'; + +export function getRollupCreatorAddress( + client: Client, +) { + const chainId = validateParentChain(client); + + if (!rollupCreatorAddress[chainId]) { + throw new Error(`Parent chain not supported: ${chainId}`); + } + + return rollupCreatorAddress[chainId]; +} diff --git a/src/utils/getters.ts b/src/utils/getTokenBridgeCreatorAddress.ts similarity index 51% rename from src/utils/getters.ts rename to src/utils/getTokenBridgeCreatorAddress.ts index e22fa15f..5fe88893 100644 --- a/src/utils/getters.ts +++ b/src/utils/getTokenBridgeCreatorAddress.ts @@ -1,21 +1,8 @@ import { Client, Transport, Chain } from 'viem'; -import { rollupCreatorAddress } from '../contracts/RollupCreator'; import { tokenBridgeCreatorAddress } from '../contracts/TokenBridgeCreator'; import { validateParentChain } from '../types/ParentChain'; -export function getRollupCreatorAddress( - client: Client, -) { - const chainId = validateParentChain(client); - - if (!rollupCreatorAddress[chainId]) { - throw new Error(`Parent chain not supported: ${chainId}`); - } - - return rollupCreatorAddress[chainId]; -} - export function getTokenBridgeCreatorAddress( client: Client, ) { @@ -27,7 +14,3 @@ export function getTokenBridgeCreatorAddress( return tokenBridgeCreatorAddress[chainId]; } - -export function getBlockExplorerUrl(chain: Chain) { - return chain.blockExplorers?.default.url; -} diff --git a/src/utils/index.ts b/src/utils/index.ts index f9af6393..72b138ab 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -4,7 +4,8 @@ import { getParentChainLayer } from './getParentChainLayer'; import { sanitizePrivateKey } from './sanitizePrivateKey'; import { getArbOSVersion } from './getArbOSVersion'; import { getClientVersion } from './getClientVersion'; -import { getRollupCreatorAddress, getTokenBridgeCreatorAddress } from './getters'; +import { getRollupCreatorAddress } from './getRollupCreatorAddress'; +import { getTokenBridgeCreatorAddress } from './getTokenBridgeCreatorAddress'; export { generateChainId,