@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 CHANGED
@@ -16,45 +16,75 @@ function _extends() {
16
16
 
17
17
  const ERROR_CREATE_WALLET_ACCOUNT = 'Error creating svm wallet account';
18
18
 
19
- async function getBalance({ address, rpcUrl = node.SOLANA_RPC_URL }) {
20
- const connection = new web3_js.Connection(rpcUrl != null ? rpcUrl : 'https://api.devnet.solana.com');
21
- const balance = await connection.getBalance(new web3_js.PublicKey(address));
22
- return balance;
23
- }
24
- async function createSolanaTransaction({ senderSolanaAddress, amount, to, rpcUrl = 'https://api.devnet.solana.com' }) {
25
- const connection = new web3_js.Connection(rpcUrl != null ? rpcUrl : 'https://api.devnet.solana.com');
26
- const balance = await getBalance({
27
- address: senderSolanaAddress,
28
- rpcUrl
29
- });
30
- if (balance < amount * 1e9) {
31
- throw new Error('Insufficient balance');
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
- const fromPubkey = new web3_js.PublicKey(senderSolanaAddress);
34
- const transaction = new web3_js.Transaction().add(web3_js.SystemProgram.transfer({
35
- fromPubkey: fromPubkey,
36
- toPubkey: new web3_js.PublicKey(to),
37
- lamports: amount * 1e9
38
- }));
39
- const { blockhash } = await connection.getLatestBlockhash();
40
- transaction.recentBlockhash = blockhash;
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
- const addSignatureToTransaction = ({ transaction, signature, signerPublicKey })=>{
49
- transaction.addSignature(signerPublicKey, Buffer.from(signature));
50
- return transaction;
51
- };
52
- async function sendTransaction({ signedTransaction, rpcUrl = 'https://api.devnet.solana.com' }) {
53
- const connection = new web3_js.Connection(rpcUrl != null ? rpcUrl : 'https://api.devnet.solana.com');
54
- const txid = await connection.sendRawTransaction(Buffer.from(signedTransaction));
55
- return txid;
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
- const pubKeyBytes = typeof rawPublicKey === 'string' ? Buffer.from(rawPublicKey, 'hex') : rawPublicKey;
121
- // Create PublicKey from bytes and convert to base58
122
- const pubKey = new web3_js.PublicKey(pubKeyBytes);
123
- const accountAddress = pubKey.toBase58();
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: accountAddress,
183
+ message: messageHex,
184
+ accountAddress,
147
185
  chainName: this.chainName,
148
186
  password,
149
187
  externalServerKeyShares
150
188
  });
151
- // Use PublicKey to encode signature
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 instanceof web3_js.VersionedTransaction) {
169
- // For versioned transactions, we need to sign the message directly
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 = Buffer.from(messageBytes).toString('hex');
215
+ messageToSign = toHex(messageBytes);
172
216
  } else {
173
- // For legacy transactions, serialize the message
174
217
  const messageBytes = transaction.serializeMessage();
175
- messageToSign = Buffer.from(messageBytes).toString('hex');
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
- const senderPublicKey = new web3_js.PublicKey(senderAddress);
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 = new web3_js.PublicKey(privateKey).toBuffer();
242
- const slicedBytes = decoded.slice(0, 32);
243
- return Buffer.from(slicedBytes).toString('hex');
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 privateKeyBytes = new web3_js.PublicKey(privateKey).toBuffer();
247
- const keypair = web3_js.Keypair.fromSecretKey(privateKeyBytes);
248
- const publicKeyBase58 = keypair.publicKey.toBase58();
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
- return new web3_js.PublicKey(publicKey).toBase58();
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 { SOLANA_RPC_URL, DynamicWalletClient, getExternalServerKeyShareBackupInfo, WalletOperation } from '@dynamic-labs-wallet/node';
2
- import { Connection, PublicKey, Transaction, SystemProgram, VersionedTransaction, Keypair } from '@solana/web3.js';
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
- async function getBalance({ address, rpcUrl = SOLANA_RPC_URL }) {
18
- const connection = new Connection(rpcUrl != null ? rpcUrl : 'https://api.devnet.solana.com');
19
- const balance = await connection.getBalance(new PublicKey(address));
20
- return balance;
21
- }
22
- async function createSolanaTransaction({ senderSolanaAddress, amount, to, rpcUrl = 'https://api.devnet.solana.com' }) {
23
- const connection = new Connection(rpcUrl != null ? rpcUrl : 'https://api.devnet.solana.com');
24
- const balance = await getBalance({
25
- address: senderSolanaAddress,
26
- rpcUrl
27
- });
28
- if (balance < amount * 1e9) {
29
- throw new Error('Insufficient balance');
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
- const fromPubkey = new PublicKey(senderSolanaAddress);
32
- const transaction = new Transaction().add(SystemProgram.transfer({
33
- fromPubkey: fromPubkey,
34
- toPubkey: new PublicKey(to),
35
- lamports: amount * 1e9
36
- }));
37
- const { blockhash } = await connection.getLatestBlockhash();
38
- transaction.recentBlockhash = blockhash;
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
- const addSignatureToTransaction = ({ transaction, signature, signerPublicKey })=>{
47
- transaction.addSignature(signerPublicKey, Buffer.from(signature));
48
- return transaction;
49
- };
50
- async function sendTransaction({ signedTransaction, rpcUrl = 'https://api.devnet.solana.com' }) {
51
- const connection = new Connection(rpcUrl != null ? rpcUrl : 'https://api.devnet.solana.com');
52
- const txid = await connection.sendRawTransaction(Buffer.from(signedTransaction));
53
- return txid;
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
- const pubKeyBytes = typeof rawPublicKey === 'string' ? Buffer.from(rawPublicKey, 'hex') : rawPublicKey;
119
- // Create PublicKey from bytes and convert to base58
120
- const pubKey = new PublicKey(pubKeyBytes);
121
- const accountAddress = pubKey.toBase58();
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: accountAddress,
181
+ message: messageHex,
182
+ accountAddress,
145
183
  chainName: this.chainName,
146
184
  password,
147
185
  externalServerKeyShares
148
186
  });
149
- // Use PublicKey to encode signature
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 instanceof VersionedTransaction) {
167
- // For versioned transactions, we need to sign the message directly
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 = Buffer.from(messageBytes).toString('hex');
213
+ messageToSign = toHex(messageBytes);
170
214
  } else {
171
- // For legacy transactions, serialize the message
172
215
  const messageBytes = transaction.serializeMessage();
173
- messageToSign = Buffer.from(messageBytes).toString('hex');
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
- const senderPublicKey = new PublicKey(senderAddress);
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 = new PublicKey(privateKey).toBuffer();
240
- const slicedBytes = decoded.slice(0, 32);
241
- return Buffer.from(slicedBytes).toString('hex');
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 privateKeyBytes = new PublicKey(privateKey).toBuffer();
245
- const keypair = Keypair.fromSecretKey(privateKeyBytes);
246
- const publicKeyBase58 = keypair.publicKey.toBase58();
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
- return new PublicKey(publicKey).toBase58();
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
- export { DynamicSvmWalletClient, addSignatureToTransaction, createSolanaTransaction, getBalance, sendTransaction };
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.310",
3
+ "version": "0.0.0-beta.311",
4
4
  "license": "MIT",
5
5
  "dependencies": {
6
- "@dynamic-labs-wallet/node": "0.0.0-beta.310",
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,3 @@
1
+ export declare function encodeBase58(source: Uint8Array): string;
2
+ export declare function decodeBase58(str: string): Uint8Array;
3
+ //# sourceMappingURL=base58.d.ts.map
@@ -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"}
@@ -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<VersionedTransaction | Transaction>;
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":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
1
  export * from './client';
2
2
  export * from './utils';
3
+ export * from './base58';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -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"}
@@ -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;AAEzB,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,wBAAsB,eAAe,CAAC,EACpC,iBAAiB,EACjB,MAAwC,GACzC,EAAE;IACD,iBAAiB,EAAE,UAAU,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,mBAMA"}
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"}