From 5a8ed7fb55740935ff243fcc4af98106789ae63b Mon Sep 17 00:00:00 2001 From: Christophe Date: Tue, 23 Apr 2024 19:43:57 +0000 Subject: [PATCH 01/14] Add placeholder ABIs --- src/contracts.ts | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/contracts.ts b/src/contracts.ts index 53997932..d004cc33 100644 --- a/src/contracts.ts +++ b/src/contracts.ts @@ -198,3 +198,40 @@ export const tokenBridgeCreator = { export const sequencerInbox = { abi: sequencerInboxABI, }; +export type ArbOSVersions = 10 | 11 | 20; + +export const ArbOwnerABIs = { + 10: [ + { + stateMutability: 'view', + type: 'function', + inputs: [], + name: 'getAllChainOwners', + outputs: [{ name: '', internalType: 'address', type: 'address' }], + }, + { + stateMutability: 'view', + type: 'function', + inputs: [], + name: 'onlyOnArbOS10', + outputs: [{ name: '', internalType: 'address[]', type: 'address[]' }], + }, + ], + 11: [ + { + stateMutability: 'view', + type: 'function', + inputs: [], + name: 'getAllChainOwners', + outputs: [{ name: '', internalType: 'uint256', type: 'uint256' }], + }, + { + stateMutability: 'view', + type: 'function', + inputs: [], + name: 'onlyOnArbOS11', + outputs: [{ name: '', internalType: 'address[]', type: 'address[]' }], + }, + ], + 20: arbOwnerConfig.abi, +} as const; From ec536e3718b10ad9c5357b8166ed4b2684012dc7 Mon Sep 17 00:00:00 2001 From: Christophe Date: Tue, 23 Apr 2024 19:44:23 +0000 Subject: [PATCH 02/14] Update types --- src/arbOwnerPrepareTransactionRequest.ts | 14 +++++----- src/arbOwnerReadContract.ts | 34 ++++++++++++++++-------- src/decorators/arbOwnerPublicActions.ts | 26 ++++++++++++------ 3 files changed, 49 insertions(+), 25 deletions(-) diff --git a/src/arbOwnerPrepareTransactionRequest.ts b/src/arbOwnerPrepareTransactionRequest.ts index b48005c8..c9f65b82 100644 --- a/src/arbOwnerPrepareTransactionRequest.ts +++ b/src/arbOwnerPrepareTransactionRequest.ts @@ -7,7 +7,7 @@ import { Transport, } from 'viem'; -import { arbOwner } from './contracts'; +import { arbOwner, ArbOSVersions } from './contracts'; import { upgradeExecutorEncodeFunctionData } from './upgradeExecutor'; import { GetFunctionName } from './types/utils'; @@ -61,18 +61,20 @@ function arbOwnerPrepareFunctionData< }; } -export type ArbOwnerPrepareTransactionRequestParameters< - TFunctionName extends ArbOwnerPrepareTransactionRequestFunctionName, -> = Omit, 'abi'> & { +export type ArbOwnerPrepareTransactionRequestParameters = ArbOwnerEncodeFunctionDataParameters & { + upgradeExecutor: Address | false; account: Address; -}; +} export async function arbOwnerPrepareTransactionRequest< + TArbOsVersion extends ArbOSVersions, TFunctionName extends ArbOwnerPrepareTransactionRequestFunctionName, TChain extends Chain | undefined, >( client: PublicClient, - params: ArbOwnerPrepareTransactionRequestParameters, + params: ArbOwnerPrepareTransactionRequestParameters & { + arbOSVersion: TArbOsVersion; + }, ) { if (typeof client.chain === 'undefined') { throw new Error('[arbOwnerPrepareTransactionRequest] client.chain is undefined'); diff --git a/src/arbOwnerReadContract.ts b/src/arbOwnerReadContract.ts index 62a5ff15..6c94dc74 100644 --- a/src/arbOwnerReadContract.ts +++ b/src/arbOwnerReadContract.ts @@ -1,29 +1,41 @@ import { Chain, GetFunctionArgs, PublicClient, ReadContractReturnType, Transport } from 'viem'; -import { arbOwnerPublic } from './contracts'; +import { ArbOSVersions, ArbOwnerABIs, arbOwnerPublic } from './contracts'; import { GetFunctionName } from './types/utils'; -export type ArbOwnerPublicAbi = typeof arbOwnerPublic.abi; -export type ArbOwnerPublicFunctionName = GetFunctionName; +export type ArbOwnerPublicAbi = + (typeof ArbOwnerABIs)[TArbOsVersion]; -export type ArbOwnerReadContractParameters = { +export type ArbOwnerPublicFunctionName = GetFunctionName< + ArbOwnerPublicAbi +>; + +export type ArbOwnerReadContractParameters< + TArbOsVersion extends ArbOSVersions, + TFunctionName extends ArbOwnerPublicFunctionName, +> = { functionName: TFunctionName; -} & GetFunctionArgs; +} & GetFunctionArgs, TFunctionName>; -export type ArbOwnerReadContractReturnType = - ReadContractReturnType; +export type ArbOwnerReadContractReturnType< + TArbOsVersion extends ArbOSVersions, + TFunctionName extends ArbOwnerPublicFunctionName, +> = ReadContractReturnType, TFunctionName>; export function arbOwnerReadContract< + TArbOsVersion extends ArbOSVersions, TChain extends Chain | undefined, - TFunctionName extends ArbOwnerPublicFunctionName, + TFunctionName extends ArbOwnerPublicFunctionName, >( client: PublicClient, - params: ArbOwnerReadContractParameters, -): Promise> { + params: ArbOwnerReadContractParameters & { + arbOSVersion: TArbOsVersion; + }, +): Promise> { // @ts-ignore (todo: fix viem type issue) return client.readContract({ address: arbOwnerPublic.address, - abi: arbOwnerPublic.abi, + abi: ArbOwnerABIs[params.arbOSVersion], functionName: params.functionName, args: params.args, }); diff --git a/src/decorators/arbOwnerPublicActions.ts b/src/decorators/arbOwnerPublicActions.ts index 0776fb17..b819a2a0 100644 --- a/src/decorators/arbOwnerPublicActions.ts +++ b/src/decorators/arbOwnerPublicActions.ts @@ -11,11 +11,15 @@ import { ArbOwnerPrepareTransactionRequestFunctionName, ArbOwnerPrepareTransactionRequestParameters, } from '../arbOwnerPrepareTransactionRequest'; +import { ArbOSVersions } from '../contracts'; -export type ArbOwnerPublicActions = { - arbOwnerReadContract: ( - args: ArbOwnerReadContractParameters, - ) => Promise>; +export type ArbOwnerPublicActions< + TArbOsVersion extends ArbOSVersions, + TChain extends Chain | undefined = Chain | undefined, +> = { + arbOwnerReadContract: >( + args: ArbOwnerReadContractParameters, + ) => Promise>; arbOwnerPrepareTransactionRequest: < TFunctionName extends ArbOwnerPrepareTransactionRequestFunctionName, @@ -25,12 +29,18 @@ export type ArbOwnerPublicActions(client: PublicClient): ArbOwnerPublicActions { - return { - arbOwnerReadContract: (args) => arbOwnerReadContract(client, args), +>({ arbOSVersion }: { arbOSVersion: TArbOsVersion }) { + return ( + client: PublicClient, + ): ArbOwnerPublicActions => { + return { + arbOwnerReadContract: (args) => arbOwnerReadContract(client, { ...args, arbOSVersion }), - arbOwnerPrepareTransactionRequest: (args) => arbOwnerPrepareTransactionRequest(client, args), + arbOwnerPrepareTransactionRequest: (args) => + arbOwnerPrepareTransactionRequest(client, { ...args, arbOSVersion }), + }; }; } From 93cd2508e83c7d718e507673dfbbb4d67c7703c2 Mon Sep 17 00:00:00 2001 From: Christophe Date: Tue, 23 Apr 2024 19:44:58 +0000 Subject: [PATCH 03/14] Update tests --- src/decorators/arbOwnerPublicActions.integration.test.ts | 2 +- .../arbOwnerPublicActionsUpgradeExecutor.integration.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/decorators/arbOwnerPublicActions.integration.test.ts b/src/decorators/arbOwnerPublicActions.integration.test.ts index 890094c3..57dcf4a0 100644 --- a/src/decorators/arbOwnerPublicActions.integration.test.ts +++ b/src/decorators/arbOwnerPublicActions.integration.test.ts @@ -15,7 +15,7 @@ const randomAccount = privateKeyToAccount(generatePrivateKey()); const client = createPublicClient({ chain: nitroTestnodeL2, transport: http(), -}).extend(arbOwnerPublicActions); +}).extend(arbOwnerPublicActions({ arbOSVersion: 20 })); it('successfully fetches network fee receiver', async () => { const result = await client.arbOwnerReadContract({ diff --git a/src/decorators/arbOwnerPublicActionsUpgradeExecutor.integration.test.ts b/src/decorators/arbOwnerPublicActionsUpgradeExecutor.integration.test.ts index 8c8e7904..07f3216d 100644 --- a/src/decorators/arbOwnerPublicActionsUpgradeExecutor.integration.test.ts +++ b/src/decorators/arbOwnerPublicActionsUpgradeExecutor.integration.test.ts @@ -20,7 +20,7 @@ const client = createPublicClient({ chain: nitroTestnodeL3, transport: http(), }) - .extend(arbOwnerPublicActions) + .extend(arbOwnerPublicActions({ arbOSVersion: 20 })) .extend(arbGasInfoPublicActions); it('succesfully adds chain owner using upgrade executor', async () => { From e7e320405b454bedf437464f4f4d2083c2d6f2d3 Mon Sep 17 00:00:00 2001 From: Christophe Date: Tue, 23 Apr 2024 19:45:05 +0000 Subject: [PATCH 04/14] Add unit tests --- .../arbOwnerPublicActions.unit.test.ts | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 src/decorators/arbOwnerPublicActions.unit.test.ts diff --git a/src/decorators/arbOwnerPublicActions.unit.test.ts b/src/decorators/arbOwnerPublicActions.unit.test.ts new file mode 100644 index 00000000..6ba09ca4 --- /dev/null +++ b/src/decorators/arbOwnerPublicActions.unit.test.ts @@ -0,0 +1,78 @@ +import { it, expect, expectTypeOf } from 'vitest'; + +import { AbiFunctionNotFoundError, createPublicClient, http } from 'viem'; +import { nitroTestnodeL2 } from '../chains'; +import { arbOwnerPublicActions } from './arbOwnerPublicActions'; + +const client10 = createPublicClient({ + chain: nitroTestnodeL2, + transport: http(), +}).extend(arbOwnerPublicActions({ arbOSVersion: 10 })); +const client11 = createPublicClient({ + chain: nitroTestnodeL2, + transport: http(), +}).extend(arbOwnerPublicActions({ arbOSVersion: 11 })); +const client20 = createPublicClient({ + chain: nitroTestnodeL2, + transport: http(), +}).extend(arbOwnerPublicActions({ arbOSVersion: 20 })); + +it('Accept function name based on arbOSVersion', async () => { + // Version 10 + client10.arbOwnerReadContract({ + functionName: 'onlyOnArbOS10', + }); + + expect( + client10.arbOwnerReadContract({ + functionName: 'onlyOnArbOS20', + }), + ).rejects.toThrowError(AbiFunctionNotFoundError); + + // Version 11 + client11.arbOwnerReadContract({ + functionName: 'onlyOnArbOS11', + }); + + expect( + client11.arbOwnerReadContract({ + functionName: 'onlyOnArbOS20', + }), + ).rejects.toThrowError(AbiFunctionNotFoundError); + + // Version 20 + client20.arbOwnerReadContract({ + functionName: 'getInfraFeeAccount', + }); + + expect( + client20.arbOwnerReadContract({ + functionName: 'onlyOnArbOS10', + }), + ).rejects.toThrowError(AbiFunctionNotFoundError); +}); + +// Those tests won't fail if the return type is wrong +// But they will display an error in the IDE +it('Type return values for function in multiple versions', () => { + // Version 10 + expectTypeOf( + client10.arbOwnerReadContract({ + functionName: 'getAllChainOwners', + }), + ).resolves.toEqualTypeOf<`0x${string}`>(); + + // Version 11 + expectTypeOf( + client11.arbOwnerReadContract({ + functionName: 'getAllChainOwners', + }), + ).resolves.toEqualTypeOf(); + + // Version 20 + expectTypeOf( + client20.arbOwnerReadContract({ + functionName: 'getAllChainOwners', + }), + ).resolves.toEqualTypeOf(); +}); From aff15db28e8768aec7894f7ee2644188c629fb16 Mon Sep 17 00:00:00 2001 From: Christophe Date: Thu, 25 Apr 2024 07:54:33 +0000 Subject: [PATCH 05/14] Fix TS errors in unit test --- src/decorators/arbOwnerPublicActions.unit.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/decorators/arbOwnerPublicActions.unit.test.ts b/src/decorators/arbOwnerPublicActions.unit.test.ts index 6ba09ca4..efc0f64a 100644 --- a/src/decorators/arbOwnerPublicActions.unit.test.ts +++ b/src/decorators/arbOwnerPublicActions.unit.test.ts @@ -25,6 +25,7 @@ it('Accept function name based on arbOSVersion', async () => { expect( client10.arbOwnerReadContract({ + // @ts-expect-error Not available for version 10 functionName: 'onlyOnArbOS20', }), ).rejects.toThrowError(AbiFunctionNotFoundError); @@ -36,6 +37,7 @@ it('Accept function name based on arbOSVersion', async () => { expect( client11.arbOwnerReadContract({ + // @ts-expect-error Not available for version 11 functionName: 'onlyOnArbOS20', }), ).rejects.toThrowError(AbiFunctionNotFoundError); @@ -47,6 +49,7 @@ it('Accept function name based on arbOSVersion', async () => { expect( client20.arbOwnerReadContract({ + // @ts-expect-error Not available for version 20 functionName: 'onlyOnArbOS10', }), ).rejects.toThrowError(AbiFunctionNotFoundError); From b00254bbab4f6f73561113070aa7c5856be1b615 Mon Sep 17 00:00:00 2001 From: Christophe Date: Tue, 7 May 2024 19:47:06 +0200 Subject: [PATCH 06/14] Fix typing for prepareTransactionRequest --- src/arbOwnerPrepareTransactionRequest.ts | 60 +++----- src/arbOwnerReadContract.ts | 4 +- src/contracts.ts | 14 ++ ...wnerPrepareTransactionRequest.unit.test.ts | 2 +- .../arbOwnerPublicActions.integration.test.ts | 2 +- src/decorators/arbOwnerPublicActions.ts | 11 +- .../arbOwnerPublicActions.unit.test.ts | 143 +++++++++++------- 7 files changed, 134 insertions(+), 102 deletions(-) diff --git a/src/arbOwnerPrepareTransactionRequest.ts b/src/arbOwnerPrepareTransactionRequest.ts index c9f65b82..bd33429a 100644 --- a/src/arbOwnerPrepareTransactionRequest.ts +++ b/src/arbOwnerPrepareTransactionRequest.ts @@ -5,45 +5,30 @@ import { Address, Chain, Transport, + GetFunctionArgs, } from 'viem'; -import { arbOwner, ArbOSVersions } from './contracts'; +import { arbOwner, ArbOSVersions, ArbOwnerABIs } from './contracts'; import { upgradeExecutorEncodeFunctionData } from './upgradeExecutor'; -import { GetFunctionName } from './types/utils'; +import { ArbOwnerPublicAbi, ArbOwnerPublicFunctionName } from './arbOwnerReadContract'; -type ArbOwnerAbi = typeof arbOwner.abi; -export type ArbOwnerPrepareTransactionRequestFunctionName = GetFunctionName; export type ArbOwnerEncodeFunctionDataParameters< - TFunctionName extends ArbOwnerPrepareTransactionRequestFunctionName, -> = EncodeFunctionDataParameters; - -function arbOwnerEncodeFunctionData< - TFunctionName extends ArbOwnerPrepareTransactionRequestFunctionName, ->({ functionName, abi, args }: ArbOwnerEncodeFunctionDataParameters) { - return encodeFunctionData({ - abi, - functionName, - args, - }); -} - -type ArbOwnerPrepareFunctionDataParameters< - TFunctionName extends ArbOwnerPrepareTransactionRequestFunctionName, -> = ArbOwnerEncodeFunctionDataParameters & { + TArbOsVersion extends ArbOSVersions, + TFunctionName extends ArbOwnerPublicFunctionName, +> = EncodeFunctionDataParameters, TFunctionName> & { upgradeExecutor: Address | false; - abi: ArbOwnerAbi; }; + function arbOwnerPrepareFunctionData< - TFunctionName extends ArbOwnerPrepareTransactionRequestFunctionName, ->(params: ArbOwnerPrepareFunctionDataParameters) { + TArbOsVersion extends ArbOSVersions, + TFunctionName extends ArbOwnerPublicFunctionName, +>(params: ArbOwnerEncodeFunctionDataParameters) { const { upgradeExecutor } = params; if (!upgradeExecutor) { return { to: arbOwner.address, - data: arbOwnerEncodeFunctionData( - params as ArbOwnerEncodeFunctionDataParameters, - ), + data: encodeFunctionData(params as EncodeFunctionDataParameters, TFunctionName>), value: BigInt(0), }; } @@ -54,37 +39,40 @@ function arbOwnerPrepareFunctionData< functionName: 'executeCall', args: [ arbOwner.address, // target - arbOwnerEncodeFunctionData(params as ArbOwnerEncodeFunctionDataParameters), // targetCallData + encodeFunctionData(params as EncodeFunctionDataParameters, TFunctionName>), // targetCallData ], }), value: BigInt(0), }; } -export type ArbOwnerPrepareTransactionRequestParameters = ArbOwnerEncodeFunctionDataParameters & { - upgradeExecutor: Address | false; +export type ArbOwnerPrepareTransactionRequestParameters< + TArbOsVersion extends ArbOSVersions, + TFunctionName extends ArbOwnerPublicFunctionName, +> = { + functionName: TFunctionName; account: Address; -} + upgradeExecutor: Address | false; +} & GetFunctionArgs, TFunctionName>; export async function arbOwnerPrepareTransactionRequest< TArbOsVersion extends ArbOSVersions, - TFunctionName extends ArbOwnerPrepareTransactionRequestFunctionName, TChain extends Chain | undefined, + TFunctionName extends ArbOwnerPublicFunctionName, >( client: PublicClient, - params: ArbOwnerPrepareTransactionRequestParameters & { - arbOSVersion: TArbOsVersion; + params: ArbOwnerPrepareTransactionRequestParameters & { + arbOsVersion: TArbOsVersion; }, ) { if (typeof client.chain === 'undefined') { throw new Error('[arbOwnerPrepareTransactionRequest] client.chain is undefined'); } - // params is extending ArbOwnerPrepareFunctionDataParameters, it's safe to cast const { to, data, value } = arbOwnerPrepareFunctionData({ ...params, - abi: arbOwner.abi, - } as unknown as ArbOwnerPrepareFunctionDataParameters); + abi: ArbOwnerABIs[params.arbOsVersion], + } as unknown as ArbOwnerEncodeFunctionDataParameters); // @ts-ignore (todo: fix viem type issue) const request = await client.prepareTransactionRequest({ diff --git a/src/arbOwnerReadContract.ts b/src/arbOwnerReadContract.ts index 6c94dc74..0d2bb9c0 100644 --- a/src/arbOwnerReadContract.ts +++ b/src/arbOwnerReadContract.ts @@ -29,13 +29,13 @@ export function arbOwnerReadContract< >( client: PublicClient, params: ArbOwnerReadContractParameters & { - arbOSVersion: TArbOsVersion; + arbOsVersion: TArbOsVersion; }, ): Promise> { // @ts-ignore (todo: fix viem type issue) return client.readContract({ address: arbOwnerPublic.address, - abi: ArbOwnerABIs[params.arbOSVersion], + abi: ArbOwnerABIs[params.arbOsVersion], functionName: params.functionName, args: params.args, }); diff --git a/src/contracts.ts b/src/contracts.ts index d004cc33..1637ef5f 100644 --- a/src/contracts.ts +++ b/src/contracts.ts @@ -216,6 +216,13 @@ export const ArbOwnerABIs = { name: 'onlyOnArbOS10', outputs: [{ name: '', internalType: 'address[]', type: 'address[]' }], }, + { + stateMutability: 'nonpayable', + type: 'function', + inputs: [{ name: 'recipient', internalType: 'address[]', type: 'address[]' }], + name: 'setL1PricingRewardRecipient', + outputs: [], + }, ], 11: [ { @@ -232,6 +239,13 @@ export const ArbOwnerABIs = { name: 'onlyOnArbOS11', outputs: [{ name: '', internalType: 'address[]', type: 'address[]' }], }, + { + stateMutability: 'nonpayable', + type: 'function', + inputs: [{ name: 'recipient', internalType: 'uint64', type: 'uint64' }], + name: 'setL1PricingRewardRecipient', + outputs: [], + }, ], 20: arbOwnerConfig.abi, } as const; diff --git a/src/decorators/arbOwnerPrepareTransactionRequest.unit.test.ts b/src/decorators/arbOwnerPrepareTransactionRequest.unit.test.ts index 5c3639f7..6535d013 100644 --- a/src/decorators/arbOwnerPrepareTransactionRequest.unit.test.ts +++ b/src/decorators/arbOwnerPrepareTransactionRequest.unit.test.ts @@ -14,7 +14,7 @@ import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'; const client = createPublicClient({ chain: nitroTestnodeL2, transport: http(), -}).extend(arbOwnerPublicActions); +}).extend(arbOwnerPublicActions({ arbOsVersion: 20 })); const randomAccount = privateKeyToAccount(generatePrivateKey()); it('Infer parameters based on function name', async () => { diff --git a/src/decorators/arbOwnerPublicActions.integration.test.ts b/src/decorators/arbOwnerPublicActions.integration.test.ts index 57dcf4a0..c354aa5f 100644 --- a/src/decorators/arbOwnerPublicActions.integration.test.ts +++ b/src/decorators/arbOwnerPublicActions.integration.test.ts @@ -15,7 +15,7 @@ const randomAccount = privateKeyToAccount(generatePrivateKey()); const client = createPublicClient({ chain: nitroTestnodeL2, transport: http(), -}).extend(arbOwnerPublicActions({ arbOSVersion: 20 })); +}).extend(arbOwnerPublicActions({ arbOsVersion: 20 })); it('successfully fetches network fee receiver', async () => { const result = await client.arbOwnerReadContract({ diff --git a/src/decorators/arbOwnerPublicActions.ts b/src/decorators/arbOwnerPublicActions.ts index b819a2a0..6c2ce6bc 100644 --- a/src/decorators/arbOwnerPublicActions.ts +++ b/src/decorators/arbOwnerPublicActions.ts @@ -8,7 +8,6 @@ import { } from '../arbOwnerReadContract'; import { arbOwnerPrepareTransactionRequest, - ArbOwnerPrepareTransactionRequestFunctionName, ArbOwnerPrepareTransactionRequestParameters, } from '../arbOwnerPrepareTransactionRequest'; import { ArbOSVersions } from '../contracts'; @@ -22,9 +21,9 @@ export type ArbOwnerPublicActions< ) => Promise>; arbOwnerPrepareTransactionRequest: < - TFunctionName extends ArbOwnerPrepareTransactionRequestFunctionName, + TFunctionName extends ArbOwnerPublicFunctionName, >( - args: ArbOwnerPrepareTransactionRequestParameters, + args: ArbOwnerPrepareTransactionRequestParameters, ) => Promise & { chainId: number }>; }; @@ -32,15 +31,15 @@ export function arbOwnerPublicActions< TArbOsVersion extends ArbOSVersions, TTransport extends Transport = Transport, TChain extends Chain | undefined = Chain | undefined, ->({ arbOSVersion }: { arbOSVersion: TArbOsVersion }) { +>({ arbOsVersion }: { arbOsVersion: TArbOsVersion }) { return ( client: PublicClient, ): ArbOwnerPublicActions => { return { - arbOwnerReadContract: (args) => arbOwnerReadContract(client, { ...args, arbOSVersion }), + arbOwnerReadContract: (args) => arbOwnerReadContract(client, { ...args, arbOsVersion }), arbOwnerPrepareTransactionRequest: (args) => - arbOwnerPrepareTransactionRequest(client, { ...args, arbOSVersion }), + arbOwnerPrepareTransactionRequest(client, { ...args, arbOsVersion }), }; }; } diff --git a/src/decorators/arbOwnerPublicActions.unit.test.ts b/src/decorators/arbOwnerPublicActions.unit.test.ts index efc0f64a..639b8371 100644 --- a/src/decorators/arbOwnerPublicActions.unit.test.ts +++ b/src/decorators/arbOwnerPublicActions.unit.test.ts @@ -1,81 +1,112 @@ -import { it, expect, expectTypeOf } from 'vitest'; +import { describe, it, expect, expectTypeOf } from 'vitest'; import { AbiFunctionNotFoundError, createPublicClient, http } from 'viem'; import { nitroTestnodeL2 } from '../chains'; import { arbOwnerPublicActions } from './arbOwnerPublicActions'; +import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'; const client10 = createPublicClient({ chain: nitroTestnodeL2, transport: http(), -}).extend(arbOwnerPublicActions({ arbOSVersion: 10 })); +}).extend(arbOwnerPublicActions({ arbOsVersion: 10 })); const client11 = createPublicClient({ chain: nitroTestnodeL2, transport: http(), -}).extend(arbOwnerPublicActions({ arbOSVersion: 11 })); +}).extend(arbOwnerPublicActions({ arbOsVersion: 11 })); const client20 = createPublicClient({ chain: nitroTestnodeL2, transport: http(), -}).extend(arbOwnerPublicActions({ arbOSVersion: 20 })); +}).extend(arbOwnerPublicActions({ arbOsVersion: 20 })); +const randomAccount = privateKeyToAccount(generatePrivateKey()); +const upgradeExecutorAddress = '0x24198F8A339cd3C47AEa3A764A20d2dDaB4D1b5b'; -it('Accept function name based on arbOSVersion', async () => { - // Version 10 - client10.arbOwnerReadContract({ - functionName: 'onlyOnArbOS10', - }); +describe('Accept function name based on arbOSVersion', async () => { + it('Version 10', () => { + expectTypeOf>().toBeCallableWith({ + functionName: 'onlyOnArbOS10' + }) - expect( - client10.arbOwnerReadContract({ - // @ts-expect-error Not available for version 10 - functionName: 'onlyOnArbOS20', - }), - ).rejects.toThrowError(AbiFunctionNotFoundError); + expectTypeOf>().toBeCallableWith({ + functionName: 'setL1PricingRewardRecipient', + account: randomAccount.address, + upgradeExecutor: upgradeExecutorAddress, + args: [[randomAccount.address, randomAccount.address]] + }) - // Version 11 - client11.arbOwnerReadContract({ - functionName: 'onlyOnArbOS11', - }); + expect( + client10.arbOwnerReadContract({ + // @ts-expect-error Not available for version 10 + functionName: 'onlyOnArbOS20', + }), + ).rejects.toThrowError(AbiFunctionNotFoundError); - expect( - client11.arbOwnerReadContract({ - // @ts-expect-error Not available for version 11 - functionName: 'onlyOnArbOS20', - }), - ).rejects.toThrowError(AbiFunctionNotFoundError); - // Version 20 - client20.arbOwnerReadContract({ - functionName: 'getInfraFeeAccount', - }); + }) - expect( - client20.arbOwnerReadContract({ - // @ts-expect-error Not available for version 20 - functionName: 'onlyOnArbOS10', - }), - ).rejects.toThrowError(AbiFunctionNotFoundError); + it('Version 11', () => { + expectTypeOf>().toBeCallableWith({ + functionName: 'onlyOnArbOS11' + }) + + expectTypeOf>().toBeCallableWith({ + functionName: 'setL1PricingRewardRecipient', + account: randomAccount.address, + upgradeExecutor: upgradeExecutorAddress, + args: [100n] + }) + + expect( + client11.arbOwnerReadContract({ + // @ts-expect-error Not available for version 11 + functionName: 'onlyOnArbOS20', + }), + ).rejects.toThrowError(AbiFunctionNotFoundError); + }) + + it('Version 20', () => { + expectTypeOf>().toBeCallableWith({ + functionName: 'getInfraFeeAccount' + }) + + expectTypeOf>().toBeCallableWith({ + functionName: 'setL1PricingRewardRecipient', + account: randomAccount.address, + upgradeExecutor: upgradeExecutorAddress, + args: [randomAccount.address] + }) + + expect( + client20.arbOwnerReadContract({ + // @ts-expect-error Not available for version 20 + functionName: 'onlyOnArbOS10', + }), + ).rejects.toThrowError(AbiFunctionNotFoundError); + }) }); // Those tests won't fail if the return type is wrong // But they will display an error in the IDE -it('Type return values for function in multiple versions', () => { - // Version 10 - expectTypeOf( - client10.arbOwnerReadContract({ - functionName: 'getAllChainOwners', - }), - ).resolves.toEqualTypeOf<`0x${string}`>(); - - // Version 11 - expectTypeOf( - client11.arbOwnerReadContract({ - functionName: 'getAllChainOwners', - }), - ).resolves.toEqualTypeOf(); +describe('Type return values for function in multiple versions', () => { + it('Version 10', () => { + expectTypeOf( + client10.arbOwnerReadContract({ + functionName: 'getAllChainOwners', + }), + ).resolves.toEqualTypeOf<`0x${string}`>(); + }) - // Version 20 - expectTypeOf( - client20.arbOwnerReadContract({ - functionName: 'getAllChainOwners', - }), - ).resolves.toEqualTypeOf(); -}); + it('Version 11', () => { + expectTypeOf( + client11.arbOwnerReadContract({ + functionName: 'getAllChainOwners', + }), + ).resolves.toEqualTypeOf(); + }) + it('Version 11', () => { + expectTypeOf( + client20.arbOwnerReadContract({ + functionName: 'getAllChainOwners', + }), + ).resolves.toEqualTypeOf(); + }) +}); \ No newline at end of file From 9c3beec4c9f52ef666fc8cff8009dd87e57a71a3 Mon Sep 17 00:00:00 2001 From: Christophe Date: Tue, 7 May 2024 19:54:38 +0200 Subject: [PATCH 07/14] Format --- src/arbOwnerPrepareTransactionRequest.ts | 8 ++- .../arbOwnerPublicActions.unit.test.ts | 52 ++++++++++--------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/arbOwnerPrepareTransactionRequest.ts b/src/arbOwnerPrepareTransactionRequest.ts index bd33429a..1aedb57e 100644 --- a/src/arbOwnerPrepareTransactionRequest.ts +++ b/src/arbOwnerPrepareTransactionRequest.ts @@ -28,7 +28,9 @@ function arbOwnerPrepareFunctionData< if (!upgradeExecutor) { return { to: arbOwner.address, - data: encodeFunctionData(params as EncodeFunctionDataParameters, TFunctionName>), + data: encodeFunctionData( + params as EncodeFunctionDataParameters, TFunctionName>, + ), value: BigInt(0), }; } @@ -39,7 +41,9 @@ function arbOwnerPrepareFunctionData< functionName: 'executeCall', args: [ arbOwner.address, // target - encodeFunctionData(params as EncodeFunctionDataParameters, TFunctionName>), // targetCallData + encodeFunctionData( + params as EncodeFunctionDataParameters, TFunctionName>, + ), // targetCallData ], }), value: BigInt(0), diff --git a/src/decorators/arbOwnerPublicActions.unit.test.ts b/src/decorators/arbOwnerPublicActions.unit.test.ts index 639b8371..568a5189 100644 --- a/src/decorators/arbOwnerPublicActions.unit.test.ts +++ b/src/decorators/arbOwnerPublicActions.unit.test.ts @@ -23,15 +23,17 @@ const upgradeExecutorAddress = '0x24198F8A339cd3C47AEa3A764A20d2dDaB4D1b5b'; describe('Accept function name based on arbOSVersion', async () => { it('Version 10', () => { expectTypeOf>().toBeCallableWith({ - functionName: 'onlyOnArbOS10' - }) + functionName: 'onlyOnArbOS10', + }); - expectTypeOf>().toBeCallableWith({ + expectTypeOf< + typeof client10.arbOwnerPrepareTransactionRequest<'setL1PricingRewardRecipient'> + >().toBeCallableWith({ functionName: 'setL1PricingRewardRecipient', account: randomAccount.address, upgradeExecutor: upgradeExecutorAddress, - args: [[randomAccount.address, randomAccount.address]] - }) + args: [[randomAccount.address, randomAccount.address]], + }); expect( client10.arbOwnerReadContract({ @@ -39,21 +41,21 @@ describe('Accept function name based on arbOSVersion', async () => { functionName: 'onlyOnArbOS20', }), ).rejects.toThrowError(AbiFunctionNotFoundError); - - - }) + }); it('Version 11', () => { expectTypeOf>().toBeCallableWith({ - functionName: 'onlyOnArbOS11' - }) + functionName: 'onlyOnArbOS11', + }); - expectTypeOf>().toBeCallableWith({ + expectTypeOf< + typeof client11.arbOwnerPrepareTransactionRequest<'setL1PricingRewardRecipient'> + >().toBeCallableWith({ functionName: 'setL1PricingRewardRecipient', account: randomAccount.address, upgradeExecutor: upgradeExecutorAddress, - args: [100n] - }) + args: [100n], + }); expect( client11.arbOwnerReadContract({ @@ -61,19 +63,21 @@ describe('Accept function name based on arbOSVersion', async () => { functionName: 'onlyOnArbOS20', }), ).rejects.toThrowError(AbiFunctionNotFoundError); - }) + }); it('Version 20', () => { expectTypeOf>().toBeCallableWith({ - functionName: 'getInfraFeeAccount' - }) + functionName: 'getInfraFeeAccount', + }); - expectTypeOf>().toBeCallableWith({ + expectTypeOf< + typeof client20.arbOwnerPrepareTransactionRequest<'setL1PricingRewardRecipient'> + >().toBeCallableWith({ functionName: 'setL1PricingRewardRecipient', account: randomAccount.address, upgradeExecutor: upgradeExecutorAddress, - args: [randomAccount.address] - }) + args: [randomAccount.address], + }); expect( client20.arbOwnerReadContract({ @@ -81,7 +85,7 @@ describe('Accept function name based on arbOSVersion', async () => { functionName: 'onlyOnArbOS10', }), ).rejects.toThrowError(AbiFunctionNotFoundError); - }) + }); }); // Those tests won't fail if the return type is wrong @@ -93,7 +97,7 @@ describe('Type return values for function in multiple versions', () => { functionName: 'getAllChainOwners', }), ).resolves.toEqualTypeOf<`0x${string}`>(); - }) + }); it('Version 11', () => { expectTypeOf( @@ -101,12 +105,12 @@ describe('Type return values for function in multiple versions', () => { functionName: 'getAllChainOwners', }), ).resolves.toEqualTypeOf(); - }) + }); it('Version 11', () => { expectTypeOf( client20.arbOwnerReadContract({ functionName: 'getAllChainOwners', }), ).resolves.toEqualTypeOf(); - }) -}); \ No newline at end of file + }); +}); From 797c5b44663559a88546da613d69c5bc4a9a2b12 Mon Sep 17 00:00:00 2001 From: Christophe Date: Tue, 7 May 2024 18:05:34 +0000 Subject: [PATCH 08/14] Update parameter name in integration test --- .../arbOwnerPublicActionsUpgradeExecutor.integration.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/decorators/arbOwnerPublicActionsUpgradeExecutor.integration.test.ts b/src/decorators/arbOwnerPublicActionsUpgradeExecutor.integration.test.ts index 07f3216d..431ccc8d 100644 --- a/src/decorators/arbOwnerPublicActionsUpgradeExecutor.integration.test.ts +++ b/src/decorators/arbOwnerPublicActionsUpgradeExecutor.integration.test.ts @@ -20,7 +20,7 @@ const client = createPublicClient({ chain: nitroTestnodeL3, transport: http(), }) - .extend(arbOwnerPublicActions({ arbOSVersion: 20 })) + .extend(arbOwnerPublicActions({ arbOsVersion: 20 })) .extend(arbGasInfoPublicActions); it('succesfully adds chain owner using upgrade executor', async () => { From a4bb1c2826047857da369e5705bb8b83c0695308 Mon Sep 17 00:00:00 2001 From: Christophe Date: Tue, 7 May 2024 19:19:50 +0000 Subject: [PATCH 09/14] Fix type --- src/decorators/arbOwnerPublicActions.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/decorators/arbOwnerPublicActions.ts b/src/decorators/arbOwnerPublicActions.ts index 6c2ce6bc..0db18430 100644 --- a/src/decorators/arbOwnerPublicActions.ts +++ b/src/decorators/arbOwnerPublicActions.ts @@ -39,7 +39,11 @@ export function arbOwnerPublicActions< arbOwnerReadContract: (args) => arbOwnerReadContract(client, { ...args, arbOsVersion }), arbOwnerPrepareTransactionRequest: (args) => - arbOwnerPrepareTransactionRequest(client, { ...args, arbOsVersion }), + // @ts-ignore (todo: fix viem type issue) + arbOwnerPrepareTransactionRequest(client, { + ...args, + arbOsVersion, + }), }; }; } From 204310757f0d809ebb91bf8bb66e231a6f50cfbc Mon Sep 17 00:00:00 2001 From: Christophe Date: Wed, 15 May 2024 15:50:00 +0000 Subject: [PATCH 10/14] Fix typing Allow arbOwnerPublicActions to be either called: - without parameters (`.extend(arbOwnerPublicActions)`) - with parameters (`.extend(arbOwnerPublicActions({ arbOsVersion: X}))`) --- src/decorators/arbOwnerPublicActions.ts | 56 +++++++++++++++---- .../arbOwnerPublicActions.unit.test.ts | 43 +++++++++++++- 2 files changed, 85 insertions(+), 14 deletions(-) diff --git a/src/decorators/arbOwnerPublicActions.ts b/src/decorators/arbOwnerPublicActions.ts index 0db18430..f7335b5f 100644 --- a/src/decorators/arbOwnerPublicActions.ts +++ b/src/decorators/arbOwnerPublicActions.ts @@ -1,4 +1,4 @@ -import { Transport, Chain, PrepareTransactionRequestReturnType, PublicClient } from 'viem'; +import { Transport, Chain, PrepareTransactionRequestReturnType, Client, Account } from 'viem'; import { arbOwnerReadContract, @@ -13,7 +13,7 @@ import { import { ArbOSVersions } from '../contracts'; export type ArbOwnerPublicActions< - TArbOsVersion extends ArbOSVersions, + TArbOsVersion extends ArbOSVersions = 20, TChain extends Chain | undefined = Chain | undefined, > = { arbOwnerReadContract: >( @@ -24,26 +24,58 @@ export type ArbOwnerPublicActions< TFunctionName extends ArbOwnerPublicFunctionName, >( args: ArbOwnerPrepareTransactionRequestParameters, - ) => Promise & { chainId: number }>; + ) => Promise>; }; +const defaultArbOsVersion = 20; + +// arbOsVersion is passed as a parameter `client.extend(arbOwnerPublicActions({ arbOsVersion: 10 }))` export function arbOwnerPublicActions< TArbOsVersion extends ArbOSVersions, TTransport extends Transport = Transport, TChain extends Chain | undefined = Chain | undefined, ->({ arbOsVersion }: { arbOsVersion: TArbOsVersion }) { - return ( - client: PublicClient, - ): ArbOwnerPublicActions => { - return { - arbOwnerReadContract: (args) => arbOwnerReadContract(client, { ...args, arbOsVersion }), - + TAccount extends Account | undefined = Account | undefined, +>(param: { + arbOsVersion: TArbOsVersion; +}): (client: Client) => ArbOwnerPublicActions; +// No parameter are passed `client.extend(arbOwnerPublicActions)` +export function arbOwnerPublicActions< + TArbOsVersion extends ArbOSVersions, + TTransport extends Transport = Transport, + TChain extends Chain | undefined = Chain | undefined, + TAccount extends Account | undefined = Account | undefined, +>(param: Client): ArbOwnerPublicActions; +export function arbOwnerPublicActions< + TArbOsVersion extends ArbOSVersions, + TTransport extends Transport = Transport, + TChain extends Chain | undefined = Chain | undefined, + TAccount extends Account | undefined = Account | undefined, +>(paramOrClient: { arbOsVersion: TArbOsVersion } | Client) { + if ('arbOsVersion' in paramOrClient) { + const result: (client: Client) => ArbOwnerPublicActions = (client) => ({ + arbOwnerReadContract: (args) => + arbOwnerReadContract(client, { ...args, arbOsVersion: paramOrClient.arbOsVersion }), arbOwnerPrepareTransactionRequest: (args) => // @ts-ignore (todo: fix viem type issue) arbOwnerPrepareTransactionRequest(client, { ...args, - arbOsVersion, + arbOsVersion: paramOrClient.arbOsVersion, }), - }; + }); + + return result; + } + + const result: ArbOwnerPublicActions = { + arbOwnerReadContract: (args) => + // @ts-ignore (todo: fix viem type issue) + arbOwnerReadContract(paramOrClient, { ...args, arbOsVersion: defaultArbOsVersion }), + arbOwnerPrepareTransactionRequest: (args) => + // @ts-ignore (todo: fix viem type issue) + arbOwnerPrepareTransactionRequest(paramOrClient, { + ...args, + arbOsVersion: defaultArbOsVersion, + }), }; + return result; } diff --git a/src/decorators/arbOwnerPublicActions.unit.test.ts b/src/decorators/arbOwnerPublicActions.unit.test.ts index 568a5189..05f1f86d 100644 --- a/src/decorators/arbOwnerPublicActions.unit.test.ts +++ b/src/decorators/arbOwnerPublicActions.unit.test.ts @@ -5,6 +5,10 @@ import { nitroTestnodeL2 } from '../chains'; import { arbOwnerPublicActions } from './arbOwnerPublicActions'; import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'; +const clientWithoutParam = createPublicClient({ + chain: nitroTestnodeL2, + transport: http(), +}).extend(arbOwnerPublicActions); const client10 = createPublicClient({ chain: nitroTestnodeL2, transport: http(), @@ -19,6 +23,10 @@ const client20 = createPublicClient({ }).extend(arbOwnerPublicActions({ arbOsVersion: 20 })); const randomAccount = privateKeyToAccount(generatePrivateKey()); const upgradeExecutorAddress = '0x24198F8A339cd3C47AEa3A764A20d2dDaB4D1b5b'; +const client = createPublicClient({ + chain: nitroTestnodeL2, + transport: http(), +}) describe('Accept function name based on arbOSVersion', async () => { it('Version 10', () => { @@ -86,6 +94,31 @@ describe('Accept function name based on arbOSVersion', async () => { }), ).rejects.toThrowError(AbiFunctionNotFoundError); }); + + it('Default version (20)', () => { + // arbOwnerPublicActions without params is defaulted to arbOsVersion 20 + expectTypeOf< + typeof clientWithoutParam.arbOwnerReadContract<'getInfraFeeAccount'> + >().toBeCallableWith({ + functionName: 'getInfraFeeAccount', + }); + + expectTypeOf< + typeof clientWithoutParam.arbOwnerPrepareTransactionRequest<'setL1PricingRewardRecipient'> + >().toBeCallableWith({ + functionName: 'setL1PricingRewardRecipient', + account: randomAccount.address, + upgradeExecutor: upgradeExecutorAddress, + args: [randomAccount.address], + }); + + expect( + clientWithoutParam.arbOwnerReadContract({ + // @ts-expect-error Not available for version 20 + functionName: 'onlyOnArbOS10', + }), + ).rejects.toThrowError(AbiFunctionNotFoundError); + }); }); // Those tests won't fail if the return type is wrong @@ -98,7 +131,6 @@ describe('Type return values for function in multiple versions', () => { }), ).resolves.toEqualTypeOf<`0x${string}`>(); }); - it('Version 11', () => { expectTypeOf( client11.arbOwnerReadContract({ @@ -106,11 +138,18 @@ describe('Type return values for function in multiple versions', () => { }), ).resolves.toEqualTypeOf(); }); - it('Version 11', () => { + it('Version 20', () => { expectTypeOf( client20.arbOwnerReadContract({ functionName: 'getAllChainOwners', }), ).resolves.toEqualTypeOf(); }); + it('Default version (20)', () => { + expectTypeOf( + clientWithoutParam.arbOwnerReadContract({ + functionName: 'getAllChainOwners', + }), + ).resolves.toEqualTypeOf(); + }); }); From 34f0075a561f280b5d23a6051b912246d1469853 Mon Sep 17 00:00:00 2001 From: Christophe Date: Wed, 15 May 2024 15:53:44 +0000 Subject: [PATCH 11/14] Replace PublicClient with Client --- src/arbOwnerReadContract.ts | 4 ++-- src/decorators/arbOwnerPublicActions.ts | 4 +++- src/decorators/arbOwnerPublicActions.unit.test.ts | 7 ------- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/arbOwnerReadContract.ts b/src/arbOwnerReadContract.ts index 0d2bb9c0..8eb4892f 100644 --- a/src/arbOwnerReadContract.ts +++ b/src/arbOwnerReadContract.ts @@ -1,4 +1,4 @@ -import { Chain, GetFunctionArgs, PublicClient, ReadContractReturnType, Transport } from 'viem'; +import { Chain, GetFunctionArgs, Client, ReadContractReturnType, Transport } from 'viem'; import { ArbOSVersions, ArbOwnerABIs, arbOwnerPublic } from './contracts'; import { GetFunctionName } from './types/utils'; @@ -27,7 +27,7 @@ export function arbOwnerReadContract< TChain extends Chain | undefined, TFunctionName extends ArbOwnerPublicFunctionName, >( - client: PublicClient, + client: Client, params: ArbOwnerReadContractParameters & { arbOsVersion: TArbOsVersion; }, diff --git a/src/decorators/arbOwnerPublicActions.ts b/src/decorators/arbOwnerPublicActions.ts index f7335b5f..b341f1a4 100644 --- a/src/decorators/arbOwnerPublicActions.ts +++ b/src/decorators/arbOwnerPublicActions.ts @@ -52,7 +52,9 @@ export function arbOwnerPublicActions< TAccount extends Account | undefined = Account | undefined, >(paramOrClient: { arbOsVersion: TArbOsVersion } | Client) { if ('arbOsVersion' in paramOrClient) { - const result: (client: Client) => ArbOwnerPublicActions = (client) => ({ + const result: ( + client: Client, + ) => ArbOwnerPublicActions = (client) => ({ arbOwnerReadContract: (args) => arbOwnerReadContract(client, { ...args, arbOsVersion: paramOrClient.arbOsVersion }), arbOwnerPrepareTransactionRequest: (args) => diff --git a/src/decorators/arbOwnerPublicActions.unit.test.ts b/src/decorators/arbOwnerPublicActions.unit.test.ts index 05f1f86d..659a34cd 100644 --- a/src/decorators/arbOwnerPublicActions.unit.test.ts +++ b/src/decorators/arbOwnerPublicActions.unit.test.ts @@ -23,10 +23,6 @@ const client20 = createPublicClient({ }).extend(arbOwnerPublicActions({ arbOsVersion: 20 })); const randomAccount = privateKeyToAccount(generatePrivateKey()); const upgradeExecutorAddress = '0x24198F8A339cd3C47AEa3A764A20d2dDaB4D1b5b'; -const client = createPublicClient({ - chain: nitroTestnodeL2, - transport: http(), -}) describe('Accept function name based on arbOSVersion', async () => { it('Version 10', () => { @@ -50,7 +46,6 @@ describe('Accept function name based on arbOSVersion', async () => { }), ).rejects.toThrowError(AbiFunctionNotFoundError); }); - it('Version 11', () => { expectTypeOf>().toBeCallableWith({ functionName: 'onlyOnArbOS11', @@ -72,7 +67,6 @@ describe('Accept function name based on arbOSVersion', async () => { }), ).rejects.toThrowError(AbiFunctionNotFoundError); }); - it('Version 20', () => { expectTypeOf>().toBeCallableWith({ functionName: 'getInfraFeeAccount', @@ -94,7 +88,6 @@ describe('Accept function name based on arbOSVersion', async () => { }), ).rejects.toThrowError(AbiFunctionNotFoundError); }); - it('Default version (20)', () => { // arbOwnerPublicActions without params is defaulted to arbOsVersion 20 expectTypeOf< From e9a335ae7cc4d3e3735cf236389cd4f867f279a0 Mon Sep 17 00:00:00 2001 From: Christophe Date: Wed, 15 May 2024 16:00:34 +0000 Subject: [PATCH 12/14] Format --- src/decorators/arbOwnerPublicActions.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/decorators/arbOwnerPublicActions.ts b/src/decorators/arbOwnerPublicActions.ts index b341f1a4..4bbae5b6 100644 --- a/src/decorators/arbOwnerPublicActions.ts +++ b/src/decorators/arbOwnerPublicActions.ts @@ -44,7 +44,9 @@ export function arbOwnerPublicActions< TTransport extends Transport = Transport, TChain extends Chain | undefined = Chain | undefined, TAccount extends Account | undefined = Account | undefined, ->(param: Client): ArbOwnerPublicActions; +>( + param: Client, +): ArbOwnerPublicActions; export function arbOwnerPublicActions< TArbOsVersion extends ArbOSVersions, TTransport extends Transport = Transport, From 1cacecce2edf4614be363fe384c96fb1199b53ff Mon Sep 17 00:00:00 2001 From: Christophe Date: Wed, 15 May 2024 17:50:56 +0000 Subject: [PATCH 13/14] Replace PublicClient with Client --- src/arbOwnerPrepareTransactionRequest.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/arbOwnerPrepareTransactionRequest.ts b/src/arbOwnerPrepareTransactionRequest.ts index 1aedb57e..9787780e 100644 --- a/src/arbOwnerPrepareTransactionRequest.ts +++ b/src/arbOwnerPrepareTransactionRequest.ts @@ -1,5 +1,5 @@ import { - PublicClient, + Client, encodeFunctionData, EncodeFunctionDataParameters, Address, @@ -64,7 +64,7 @@ export async function arbOwnerPrepareTransactionRequest< TChain extends Chain | undefined, TFunctionName extends ArbOwnerPublicFunctionName, >( - client: PublicClient, + client: Client, params: ArbOwnerPrepareTransactionRequestParameters & { arbOsVersion: TArbOsVersion; }, From e063eae6ee5b2fc0ac2e7e571b34b71ced3a6020 Mon Sep 17 00:00:00 2001 From: Christophe Date: Wed, 15 May 2024 17:57:55 +0000 Subject: [PATCH 14/14] Allow actions to be called manually --- src/decorators/arbOwnerPublicActions.ts | 42 ++++++++-- .../arbOwnerPublicActions.unit.test.ts | 83 +++++++++++++++++-- 2 files changed, 109 insertions(+), 16 deletions(-) diff --git a/src/decorators/arbOwnerPublicActions.ts b/src/decorators/arbOwnerPublicActions.ts index 4bbae5b6..614bbdac 100644 --- a/src/decorators/arbOwnerPublicActions.ts +++ b/src/decorators/arbOwnerPublicActions.ts @@ -24,12 +24,24 @@ export type ArbOwnerPublicActions< TFunctionName extends ArbOwnerPublicFunctionName, >( args: ArbOwnerPrepareTransactionRequestParameters, - ) => Promise>; + ) => Promise & { chainId: number }>; }; const defaultArbOsVersion = 20; -// arbOsVersion is passed as a parameter `client.extend(arbOwnerPublicActions({ arbOsVersion: 10 }))` +// Client is passed explicitly +// `arbOwnerPublicActions(client)`, `arbOwnerPublicActions(client, { arbOsVersion: 20 })` +export function arbOwnerPublicActions< + TArbOsVersion extends ArbOSVersions, + TTransport extends Transport = Transport, + TChain extends Chain | undefined = Chain | undefined, + TAccount extends Account | undefined = Account | undefined, +>( + client: Client, + { arbOsVersion }: { arbOsVersion: TArbOsVersion }, +): ArbOwnerPublicActions; +// arbOsVersion is passed as a parameter +// `client.extend(arbOwnerPublicActions({ arbOsVersion: 10 }))` export function arbOwnerPublicActions< TArbOsVersion extends ArbOSVersions, TTransport extends Transport = Transport, @@ -37,8 +49,9 @@ export function arbOwnerPublicActions< TAccount extends Account | undefined = Account | undefined, >(param: { arbOsVersion: TArbOsVersion; -}): (client: Client) => ArbOwnerPublicActions; -// No parameter are passed `client.extend(arbOwnerPublicActions)` +}): (client: Client) => ArbOwnerPublicActions; +// No parameter are passed +// `client.extend(arbOwnerPublicActions)` export function arbOwnerPublicActions< TArbOsVersion extends ArbOSVersions, TTransport extends Transport = Transport, @@ -52,7 +65,11 @@ export function arbOwnerPublicActions< TTransport extends Transport = Transport, TChain extends Chain | undefined = Chain | undefined, TAccount extends Account | undefined = Account | undefined, ->(paramOrClient: { arbOsVersion: TArbOsVersion } | Client) { +>( + paramOrClient: { arbOsVersion: TArbOsVersion } | Client, + options?: { arbOsVersion: TArbOsVersion }, +) { + // arbOsVersion is passed as a parameter, return actions with curried arbOsVersion if ('arbOsVersion' in paramOrClient) { const result: ( client: Client, @@ -70,15 +87,24 @@ export function arbOwnerPublicActions< return result; } - const result: ArbOwnerPublicActions = { + /** + * Parameter is a client, we either have: + * - client.extend(arbOwnerPublicActions) + * - arbOwnerPublicActions(client) + * - arbOwnerPublicActions(client, { arbOsVersion: X }) + * + * If we don't have arbOsVersion (the 2 first cases), default the version to defaultArbOsVersion + */ + const version = options?.arbOsVersion ?? defaultArbOsVersion; + const result: ArbOwnerPublicActions = { arbOwnerReadContract: (args) => // @ts-ignore (todo: fix viem type issue) - arbOwnerReadContract(paramOrClient, { ...args, arbOsVersion: defaultArbOsVersion }), + arbOwnerReadContract(paramOrClient, { ...args, arbOsVersion: version }), arbOwnerPrepareTransactionRequest: (args) => // @ts-ignore (todo: fix viem type issue) arbOwnerPrepareTransactionRequest(paramOrClient, { ...args, - arbOsVersion: defaultArbOsVersion, + arbOsVersion: version, }), }; return result; diff --git a/src/decorators/arbOwnerPublicActions.unit.test.ts b/src/decorators/arbOwnerPublicActions.unit.test.ts index 659a34cd..ab335df6 100644 --- a/src/decorators/arbOwnerPublicActions.unit.test.ts +++ b/src/decorators/arbOwnerPublicActions.unit.test.ts @@ -24,8 +24,15 @@ const client20 = createPublicClient({ const randomAccount = privateKeyToAccount(generatePrivateKey()); const upgradeExecutorAddress = '0x24198F8A339cd3C47AEa3A764A20d2dDaB4D1b5b'; +const client = createPublicClient({ + chain: nitroTestnodeL2, + transport: http(), +}); +const actionsWithVersion = arbOwnerPublicActions(client, { arbOsVersion: 11 }); +const actionsWithDefaultVersion = arbOwnerPublicActions(client); + describe('Accept function name based on arbOSVersion', async () => { - it('Version 10', () => { + it('Client with actions (version 10)', () => { expectTypeOf>().toBeCallableWith({ functionName: 'onlyOnArbOS10', }); @@ -46,7 +53,7 @@ describe('Accept function name based on arbOSVersion', async () => { }), ).rejects.toThrowError(AbiFunctionNotFoundError); }); - it('Version 11', () => { + it('Client with actions (version 11)', () => { expectTypeOf>().toBeCallableWith({ functionName: 'onlyOnArbOS11', }); @@ -67,7 +74,7 @@ describe('Accept function name based on arbOSVersion', async () => { }), ).rejects.toThrowError(AbiFunctionNotFoundError); }); - it('Version 20', () => { + it('Client with actions (version 20)', () => { expectTypeOf>().toBeCallableWith({ functionName: 'getInfraFeeAccount', }); @@ -88,7 +95,7 @@ describe('Accept function name based on arbOSVersion', async () => { }), ).rejects.toThrowError(AbiFunctionNotFoundError); }); - it('Default version (20)', () => { + it('Client with actions (default version)', () => { // arbOwnerPublicActions without params is defaulted to arbOsVersion 20 expectTypeOf< typeof clientWithoutParam.arbOwnerReadContract<'getInfraFeeAccount'> @@ -112,37 +119,97 @@ describe('Accept function name based on arbOSVersion', async () => { }), ).rejects.toThrowError(AbiFunctionNotFoundError); }); + it('Standalone actions (version 11) ', () => { + expectTypeOf< + typeof actionsWithVersion.arbOwnerReadContract<'onlyOnArbOS11'> + >().toBeCallableWith({ + functionName: 'onlyOnArbOS11', + }); + + expectTypeOf< + typeof actionsWithVersion.arbOwnerPrepareTransactionRequest<'setL1PricingRewardRecipient'> + >().toBeCallableWith({ + functionName: 'setL1PricingRewardRecipient', + account: randomAccount.address, + upgradeExecutor: upgradeExecutorAddress, + args: [100n], + }); + + expect( + actionsWithVersion.arbOwnerReadContract({ + // @ts-expect-error Not available for version 11 + functionName: 'onlyOnArbOS20', + }), + ).rejects.toThrowError(AbiFunctionNotFoundError); + }); + it('Standalone actions (default version) ', () => { + expectTypeOf< + typeof actionsWithDefaultVersion.arbOwnerReadContract<'getInfraFeeAccount'> + >().toBeCallableWith({ + functionName: 'getInfraFeeAccount', + }); + + expectTypeOf< + typeof actionsWithDefaultVersion.arbOwnerPrepareTransactionRequest<'setL1PricingRewardRecipient'> + >().toBeCallableWith({ + functionName: 'setL1PricingRewardRecipient', + account: randomAccount.address, + upgradeExecutor: upgradeExecutorAddress, + args: [randomAccount.address], + }); + + expect( + actionsWithDefaultVersion.arbOwnerReadContract({ + // @ts-expect-error Not available for version 20 + functionName: 'onlyOnArbOS10', + }), + ).rejects.toThrowError(AbiFunctionNotFoundError); + }); }); // Those tests won't fail if the return type is wrong // But they will display an error in the IDE describe('Type return values for function in multiple versions', () => { - it('Version 10', () => { + it('Client with actions (Version 10)', () => { expectTypeOf( client10.arbOwnerReadContract({ functionName: 'getAllChainOwners', }), ).resolves.toEqualTypeOf<`0x${string}`>(); }); - it('Version 11', () => { + it('Client with actions (Version 11)', () => { expectTypeOf( client11.arbOwnerReadContract({ functionName: 'getAllChainOwners', }), ).resolves.toEqualTypeOf(); }); - it('Version 20', () => { + it('Client with actions (Version 20)', () => { expectTypeOf( client20.arbOwnerReadContract({ functionName: 'getAllChainOwners', }), ).resolves.toEqualTypeOf(); }); - it('Default version (20)', () => { + it('Client with actions (default version)', () => { expectTypeOf( clientWithoutParam.arbOwnerReadContract({ functionName: 'getAllChainOwners', }), ).resolves.toEqualTypeOf(); }); + it('Standalone actions (version 11)', () => { + expectTypeOf( + actionsWithVersion.arbOwnerReadContract({ + functionName: 'getAllChainOwners', + }), + ).resolves.toEqualTypeOf(); + }); + it('Standalone actions (default version)', () => { + expectTypeOf( + actionsWithDefaultVersion.arbOwnerReadContract({ + functionName: 'getAllChainOwners', + }), + ).resolves.toEqualTypeOf(); + }); });