Skip to content
This repository was archived by the owner on Apr 18, 2026. It is now read-only.

Commit 88a2e4c

Browse files
fix: make getLinkedAgw parameters optional (#419)
* fix: make getLinkedAgw parameters optional * adjust typing; add changeset --------- Co-authored-by: coffee ☕️ <coffee@cubelabs.xyz>
1 parent 9f673e4 commit 88a2e4c

5 files changed

Lines changed: 121 additions & 21 deletions

File tree

.changeset/funny-pugs-thank.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@abstract-foundation/agw-client": minor
3+
---
4+
5+
Tighten `getLinkedAgw` typing so account-hoisted clients can call it with zero arguments, while clients without a hoisted account must pass `{ address }`.

packages/agw-client/src/actions/getLinkedAgw.ts

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import {
66
type Client,
77
getAddress,
88
InvalidAddressError,
9+
type IsUndefined,
910
isAddress,
11+
type MaybeRequired,
1012
type Transport,
1113
} from 'viem';
1214
import { readContract } from 'viem/actions';
@@ -24,9 +26,19 @@ export interface GetLinkedAgwReturnType {
2426
agw: Address | undefined;
2527
}
2628

27-
export interface GetLinkedAgwParameters {
28-
address?: Address | undefined;
29-
}
29+
export type GetLinkedAgwParameters<
30+
account extends Account | undefined = Account | undefined,
31+
> = MaybeRequired<{ address?: Address | undefined }, IsUndefined<account>>;
32+
33+
export type GetLinkedAgwAction<
34+
account extends Account | undefined = Account | undefined,
35+
> = IsUndefined<account> extends true
36+
? (
37+
parameters: GetLinkedAgwParameters<account>,
38+
) => Promise<GetLinkedAgwReturnType>
39+
: (
40+
parameters?: GetLinkedAgwParameters<account>,
41+
) => Promise<GetLinkedAgwReturnType>;
3042

3143
export interface IsLinkedAccountParameters {
3244
address: Address;
@@ -63,18 +75,33 @@ export interface IsLinkedAccountParameters {
6375
* }
6476
* ```
6577
*
66-
* @param parameters - Parameters for getting the linked AGW
78+
* @param parameters - Parameters for getting the linked AGW. If the client has a connected account, this can be omitted
6779
* @param parameters.address - The Ethereum Mainnet address to check for a linked AGW. If not provided, defaults to the connected account's address
6880
* @returns Object containing the address of the linked AGW, or undefined if no AGW is linked
6981
*/
82+
export async function getLinkedAgw<
83+
chain extends Chain | undefined = Chain | undefined,
84+
>(
85+
client: Client<Transport, chain, undefined>,
86+
parameters: GetLinkedAgwParameters<undefined>,
87+
): Promise<GetLinkedAgwReturnType>;
88+
export async function getLinkedAgw<
89+
chain extends Chain | undefined = Chain | undefined,
90+
account extends Account = Account,
91+
>(
92+
client: Client<Transport, chain, account>,
93+
parameters?: GetLinkedAgwParameters<account>,
94+
): Promise<GetLinkedAgwReturnType>;
7095
export async function getLinkedAgw<
7196
chain extends Chain | undefined = Chain | undefined,
7297
account extends Account | undefined = Account | undefined,
7398
>(
7499
client: Client<Transport, chain, account>,
75-
parameters: GetLinkedAgwParameters,
100+
parameters?: GetLinkedAgwParameters<account>,
76101
): Promise<GetLinkedAgwReturnType> {
77-
const { address = client.account?.address } = parameters;
102+
const { address = client.account?.address } = (parameters ?? {}) as {
103+
address?: Address | undefined;
104+
};
78105

79106
if (address === undefined) {
80107
throw new BaseError('No address provided');

packages/agw-client/src/clients/decorators/linkablePublic.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ import {
66
getLinkedAccounts,
77
} from '../../actions/getLinkedAccounts.js';
88
import {
9+
type GetLinkedAgwAction,
910
type GetLinkedAgwParameters,
10-
type GetLinkedAgwReturnType,
1111
getLinkedAgw,
1212
} from '../../actions/getLinkedAgw.js';
1313

14-
export interface LinkablePublicActions {
15-
getLinkedAgw: (
16-
args: GetLinkedAgwParameters,
17-
) => Promise<GetLinkedAgwReturnType>;
14+
export interface LinkablePublicActions<
15+
account extends Account | undefined = Account | undefined,
16+
> {
17+
getLinkedAgw: GetLinkedAgwAction<account>;
1818
getLinkedAccounts: (
1919
args: GetLinkedAccountsParameters,
2020
) => Promise<GetLinkedAccountsReturnType>;
@@ -25,10 +25,18 @@ export function linkablePublicActions<
2525
chain extends ChainEIP712 | undefined = ChainEIP712 | undefined,
2626
account extends Account | undefined = Account | undefined,
2727
>() {
28-
return (
29-
client: Client<transport, chain, account>,
30-
): LinkablePublicActions => ({
31-
getLinkedAgw: (args) => getLinkedAgw(client, args),
28+
return <
29+
clientTransport extends transport = transport,
30+
clientChain extends chain = chain,
31+
clientAccount extends account = account,
32+
>(
33+
client: Client<clientTransport, clientChain, clientAccount>,
34+
): LinkablePublicActions<clientAccount> => ({
35+
getLinkedAgw: ((parameters?: GetLinkedAgwParameters<clientAccount>) =>
36+
getLinkedAgw(
37+
client as Client<clientTransport, clientChain, Account>,
38+
(parameters ?? {}) as GetLinkedAgwParameters<Account>,
39+
)) as GetLinkedAgwAction<clientAccount>,
3240
getLinkedAccounts: (args) => getLinkedAccounts(client, args),
3341
});
3442
}

packages/agw-client/src/clients/decorators/linkableWallet.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import {
77
walletActions,
88
} from 'viem';
99
import {
10-
type GetLinkedAgwReturnType,
10+
type GetLinkedAgwAction,
11+
type GetLinkedAgwParameters,
1112
getLinkedAgw,
1213
} from '../../actions/getLinkedAgw.js';
1314
import {
@@ -21,19 +22,27 @@ export type LinkableWalletActions<
2122
account extends Account | undefined = Account | undefined,
2223
> = WalletActions<chain, account> & {
2324
linkToAgw: (args: LinkToAgwParameters) => Promise<LinkToAgwReturnType>;
24-
getLinkedAgw: () => Promise<GetLinkedAgwReturnType>;
25+
getLinkedAgw: GetLinkedAgwAction<account>;
2526
};
2627

2728
export function linkableWalletActions<
2829
transport extends Transport = Transport,
2930
chain extends Chain | undefined = Chain | undefined,
3031
account extends Account | undefined = Account | undefined,
3132
>() {
32-
return (
33-
client: WalletClient<transport, chain, account>,
34-
): LinkableWalletActions<chain, account> => ({
33+
return <
34+
clientTransport extends transport = transport,
35+
clientChain extends chain = chain,
36+
clientAccount extends account = account,
37+
>(
38+
client: WalletClient<clientTransport, clientChain, clientAccount>,
39+
): LinkableWalletActions<clientChain, clientAccount> => ({
3540
...walletActions(client),
3641
linkToAgw: (args) => linkToAgw(client, args),
37-
getLinkedAgw: () => getLinkedAgw(client, {}),
42+
getLinkedAgw: ((parameters?: GetLinkedAgwParameters<clientAccount>) =>
43+
getLinkedAgw(
44+
client as WalletClient<clientTransport, clientChain, Account>,
45+
(parameters ?? {}) as GetLinkedAgwParameters<Account>,
46+
)) as GetLinkedAgwAction<clientAccount>,
3847
});
3948
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { createClient, getAddress } from 'viem';
2+
import { ChainEIP712 } from 'viem/zksync';
3+
import { beforeEach, describe, expect, it, vi } from 'vitest';
4+
5+
import { getLinkedAgw } from '../../../src/actions/getLinkedAgw.js';
6+
import {
7+
AGW_LINK_DELEGATION_RIGHTS,
8+
CANONICAL_EXCLUSIVE_DELEGATE_RESOLVER_ADDRESS,
9+
} from '../../../src/constants.js';
10+
import { anvilAbstractTestnet } from '../../anvil.js';
11+
import { address } from '../../constants.js';
12+
13+
vi.mock('viem/actions', async (importOriginal) => {
14+
const actual = await importOriginal();
15+
return {
16+
...(actual as object),
17+
readContract: vi.fn(),
18+
};
19+
});
20+
21+
import { readContract } from 'viem/actions';
22+
23+
const baseClient = createClient({
24+
account: address.smartAccountAddress,
25+
chain: anvilAbstractTestnet.chain as ChainEIP712,
26+
transport: anvilAbstractTestnet.clientConfig.transport,
27+
});
28+
29+
beforeEach(() => {
30+
vi.resetAllMocks();
31+
});
32+
33+
describe('getLinkedAgw', () => {
34+
it('uses the connected account when parameters are omitted', async () => {
35+
const linkedAgw = '0x1234567890123456789012345678901234567890';
36+
vi.mocked(readContract).mockResolvedValue(linkedAgw);
37+
38+
const result = await getLinkedAgw(baseClient);
39+
40+
expect(readContract).toHaveBeenCalledWith(baseClient, {
41+
abi: expect.any(Array),
42+
address: CANONICAL_EXCLUSIVE_DELEGATE_RESOLVER_ADDRESS,
43+
functionName: 'exclusiveWalletByRights',
44+
args: [
45+
getAddress(address.smartAccountAddress),
46+
AGW_LINK_DELEGATION_RIGHTS,
47+
],
48+
});
49+
expect(result).toEqual({ agw: linkedAgw });
50+
});
51+
});

0 commit comments

Comments
 (0)