Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support multiple ArbOwner versions #85

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
66 changes: 30 additions & 36 deletions src/arbOwnerPrepareTransactionRequest.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,35 @@
import {
PublicClient,
Client,
encodeFunctionData,
EncodeFunctionDataParameters,
Address,
Chain,
Transport,
GetFunctionArgs,
} from 'viem';

import { arbOwner } 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<ArbOwnerAbi>;
export type ArbOwnerEncodeFunctionDataParameters<
TFunctionName extends ArbOwnerPrepareTransactionRequestFunctionName,
> = EncodeFunctionDataParameters<ArbOwnerAbi, TFunctionName>;

function arbOwnerEncodeFunctionData<
TFunctionName extends ArbOwnerPrepareTransactionRequestFunctionName,
>({ functionName, abi, args }: ArbOwnerEncodeFunctionDataParameters<TFunctionName>) {
return encodeFunctionData({
abi,
functionName,
args,
});
}

type ArbOwnerPrepareFunctionDataParameters<
TFunctionName extends ArbOwnerPrepareTransactionRequestFunctionName,
> = ArbOwnerEncodeFunctionDataParameters<TFunctionName> & {
TArbOsVersion extends ArbOSVersions,
TFunctionName extends ArbOwnerPublicFunctionName<TArbOsVersion>,
> = EncodeFunctionDataParameters<ArbOwnerPublicAbi<TArbOsVersion>, TFunctionName> & {
upgradeExecutor: Address | false;
abi: ArbOwnerAbi;
};

function arbOwnerPrepareFunctionData<
TFunctionName extends ArbOwnerPrepareTransactionRequestFunctionName,
>(params: ArbOwnerPrepareFunctionDataParameters<TFunctionName>) {
TArbOsVersion extends ArbOSVersions,
TFunctionName extends ArbOwnerPublicFunctionName<TArbOsVersion>,
>(params: ArbOwnerEncodeFunctionDataParameters<TArbOsVersion, TFunctionName>) {
const { upgradeExecutor } = params;

if (!upgradeExecutor) {
return {
to: arbOwner.address,
data: arbOwnerEncodeFunctionData(
params as ArbOwnerEncodeFunctionDataParameters<TFunctionName>,
data: encodeFunctionData(
params as EncodeFunctionDataParameters<ArbOwnerPublicAbi<TArbOsVersion>, TFunctionName>,
),
value: BigInt(0),
};
Expand All @@ -54,35 +41,42 @@ function arbOwnerPrepareFunctionData<
functionName: 'executeCall',
args: [
arbOwner.address, // target
arbOwnerEncodeFunctionData(params as ArbOwnerEncodeFunctionDataParameters<TFunctionName>), // targetCallData
encodeFunctionData(
params as EncodeFunctionDataParameters<ArbOwnerPublicAbi<TArbOsVersion>, TFunctionName>,
), // targetCallData
],
}),
value: BigInt(0),
};
}

export type ArbOwnerPrepareTransactionRequestParameters<
TFunctionName extends ArbOwnerPrepareTransactionRequestFunctionName,
> = Omit<ArbOwnerPrepareFunctionDataParameters<TFunctionName>, 'abi'> & {
TArbOsVersion extends ArbOSVersions,
TFunctionName extends ArbOwnerPublicFunctionName<TArbOsVersion>,
> = {
functionName: TFunctionName;
account: Address;
};
upgradeExecutor: Address | false;
} & GetFunctionArgs<ArbOwnerPublicAbi<TArbOsVersion>, TFunctionName>;

export async function arbOwnerPrepareTransactionRequest<
TFunctionName extends ArbOwnerPrepareTransactionRequestFunctionName,
TArbOsVersion extends ArbOSVersions,
TChain extends Chain | undefined,
TFunctionName extends ArbOwnerPublicFunctionName<TArbOsVersion>,
>(
client: PublicClient<Transport, TChain>,
params: ArbOwnerPrepareTransactionRequestParameters<TFunctionName>,
client: Client<Transport, TChain>,
params: ArbOwnerPrepareTransactionRequestParameters<TArbOsVersion, TFunctionName> & {
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<TFunctionName>);
abi: ArbOwnerABIs[params.arbOsVersion],
} as unknown as ArbOwnerEncodeFunctionDataParameters<TArbOsVersion, TFunctionName>);

// @ts-ignore (todo: fix viem type issue)
const request = await client.prepareTransactionRequest({
Expand Down
38 changes: 25 additions & 13 deletions src/arbOwnerReadContract.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,41 @@
import { Chain, GetFunctionArgs, PublicClient, ReadContractReturnType, Transport } from 'viem';
import { Chain, GetFunctionArgs, Client, 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<ArbOwnerPublicAbi>;
export type ArbOwnerPublicAbi<TArbOsVersion extends ArbOSVersions> =
(typeof ArbOwnerABIs)[TArbOsVersion];

export type ArbOwnerReadContractParameters<TFunctionName extends ArbOwnerPublicFunctionName> = {
export type ArbOwnerPublicFunctionName<TArbOsVersion extends ArbOSVersions> = GetFunctionName<
ArbOwnerPublicAbi<TArbOsVersion>
>;

export type ArbOwnerReadContractParameters<
TArbOsVersion extends ArbOSVersions,
TFunctionName extends ArbOwnerPublicFunctionName<TArbOsVersion>,
> = {
functionName: TFunctionName;
} & GetFunctionArgs<ArbOwnerPublicAbi, TFunctionName>;
} & GetFunctionArgs<ArbOwnerPublicAbi<TArbOsVersion>, TFunctionName>;

export type ArbOwnerReadContractReturnType<TFunctionName extends ArbOwnerPublicFunctionName> =
ReadContractReturnType<ArbOwnerPublicAbi, TFunctionName>;
export type ArbOwnerReadContractReturnType<
TArbOsVersion extends ArbOSVersions,
TFunctionName extends ArbOwnerPublicFunctionName<TArbOsVersion>,
> = ReadContractReturnType<ArbOwnerPublicAbi<TArbOsVersion>, TFunctionName>;

export function arbOwnerReadContract<
TArbOsVersion extends ArbOSVersions,
TChain extends Chain | undefined,
TFunctionName extends ArbOwnerPublicFunctionName,
TFunctionName extends ArbOwnerPublicFunctionName<TArbOsVersion>,
>(
client: PublicClient<Transport, TChain>,
params: ArbOwnerReadContractParameters<TFunctionName>,
): Promise<ArbOwnerReadContractReturnType<TFunctionName>> {
client: Client<Transport, TChain>,
params: ArbOwnerReadContractParameters<TArbOsVersion, TFunctionName> & {
arbOsVersion: TArbOsVersion;
},
): Promise<ArbOwnerReadContractReturnType<TArbOsVersion, TFunctionName>> {
// @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,
});
Expand Down
51 changes: 51 additions & 0 deletions src/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,54 @@ 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[]' }],
},
{
stateMutability: 'nonpayable',
type: 'function',
inputs: [{ name: 'recipient', internalType: 'address[]', type: 'address[]' }],
name: 'setL1PricingRewardRecipient',
outputs: [],
},
],
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[]' }],
},
{
stateMutability: 'nonpayable',
type: 'function',
inputs: [{ name: 'recipient', internalType: 'uint64', type: 'uint64' }],
name: 'setL1PricingRewardRecipient',
outputs: [],
},
],
20: arbOwnerConfig.abi,
} as const;
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down
2 changes: 1 addition & 1 deletion src/decorators/arbOwnerPublicActions.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
99 changes: 87 additions & 12 deletions src/decorators/arbOwnerPublicActions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Transport, Chain, PrepareTransactionRequestReturnType, PublicClient } from 'viem';
import { Transport, Chain, PrepareTransactionRequestReturnType, Client, Account } from 'viem';

import {
arbOwnerReadContract,
Expand All @@ -8,29 +8,104 @@ import {
} from '../arbOwnerReadContract';
import {
arbOwnerPrepareTransactionRequest,
ArbOwnerPrepareTransactionRequestFunctionName,
ArbOwnerPrepareTransactionRequestParameters,
} from '../arbOwnerPrepareTransactionRequest';
import { ArbOSVersions } from '../contracts';

export type ArbOwnerPublicActions<TChain extends Chain | undefined = Chain | undefined> = {
arbOwnerReadContract: <TFunctionName extends ArbOwnerPublicFunctionName>(
args: ArbOwnerReadContractParameters<TFunctionName>,
) => Promise<ArbOwnerReadContractReturnType<TFunctionName>>;
export type ArbOwnerPublicActions<
TArbOsVersion extends ArbOSVersions = 20,
TChain extends Chain | undefined = Chain | undefined,
> = {
arbOwnerReadContract: <TFunctionName extends ArbOwnerPublicFunctionName<TArbOsVersion>>(
args: ArbOwnerReadContractParameters<TArbOsVersion, TFunctionName>,
) => Promise<ArbOwnerReadContractReturnType<TArbOsVersion, TFunctionName>>;

arbOwnerPrepareTransactionRequest: <
TFunctionName extends ArbOwnerPrepareTransactionRequestFunctionName,
TFunctionName extends ArbOwnerPublicFunctionName<TArbOsVersion>,
>(
args: ArbOwnerPrepareTransactionRequestParameters<TFunctionName>,
args: ArbOwnerPrepareTransactionRequestParameters<TArbOsVersion, TFunctionName>,
) => Promise<PrepareTransactionRequestReturnType<TChain> & { chainId: number }>;
};

const defaultArbOsVersion = 20;

// 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<TTransport, TChain, TAccount>,
{ arbOsVersion }: { arbOsVersion: TArbOsVersion },
): ArbOwnerPublicActions<TArbOsVersion, TChain>;
// 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,
TAccount extends Account | undefined = Account | undefined,
>(param: {
arbOsVersion: TArbOsVersion;
}): (client: Client<TTransport, TChain, TAccount>) => ArbOwnerPublicActions<TArbOsVersion, TChain>;
// 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<TTransport, TChain, TAccount>,
): ArbOwnerPublicActions<typeof defaultArbOsVersion, TChain>;
export function arbOwnerPublicActions<
TArbOsVersion extends ArbOSVersions,
TTransport extends Transport = Transport,
TChain extends Chain | undefined = Chain | undefined,
>(client: PublicClient<TTransport, TChain>): ArbOwnerPublicActions<TChain> {
return {
arbOwnerReadContract: (args) => arbOwnerReadContract(client, args),
TAccount extends Account | undefined = Account | undefined,
>(
paramOrClient: { arbOsVersion: TArbOsVersion } | Client<TTransport, TChain, TAccount>,
options?: { arbOsVersion: TArbOsVersion },
) {
// arbOsVersion is passed as a parameter, return actions with curried arbOsVersion
if ('arbOsVersion' in paramOrClient) {
const result: (
client: Client<TTransport, TChain, TAccount>,
) => ArbOwnerPublicActions<TArbOsVersion, TChain> = (client) => ({
arbOwnerReadContract: (args) =>
arbOwnerReadContract(client, { ...args, arbOsVersion: paramOrClient.arbOsVersion }),
arbOwnerPrepareTransactionRequest: (args) =>
// @ts-ignore (todo: fix viem type issue)
arbOwnerPrepareTransactionRequest(client, {
...args,
arbOsVersion: paramOrClient.arbOsVersion,
}),
});

return result;
}

arbOwnerPrepareTransactionRequest: (args) => arbOwnerPrepareTransactionRequest(client, args),
/**
* 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<typeof version, TChain> = {
arbOwnerReadContract: (args) =>
// @ts-ignore (todo: fix viem type issue)
arbOwnerReadContract(paramOrClient, { ...args, arbOsVersion: version }),
arbOwnerPrepareTransactionRequest: (args) =>
// @ts-ignore (todo: fix viem type issue)
arbOwnerPrepareTransactionRequest(paramOrClient, {
...args,
arbOsVersion: version,
}),
};
return result;
}
Loading
Loading