diff --git a/src/decorators/publicActionsParentChain.ts b/src/decorators/publicActionsParentChain.ts new file mode 100644 index 00000000..b4995147 --- /dev/null +++ b/src/decorators/publicActionsParentChain.ts @@ -0,0 +1,161 @@ +import { Address, Chain, PublicClient, Transport } from 'viem'; + +// Getters +import { + getMaxTimeVariation, + GetMaxTimeVariationParameters, + GetMaxTimeVariationReturnType, +} from '../actions/getMaxTimeVariation'; +import { + isBatchPoster, + IsBatchPosterParameters, + IsBatchPosterReturnType, +} from '../actions/isBatchPoster'; +import { + isValidKeysetHash, + IsValidKeysetHashParameters, + IsValidKeysetHashReturnType, +} from '../actions/isValidKeysetHash'; +// Setters +import { + invalidateKeysetHash, + InvalidateKeysetHashParameters, + InvalidateKeysetHashReturnType, +} from '../actions/invalidateKeysetHash'; +import { + enableBatchPoster, + disableBatchPoster, + SetIsBatchPosterParameters, + SetIsBatchPosterReturnType, +} from '../actions/setIsbatchPoster'; +import { setKeyset, SetKeysetParameters, SetKeysetReturnType } from '../actions/setKeyset'; +import { + setMaxTimeVariation, + SetMaxTimeVariationParameters, + SetMaxTimeVariationReturnType, +} from '../actions/setMaxTimeVariation'; + +type Params = { sequencerInbox: Address } | void; + +export type PublicActionsParentChain = { + // Getters + getMaxTimeVariation: ( + parameters: GetMaxTimeVariationParameters, + ) => Promise; + isBatchPoster: (parameters: IsBatchPosterParameters) => Promise; + isValidKeysetHash: ( + parameters: IsValidKeysetHashParameters, + ) => Promise; + // Setters + invalidateKeysetHash: ( + parameters: InvalidateKeysetHashParameters, + ) => Promise; + enableBatchPoster: ( + parameters: SetIsBatchPosterParameters, + ) => Promise; + disableBatchPoster: ( + parameters: SetIsBatchPosterParameters, + ) => Promise; + setKeyset: (parameters: SetKeysetParameters) => Promise; + setMaxTimeVariation: ( + parameters: SetMaxTimeVariationParameters, + ) => Promise; +}; + +/** + * Simplifies the overall typing with curried sequencerInbox address + * + * By design, sequencerInbox is either passed initially from the decorator, or on each call + * + * Address passed through each call has the priority over the address passed to the decorator, for override + */ + +function getSequencerInboxAddress( + params: Params, + args: { sequencerInbox?: Address } | void, +): Address { + return ((args && args.sequencerInbox) ?? (params && params.sequencerInbox)) as unknown as Address; +} + +/** + * Public actions for parent chain + * + * @example + * import { createPublicClient, http } from 'viem' + * import { publicActionsParentChain } from '@arbitrum/orbit-sdk' + * import { arbitrum } from 'viem/chains' + * + * export const publicClientParentChain = createPublicClient({ + * chain: arbitrum, + * transport: http(), + * }).extend(publicActionsParentChain({ + * sequencerInbox: '0x1c479675ad559DC151F6Ec7ed3FbF8ceE79582B6' + * })) + * + * const { delayBlocks, futureBlocks, delaySeconds, futureSeconds } = await publicClientParentChain.getMaxTimeVariation() + */ +export function publicActionsParentChain< + TParams extends Params = void, + TTransport extends Transport = Transport, + TChain extends Chain | undefined = Chain | undefined, +>(params: void): (client: PublicClient) => PublicActionsParentChain; +export function publicActionsParentChain< + TParams extends Params = { sequencerInbox: Address }, + TTransport extends Transport = Transport, + TChain extends Chain | undefined = Chain | undefined, +>(params: TParams): (client: PublicClient) => PublicActionsParentChain; +export function publicActionsParentChain< + TParams extends Params, + TTransport extends Transport = Transport, + TChain extends Chain | undefined = Chain | undefined, +>(params: TParams) { + return (client: PublicClient) => { + // sequencerInbox is curried, sequencerInbox param is optional. + return { + // Getters + getMaxTimeVariation: (args) => + getMaxTimeVariation(client, { + ...args, + sequencerInbox: getSequencerInboxAddress(params, args), + }), + isBatchPoster: (args) => + isBatchPoster(client, { + ...args, + sequencerInbox: getSequencerInboxAddress(params, args), + }), + isValidKeysetHash: (args) => + isValidKeysetHash(client, { + ...args, + sequencerInbox: getSequencerInboxAddress(params, args), + }), + // Setters + invalidateKeysetHash: (args) => + invalidateKeysetHash(client, { + ...args, + sequencerInbox: getSequencerInboxAddress(params, args), + }), + enableBatchPoster: (args) => + enableBatchPoster(client, { + ...args, + sequencerInbox: getSequencerInboxAddress(params, args), + }), + disableBatchPoster: (args) => + disableBatchPoster(client, { + ...args, + sequencerInbox: getSequencerInboxAddress(params, args), + }), + setKeyset: (args) => + setKeyset(client, { + ...args, + sequencerInbox: getSequencerInboxAddress(params, args), + }), + setMaxTimeVariation: (args) => + setMaxTimeVariation(client, { + ...args, + sequencerInbox: getSequencerInboxAddress(params, args), + }), + } satisfies PublicActionsParentChain< + TParams extends { sequencerInbox: Address } ? true : false + >; + }; +} diff --git a/src/decorators/publicActionsParentChain.unit.test.ts b/src/decorators/publicActionsParentChain.unit.test.ts new file mode 100644 index 00000000..d65bfcfd --- /dev/null +++ b/src/decorators/publicActionsParentChain.unit.test.ts @@ -0,0 +1,45 @@ +import { it, expect, describe } from 'vitest'; + +import { createPublicClient, http, padHex, zeroAddress } from 'viem'; +import { mainnet } from '../chains'; +import { publicActionsParentChain } from './publicActionsParentChain'; + +const arbOneSequencerInbox = '0x1c479675ad559DC151F6Ec7ed3FbF8ceE79582B6'; + +const client = createPublicClient({ + chain: mainnet, + transport: http(), +}).extend(publicActionsParentChain({ sequencerInbox: arbOneSequencerInbox })); + +describe('Getters', () => { + it('[maxTimeVariation] Should return max time variation', async () => { + const maxTimeVariation = await client.getMaxTimeVariation(); + expect(maxTimeVariation).toEqual({ + delayBlocks: 5760n, + futureBlocks: 64n, + delaySeconds: 86400n, + futureSeconds: 768n, + }); + }); + + it('[isBatchPoster] Should return if an address is a batch poster', async () => { + const isZeroAddressBatchPoster = await client.isBatchPoster({ + batchPoster: zeroAddress, + }); + expect(isZeroAddressBatchPoster).toBeFalsy(); + }); + + it('[isValidKeysetHash] Should return if a keysetHash is a valid one', async () => { + const isEmptyHashValidKeysetHash = await client.isValidKeysetHash({ + keysetHash: padHex('0x'), + }); + expect(isEmptyHashValidKeysetHash).toBeFalsy(); + + // Test on Nova + const isAValidKeysetHashOnNova = await client.isValidKeysetHash({ + keysetHash: '0x01191accc7ad5a8020e6c6d122984540e9fc48d0457bda63e0a32c8c31994f4a', + sequencerInbox: '0x211e1c4c7f1bf5351ac850ed10fd68cffcf6c21b', + }); + expect(isAValidKeysetHashOnNova).toBeTruthy(); + }); +});