Skip to content

Commit c1f3296

Browse files
committed
✨ Move wallet cli to typescript
1 parent dc69926 commit c1f3296

File tree

5 files changed

+140
-41
lines changed

5 files changed

+140
-41
lines changed

script/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
node_modules
2+
dist/
3+
*.js.map

script/bun.lock

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
"devDependencies": {
1616
"@types/crypto-js": "^4.2.2",
1717
"@types/elliptic": "^6.4.18",
18+
"@types/node": "^20.0.0",
19+
"typescript": "^5.0.0",
1820
},
1921
},
2022
},
@@ -41,7 +43,7 @@
4143

4244
"@types/history": ["@types/[email protected]", "", {}, "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA=="],
4345

44-
"@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="],
46+
"@types/node": ["@types/node@20.19.16", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-VS6TTONVdgwJwtJr7U+ghEjpfmQdqehLLpg/iMYGOd1+ilaFjdBJwFuPggJ4EAYPDCzWfDUHoIxyVnu+tOWVuQ=="],
4547

4648
"@types/react": ["@types/[email protected]", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w=="],
4749

@@ -117,6 +119,12 @@
117119

118120
"tslib": ["[email protected]", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
119121

120-
"undici-types": ["[email protected]", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="],
122+
"typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
123+
124+
"undici-types": ["[email protected]", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
125+
126+
"@types/bn.js/@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="],
127+
128+
"@types/bn.js/@types/node/undici-types": ["[email protected]", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="],
121129
}
122130
}

script/hyli-wallet.js renamed to script/hyli-wallet.ts

Lines changed: 87 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,73 @@
11
#!/usr/bin/env node
22

33
import { Buffer } from "buffer";
4-
import { NodeApiHttpClient, IndexerApiHttpClient, blob_builder } from "hyli";
4+
import { NodeApiHttpClient, IndexerApiHttpClient, blob_builder, Blob, BlobTransaction } from "hyli";
55
import { check_secret } from "hyli-noir";
66
import EC from "elliptic";
77
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+
910
const { sha3_256 } = pkg;
1011

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+
1146
// Configuration - update these values
12-
const CONFIG = {
47+
const CONFIG: Config = {
1348
NODE_BASE_URL: process.env.NODE_BASE_URL || "http://localhost:4321",
1449
INDEXER_BASE_URL: process.env.INDEXER_BASE_URL || "http://localhost:4322",
1550
WALLET_API_BASE_URL: process.env.WALLET_API_BASE_URL || "http://localhost:4000",
1651
WALLET_CONTRACT_NAME: "wallet",
1752
};
1853

1954
// Utility functions
20-
const encodeToHex = (data) => {
55+
const encodeToHex = (data: Uint8Array): string => {
2156
return Array.from(data)
2257
.map((byte) => byte.toString(16).padStart(2, "0"))
2358
.join("");
2459
};
2560

26-
async function hashBlob(blob) {
61+
async function hashBlob(blob: Blob): Promise<string> {
2762
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);
2964
const input = new Uint8Array(contractBytes.length + dataBytes.length);
3065
input.set(contractBytes, 0);
3166
input.set(dataBytes, contractBytes.length);
3267
return encodeToHex(new Uint8Array(sha3_256.arrayBuffer(input)));
3368
}
3469

35-
async function hashBlobTransaction(tx) {
70+
async function hashBlobTransaction(tx: BlobTransaction): Promise<string> {
3671
const identityBytes = new TextEncoder().encode(tx.identity);
3772
let input = new Uint8Array(identityBytes.length);
3873
input.set(identityBytes, 0);
@@ -47,8 +82,13 @@ async function hashBlobTransaction(tx) {
4782
}
4883

4984
// Password validation function
50-
async function validatePassword(username, password, accountInfo) {
85+
async function validatePassword(username: string, password: string, accountInfo: AccountInfo): Promise<boolean> {
5186
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+
5292
const storedHash = accountInfo.auth_method.Password.hash;
5393
const storedSalt = accountInfo.salt;
5494

@@ -59,7 +99,7 @@ async function validatePassword(username, password, accountInfo) {
5999
}
60100

61101
// Session key generation
62-
function generateSessionKey(expiration, whitelist = []) {
102+
function generateSessionKey(expiration: number, whitelist: string[] = []): SessionKey {
63103
const ec = new EC.ec("secp256k1");
64104
const keyPair = ec.genKeyPair();
65105

@@ -82,15 +122,21 @@ function generateSessionKey(expiration, whitelist = []) {
82122
}
83123

84124
// 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> {
86132
console.log(`Starting registration for username: ${username}`);
87133

88134
try {
89135
// Initialize services
90136
const nodeService = new NodeApiHttpClient(CONFIG.NODE_BASE_URL);
91137

92138
// Check if account already exists
93-
let accountInfo;
139+
let accountInfo: AccountInfo | undefined;
94140
try {
95141
const response = await fetch(
96142
`${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
106152

107153
if (accountInfo) {
108154
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+
} };
110160
}
111161

112162
// Validate password
@@ -116,7 +166,7 @@ async function registerAccount(username, password, inviteCode, salt, enableSessi
116166

117167
// Claim invite code
118168
console.log("Claiming invite code...");
119-
let inviteCodeBlob;
169+
let inviteCodeBlob: Blob;
120170
try {
121171
const response = await fetch(`${CONFIG.WALLET_API_BASE_URL}/api/consume_invite`, {
122172
method: "POST",
@@ -135,7 +185,7 @@ async function registerAccount(username, password, inviteCode, salt, enableSessi
135185

136186
inviteCodeBlob = await response.json();
137187
console.log("Invite code claimed successfully");
138-
} catch (error) {
188+
} catch (error: any) {
139189
throw new Error(`Failed to claim invite code: ${error.message}`);
140190
}
141191

@@ -148,13 +198,13 @@ async function registerAccount(username, password, inviteCode, salt, enableSessi
148198
const hash = Buffer.from(blob0.data).toString("hex");
149199
const blob1 = registerBlob(username, Date.now(), salt, { Password: { hash: hash } }, inviteCode);
150200

151-
const blobTx = {
201+
const blobTx: BlobTransaction = {
152202
identity,
153203
blobs: [blob0, blob1, inviteCodeBlob],
154204
};
155205

156206
// Generate session key if requested
157-
let newSessionKey;
207+
let newSessionKey: SessionKey | undefined;
158208
if (enableSessionKey) {
159209
console.log("Generating session key...");
160210
const { duration = 24 * 60 * 60 * 1000, whitelist = [] } = {}; // 24 hours default
@@ -192,7 +242,7 @@ async function registerAccount(username, password, inviteCode, salt, enableSessi
192242
console.log(`Proof transaction hash: ${proofTxHash}`);
193243

194244
// Create wallet object
195-
const wallet = {
245+
const wallet: Wallet = {
196246
username,
197247
address: identity,
198248
salt,
@@ -206,14 +256,20 @@ async function registerAccount(username, password, inviteCode, salt, enableSessi
206256
console.log("Wallet:", JSON.stringify(wallet, null, 2));
207257

208258
return { success: true, wallet };
209-
} catch (error) {
259+
} catch (error: any) {
210260
console.error("Registration failed:", error);
211261
return { success: false, error: error.message };
212262
}
213263
}
214264

215265
// 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> {
217273
console.log(`Starting transfer from ${username} to ${destination}`);
218274
console.log(`Amount: ${amount} ${token}`);
219275

@@ -234,7 +290,7 @@ async function transferFunds(username, password, amount, token, destination) {
234290

235291
// Check if account exists and get account info
236292
console.log("Checking account information...");
237-
let accountInfo;
293+
let accountInfo: AccountInfo;
238294
try {
239295
const response = await fetch(
240296
`${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) {
247303
if (!accountInfo) {
248304
throw new Error(`Account with username "${username}" does not exist.`);
249305
}
250-
} catch (error) {
306+
} catch (error: any) {
251307
throw new Error(`Failed to get account info: ${error.message}`);
252308
}
253309

@@ -263,14 +319,14 @@ async function transferFunds(username, password, amount, token, destination) {
263319

264320
// Check that account has enough balance
265321
console.log("Checking balance...");
266-
let balance;
322+
let balance: number;
267323
try {
268324
const result = await indexerService.get(
269325
`v1/indexer/contract/${token}/balance/${identity}`,
270326
"Checking balance",
271-
);
327+
) as { balance: number };
272328
balance = result.balance;
273-
} catch (error) {
329+
} catch (error: any) {
274330
throw new Error(`Failed to get balance: ${error.message}. User might have no balance for this token.`);
275331
}
276332
console.log(`Balance for ${username} is`, balance);
@@ -285,7 +341,7 @@ async function transferFunds(username, password, amount, token, destination) {
285341
const blob1 = verifyIdentity(username, Date.now());
286342
const blob2 = blob_builder.smt_token.transfer(identity, destination, token, BigInt(parsedAmount), null);
287343

288-
const blobTx = {
344+
const blobTx: BlobTransaction = {
289345
identity,
290346
blobs: [blob0, blob1, blob2],
291347
};
@@ -328,14 +384,14 @@ async function transferFunds(username, password, amount, token, destination) {
328384
token: token,
329385
},
330386
};
331-
} catch (error) {
387+
} catch (error: any) {
332388
console.error("Transfer failed:", error);
333389
return { success: false, error: error.message };
334390
}
335391
}
336392

337393
// CLI interface
338-
async function main() {
394+
async function main(): Promise<void> {
339395
const args = process.argv.slice(2);
340396

341397
if (args.length < 1) {
@@ -356,7 +412,7 @@ async function main() {
356412
}
357413
}
358414

359-
function showUsage() {
415+
function showUsage(): void {
360416
console.log("Usage: hyli-wallet <command> [options]");
361417
console.log("");
362418
console.log("Commands:");
@@ -388,14 +444,14 @@ function showUsage() {
388444
console.log(" NODE_BASE_URL=http://localhost:4321 hyli-wallet register myuser mypassword123 INVITE123");
389445
}
390446

391-
async function handleRegisterCommand(args) {
447+
async function handleRegisterCommand(args: string[]): Promise<void> {
392448
if (args.length < 3) {
393449
console.log("Register command requires at least 3 arguments: username, password, inviteCode");
394450
console.log("Usage: hyli-wallet register <username> <password> <inviteCode> [salt] [enableSessionKey]");
395451
process.exit(1);
396452
}
397453

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"] =
399455
args;
400456

401457
console.log("Configuration:");
@@ -418,7 +474,7 @@ async function handleRegisterCommand(args) {
418474
}
419475
}
420476

421-
async function handleTransferCommand(args) {
477+
async function handleTransferCommand(args: string[]): Promise<void> {
422478
if (args.length < 5) {
423479
console.log("Transfer command requires 5 arguments: username, password, amount, token, destination");
424480
console.log("Usage: hyli-wallet transfer <username> <password> <amount> <token> <destination>");

script/package.json

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "hyli-wallet-cli",
3-
"version": "0.5.2",
3+
"version": "0.6.0",
44
"type": "module",
55
"license": "MIT",
66
"repository": {
@@ -11,18 +11,20 @@
1111
"bugs": {
1212
"url": "https://github.com/hyli-org/wallet/issues"
1313
},
14-
"main": "hyli-wallet.js",
14+
"main": "dist/hyli-wallet.js",
1515
"bin": {
16-
"hyli-wallet": "./hyli-wallet.js"
16+
"hyli-wallet": "./dist/hyli-wallet.js"
1717
},
1818
"files": [
19-
"hyli-wallet.js",
19+
"dist/",
2020
"README.md"
2121
],
2222
"scripts": {
23-
"start": "node hyli-wallet.js",
24-
"register": "node hyli-wallet.js",
25-
"pub": "npm publish"
23+
"build": "tsc",
24+
"prepublishOnly": "bun run build",
25+
"start": "node dist/hyli-wallet.js",
26+
"dev": "tsc --watch",
27+
"pub": "bun publish"
2628
},
2729
"dependencies": {
2830
"buffer": "^6.0.3",
@@ -35,7 +37,9 @@
3537
},
3638
"devDependencies": {
3739
"@types/crypto-js": "^4.2.2",
38-
"@types/elliptic": "^6.4.18"
40+
"@types/elliptic": "^6.4.18",
41+
"@types/node": "^20.0.0",
42+
"typescript": "^5.0.0"
3943
},
4044
"keywords": [
4145
"wallet",

0 commit comments

Comments
 (0)