@dynamic-labs-wallet/node-svm 0.0.0-beta.310 → 0.0.0-beta.311
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.cjs.js +156 -67
- package/index.esm.js +156 -70
- package/package.json +2 -2
- package/src/client/base58.d.ts +3 -0
- package/src/client/base58.d.ts.map +1 -0
- package/src/client/client.d.ts +3 -3
- package/src/client/client.d.ts.map +1 -1
- package/src/client/index.d.ts +1 -0
- package/src/client/index.d.ts.map +1 -1
- package/src/client/utils.d.ts +5 -0
- package/src/client/utils.d.ts.map +1 -1
package/index.cjs.js
CHANGED
|
@@ -16,45 +16,75 @@ function _extends() {
|
|
|
16
16
|
|
|
17
17
|
const ERROR_CREATE_WALLET_ACCOUNT = 'Error creating svm wallet account';
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
19
|
+
// Base58 encoding/decoding (Bitcoin alphabet) without external dependencies
|
|
20
|
+
// Implementation adapted from the reference algorithm; suitable for keys/signatures
|
|
21
|
+
const ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
|
22
|
+
const ALPHABET_MAP = {};
|
|
23
|
+
for(let i = 0; i < ALPHABET.length; i++)ALPHABET_MAP[ALPHABET[i]] = i;
|
|
24
|
+
function encodeBase58(source) {
|
|
25
|
+
const bytes = source instanceof Uint8Array ? source : new Uint8Array(source);
|
|
26
|
+
if (bytes.length === 0) return '';
|
|
27
|
+
// Count leading zeros
|
|
28
|
+
let zeros = 0;
|
|
29
|
+
while(zeros < bytes.length && bytes[zeros] === 0)zeros++;
|
|
30
|
+
// Allocate enough space in big-endian base58 representation.
|
|
31
|
+
// log(256) / log(58), rounded up.
|
|
32
|
+
const size = (bytes.length - zeros) * 138 / 100 + 1 >>> 0;
|
|
33
|
+
const b58 = new Uint8Array(size);
|
|
34
|
+
let length = 0;
|
|
35
|
+
for(let i = zeros; i < bytes.length; i++){
|
|
36
|
+
let carry = bytes[i];
|
|
37
|
+
let j = 0;
|
|
38
|
+
for(let k = size - 1; (carry !== 0 || j < length) && k >= 0; k--, j++){
|
|
39
|
+
carry += 256 * b58[k] >>> 0;
|
|
40
|
+
b58[k] = carry % 58 >>> 0;
|
|
41
|
+
carry = carry / 58 >>> 0;
|
|
42
|
+
}
|
|
43
|
+
length = j;
|
|
32
44
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
transaction.feePayer = fromPubkey;
|
|
42
|
-
const serializedTransaction = transaction.serializeMessage();
|
|
43
|
-
return {
|
|
44
|
-
transaction,
|
|
45
|
-
serializedTransaction
|
|
46
|
-
};
|
|
45
|
+
// Skip leading zeros in base58 result
|
|
46
|
+
let it = size - length;
|
|
47
|
+
while(it < size && b58[it] === 0)it++;
|
|
48
|
+
// Translate the result into a string.
|
|
49
|
+
let str = '';
|
|
50
|
+
for(let i = 0; i < zeros; i++)str += '1';
|
|
51
|
+
for(; it < size; it++)str += ALPHABET[b58[it]];
|
|
52
|
+
return str;
|
|
47
53
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
function decodeBase58(str) {
|
|
55
|
+
if (str.length === 0) return new Uint8Array();
|
|
56
|
+
// Count leading zeros
|
|
57
|
+
let zeros = 0;
|
|
58
|
+
while(zeros < str.length && str[zeros] === '1')zeros++;
|
|
59
|
+
// Allocate enough space in big-endian base256 representation.
|
|
60
|
+
// log(58) / log(256), rounded up.
|
|
61
|
+
const size = (str.length - zeros) * 733 / 1000 + 1 >>> 0;
|
|
62
|
+
const b256 = new Uint8Array(size);
|
|
63
|
+
let length = 0;
|
|
64
|
+
for(let i = zeros; i < str.length; i++){
|
|
65
|
+
const value = ALPHABET_MAP[str[i]];
|
|
66
|
+
if (value === undefined) throw new Error('Invalid base58 character');
|
|
67
|
+
let carry = value;
|
|
68
|
+
let j = 0;
|
|
69
|
+
for(let k = size - 1; (carry !== 0 || j < length) && k >= 0; k--, j++){
|
|
70
|
+
carry += 58 * b256[k] >>> 0;
|
|
71
|
+
b256[k] = (carry & 0xff) >>> 0;
|
|
72
|
+
carry = carry >> 8 >>> 0;
|
|
73
|
+
}
|
|
74
|
+
length = j;
|
|
75
|
+
}
|
|
76
|
+
// Skip leading zeros in b256
|
|
77
|
+
let it = size - length;
|
|
78
|
+
while(it < size && b256[it] === 0)it++;
|
|
79
|
+
const out = new Uint8Array(zeros + (size - it));
|
|
80
|
+
out.fill(0, 0, zeros);
|
|
81
|
+
let j = zeros;
|
|
82
|
+
while(it < size)out[j++] = b256[it++];
|
|
83
|
+
return out;
|
|
56
84
|
}
|
|
57
85
|
|
|
86
|
+
// Helper: normalize bytes to hex without triggering Buffer.from(string, encoding) overload
|
|
87
|
+
const toHex = (bytes)=>(Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes)).toString('hex');
|
|
58
88
|
class DynamicSvmWalletClient extends node.DynamicWalletClient {
|
|
59
89
|
/**
|
|
60
90
|
* Creates a wallet account on the Solana chain
|
|
@@ -117,10 +147,12 @@ class DynamicSvmWalletClient extends node.DynamicWalletClient {
|
|
|
117
147
|
}
|
|
118
148
|
// Function to properly derive account address
|
|
119
149
|
async deriveAccountAddress(rawPublicKey) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
150
|
+
function ensure32(u8) {
|
|
151
|
+
if (u8.length !== 32) throw new Error(`Invalid pubkey length: ${u8.length}`);
|
|
152
|
+
return u8;
|
|
153
|
+
}
|
|
154
|
+
const pubKeyBytes = typeof rawPublicKey === 'string' ? new Uint8Array(Buffer.from(rawPublicKey, 'hex')) : rawPublicKey;
|
|
155
|
+
const accountAddress = new web3_js.PublicKey(ensure32(pubKeyBytes)).toBase58();
|
|
124
156
|
return {
|
|
125
157
|
accountAddress
|
|
126
158
|
};
|
|
@@ -132,25 +164,29 @@ class DynamicSvmWalletClient extends node.DynamicWalletClient {
|
|
|
132
164
|
* @param accountAddress Solana address (base58 encoded)
|
|
133
165
|
* @param password The password for encrypted backup shares
|
|
134
166
|
*/ async signMessage({ message, accountAddress, password = undefined, externalServerKeyShares }) {
|
|
167
|
+
// Validate inputs early
|
|
168
|
+
if (!accountAddress) {
|
|
169
|
+
throw new Error('Account address is required');
|
|
170
|
+
}
|
|
171
|
+
if (Array.isArray(externalServerKeyShares) && externalServerKeyShares.length === 0) {
|
|
172
|
+
throw new Error('External server key shares are required');
|
|
173
|
+
}
|
|
135
174
|
await this.verifyPassword({
|
|
136
175
|
accountAddress,
|
|
137
176
|
password,
|
|
138
177
|
walletOperation: node.WalletOperation.SIGN_MESSAGE
|
|
139
178
|
});
|
|
140
|
-
if (!accountAddress) {
|
|
141
|
-
throw new Error('Account address is required');
|
|
142
|
-
}
|
|
143
179
|
try {
|
|
180
|
+
const messageBytes = typeof message === 'string' ? new TextEncoder().encode(message) : message;
|
|
181
|
+
const messageHex = toHex(messageBytes);
|
|
144
182
|
const signatureEd25519 = await this.sign({
|
|
145
|
-
message,
|
|
146
|
-
accountAddress
|
|
183
|
+
message: messageHex,
|
|
184
|
+
accountAddress,
|
|
147
185
|
chainName: this.chainName,
|
|
148
186
|
password,
|
|
149
187
|
externalServerKeyShares
|
|
150
188
|
});
|
|
151
|
-
|
|
152
|
-
const base58Signature = new web3_js.PublicKey(signatureEd25519).toBase58();
|
|
153
|
-
return base58Signature;
|
|
189
|
+
return encodeBase58(signatureEd25519);
|
|
154
190
|
} catch (error) {
|
|
155
191
|
this.logger.error('Error signing message:', error);
|
|
156
192
|
throw error;
|
|
@@ -158,6 +194,13 @@ class DynamicSvmWalletClient extends node.DynamicWalletClient {
|
|
|
158
194
|
}
|
|
159
195
|
//todo:should txn just be a string?
|
|
160
196
|
async signTransaction({ senderAddress, transaction, password = undefined, externalServerKeyShares }) {
|
|
197
|
+
// Validate inputs early
|
|
198
|
+
if (!senderAddress) {
|
|
199
|
+
throw new Error('Sender address is required');
|
|
200
|
+
}
|
|
201
|
+
if (Array.isArray(externalServerKeyShares) && externalServerKeyShares.length === 0) {
|
|
202
|
+
throw new Error('External server key shares are required');
|
|
203
|
+
}
|
|
161
204
|
await this.verifyPassword({
|
|
162
205
|
accountAddress: senderAddress,
|
|
163
206
|
password,
|
|
@@ -165,14 +208,14 @@ class DynamicSvmWalletClient extends node.DynamicWalletClient {
|
|
|
165
208
|
});
|
|
166
209
|
try {
|
|
167
210
|
let messageToSign;
|
|
168
|
-
if (transaction
|
|
169
|
-
|
|
211
|
+
if (typeof transaction === 'string') {
|
|
212
|
+
messageToSign = transaction.startsWith('0x') ? transaction.slice(2) : transaction;
|
|
213
|
+
} else if (transaction instanceof web3_js.VersionedTransaction) {
|
|
170
214
|
const messageBytes = transaction.message.serialize();
|
|
171
|
-
messageToSign =
|
|
215
|
+
messageToSign = toHex(messageBytes);
|
|
172
216
|
} else {
|
|
173
|
-
// For legacy transactions, serialize the message
|
|
174
217
|
const messageBytes = transaction.serializeMessage();
|
|
175
|
-
messageToSign = Buffer.
|
|
218
|
+
messageToSign = toHex(Buffer.isBuffer(messageBytes) ? messageBytes : messageBytes);
|
|
176
219
|
}
|
|
177
220
|
const signatureEd25519 = await this.sign({
|
|
178
221
|
message: messageToSign,
|
|
@@ -184,13 +227,7 @@ class DynamicSvmWalletClient extends node.DynamicWalletClient {
|
|
|
184
227
|
if (!signatureEd25519) {
|
|
185
228
|
throw new Error('Signature is undefined');
|
|
186
229
|
}
|
|
187
|
-
|
|
188
|
-
const signedTransaction = addSignatureToTransaction({
|
|
189
|
-
transaction,
|
|
190
|
-
signature: signatureEd25519,
|
|
191
|
-
signerPublicKey: senderPublicKey
|
|
192
|
-
});
|
|
193
|
-
return signedTransaction;
|
|
230
|
+
return encodeBase58(signatureEd25519);
|
|
194
231
|
} catch (error) {
|
|
195
232
|
this.logger.error('Error in signTransaction:', error);
|
|
196
233
|
if (error instanceof Error) {
|
|
@@ -238,18 +275,19 @@ class DynamicSvmWalletClient extends node.DynamicWalletClient {
|
|
|
238
275
|
* @param privateKey The private key to convert
|
|
239
276
|
* @returns The hex string
|
|
240
277
|
*/ decodePrivateKeyForSolana(privateKey) {
|
|
241
|
-
const decoded =
|
|
242
|
-
|
|
243
|
-
return Buffer.from(
|
|
278
|
+
const decoded = decodeBase58(privateKey); // 64 bytes
|
|
279
|
+
if (decoded.length !== 64) throw new Error('Invalid Solana secret key length');
|
|
280
|
+
return Buffer.from(decoded.slice(0, 32)).toString('hex');
|
|
244
281
|
}
|
|
245
282
|
getPublicKeyFromPrivateKey(privateKey) {
|
|
246
|
-
const
|
|
247
|
-
const keypair = web3_js.Keypair.fromSecretKey(
|
|
248
|
-
|
|
249
|
-
return publicKeyBase58;
|
|
283
|
+
const secret = decodeBase58(privateKey);
|
|
284
|
+
const keypair = web3_js.Keypair.fromSecretKey(secret);
|
|
285
|
+
return keypair.publicKey.toBase58();
|
|
250
286
|
}
|
|
251
287
|
encodePublicKey(publicKey) {
|
|
252
|
-
|
|
288
|
+
// Ensure a plain Uint8Array is passed to PublicKey
|
|
289
|
+
const bytes = publicKey instanceof Uint8Array ? publicKey : new Uint8Array(publicKey);
|
|
290
|
+
return new web3_js.PublicKey(bytes).toBase58();
|
|
253
291
|
}
|
|
254
292
|
/**
|
|
255
293
|
* Imports the private key for a given account address
|
|
@@ -319,8 +357,59 @@ class DynamicSvmWalletClient extends node.DynamicWalletClient {
|
|
|
319
357
|
}
|
|
320
358
|
}
|
|
321
359
|
|
|
360
|
+
async function getBalance({ address, rpcUrl = node.SOLANA_RPC_URL }) {
|
|
361
|
+
const connection = new web3_js.Connection(rpcUrl != null ? rpcUrl : 'https://api.devnet.solana.com');
|
|
362
|
+
const balance = await connection.getBalance(new web3_js.PublicKey(address));
|
|
363
|
+
return balance;
|
|
364
|
+
}
|
|
365
|
+
async function createSolanaTransaction({ senderSolanaAddress, amount, to, rpcUrl = 'https://api.devnet.solana.com' }) {
|
|
366
|
+
const connection = new web3_js.Connection(rpcUrl != null ? rpcUrl : 'https://api.devnet.solana.com');
|
|
367
|
+
const balance = await getBalance({
|
|
368
|
+
address: senderSolanaAddress,
|
|
369
|
+
rpcUrl
|
|
370
|
+
});
|
|
371
|
+
if (balance < amount * 1e9) {
|
|
372
|
+
throw new Error('Insufficient balance');
|
|
373
|
+
}
|
|
374
|
+
const fromPubkey = new web3_js.PublicKey(senderSolanaAddress);
|
|
375
|
+
const transaction = new web3_js.Transaction().add(web3_js.SystemProgram.transfer({
|
|
376
|
+
fromPubkey: fromPubkey,
|
|
377
|
+
toPubkey: new web3_js.PublicKey(to),
|
|
378
|
+
lamports: amount * 1e9
|
|
379
|
+
}));
|
|
380
|
+
const { blockhash } = await connection.getLatestBlockhash();
|
|
381
|
+
transaction.recentBlockhash = blockhash;
|
|
382
|
+
transaction.feePayer = fromPubkey;
|
|
383
|
+
const serializedTransaction = transaction.serializeMessage();
|
|
384
|
+
return {
|
|
385
|
+
transaction,
|
|
386
|
+
serializedTransaction
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
const addSignatureToTransaction = ({ transaction, signature, signerPublicKey })=>{
|
|
390
|
+
transaction.addSignature(signerPublicKey, Buffer.from(signature));
|
|
391
|
+
return transaction;
|
|
392
|
+
};
|
|
393
|
+
function attachSignature({ transaction, signatureBase58, senderAddress }) {
|
|
394
|
+
const sigBytes = decodeBase58(signatureBase58);
|
|
395
|
+
const signerPubkey = new web3_js.PublicKey(senderAddress);
|
|
396
|
+
return addSignatureToTransaction({
|
|
397
|
+
transaction,
|
|
398
|
+
signature: sigBytes,
|
|
399
|
+
signerPublicKey: signerPubkey
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
async function sendTransaction({ signedTransaction, rpcUrl = 'https://api.devnet.solana.com' }) {
|
|
403
|
+
const connection = new web3_js.Connection(rpcUrl != null ? rpcUrl : 'https://api.devnet.solana.com');
|
|
404
|
+
const txid = await connection.sendRawTransaction(Buffer.from(signedTransaction));
|
|
405
|
+
return txid;
|
|
406
|
+
}
|
|
407
|
+
|
|
322
408
|
exports.DynamicSvmWalletClient = DynamicSvmWalletClient;
|
|
323
409
|
exports.addSignatureToTransaction = addSignatureToTransaction;
|
|
410
|
+
exports.attachSignature = attachSignature;
|
|
324
411
|
exports.createSolanaTransaction = createSolanaTransaction;
|
|
412
|
+
exports.decodeBase58 = decodeBase58;
|
|
413
|
+
exports.encodeBase58 = encodeBase58;
|
|
325
414
|
exports.getBalance = getBalance;
|
|
326
415
|
exports.sendTransaction = sendTransaction;
|
package/index.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { DynamicWalletClient, getExternalServerKeyShareBackupInfo, WalletOperation, SOLANA_RPC_URL } from '@dynamic-labs-wallet/node';
|
|
2
|
+
import { PublicKey, VersionedTransaction, Keypair, Connection, Transaction, SystemProgram } from '@solana/web3.js';
|
|
3
3
|
|
|
4
4
|
function _extends() {
|
|
5
5
|
_extends = Object.assign || function assign(target) {
|
|
@@ -14,45 +14,75 @@ function _extends() {
|
|
|
14
14
|
|
|
15
15
|
const ERROR_CREATE_WALLET_ACCOUNT = 'Error creating svm wallet account';
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
17
|
+
// Base58 encoding/decoding (Bitcoin alphabet) without external dependencies
|
|
18
|
+
// Implementation adapted from the reference algorithm; suitable for keys/signatures
|
|
19
|
+
const ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
|
20
|
+
const ALPHABET_MAP = {};
|
|
21
|
+
for(let i = 0; i < ALPHABET.length; i++)ALPHABET_MAP[ALPHABET[i]] = i;
|
|
22
|
+
function encodeBase58(source) {
|
|
23
|
+
const bytes = source instanceof Uint8Array ? source : new Uint8Array(source);
|
|
24
|
+
if (bytes.length === 0) return '';
|
|
25
|
+
// Count leading zeros
|
|
26
|
+
let zeros = 0;
|
|
27
|
+
while(zeros < bytes.length && bytes[zeros] === 0)zeros++;
|
|
28
|
+
// Allocate enough space in big-endian base58 representation.
|
|
29
|
+
// log(256) / log(58), rounded up.
|
|
30
|
+
const size = (bytes.length - zeros) * 138 / 100 + 1 >>> 0;
|
|
31
|
+
const b58 = new Uint8Array(size);
|
|
32
|
+
let length = 0;
|
|
33
|
+
for(let i = zeros; i < bytes.length; i++){
|
|
34
|
+
let carry = bytes[i];
|
|
35
|
+
let j = 0;
|
|
36
|
+
for(let k = size - 1; (carry !== 0 || j < length) && k >= 0; k--, j++){
|
|
37
|
+
carry += 256 * b58[k] >>> 0;
|
|
38
|
+
b58[k] = carry % 58 >>> 0;
|
|
39
|
+
carry = carry / 58 >>> 0;
|
|
40
|
+
}
|
|
41
|
+
length = j;
|
|
30
42
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
transaction.feePayer = fromPubkey;
|
|
40
|
-
const serializedTransaction = transaction.serializeMessage();
|
|
41
|
-
return {
|
|
42
|
-
transaction,
|
|
43
|
-
serializedTransaction
|
|
44
|
-
};
|
|
43
|
+
// Skip leading zeros in base58 result
|
|
44
|
+
let it = size - length;
|
|
45
|
+
while(it < size && b58[it] === 0)it++;
|
|
46
|
+
// Translate the result into a string.
|
|
47
|
+
let str = '';
|
|
48
|
+
for(let i = 0; i < zeros; i++)str += '1';
|
|
49
|
+
for(; it < size; it++)str += ALPHABET[b58[it]];
|
|
50
|
+
return str;
|
|
45
51
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
function decodeBase58(str) {
|
|
53
|
+
if (str.length === 0) return new Uint8Array();
|
|
54
|
+
// Count leading zeros
|
|
55
|
+
let zeros = 0;
|
|
56
|
+
while(zeros < str.length && str[zeros] === '1')zeros++;
|
|
57
|
+
// Allocate enough space in big-endian base256 representation.
|
|
58
|
+
// log(58) / log(256), rounded up.
|
|
59
|
+
const size = (str.length - zeros) * 733 / 1000 + 1 >>> 0;
|
|
60
|
+
const b256 = new Uint8Array(size);
|
|
61
|
+
let length = 0;
|
|
62
|
+
for(let i = zeros; i < str.length; i++){
|
|
63
|
+
const value = ALPHABET_MAP[str[i]];
|
|
64
|
+
if (value === undefined) throw new Error('Invalid base58 character');
|
|
65
|
+
let carry = value;
|
|
66
|
+
let j = 0;
|
|
67
|
+
for(let k = size - 1; (carry !== 0 || j < length) && k >= 0; k--, j++){
|
|
68
|
+
carry += 58 * b256[k] >>> 0;
|
|
69
|
+
b256[k] = (carry & 0xff) >>> 0;
|
|
70
|
+
carry = carry >> 8 >>> 0;
|
|
71
|
+
}
|
|
72
|
+
length = j;
|
|
73
|
+
}
|
|
74
|
+
// Skip leading zeros in b256
|
|
75
|
+
let it = size - length;
|
|
76
|
+
while(it < size && b256[it] === 0)it++;
|
|
77
|
+
const out = new Uint8Array(zeros + (size - it));
|
|
78
|
+
out.fill(0, 0, zeros);
|
|
79
|
+
let j = zeros;
|
|
80
|
+
while(it < size)out[j++] = b256[it++];
|
|
81
|
+
return out;
|
|
54
82
|
}
|
|
55
83
|
|
|
84
|
+
// Helper: normalize bytes to hex without triggering Buffer.from(string, encoding) overload
|
|
85
|
+
const toHex = (bytes)=>(Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes)).toString('hex');
|
|
56
86
|
class DynamicSvmWalletClient extends DynamicWalletClient {
|
|
57
87
|
/**
|
|
58
88
|
* Creates a wallet account on the Solana chain
|
|
@@ -115,10 +145,12 @@ class DynamicSvmWalletClient extends DynamicWalletClient {
|
|
|
115
145
|
}
|
|
116
146
|
// Function to properly derive account address
|
|
117
147
|
async deriveAccountAddress(rawPublicKey) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
148
|
+
function ensure32(u8) {
|
|
149
|
+
if (u8.length !== 32) throw new Error(`Invalid pubkey length: ${u8.length}`);
|
|
150
|
+
return u8;
|
|
151
|
+
}
|
|
152
|
+
const pubKeyBytes = typeof rawPublicKey === 'string' ? new Uint8Array(Buffer.from(rawPublicKey, 'hex')) : rawPublicKey;
|
|
153
|
+
const accountAddress = new PublicKey(ensure32(pubKeyBytes)).toBase58();
|
|
122
154
|
return {
|
|
123
155
|
accountAddress
|
|
124
156
|
};
|
|
@@ -130,25 +162,29 @@ class DynamicSvmWalletClient extends DynamicWalletClient {
|
|
|
130
162
|
* @param accountAddress Solana address (base58 encoded)
|
|
131
163
|
* @param password The password for encrypted backup shares
|
|
132
164
|
*/ async signMessage({ message, accountAddress, password = undefined, externalServerKeyShares }) {
|
|
165
|
+
// Validate inputs early
|
|
166
|
+
if (!accountAddress) {
|
|
167
|
+
throw new Error('Account address is required');
|
|
168
|
+
}
|
|
169
|
+
if (Array.isArray(externalServerKeyShares) && externalServerKeyShares.length === 0) {
|
|
170
|
+
throw new Error('External server key shares are required');
|
|
171
|
+
}
|
|
133
172
|
await this.verifyPassword({
|
|
134
173
|
accountAddress,
|
|
135
174
|
password,
|
|
136
175
|
walletOperation: WalletOperation.SIGN_MESSAGE
|
|
137
176
|
});
|
|
138
|
-
if (!accountAddress) {
|
|
139
|
-
throw new Error('Account address is required');
|
|
140
|
-
}
|
|
141
177
|
try {
|
|
178
|
+
const messageBytes = typeof message === 'string' ? new TextEncoder().encode(message) : message;
|
|
179
|
+
const messageHex = toHex(messageBytes);
|
|
142
180
|
const signatureEd25519 = await this.sign({
|
|
143
|
-
message,
|
|
144
|
-
accountAddress
|
|
181
|
+
message: messageHex,
|
|
182
|
+
accountAddress,
|
|
145
183
|
chainName: this.chainName,
|
|
146
184
|
password,
|
|
147
185
|
externalServerKeyShares
|
|
148
186
|
});
|
|
149
|
-
|
|
150
|
-
const base58Signature = new PublicKey(signatureEd25519).toBase58();
|
|
151
|
-
return base58Signature;
|
|
187
|
+
return encodeBase58(signatureEd25519);
|
|
152
188
|
} catch (error) {
|
|
153
189
|
this.logger.error('Error signing message:', error);
|
|
154
190
|
throw error;
|
|
@@ -156,6 +192,13 @@ class DynamicSvmWalletClient extends DynamicWalletClient {
|
|
|
156
192
|
}
|
|
157
193
|
//todo:should txn just be a string?
|
|
158
194
|
async signTransaction({ senderAddress, transaction, password = undefined, externalServerKeyShares }) {
|
|
195
|
+
// Validate inputs early
|
|
196
|
+
if (!senderAddress) {
|
|
197
|
+
throw new Error('Sender address is required');
|
|
198
|
+
}
|
|
199
|
+
if (Array.isArray(externalServerKeyShares) && externalServerKeyShares.length === 0) {
|
|
200
|
+
throw new Error('External server key shares are required');
|
|
201
|
+
}
|
|
159
202
|
await this.verifyPassword({
|
|
160
203
|
accountAddress: senderAddress,
|
|
161
204
|
password,
|
|
@@ -163,14 +206,14 @@ class DynamicSvmWalletClient extends DynamicWalletClient {
|
|
|
163
206
|
});
|
|
164
207
|
try {
|
|
165
208
|
let messageToSign;
|
|
166
|
-
if (transaction
|
|
167
|
-
|
|
209
|
+
if (typeof transaction === 'string') {
|
|
210
|
+
messageToSign = transaction.startsWith('0x') ? transaction.slice(2) : transaction;
|
|
211
|
+
} else if (transaction instanceof VersionedTransaction) {
|
|
168
212
|
const messageBytes = transaction.message.serialize();
|
|
169
|
-
messageToSign =
|
|
213
|
+
messageToSign = toHex(messageBytes);
|
|
170
214
|
} else {
|
|
171
|
-
// For legacy transactions, serialize the message
|
|
172
215
|
const messageBytes = transaction.serializeMessage();
|
|
173
|
-
messageToSign = Buffer.
|
|
216
|
+
messageToSign = toHex(Buffer.isBuffer(messageBytes) ? messageBytes : messageBytes);
|
|
174
217
|
}
|
|
175
218
|
const signatureEd25519 = await this.sign({
|
|
176
219
|
message: messageToSign,
|
|
@@ -182,13 +225,7 @@ class DynamicSvmWalletClient extends DynamicWalletClient {
|
|
|
182
225
|
if (!signatureEd25519) {
|
|
183
226
|
throw new Error('Signature is undefined');
|
|
184
227
|
}
|
|
185
|
-
|
|
186
|
-
const signedTransaction = addSignatureToTransaction({
|
|
187
|
-
transaction,
|
|
188
|
-
signature: signatureEd25519,
|
|
189
|
-
signerPublicKey: senderPublicKey
|
|
190
|
-
});
|
|
191
|
-
return signedTransaction;
|
|
228
|
+
return encodeBase58(signatureEd25519);
|
|
192
229
|
} catch (error) {
|
|
193
230
|
this.logger.error('Error in signTransaction:', error);
|
|
194
231
|
if (error instanceof Error) {
|
|
@@ -236,18 +273,19 @@ class DynamicSvmWalletClient extends DynamicWalletClient {
|
|
|
236
273
|
* @param privateKey The private key to convert
|
|
237
274
|
* @returns The hex string
|
|
238
275
|
*/ decodePrivateKeyForSolana(privateKey) {
|
|
239
|
-
const decoded =
|
|
240
|
-
|
|
241
|
-
return Buffer.from(
|
|
276
|
+
const decoded = decodeBase58(privateKey); // 64 bytes
|
|
277
|
+
if (decoded.length !== 64) throw new Error('Invalid Solana secret key length');
|
|
278
|
+
return Buffer.from(decoded.slice(0, 32)).toString('hex');
|
|
242
279
|
}
|
|
243
280
|
getPublicKeyFromPrivateKey(privateKey) {
|
|
244
|
-
const
|
|
245
|
-
const keypair = Keypair.fromSecretKey(
|
|
246
|
-
|
|
247
|
-
return publicKeyBase58;
|
|
281
|
+
const secret = decodeBase58(privateKey);
|
|
282
|
+
const keypair = Keypair.fromSecretKey(secret);
|
|
283
|
+
return keypair.publicKey.toBase58();
|
|
248
284
|
}
|
|
249
285
|
encodePublicKey(publicKey) {
|
|
250
|
-
|
|
286
|
+
// Ensure a plain Uint8Array is passed to PublicKey
|
|
287
|
+
const bytes = publicKey instanceof Uint8Array ? publicKey : new Uint8Array(publicKey);
|
|
288
|
+
return new PublicKey(bytes).toBase58();
|
|
251
289
|
}
|
|
252
290
|
/**
|
|
253
291
|
* Imports the private key for a given account address
|
|
@@ -317,4 +355,52 @@ class DynamicSvmWalletClient extends DynamicWalletClient {
|
|
|
317
355
|
}
|
|
318
356
|
}
|
|
319
357
|
|
|
320
|
-
|
|
358
|
+
async function getBalance({ address, rpcUrl = SOLANA_RPC_URL }) {
|
|
359
|
+
const connection = new Connection(rpcUrl != null ? rpcUrl : 'https://api.devnet.solana.com');
|
|
360
|
+
const balance = await connection.getBalance(new PublicKey(address));
|
|
361
|
+
return balance;
|
|
362
|
+
}
|
|
363
|
+
async function createSolanaTransaction({ senderSolanaAddress, amount, to, rpcUrl = 'https://api.devnet.solana.com' }) {
|
|
364
|
+
const connection = new Connection(rpcUrl != null ? rpcUrl : 'https://api.devnet.solana.com');
|
|
365
|
+
const balance = await getBalance({
|
|
366
|
+
address: senderSolanaAddress,
|
|
367
|
+
rpcUrl
|
|
368
|
+
});
|
|
369
|
+
if (balance < amount * 1e9) {
|
|
370
|
+
throw new Error('Insufficient balance');
|
|
371
|
+
}
|
|
372
|
+
const fromPubkey = new PublicKey(senderSolanaAddress);
|
|
373
|
+
const transaction = new Transaction().add(SystemProgram.transfer({
|
|
374
|
+
fromPubkey: fromPubkey,
|
|
375
|
+
toPubkey: new PublicKey(to),
|
|
376
|
+
lamports: amount * 1e9
|
|
377
|
+
}));
|
|
378
|
+
const { blockhash } = await connection.getLatestBlockhash();
|
|
379
|
+
transaction.recentBlockhash = blockhash;
|
|
380
|
+
transaction.feePayer = fromPubkey;
|
|
381
|
+
const serializedTransaction = transaction.serializeMessage();
|
|
382
|
+
return {
|
|
383
|
+
transaction,
|
|
384
|
+
serializedTransaction
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
const addSignatureToTransaction = ({ transaction, signature, signerPublicKey })=>{
|
|
388
|
+
transaction.addSignature(signerPublicKey, Buffer.from(signature));
|
|
389
|
+
return transaction;
|
|
390
|
+
};
|
|
391
|
+
function attachSignature({ transaction, signatureBase58, senderAddress }) {
|
|
392
|
+
const sigBytes = decodeBase58(signatureBase58);
|
|
393
|
+
const signerPubkey = new PublicKey(senderAddress);
|
|
394
|
+
return addSignatureToTransaction({
|
|
395
|
+
transaction,
|
|
396
|
+
signature: sigBytes,
|
|
397
|
+
signerPublicKey: signerPubkey
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
async function sendTransaction({ signedTransaction, rpcUrl = 'https://api.devnet.solana.com' }) {
|
|
401
|
+
const connection = new Connection(rpcUrl != null ? rpcUrl : 'https://api.devnet.solana.com');
|
|
402
|
+
const txid = await connection.sendRawTransaction(Buffer.from(signedTransaction));
|
|
403
|
+
return txid;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
export { DynamicSvmWalletClient, addSignatureToTransaction, attachSignature, createSolanaTransaction, decodeBase58, encodeBase58, getBalance, sendTransaction };
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dynamic-labs-wallet/node-svm",
|
|
3
|
-
"version": "0.0.0-beta.
|
|
3
|
+
"version": "0.0.0-beta.311",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@dynamic-labs-wallet/node": "0.0.0-beta.
|
|
6
|
+
"@dynamic-labs-wallet/node": "0.0.0-beta.311",
|
|
7
7
|
"@solana/web3.js": "^1.98.2"
|
|
8
8
|
},
|
|
9
9
|
"publishConfig": {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base58.d.ts","sourceRoot":"","sources":["../../src/client/base58.ts"],"names":[],"mappings":"AAOA,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAmCvD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAmCpD"}
|
package/src/client/client.d.ts
CHANGED
|
@@ -36,17 +36,17 @@ export declare class DynamicSvmWalletClient extends DynamicWalletClient {
|
|
|
36
36
|
* @param password The password for encrypted backup shares
|
|
37
37
|
*/
|
|
38
38
|
signMessage({ message, accountAddress, password, externalServerKeyShares, }: {
|
|
39
|
-
message: string;
|
|
39
|
+
message: string | Uint8Array;
|
|
40
40
|
accountAddress: string;
|
|
41
41
|
password?: string;
|
|
42
42
|
externalServerKeyShares?: ServerKeyShare[];
|
|
43
43
|
}): Promise<string>;
|
|
44
44
|
signTransaction({ senderAddress, transaction, password, externalServerKeyShares, }: {
|
|
45
45
|
senderAddress: string;
|
|
46
|
-
transaction: VersionedTransaction | Transaction;
|
|
46
|
+
transaction: VersionedTransaction | Transaction | string;
|
|
47
47
|
password?: string;
|
|
48
48
|
externalServerKeyShares?: ServerKeyShare[];
|
|
49
|
-
}): Promise<
|
|
49
|
+
}): Promise<string>;
|
|
50
50
|
/**
|
|
51
51
|
* Exports the private key for a given account address
|
|
52
52
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,mBAAmB,EACnB,wBAAwB,EAGzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAEL,WAAW,EACX,oBAAoB,EAErB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"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"}
|
package/src/client/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC"}
|
package/src/client/utils.d.ts
CHANGED
|
@@ -17,6 +17,11 @@ export declare const addSignatureToTransaction: ({ transaction, signature, signe
|
|
|
17
17
|
signature: Uint8Array;
|
|
18
18
|
signerPublicKey: PublicKey;
|
|
19
19
|
}) => VersionedTransaction | Transaction;
|
|
20
|
+
export declare function attachSignature({ transaction, signatureBase58, senderAddress, }: {
|
|
21
|
+
transaction: Transaction | VersionedTransaction;
|
|
22
|
+
signatureBase58: string;
|
|
23
|
+
senderAddress: string;
|
|
24
|
+
}): VersionedTransaction | Transaction;
|
|
20
25
|
export declare function sendTransaction({ signedTransaction, rpcUrl, }: {
|
|
21
26
|
signedTransaction: Uint8Array;
|
|
22
27
|
rpcUrl?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/client/utils.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,SAAS,EAET,WAAW,EACX,oBAAoB,EACrB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/client/utils.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,SAAS,EAET,WAAW,EACX,oBAAoB,EACrB,MAAM,iBAAiB,CAAC;AAGzB,wBAAsB,UAAU,CAAC,EAC/B,OAAO,EACP,MAAuB,GACxB,EAAE;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,mBAIA;AAED,wBAAsB,uBAAuB,CAAC,EAC5C,mBAAmB,EACnB,MAAM,EACN,EAAE,EACF,MAAwC,GACzC,EAAE;IACD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;;;GAwBA;AAED,eAAO,MAAM,yBAAyB,iDAInC;IACD,WAAW,EAAE,WAAW,GAAG,oBAAoB,CAAC;IAChD,SAAS,EAAE,UAAU,CAAC;IACtB,eAAe,EAAE,SAAS,CAAC;CAC5B,KAAG,oBAAoB,GAAG,WAG1B,CAAC;AAEF,wBAAgB,eAAe,CAAC,EAC9B,WAAW,EACX,eAAe,EACf,aAAa,GACd,EAAE;IACD,WAAW,EAAE,WAAW,GAAG,oBAAoB,CAAC;IAChD,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;CACvB,GAAG,oBAAoB,GAAG,WAAW,CAQrC;AAED,wBAAsB,eAAe,CAAC,EACpC,iBAAiB,EACjB,MAAwC,GACzC,EAAE;IACD,iBAAiB,EAAE,UAAU,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,mBAMA"}
|