pwc-sdk-wallet 0.6.3
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/README.md +2062 -0
- package/dist/Vault.d.ts +493 -0
- package/dist/Vault.js +1090 -0
- package/dist/chain/ChainService.d.ts +84 -0
- package/dist/chain/ChainService.js +136 -0
- package/dist/chain/SolanaChainService.d.ts +94 -0
- package/dist/chain/SolanaChainService.js +167 -0
- package/dist/config/chains.d.ts +233 -0
- package/dist/config/chains.js +285 -0
- package/dist/config/constants.d.ts +102 -0
- package/dist/config/constants.js +109 -0
- package/dist/config/environment.d.ts +46 -0
- package/dist/config/environment.js +114 -0
- package/dist/config/gas.d.ts +107 -0
- package/dist/config/gas.js +123 -0
- package/dist/crypto/EncryptionService.d.ts +74 -0
- package/dist/crypto/EncryptionService.js +178 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +96 -0
- package/dist/keyrings/HDKeyring.d.ts +72 -0
- package/dist/keyrings/HDKeyring.js +156 -0
- package/dist/keyrings/SimpleKeyring.d.ts +31 -0
- package/dist/keyrings/SimpleKeyring.js +49 -0
- package/dist/keyrings/SolanaKeyring.d.ts +71 -0
- package/dist/keyrings/SolanaKeyring.js +159 -0
- package/dist/services/BatchProcessor.d.ts +42 -0
- package/dist/services/BatchProcessor.js +188 -0
- package/dist/services/MultiTransferService.d.ts +78 -0
- package/dist/services/MultiTransferService.js +252 -0
- package/dist/services/QRCodeService.d.ts +193 -0
- package/dist/services/QRCodeService.js +299 -0
- package/dist/services/TokenUtils.d.ts +307 -0
- package/dist/services/TokenUtils.js +429 -0
- package/dist/services/nft/MetadataResolver.d.ts +57 -0
- package/dist/services/nft/MetadataResolver.js +162 -0
- package/dist/services/nft/NFTAPIService.d.ts +53 -0
- package/dist/services/nft/NFTAPIService.js +122 -0
- package/dist/services/nft/NFTService.d.ts +241 -0
- package/dist/services/nft/NFTService.js +910 -0
- package/dist/types/multiTransfer.d.ts +68 -0
- package/dist/types/multiTransfer.js +2 -0
- package/dist/types/nft/index.d.ts +68 -0
- package/dist/types/nft/index.js +2 -0
- package/dist/types/nft.d.ts +265 -0
- package/dist/types/nft.js +6 -0
- package/package.json +70 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Keypair } from '@solana/web3.js';
|
|
2
|
+
/**
|
|
3
|
+
* Solana keyring for managing multiple Solana accounts
|
|
4
|
+
* derived from a single mnemonic phrase using BIP-44 derivation paths.
|
|
5
|
+
* Supports Solana blockchain with Ed25519 keypairs.
|
|
6
|
+
*/
|
|
7
|
+
export declare class SolanaKeyring {
|
|
8
|
+
readonly type = "Solana";
|
|
9
|
+
private keypairs;
|
|
10
|
+
accounts: string[];
|
|
11
|
+
private mnemonic;
|
|
12
|
+
/**
|
|
13
|
+
* Creates a new SolanaKeyring instance from a mnemonic phrase.
|
|
14
|
+
* @param mnemonic - The mnemonic phrase (12, 15, 18, 21, or 24 words)
|
|
15
|
+
* @throws Error if the mnemonic is invalid
|
|
16
|
+
*/
|
|
17
|
+
constructor(mnemonic: string);
|
|
18
|
+
/**
|
|
19
|
+
* Initializes the keyring by generating the seed and creating the first account.
|
|
20
|
+
* @param mnemonic - The mnemonic phrase to initialize from
|
|
21
|
+
*/
|
|
22
|
+
private initializeFromMnemonic;
|
|
23
|
+
/**
|
|
24
|
+
* Derives a Solana keypair at a specific index using BIP-44 derivation.
|
|
25
|
+
* @param seed - The seed buffer to derive from
|
|
26
|
+
* @param index - The account index to derive (0-based)
|
|
27
|
+
* @returns The derived Solana keypair
|
|
28
|
+
*/
|
|
29
|
+
private deriveKeypair;
|
|
30
|
+
/**
|
|
31
|
+
* Adds a new Solana account derived from the mnemonic at the next available index.
|
|
32
|
+
* @returns Promise resolving to the public key address of the newly created account
|
|
33
|
+
* @throws Error if account derivation fails or duplicate address is generated
|
|
34
|
+
*/
|
|
35
|
+
addNewAccount(): Promise<string>;
|
|
36
|
+
/**
|
|
37
|
+
* Gets the private key for a given address managed by this keyring.
|
|
38
|
+
* @param address - The account's public key address to get the private key for
|
|
39
|
+
* @returns Promise resolving to the private key as a hex string
|
|
40
|
+
* @throws Error if the address is not found in this keyring
|
|
41
|
+
*/
|
|
42
|
+
getPrivateKeyForAddress(address: string): Promise<string>;
|
|
43
|
+
/**
|
|
44
|
+
* Returns the mnemonic phrase. USE WITH CAUTION - this exposes sensitive data.
|
|
45
|
+
* @returns The mnemonic phrase as a string
|
|
46
|
+
*/
|
|
47
|
+
getMnemonic(): string;
|
|
48
|
+
/**
|
|
49
|
+
* Serializes the keyring data for encryption and storage.
|
|
50
|
+
* @returns An object containing the keyring type and mnemonic
|
|
51
|
+
*/
|
|
52
|
+
serialize(): any;
|
|
53
|
+
/**
|
|
54
|
+
* Deserializes data into a SolanaKeyring instance.
|
|
55
|
+
* @param data - The serialized keyring data
|
|
56
|
+
* @returns Promise resolving to a new SolanaKeyring instance
|
|
57
|
+
* @throws Error if the data is invalid or missing required fields
|
|
58
|
+
*/
|
|
59
|
+
static deserialize(data: any): Promise<SolanaKeyring>;
|
|
60
|
+
/**
|
|
61
|
+
* Gets the Solana keypair for a given address.
|
|
62
|
+
* @param address - The account's public key address
|
|
63
|
+
* @returns The Solana keypair or undefined if not found
|
|
64
|
+
*/
|
|
65
|
+
getKeypair(address: string): Keypair | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* Gets all keypairs managed by this keyring.
|
|
68
|
+
* @returns Array of all Solana keypairs
|
|
69
|
+
*/
|
|
70
|
+
getAllKeypairs(): Keypair[];
|
|
71
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.SolanaKeyring = void 0;
|
|
37
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
38
|
+
const ed25519_hd_key_1 = require("ed25519-hd-key");
|
|
39
|
+
const bip39 = __importStar(require("bip39"));
|
|
40
|
+
const buffer_1 = require("buffer");
|
|
41
|
+
const chains_1 = require("../config/chains");
|
|
42
|
+
/**
|
|
43
|
+
* Solana keyring for managing multiple Solana accounts
|
|
44
|
+
* derived from a single mnemonic phrase using BIP-44 derivation paths.
|
|
45
|
+
* Supports Solana blockchain with Ed25519 keypairs.
|
|
46
|
+
*/
|
|
47
|
+
class SolanaKeyring {
|
|
48
|
+
/**
|
|
49
|
+
* Creates a new SolanaKeyring instance from a mnemonic phrase.
|
|
50
|
+
* @param mnemonic - The mnemonic phrase (12, 15, 18, 21, or 24 words)
|
|
51
|
+
* @throws Error if the mnemonic is invalid
|
|
52
|
+
*/
|
|
53
|
+
constructor(mnemonic) {
|
|
54
|
+
this.type = 'Solana';
|
|
55
|
+
this.keypairs = new Map();
|
|
56
|
+
this.accounts = [];
|
|
57
|
+
if (!bip39.validateMnemonic(mnemonic)) {
|
|
58
|
+
throw new Error('Invalid mnemonic');
|
|
59
|
+
}
|
|
60
|
+
this.mnemonic = mnemonic;
|
|
61
|
+
this.initializeFromMnemonic(mnemonic);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Initializes the keyring by generating the seed and creating the first account.
|
|
65
|
+
* @param mnemonic - The mnemonic phrase to initialize from
|
|
66
|
+
*/
|
|
67
|
+
initializeFromMnemonic(mnemonic) {
|
|
68
|
+
const seed = bip39.mnemonicToSeedSync(mnemonic);
|
|
69
|
+
const firstKeypair = this.deriveKeypair(seed, 0);
|
|
70
|
+
this.keypairs.set(firstKeypair.publicKey.toString(), firstKeypair);
|
|
71
|
+
this.accounts.push(firstKeypair.publicKey.toString());
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Derives a Solana keypair at a specific index using BIP-44 derivation.
|
|
75
|
+
* @param seed - The seed buffer to derive from
|
|
76
|
+
* @param index - The account index to derive (0-based)
|
|
77
|
+
* @returns The derived Solana keypair
|
|
78
|
+
*/
|
|
79
|
+
deriveKeypair(seed, index) {
|
|
80
|
+
const path = `${chains_1.DERIVATION_PATHS.SOLANA.slice(0, -1)}${index}'`;
|
|
81
|
+
const derivedSeed = (0, ed25519_hd_key_1.derivePath)(path, seed.toString('hex')).key;
|
|
82
|
+
return web3_js_1.Keypair.fromSeed(derivedSeed);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Adds a new Solana account derived from the mnemonic at the next available index.
|
|
86
|
+
* @returns Promise resolving to the public key address of the newly created account
|
|
87
|
+
* @throws Error if account derivation fails or duplicate address is generated
|
|
88
|
+
*/
|
|
89
|
+
async addNewAccount() {
|
|
90
|
+
const newIndex = this.accounts.length;
|
|
91
|
+
const seed = bip39.mnemonicToSeedSync(this.mnemonic);
|
|
92
|
+
const newKeypair = this.deriveKeypair(seed, newIndex);
|
|
93
|
+
if (this.accounts.includes(newKeypair.publicKey.toString())) {
|
|
94
|
+
throw new Error('Duplicate account derived. Please check derivation path logic.');
|
|
95
|
+
}
|
|
96
|
+
this.keypairs.set(newKeypair.publicKey.toString(), newKeypair);
|
|
97
|
+
this.accounts.push(newKeypair.publicKey.toString());
|
|
98
|
+
return newKeypair.publicKey.toString();
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Gets the private key for a given address managed by this keyring.
|
|
102
|
+
* @param address - The account's public key address to get the private key for
|
|
103
|
+
* @returns Promise resolving to the private key as a hex string
|
|
104
|
+
* @throws Error if the address is not found in this keyring
|
|
105
|
+
*/
|
|
106
|
+
async getPrivateKeyForAddress(address) {
|
|
107
|
+
const keypair = this.keypairs.get(address);
|
|
108
|
+
if (!keypair) {
|
|
109
|
+
throw new Error('Address not found in this keyring.');
|
|
110
|
+
}
|
|
111
|
+
return buffer_1.Buffer.from(keypair.secretKey).toString('hex');
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Returns the mnemonic phrase. USE WITH CAUTION - this exposes sensitive data.
|
|
115
|
+
* @returns The mnemonic phrase as a string
|
|
116
|
+
*/
|
|
117
|
+
getMnemonic() {
|
|
118
|
+
return this.mnemonic;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Serializes the keyring data for encryption and storage.
|
|
122
|
+
* @returns An object containing the keyring type and mnemonic
|
|
123
|
+
*/
|
|
124
|
+
serialize() {
|
|
125
|
+
return {
|
|
126
|
+
type: this.type,
|
|
127
|
+
mnemonic: this.mnemonic
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Deserializes data into a SolanaKeyring instance.
|
|
132
|
+
* @param data - The serialized keyring data
|
|
133
|
+
* @returns Promise resolving to a new SolanaKeyring instance
|
|
134
|
+
* @throws Error if the data is invalid or missing required fields
|
|
135
|
+
*/
|
|
136
|
+
static async deserialize(data) {
|
|
137
|
+
if (data.type !== 'Solana' || !data.mnemonic) {
|
|
138
|
+
throw new Error('Invalid data for SolanaKeyring deserialization.');
|
|
139
|
+
}
|
|
140
|
+
// Chỉ khôi phục mnemonic, không khôi phục lại accounts
|
|
141
|
+
return new SolanaKeyring(data.mnemonic);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Gets the Solana keypair for a given address.
|
|
145
|
+
* @param address - The account's public key address
|
|
146
|
+
* @returns The Solana keypair or undefined if not found
|
|
147
|
+
*/
|
|
148
|
+
getKeypair(address) {
|
|
149
|
+
return this.keypairs.get(address);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Gets all keypairs managed by this keyring.
|
|
153
|
+
* @returns Array of all Solana keypairs
|
|
154
|
+
*/
|
|
155
|
+
getAllKeypairs() {
|
|
156
|
+
return Array.from(this.keypairs.values());
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
exports.SolanaKeyring = SolanaKeyring;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ChainService } from '../chain/ChainService';
|
|
2
|
+
import { SolanaChainService } from '../chain/SolanaChainService';
|
|
3
|
+
import { Recipient, BatchResult } from '../types/multiTransfer';
|
|
4
|
+
/**
|
|
5
|
+
* Handles batch processing of transfers for both native tokens and ERC-20/SPL tokens.
|
|
6
|
+
* Processes transfers in batches to optimize gas usage and provide progress tracking.
|
|
7
|
+
*/
|
|
8
|
+
export declare class BatchProcessor {
|
|
9
|
+
/**
|
|
10
|
+
* Processes a batch of native token transfers.
|
|
11
|
+
* @param chainService - The chain service instance (EVM or Solana)
|
|
12
|
+
* @param recipients - Array of recipients to transfer to
|
|
13
|
+
* @param onProgress - Optional progress callback
|
|
14
|
+
* @returns Promise resolving to batch processing results
|
|
15
|
+
*/
|
|
16
|
+
static processNativeTokenBatch(chainService: ChainService | SolanaChainService, recipients: Recipient[], onProgress?: (completed: number, total: number, txHash: string) => void): Promise<BatchResult>;
|
|
17
|
+
/**
|
|
18
|
+
* Processes a batch of ERC-20/SPL token transfers.
|
|
19
|
+
* @param chainService - The chain service instance (EVM or Solana)
|
|
20
|
+
* @param tokenAddress - The token contract address
|
|
21
|
+
* @param recipients - Array of recipients to transfer to
|
|
22
|
+
* @param onProgress - Optional progress callback
|
|
23
|
+
* @returns Promise resolving to batch processing results
|
|
24
|
+
*/
|
|
25
|
+
static processTokenBatch(chainService: ChainService | SolanaChainService, tokenAddress: string, recipients: Recipient[], onProgress?: (completed: number, total: number, txHash: string) => void): Promise<BatchResult>;
|
|
26
|
+
/**
|
|
27
|
+
* Splits recipients into batches of specified size.
|
|
28
|
+
* @param recipients - Array of all recipients
|
|
29
|
+
* @param batchSize - Size of each batch
|
|
30
|
+
* @returns Array of recipient batches
|
|
31
|
+
*/
|
|
32
|
+
static splitIntoBatches(recipients: Recipient[], batchSize: number): Recipient[][];
|
|
33
|
+
/**
|
|
34
|
+
* Validates recipient addresses and amounts.
|
|
35
|
+
* @param recipients - Array of recipients to validate
|
|
36
|
+
* @returns Validation result
|
|
37
|
+
*/
|
|
38
|
+
static validateRecipients(recipients: Recipient[]): {
|
|
39
|
+
isValid: boolean;
|
|
40
|
+
errors: string[];
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BatchProcessor = void 0;
|
|
4
|
+
const ChainService_1 = require("../chain/ChainService");
|
|
5
|
+
const SolanaChainService_1 = require("../chain/SolanaChainService");
|
|
6
|
+
const ethers_1 = require("ethers");
|
|
7
|
+
/**
|
|
8
|
+
* Handles batch processing of transfers for both native tokens and ERC-20/SPL tokens.
|
|
9
|
+
* Processes transfers in batches to optimize gas usage and provide progress tracking.
|
|
10
|
+
*/
|
|
11
|
+
class BatchProcessor {
|
|
12
|
+
/**
|
|
13
|
+
* Processes a batch of native token transfers.
|
|
14
|
+
* @param chainService - The chain service instance (EVM or Solana)
|
|
15
|
+
* @param recipients - Array of recipients to transfer to
|
|
16
|
+
* @param onProgress - Optional progress callback
|
|
17
|
+
* @returns Promise resolving to batch processing results
|
|
18
|
+
*/
|
|
19
|
+
static async processNativeTokenBatch(chainService, recipients, onProgress) {
|
|
20
|
+
const transactions = [];
|
|
21
|
+
const failed = [];
|
|
22
|
+
let totalGasUsed = BigInt(0);
|
|
23
|
+
for (let i = 0; i < recipients.length; i++) {
|
|
24
|
+
const recipient = recipients[i];
|
|
25
|
+
try {
|
|
26
|
+
let txResult;
|
|
27
|
+
if (chainService instanceof SolanaChainService_1.SolanaChainService) {
|
|
28
|
+
// Solana native token transfer
|
|
29
|
+
txResult = await chainService.sendTransaction(recipient.address, recipient.amount);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
// EVM native token transfer
|
|
33
|
+
txResult = await chainService.sendTransaction({
|
|
34
|
+
to: recipient.address,
|
|
35
|
+
value: ethers_1.ethers.parseEther(recipient.amount)
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
transactions.push(txResult);
|
|
39
|
+
// Update gas used (only for EVM chains that support it)
|
|
40
|
+
if (chainService instanceof ChainService_1.ChainService && txResult && typeof txResult === 'object' && 'gasUsed' in txResult) {
|
|
41
|
+
const gasUsed = txResult.gasUsed;
|
|
42
|
+
if (gasUsed && typeof gasUsed === 'bigint') {
|
|
43
|
+
totalGasUsed += gasUsed;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Progress callback
|
|
47
|
+
if (onProgress) {
|
|
48
|
+
onProgress(i + 1, recipients.length, txResult.hash);
|
|
49
|
+
}
|
|
50
|
+
// Small delay to prevent rate limiting
|
|
51
|
+
if (i < recipients.length - 1) {
|
|
52
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
57
|
+
failed.push({
|
|
58
|
+
recipient,
|
|
59
|
+
error: new Error(`Failed to transfer to ${recipient.address}: ${errorMessage}`)
|
|
60
|
+
});
|
|
61
|
+
// Still call progress callback for failed transfers
|
|
62
|
+
if (onProgress) {
|
|
63
|
+
onProgress(i + 1, recipients.length, 'FAILED');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
transactions,
|
|
69
|
+
failed,
|
|
70
|
+
gasUsed: totalGasUsed
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Processes a batch of ERC-20/SPL token transfers.
|
|
75
|
+
* @param chainService - The chain service instance (EVM or Solana)
|
|
76
|
+
* @param tokenAddress - The token contract address
|
|
77
|
+
* @param recipients - Array of recipients to transfer to
|
|
78
|
+
* @param onProgress - Optional progress callback
|
|
79
|
+
* @returns Promise resolving to batch processing results
|
|
80
|
+
*/
|
|
81
|
+
static async processTokenBatch(chainService, tokenAddress, recipients, onProgress) {
|
|
82
|
+
const transactions = [];
|
|
83
|
+
const failed = [];
|
|
84
|
+
let totalGasUsed = BigInt(0);
|
|
85
|
+
for (let i = 0; i < recipients.length; i++) {
|
|
86
|
+
const recipient = recipients[i];
|
|
87
|
+
try {
|
|
88
|
+
let txResult;
|
|
89
|
+
if (chainService instanceof SolanaChainService_1.SolanaChainService) {
|
|
90
|
+
// Solana SPL token transfer
|
|
91
|
+
txResult = await chainService.sendToken(tokenAddress, recipient.address, recipient.amount);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
// EVM ERC-20 token transfer
|
|
95
|
+
txResult = await chainService.sendToken(tokenAddress, recipient.address, recipient.amount);
|
|
96
|
+
}
|
|
97
|
+
transactions.push(txResult);
|
|
98
|
+
// Update gas used (only for EVM chains that support it)
|
|
99
|
+
if (chainService instanceof ChainService_1.ChainService && txResult && typeof txResult === 'object' && 'gasUsed' in txResult) {
|
|
100
|
+
const gasUsed = txResult.gasUsed;
|
|
101
|
+
if (gasUsed && typeof gasUsed === 'bigint') {
|
|
102
|
+
totalGasUsed += gasUsed;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Progress callback
|
|
106
|
+
if (onProgress) {
|
|
107
|
+
onProgress(i + 1, recipients.length, txResult.hash);
|
|
108
|
+
}
|
|
109
|
+
// Small delay to prevent rate limiting
|
|
110
|
+
if (i < recipients.length - 1) {
|
|
111
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
116
|
+
failed.push({
|
|
117
|
+
recipient,
|
|
118
|
+
error: new Error(`Failed to transfer token to ${recipient.address}: ${errorMessage}`)
|
|
119
|
+
});
|
|
120
|
+
// Still call progress callback for failed transfers
|
|
121
|
+
if (onProgress) {
|
|
122
|
+
onProgress(i + 1, recipients.length, 'FAILED');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
transactions,
|
|
128
|
+
failed,
|
|
129
|
+
gasUsed: totalGasUsed
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Splits recipients into batches of specified size.
|
|
134
|
+
* @param recipients - Array of all recipients
|
|
135
|
+
* @param batchSize - Size of each batch
|
|
136
|
+
* @returns Array of recipient batches
|
|
137
|
+
*/
|
|
138
|
+
static splitIntoBatches(recipients, batchSize) {
|
|
139
|
+
const batches = [];
|
|
140
|
+
for (let i = 0; i < recipients.length; i += batchSize) {
|
|
141
|
+
batches.push(recipients.slice(i, i + batchSize));
|
|
142
|
+
}
|
|
143
|
+
return batches;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Validates recipient addresses and amounts.
|
|
147
|
+
* @param recipients - Array of recipients to validate
|
|
148
|
+
* @returns Validation result
|
|
149
|
+
*/
|
|
150
|
+
static validateRecipients(recipients) {
|
|
151
|
+
const errors = [];
|
|
152
|
+
if (!recipients || recipients.length === 0) {
|
|
153
|
+
errors.push('No recipients provided');
|
|
154
|
+
return { isValid: false, errors };
|
|
155
|
+
}
|
|
156
|
+
if (recipients.length > 100) {
|
|
157
|
+
errors.push('Maximum 100 recipients allowed per operation');
|
|
158
|
+
}
|
|
159
|
+
const addresses = new Set();
|
|
160
|
+
recipients.forEach((recipient, index) => {
|
|
161
|
+
// Validate address
|
|
162
|
+
if (!recipient.address || recipient.address.trim() === '') {
|
|
163
|
+
errors.push(`Recipient ${index + 1}: Invalid address`);
|
|
164
|
+
}
|
|
165
|
+
else if (addresses.has(recipient.address.toLowerCase())) {
|
|
166
|
+
errors.push(`Recipient ${index + 1}: Duplicate address ${recipient.address}`);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
addresses.add(recipient.address.toLowerCase());
|
|
170
|
+
}
|
|
171
|
+
// Validate amount
|
|
172
|
+
if (!recipient.amount || recipient.amount.trim() === '') {
|
|
173
|
+
errors.push(`Recipient ${index + 1}: Invalid amount`);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
const amount = parseFloat(recipient.amount);
|
|
177
|
+
if (isNaN(amount) || amount <= 0) {
|
|
178
|
+
errors.push(`Recipient ${index + 1}: Amount must be a positive number`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
return {
|
|
183
|
+
isValid: errors.length === 0,
|
|
184
|
+
errors
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
exports.BatchProcessor = BatchProcessor;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Vault } from '../Vault';
|
|
2
|
+
import { ChainService } from '../chain/ChainService';
|
|
3
|
+
import { SolanaChainService } from '../chain/SolanaChainService';
|
|
4
|
+
import { Recipient, MultiTransferOptions, MultiTransferResult } from '../types/multiTransfer';
|
|
5
|
+
import { ChainId } from '../config/chains';
|
|
6
|
+
/**
|
|
7
|
+
* Service for handling multi-transfer operations across different blockchain networks.
|
|
8
|
+
* Supports both native tokens and ERC-20/SPL tokens with batch processing and progress tracking.
|
|
9
|
+
*/
|
|
10
|
+
export declare class MultiTransferService {
|
|
11
|
+
private vault;
|
|
12
|
+
private chainService;
|
|
13
|
+
private chainId;
|
|
14
|
+
/**
|
|
15
|
+
* Creates a new MultiTransferService instance.
|
|
16
|
+
* @param vault - The vault instance containing the accounts
|
|
17
|
+
* @param chainService - The chain service for the target blockchain
|
|
18
|
+
* @param chainId - The chain ID for all operations
|
|
19
|
+
*/
|
|
20
|
+
constructor(vault: Vault, chainService: ChainService | SolanaChainService, chainId: ChainId);
|
|
21
|
+
/**
|
|
22
|
+
* Transfers native tokens to multiple recipients.
|
|
23
|
+
* @param fromAddress - The sender's address
|
|
24
|
+
* @param recipients - Array of recipients with addresses and amounts
|
|
25
|
+
* @param chainId - The chain ID for the transfer
|
|
26
|
+
* @param options - Optional configuration for the transfer operation
|
|
27
|
+
* @returns Promise resolving to the multi-transfer result
|
|
28
|
+
* @throws Error if validation fails or insufficient balance
|
|
29
|
+
*/
|
|
30
|
+
transferNativeTokens(fromAddress: string, recipients: Recipient[], chainId: ChainId, options?: MultiTransferOptions): Promise<MultiTransferResult>;
|
|
31
|
+
/**
|
|
32
|
+
* Transfers ERC-20/SPL tokens to multiple recipients.
|
|
33
|
+
* @param fromAddress - The sender's address
|
|
34
|
+
* @param tokenAddress - The token contract address
|
|
35
|
+
* @param recipients - Array of recipients with addresses and amounts
|
|
36
|
+
* @param chainId - The chain ID for the transfer
|
|
37
|
+
* @param options - Optional configuration for the transfer operation
|
|
38
|
+
* @returns Promise resolving to the multi-transfer result
|
|
39
|
+
* @throws Error if validation fails or insufficient balance
|
|
40
|
+
*/
|
|
41
|
+
transferTokens(fromAddress: string, tokenAddress: string, recipients: Recipient[], chainId: ChainId, options?: MultiTransferOptions): Promise<MultiTransferResult>;
|
|
42
|
+
/**
|
|
43
|
+
* Validates transfer inputs including recipients and amounts.
|
|
44
|
+
* @param fromAddress - The sender's address
|
|
45
|
+
* @param recipients - Array of recipients to validate
|
|
46
|
+
* @param options - Transfer options
|
|
47
|
+
* @returns Validation result with errors and total amount
|
|
48
|
+
*/
|
|
49
|
+
private validateTransferInputs;
|
|
50
|
+
/**
|
|
51
|
+
* Checks if the account has sufficient balance for the transfer.
|
|
52
|
+
* @param address - The account address to check
|
|
53
|
+
* @param amount - The amount to transfer
|
|
54
|
+
* @param isNative - Whether checking native token balance
|
|
55
|
+
* @param tokenAddress - Token address (for non-native tokens)
|
|
56
|
+
* @param chainId - The chain ID for the check
|
|
57
|
+
* @throws Error if insufficient balance
|
|
58
|
+
*/
|
|
59
|
+
private checkBalance;
|
|
60
|
+
/**
|
|
61
|
+
* Estimates gas cost for a multi-transfer operation.
|
|
62
|
+
* @param recipients - Array of recipients to estimate gas for
|
|
63
|
+
* @param chainId - The chain ID for the estimation
|
|
64
|
+
* @param isNative - Whether estimating for native tokens (default: true)
|
|
65
|
+
* @param tokenAddress - Token contract address (required for non-native tokens)
|
|
66
|
+
* @returns Promise resolving to estimated gas cost in wei
|
|
67
|
+
*/
|
|
68
|
+
estimateGasCost(recipients: Recipient[], chainId: ChainId, isNative?: boolean, tokenAddress?: string): Promise<bigint>;
|
|
69
|
+
/**
|
|
70
|
+
* Factory method: Create MultiTransferService from vault, fromAddress, and chainId.
|
|
71
|
+
* Handles private key and chainService creation internally for simplicity.
|
|
72
|
+
* @param vault - The vault instance
|
|
73
|
+
* @param fromAddress - The sender's address
|
|
74
|
+
* @param chainId - The chain ID
|
|
75
|
+
* @returns Promise resolving to a MultiTransferService instance
|
|
76
|
+
*/
|
|
77
|
+
static fromVault(vault: Vault, fromAddress: string, chainId: ChainId): Promise<MultiTransferService>;
|
|
78
|
+
}
|