@deserialize/multi-vm-wallet 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/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/utils/IChainWallet.d.ts +16 -0
- package/dist/utils/IChainWallet.js +22 -0
- package/dist/utils/bip32.d.ts +11 -0
- package/dist/utils/bip32.js +99 -0
- package/dist/utils/evm/evm.d.ts +30 -0
- package/dist/utils/evm/evm.js +72 -0
- package/dist/utils/evm/index.d.ts +2 -0
- package/dist/utils/evm/index.js +18 -0
- package/dist/utils/evm/utils.d.ts +92 -0
- package/dist/utils/evm/utils.js +346 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.js +22 -0
- package/dist/utils/svm/index.d.ts +1 -0
- package/dist/utils/svm/index.js +17 -0
- package/dist/utils/svm/svm.d.ts +24 -0
- package/dist/utils/svm/svm.js +71 -0
- package/dist/utils/svm/transactionSender.d.ts +8 -0
- package/dist/utils/svm/transactionSender.js +83 -0
- package/dist/utils/svm/utils.d.ts +26 -0
- package/dist/utils/svm/utils.js +161 -0
- package/dist/utils/types.d.ts +44 -0
- package/dist/utils/types.js +9 -0
- package/dist/utils/vm.d.ts +13 -0
- package/dist/utils/vm.js +49 -0
- package/package.json +42 -0
- package/tsconfig.json +115 -0
- package/utils/IChainWallet.ts +36 -0
- package/utils/bip32.ts +66 -0
- package/utils/evm/evm.ts +84 -0
- package/utils/evm/index.ts +2 -0
- package/utils/evm/utils.ts +504 -0
- package/utils/index.ts +6 -0
- package/utils/svm/index.js +17 -0
- package/utils/svm/index.ts +1 -0
- package/utils/svm/svm.js +71 -0
- package/utils/svm/svm.ts +75 -0
- package/utils/svm/transactionSender.js +83 -0
- package/utils/svm/transactionSender.ts +108 -0
- package/utils/svm/utils.js +161 -0
- package/utils/svm/utils.ts +203 -0
- package/utils/types.ts +53 -0
- package/utils/vm.ts +26 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Balance } from '../types';
|
|
2
|
+
import { JsonRpcProvider, Wallet, TransactionReceipt } from 'ethers';
|
|
3
|
+
interface TransactionParams {
|
|
4
|
+
to: string;
|
|
5
|
+
value?: string | bigint;
|
|
6
|
+
data?: string;
|
|
7
|
+
gasLimit?: string | bigint;
|
|
8
|
+
gasPrice?: string | bigint;
|
|
9
|
+
maxFeePerGas?: string | bigint;
|
|
10
|
+
maxPriorityFeePerGas?: string | bigint;
|
|
11
|
+
nonce?: number;
|
|
12
|
+
}
|
|
13
|
+
interface TransactionResult {
|
|
14
|
+
hash: string;
|
|
15
|
+
receipt: TransactionReceipt | null;
|
|
16
|
+
success: boolean;
|
|
17
|
+
gasUsed?: bigint;
|
|
18
|
+
effectiveGasPrice?: bigint;
|
|
19
|
+
blockNumber?: number;
|
|
20
|
+
confirmations: number;
|
|
21
|
+
}
|
|
22
|
+
export declare const getNativeBalance: (address: string, provider: JsonRpcProvider) => Promise<Balance>;
|
|
23
|
+
export declare const getTokenBalance: (tokenAddress: string, walletAddress: string, provider: JsonRpcProvider) => Promise<Balance>;
|
|
24
|
+
/**
|
|
25
|
+
* Sign, send, and confirm any EVM transaction
|
|
26
|
+
*/
|
|
27
|
+
export declare const signSendAndConfirm: (wallet: Wallet, transactionParams: TransactionParams, confirmations?: number, timeout?: number) => Promise<TransactionResult>;
|
|
28
|
+
/**
|
|
29
|
+
* Send native token (ETH, BNB, MATIC, etc.)
|
|
30
|
+
*/
|
|
31
|
+
export declare const sendNativeToken: (wallet: Wallet, to: string, amount: string | bigint, gasLimit?: string | bigint, confirmations?: number) => Promise<TransactionResult>;
|
|
32
|
+
/**
|
|
33
|
+
* Send ERC-20 token
|
|
34
|
+
*/
|
|
35
|
+
export declare const sendERC20Token: (wallet: Wallet, tokenAddress: string, to: string, amount: string | bigint, gasLimit?: string | bigint, confirmations?: number) => Promise<TransactionResult>;
|
|
36
|
+
/**
|
|
37
|
+
* Execute any contract method
|
|
38
|
+
*/
|
|
39
|
+
export declare const executeContractMethod: (wallet: Wallet, contractAddress: string, abi: any[], methodName: string, methodParams?: any[], value?: string | bigint, gasLimit?: string | bigint, confirmations?: number) => Promise<TransactionResult>;
|
|
40
|
+
/**
|
|
41
|
+
* Get current gas prices (both legacy and EIP-1559)
|
|
42
|
+
*/
|
|
43
|
+
export declare const getGasPrices: (provider: JsonRpcProvider) => Promise<{
|
|
44
|
+
gasPrice: bigint;
|
|
45
|
+
maxFeePerGas: bigint;
|
|
46
|
+
maxPriorityFeePerGas: bigint;
|
|
47
|
+
}>;
|
|
48
|
+
/**
|
|
49
|
+
* Estimate gas for a transaction
|
|
50
|
+
*/
|
|
51
|
+
export declare const estimateGas: (provider: JsonRpcProvider, transactionParams: TransactionParams) => Promise<bigint>;
|
|
52
|
+
/**
|
|
53
|
+
* Check ERC-20 token allowance
|
|
54
|
+
*/
|
|
55
|
+
export declare const checkAllowance: (tokenAddress: string, owner: string, spender: string, provider: JsonRpcProvider) => Promise<{
|
|
56
|
+
allowance: bigint;
|
|
57
|
+
formatted: string;
|
|
58
|
+
decimals: number;
|
|
59
|
+
}>;
|
|
60
|
+
/**
|
|
61
|
+
* Check if allowance is sufficient for a transaction
|
|
62
|
+
*/
|
|
63
|
+
export declare const isAllowanceSufficient: (tokenAddress: string, owner: string, spender: string, requiredAmount: string | bigint, provider: JsonRpcProvider) => Promise<boolean>;
|
|
64
|
+
/**
|
|
65
|
+
* Approve ERC-20 token spending
|
|
66
|
+
*/
|
|
67
|
+
export declare const approveToken: (wallet: Wallet, tokenAddress: string, spender: string, amount: string | bigint, gasLimit?: string | bigint, confirmations?: number) => Promise<TransactionResult>;
|
|
68
|
+
/**
|
|
69
|
+
* Approve unlimited token spending (MaxUint256)
|
|
70
|
+
*/
|
|
71
|
+
export declare const approveTokenUnlimited: (wallet: Wallet, tokenAddress: string, spender: string, gasLimit?: string | bigint, confirmations?: number) => Promise<TransactionResult>;
|
|
72
|
+
/**
|
|
73
|
+
* Check allowance and approve if necessary
|
|
74
|
+
*/
|
|
75
|
+
export declare const checkAndApprove: (wallet: Wallet, tokenAddress: string, spender: string, requiredAmount: string | bigint, approvalAmount?: string | bigint, gasLimit?: string | bigint, confirmations?: number) => Promise<{
|
|
76
|
+
approvalNeeded: boolean;
|
|
77
|
+
currentAllowance: bigint;
|
|
78
|
+
approvalResult?: TransactionResult;
|
|
79
|
+
}>;
|
|
80
|
+
/**
|
|
81
|
+
* Reset token allowance to zero (security best practice before setting new allowance)
|
|
82
|
+
*/
|
|
83
|
+
export declare const resetAllowance: (wallet: Wallet, tokenAddress: string, spender: string, gasLimit?: string | bigint, confirmations?: number) => Promise<TransactionResult>;
|
|
84
|
+
/**
|
|
85
|
+
* Safe approve: Reset to zero first, then approve the desired amount
|
|
86
|
+
* (Some tokens like USDT require this)
|
|
87
|
+
*/
|
|
88
|
+
export declare const safeApprove: (wallet: Wallet, tokenAddress: string, spender: string, amount: string | bigint, gasLimit?: string | bigint, confirmations?: number) => Promise<{
|
|
89
|
+
resetResult: TransactionResult;
|
|
90
|
+
approveResult: TransactionResult;
|
|
91
|
+
}>;
|
|
92
|
+
export {};
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.safeApprove = exports.resetAllowance = exports.checkAndApprove = exports.approveTokenUnlimited = exports.approveToken = exports.isAllowanceSufficient = exports.checkAllowance = exports.estimateGas = exports.getGasPrices = exports.executeContractMethod = exports.sendERC20Token = exports.sendNativeToken = exports.signSendAndConfirm = exports.getTokenBalance = exports.getNativeBalance = void 0;
|
|
7
|
+
const ethers_1 = require("ethers");
|
|
8
|
+
const bn_js_1 = __importDefault(require("bn.js"));
|
|
9
|
+
// ERC-20 ABI
|
|
10
|
+
const ERC20_ABI = [
|
|
11
|
+
"function balanceOf(address owner) view returns (uint256)",
|
|
12
|
+
"function decimals() view returns (uint8)",
|
|
13
|
+
"function symbol() view returns (string)",
|
|
14
|
+
"function name() view returns (string)",
|
|
15
|
+
"function transfer(address to, uint256 amount) returns (bool)",
|
|
16
|
+
"function transferFrom(address from, address to, uint256 amount) returns (bool)",
|
|
17
|
+
"function approve(address spender, uint256 amount) returns (bool)",
|
|
18
|
+
"function allowance(address owner, address spender) view returns (uint256)"
|
|
19
|
+
];
|
|
20
|
+
const getNativeBalance = async (address, provider) => {
|
|
21
|
+
const balance = await provider.getBalance(address);
|
|
22
|
+
return {
|
|
23
|
+
balance: new bn_js_1.default(balance),
|
|
24
|
+
formatted: Number(balance / 10n ** 18n),
|
|
25
|
+
decimal: 18
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
exports.getNativeBalance = getNativeBalance;
|
|
29
|
+
const getTokenBalance = async (tokenAddress, walletAddress, provider) => {
|
|
30
|
+
try {
|
|
31
|
+
// Create contract instance
|
|
32
|
+
const tokenContract = new ethers_1.Contract(tokenAddress, ERC20_ABI, provider);
|
|
33
|
+
// Get balance (returns BigNumber)
|
|
34
|
+
const balance = await tokenContract.balanceOf(walletAddress);
|
|
35
|
+
// Get decimals to format the balance properly
|
|
36
|
+
const decimals = await tokenContract.decimals();
|
|
37
|
+
// Format balance by dividing by 10^decimals
|
|
38
|
+
const formattedBalance = balance / (10n ** BigInt(decimals));
|
|
39
|
+
return {
|
|
40
|
+
balance: new bn_js_1.default(balance.toString()), // Raw balance as BigNumber
|
|
41
|
+
formatted: Number(formattedBalance.toString()),
|
|
42
|
+
decimal: decimals
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error('Error fetching token balance:', error);
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
exports.getTokenBalance = getTokenBalance;
|
|
51
|
+
/**
|
|
52
|
+
* Sign, send, and confirm any EVM transaction
|
|
53
|
+
*/
|
|
54
|
+
const signSendAndConfirm = async (wallet, // Connected wallet (private key + provider)
|
|
55
|
+
transactionParams, confirmations = 1, // Number of confirmations to wait for
|
|
56
|
+
timeout = 300000 // 5 minutes timeout
|
|
57
|
+
) => {
|
|
58
|
+
try {
|
|
59
|
+
// Prepare transaction object
|
|
60
|
+
const tx = {
|
|
61
|
+
to: transactionParams.to,
|
|
62
|
+
value: transactionParams.value || 0,
|
|
63
|
+
data: transactionParams.data || '0x',
|
|
64
|
+
};
|
|
65
|
+
// Set gas parameters
|
|
66
|
+
if (transactionParams.gasLimit) {
|
|
67
|
+
tx.gasLimit = transactionParams.gasLimit;
|
|
68
|
+
}
|
|
69
|
+
// Handle gas pricing (EIP-1559 vs legacy)
|
|
70
|
+
if (transactionParams.maxFeePerGas && transactionParams.maxPriorityFeePerGas) {
|
|
71
|
+
// EIP-1559 transaction
|
|
72
|
+
tx.maxFeePerGas = transactionParams.maxFeePerGas;
|
|
73
|
+
tx.maxPriorityFeePerGas = transactionParams.maxPriorityFeePerGas;
|
|
74
|
+
}
|
|
75
|
+
else if (transactionParams.gasPrice) {
|
|
76
|
+
// Legacy transaction
|
|
77
|
+
tx.gasPrice = transactionParams.gasPrice;
|
|
78
|
+
}
|
|
79
|
+
// Set nonce if provided
|
|
80
|
+
if (transactionParams.nonce !== undefined) {
|
|
81
|
+
tx.nonce = transactionParams.nonce;
|
|
82
|
+
}
|
|
83
|
+
// Estimate gas if not provided
|
|
84
|
+
if (!tx.gasLimit) {
|
|
85
|
+
tx.gasLimit = await wallet.estimateGas(tx);
|
|
86
|
+
}
|
|
87
|
+
console.log('Sending transaction:', tx);
|
|
88
|
+
// Sign and send transaction
|
|
89
|
+
const txResponse = await wallet.sendTransaction(tx);
|
|
90
|
+
console.log(`Transaction sent with hash: ${txResponse.hash}`);
|
|
91
|
+
// Wait for confirmation with timeout
|
|
92
|
+
const receipt = await Promise.race([
|
|
93
|
+
txResponse.wait(confirmations),
|
|
94
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('Transaction confirmation timeout')), timeout))
|
|
95
|
+
]);
|
|
96
|
+
const result = {
|
|
97
|
+
hash: txResponse.hash,
|
|
98
|
+
receipt: receipt,
|
|
99
|
+
success: receipt?.status === 1,
|
|
100
|
+
gasUsed: receipt?.gasUsed,
|
|
101
|
+
effectiveGasPrice: receipt?.gasPrice,
|
|
102
|
+
blockNumber: receipt?.blockNumber,
|
|
103
|
+
confirmations: confirmations
|
|
104
|
+
};
|
|
105
|
+
console.log(`Transaction ${result.success ? 'successful' : 'failed'}:`, result);
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
console.error('Transaction failed:', error);
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
exports.signSendAndConfirm = signSendAndConfirm;
|
|
114
|
+
/**
|
|
115
|
+
* Send native token (ETH, BNB, MATIC, etc.)
|
|
116
|
+
*/
|
|
117
|
+
const sendNativeToken = async (wallet, to, amount, // Amount in wei
|
|
118
|
+
gasLimit, confirmations = 1) => {
|
|
119
|
+
return await (0, exports.signSendAndConfirm)(wallet, {
|
|
120
|
+
to,
|
|
121
|
+
value: (Number(amount) * 10 ** 18).toString(),
|
|
122
|
+
gasLimit
|
|
123
|
+
}, confirmations);
|
|
124
|
+
};
|
|
125
|
+
exports.sendNativeToken = sendNativeToken;
|
|
126
|
+
/**
|
|
127
|
+
* Send ERC-20 token
|
|
128
|
+
*/
|
|
129
|
+
const sendERC20Token = async (wallet, tokenAddress, to, amount, // Amount in token's smallest unit
|
|
130
|
+
gasLimit, confirmations = 1) => {
|
|
131
|
+
try {
|
|
132
|
+
// Create contract instance
|
|
133
|
+
const tokenContract = new ethers_1.Contract(tokenAddress, ERC20_ABI, wallet);
|
|
134
|
+
// Encode transfer function call
|
|
135
|
+
const data = tokenContract.interface.encodeFunctionData('transfer', [to, amount]);
|
|
136
|
+
return await (0, exports.signSendAndConfirm)(wallet, {
|
|
137
|
+
to: tokenAddress,
|
|
138
|
+
data,
|
|
139
|
+
gasLimit
|
|
140
|
+
}, confirmations);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
console.error('ERC-20 transfer failed:', error);
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
exports.sendERC20Token = sendERC20Token;
|
|
148
|
+
/**
|
|
149
|
+
* Execute any contract method
|
|
150
|
+
*/
|
|
151
|
+
const executeContractMethod = async (wallet, contractAddress, abi, methodName, methodParams = [], value, // For payable methods
|
|
152
|
+
gasLimit, confirmations = 1) => {
|
|
153
|
+
try {
|
|
154
|
+
// Create contract instance
|
|
155
|
+
const contract = new ethers_1.Contract(contractAddress, abi, wallet);
|
|
156
|
+
// Encode method call
|
|
157
|
+
const data = contract.interface.encodeFunctionData(methodName, methodParams);
|
|
158
|
+
return await (0, exports.signSendAndConfirm)(wallet, {
|
|
159
|
+
to: contractAddress,
|
|
160
|
+
data,
|
|
161
|
+
value,
|
|
162
|
+
gasLimit
|
|
163
|
+
}, confirmations);
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
console.error('Contract method execution failed:', error);
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
exports.executeContractMethod = executeContractMethod;
|
|
171
|
+
/**
|
|
172
|
+
* Get current gas prices (both legacy and EIP-1559)
|
|
173
|
+
*/
|
|
174
|
+
const getGasPrices = async (provider) => {
|
|
175
|
+
try {
|
|
176
|
+
const feeData = await provider.getFeeData();
|
|
177
|
+
return {
|
|
178
|
+
// Legacy
|
|
179
|
+
gasPrice: feeData.gasPrice,
|
|
180
|
+
// EIP-1559
|
|
181
|
+
maxFeePerGas: feeData.maxFeePerGas,
|
|
182
|
+
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
console.error('Error fetching gas prices:', error);
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
exports.getGasPrices = getGasPrices;
|
|
191
|
+
/**
|
|
192
|
+
* Estimate gas for a transaction
|
|
193
|
+
*/
|
|
194
|
+
const estimateGas = async (provider, transactionParams) => {
|
|
195
|
+
try {
|
|
196
|
+
const tx = {
|
|
197
|
+
to: transactionParams.to,
|
|
198
|
+
value: transactionParams.value || 0,
|
|
199
|
+
data: transactionParams.data || '0x'
|
|
200
|
+
};
|
|
201
|
+
return await provider.estimateGas(tx);
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
console.error('Gas estimation failed:', error);
|
|
205
|
+
throw error;
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
exports.estimateGas = estimateGas;
|
|
209
|
+
/**
|
|
210
|
+
* Check ERC-20 token allowance
|
|
211
|
+
*/
|
|
212
|
+
const checkAllowance = async (tokenAddress, owner, spender, provider) => {
|
|
213
|
+
try {
|
|
214
|
+
const tokenContract = new ethers_1.Contract(tokenAddress, ERC20_ABI, provider);
|
|
215
|
+
// Get allowance and decimals
|
|
216
|
+
const [allowance, decimals] = await Promise.all([
|
|
217
|
+
tokenContract.allowance(owner, spender),
|
|
218
|
+
tokenContract.decimals()
|
|
219
|
+
]);
|
|
220
|
+
// Format allowance for display
|
|
221
|
+
const formattedAllowance = allowance / (10n ** BigInt(decimals));
|
|
222
|
+
return {
|
|
223
|
+
allowance,
|
|
224
|
+
formatted: formattedAllowance.toString(),
|
|
225
|
+
decimals
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
console.error('Error checking allowance:', error);
|
|
230
|
+
throw error;
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
exports.checkAllowance = checkAllowance;
|
|
234
|
+
/**
|
|
235
|
+
* Check if allowance is sufficient for a transaction
|
|
236
|
+
*/
|
|
237
|
+
const isAllowanceSufficient = async (tokenAddress, owner, spender, requiredAmount, provider) => {
|
|
238
|
+
try {
|
|
239
|
+
const { allowance } = await (0, exports.checkAllowance)(tokenAddress, owner, spender, provider);
|
|
240
|
+
const required = typeof requiredAmount === 'string' ? BigInt(requiredAmount) : requiredAmount;
|
|
241
|
+
return allowance >= required;
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
console.error('Error checking allowance sufficiency:', error);
|
|
245
|
+
throw error;
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
exports.isAllowanceSufficient = isAllowanceSufficient;
|
|
249
|
+
/**
|
|
250
|
+
* Approve ERC-20 token spending
|
|
251
|
+
*/
|
|
252
|
+
const approveToken = async (wallet, tokenAddress, spender, amount, // Amount to approve, use MaxUint256 for unlimited
|
|
253
|
+
gasLimit, confirmations = 1) => {
|
|
254
|
+
try {
|
|
255
|
+
const tokenContract = new ethers_1.Contract(tokenAddress, ERC20_ABI, wallet);
|
|
256
|
+
// Encode approve function call
|
|
257
|
+
const data = tokenContract.interface.encodeFunctionData('approve', [spender, amount]);
|
|
258
|
+
return await (0, exports.signSendAndConfirm)(wallet, {
|
|
259
|
+
to: tokenAddress,
|
|
260
|
+
data,
|
|
261
|
+
gasLimit
|
|
262
|
+
}, confirmations);
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
console.error('Token approval failed:', error);
|
|
266
|
+
throw error;
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
exports.approveToken = approveToken;
|
|
270
|
+
/**
|
|
271
|
+
* Approve unlimited token spending (MaxUint256)
|
|
272
|
+
*/
|
|
273
|
+
const approveTokenUnlimited = async (wallet, tokenAddress, spender, gasLimit, confirmations = 1) => {
|
|
274
|
+
// MaxUint256 = 2^256 - 1
|
|
275
|
+
const MAX_UINT256 = "115792089237316195423570985008687907853269984665640564039457584007913129639935";
|
|
276
|
+
return await (0, exports.approveToken)(wallet, tokenAddress, spender, MAX_UINT256, gasLimit, confirmations);
|
|
277
|
+
};
|
|
278
|
+
exports.approveTokenUnlimited = approveTokenUnlimited;
|
|
279
|
+
/**
|
|
280
|
+
* Check allowance and approve if necessary
|
|
281
|
+
*/
|
|
282
|
+
const checkAndApprove = async (wallet, tokenAddress, spender, requiredAmount, approvalAmount, // If not provided, will approve exactly the required amount
|
|
283
|
+
gasLimit, confirmations = 1) => {
|
|
284
|
+
try {
|
|
285
|
+
const owner = await wallet.getAddress();
|
|
286
|
+
const provider = wallet.provider;
|
|
287
|
+
// Check current allowance
|
|
288
|
+
const { allowance } = await (0, exports.checkAllowance)(tokenAddress, owner, spender, provider);
|
|
289
|
+
const required = typeof requiredAmount === 'string' ? BigInt(requiredAmount) : requiredAmount;
|
|
290
|
+
const approvalNeeded = allowance < required;
|
|
291
|
+
if (!approvalNeeded) {
|
|
292
|
+
return {
|
|
293
|
+
approvalNeeded: false,
|
|
294
|
+
currentAllowance: allowance
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
console.log(`Approval needed. Current: ${allowance}, Required: ${required}`);
|
|
298
|
+
// Determine approval amount
|
|
299
|
+
const amountToApprove = approvalAmount || required;
|
|
300
|
+
// Execute approval
|
|
301
|
+
const approvalResult = await (0, exports.approveToken)(wallet, tokenAddress, spender, amountToApprove, gasLimit, confirmations);
|
|
302
|
+
return {
|
|
303
|
+
approvalNeeded: true,
|
|
304
|
+
currentAllowance: allowance,
|
|
305
|
+
approvalResult
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
console.error('Check and approve failed:', error);
|
|
310
|
+
throw error;
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
exports.checkAndApprove = checkAndApprove;
|
|
314
|
+
/**
|
|
315
|
+
* Reset token allowance to zero (security best practice before setting new allowance)
|
|
316
|
+
*/
|
|
317
|
+
const resetAllowance = async (wallet, tokenAddress, spender, gasLimit, confirmations = 1) => {
|
|
318
|
+
return await (0, exports.approveToken)(wallet, tokenAddress, spender, "0", gasLimit, confirmations);
|
|
319
|
+
};
|
|
320
|
+
exports.resetAllowance = resetAllowance;
|
|
321
|
+
/**
|
|
322
|
+
* Safe approve: Reset to zero first, then approve the desired amount
|
|
323
|
+
* (Some tokens like USDT require this)
|
|
324
|
+
*/
|
|
325
|
+
const safeApprove = async (wallet, tokenAddress, spender, amount, gasLimit, confirmations = 1) => {
|
|
326
|
+
try {
|
|
327
|
+
console.log('Performing safe approve: reset then approve');
|
|
328
|
+
// First reset to zero
|
|
329
|
+
const resetResult = await (0, exports.resetAllowance)(wallet, tokenAddress, spender, gasLimit, confirmations);
|
|
330
|
+
if (!resetResult.success) {
|
|
331
|
+
throw new Error('Failed to reset allowance to zero');
|
|
332
|
+
}
|
|
333
|
+
// Then approve the desired amount
|
|
334
|
+
const approveResult = await (0, exports.approveToken)(wallet, tokenAddress, spender, amount, gasLimit, confirmations);
|
|
335
|
+
return {
|
|
336
|
+
resetResult,
|
|
337
|
+
approveResult
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
catch (error) {
|
|
341
|
+
console.error('Safe approve failed:', error);
|
|
342
|
+
throw error;
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
exports.safeApprove = safeApprove;
|
|
346
|
+
//swaps
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./bip32"), exports);
|
|
18
|
+
__exportStar(require("./evm/evm"), exports);
|
|
19
|
+
__exportStar(require("./types"), exports);
|
|
20
|
+
__exportStar(require("./vm"), exports);
|
|
21
|
+
__exportStar(require("./evm"), exports);
|
|
22
|
+
__exportStar(require("./svm"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./svm";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./svm"), exports);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Connection, Keypair, PublicKey } from "@solana/web3.js";
|
|
2
|
+
import { VM } from "../vm";
|
|
3
|
+
import { ChainWallet } from "../IChainWallet";
|
|
4
|
+
import { Balance, ChainWalletConfig, TokenInfo, TransactionResult } from "../types";
|
|
5
|
+
export declare class SVMVM extends VM<PublicKey, Keypair, Connection> {
|
|
6
|
+
derivationPath: string;
|
|
7
|
+
constructor(mnemonic: string);
|
|
8
|
+
static validateAddress(address: PublicKey): boolean;
|
|
9
|
+
static getNativeBalance(address: PublicKey, connection: Connection): Promise<Balance>;
|
|
10
|
+
static getTokenBalance(address: PublicKey, tokenAddress: PublicKey, connection: Connection): Promise<Balance>;
|
|
11
|
+
static signAndSendTransaction: (transaction: import("@solana/web3.js").VersionedTransaction, connection: Connection, signers: Keypair[]) => Promise<string>;
|
|
12
|
+
generatePrivateKey(index: number, seedPhrase?: string, derivationPath?: string): {
|
|
13
|
+
privateKey: Keypair;
|
|
14
|
+
index: number;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export declare class SVMChainWallet extends ChainWallet<PublicKey, Keypair, Connection> {
|
|
18
|
+
constructor(config: ChainWalletConfig, privateKey: Keypair, index: number);
|
|
19
|
+
generateAddress(): PublicKey;
|
|
20
|
+
getNativeBalance(): Promise<Balance>;
|
|
21
|
+
getTokenBalance(tokenAddress: PublicKey): Promise<Balance>;
|
|
22
|
+
transferNative(to: PublicKey, amount: number): Promise<TransactionResult>;
|
|
23
|
+
transferToken(token: TokenInfo, to: PublicKey, amount: number): Promise<TransactionResult>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SVMChainWallet = exports.SVMVM = void 0;
|
|
7
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
8
|
+
const bip32_1 = require("../bip32");
|
|
9
|
+
const vm_1 = require("../vm");
|
|
10
|
+
const IChainWallet_1 = require("../IChainWallet");
|
|
11
|
+
const utils_1 = require("./utils");
|
|
12
|
+
const bn_js_1 = __importDefault(require("bn.js"));
|
|
13
|
+
class SVMVM extends vm_1.VM {
|
|
14
|
+
constructor(mnemonic) {
|
|
15
|
+
super(mnemonic, "SVM");
|
|
16
|
+
this.derivationPath = "m/44'/501'/"; // Default EVM derivation path
|
|
17
|
+
}
|
|
18
|
+
static validateAddress(address) {
|
|
19
|
+
return web3_js_1.PublicKey.isOnCurve(address.toBuffer());
|
|
20
|
+
}
|
|
21
|
+
static getNativeBalance(address, connection) {
|
|
22
|
+
return (0, utils_1.getSvmNativeBalance)(address, connection);
|
|
23
|
+
}
|
|
24
|
+
static async getTokenBalance(address, tokenAddress, connection) {
|
|
25
|
+
const balance = await (0, utils_1.getTokenBalance)(address, tokenAddress, connection);
|
|
26
|
+
if (balance === 0) {
|
|
27
|
+
return { balance: new bn_js_1.default(0), formatted: 0, decimal: 0 };
|
|
28
|
+
}
|
|
29
|
+
return { balance: new bn_js_1.default(balance.amount), formatted: balance.uiAmount || parseInt(balance.amount) / 10 ** balance.decimals, decimal: balance.decimals };
|
|
30
|
+
}
|
|
31
|
+
generatePrivateKey(index, seedPhrase = this.mnemonic, derivationPath = this.derivationPath) {
|
|
32
|
+
const seed = this.mnemonicToSeed(seedPhrase);
|
|
33
|
+
const privateKey = (0, bip32_1.SVMDeriveChildPrivateKey)(seed, index, derivationPath);
|
|
34
|
+
return { privateKey, index };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.SVMVM = SVMVM;
|
|
38
|
+
SVMVM.signAndSendTransaction = utils_1.signAndSendTransaction;
|
|
39
|
+
class SVMChainWallet extends IChainWallet_1.ChainWallet {
|
|
40
|
+
constructor(config, privateKey, index) {
|
|
41
|
+
super(config, privateKey, index);
|
|
42
|
+
this.address = privateKey.publicKey;
|
|
43
|
+
this.privateKey = privateKey;
|
|
44
|
+
this.connection = new web3_js_1.Connection(config.rpcUrl);
|
|
45
|
+
}
|
|
46
|
+
generateAddress() {
|
|
47
|
+
return this.address;
|
|
48
|
+
}
|
|
49
|
+
async getNativeBalance() {
|
|
50
|
+
// Implement native balance retrieval logic here
|
|
51
|
+
return await SVMVM.getNativeBalance(this.address, this.connection);
|
|
52
|
+
}
|
|
53
|
+
async getTokenBalance(tokenAddress) {
|
|
54
|
+
// Implement token balance retrieval logic here
|
|
55
|
+
return await SVMVM.getTokenBalance(this.address, (tokenAddress), this.connection);
|
|
56
|
+
}
|
|
57
|
+
async transferNative(to, amount) {
|
|
58
|
+
// Implement native transfer logic here
|
|
59
|
+
const transaction = await (0, utils_1.getTransferNativeTransaction)(this.privateKey, to, amount, this.connection);
|
|
60
|
+
const hash = await SVMVM.signAndSendTransaction(transaction, this.connection, [this.privateKey]);
|
|
61
|
+
return { success: true, hash }; // Placeholder
|
|
62
|
+
}
|
|
63
|
+
async transferToken(token, to, amount) {
|
|
64
|
+
// Implement token transfer logic here
|
|
65
|
+
const transaction = await (0, utils_1.getTransferTokenTransaction)(this.privateKey, new web3_js_1.PublicKey(to), token, (amount), this.connection);
|
|
66
|
+
const hash = await SVMVM.signAndSendTransaction(transaction, this.connection, [this.privateKey]);
|
|
67
|
+
return { success: true, hash }; // Placeholder
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
exports.SVMChainWallet = SVMChainWallet;
|
|
71
|
+
//swaps jupiter swap here
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { BlockhashWithExpiryBlockHeight, Connection, VersionedTransactionResponse } from "@solana/web3.js";
|
|
2
|
+
type TransactionSenderAndConfirmationWaiterArgs = {
|
|
3
|
+
connection: Connection;
|
|
4
|
+
serializedTransaction: Buffer;
|
|
5
|
+
blockhashWithExpiryBlockHeight: BlockhashWithExpiryBlockHeight;
|
|
6
|
+
};
|
|
7
|
+
export declare function transactionSenderAndConfirmationWaiter({ connection, serializedTransaction, blockhashWithExpiryBlockHeight, }: TransactionSenderAndConfirmationWaiterArgs): Promise<VersionedTransactionResponse | null>;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.transactionSenderAndConfirmationWaiter = transactionSenderAndConfirmationWaiter;
|
|
7
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
8
|
+
const promise_retry_1 = __importDefault(require("promise-retry"));
|
|
9
|
+
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
10
|
+
const SEND_OPTIONS = {
|
|
11
|
+
skipPreflight: false,
|
|
12
|
+
};
|
|
13
|
+
async function transactionSenderAndConfirmationWaiter({ connection, serializedTransaction, blockhashWithExpiryBlockHeight, }) {
|
|
14
|
+
const txid = await connection.sendRawTransaction(serializedTransaction, SEND_OPTIONS);
|
|
15
|
+
const controller = new AbortController();
|
|
16
|
+
const abortSignal = controller.signal;
|
|
17
|
+
const abortableResender = async () => {
|
|
18
|
+
while (true) {
|
|
19
|
+
await wait(2000);
|
|
20
|
+
if (abortSignal.aborted)
|
|
21
|
+
return;
|
|
22
|
+
try {
|
|
23
|
+
await connection.sendRawTransaction(serializedTransaction, SEND_OPTIONS);
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
console.warn(`Failed to resend transaction: ${e}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
try {
|
|
31
|
+
abortableResender();
|
|
32
|
+
const lastValidBlockHeight = blockhashWithExpiryBlockHeight.lastValidBlockHeight - 150;
|
|
33
|
+
// this would throw TransactionExpiredBlockheightExceededError
|
|
34
|
+
await Promise.race([
|
|
35
|
+
connection.confirmTransaction({
|
|
36
|
+
...blockhashWithExpiryBlockHeight,
|
|
37
|
+
lastValidBlockHeight,
|
|
38
|
+
signature: txid,
|
|
39
|
+
abortSignal,
|
|
40
|
+
}, "confirmed"),
|
|
41
|
+
new Promise(async (resolve) => {
|
|
42
|
+
// in case ws socket died
|
|
43
|
+
while (!abortSignal.aborted) {
|
|
44
|
+
await wait(2000);
|
|
45
|
+
const tx = await connection.getSignatureStatus(txid, {
|
|
46
|
+
searchTransactionHistory: false,
|
|
47
|
+
});
|
|
48
|
+
if (tx?.value?.confirmationStatus === "confirmed") {
|
|
49
|
+
resolve(tx);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}),
|
|
53
|
+
]);
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
if (e instanceof web3_js_1.TransactionExpiredBlockheightExceededError) {
|
|
57
|
+
// we consume this error and getTransaction would return null
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
// invalid state from web3.js
|
|
62
|
+
throw e;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
controller.abort();
|
|
67
|
+
}
|
|
68
|
+
// in case rpc is not synced yet, we add some retries
|
|
69
|
+
const response = (0, promise_retry_1.default)(async (retry) => {
|
|
70
|
+
const response = await connection.getTransaction(txid, {
|
|
71
|
+
commitment: "confirmed",
|
|
72
|
+
maxSupportedTransactionVersion: 0,
|
|
73
|
+
});
|
|
74
|
+
if (!response) {
|
|
75
|
+
retry(response);
|
|
76
|
+
}
|
|
77
|
+
return response;
|
|
78
|
+
}, {
|
|
79
|
+
retries: 5,
|
|
80
|
+
minTimeout: 1e3,
|
|
81
|
+
});
|
|
82
|
+
return response;
|
|
83
|
+
}
|