@framers/agentos-ext-wallet 0.2.0 → 0.2.2
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/manifest.json +4 -4
- package/package.json +13 -10
- package/src/AgentWalletManager.ts +0 -324
- package/src/SpendingPolicyEnforcer.ts +0 -169
- package/src/cards/CardManager.ts +0 -274
- package/src/cards/LithicCardAdapter.ts +0 -159
- package/src/cards/MccCategoryMap.ts +0 -138
- package/src/cards/types.ts +0 -169
- package/src/chains/EvmWalletAdapter.ts +0 -170
- package/src/chains/SolanaWalletAdapter.ts +0 -184
- package/src/index.ts +0 -315
- package/src/tools/CardSpendingSummaryTool.ts +0 -52
- package/src/tools/CardStatusTool.ts +0 -68
- package/src/tools/CheckBalanceTool.ts +0 -145
- package/src/tools/FreezeCardTool.ts +0 -52
- package/src/tools/IssueCardTool.ts +0 -76
- package/src/tools/PayWithCardTool.ts +0 -109
- package/src/tools/SendCryptoTool.ts +0 -185
- package/src/tools/UnfreezeCardTool.ts +0 -52
- package/src/tools/WalletHistoryTool.ts +0 -113
- package/src/types.ts +0 -230
- package/test/AgentWalletManager.spec.ts +0 -301
- package/test/CardManager.spec.ts +0 -428
- package/test/LithicCardAdapter.spec.ts +0 -224
- package/test/MccCategoryMap.spec.ts +0 -153
- package/test/SpendingPolicyEnforcer.spec.ts +0 -254
- package/test/card-integration.spec.ts +0 -242
- package/test/card-tools.spec.ts +0 -285
- package/test/integration.spec.ts +0 -171
- package/test/wallet-tools.spec.ts +0 -275
- package/tsconfig.json +0 -22
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
/**
|
|
3
|
-
* @fileoverview Unit tests for wallet tools (CheckBalance, SendCrypto, WalletHistory).
|
|
4
|
-
*
|
|
5
|
-
* Tests cover: tool metadata, successful execution, error handling,
|
|
6
|
-
* missing wallet handling, and spending policy violation surfacing.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
10
|
-
import { createCheckBalanceTool } from '../src/tools/CheckBalanceTool.js';
|
|
11
|
-
import { createSendCryptoTool } from '../src/tools/SendCryptoTool.js';
|
|
12
|
-
import { createWalletHistoryTool } from '../src/tools/WalletHistoryTool.js';
|
|
13
|
-
import { AgentWalletManager, SpendingPolicyViolation } from '../src/AgentWalletManager.js';
|
|
14
|
-
import { SpendingPolicyEnforcer } from '../src/SpendingPolicyEnforcer.js';
|
|
15
|
-
import type { IChainWalletAdapter, ChainId, WalletTxStatus } from '../src/types.js';
|
|
16
|
-
import { DEFAULT_SPENDING_POLICY } from '../src/types.js';
|
|
17
|
-
|
|
18
|
-
/* ------------------------------------------------------------------ */
|
|
19
|
-
/* Test helpers */
|
|
20
|
-
/* ------------------------------------------------------------------ */
|
|
21
|
-
|
|
22
|
-
const CTX = {
|
|
23
|
-
gmiId: 'test-agent',
|
|
24
|
-
personaId: 'persona-1',
|
|
25
|
-
userContext: { userId: 'user-1' } as any,
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
function createMockAdapter(): IChainWalletAdapter {
|
|
29
|
-
return {
|
|
30
|
-
chain: 'solana',
|
|
31
|
-
generateKeypair: vi.fn(async () => ({
|
|
32
|
-
publicKey: 'SolPubKey123',
|
|
33
|
-
secretKey: new Uint8Array(64).fill(1),
|
|
34
|
-
})),
|
|
35
|
-
getBalance: vi.fn(async () => 2_500_000_000n), // 2.5 SOL
|
|
36
|
-
getTokenBalance: vi.fn(async () => 100_000_000n), // 100 USDC
|
|
37
|
-
signTransfer: vi.fn(async () => new Uint8Array([1])),
|
|
38
|
-
signTokenTransfer: vi.fn(async () => new Uint8Array([2])),
|
|
39
|
-
broadcast: vi.fn(async () => 'tx-hash-001'),
|
|
40
|
-
getTransactionStatus: vi.fn(async () => 'confirmed' as WalletTxStatus),
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function createTestManager() {
|
|
45
|
-
const adapter = createMockAdapter();
|
|
46
|
-
const wallets = new Map<string, any>();
|
|
47
|
-
const txs = new Map<string, any[]>();
|
|
48
|
-
const ledger: any[] = [];
|
|
49
|
-
|
|
50
|
-
const store = {
|
|
51
|
-
async getWallet(agentId: string, chain: string) { return wallets.get(`${agentId}:${chain}`) || null; },
|
|
52
|
-
async getAllWallets(agentId: string) { return [...wallets.values()].filter((w: any) => w.agentId === agentId); },
|
|
53
|
-
async saveWallet(record: any) { wallets.set(`${record.agentId}:${record.chain}`, record); },
|
|
54
|
-
async insertTransaction(record: any) { const l = txs.get(record.walletId) || []; l.push(record); txs.set(record.walletId, l); },
|
|
55
|
-
async updateTransactionStatus(id: string, status: string, txHash?: string) {
|
|
56
|
-
for (const [, l] of txs) { const t = l.find((x: any) => x.id === id); if (t) { t.status = status; if (txHash) t.txHash = txHash; } }
|
|
57
|
-
},
|
|
58
|
-
async getTransactions(walletId: string, limit = 20) { return (txs.get(walletId) || []).slice(-limit).reverse(); },
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
const ledgerStore = {
|
|
62
|
-
async sumByPeriod(agentId: string, periodKey: string) { return ledger.filter(e => e.agentId === agentId && e.periodKey === periodKey).reduce((s, e) => s + e.amountUsd, 0); },
|
|
63
|
-
async sumByCategoryAndPeriod(agentId: string, category: string, periodKey: string) { return ledger.filter(e => e.agentId === agentId && e.category === category && e.periodKey === periodKey).reduce((s, e) => s + e.amountUsd, 0); },
|
|
64
|
-
async insert(entry: any) { ledger.push(entry); },
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
const policyEnforcer = new SpendingPolicyEnforcer(DEFAULT_SPENDING_POLICY, ledgerStore);
|
|
68
|
-
const adapters = new Map<ChainId, IChainWalletAdapter>([['solana', adapter]]);
|
|
69
|
-
|
|
70
|
-
const manager = new AgentWalletManager({
|
|
71
|
-
masterSecret: 'test-secret-for-tools-32bytes!',
|
|
72
|
-
walletConfig: { enabled: true, chains: ['solana'], custodyMode: 'encrypted-hot', allowedTokens: ['SOL', 'USDC'], spendingPolicy: DEFAULT_SPENDING_POLICY },
|
|
73
|
-
store,
|
|
74
|
-
policyEnforcer,
|
|
75
|
-
adapters,
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
return { manager, adapter, wallets, txs };
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/* ------------------------------------------------------------------ */
|
|
82
|
-
/* CheckBalanceTool */
|
|
83
|
-
/* ------------------------------------------------------------------ */
|
|
84
|
-
|
|
85
|
-
describe('CheckBalanceTool', () => {
|
|
86
|
-
let manager: AgentWalletManager;
|
|
87
|
-
|
|
88
|
-
beforeEach(async () => {
|
|
89
|
-
const setup = createTestManager();
|
|
90
|
-
manager = setup.manager;
|
|
91
|
-
await manager.createWallet(CTX.gmiId, 'solana');
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('should have correct metadata', () => {
|
|
95
|
-
const tool = createCheckBalanceTool(manager);
|
|
96
|
-
expect(tool.id).toBe('wallet-check-balance-v1');
|
|
97
|
-
expect(tool.name).toBe('check_wallet_balance');
|
|
98
|
-
expect(tool.hasSideEffects).toBe(false);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it('should return formatted SOL balance', async () => {
|
|
102
|
-
const tool = createCheckBalanceTool(manager);
|
|
103
|
-
const result = await tool.execute({ chain: 'solana' }, CTX);
|
|
104
|
-
|
|
105
|
-
expect(result.success).toBe(true);
|
|
106
|
-
expect(result.output?.token).toBe('SOL');
|
|
107
|
-
expect(result.output?.balanceFormatted).toContain('2.5');
|
|
108
|
-
expect(result.output?.address).toBeTruthy();
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('should default to solana chain', async () => {
|
|
112
|
-
const tool = createCheckBalanceTool(manager);
|
|
113
|
-
const result = await tool.execute({}, CTX);
|
|
114
|
-
|
|
115
|
-
expect(result.success).toBe(true);
|
|
116
|
-
expect(result.output?.chain).toBe('solana');
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('should return error for non-existent wallet', async () => {
|
|
120
|
-
const tool = createCheckBalanceTool(manager);
|
|
121
|
-
const result = await tool.execute({ chain: 'ethereum' }, CTX);
|
|
122
|
-
|
|
123
|
-
expect(result.success).toBe(false);
|
|
124
|
-
expect(result.error).toContain('No ethereum wallet found');
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
/* ------------------------------------------------------------------ */
|
|
129
|
-
/* SendCryptoTool */
|
|
130
|
-
/* ------------------------------------------------------------------ */
|
|
131
|
-
|
|
132
|
-
describe('SendCryptoTool', () => {
|
|
133
|
-
let manager: AgentWalletManager;
|
|
134
|
-
|
|
135
|
-
beforeEach(async () => {
|
|
136
|
-
const setup = createTestManager();
|
|
137
|
-
manager = setup.manager;
|
|
138
|
-
await manager.createWallet(CTX.gmiId, 'solana');
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
afterEach(() => {
|
|
142
|
-
delete process.env.WALLET_PRICE_SOL_USD;
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
it('should have correct metadata', () => {
|
|
146
|
-
const tool = createSendCryptoTool(manager);
|
|
147
|
-
expect(tool.id).toBe('wallet-send-crypto-v1');
|
|
148
|
-
expect(tool.name).toBe('send_crypto');
|
|
149
|
-
expect(tool.hasSideEffects).toBe(true);
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it('should send native SOL successfully', async () => {
|
|
153
|
-
const tool = createSendCryptoTool(manager);
|
|
154
|
-
const result = await tool.execute({
|
|
155
|
-
to: 'dest-address',
|
|
156
|
-
amount: '0.5',
|
|
157
|
-
chain: 'solana',
|
|
158
|
-
}, CTX);
|
|
159
|
-
|
|
160
|
-
expect(result.success).toBe(true);
|
|
161
|
-
expect(result.output?.txHash).toBe('tx-hash-001');
|
|
162
|
-
expect(result.output?.status).toBe('confirmed');
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it('should return error for invalid amount', async () => {
|
|
166
|
-
const tool = createSendCryptoTool(manager);
|
|
167
|
-
const result = await tool.execute({
|
|
168
|
-
to: 'dest',
|
|
169
|
-
amount: 'not-a-number',
|
|
170
|
-
}, CTX);
|
|
171
|
-
|
|
172
|
-
expect(result.success).toBe(false);
|
|
173
|
-
expect(result.error).toContain('Invalid amount');
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it('should return error for negative amount', async () => {
|
|
177
|
-
const tool = createSendCryptoTool(manager);
|
|
178
|
-
const result = await tool.execute({
|
|
179
|
-
to: 'dest',
|
|
180
|
-
amount: '-5',
|
|
181
|
-
}, CTX);
|
|
182
|
-
|
|
183
|
-
expect(result.success).toBe(false);
|
|
184
|
-
expect(result.error).toContain('Invalid amount');
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it('should surface spending policy violations cleanly', async () => {
|
|
188
|
-
const tool = createSendCryptoTool(manager);
|
|
189
|
-
// per-tx limit is $20, send $25
|
|
190
|
-
const result = await tool.execute({
|
|
191
|
-
to: 'dest',
|
|
192
|
-
amount: '25',
|
|
193
|
-
chain: 'solana',
|
|
194
|
-
}, CTX);
|
|
195
|
-
|
|
196
|
-
expect(result.success).toBe(false);
|
|
197
|
-
expect(result.details?.policyViolation).toBe(true);
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
it('should return error for unsupported token transfers', async () => {
|
|
201
|
-
const tool = createSendCryptoTool(manager);
|
|
202
|
-
const result = await tool.execute({
|
|
203
|
-
to: 'dest',
|
|
204
|
-
amount: '10',
|
|
205
|
-
token: 'USDC',
|
|
206
|
-
}, CTX);
|
|
207
|
-
|
|
208
|
-
expect(result.success).toBe(false);
|
|
209
|
-
expect(result.error).toContain('not yet supported');
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
it('should honor configurable reference USD prices for policy checks', async () => {
|
|
213
|
-
process.env.WALLET_PRICE_SOL_USD = '100';
|
|
214
|
-
const tool = createSendCryptoTool(manager);
|
|
215
|
-
const result = await tool.execute({
|
|
216
|
-
to: 'dest',
|
|
217
|
-
amount: '0.5',
|
|
218
|
-
chain: 'solana',
|
|
219
|
-
}, CTX);
|
|
220
|
-
|
|
221
|
-
expect(result.success).toBe(false);
|
|
222
|
-
expect(result.details?.policyViolation).toBe(true);
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
/* ------------------------------------------------------------------ */
|
|
227
|
-
/* WalletHistoryTool */
|
|
228
|
-
/* ------------------------------------------------------------------ */
|
|
229
|
-
|
|
230
|
-
describe('WalletHistoryTool', () => {
|
|
231
|
-
let manager: AgentWalletManager;
|
|
232
|
-
|
|
233
|
-
beforeEach(async () => {
|
|
234
|
-
const setup = createTestManager();
|
|
235
|
-
manager = setup.manager;
|
|
236
|
-
await manager.createWallet(CTX.gmiId, 'solana');
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
it('should have correct metadata', () => {
|
|
240
|
-
const tool = createWalletHistoryTool(manager);
|
|
241
|
-
expect(tool.id).toBe('wallet-transaction-history-v1');
|
|
242
|
-
expect(tool.name).toBe('wallet_transaction_history');
|
|
243
|
-
expect(tool.hasSideEffects).toBe(false);
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
it('should return empty history for new wallet', async () => {
|
|
247
|
-
const tool = createWalletHistoryTool(manager);
|
|
248
|
-
const result = await tool.execute({}, CTX);
|
|
249
|
-
|
|
250
|
-
expect(result.success).toBe(true);
|
|
251
|
-
expect(result.output?.transactions).toEqual([]);
|
|
252
|
-
expect(result.output?.totalCount).toBe(0);
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
it('should return transactions after sends', async () => {
|
|
256
|
-
await manager.sendNative(CTX.gmiId, 'solana', 'dest1', 100n, 1, 'transfers');
|
|
257
|
-
await manager.sendNative(CTX.gmiId, 'solana', 'dest2', 200n, 2, 'shopping');
|
|
258
|
-
|
|
259
|
-
const tool = createWalletHistoryTool(manager);
|
|
260
|
-
const result = await tool.execute({ chain: 'solana', limit: 10 }, CTX);
|
|
261
|
-
|
|
262
|
-
expect(result.success).toBe(true);
|
|
263
|
-
expect(result.output?.totalCount).toBe(2);
|
|
264
|
-
expect(result.output?.transactions[0].to).toBe('dest2');
|
|
265
|
-
expect(result.output?.transactions[1].to).toBe('dest1');
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
it('should return error for non-existent wallet', async () => {
|
|
269
|
-
const tool = createWalletHistoryTool(manager);
|
|
270
|
-
const result = await tool.execute({ chain: 'ethereum' }, CTX);
|
|
271
|
-
|
|
272
|
-
expect(result.success).toBe(false);
|
|
273
|
-
expect(result.error).toContain('No ethereum wallet found');
|
|
274
|
-
});
|
|
275
|
-
});
|
package/tsconfig.json
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"lib": ["ES2022"],
|
|
6
|
-
"outDir": "./dist",
|
|
7
|
-
"rootDir": "./src",
|
|
8
|
-
"strict": true,
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"skipLibCheck": true,
|
|
11
|
-
"noEmitOnError": false,
|
|
12
|
-
"forceConsistentCasingInFileNames": true,
|
|
13
|
-
"declaration": true,
|
|
14
|
-
"declarationMap": true,
|
|
15
|
-
"sourceMap": true,
|
|
16
|
-
"resolveJsonModule": true,
|
|
17
|
-
"moduleResolution": "Bundler",
|
|
18
|
-
"types": ["node"]
|
|
19
|
-
},
|
|
20
|
-
"include": ["src/**/*"],
|
|
21
|
-
"exclude": ["node_modules", "dist", "test"]
|
|
22
|
-
}
|