@@ -5,84 +5,66 @@ import {
5
5
type Transfer ,
6
6
type WalletData ,
7
7
} from "@coinbase/coinbase-sdk" ;
8
- import { isAddress , keccak256 , toBytes , toHex } from "viem" ;
8
+ import { isAddress } from "viem" ;
9
9
import { getWalletData , saveWalletData } from "./storage" ;
10
10
11
- // Warn about optional variables
12
- if ( ! process . env . NETWORK_ID ) {
13
- console . warn ( "Warning: NETWORK_ID not set, defaulting to base-mainnet" ) ;
14
- }
15
-
11
+ const coinbaseApiKeyName = process . env . CDP_API_KEY_NAME ;
12
+ let coinbaseApiKeyPrivateKey = process . env . CDP_API_KEY_PRIVATE_KEY ;
16
13
const networkId = process . env . NETWORK_ID ?? "base-sepolia" ;
17
14
15
+ if ( ! coinbaseApiKeyName || ! coinbaseApiKeyPrivateKey || ! networkId ) {
16
+ console . error (
17
+ "Either networkId, CDP_API_KEY_NAME or CDP_API_KEY_PRIVATE_KEY must be set" ,
18
+ ) ;
19
+ process . exit ( 1 ) ;
20
+ }
21
+
18
22
class WalletStorage {
19
- async get ( key : string ) : Promise < string | undefined > {
23
+ async get ( inboxId : string ) : Promise < string | undefined > {
20
24
try {
21
- const data = await getWalletData ( key , networkId ) ;
25
+ const data = await getWalletData ( inboxId , networkId ) ;
22
26
return data ?? undefined ;
23
27
} catch ( error : unknown ) {
24
28
const errorMessage =
25
29
error instanceof Error ? error . message : String ( error ) ;
26
- console . error ( `Error getting wallet data for ${ key } :` , errorMessage ) ;
30
+ console . error ( `Error getting wallet data for ${ inboxId } :` , errorMessage ) ;
27
31
return undefined ;
28
32
}
29
33
}
30
34
31
- async set ( key : string , value : string ) : Promise < void > {
35
+ async set ( inboxId : string , value : string ) : Promise < void > {
32
36
try {
33
- await saveWalletData ( key , value , networkId ) ;
37
+ await saveWalletData ( inboxId , value , networkId ) ;
34
38
} catch ( error : unknown ) {
35
39
const errorMessage =
36
40
error instanceof Error ? error . message : String ( error ) ;
37
- console . error ( `Error saving wallet data for ${ key } :` , errorMessage ) ;
41
+ console . error ( `Error saving wallet data for ${ inboxId } :` , errorMessage ) ;
38
42
}
39
43
}
40
44
41
- async del ( key : string ) : Promise < void > {
45
+ async del ( inboxId : string ) : Promise < void > {
42
46
try {
43
- await saveWalletData ( key , "" , networkId ) ;
47
+ await saveWalletData ( inboxId , "" , networkId ) ;
44
48
} catch ( error : unknown ) {
45
49
const errorMessage =
46
50
error instanceof Error ? error . message : String ( error ) ;
47
- console . error ( `Error deleting wallet data for ${ key } :` , errorMessage ) ;
51
+ console . error ( `Error deleting wallet data for ${ inboxId } :` , errorMessage ) ;
48
52
}
49
53
}
50
54
}
51
55
52
56
// Initialize Coinbase SDK
53
57
function initializeCoinbaseSDK ( ) : boolean {
54
- const coinbaseApiKeyName =
55
- process . env . COINBASE_API_KEY_NAME || process . env . CDP_API_KEY_NAME ;
56
- let coinbaseApiKeyPrivateKey =
57
- process . env . COINBASE_API_KEY_PRIVATE_KEY ||
58
- process . env . CDP_API_KEY_PRIVATE_KEY ;
59
58
// Replace \\n with actual newlines if present in the private key
60
59
if ( coinbaseApiKeyPrivateKey ) {
61
60
coinbaseApiKeyPrivateKey = coinbaseApiKeyPrivateKey . replace ( / \\ n / g, "\n" ) ;
62
61
}
63
-
64
- console . log (
65
- "coinbaseApiKeyName:" ,
66
- coinbaseApiKeyName ? "Defined" : "Undefined" ,
67
- ) ;
68
- console . log (
69
- "coinbaseApiKeyPrivateKey:" ,
70
- coinbaseApiKeyPrivateKey ? "Defined" : "Undefined" ,
71
- ) ;
72
- console . log ( "networkId:" , networkId ? "Defined" : "Undefined" ) ;
73
- if ( ! coinbaseApiKeyName || ! coinbaseApiKeyPrivateKey ) {
74
- console . error (
75
- "Either COINBASE_API_KEY_NAME/COINBASE_API_KEY_PRIVATE_KEY or CDP_API_KEY_NAME/CDP_API_KEY_PRIVATE_KEY must be set" ,
76
- ) ;
77
- return false ;
78
- }
79
-
80
62
try {
81
63
Coinbase . configure ( {
82
- apiKeyName : coinbaseApiKeyName ,
83
- privateKey : coinbaseApiKeyPrivateKey ,
64
+ apiKeyName : coinbaseApiKeyName as string ,
65
+ privateKey : coinbaseApiKeyPrivateKey as string ,
84
66
} ) ;
85
- console . log ( "Coinbase SDK initialized successfully" ) ;
67
+ console . log ( "Coinbase SDK initialized successfully, network:" , networkId ) ;
86
68
return true ;
87
69
} catch ( error : unknown ) {
88
70
const errorMessage = error instanceof Error ? error . message : String ( error ) ;
@@ -91,77 +73,41 @@ function initializeCoinbaseSDK(): boolean {
91
73
}
92
74
}
93
75
94
- // Define wallet information structure
95
- interface WalletInfo {
96
- data : WalletData ;
97
- agent_address : string ;
98
- address : string ;
99
- key : string ;
100
- }
101
-
102
76
// Agent wallet data
103
77
export type AgentWalletData = {
104
78
id : string ;
105
79
wallet : Wallet ;
106
- address : string ;
80
+ data : WalletData ;
81
+ human_address : string ;
107
82
agent_address : string ;
108
83
blockchain ?: string ;
109
84
state ?: string ;
110
- key : string ;
85
+ inboxId : string ;
111
86
} ;
112
87
113
88
// Wallet service class based on cointoss implementation
114
89
export class WalletService {
115
90
private walletStorage : WalletStorage ;
116
- private cdpEncryptionKey : string ;
117
- private senderAddress : string ;
91
+ private humanAddress : string ;
92
+ private inboxId : string ;
118
93
private sdkInitialized : boolean ;
119
94
120
- constructor ( sender : string ) {
95
+ constructor ( inboxId : string , address : string ) {
121
96
this . sdkInitialized = initializeCoinbaseSDK ( ) ;
122
97
this . walletStorage = new WalletStorage ( ) ;
123
- // Use either KEY or ENCRYPTION_KEY environment variable for local wallet encryption
124
- this . cdpEncryptionKey = (
125
- process . env . KEY ||
126
- process . env . ENCRYPTION_KEY ||
127
- ""
128
- ) . toLowerCase ( ) ;
129
- this . senderAddress = sender . toLowerCase ( ) ;
130
- console . log ( "WalletService initialized with sender" , this . senderAddress ) ;
131
- }
132
-
133
- encrypt ( data : unknown ) : string {
134
- let stringData = "" ;
135
- if ( typeof data === "string" ) {
136
- stringData = data . toLowerCase ( ) ;
137
- } else {
138
- stringData = JSON . stringify ( data ) ;
139
- }
140
-
141
- const key = keccak256 ( toHex ( this . cdpEncryptionKey ) ) ;
142
- // Simple XOR encryption with the key
143
- const encrypted = Buffer . from ( stringData ) . map (
144
- ( byte , i ) => byte ^ parseInt ( key . slice ( 2 + ( i % 64 ) , 4 + ( i % 64 ) ) , 16 ) ,
145
- ) ;
146
- return toHex ( encrypted ) . toLowerCase ( ) ;
147
- }
148
-
149
- decrypt ( data : string ) : WalletInfo {
150
- if ( typeof data === "string" ) {
151
- data = data . toLowerCase ( ) ;
152
- }
153
- const key = keccak256 ( toHex ( this . cdpEncryptionKey ) ) ;
154
- const encrypted = toBytes ( data ) ;
155
- const decrypted = encrypted . map (
156
- ( byte , i ) => byte ^ parseInt ( key . slice ( 2 + ( i % 64 ) , 4 + ( i % 64 ) ) , 16 ) ,
98
+ this . humanAddress = address ;
99
+ this . inboxId = inboxId ;
100
+ console . log (
101
+ "WalletService initialized with sender address" ,
102
+ this . humanAddress ,
103
+ "and inboxId" ,
104
+ this . inboxId ,
157
105
) ;
158
- return JSON . parse ( Buffer . from ( decrypted ) . toString ( ) ) as WalletInfo ;
159
106
}
160
107
161
- async createWallet ( key : string ) : Promise < AgentWalletData > {
108
+ async createWallet ( ) : Promise < AgentWalletData > {
162
109
try {
163
- key = key . toLowerCase ( ) ;
164
- console . log ( `Creating new wallet for key ${ key } ...` ) ;
110
+ console . log ( `Creating new wallet for key ${ this . inboxId } ...` ) ;
165
111
166
112
// Initialize SDK if not already done
167
113
if ( ! this . sdkInitialized ) {
@@ -190,30 +136,35 @@ export class WalletService {
190
136
191
137
// Make the wallet address visible in the logs for funding
192
138
console . log ( "-----------------------------------------------------" ) ;
193
- console . log ( `NEW WALLET CREATED FOR USER: ${ key } ` ) ;
139
+ console . log ( `NEW WALLET CREATED FOR USER: ${ this . humanAddress } ` ) ;
194
140
console . log ( `WALLET ADDRESS: ${ walletAddress } ` ) ;
195
141
console . log ( `NETWORK: ${ networkId } ` ) ;
196
142
console . log ( `SEND FUNDS TO THIS ADDRESS TO TEST: ${ walletAddress } ` ) ;
197
143
console . log ( "-----------------------------------------------------" ) ;
198
144
199
- const walletInfo : WalletInfo = {
200
- data,
145
+ const walletInfo : AgentWalletData = {
146
+ id : walletAddress ,
147
+ wallet : wallet ,
148
+ data : data ,
149
+ human_address : this . humanAddress ,
201
150
agent_address : walletAddress ,
202
- address : this . senderAddress ,
203
- key,
151
+ inboxId : this . inboxId ,
204
152
} ;
205
153
206
154
console . log ( "Saving wallet data to storage..." ) ;
207
- await this . walletStorage . set ( this . encrypt ( key ) , this . encrypt ( walletInfo ) ) ;
208
-
209
- console . log ( "Wallet created and saved successfully" ) ;
210
- return {
211
- id : walletAddress ,
212
- wallet : wallet ,
213
- address : this . senderAddress ,
155
+ const walletInfoToStore = {
156
+ data : data ,
157
+ human_address : this . humanAddress ,
214
158
agent_address : walletAddress ,
215
- key : key ,
159
+ inboxId : this . inboxId ,
216
160
} ;
161
+ await this . walletStorage . set (
162
+ this . inboxId ,
163
+ JSON . stringify ( walletInfoToStore ) ,
164
+ ) ;
165
+
166
+ console . log ( "Wallet created and saved successfully" ) ;
167
+ return walletInfo ;
217
168
} catch ( error : unknown ) {
218
169
console . error ( "Failed to create wallet:" , error ) ;
219
170
@@ -227,21 +178,19 @@ export class WalletService {
227
178
}
228
179
229
180
async getWallet (
230
- key : string ,
181
+ inboxId : string ,
231
182
createIfNotFound : boolean = true ,
232
183
) : Promise < AgentWalletData | undefined > {
233
- console . log ( "Getting wallet for:" , key ) ;
234
- key = key . toLowerCase ( ) ;
235
- const encryptedKey = this . encrypt ( key ) ;
236
- const walletData = await this . walletStorage . get ( encryptedKey ) ;
184
+ console . log ( "Getting wallet for:" , inboxId ) ;
185
+ const walletData = await this . walletStorage . get ( inboxId ) ;
237
186
238
187
// If no wallet exists, create one
239
188
if ( ! walletData ) {
240
- console . log ( "No wallet found for" , key ) ;
189
+ console . log ( "No wallet found for" , inboxId ) ;
241
190
if ( createIfNotFound ) {
242
191
console . log ( "Creating new wallet as none was found" ) ;
243
192
try {
244
- const wallet = await this . createWallet ( key ) ;
193
+ const wallet = await this . createWallet ( ) ;
245
194
console . log ( "Successfully created new wallet, returning wallet data" ) ;
246
195
return wallet ;
247
196
} catch ( error : unknown ) {
@@ -256,10 +205,10 @@ export class WalletService {
256
205
257
206
try {
258
207
console . log ( "Found existing wallet data, decrypting..." ) ;
259
- const decrypted = this . decrypt ( walletData ) ;
208
+ const walletInfo = JSON . parse ( walletData ) as AgentWalletData ;
260
209
261
210
console . log ( "Importing wallet from stored data..." ) ;
262
- const importedWallet = await Wallet . import ( decrypted . data ) . catch (
211
+ const importedWallet = await Wallet . import ( walletInfo . data ) . catch (
263
212
( err : unknown ) => {
264
213
const errorMessage = err instanceof Error ? err . message : String ( err ) ;
265
214
console . error ( "Error importing wallet:" , errorMessage ) ;
@@ -271,9 +220,10 @@ export class WalletService {
271
220
return {
272
221
id : importedWallet . getId ( ) ?? "" ,
273
222
wallet : importedWallet ,
274
- agent_address : decrypted . agent_address ,
275
- address : decrypted . address ,
276
- key : decrypted . key ,
223
+ data : walletInfo . data ,
224
+ human_address : walletInfo . human_address ,
225
+ agent_address : walletInfo . agent_address ,
226
+ inboxId : walletInfo . inboxId ,
277
227
} ;
278
228
} catch ( error : unknown ) {
279
229
const errorMessage =
@@ -283,27 +233,26 @@ export class WalletService {
283
233
// If we failed to import, but have wallet data, attempt to recreate
284
234
if ( createIfNotFound ) {
285
235
console . log ( "Attempting to recreate wallet after import failure" ) ;
286
- return this . createWallet ( key ) ;
236
+ return this . createWallet ( ) ;
287
237
}
288
238
289
239
throw new Error ( "Invalid wallet access" ) ;
290
240
}
291
241
}
292
242
293
243
async checkBalance (
294
- humanAddress : string ,
244
+ inboxId : string ,
295
245
) : Promise < { address : string | undefined ; balance : number } > {
296
- humanAddress = humanAddress . toLowerCase ( ) ;
297
- console . log ( `⚖️ Checking balance for user: ${ humanAddress } ...` ) ;
246
+ console . log ( `⚖️ Checking balance for user with inboxId: ${ inboxId } ...` ) ;
298
247
299
- const walletData = await this . getWallet ( humanAddress ) ;
248
+ const walletData = await this . getWallet ( inboxId ) ;
300
249
if ( ! walletData ) {
301
- console . log ( `❌ No wallet found for ${ humanAddress } ` ) ;
250
+ console . log ( `❌ No wallet found for user with inboxId: ${ inboxId } ` ) ;
302
251
return { address : undefined , balance : 0 } ;
303
252
}
304
253
305
254
console . log (
306
- `✅ Retrieved wallet with address: ${ walletData . agent_address } for user: ${ humanAddress } ` ,
255
+ `✅ Retrieved wallet with address: ${ walletData . agent_address } for user with inboxId : ${ inboxId } ` ,
307
256
) ;
308
257
309
258
try {
@@ -312,7 +261,7 @@ export class WalletService {
312
261
) ;
313
262
const balance = await walletData . wallet . getBalance ( Coinbase . assets . Usdc ) ;
314
263
console . log (
315
- `💵 USDC Balance for ${ humanAddress } : ${ Number ( balance ) } USDC` ,
264
+ `💵 USDC Balance for user with inboxId: ${ inboxId } : ${ Number ( balance ) } USDC` ,
316
265
) ;
317
266
console . log ( Coinbase . assets . Usdc ) ;
318
267
@@ -324,7 +273,7 @@ export class WalletService {
324
273
const errorMessage =
325
274
error instanceof Error ? error . message : String ( error ) ;
326
275
console . error (
327
- `❌ Error getting balance for ${ humanAddress } :` ,
276
+ `❌ Error getting balance for user with inboxId: ${ inboxId } :` ,
328
277
errorMessage ,
329
278
) ;
330
279
return {
@@ -335,23 +284,24 @@ export class WalletService {
335
284
}
336
285
337
286
async transfer (
338
- fromAddress : string ,
287
+ inboxId : string ,
288
+ humanAddress : string ,
339
289
toAddress : string ,
340
290
amount : number ,
341
291
) : Promise < Transfer | undefined > {
342
- fromAddress = fromAddress . toLowerCase ( ) ;
292
+ humanAddress = humanAddress . toLowerCase ( ) ;
343
293
toAddress = toAddress . toLowerCase ( ) ;
344
294
345
295
console . log ( "📤 TRANSFER INITIATED" ) ;
346
296
console . log ( `💸 Amount: ${ amount } USDC` ) ;
347
- console . log ( `🔍 From user: ${ fromAddress } ` ) ;
297
+ console . log ( `🔍 From user: ${ humanAddress } ` ) ;
348
298
console . log ( `🔍 To: ${ toAddress } ` ) ;
349
299
350
300
// Get the source wallet
351
- console . log ( `🔑 Retrieving source wallet for user: ${ fromAddress } ...` ) ;
352
- const from = await this . getWallet ( fromAddress ) ;
301
+ console . log ( `🔑 Retrieving source wallet for user: ${ humanAddress } ...` ) ;
302
+ const from = await this . getWallet ( inboxId ) ;
353
303
if ( ! from ) {
354
- console . error ( `❌ No wallet found for sender: ${ fromAddress } ` ) ;
304
+ console . error ( `❌ No wallet found for sender: ${ humanAddress } ` ) ;
355
305
return undefined ;
356
306
}
357
307
console . log ( `✅ Source wallet found: ${ from . agent_address } ` ) ;
0 commit comments