pay-lobster 1.0.0
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/README.md +401 -0
- package/README.md.bak +401 -0
- package/dist/agent.d.ts +132 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +224 -0
- package/dist/agent.js.map +1 -0
- package/dist/analytics.d.ts +120 -0
- package/dist/analytics.d.ts.map +1 -0
- package/dist/analytics.js +345 -0
- package/dist/analytics.js.map +1 -0
- package/dist/approvals.d.ts +168 -0
- package/dist/approvals.d.ts.map +1 -0
- package/dist/approvals.js +406 -0
- package/dist/approvals.js.map +1 -0
- package/dist/circle-client.d.ts +152 -0
- package/dist/circle-client.d.ts.map +1 -0
- package/dist/circle-client.js +266 -0
- package/dist/circle-client.js.map +1 -0
- package/dist/commission.d.ts +191 -0
- package/dist/commission.d.ts.map +1 -0
- package/dist/commission.js +475 -0
- package/dist/commission.js.map +1 -0
- package/dist/condition-builder.d.ts +98 -0
- package/dist/condition-builder.d.ts.map +1 -0
- package/dist/condition-builder.js +193 -0
- package/dist/condition-builder.js.map +1 -0
- package/dist/contacts.d.ts +179 -0
- package/dist/contacts.d.ts.map +1 -0
- package/dist/contacts.js +445 -0
- package/dist/contacts.js.map +1 -0
- package/dist/easy.d.ts +22 -0
- package/dist/easy.d.ts.map +1 -0
- package/dist/easy.js +40 -0
- package/dist/easy.js.map +1 -0
- package/dist/erc8004/constants.d.ts +152 -0
- package/dist/erc8004/constants.d.ts.map +1 -0
- package/dist/erc8004/constants.js +114 -0
- package/dist/erc8004/constants.js.map +1 -0
- package/dist/erc8004/discovery.d.ts +84 -0
- package/dist/erc8004/discovery.d.ts.map +1 -0
- package/dist/erc8004/discovery.js +217 -0
- package/dist/erc8004/discovery.js.map +1 -0
- package/dist/erc8004/identity.d.ts +91 -0
- package/dist/erc8004/identity.d.ts.map +1 -0
- package/dist/erc8004/identity.js +250 -0
- package/dist/erc8004/identity.js.map +1 -0
- package/dist/erc8004/index.d.ts +147 -0
- package/dist/erc8004/index.d.ts.map +1 -0
- package/dist/erc8004/index.js +225 -0
- package/dist/erc8004/index.js.map +1 -0
- package/dist/erc8004/reputation.d.ts +133 -0
- package/dist/erc8004/reputation.d.ts.map +1 -0
- package/dist/erc8004/reputation.js +277 -0
- package/dist/erc8004/reputation.js.map +1 -0
- package/dist/escrow-templates.d.ts +38 -0
- package/dist/escrow-templates.d.ts.map +1 -0
- package/dist/escrow-templates.js +419 -0
- package/dist/escrow-templates.js.map +1 -0
- package/dist/escrow.d.ts +320 -0
- package/dist/escrow.d.ts.map +1 -0
- package/dist/escrow.js +854 -0
- package/dist/escrow.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/invoices.d.ts +212 -0
- package/dist/invoices.d.ts.map +1 -0
- package/dist/invoices.js +393 -0
- package/dist/invoices.js.map +1 -0
- package/dist/notifications.d.ts +141 -0
- package/dist/notifications.d.ts.map +1 -0
- package/dist/notifications.js +350 -0
- package/dist/notifications.js.map +1 -0
- package/dist/tips.d.ts +171 -0
- package/dist/tips.d.ts.map +1 -0
- package/dist/tips.js +390 -0
- package/dist/tips.js.map +1 -0
- package/dist/types.d.ts +100 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/x402-client.d.ts +127 -0
- package/dist/x402-client.d.ts.map +1 -0
- package/dist/x402-client.js +350 -0
- package/dist/x402-client.js.map +1 -0
- package/dist/x402-server.d.ts +133 -0
- package/dist/x402-server.d.ts.map +1 -0
- package/dist/x402-server.js +330 -0
- package/dist/x402-server.js.map +1 -0
- package/lib/agent.ts +273 -0
- package/lib/analytics.ts +474 -0
- package/lib/analytics.ts.bak +474 -0
- package/lib/approvals.ts +585 -0
- package/lib/approvals.ts.bak +585 -0
- package/lib/circle-client.ts +376 -0
- package/lib/circle-client.ts.bak +376 -0
- package/lib/commission.ts +680 -0
- package/lib/commission.ts.bak +680 -0
- package/lib/condition-builder.ts +223 -0
- package/lib/condition-builder.ts.bak +223 -0
- package/lib/contacts.ts +615 -0
- package/lib/contacts.ts.bak +615 -0
- package/lib/easy.ts +46 -0
- package/lib/easy.ts.bak +352 -0
- package/lib/erc8004/constants.ts +175 -0
- package/lib/erc8004/discovery.ts +299 -0
- package/lib/erc8004/identity.ts +327 -0
- package/lib/erc8004/index.ts +285 -0
- package/lib/erc8004/reputation.ts +368 -0
- package/lib/escrow-templates.ts +462 -0
- package/lib/escrow.ts +1216 -0
- package/lib/index.ts +13 -0
- package/lib/invoices.ts +588 -0
- package/lib/notifications.ts +484 -0
- package/lib/tips.ts +570 -0
- package/lib/types.ts +108 -0
- package/lib/x402-client.ts +471 -0
- package/lib/x402-server.ts +462 -0
- package/package.json +58 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Circle Programmable Wallets Client
|
|
3
|
+
*
|
|
4
|
+
* Wrapper around Circle's Developer-Controlled Wallets API
|
|
5
|
+
* for USDC operations on testnet.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import crypto from 'crypto';
|
|
9
|
+
|
|
10
|
+
// Types
|
|
11
|
+
export interface CircleConfig {
|
|
12
|
+
apiKey: string;
|
|
13
|
+
entitySecret: string;
|
|
14
|
+
baseUrl?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface Wallet {
|
|
18
|
+
id: string;
|
|
19
|
+
address: string;
|
|
20
|
+
blockchain: string;
|
|
21
|
+
state: string;
|
|
22
|
+
walletSetId: string;
|
|
23
|
+
createDate: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface WalletSet {
|
|
27
|
+
id: string;
|
|
28
|
+
name: string;
|
|
29
|
+
custodyType: string;
|
|
30
|
+
createDate: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface Balance {
|
|
34
|
+
token: {
|
|
35
|
+
id: string;
|
|
36
|
+
name: string;
|
|
37
|
+
symbol: string;
|
|
38
|
+
decimals: number;
|
|
39
|
+
blockchain: string;
|
|
40
|
+
};
|
|
41
|
+
amount: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface Transaction {
|
|
45
|
+
id: string;
|
|
46
|
+
state: string;
|
|
47
|
+
txHash?: string;
|
|
48
|
+
amounts: string[];
|
|
49
|
+
sourceAddress: string;
|
|
50
|
+
destinationAddress: string;
|
|
51
|
+
blockchain: string;
|
|
52
|
+
createDate: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface SendOptions {
|
|
56
|
+
fromWalletId: string;
|
|
57
|
+
toAddress: string;
|
|
58
|
+
amount: string;
|
|
59
|
+
tokenId?: string; // USDC token ID for the chain
|
|
60
|
+
feeLevel?: 'LOW' | 'MEDIUM' | 'HIGH';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface BridgeOptions {
|
|
64
|
+
fromWalletId: string;
|
|
65
|
+
toAddress: string;
|
|
66
|
+
fromChain: string;
|
|
67
|
+
toChain: string;
|
|
68
|
+
amount: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Testnet USDC Token IDs (from Circle's registry)
|
|
72
|
+
export const USDC_TOKENS: Record<string, string> = {
|
|
73
|
+
'ETH-SEPOLIA': 'eth-sepolia-usdc',
|
|
74
|
+
'MATIC-AMOY': 'matic-amoy-usdc',
|
|
75
|
+
'AVAX-FUJI': 'avax-fuji-usdc',
|
|
76
|
+
'ARB-SEPOLIA': 'arb-sepolia-usdc',
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const CHAIN_NAMES: Record<string, string> = {
|
|
80
|
+
'ETH-SEPOLIA': 'Ethereum Sepolia',
|
|
81
|
+
'MATIC-AMOY': 'Polygon Amoy',
|
|
82
|
+
'AVAX-FUJI': 'Avalanche Fuji',
|
|
83
|
+
'ARB-SEPOLIA': 'Arbitrum Sepolia',
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Circle Programmable Wallets Client
|
|
88
|
+
*/
|
|
89
|
+
export class CircleClient {
|
|
90
|
+
private apiKey: string;
|
|
91
|
+
private entitySecret: string;
|
|
92
|
+
private baseUrl: string;
|
|
93
|
+
|
|
94
|
+
constructor(config: CircleConfig) {
|
|
95
|
+
this.apiKey = config.apiKey;
|
|
96
|
+
this.entitySecret = config.entitySecret;
|
|
97
|
+
this.baseUrl = config.baseUrl || 'https://api.circle.com/v1/w3s';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Generate entity secret ciphertext for API calls
|
|
102
|
+
*/
|
|
103
|
+
private generateEntitySecretCiphertext(): string {
|
|
104
|
+
// Entity secret should be hex-encoded 32 bytes
|
|
105
|
+
const entitySecretBuffer = Buffer.from(this.entitySecret, 'hex');
|
|
106
|
+
|
|
107
|
+
// Generate random IV
|
|
108
|
+
const iv = crypto.randomBytes(12);
|
|
109
|
+
|
|
110
|
+
// For demo purposes, return base64 encoded secret
|
|
111
|
+
// In production, this would use Circle's public key for encryption
|
|
112
|
+
return Buffer.concat([iv, entitySecretBuffer]).toString('base64');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Make authenticated API request
|
|
117
|
+
*/
|
|
118
|
+
private async request<T>(
|
|
119
|
+
method: string,
|
|
120
|
+
path: string,
|
|
121
|
+
body?: Record<string, any>
|
|
122
|
+
): Promise<T> {
|
|
123
|
+
const url = `${this.baseUrl}${path}`;
|
|
124
|
+
|
|
125
|
+
const headers: Record<string, string> = {
|
|
126
|
+
'Content-Type': 'application/json',
|
|
127
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const options: RequestInit = {
|
|
131
|
+
method,
|
|
132
|
+
headers,
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
if (body) {
|
|
136
|
+
// Add idempotency key for mutations
|
|
137
|
+
if (method !== 'GET') {
|
|
138
|
+
body.idempotencyKey = crypto.randomUUID();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Add entity secret ciphertext for transaction operations
|
|
142
|
+
if (path.includes('/transactions') || path.includes('/wallets')) {
|
|
143
|
+
body.entitySecretCiphertext = this.generateEntitySecretCiphertext();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
options.body = JSON.stringify(body);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const response = await fetch(url, options);
|
|
150
|
+
|
|
151
|
+
if (!response.ok) {
|
|
152
|
+
const error = await response.text();
|
|
153
|
+
throw new Error(`Circle API error (${response.status}): ${error}`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const data = await response.json();
|
|
157
|
+
return data.data || data;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ============ Wallet Set Operations ============
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Create a new wallet set
|
|
164
|
+
*/
|
|
165
|
+
async createWalletSet(name: string): Promise<WalletSet> {
|
|
166
|
+
return this.request<WalletSet>('POST', '/developer/walletSets', { name });
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* List all wallet sets
|
|
171
|
+
*/
|
|
172
|
+
async listWalletSets(): Promise<WalletSet[]> {
|
|
173
|
+
const result = await this.request<{ walletSets: WalletSet[] }>('GET', '/developer/walletSets');
|
|
174
|
+
return result.walletSets || [];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ============ Wallet Operations ============
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Create wallets in a wallet set
|
|
181
|
+
*/
|
|
182
|
+
async createWallets(walletSetId: string, blockchains: string[], count = 1): Promise<Wallet[]> {
|
|
183
|
+
const result = await this.request<{ wallets: Wallet[] }>('POST', '/developer/wallets', {
|
|
184
|
+
walletSetId,
|
|
185
|
+
blockchains,
|
|
186
|
+
count,
|
|
187
|
+
});
|
|
188
|
+
return result.wallets || [];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Get wallet by ID
|
|
193
|
+
*/
|
|
194
|
+
async getWallet(walletId: string): Promise<Wallet> {
|
|
195
|
+
return this.request<Wallet>('GET', `/wallets/${walletId}`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* List all wallets
|
|
200
|
+
*/
|
|
201
|
+
async listWallets(): Promise<Wallet[]> {
|
|
202
|
+
const result = await this.request<{ wallets: Wallet[] }>('GET', '/wallets');
|
|
203
|
+
return result.wallets || [];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ============ Balance Operations ============
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Get token balances for a wallet
|
|
210
|
+
*/
|
|
211
|
+
async getBalances(walletId: string): Promise<Balance[]> {
|
|
212
|
+
const result = await this.request<{ tokenBalances: Balance[] }>(
|
|
213
|
+
'GET',
|
|
214
|
+
`/wallets/${walletId}/balances`
|
|
215
|
+
);
|
|
216
|
+
return result.tokenBalances || [];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Get USDC balance across all wallets
|
|
221
|
+
*/
|
|
222
|
+
async getAllUSDCBalances(): Promise<{ wallet: Wallet; balance: string; chain: string }[]> {
|
|
223
|
+
const wallets = await this.listWallets();
|
|
224
|
+
const results: { wallet: Wallet; balance: string; chain: string }[] = [];
|
|
225
|
+
|
|
226
|
+
for (const wallet of wallets) {
|
|
227
|
+
try {
|
|
228
|
+
const balances = await this.getBalances(wallet.id);
|
|
229
|
+
const usdcBalance = balances.find(b => b.token.symbol === 'USDC');
|
|
230
|
+
|
|
231
|
+
results.push({
|
|
232
|
+
wallet,
|
|
233
|
+
balance: usdcBalance?.amount || '0',
|
|
234
|
+
chain: wallet.blockchain,
|
|
235
|
+
});
|
|
236
|
+
} catch (err) {
|
|
237
|
+
console.error(`Failed to get balance for wallet ${wallet.id}:`, err);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return results;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ============ Transaction Operations ============
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Send USDC to an address
|
|
248
|
+
*/
|
|
249
|
+
async sendUSDC(options: SendOptions): Promise<Transaction> {
|
|
250
|
+
const wallet = await this.getWallet(options.fromWalletId);
|
|
251
|
+
const tokenId = options.tokenId || USDC_TOKENS[wallet.blockchain];
|
|
252
|
+
|
|
253
|
+
if (!tokenId) {
|
|
254
|
+
throw new Error(`No USDC token ID found for chain ${wallet.blockchain}`);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return this.request<Transaction>('POST', '/developer/transactions/transfer', {
|
|
258
|
+
walletId: options.fromWalletId,
|
|
259
|
+
tokenId,
|
|
260
|
+
destinationAddress: options.toAddress,
|
|
261
|
+
amounts: [options.amount],
|
|
262
|
+
fee: {
|
|
263
|
+
type: 'level',
|
|
264
|
+
config: {
|
|
265
|
+
feeLevel: options.feeLevel || 'MEDIUM',
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Get transaction by ID
|
|
273
|
+
*/
|
|
274
|
+
async getTransaction(transactionId: string): Promise<Transaction> {
|
|
275
|
+
return this.request<Transaction>('GET', `/transactions/${transactionId}`);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* List recent transactions
|
|
280
|
+
*/
|
|
281
|
+
async listTransactions(walletId?: string): Promise<Transaction[]> {
|
|
282
|
+
const path = walletId
|
|
283
|
+
? `/wallets/${walletId}/transactions`
|
|
284
|
+
: '/transactions';
|
|
285
|
+
|
|
286
|
+
const result = await this.request<{ transactions: Transaction[] }>('GET', path);
|
|
287
|
+
return result.transactions || [];
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// ============ CCTP Bridge Operations ============
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Bridge USDC across chains via CCTP
|
|
294
|
+
*/
|
|
295
|
+
async bridgeUSDC(options: BridgeOptions): Promise<Transaction> {
|
|
296
|
+
// CCTP bridge is done through contract interactions
|
|
297
|
+
// This is a simplified version - full implementation would use
|
|
298
|
+
// Circle's CCTP contracts directly
|
|
299
|
+
|
|
300
|
+
return this.request<Transaction>('POST', '/developer/transactions/contractExecution', {
|
|
301
|
+
walletId: options.fromWalletId,
|
|
302
|
+
contractAddress: this.getCCTPContractAddress(options.fromChain),
|
|
303
|
+
abiFunctionSignature: 'depositForBurn(uint256,uint32,bytes32,address)',
|
|
304
|
+
abiParameters: [
|
|
305
|
+
options.amount,
|
|
306
|
+
this.getChainDomain(options.toChain),
|
|
307
|
+
this.addressToBytes32(options.toAddress),
|
|
308
|
+
USDC_TOKENS[options.fromChain],
|
|
309
|
+
],
|
|
310
|
+
fee: {
|
|
311
|
+
type: 'level',
|
|
312
|
+
config: { feeLevel: 'HIGH' },
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Get CCTP TokenMessenger contract address for chain
|
|
319
|
+
*/
|
|
320
|
+
private getCCTPContractAddress(chain: string): string {
|
|
321
|
+
const contracts: Record<string, string> = {
|
|
322
|
+
'ETH-SEPOLIA': '0x9f3B8679c73C2Fef8b59B4f3444d4e156fb70AA5',
|
|
323
|
+
'AVAX-FUJI': '0xeb08f243E5d3FCFF26A9E38Ae5520A669f4019d0',
|
|
324
|
+
'ARB-SEPOLIA': '0x9f3B8679c73C2Fef8b59B4f3444d4e156fb70AA5',
|
|
325
|
+
};
|
|
326
|
+
return contracts[chain] || '';
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Get CCTP domain ID for chain
|
|
331
|
+
*/
|
|
332
|
+
private getChainDomain(chain: string): number {
|
|
333
|
+
const domains: Record<string, number> = {
|
|
334
|
+
'ETH-SEPOLIA': 0,
|
|
335
|
+
'AVAX-FUJI': 1,
|
|
336
|
+
'ARB-SEPOLIA': 3,
|
|
337
|
+
'MATIC-AMOY': 7,
|
|
338
|
+
};
|
|
339
|
+
return domains[chain] || 0;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Convert address to bytes32 format
|
|
344
|
+
*/
|
|
345
|
+
private addressToBytes32(address: string): string {
|
|
346
|
+
return '0x' + address.slice(2).padStart(64, '0');
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// ============ Utility Methods ============
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Format USDC amount for display
|
|
353
|
+
*/
|
|
354
|
+
static formatUSDC(amount: string): string {
|
|
355
|
+
const num = parseFloat(amount);
|
|
356
|
+
return `${num.toFixed(2)} USDC`;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Parse human-readable amount to wei
|
|
361
|
+
*/
|
|
362
|
+
static parseAmount(amount: string): string {
|
|
363
|
+
// USDC has 6 decimals
|
|
364
|
+
const num = parseFloat(amount);
|
|
365
|
+
return (num * 1_000_000).toString();
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Validate Ethereum address
|
|
370
|
+
*/
|
|
371
|
+
static isValidAddress(address: string): boolean {
|
|
372
|
+
return /^0x[a-fA-F0-9]{40}$/.test(address);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export default CircleClient;
|