hodl-wallet 1.8.0 → 1.8.4
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/index.js +8 -2
- package/network/lib/BaseNetwork.js +20 -1
- package/network/lib/BitcoinNetwork.js +23 -18
- package/network/lib/TONNetwork.js +0 -26
- package/network/lib/Web3Network.js +13 -22
- package/package.json +2 -1
package/index.js
CHANGED
|
@@ -464,17 +464,23 @@ class Wallet {
|
|
|
464
464
|
|
|
465
465
|
const transactionHash = receipt?.transactionHash || receipt?.hash || 'UNKNOWN_HASH';
|
|
466
466
|
|
|
467
|
-
//
|
|
467
|
+
// Calculate the post-transaction balance
|
|
468
468
|
let currentBalance = 0;
|
|
469
469
|
try {
|
|
470
470
|
const walletAddress = await this.getAddress();
|
|
471
|
+
|
|
472
|
+
// Get the balance AFTER transaction (not before)
|
|
471
473
|
if (token === this.selectedNetwork.nativeToken) {
|
|
472
474
|
currentBalance = await this.network.getBalance(walletAddress);
|
|
473
475
|
} else {
|
|
474
476
|
currentBalance = await this.network.getTokenBalance(walletAddress, token);
|
|
475
477
|
}
|
|
478
|
+
|
|
479
|
+
// Round to avoid floating point precision issues
|
|
480
|
+
currentBalance = Math.round(currentBalance * 100000000) / 100000000;
|
|
481
|
+
|
|
476
482
|
} catch (error) {
|
|
477
|
-
console.error('Error getting
|
|
483
|
+
console.error('Error getting post-transaction balance:', error.message);
|
|
478
484
|
}
|
|
479
485
|
|
|
480
486
|
await this.displayTransactionResult(address, token, numericAmount, transactionHash, currentBalance);
|
|
@@ -47,7 +47,26 @@ export default class BaseNetwork {
|
|
|
47
47
|
throw new Error("Method 'validateMnemonic' must be implemented.");
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
async getTokenBalance(address, tokenSymbol) {
|
|
51
|
+
throw new Error("Method 'getTokenBalance' must be implemented.");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async getTokenBalances(address) {
|
|
55
|
+
const balances = [];
|
|
56
|
+
|
|
57
|
+
// Get native token balance
|
|
58
|
+
const nativeBalance = await this.getBalance(address);
|
|
59
|
+
balances.push([this.config.nativeToken, parseFloat(nativeBalance)]);
|
|
60
|
+
|
|
61
|
+
// Get balances for all configured tokens
|
|
62
|
+
for (const symbol of Object.keys(this.config.tokens)) {
|
|
63
|
+
const tokenBalance = await this.getTokenBalance(address, symbol);
|
|
64
|
+
balances.push([symbol, tokenBalance]);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return balances;
|
|
68
|
+
}
|
|
69
|
+
|
|
51
70
|
async sendSignedTransaction(signedTx) {
|
|
52
71
|
throw new Error("Method 'sendSignedTransaction' must be implemented.");
|
|
53
72
|
}
|
|
@@ -30,20 +30,17 @@ export default class BitcoinNetwork extends BaseNetwork {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
balances.push([this.config.nativeToken, nativeBalance]);
|
|
39
|
-
|
|
40
|
-
return balances;
|
|
33
|
+
async getTokenBalance(address, tokenSymbol) {
|
|
34
|
+
if (tokenSymbol === this.config.nativeToken) {
|
|
35
|
+
return await this.getBalance(address);
|
|
36
|
+
}
|
|
37
|
+
throw new Error(`Token ${tokenSymbol} not supported on Bitcoin network`);
|
|
41
38
|
}
|
|
42
39
|
|
|
43
40
|
async transfer(from, to, amount, options = {}) {
|
|
44
41
|
try {
|
|
45
42
|
const utxos = await this.getUTXOs(from.address);
|
|
46
|
-
const satoshis =
|
|
43
|
+
const satoshis = this.BTCToSatoshis(amount); // Regular number, not BigInt
|
|
47
44
|
const feeRate = options.feeRate || 10; // sats/byte
|
|
48
45
|
|
|
49
46
|
// Create PSBT instance with network
|
|
@@ -51,7 +48,7 @@ export default class BitcoinNetwork extends BaseNetwork {
|
|
|
51
48
|
psbt.setVersion(2);
|
|
52
49
|
psbt.setLocktime(0);
|
|
53
50
|
|
|
54
|
-
let totalInputValue =
|
|
51
|
+
let totalInputValue = 0;
|
|
55
52
|
|
|
56
53
|
// Add inputs
|
|
57
54
|
for (const utxo of utxos) {
|
|
@@ -65,16 +62,16 @@ export default class BitcoinNetwork extends BaseNetwork {
|
|
|
65
62
|
sequence: 0xffffffff
|
|
66
63
|
});
|
|
67
64
|
|
|
68
|
-
totalInputValue +=
|
|
65
|
+
totalInputValue += utxo.value;
|
|
69
66
|
|
|
70
67
|
// Break if we have enough funds (considering estimated fee)
|
|
71
|
-
const estimatedFee =
|
|
68
|
+
const estimatedFee = this.estimateTxSize(psbt.inputCount, 2) * feeRate;
|
|
72
69
|
if (totalInputValue >= satoshis + estimatedFee) {
|
|
73
70
|
break;
|
|
74
71
|
}
|
|
75
72
|
}
|
|
76
73
|
|
|
77
|
-
if (totalInputValue < satoshis +
|
|
74
|
+
if (totalInputValue < satoshis + feeRate) {
|
|
78
75
|
throw new Error('Insufficient balance for the transaction including fees.');
|
|
79
76
|
}
|
|
80
77
|
|
|
@@ -85,9 +82,9 @@ export default class BitcoinNetwork extends BaseNetwork {
|
|
|
85
82
|
});
|
|
86
83
|
|
|
87
84
|
// Add change output if needed
|
|
88
|
-
const estimatedFee =
|
|
85
|
+
const estimatedFee = this.estimateTxSize(psbt.inputCount, 2) * feeRate;
|
|
89
86
|
const changeValue = totalInputValue - satoshis - estimatedFee;
|
|
90
|
-
if (changeValue >
|
|
87
|
+
if (changeValue > 546) { // Dust threshold
|
|
91
88
|
psbt.addOutput({
|
|
92
89
|
address: from.address,
|
|
93
90
|
value: changeValue
|
|
@@ -167,15 +164,23 @@ export default class BitcoinNetwork extends BaseNetwork {
|
|
|
167
164
|
const path = `m/84'/0'/0'/0/0`; // BIP84 para SegWit nativo
|
|
168
165
|
const child = root.derivePath(path);
|
|
169
166
|
|
|
167
|
+
// Convert Uint8Array to Buffer if needed
|
|
168
|
+
const privateKeyBuffer = Buffer.isBuffer(child.privateKey)
|
|
169
|
+
? child.privateKey
|
|
170
|
+
: Buffer.from(child.privateKey);
|
|
171
|
+
|
|
172
|
+
// Create ECPair from the private key buffer
|
|
173
|
+
const keyPair = ECPair.fromPrivateKey(privateKeyBuffer, { network: this.network });
|
|
174
|
+
|
|
170
175
|
const { address } = bitcoin.payments.p2wpkh({
|
|
171
|
-
pubkey:
|
|
176
|
+
pubkey: keyPair.publicKey,
|
|
172
177
|
network: this.network
|
|
173
178
|
});
|
|
174
179
|
|
|
175
180
|
return {
|
|
176
181
|
address,
|
|
177
|
-
privateKey:
|
|
178
|
-
publicKey:
|
|
182
|
+
privateKey: keyPair.toWIF(),
|
|
183
|
+
publicKey: keyPair.publicKey.toString('hex'),
|
|
179
184
|
mnemonic
|
|
180
185
|
};
|
|
181
186
|
}
|
|
@@ -137,32 +137,6 @@ export default class TONNetwork extends BaseNetwork {
|
|
|
137
137
|
}));
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
async getTokenBalances(address) {
|
|
141
|
-
const balances = [];
|
|
142
|
-
|
|
143
|
-
// Add native TON balance
|
|
144
|
-
try {
|
|
145
|
-
const nativeBalance = await this.getBalance(address);
|
|
146
|
-
balances.push([this.config.nativeToken, nativeBalance]);
|
|
147
|
-
} catch (error) {
|
|
148
|
-
console.error('Failed to get TON balance:', error);
|
|
149
|
-
balances.push([this.config.nativeToken, 0]);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Add token balances (not implemented yet)
|
|
153
|
-
for (const tokenSymbol in this.config.tokens) {
|
|
154
|
-
try {
|
|
155
|
-
const tokenBalance = await this.getTokenBalance(address, tokenSymbol);
|
|
156
|
-
balances.push([tokenSymbol, tokenBalance]);
|
|
157
|
-
} catch (error) {
|
|
158
|
-
console.error(`Failed to get ${tokenSymbol} balance:`, error);
|
|
159
|
-
balances.push([tokenSymbol, 0]);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return balances;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
140
|
async sendSignedTransaction(signedTx) {
|
|
167
141
|
// For TON, the transaction is already sent and signedTx contains the result
|
|
168
142
|
let transactionHash;
|
|
@@ -98,30 +98,21 @@ export default class Web3Network extends BaseNetwork {
|
|
|
98
98
|
return this.transfer(account, recipient, amount, { gasLimit, gasPrice });
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
async
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// Get native token balance
|
|
105
|
-
const nativeBalance = await this.getBalance(address);
|
|
106
|
-
balances.push([this.config.nativeToken, parseFloat(nativeBalance)]);
|
|
107
|
-
|
|
108
|
-
// Get balances for all configured tokens
|
|
109
|
-
for (const [symbol, tokenConfig] of Object.entries(this.config.tokens)) {
|
|
110
|
-
const contract = new this.web3.eth.Contract(
|
|
111
|
-
ERC20_ABI,
|
|
112
|
-
tokenConfig.address
|
|
113
|
-
);
|
|
114
|
-
const balance = await contract.methods.balanceOf(address).call();
|
|
115
|
-
const decimals = await contract.methods.decimals().call();
|
|
116
|
-
|
|
117
|
-
const formattedBalance = Number(
|
|
118
|
-
(BigInt(balance) * 100n) / (10n ** BigInt(decimals))
|
|
119
|
-
) / 100;
|
|
120
|
-
|
|
121
|
-
balances.push([symbol, formattedBalance]);
|
|
101
|
+
async getTokenBalance(address, tokenSymbol) {
|
|
102
|
+
if (tokenSymbol === this.config.nativeToken) {
|
|
103
|
+
return await this.getBalance(address);
|
|
122
104
|
}
|
|
123
105
|
|
|
124
|
-
|
|
106
|
+
const tokenConfig = this.config.tokens[tokenSymbol];
|
|
107
|
+
if (!tokenConfig) throw new Error(`Token ${tokenSymbol} not supported`);
|
|
108
|
+
|
|
109
|
+
const contract = new this.web3.eth.Contract(ERC20_ABI, tokenConfig.address);
|
|
110
|
+
const balance = await contract.methods.balanceOf(address).call();
|
|
111
|
+
const decimals = await contract.methods.decimals().call();
|
|
112
|
+
|
|
113
|
+
return Number(
|
|
114
|
+
(BigInt(balance) * 100n) / (10n ** BigInt(decimals))
|
|
115
|
+
) / 100;
|
|
125
116
|
}
|
|
126
117
|
|
|
127
118
|
async privateKeyToAccount(privateKey) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hodl-wallet",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.4",
|
|
4
4
|
"description": "🧊 HODL Wallet - Fast CLI crypto wallet!",
|
|
5
5
|
"author": "Martin Clasen",
|
|
6
6
|
"repository": {
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"bep20",
|
|
30
30
|
"bnb",
|
|
31
31
|
"eth",
|
|
32
|
+
"ton",
|
|
32
33
|
"binance",
|
|
33
34
|
"trust",
|
|
34
35
|
"blockchain",
|