1
1
#!/usr/bin/env node
2
2
3
3
import { Buffer } from "buffer" ;
4
- import { NodeApiHttpClient , IndexerApiHttpClient , blob_builder } from "hyli" ;
4
+ import { NodeApiHttpClient , IndexerApiHttpClient , blob_builder , Blob , BlobTransaction } from "hyli" ;
5
5
import { check_secret } from "hyli-noir" ;
6
6
import EC from "elliptic" ;
7
7
import pkg from "js-sha3" ;
8
- import { register as registerBlob , addSessionKey as addSessionKeyBlob , verifyIdentity } from "hyli-wallet" ;
8
+ import { register as registerBlob , addSessionKey as addSessionKeyBlob , verifyIdentity , Wallet , AccountInfo } from "hyli-wallet" ;
9
+
9
10
const { sha3_256 } = pkg ;
10
11
12
+ // Type definitions
13
+ interface Config {
14
+ NODE_BASE_URL: string ;
15
+ INDEXER_BASE_URL: string ;
16
+ WALLET_API_BASE_URL: string ;
17
+ WALLET_CONTRACT_NAME: string ;
18
+ }
19
+
20
+ interface SessionKey {
21
+ publicKey: string ;
22
+ privateKey: string ;
23
+ expiration: number ;
24
+ whitelist: string [ ] ;
25
+ }
26
+
27
+ interface RegistrationResult {
28
+ success: boolean ;
29
+ wallet ? : Wallet ;
30
+ error ? : string ;
31
+ }
32
+
33
+ interface TransferResult {
34
+ success: boolean ;
35
+ transactionHash ? : string ;
36
+ proofTransactionHash ? : string ;
37
+ transfer ?: {
38
+ from : string ;
39
+ to: string ;
40
+ amount: number ;
41
+ token: string ;
42
+ } ;
43
+ error ? : string ;
44
+ }
45
+
11
46
// Configuration - update these values
12
- const CONFIG = {
47
+ const CONFIG : Config = {
13
48
NODE_BASE_URL : process . env . NODE_BASE_URL || "http://localhost:4321" ,
14
49
INDEXER_BASE_URL : process . env . INDEXER_BASE_URL || "http://localhost:4322" ,
15
50
WALLET_API_BASE_URL : process . env . WALLET_API_BASE_URL || "http://localhost:4000" ,
16
51
WALLET_CONTRACT_NAME : "wallet" ,
17
52
} ;
18
53
19
54
// Utility functions
20
- const encodeToHex = ( data ) => {
55
+ const encodeToHex = ( data : Uint8Array ) : string => {
21
56
return Array . from ( data )
22
57
. map ( ( byte ) => byte . toString ( 16 ) . padStart ( 2 , "0" ) )
23
58
. join ( "" ) ;
24
59
} ;
25
60
26
- async function hashBlob ( blob ) {
61
+ async function hashBlob ( blob : Blob ) : Promise < string > {
27
62
const contractBytes = new TextEncoder ( ) . encode ( blob . contract_name ) ;
28
- const dataBytes = blob . data instanceof Uint8Array ? blob . data : new Uint8Array ( blob . data ) ;
63
+ const dataBytes = new Uint8Array ( blob . data ) ;
29
64
const input = new Uint8Array ( contractBytes . length + dataBytes . length ) ;
30
65
input . set ( contractBytes , 0 ) ;
31
66
input . set ( dataBytes , contractBytes . length ) ;
32
67
return encodeToHex ( new Uint8Array ( sha3_256 . arrayBuffer ( input ) ) ) ;
33
68
}
34
69
35
- async function hashBlobTransaction ( tx ) {
70
+ async function hashBlobTransaction ( tx : BlobTransaction ) : Promise < string > {
36
71
const identityBytes = new TextEncoder ( ) . encode ( tx . identity ) ;
37
72
let input = new Uint8Array ( identityBytes . length ) ;
38
73
input . set ( identityBytes , 0 ) ;
@@ -47,8 +82,13 @@ async function hashBlobTransaction(tx) {
47
82
}
48
83
49
84
// Password validation function
50
- async function validatePassword ( username , password , accountInfo ) {
85
+ async function validatePassword ( username : string , password : string , accountInfo : AccountInfo ) : Promise < boolean > {
51
86
const identity = `${ username } @${ CONFIG . WALLET_CONTRACT_NAME } ` ;
87
+
88
+ if ( ! ( 'Password' in accountInfo . auth_method ) ) {
89
+ throw new Error ( "Account does not use password authentication" ) ;
90
+ }
91
+
52
92
const storedHash = accountInfo . auth_method . Password . hash ;
53
93
const storedSalt = accountInfo . salt ;
54
94
@@ -59,7 +99,7 @@ async function validatePassword(username, password, accountInfo) {
59
99
}
60
100
61
101
// Session key generation
62
- function generateSessionKey ( expiration , whitelist = [ ] ) {
102
+ function generateSessionKey ( expiration : number , whitelist : string [ ] = [ ] ) : SessionKey {
63
103
const ec = new EC . ec ( "secp256k1" ) ;
64
104
const keyPair = ec . genKeyPair ( ) ;
65
105
@@ -82,15 +122,21 @@ function generateSessionKey(expiration, whitelist = []) {
82
122
}
83
123
84
124
// Main registration function
85
- async function registerAccount ( username , password , inviteCode , salt , enableSessionKey = false ) {
125
+ async function registerAccount (
126
+ username : string ,
127
+ password : string ,
128
+ inviteCode : string ,
129
+ salt : string ,
130
+ enableSessionKey : boolean = false
131
+ ) : Promise < RegistrationResult > {
86
132
console . log ( `Starting registration for username: ${ username } ` ) ;
87
133
88
134
try {
89
135
// Initialize services
90
136
const nodeService = new NodeApiHttpClient ( CONFIG . NODE_BASE_URL ) ;
91
137
92
138
// Check if account already exists
93
- let accountInfo ;
139
+ let accountInfo : AccountInfo | undefined ;
94
140
try {
95
141
const response = await fetch (
96
142
`${ CONFIG . WALLET_API_BASE_URL } /v1/indexer/contract/${ CONFIG . WALLET_CONTRACT_NAME } /account/${ username } ` ,
@@ -106,7 +152,11 @@ async function registerAccount(username, password, inviteCode, salt, enableSessi
106
152
107
153
if ( accountInfo ) {
108
154
console . log ( `Account ${ username } already exists` ) ;
109
- return { success : true , wallet : accountInfo } ;
155
+ return { success : true , wallet : {
156
+ username,
157
+ address : `${ username } @${ CONFIG . WALLET_CONTRACT_NAME } ` ,
158
+ salt : accountInfo . salt
159
+ } } ;
110
160
}
111
161
112
162
// Validate password
@@ -116,7 +166,7 @@ async function registerAccount(username, password, inviteCode, salt, enableSessi
116
166
117
167
// Claim invite code
118
168
console . log ( "Claiming invite code..." ) ;
119
- let inviteCodeBlob ;
169
+ let inviteCodeBlob : Blob ;
120
170
try {
121
171
const response = await fetch ( `${ CONFIG . WALLET_API_BASE_URL } /api/consume_invite` , {
122
172
method : "POST" ,
@@ -135,7 +185,7 @@ async function registerAccount(username, password, inviteCode, salt, enableSessi
135
185
136
186
inviteCodeBlob = await response . json ( ) ;
137
187
console . log ( "Invite code claimed successfully" ) ;
138
- } catch ( error ) {
188
+ } catch ( error : any ) {
139
189
throw new Error ( `Failed to claim invite code: ${ error . message } ` ) ;
140
190
}
141
191
@@ -148,13 +198,13 @@ async function registerAccount(username, password, inviteCode, salt, enableSessi
148
198
const hash = Buffer . from ( blob0 . data ) . toString ( "hex" ) ;
149
199
const blob1 = registerBlob ( username , Date . now ( ) , salt , { Password : { hash : hash } } , inviteCode ) ;
150
200
151
- const blobTx = {
201
+ const blobTx : BlobTransaction = {
152
202
identity,
153
203
blobs : [ blob0 , blob1 , inviteCodeBlob ] ,
154
204
} ;
155
205
156
206
// Generate session key if requested
157
- let newSessionKey ;
207
+ let newSessionKey : SessionKey | undefined ;
158
208
if ( enableSessionKey ) {
159
209
console . log ( "Generating session key..." ) ;
160
210
const { duration = 24 * 60 * 60 * 1000 , whitelist = [ ] } = { } ; // 24 hours default
@@ -192,7 +242,7 @@ async function registerAccount(username, password, inviteCode, salt, enableSessi
192
242
console . log ( `Proof transaction hash: ${ proofTxHash } ` ) ;
193
243
194
244
// Create wallet object
195
- const wallet = {
245
+ const wallet : Wallet = {
196
246
username,
197
247
address : identity ,
198
248
salt,
@@ -206,14 +256,20 @@ async function registerAccount(username, password, inviteCode, salt, enableSessi
206
256
console . log ( "Wallet:" , JSON . stringify ( wallet , null , 2 ) ) ;
207
257
208
258
return { success : true , wallet } ;
209
- } catch ( error ) {
259
+ } catch ( error : any ) {
210
260
console . error ( "Registration failed:" , error ) ;
211
261
return { success : false , error : error . message } ;
212
262
}
213
263
}
214
264
215
265
// Transfer function
216
- async function transferFunds ( username , password , amount , token , destination ) {
266
+ async function transferFunds (
267
+ username : string ,
268
+ password : string ,
269
+ amount : string ,
270
+ token : string ,
271
+ destination : string
272
+ ) : Promise < TransferResult > {
217
273
console . log ( `Starting transfer from ${ username } to ${ destination } ` ) ;
218
274
console . log ( `Amount: ${ amount } ${ token } ` ) ;
219
275
@@ -234,7 +290,7 @@ async function transferFunds(username, password, amount, token, destination) {
234
290
235
291
// Check if account exists and get account info
236
292
console . log ( "Checking account information..." ) ;
237
- let accountInfo ;
293
+ let accountInfo : AccountInfo ;
238
294
try {
239
295
const response = await fetch (
240
296
`${ CONFIG . WALLET_API_BASE_URL } /v1/indexer/contract/${ CONFIG . WALLET_CONTRACT_NAME } /account/${ username } ` ,
@@ -247,7 +303,7 @@ async function transferFunds(username, password, amount, token, destination) {
247
303
if ( ! accountInfo ) {
248
304
throw new Error ( `Account with username "${ username } " does not exist.` ) ;
249
305
}
250
- } catch ( error ) {
306
+ } catch ( error : any ) {
251
307
throw new Error ( `Failed to get account info: ${ error . message } ` ) ;
252
308
}
253
309
@@ -263,14 +319,14 @@ async function transferFunds(username, password, amount, token, destination) {
263
319
264
320
// Check that account has enough balance
265
321
console . log ( "Checking balance..." ) ;
266
- let balance ;
322
+ let balance : number ;
267
323
try {
268
324
const result = await indexerService . get (
269
325
`v1/indexer/contract/${ token } /balance/${ identity } ` ,
270
326
"Checking balance" ,
271
- ) ;
327
+ ) as { balance : number } ;
272
328
balance = result . balance ;
273
- } catch ( error ) {
329
+ } catch ( error : any ) {
274
330
throw new Error ( `Failed to get balance: ${ error . message } . User might have no balance for this token.` ) ;
275
331
}
276
332
console . log ( `Balance for ${ username } is` , balance ) ;
@@ -285,7 +341,7 @@ async function transferFunds(username, password, amount, token, destination) {
285
341
const blob1 = verifyIdentity ( username , Date . now ( ) ) ;
286
342
const blob2 = blob_builder . smt_token . transfer ( identity , destination , token , BigInt ( parsedAmount ) , null ) ;
287
343
288
- const blobTx = {
344
+ const blobTx : BlobTransaction = {
289
345
identity,
290
346
blobs : [ blob0 , blob1 , blob2 ] ,
291
347
} ;
@@ -328,14 +384,14 @@ async function transferFunds(username, password, amount, token, destination) {
328
384
token : token ,
329
385
} ,
330
386
} ;
331
- } catch ( error ) {
387
+ } catch ( error : any ) {
332
388
console . error ( "Transfer failed:" , error ) ;
333
389
return { success : false , error : error . message } ;
334
390
}
335
391
}
336
392
337
393
// CLI interface
338
- async function main ( ) {
394
+ async function main ( ) : Promise < void > {
339
395
const args = process . argv . slice ( 2 ) ;
340
396
341
397
if ( args . length < 1 ) {
@@ -356,7 +412,7 @@ async function main() {
356
412
}
357
413
}
358
414
359
- function showUsage ( ) {
415
+ function showUsage ( ) : void {
360
416
console. log ( "Usage: hyli-wallet <command> [options]" ) ;
361
417
console . log ( "" ) ;
362
418
console . log ( "Commands:" ) ;
@@ -388,14 +444,14 @@ function showUsage() {
388
444
console . log ( " NODE_BASE_URL=http://localhost:4321 hyli-wallet register myuser mypassword123 INVITE123" ) ;
389
445
}
390
446
391
- async function handleRegisterCommand ( args ) {
447
+ async function handleRegisterCommand ( args : string [ ] ) : Promise < void > {
392
448
if ( args . length < 3 ) {
393
449
console . log ( "Register command requires at least 3 arguments: username, password, inviteCode" ) ;
394
450
console . log ( "Usage: hyli-wallet register <username> <password> <inviteCode> [salt] [enableSessionKey]" ) ;
395
451
process . exit ( 1 ) ;
396
452
}
397
453
398
- const [ username , password , inviteCode , salt = Math . random ( ) . toString ( 36 ) . substring ( 2 ) , enableSessionKey = false ] =
454
+ const [ username , password , inviteCode , salt = Math . random ( ) . toString ( 36 ) . substring ( 2 ) , enableSessionKey = " false" ] =
399
455
args ;
400
456
401
457
console . log ( "Configuration:" ) ;
@@ -418,7 +474,7 @@ async function handleRegisterCommand(args) {
418
474
}
419
475
}
420
476
421
- async function handleTransferCommand ( args ) {
477
+ async function handleTransferCommand ( args : string [ ] ) : Promise < void > {
422
478
if ( args . length < 5 ) {
423
479
console . log ( "Transfer command requires 5 arguments: username, password, amount, token, destination" ) ;
424
480
console . log ( "Usage: hyli-wallet transfer <username> <password> <amount> <token> <destination>" ) ;
0 commit comments