@aastar/enduser 0.16.11 → 0.16.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -2
- package/__tests__/CommunityClient.test.ts +0 -205
- package/__tests__/UserClient.test.ts +0 -294
- package/__tests__/index.test.ts +0 -16
- package/__tests__/mocks/client.ts +0 -22
- package/coverage/CommunityClient.ts.html +0 -790
- package/coverage/UserClient.ts.html +0 -1423
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/coverage-final.json +0 -3
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -131
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -210
- package/src/CommunityClient.ts +0 -235
- package/src/UserClient.ts +0 -447
- package/src/index.ts +0 -2
- package/src/testAccountManager.ts +0 -374
- package/tsconfig.json +0 -10
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aastar/enduser",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Enduser client for AAstar SDK",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
8
11
|
"keywords": [
|
|
9
12
|
"aastar",
|
|
10
13
|
"enduser",
|
|
@@ -14,7 +17,7 @@
|
|
|
14
17
|
"license": "MIT",
|
|
15
18
|
"dependencies": {
|
|
16
19
|
"viem": "2.43.3",
|
|
17
|
-
"@aastar/core": "0.16.
|
|
20
|
+
"@aastar/core": "0.16.12"
|
|
18
21
|
},
|
|
19
22
|
"devDependencies": {
|
|
20
23
|
"typescript": "5.7.2"
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { CommunityClient } from '../src/CommunityClient';
|
|
3
|
-
import { createMockPublicClient, createMockWalletClient, resetMocks } from './mocks/client';
|
|
4
|
-
import { parseEther } from 'viem';
|
|
5
|
-
|
|
6
|
-
// Define mocks using vi.hoisted to ensure they are available for vi.mock
|
|
7
|
-
const mocks = vi.hoisted(() => {
|
|
8
|
-
const mockXPNTsFactory = { createToken: vi.fn() };
|
|
9
|
-
const mockRegistry = { ROLE_COMMUNITY: vi.fn(), registerRoleSelf: vi.fn() };
|
|
10
|
-
const mockToken = { allowance: vi.fn(), approve: vi.fn(), transferOwnership: vi.fn() };
|
|
11
|
-
const mockSBT = { airdropMint: vi.fn(), mintForRole: vi.fn(), burnSBT: vi.fn() };
|
|
12
|
-
const mockReputation = { setReputationRule: vi.fn() };
|
|
13
|
-
|
|
14
|
-
return {
|
|
15
|
-
mockXPNTsFactory,
|
|
16
|
-
mockRegistry,
|
|
17
|
-
mockToken,
|
|
18
|
-
mockSBT,
|
|
19
|
-
mockReputation,
|
|
20
|
-
// The actions typically return a function that accepts the client and returns the methods
|
|
21
|
-
// xPNTsFactoryActions(addr)(client).createToken(...)
|
|
22
|
-
mockXPNTsFactoryActions: vi.fn(() => () => mockXPNTsFactory),
|
|
23
|
-
mockRegistryActions: vi.fn(() => () => mockRegistry),
|
|
24
|
-
mockTokenActions: vi.fn(() => () => mockToken),
|
|
25
|
-
mockSBTActions: vi.fn(() => () => mockSBT),
|
|
26
|
-
mockReputationActions: vi.fn(() => () => mockReputation),
|
|
27
|
-
};
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
vi.mock('@aastar/core', async () => {
|
|
31
|
-
const actual = await vi.importActual('@aastar/core');
|
|
32
|
-
return {
|
|
33
|
-
...actual,
|
|
34
|
-
xPNTsFactoryActions: mocks.mockXPNTsFactoryActions,
|
|
35
|
-
registryActions: mocks.mockRegistryActions,
|
|
36
|
-
tokenActions: mocks.mockTokenActions,
|
|
37
|
-
sbtActions: mocks.mockSBTActions,
|
|
38
|
-
reputationActions: mocks.mockReputationActions,
|
|
39
|
-
};
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
describe('CommunityClient', () => {
|
|
43
|
-
let client: CommunityClient;
|
|
44
|
-
const mockPublicClient = createMockPublicClient();
|
|
45
|
-
const mockWalletClient = createMockWalletClient();
|
|
46
|
-
|
|
47
|
-
const config = {
|
|
48
|
-
params: {
|
|
49
|
-
chainId: 11155111,
|
|
50
|
-
rpcUrl: 'https://rpc.sepolia.org',
|
|
51
|
-
},
|
|
52
|
-
services: {
|
|
53
|
-
paymasterUrl: 'https://paymaster.example.com',
|
|
54
|
-
},
|
|
55
|
-
// Required by BaseClient
|
|
56
|
-
client: mockWalletClient as any,
|
|
57
|
-
registryAddress: '0xRegistryAddress' as `0x${string}`,
|
|
58
|
-
gTokenAddress: '0xGTokenAddress' as `0x${string}`,
|
|
59
|
-
gTokenStakingAddress: '0xStakingAddress' as `0x${string}`,
|
|
60
|
-
// Required by CommunityClient
|
|
61
|
-
sbtAddress: '0xSBTAddress' as `0x${string}`,
|
|
62
|
-
factoryAddress: '0xFactoryAddress' as `0x${string}`,
|
|
63
|
-
reputationAddress: '0xReputationAddress' as `0x${string}`,
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
beforeEach(() => {
|
|
67
|
-
resetMocks();
|
|
68
|
-
vi.clearAllMocks();
|
|
69
|
-
|
|
70
|
-
// Setup default successful mocks
|
|
71
|
-
mocks.mockRegistry.ROLE_COMMUNITY.mockResolvedValue(100n);
|
|
72
|
-
mocks.mockToken.allowance.mockResolvedValue(parseEther('100'));
|
|
73
|
-
mocks.mockXPNTsFactory.createToken.mockResolvedValue('0xTxHash');
|
|
74
|
-
mocks.mockRegistry.registerRoleSelf.mockResolvedValue('0xTxHash');
|
|
75
|
-
mocks.mockSBT.airdropMint.mockResolvedValue('0xTxHash');
|
|
76
|
-
mocks.mockSBT.burnSBT.mockResolvedValue('0xTxHash');
|
|
77
|
-
mocks.mockReputation.setReputationRule.mockResolvedValue('0xTxHash');
|
|
78
|
-
mocks.mockToken.transferOwnership.mockResolvedValue('0xTxHash');
|
|
79
|
-
|
|
80
|
-
client = new CommunityClient(config);
|
|
81
|
-
(client as any).client = mockWalletClient;
|
|
82
|
-
(client as any).publicClient = mockPublicClient;
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
describe('createCommunityToken', () => {
|
|
86
|
-
it('should successfully create a token', async () => {
|
|
87
|
-
const result = await client.createCommunityToken({
|
|
88
|
-
name: 'Test Token',
|
|
89
|
-
tokenSymbol: 'TEST'
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
expect(mocks.mockXPNTsFactoryActions).toHaveBeenCalledWith(config.factoryAddress);
|
|
93
|
-
expect(mocks.mockXPNTsFactory.createToken).toHaveBeenCalledWith(expect.objectContaining({
|
|
94
|
-
name: 'Test Token',
|
|
95
|
-
symbol: 'TEST',
|
|
96
|
-
community: expect.stringContaining('0x000')
|
|
97
|
-
}));
|
|
98
|
-
expect(result).toBe('0xTxHash');
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it('should throw if factoryAddress is missing', async () => {
|
|
102
|
-
client.factoryAddress = undefined;
|
|
103
|
-
await expect(client.createCommunityToken({ name: 'T', tokenSymbol: 'T' }))
|
|
104
|
-
.rejects.toThrow('Factory address required');
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('should propagate core errors', async () => {
|
|
108
|
-
mocks.mockXPNTsFactory.createToken.mockRejectedValue(new Error('Core Error'));
|
|
109
|
-
await expect(client.createCommunityToken({ name: 'T', tokenSymbol: 'T' }))
|
|
110
|
-
.rejects.toThrow('Core Error');
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
describe('registerAsCommunity', () => {
|
|
115
|
-
it('should register successfully with sufficient allowance', async () => {
|
|
116
|
-
mocks.mockToken.allowance.mockResolvedValue(parseEther('100'));
|
|
117
|
-
|
|
118
|
-
await client.registerAsCommunity({ name: 'My Community' });
|
|
119
|
-
|
|
120
|
-
expect(mocks.mockToken.approve).not.toHaveBeenCalled();
|
|
121
|
-
expect(mocks.mockRegistry.registerRoleSelf).toHaveBeenCalledWith(expect.objectContaining({
|
|
122
|
-
roleId: 100n
|
|
123
|
-
}));
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('should approve if allowance is insufficient', async () => {
|
|
127
|
-
mocks.mockToken.allowance.mockResolvedValue(0n);
|
|
128
|
-
mocks.mockToken.approve.mockResolvedValue('0xApproveHash');
|
|
129
|
-
|
|
130
|
-
await client.registerAsCommunity({ name: 'My Community' });
|
|
131
|
-
|
|
132
|
-
expect(mocks.mockToken.approve).toHaveBeenCalled();
|
|
133
|
-
expect(mockPublicClient.waitForTransactionReceipt).toHaveBeenCalledWith({ hash: '0xApproveHash' });
|
|
134
|
-
expect(mocks.mockRegistry.registerRoleSelf).toHaveBeenCalled();
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
describe('airdropSBT', () => {
|
|
139
|
-
it('should airdrop to a single user', async () => {
|
|
140
|
-
const user = '0xUser' as `0x${string}`;
|
|
141
|
-
await client.airdropSBT([user], 1n);
|
|
142
|
-
|
|
143
|
-
expect(mocks.mockSBTActions).toHaveBeenCalledWith(config.sbtAddress);
|
|
144
|
-
expect(mocks.mockSBT.mintForRole).toHaveBeenCalledWith(expect.objectContaining({
|
|
145
|
-
user: user
|
|
146
|
-
}));
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('should throw if sbtAddress is missing', async () => {
|
|
150
|
-
client.sbtAddress = undefined;
|
|
151
|
-
await expect(client.airdropSBT(['0xUser'], 1n))
|
|
152
|
-
.rejects.toThrow('SBT address required');
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it('should throw if multiple users provided (not implemented)', async () => {
|
|
156
|
-
await expect(client.airdropSBT(['0xU1', '0xU2'], 1n))
|
|
157
|
-
.rejects.toThrow('Batch airdrop not fully implemented');
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
describe('setReputationRule', () => {
|
|
162
|
-
it('setReputationRule should call reputation setReputationRule', async () => {
|
|
163
|
-
await client.setReputationRule(1n, {});
|
|
164
|
-
expect(mocks.mockReputationActions).toHaveBeenCalledWith(config.reputationAddress);
|
|
165
|
-
// Mock passes ruleId as hex because implementation converts it
|
|
166
|
-
expect(mocks.mockReputation.setReputationRule).toHaveBeenCalledWith(expect.objectContaining({
|
|
167
|
-
ruleId: '0x0000000000000000000000000000000000000000000000000000000000000001'
|
|
168
|
-
}));
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it('should throw if reputationAddress is missing', async () => {
|
|
172
|
-
client.reputationAddress = undefined;
|
|
173
|
-
await expect(client.setReputationRule(1n, {}))
|
|
174
|
-
.rejects.toThrow('Reputation address required');
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
describe('revokeMembership', () => {
|
|
179
|
-
it('should burn SBT', async () => {
|
|
180
|
-
const user = '0xUser' as `0x${string}`;
|
|
181
|
-
await client.revokeMembership(user);
|
|
182
|
-
expect(mocks.mockSBTActions).toHaveBeenCalledWith(config.sbtAddress);
|
|
183
|
-
expect(mocks.mockSBT.burnSBT).toHaveBeenCalledWith(expect.objectContaining({
|
|
184
|
-
user: user
|
|
185
|
-
}));
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it('should throw if sbtAddress is missing', async () => {
|
|
189
|
-
client.sbtAddress = undefined;
|
|
190
|
-
await expect(client.revokeMembership(123n))
|
|
191
|
-
.rejects.toThrow('SBT address required');
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
describe('transferCommunityTokenOwnership', () => {
|
|
196
|
-
it('should transfer ownership', async () => {
|
|
197
|
-
await client.transferCommunityTokenOwnership('0xToken', '0x2222222222222222222222222222222222222222');
|
|
198
|
-
expect(mocks.mockTokenActions).toHaveBeenCalled();
|
|
199
|
-
expect(mocks.mockToken.transferOwnership).toHaveBeenCalledWith(expect.objectContaining({
|
|
200
|
-
token: '0xToken',
|
|
201
|
-
newOwner: '0x2222222222222222222222222222222222222222'
|
|
202
|
-
}));
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
});
|
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { UserClient } from '../src/UserClient';
|
|
3
|
-
import { createMockPublicClient, createMockWalletClient, resetMocks } from './mocks/client';
|
|
4
|
-
import { parseEther } from 'viem';
|
|
5
|
-
|
|
6
|
-
// Define mocks using vi.hoisted
|
|
7
|
-
const mocks = vi.hoisted(() => {
|
|
8
|
-
const mockAccount = { owner: vi.fn(), execute: vi.fn(), executeBatch: vi.fn() };
|
|
9
|
-
const mockSBT = { balanceOf: vi.fn(), mintForRole: vi.fn(), leaveCommunity: vi.fn() };
|
|
10
|
-
const mockToken = { transfer: vi.fn(), balanceOf: vi.fn(), allowance: vi.fn() };
|
|
11
|
-
const mockStaking = { lockStake: vi.fn(), unlockAndTransfer: vi.fn(), getLockedStake: vi.fn() };
|
|
12
|
-
const mockRegistry = { exitRole: vi.fn(), registerRoleSelf: vi.fn() }; // registerRoleSelf calls execute in implementation logic but mocked here just in case? No, registerAsEndUser constructs tx data and calls execute/executeBatch.
|
|
13
|
-
// Wait, registerAsEndUser calls this.execute or this.executeBatch, so we don't mock registryActions for that call directly if we test logic flow,
|
|
14
|
-
// BUT registerAsEndUser calls registryActions(this.registryAddress) effectively to get address? No, it uses this.registryAddress.
|
|
15
|
-
// It calls registryActions to get ABI maybe? In the code:
|
|
16
|
-
// const registry = registryActions(this.registryAddress);
|
|
17
|
-
// ... encodeFunctionData ...
|
|
18
|
-
// registerAsEndUser MANUALLY constructs tx data using encodeFunctionData. It DOES NOT use registryActions returned object methods for the transaction itself to send via execute.
|
|
19
|
-
// EXCEPT: It initializes registry = registryActions(...) which might be unused in logic?
|
|
20
|
-
// Let's look at source:
|
|
21
|
-
// const registry = registryActions(this.registryAddress);
|
|
22
|
-
// ...
|
|
23
|
-
// It effectively uses it for nothing if it builds manually?
|
|
24
|
-
// Line 293: const registry = registryActions(this.registryAddress);
|
|
25
|
-
// Then lines 315-337: const roleData = ... encodeFunctionData ...
|
|
26
|
-
// So registryActions might be just for show? Or maybe strict mode requires it.
|
|
27
|
-
|
|
28
|
-
const mockEntryPoint = { getNonce: vi.fn() };
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
mockAccount,
|
|
32
|
-
mockSBT,
|
|
33
|
-
mockToken,
|
|
34
|
-
mockStaking,
|
|
35
|
-
mockRegistry,
|
|
36
|
-
mockEntryPoint,
|
|
37
|
-
// Factory functions
|
|
38
|
-
mockAccountActions: vi.fn(() => () => mockAccount),
|
|
39
|
-
mockSBTActions: vi.fn(() => () => mockSBT),
|
|
40
|
-
mockTokenActions: vi.fn(() => () => mockToken),
|
|
41
|
-
mockStakingActions: vi.fn(() => () => mockStaking),
|
|
42
|
-
mockRegistryActions: vi.fn(() => () => mockRegistry),
|
|
43
|
-
mockEntryPointActions: vi.fn(() => () => mockEntryPoint),
|
|
44
|
-
// Bundler actions (mock extend)
|
|
45
|
-
mockBundlerActions: {},
|
|
46
|
-
};
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
vi.mock('@aastar/core', async () => {
|
|
50
|
-
const actual = await vi.importActual('@aastar/core');
|
|
51
|
-
return {
|
|
52
|
-
...actual,
|
|
53
|
-
accountActions: mocks.mockAccountActions,
|
|
54
|
-
sbtActions: mocks.mockSBTActions,
|
|
55
|
-
tokenActions: mocks.mockTokenActions,
|
|
56
|
-
stakingActions: mocks.mockStakingActions,
|
|
57
|
-
registryActions: mocks.mockRegistryActions,
|
|
58
|
-
entryPointActions: mocks.mockEntryPointActions,
|
|
59
|
-
};
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
vi.mock('viem/account-abstraction', async () => {
|
|
63
|
-
const actual = await vi.importActual('viem/account-abstraction');
|
|
64
|
-
return {
|
|
65
|
-
...actual,
|
|
66
|
-
bundlerActions: () => ({
|
|
67
|
-
estimateUserOperationGas: vi.fn().mockResolvedValue({
|
|
68
|
-
callGasLimit: 1000n,
|
|
69
|
-
verificationGasLimit: 1000n,
|
|
70
|
-
preVerificationGas: 1000n
|
|
71
|
-
}),
|
|
72
|
-
sendUserOperation: vi.fn().mockResolvedValue('0xUserOpHash')
|
|
73
|
-
}),
|
|
74
|
-
getUserOperationHash: vi.fn().mockReturnValue('0xHash'),
|
|
75
|
-
};
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
describe('UserClient', () => {
|
|
79
|
-
let client: UserClient;
|
|
80
|
-
const mockPublicClient = createMockPublicClient();
|
|
81
|
-
const mockWalletClient = createMockWalletClient();
|
|
82
|
-
|
|
83
|
-
const config = {
|
|
84
|
-
params: {
|
|
85
|
-
chainId: 11155111,
|
|
86
|
-
rpcUrl: 'https://rpc.sepolia.org',
|
|
87
|
-
},
|
|
88
|
-
services: {
|
|
89
|
-
paymasterUrl: 'https://paymaster.example.com',
|
|
90
|
-
},
|
|
91
|
-
client: mockWalletClient as any,
|
|
92
|
-
accountAddress: '0x1234567890123456789012345678901234567890' as `0x${string}`,
|
|
93
|
-
sbtAddress: '0x1111111111111111111111111111111111111111' as `0x${string}`,
|
|
94
|
-
entryPointAddress: '0x2222222222222222222222222222222222222222' as `0x${string}`,
|
|
95
|
-
gTokenStakingAddress: '0x3333333333333333333333333333333333333333' as `0x${string}`,
|
|
96
|
-
registryAddress: '0x4444444444444444444444444444444444444444' as `0x${string}`,
|
|
97
|
-
gTokenAddress: '0x5555555555555555555555555555555555555555' as `0x${string}`,
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
beforeEach(() => {
|
|
101
|
-
resetMocks();
|
|
102
|
-
vi.clearAllMocks();
|
|
103
|
-
|
|
104
|
-
// Default Resolves
|
|
105
|
-
mocks.mockAccount.owner.mockResolvedValue('0xOwner');
|
|
106
|
-
mocks.mockAccount.execute.mockResolvedValue('0xTxHash');
|
|
107
|
-
mocks.mockAccount.executeBatch.mockResolvedValue('0xTxHash');
|
|
108
|
-
mocks.mockEntryPoint.getNonce.mockResolvedValue(0n);
|
|
109
|
-
mocks.mockSBT.balanceOf.mockResolvedValue(1n);
|
|
110
|
-
mocks.mockSBT.mintForRole.mockResolvedValue('0xTxHash');
|
|
111
|
-
mocks.mockToken.transfer.mockResolvedValue('0xTxHash');
|
|
112
|
-
mocks.mockToken.balanceOf.mockResolvedValue(100n);
|
|
113
|
-
mocks.mockToken.allowance.mockResolvedValue(parseEther('1000'));
|
|
114
|
-
mocks.mockStaking.lockStake.mockResolvedValue('0xTxHash');
|
|
115
|
-
mocks.mockStaking.unlockAndTransfer.mockResolvedValue('0xTxHash');
|
|
116
|
-
mocks.mockStaking.getLockedStake.mockResolvedValue(50n);
|
|
117
|
-
mocks.mockRegistry.exitRole.mockResolvedValue('0xTxHash');
|
|
118
|
-
mocks.mockSBT.leaveCommunity.mockResolvedValue('0xTxHash');
|
|
119
|
-
|
|
120
|
-
// Mock public client methods needed for gasless
|
|
121
|
-
(mockPublicClient as any).estimateFeesPerGas = vi.fn().mockResolvedValue({
|
|
122
|
-
maxFeePerGas: 10n,
|
|
123
|
-
maxPriorityFeePerGas: 1n
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
client = new UserClient(config);
|
|
127
|
-
(client as any).client = mockWalletClient;
|
|
128
|
-
(client as any).publicClient = mockPublicClient;
|
|
129
|
-
// Mock extend for gasless
|
|
130
|
-
(client as any).client.extend = vi.fn().mockReturnValue({
|
|
131
|
-
estimateUserOperationGas: vi.fn().mockResolvedValue({
|
|
132
|
-
callGasLimit: 1000n,
|
|
133
|
-
verificationGasLimit: 1000n,
|
|
134
|
-
preVerificationGas: 1000n
|
|
135
|
-
}),
|
|
136
|
-
sendUserOperation: vi.fn().mockResolvedValue('0xUserOpHash'),
|
|
137
|
-
signMessage: vi.fn().mockResolvedValue('0xSig')
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
// Also mock signMessage on the client itself if used directly
|
|
141
|
-
(client as any).client.signMessage = vi.fn().mockResolvedValue('0xSig');
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
describe('Basic Account Operations', () => {
|
|
145
|
-
it('getNonce should call entryPoint', async () => {
|
|
146
|
-
const nonce = await client.getNonce();
|
|
147
|
-
expect(mocks.mockEntryPointActions).toHaveBeenCalledWith(config.entryPointAddress);
|
|
148
|
-
expect(mocks.mockEntryPoint.getNonce).toHaveBeenCalledWith({
|
|
149
|
-
sender: config.accountAddress,
|
|
150
|
-
key: 0n
|
|
151
|
-
});
|
|
152
|
-
expect(nonce).toBe(0n);
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it('getOwner should call account', async () => {
|
|
156
|
-
const owner = await client.getOwner();
|
|
157
|
-
expect(mocks.mockAccountActions).toHaveBeenCalledWith(config.accountAddress);
|
|
158
|
-
expect(mocks.mockAccount.owner).toHaveBeenCalled();
|
|
159
|
-
expect(owner).toBe('0xOwner');
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('execute should call account execute', async () => {
|
|
163
|
-
await client.execute('0x1111111111111111111111111111111111111111', 0n, '0x1234');
|
|
164
|
-
expect(mocks.mockAccountActions).toHaveBeenCalledWith(config.accountAddress);
|
|
165
|
-
expect(mocks.mockAccount.execute).toHaveBeenCalledWith(expect.objectContaining({
|
|
166
|
-
dest: '0x1111111111111111111111111111111111111111',
|
|
167
|
-
value: 0n,
|
|
168
|
-
func: '0x1234'
|
|
169
|
-
}));
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
describe('SBT Operations', () => {
|
|
174
|
-
it('getSBTBalance should call sbt', async () => {
|
|
175
|
-
await client.getSBTBalance();
|
|
176
|
-
expect(mocks.mockSBTActions).toHaveBeenCalledWith(config.sbtAddress);
|
|
177
|
-
expect(mocks.mockSBT.balanceOf).toHaveBeenCalled();
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it('mintSBT should call sbt mintForRole', async () => {
|
|
181
|
-
await client.mintSBT('0x4444444444444444444444444444444444444444444444444444444444444444');
|
|
182
|
-
expect(mocks.mockSBT.mintForRole).toHaveBeenCalledWith(expect.objectContaining({
|
|
183
|
-
roleId: '0x4444444444444444444444444444444444444444444444444444444444444444',
|
|
184
|
-
user: config.accountAddress
|
|
185
|
-
}));
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it('should throw if sbtAddress missing', async () => {
|
|
189
|
-
client.sbtAddress = undefined;
|
|
190
|
-
await expect(client.mintSBT('0x4444444444444444444444444444444444444444444444444444444444444444')).rejects.toThrow('SBT address required');
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
describe('Asset Operations', () => {
|
|
195
|
-
it('transferToken should call token transfer', async () => {
|
|
196
|
-
await client.transferToken('0x5555555555555555555555555555555555555555', '0x6666666666666666666666666666666666666666', 100n);
|
|
197
|
-
expect(mocks.mockToken.transfer).toHaveBeenCalledWith(expect.objectContaining({
|
|
198
|
-
token: '0x5555555555555555555555555555555555555555',
|
|
199
|
-
to: '0x6666666666666666666666666666666666666666',
|
|
200
|
-
amount: 100n
|
|
201
|
-
}));
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it('getTokenBalance should call token balanceOf', async () => {
|
|
205
|
-
await client.getTokenBalance('0x5555555555555555555555555555555555555555');
|
|
206
|
-
expect(mocks.mockToken.balanceOf).toHaveBeenCalledWith(expect.objectContaining({
|
|
207
|
-
token: '0x5555555555555555555555555555555555555555',
|
|
208
|
-
account: config.accountAddress
|
|
209
|
-
}));
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
describe('Staking Operations', () => {
|
|
214
|
-
it('stakeForRole should call lockStake', async () => {
|
|
215
|
-
await client.stakeForRole('0x4444444444444444444444444444444444444444444444444444444444444444', 100n);
|
|
216
|
-
expect(mocks.mockStakingActions).toHaveBeenCalledWith(config.gTokenStakingAddress);
|
|
217
|
-
expect(mocks.mockStaking.lockStake).toHaveBeenCalledWith(expect.objectContaining({
|
|
218
|
-
user: config.accountAddress,
|
|
219
|
-
roleId: '0x4444444444444444444444444444444444444444444444444444444444444444',
|
|
220
|
-
stakeAmount: 100n
|
|
221
|
-
}));
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
it('unstakeFromRole should call unlockAndTransfer', async () => {
|
|
225
|
-
await client.unstakeFromRole('0x4444444444444444444444444444444444444444444444444444444444444444');
|
|
226
|
-
expect(mocks.mockStaking.unlockAndTransfer).toHaveBeenCalledWith(expect.objectContaining({
|
|
227
|
-
user: config.accountAddress,
|
|
228
|
-
roleId: '0x4444444444444444444444444444444444444444444444444444444444444444'
|
|
229
|
-
}));
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
it('getStakedBalance should call getLockedStake', async () => {
|
|
233
|
-
await client.getStakedBalance('0x4444444444444444444444444444444444444444444444444444444444444444');
|
|
234
|
-
expect(mocks.mockStaking.getLockedStake).toHaveBeenCalled();
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
describe('Lifecycle', () => {
|
|
239
|
-
it('exitRole should call registry exitRole', async () => {
|
|
240
|
-
await client.exitRole('0x4444444444444444444444444444444444444444444444444444444444444444');
|
|
241
|
-
expect(mocks.mockRegistryActions).toHaveBeenCalledWith(config.registryAddress);
|
|
242
|
-
expect(mocks.mockRegistry.exitRole).toHaveBeenCalledWith(expect.objectContaining({
|
|
243
|
-
roleId: '0x4444444444444444444444444444444444444444444444444444444444444444'
|
|
244
|
-
}));
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
it('leaveCommunity should call sbt leaveCommunity', async () => {
|
|
248
|
-
await client.leaveCommunity('0x3333333333333333333333333333333333333333');
|
|
249
|
-
expect(mocks.mockSBT.leaveCommunity).toHaveBeenCalledWith(expect.objectContaining({
|
|
250
|
-
community: '0x3333333333333333333333333333333333333333'
|
|
251
|
-
}));
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
it('registerAsEndUser should execute batch (approve + register)', async () => {
|
|
255
|
-
mocks.mockToken.allowance.mockResolvedValue(0n); // Force approve
|
|
256
|
-
|
|
257
|
-
await client.registerAsEndUser('0x3333333333333333333333333333333333333333', 100n);
|
|
258
|
-
|
|
259
|
-
// Should verify executeBatch is called because allowance is low
|
|
260
|
-
expect(mocks.mockAccount.executeBatch).toHaveBeenCalled();
|
|
261
|
-
// first arg (targets) should contain [gToken, registry]
|
|
262
|
-
const callArgs = mocks.mockAccount.executeBatch.mock.calls[0][0];
|
|
263
|
-
expect(callArgs.dest).toHaveLength(2);
|
|
264
|
-
expect(callArgs.dest[0]).toBe(config.gTokenAddress);
|
|
265
|
-
expect(callArgs.dest[1]).toBe(config.registryAddress);
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
it('registerAsEndUser should execute single if already approved', async () => {
|
|
269
|
-
mocks.mockToken.allowance.mockResolvedValue(parseEther('10000'));
|
|
270
|
-
|
|
271
|
-
await client.registerAsEndUser('0x3333333333333333333333333333333333333333', 100n);
|
|
272
|
-
|
|
273
|
-
expect(mocks.mockAccount.execute).toHaveBeenCalled();
|
|
274
|
-
const callArgs = mocks.mockAccount.execute.mock.calls[0][0];
|
|
275
|
-
expect(callArgs.dest).toBe(config.registryAddress);
|
|
276
|
-
});
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
describe('Gasless', () => {
|
|
280
|
-
it('executeGasless should construct and send UserOp', async () => {
|
|
281
|
-
const result = await client.executeGasless({
|
|
282
|
-
target: '0x1111111111111111111111111111111111111111',
|
|
283
|
-
value: 0n,
|
|
284
|
-
data: '0x1234',
|
|
285
|
-
paymaster: '0x2222222222222222222222222222222222222222',
|
|
286
|
-
paymasterType: 'V4'
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
expect(client.client.extend).toHaveBeenCalled();
|
|
290
|
-
expect(result).toBe('0xUserOpHash');
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
});
|
package/__tests__/index.test.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { CommunityClient } from '../src/CommunityClient';
|
|
3
|
-
import { UserClient } from '../src/UserClient';
|
|
4
|
-
import * as Exports from '../src/index';
|
|
5
|
-
|
|
6
|
-
describe('EndUser Package Exports', () => {
|
|
7
|
-
it('should export CommunityClient', () => {
|
|
8
|
-
expect(Exports.CommunityClient).toBeDefined();
|
|
9
|
-
expect(Exports.CommunityClient).toBe(CommunityClient);
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it('should export UserClient', () => {
|
|
13
|
-
expect(Exports.UserClient).toBeDefined();
|
|
14
|
-
expect(Exports.UserClient).toBe(UserClient);
|
|
15
|
-
});
|
|
16
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { vi } from 'vitest';
|
|
2
|
-
import type { PublicClient, WalletClient } from 'viem';
|
|
3
|
-
|
|
4
|
-
export const createMockPublicClient = (): PublicClient => {
|
|
5
|
-
return {
|
|
6
|
-
readContract: vi.fn(),
|
|
7
|
-
chain: { id: 11155111 },
|
|
8
|
-
waitForTransactionReceipt: vi.fn().mockResolvedValue({ status: 'success' }),
|
|
9
|
-
} as any;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export const createMockWalletClient = (): WalletClient => {
|
|
13
|
-
return {
|
|
14
|
-
writeContract: vi.fn(),
|
|
15
|
-
account: { address: '0x1234567890123456789012345678901234567890' },
|
|
16
|
-
chain: { id: 11155111 },
|
|
17
|
-
} as any;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export const resetMocks = () => {
|
|
21
|
-
vi.clearAllMocks();
|
|
22
|
-
};
|