@dynamic-labs-wallet/node-svm 0.0.321 → 0.0.323

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.
Files changed (3) hide show
  1. package/index.cjs.js +140 -1
  2. package/index.esm.js +141 -4
  3. package/package.json +2 -2
package/index.cjs.js CHANGED
@@ -15,6 +15,7 @@ function _extends() {
15
15
  }
16
16
 
17
17
  const ERROR_CREATE_WALLET_ACCOUNT = 'Error creating svm wallet account';
18
+ const ERROR_SIGN_RAW_MESSAGE = 'Error signing raw message';
18
19
 
19
20
  // Base58 encoding/decoding (Bitcoin alphabet) without external dependencies
20
21
  // Implementation adapted from the reference algorithm; suitable for keys/signatures
@@ -209,12 +210,61 @@ class DynamicSvmWalletClient extends node.DynamicWalletClient {
209
210
  throw error;
210
211
  }
211
212
  }
213
+ /**
214
+ * Signs a pre-formatted (raw) message without additional encoding.
215
+ * The message must be a hex string representing already-hashed data.
216
+ *
217
+ * @param message The pre-formatted hex message to sign
218
+ * @param accountAddress Solana address (base58 encoded)
219
+ * @param password The password for encrypted backup shares
220
+ */ async signRawMessage({ message, accountAddress, password = undefined, externalServerKeyShares }) {
221
+ if (!accountAddress) {
222
+ throw new Error('Account address is required');
223
+ }
224
+ try {
225
+ // Attempt to recover key shares from backup if not provided
226
+ await this.ensureKeySharesRecovered({
227
+ accountAddress,
228
+ password,
229
+ walletOperation: node.WalletOperation.SIGN_MESSAGE,
230
+ externalServerKeyShares,
231
+ errorMessage: 'External server key shares are required to sign a raw message. No backup shares available for recovery.'
232
+ });
233
+ const messageHex = node.stripHexPrefix(message);
234
+ const signatureEd25519 = await this.sign({
235
+ message: messageHex,
236
+ accountAddress,
237
+ chainName: this.chainName,
238
+ password,
239
+ externalServerKeyShares,
240
+ isFormatted: true
241
+ });
242
+ return encodeBase58(signatureEd25519);
243
+ } catch (error) {
244
+ logError$1({
245
+ message: ERROR_SIGN_RAW_MESSAGE,
246
+ error: error,
247
+ context: {
248
+ accountAddress
249
+ }
250
+ });
251
+ throw new Error(ERROR_SIGN_RAW_MESSAGE);
252
+ }
253
+ }
212
254
  //todo:should txn just be a string?
213
- async signTransaction({ senderAddress, transaction, password = undefined, externalServerKeyShares }) {
255
+ async signTransaction({ senderAddress, transaction, password = undefined, externalServerKeyShares, sponsor = false }) {
214
256
  // Validate inputs early
215
257
  if (!senderAddress) {
216
258
  throw new Error('Sender address is required');
217
259
  }
260
+ if (sponsor) {
261
+ if (typeof transaction === 'string') {
262
+ throw new TypeError('sponsor=true requires a Transaction or VersionedTransaction object, not a hex string');
263
+ }
264
+ transaction = await this.sponsorTransaction({
265
+ transaction
266
+ });
267
+ }
218
268
  await this.verifyPassword({
219
269
  accountAddress: senderAddress,
220
270
  password
@@ -381,6 +431,37 @@ class DynamicSvmWalletClient extends node.DynamicWalletClient {
381
431
  externalKeySharesWithBackupStatus
382
432
  };
383
433
  }
434
+ /**
435
+ * Sponsors a Solana transaction via the Dynamic gas sponsorship API.
436
+ *
437
+ * Sends the unsigned transaction to Dynamic's sponsorTransaction endpoint,
438
+ * which replaces the fee payer with the sponsor's address. The returned
439
+ * transaction object has the sponsor as fee payer and should be passed to
440
+ * {@link signTransaction} instead of the original transaction.
441
+ *
442
+ * Gas sponsorship must be enabled for your environment in the Dynamic
443
+ * dashboard before calling this method.
444
+ *
445
+ * @param transaction - Unsigned Solana transaction (legacy or versioned).
446
+ * @returns The sponsored transaction with the gas sponsor as fee payer.
447
+ */ async sponsorTransaction({ transaction, traceContext }) {
448
+ // Serialize the unsigned transaction to base64 wire format
449
+ const serialized = transaction instanceof web3_js.VersionedTransaction ? transaction.serialize() : transaction.serialize({
450
+ requireAllSignatures: false,
451
+ verifySignatures: false
452
+ });
453
+ const txBase64 = Buffer.from(serialized).toString('base64');
454
+ const { transaction: sponsoredBase64 } = await this.apiClient.requestSvmSponsoredTransaction({
455
+ transaction: txBase64,
456
+ traceContext
457
+ });
458
+ const sponsoredBytes = Buffer.from(sponsoredBase64, 'base64');
459
+ // Deserialize back to the same transaction type
460
+ if (transaction instanceof web3_js.VersionedTransaction) {
461
+ return web3_js.VersionedTransaction.deserialize(sponsoredBytes);
462
+ }
463
+ return web3_js.Transaction.from(sponsoredBytes);
464
+ }
384
465
  async getSvmWallets() {
385
466
  const wallets = await this.getWallets();
386
467
  const svmWallets = wallets.filter((wallet)=>wallet.chainName === 'solana');
@@ -589,6 +670,62 @@ const logError = node.createLogError('node-svm');
589
670
  throw error;
590
671
  }
591
672
  };
673
+ /**
674
+ * Signs a pre-formatted (raw) message using delegated signing for SVM.
675
+ * The message must be a hex string representing already-hashed data.
676
+ *
677
+ * @param client - The delegated SVM wallet client
678
+ * @param options - Signing options
679
+ * @param options.walletId - The wallet ID
680
+ * @param options.walletApiKey - The wallet API key
681
+ * @param options.keyShare - The server key share
682
+ * @param options.message - Hex string of the already-hashed data to sign
683
+ *
684
+ * @returns The base58-encoded signature
685
+ *
686
+ * @example
687
+ * // Sign a pre-hashed message
688
+ * const signature = await delegatedSignRawMessage(client, {
689
+ * walletId,
690
+ * walletApiKey,
691
+ * keyShare,
692
+ * message: preHashedHex, // hex string of already-hashed data
693
+ * });
694
+ *
695
+ * @example
696
+ * // Sign a SHA-256 hash derived from arbitrary application data
697
+ * import { createHash } from 'crypto';
698
+ * const hash = createHash('sha256').update('my application data').digest('hex');
699
+ * const signature = await delegatedSignRawMessage(client, {
700
+ * walletId,
701
+ * walletApiKey,
702
+ * keyShare,
703
+ * message: hash,
704
+ * });
705
+ *
706
+ */ const delegatedSignRawMessage = async (client, { walletId, walletApiKey, keyShare, message })=>{
707
+ if (!keyShare || !walletId || !walletApiKey) {
708
+ throw new Error('Delegated key share, wallet ID, and wallet API key are required to sign a raw message');
709
+ }
710
+ try {
711
+ return await delegatedSignMessage(client, {
712
+ walletId,
713
+ walletApiKey,
714
+ keyShare,
715
+ message: node.stripHexPrefix(message),
716
+ isFormatted: true
717
+ });
718
+ } catch (error) {
719
+ logError({
720
+ message: 'Error in delegatedSignRawMessage',
721
+ error: error,
722
+ context: {
723
+ walletId
724
+ }
725
+ });
726
+ throw error;
727
+ }
728
+ };
592
729
  /**
593
730
  * Revoke delegation - delegates to the node package
594
731
  */ const revokeDelegation = async (client, params)=>{
@@ -597,12 +734,14 @@ const logError = node.createLogError('node-svm');
597
734
 
598
735
  exports.DynamicSvmWalletClient = DynamicSvmWalletClient;
599
736
  exports.ERROR_CREATE_WALLET_ACCOUNT = ERROR_CREATE_WALLET_ACCOUNT;
737
+ exports.ERROR_SIGN_RAW_MESSAGE = ERROR_SIGN_RAW_MESSAGE;
600
738
  exports.addSignatureToTransaction = addSignatureToTransaction;
601
739
  exports.attachSignature = attachSignature;
602
740
  exports.createDelegatedSvmWalletClient = createDelegatedSvmWalletClient;
603
741
  exports.createSolanaTransaction = createSolanaTransaction;
604
742
  exports.decodeBase58 = decodeBase58;
605
743
  exports.delegatedSignMessage = delegatedSignMessage;
744
+ exports.delegatedSignRawMessage = delegatedSignRawMessage;
606
745
  exports.delegatedSignTransaction = delegatedSignTransaction;
607
746
  exports.encodeBase58 = encodeBase58;
608
747
  exports.getBalance = getBalance;
package/index.esm.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createLogError, DynamicWalletClient, getMPCChainConfig, getExternalServerKeyShareBackupInfo, WalletOperation, SOLANA_RPC_URL, createDelegatedWalletClient, delegatedSignMessage as delegatedSignMessage$1, revokeDelegation as revokeDelegation$1 } from '@dynamic-labs-wallet/node';
2
- import { PublicKey, VersionedTransaction, Keypair, Connection, Transaction, SystemProgram } from '@solana/web3.js';
1
+ import { createLogError, DynamicWalletClient, getMPCChainConfig, getExternalServerKeyShareBackupInfo, WalletOperation, stripHexPrefix, SOLANA_RPC_URL, createDelegatedWalletClient, delegatedSignMessage as delegatedSignMessage$1, revokeDelegation as revokeDelegation$1 } from '@dynamic-labs-wallet/node';
2
+ import { PublicKey, VersionedTransaction, Keypair, Transaction, Connection, SystemProgram } from '@solana/web3.js';
3
3
 
4
4
  function _extends() {
5
5
  _extends = Object.assign || function assign(target) {
@@ -13,6 +13,7 @@ function _extends() {
13
13
  }
14
14
 
15
15
  const ERROR_CREATE_WALLET_ACCOUNT = 'Error creating svm wallet account';
16
+ const ERROR_SIGN_RAW_MESSAGE = 'Error signing raw message';
16
17
 
17
18
  // Base58 encoding/decoding (Bitcoin alphabet) without external dependencies
18
19
  // Implementation adapted from the reference algorithm; suitable for keys/signatures
@@ -207,12 +208,61 @@ class DynamicSvmWalletClient extends DynamicWalletClient {
207
208
  throw error;
208
209
  }
209
210
  }
211
+ /**
212
+ * Signs a pre-formatted (raw) message without additional encoding.
213
+ * The message must be a hex string representing already-hashed data.
214
+ *
215
+ * @param message The pre-formatted hex message to sign
216
+ * @param accountAddress Solana address (base58 encoded)
217
+ * @param password The password for encrypted backup shares
218
+ */ async signRawMessage({ message, accountAddress, password = undefined, externalServerKeyShares }) {
219
+ if (!accountAddress) {
220
+ throw new Error('Account address is required');
221
+ }
222
+ try {
223
+ // Attempt to recover key shares from backup if not provided
224
+ await this.ensureKeySharesRecovered({
225
+ accountAddress,
226
+ password,
227
+ walletOperation: WalletOperation.SIGN_MESSAGE,
228
+ externalServerKeyShares,
229
+ errorMessage: 'External server key shares are required to sign a raw message. No backup shares available for recovery.'
230
+ });
231
+ const messageHex = stripHexPrefix(message);
232
+ const signatureEd25519 = await this.sign({
233
+ message: messageHex,
234
+ accountAddress,
235
+ chainName: this.chainName,
236
+ password,
237
+ externalServerKeyShares,
238
+ isFormatted: true
239
+ });
240
+ return encodeBase58(signatureEd25519);
241
+ } catch (error) {
242
+ logError$1({
243
+ message: ERROR_SIGN_RAW_MESSAGE,
244
+ error: error,
245
+ context: {
246
+ accountAddress
247
+ }
248
+ });
249
+ throw new Error(ERROR_SIGN_RAW_MESSAGE);
250
+ }
251
+ }
210
252
  //todo:should txn just be a string?
211
- async signTransaction({ senderAddress, transaction, password = undefined, externalServerKeyShares }) {
253
+ async signTransaction({ senderAddress, transaction, password = undefined, externalServerKeyShares, sponsor = false }) {
212
254
  // Validate inputs early
213
255
  if (!senderAddress) {
214
256
  throw new Error('Sender address is required');
215
257
  }
258
+ if (sponsor) {
259
+ if (typeof transaction === 'string') {
260
+ throw new TypeError('sponsor=true requires a Transaction or VersionedTransaction object, not a hex string');
261
+ }
262
+ transaction = await this.sponsorTransaction({
263
+ transaction
264
+ });
265
+ }
216
266
  await this.verifyPassword({
217
267
  accountAddress: senderAddress,
218
268
  password
@@ -379,6 +429,37 @@ class DynamicSvmWalletClient extends DynamicWalletClient {
379
429
  externalKeySharesWithBackupStatus
380
430
  };
381
431
  }
432
+ /**
433
+ * Sponsors a Solana transaction via the Dynamic gas sponsorship API.
434
+ *
435
+ * Sends the unsigned transaction to Dynamic's sponsorTransaction endpoint,
436
+ * which replaces the fee payer with the sponsor's address. The returned
437
+ * transaction object has the sponsor as fee payer and should be passed to
438
+ * {@link signTransaction} instead of the original transaction.
439
+ *
440
+ * Gas sponsorship must be enabled for your environment in the Dynamic
441
+ * dashboard before calling this method.
442
+ *
443
+ * @param transaction - Unsigned Solana transaction (legacy or versioned).
444
+ * @returns The sponsored transaction with the gas sponsor as fee payer.
445
+ */ async sponsorTransaction({ transaction, traceContext }) {
446
+ // Serialize the unsigned transaction to base64 wire format
447
+ const serialized = transaction instanceof VersionedTransaction ? transaction.serialize() : transaction.serialize({
448
+ requireAllSignatures: false,
449
+ verifySignatures: false
450
+ });
451
+ const txBase64 = Buffer.from(serialized).toString('base64');
452
+ const { transaction: sponsoredBase64 } = await this.apiClient.requestSvmSponsoredTransaction({
453
+ transaction: txBase64,
454
+ traceContext
455
+ });
456
+ const sponsoredBytes = Buffer.from(sponsoredBase64, 'base64');
457
+ // Deserialize back to the same transaction type
458
+ if (transaction instanceof VersionedTransaction) {
459
+ return VersionedTransaction.deserialize(sponsoredBytes);
460
+ }
461
+ return Transaction.from(sponsoredBytes);
462
+ }
382
463
  async getSvmWallets() {
383
464
  const wallets = await this.getWallets();
384
465
  const svmWallets = wallets.filter((wallet)=>wallet.chainName === 'solana');
@@ -587,10 +668,66 @@ const logError = createLogError('node-svm');
587
668
  throw error;
588
669
  }
589
670
  };
671
+ /**
672
+ * Signs a pre-formatted (raw) message using delegated signing for SVM.
673
+ * The message must be a hex string representing already-hashed data.
674
+ *
675
+ * @param client - The delegated SVM wallet client
676
+ * @param options - Signing options
677
+ * @param options.walletId - The wallet ID
678
+ * @param options.walletApiKey - The wallet API key
679
+ * @param options.keyShare - The server key share
680
+ * @param options.message - Hex string of the already-hashed data to sign
681
+ *
682
+ * @returns The base58-encoded signature
683
+ *
684
+ * @example
685
+ * // Sign a pre-hashed message
686
+ * const signature = await delegatedSignRawMessage(client, {
687
+ * walletId,
688
+ * walletApiKey,
689
+ * keyShare,
690
+ * message: preHashedHex, // hex string of already-hashed data
691
+ * });
692
+ *
693
+ * @example
694
+ * // Sign a SHA-256 hash derived from arbitrary application data
695
+ * import { createHash } from 'crypto';
696
+ * const hash = createHash('sha256').update('my application data').digest('hex');
697
+ * const signature = await delegatedSignRawMessage(client, {
698
+ * walletId,
699
+ * walletApiKey,
700
+ * keyShare,
701
+ * message: hash,
702
+ * });
703
+ *
704
+ */ const delegatedSignRawMessage = async (client, { walletId, walletApiKey, keyShare, message })=>{
705
+ if (!keyShare || !walletId || !walletApiKey) {
706
+ throw new Error('Delegated key share, wallet ID, and wallet API key are required to sign a raw message');
707
+ }
708
+ try {
709
+ return await delegatedSignMessage(client, {
710
+ walletId,
711
+ walletApiKey,
712
+ keyShare,
713
+ message: stripHexPrefix(message),
714
+ isFormatted: true
715
+ });
716
+ } catch (error) {
717
+ logError({
718
+ message: 'Error in delegatedSignRawMessage',
719
+ error: error,
720
+ context: {
721
+ walletId
722
+ }
723
+ });
724
+ throw error;
725
+ }
726
+ };
590
727
  /**
591
728
  * Revoke delegation - delegates to the node package
592
729
  */ const revokeDelegation = async (client, params)=>{
593
730
  return revokeDelegation$1(client, params);
594
731
  };
595
732
 
596
- export { DynamicSvmWalletClient, ERROR_CREATE_WALLET_ACCOUNT, addSignatureToTransaction, attachSignature, createDelegatedSvmWalletClient, createSolanaTransaction, decodeBase58, delegatedSignMessage, delegatedSignTransaction, encodeBase58, getBalance, revokeDelegation, sendTransaction };
733
+ export { DynamicSvmWalletClient, ERROR_CREATE_WALLET_ACCOUNT, ERROR_SIGN_RAW_MESSAGE, addSignatureToTransaction, attachSignature, createDelegatedSvmWalletClient, createSolanaTransaction, decodeBase58, delegatedSignMessage, delegatedSignRawMessage, delegatedSignTransaction, encodeBase58, getBalance, revokeDelegation, sendTransaction };
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@dynamic-labs-wallet/node-svm",
3
- "version": "0.0.321",
3
+ "version": "0.0.323",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "dependencies": {
7
- "@dynamic-labs-wallet/node": "0.0.321",
7
+ "@dynamic-labs-wallet/node": "0.0.323",
8
8
  "@solana/web3.js": "^1.98.2"
9
9
  },
10
10
  "publishConfig": {