Skip to content

Commit 7f8dc0e

Browse files
committed
Add unit tests
1 parent f93163d commit 7f8dc0e

5 files changed

Lines changed: 386 additions & 1 deletion

File tree

packages/smart-accounts-kit-x402/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
"scripts": {
4444
"build": "yarn typecheck && tsup",
4545
"typecheck": "tsc --noEmit",
46+
"test": "vitest run --coverage",
47+
"test:watch": "vitest watch",
4648
"lint": "yarn lint:eslint",
4749
"lint:eslint": "eslint . --cache --ext js,ts",
4850
"lint:fix": "yarn lint:eslint --fix",
@@ -66,6 +68,7 @@
6668
"prettier": "^3.5.3",
6769
"tsup": "^8.5.0",
6870
"typescript": "5.5.4",
69-
"viem": "2.31.4"
71+
"viem": "2.31.4",
72+
"vitest": "^3.2.4"
7073
}
7174
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import { describe, expect, it, vi } from 'vitest';
2+
3+
import type { x402PaymentRequirements } from '../src/x402Client';
4+
import { x402Erc7710Client } from '../src/x402Client';
5+
6+
const baseRequirements: x402PaymentRequirements = {
7+
scheme: 'exact',
8+
network: 'eip155:8453',
9+
asset: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
10+
amount: '1000',
11+
payTo: '0x1111111111111111111111111111111111111111',
12+
maxTimeoutSeconds: 300,
13+
extra: {
14+
assetTransferMethod: 'erc7710',
15+
},
16+
};
17+
18+
describe('x402Erc7710Client', () => {
19+
it('exposes the exact scheme identifier', () => {
20+
const client = new x402Erc7710Client({
21+
delegationProvider: vi.fn(),
22+
});
23+
24+
expect(client.scheme).toBe('exact');
25+
});
26+
27+
it('creates an ERC-7710 payload and normalizes addresses', async () => {
28+
const delegationProvider = vi.fn().mockResolvedValue({
29+
delegationManager: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
30+
permissionContext: '0x1234',
31+
delegator: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
32+
});
33+
const client = new x402Erc7710Client({ delegationProvider });
34+
35+
const payload = await client.createPaymentPayload(2, baseRequirements);
36+
37+
expect(delegationProvider).toHaveBeenCalledWith(baseRequirements);
38+
expect(payload).toEqual({
39+
x402Version: 2,
40+
payload: {
41+
delegationManager: '0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa',
42+
permissionContext: '0x1234',
43+
delegator: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
44+
},
45+
});
46+
});
47+
48+
it('throws when permissionContext is empty hex', async () => {
49+
const client = new x402Erc7710Client({
50+
delegationProvider: vi.fn().mockResolvedValue({
51+
delegationManager: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
52+
permissionContext: '0x',
53+
delegator: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
54+
}),
55+
});
56+
57+
await expect(client.createPaymentPayload(2, baseRequirements)).rejects.toThrow(
58+
'Invalid delegation payload: permissionContext must be non-empty hex data',
59+
);
60+
});
61+
62+
it('throws when permissionContext is not hex', async () => {
63+
const client = new x402Erc7710Client({
64+
delegationProvider: vi.fn().mockResolvedValue({
65+
delegationManager: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
66+
permissionContext: 'not-hex',
67+
delegator: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
68+
}),
69+
});
70+
71+
await expect(client.createPaymentPayload(2, baseRequirements)).rejects.toThrow(
72+
'Invalid delegation payload: permissionContext must be non-empty hex data',
73+
);
74+
});
75+
76+
it('delegates to fallback client for non-erc7710 methods', async () => {
77+
const fallbackResult = {
78+
x402Version: 2,
79+
payload: { kind: 'fallback' },
80+
};
81+
const fallbackClient = {
82+
scheme: 'exact',
83+
createPaymentPayload: vi.fn().mockResolvedValue(fallbackResult),
84+
};
85+
const client = new x402Erc7710Client({
86+
delegationProvider: vi.fn(),
87+
fallbackClient,
88+
});
89+
const requirements: x402PaymentRequirements = {
90+
...baseRequirements,
91+
extra: { assetTransferMethod: 'eip3009' },
92+
};
93+
const context = { marker: 'ctx' };
94+
95+
const result = await client.createPaymentPayload(2, requirements, context);
96+
97+
expect(fallbackClient.createPaymentPayload).toHaveBeenCalledWith(
98+
2,
99+
requirements,
100+
context,
101+
);
102+
expect(result).toEqual(fallbackResult);
103+
});
104+
105+
it('throws for non-erc7710 methods without fallback', async () => {
106+
const client = new x402Erc7710Client({
107+
delegationProvider: vi.fn(),
108+
});
109+
const requirements: x402PaymentRequirements = {
110+
...baseRequirements,
111+
extra: { assetTransferMethod: 'eip3009' },
112+
};
113+
114+
await expect(client.createPaymentPayload(2, requirements)).rejects.toThrow(
115+
'x402Erc7710Client can only process assetTransferMethod "erc7710". Received: "eip3009"',
116+
);
117+
});
118+
119+
it('throws with undefined method when extra is missing', async () => {
120+
const client = new x402Erc7710Client({
121+
delegationProvider: vi.fn(),
122+
});
123+
const requirements: x402PaymentRequirements = {
124+
...baseRequirements,
125+
extra: undefined,
126+
};
127+
128+
await expect(client.createPaymentPayload(2, requirements)).rejects.toThrow(
129+
'x402Erc7710Client can only process assetTransferMethod "erc7710". Received: undefined',
130+
);
131+
});
132+
});
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import type { PaymentRequirements } from '@x402/core/types';
2+
import { ExactEvmScheme } from '@x402/evm/exact/server';
3+
import { afterEach, describe, expect, it, vi } from 'vitest';
4+
5+
import { x402ExactEvmErc7710ServerScheme } from '../src/x402ExactEvmErc7710ServerScheme';
6+
import { x402Erc7710Server } from '../src/x402Server';
7+
8+
describe('x402ExactEvmErc7710ServerScheme', () => {
9+
afterEach(() => {
10+
vi.restoreAllMocks();
11+
});
12+
13+
it('returns base requirements unchanged for non-erc7710 methods', async () => {
14+
const baseRequirements = {
15+
scheme: 'exact',
16+
network: 'eip155:8453',
17+
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
18+
amount: '1000',
19+
payTo: '0x1111111111111111111111111111111111111111',
20+
maxTimeoutSeconds: 300,
21+
extra: { assetTransferMethod: 'eip3009' },
22+
} as unknown as PaymentRequirements;
23+
const superSpy = vi
24+
.spyOn(ExactEvmScheme.prototype, 'enhancePaymentRequirements')
25+
.mockResolvedValue(baseRequirements);
26+
const erc7710Spy = vi.spyOn(
27+
x402Erc7710Server.prototype,
28+
'enhancePaymentRequirements',
29+
);
30+
const scheme = new x402ExactEvmErc7710ServerScheme();
31+
const supportedKind = {
32+
x402Version: 2,
33+
scheme: 'exact',
34+
network: 'eip155:8453' as const,
35+
};
36+
const facilitatorExtensions = ['extension'];
37+
38+
const result = await scheme.enhancePaymentRequirements(
39+
baseRequirements,
40+
supportedKind,
41+
facilitatorExtensions,
42+
);
43+
44+
expect(superSpy).toHaveBeenCalledWith(
45+
baseRequirements,
46+
supportedKind,
47+
facilitatorExtensions,
48+
);
49+
expect(erc7710Spy).not.toHaveBeenCalled();
50+
expect(result).toBe(baseRequirements);
51+
});
52+
53+
it('enhances requirements for erc7710 methods', async () => {
54+
const baseRequirements = {
55+
scheme: 'exact',
56+
network: 'eip155:8453',
57+
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
58+
amount: '1000',
59+
payTo: '0x1111111111111111111111111111111111111111',
60+
maxTimeoutSeconds: 300,
61+
extra: { assetTransferMethod: 'erc7710' },
62+
} as unknown as PaymentRequirements;
63+
const enhancedRequirements = {
64+
...baseRequirements,
65+
extra: {
66+
...baseRequirements.extra,
67+
facilitatorAddresses: ['0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa'],
68+
},
69+
} as unknown as PaymentRequirements;
70+
vi.spyOn(ExactEvmScheme.prototype, 'enhancePaymentRequirements').mockResolvedValue(
71+
baseRequirements,
72+
);
73+
const erc7710Spy = vi
74+
.spyOn(x402Erc7710Server.prototype, 'enhancePaymentRequirements')
75+
.mockResolvedValue(enhancedRequirements);
76+
const scheme = new x402ExactEvmErc7710ServerScheme();
77+
const supportedKind = {
78+
x402Version: 2,
79+
scheme: 'exact',
80+
network: 'eip155:8453' as const,
81+
extra: {
82+
facilitatorAddresses: ['0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'],
83+
},
84+
};
85+
86+
const result = await scheme.enhancePaymentRequirements(
87+
baseRequirements,
88+
supportedKind,
89+
[],
90+
);
91+
92+
expect(erc7710Spy).toHaveBeenCalledWith(baseRequirements, supportedKind);
93+
expect(result).toEqual(enhancedRequirements);
94+
});
95+
});

0 commit comments

Comments
 (0)