@dynamic-labs-wallet/node-svm 1.1.51 → 1.1.53

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 CHANGED
@@ -84,6 +84,29 @@ function decodeBase58(str) {
84
84
  return out;
85
85
  }
86
86
 
87
+ function _object_without_properties_loose(source, excluded) {
88
+ if (source == null) return {};
89
+ var target = {};
90
+ var sourceKeys = Object.keys(source);
91
+ var key, i;
92
+ for(i = 0; i < sourceKeys.length; i++){
93
+ key = sourceKeys[i];
94
+ if (excluded.indexOf(key) >= 0) continue;
95
+ target[key] = source[key];
96
+ }
97
+ return target;
98
+ }
99
+
100
+ const ERROR_EARN_NOT_SVM_WALLET = 'Wallet metadata is not for an SVM wallet';
101
+ const ERROR_EARN_MISSING_TRANSACTIONS = 'Earn signing payload is missing the Solana transaction sequence';
102
+ const ERROR_EARN_NON_CONTIGUOUS_ORDINALS = 'Earn signing payload transaction ordinals must be contiguous from zero';
103
+ /** Default RPC per Dynamic Solana chain id (101 mainnet, 103 devnet); `rpcUrl` overrides. */ const EARN_SOLANA_RPC_BY_CHAIN_ID = {
104
+ '101': 'https://api.mainnet-beta.solana.com',
105
+ '103': 'https://api.devnet.solana.com'
106
+ };
107
+ /** Blockhash checks run at `processed`, the commitment that sees new blockhashes first. */ const EARN_SOLANA_BLOCKHASH_COMMITMENT = 'processed';
108
+ const EARN_CONFIRMATION_POLL_INTERVAL_MS = 1000;
109
+
87
110
  async function getBalance({ address, rpcUrl = node.SOLANA_RPC_URL }) {
88
111
  const connection = new web3_js.Connection(rpcUrl != null ? rpcUrl : 'https://api.devnet.solana.com');
89
112
  const balance = await connection.getBalance(new web3_js.PublicKey(address));
@@ -132,6 +155,300 @@ async function sendTransaction({ signedTransaction, rpcUrl = 'https://api.devnet
132
155
  return txid;
133
156
  }
134
157
 
158
+ /**
159
+ * A transaction in the sequence could not land. Transactions before `ordinal` already
160
+ * confirmed on-chain, so the action may be partially applied.
161
+ */ class EarnSolanaStepError extends Error {
162
+ constructor({ cause, code, ordinal, reason, signature, total }){
163
+ const message = `Transaction ${ordinal + 1} of ${total} ${reason}.`;
164
+ const confirmed = ordinal === 1 ? 'transaction' : `${ordinal} transactions`;
165
+ super(ordinal === 0 ? message : `${message} The earlier ${confirmed} already confirmed on-chain, so the action is partially applied; check your position before retrying.`, {
166
+ cause
167
+ });
168
+ this.name = 'EarnSolanaStepError';
169
+ this.code = code;
170
+ this.ordinal = ordinal;
171
+ this.signature = signature;
172
+ }
173
+ }
174
+ // Versioned transactions are marked by the high bit on the first message byte after the
175
+ // signatures; legacy transactions start the message header there instead.
176
+ const deserializeEarnTransaction = (payload)=>{
177
+ const bytes = new Uint8Array(Buffer.from(payload, 'base64'));
178
+ var _bytes_;
179
+ const signatureCount = (_bytes_ = bytes[0]) != null ? _bytes_ : 0;
180
+ var _bytes_1;
181
+ const firstMessageByte = (_bytes_1 = bytes[1 + signatureCount * 64]) != null ? _bytes_1 : 0;
182
+ return (firstMessageByte & 0x80) !== 0 ? web3_js.VersionedTransaction.deserialize(bytes) : web3_js.Transaction.from(bytes);
183
+ };
184
+ const isStepExpired = async ({ connection, expiryBlockHeight, transaction })=>{
185
+ if (expiryBlockHeight !== undefined) {
186
+ return await connection.getBlockHeight('confirmed') > expiryBlockHeight;
187
+ }
188
+ const recentBlockhash = transaction instanceof web3_js.VersionedTransaction ? transaction.message.recentBlockhash : transaction.recentBlockhash;
189
+ // A transaction with no blockhash cannot be judged; the network rejects it on send anyway.
190
+ if (!recentBlockhash) {
191
+ return false;
192
+ }
193
+ const { value: isBlockhashValid } = await connection.isBlockhashValid(recentBlockhash, {
194
+ commitment: EARN_SOLANA_BLOCKHASH_COMMITMENT
195
+ });
196
+ return !isBlockhashValid;
197
+ };
198
+ // Polls over HTTP rather than a websocket subscription, which some RPC providers refuse.
199
+ const waitForConfirmation = async ({ connection, isExpired, signature })=>{
200
+ const hasExpired = await isExpired();
201
+ // A missing status only means "never landed" once the blockhash lapsed, so the
202
+ // lookup then needs the full transaction history rather than the recent cache.
203
+ const { value: [status] } = await connection.getSignatureStatuses([
204
+ signature
205
+ ], {
206
+ searchTransactionHistory: hasExpired
207
+ });
208
+ if (status == null ? void 0 : status.err) {
209
+ return {
210
+ error: status.err,
211
+ outcome: 'failed'
212
+ };
213
+ }
214
+ if ((status == null ? void 0 : status.confirmationStatus) === 'confirmed' || (status == null ? void 0 : status.confirmationStatus) === 'finalized') {
215
+ return {
216
+ outcome: 'confirmed'
217
+ };
218
+ }
219
+ if (hasExpired && !status) {
220
+ return {
221
+ outcome: 'expired'
222
+ };
223
+ }
224
+ await new Promise((resolve)=>{
225
+ setTimeout(resolve, EARN_CONFIRMATION_POLL_INTERVAL_MS);
226
+ });
227
+ return waitForConfirmation({
228
+ connection,
229
+ isExpired,
230
+ signature
231
+ });
232
+ };
233
+ const serializeSigned = (transaction)=>Buffer.from(transaction instanceof web3_js.VersionedTransaction ? transaction.serialize() : transaction.serialize({
234
+ verifySignatures: false
235
+ }));
236
+ // The transaction id is its first (fee payer) signature — for sponsored steps that is
237
+ // the sponsor's, not the wallet's — so the send result, not the signature
238
+ // `signTransaction` returned, is what must be confirmed.
239
+ const signAndSendStep = async ({ chainId, connection, context, externalServerKeyShares, ordinal, password, signTransaction, sponsored, total, transaction, walletMetadata })=>{
240
+ try {
241
+ const result = await signTransaction({
242
+ walletMetadata,
243
+ transaction,
244
+ password,
245
+ externalServerKeyShares,
246
+ sponsor: sponsored != null ? sponsored : false,
247
+ chainId,
248
+ context
249
+ });
250
+ const signedTransaction = typeof result === 'string' ? attachSignature({
251
+ transaction,
252
+ signatureBase58: result,
253
+ senderAddress: walletMetadata.accountAddress
254
+ }) : result.transaction;
255
+ return await connection.sendRawTransaction(serializeSigned(signedTransaction), {
256
+ preflightCommitment: EARN_SOLANA_BLOCKHASH_COMMITMENT
257
+ });
258
+ } catch (error) {
259
+ throw new EarnSolanaStepError({
260
+ cause: error instanceof Error ? error : undefined,
261
+ code: 'yield_step_send_failed',
262
+ ordinal,
263
+ reason: 'could not be signed or sent',
264
+ total
265
+ });
266
+ }
267
+ };
268
+ /**
269
+ * Builds a stateless Earn client for Solana vaults: it prepares vault actions through the
270
+ * Dynamic API and executes the returned ordered transaction sequence one transaction at a
271
+ * time — never in parallel, because a later transaction can depend on the state an earlier
272
+ * one wrote. If one fails, the sequence stops and the thrown {@link EarnSolanaStepError}
273
+ * reports how much of the action already landed.
274
+ *
275
+ * Gas sponsorship is opt-in (`sponsored: true`): vault transactions are built with the
276
+ * owner as fee payer, unlike the browser SDK which sponsors automatically.
277
+ */ const createSvmEarnClient = ({ getEarnClient, signTransaction })=>{
278
+ const executeStep = async ({ connection, context, externalServerKeyShares, chainId, onStepChange, onTransactionStepChange, password, sponsored, step, total, walletMetadata })=>{
279
+ const transaction = deserializeEarnTransaction(step.payload);
280
+ const { ordinal } = step;
281
+ const isExpired = ()=>isStepExpired({
282
+ connection,
283
+ expiryBlockHeight: step.expiryBlockHeight,
284
+ transaction
285
+ });
286
+ // The blockhash was fixed when the payload was built, so earlier slow steps can
287
+ // leave later ones unsendable — check before spending a signature.
288
+ if (await isExpired()) {
289
+ throw new EarnSolanaStepError({
290
+ code: 'yield_step_expired',
291
+ ordinal,
292
+ reason: 'expired before it could be signed',
293
+ total
294
+ });
295
+ }
296
+ onStepChange == null ? void 0 : onStepChange('transaction');
297
+ const progress = {
298
+ ordinal,
299
+ total
300
+ };
301
+ if (step.label !== undefined) {
302
+ progress.label = step.label;
303
+ }
304
+ onTransactionStepChange == null ? void 0 : onTransactionStepChange(progress);
305
+ const signature = await signAndSendStep({
306
+ chainId,
307
+ connection,
308
+ context,
309
+ externalServerKeyShares,
310
+ ordinal,
311
+ password,
312
+ signTransaction,
313
+ sponsored,
314
+ total,
315
+ transaction,
316
+ walletMetadata
317
+ });
318
+ // A sent transaction may still land even when the poll itself fails, so wrap any RPC
319
+ // error and keep the send signature on it for reconciliation.
320
+ let confirmation;
321
+ try {
322
+ confirmation = await waitForConfirmation({
323
+ connection,
324
+ isExpired,
325
+ signature
326
+ });
327
+ } catch (error) {
328
+ throw new EarnSolanaStepError({
329
+ cause: error instanceof Error ? error : undefined,
330
+ code: 'yield_step_confirmation_unknown',
331
+ ordinal,
332
+ reason: 'was sent but its confirmation could not be checked',
333
+ signature,
334
+ total
335
+ });
336
+ }
337
+ if (confirmation.outcome === 'failed') {
338
+ throw new EarnSolanaStepError({
339
+ code: 'yield_step_failed',
340
+ ordinal,
341
+ reason: `failed on-chain: ${JSON.stringify(confirmation.error)}`,
342
+ signature,
343
+ total
344
+ });
345
+ }
346
+ if (confirmation.outcome === 'expired') {
347
+ throw new EarnSolanaStepError({
348
+ code: 'yield_step_expired',
349
+ ordinal,
350
+ reason: 'was not confirmed before its blockhash expired',
351
+ signature,
352
+ total
353
+ });
354
+ }
355
+ return signature;
356
+ };
357
+ const executeEarnTransaction = async ({ signingPayload, walletMetadata, password, externalServerKeyShares, sponsored, rpcUrl, chainId, context, onStepChange, onTransactionStepChange })=>{
358
+ if (walletMetadata.chainName !== 'SVM') {
359
+ throw new Error(ERROR_EARN_NOT_SVM_WALLET);
360
+ }
361
+ if (!walletMetadata.accountAddress) {
362
+ throw new Error('Account address is required');
363
+ }
364
+ var _signingPayload_transactions;
365
+ const steps = [
366
+ ...(_signingPayload_transactions = signingPayload.transactions) != null ? _signingPayload_transactions : []
367
+ ].sort((first, second)=>first.ordinal - second.ordinal);
368
+ if (steps.length === 0) {
369
+ throw new Error(ERROR_EARN_MISSING_TRANSACTIONS);
370
+ }
371
+ if (steps.some((step, index)=>step.ordinal !== index)) {
372
+ throw new Error(ERROR_EARN_NON_CONTIGUOUS_ORDINALS);
373
+ }
374
+ var _ref;
375
+ const connection = new web3_js.Connection((_ref = rpcUrl != null ? rpcUrl : EARN_SOLANA_RPC_BY_CHAIN_ID[chainId != null ? chainId : signingPayload.chainId]) != null ? _ref : node.SOLANA_RPC_URL);
376
+ // A rejection stops the sequence at the first failure; everything after it is skipped.
377
+ const transactionHash = await steps.reduce(async (previousStep, step)=>{
378
+ await previousStep;
379
+ return executeStep({
380
+ connection,
381
+ context,
382
+ externalServerKeyShares,
383
+ chainId: chainId != null ? chainId : signingPayload.chainId,
384
+ onStepChange,
385
+ onTransactionStepChange,
386
+ password,
387
+ sponsored,
388
+ step,
389
+ total: steps.length,
390
+ walletMetadata
391
+ });
392
+ }, Promise.resolve(''));
393
+ return {
394
+ transactionHash
395
+ };
396
+ };
397
+ return {
398
+ executeEarnTransaction,
399
+ depositToEarnVault: async (_param)=>{
400
+ var { vaultId, amount, receiverAddress, traceContext } = _param, walletParams = _object_without_properties_loose(_param, [
401
+ "vaultId",
402
+ "amount",
403
+ "receiverAddress",
404
+ "traceContext"
405
+ ]);
406
+ const signingPayload = await getEarnClient().prepareDeposit({
407
+ vaultId,
408
+ ownerAddress: walletParams.walletMetadata.accountAddress,
409
+ amount,
410
+ receiverAddress,
411
+ traceContext
412
+ });
413
+ return executeEarnTransaction(_extends({
414
+ signingPayload
415
+ }, walletParams));
416
+ },
417
+ withdrawFromEarnVault: async (_param)=>{
418
+ var { vaultId, amount, receiverAddress, traceContext } = _param, walletParams = _object_without_properties_loose(_param, [
419
+ "vaultId",
420
+ "amount",
421
+ "receiverAddress",
422
+ "traceContext"
423
+ ]);
424
+ const signingPayload = await getEarnClient().prepareWithdrawal({
425
+ vaultId,
426
+ ownerAddress: walletParams.walletMetadata.accountAddress,
427
+ amount,
428
+ receiverAddress,
429
+ traceContext
430
+ });
431
+ return executeEarnTransaction(_extends({
432
+ signingPayload
433
+ }, walletParams));
434
+ },
435
+ claimEarnRewards: async (_param)=>{
436
+ var { vaultId, traceContext } = _param, walletParams = _object_without_properties_loose(_param, [
437
+ "vaultId",
438
+ "traceContext"
439
+ ]);
440
+ const signingPayload = await getEarnClient().prepareClaimRewards({
441
+ vaultId,
442
+ ownerAddress: walletParams.walletMetadata.accountAddress,
443
+ traceContext
444
+ });
445
+ return executeEarnTransaction(_extends({
446
+ signingPayload
447
+ }, walletParams));
448
+ }
449
+ };
450
+ };
451
+
135
452
  const logError$1 = node.createLogError('node-svm');
136
453
  // Helper: normalize bytes to hex without triggering Buffer.from(string, encoding) overload
137
454
  const toHex = (bytes)=>(Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes)).toString('hex');
@@ -563,6 +880,30 @@ class DynamicSvmWalletClient extends node.DynamicWalletClient {
563
880
  const svmWallets = wallets.filter((wallet)=>wallet.chainName === 'SVM');
564
881
  return svmWallets;
565
882
  }
883
+ // ---------------------------------------------------------------------------
884
+ // Earn (yield vaults)
885
+ // ---------------------------------------------------------------------------
886
+ get svmEarn() {
887
+ return createSvmEarnClient({
888
+ getEarnClient: ()=>this.earn,
889
+ signTransaction: (args)=>this.signTransaction(args)
890
+ });
891
+ }
892
+ /**
893
+ * Executes an Earn signing payload returned by the prepare* methods, signing and
894
+ * sending its ordered Solana transaction sequence one transaction at a time.
895
+ */ async executeEarnTransaction(params) {
896
+ return this.svmEarn.executeEarnTransaction(params);
897
+ }
898
+ /** Prepares and executes a deposit into an Earn vault. */ async depositToEarnVault(params) {
899
+ return this.svmEarn.depositToEarnVault(params);
900
+ }
901
+ /** Prepares and executes a withdrawal from an Earn vault. */ async withdrawFromEarnVault(params) {
902
+ return this.svmEarn.withdrawFromEarnVault(params);
903
+ }
904
+ /** Prepares and executes a claim of accrued Earn vault rewards. */ async claimEarnRewards(params) {
905
+ return this.svmEarn.claimEarnRewards(params);
906
+ }
566
907
  constructor({ environmentId, baseApiUrl, baseMPCRelayApiUrl, enableMPCAccelerator, logger, debug }){
567
908
  super({
568
909
  environmentId,
@@ -883,10 +1224,12 @@ exports.ERROR_DELEGATED_SIGN_TRANSACTION_CONTEXT_REQUIRED = ERROR_DELEGATED_SIGN
883
1224
  exports.ERROR_SIGN_RAW_MESSAGE = ERROR_SIGN_RAW_MESSAGE;
884
1225
  exports.ERROR_SIGN_TRANSACTION_CONTEXT_REQUIRED = ERROR_SIGN_TRANSACTION_CONTEXT_REQUIRED;
885
1226
  exports.ERROR_SIGN_TRANSACTION_RAW_HEX_CONTEXT_REQUIRED = ERROR_SIGN_TRANSACTION_RAW_HEX_CONTEXT_REQUIRED;
1227
+ exports.EarnSolanaStepError = EarnSolanaStepError;
886
1228
  exports.addSignatureToTransaction = addSignatureToTransaction;
887
1229
  exports.attachSignature = attachSignature;
888
1230
  exports.createDelegatedSvmWalletClient = createDelegatedSvmWalletClient;
889
1231
  exports.createSolanaTransaction = createSolanaTransaction;
1232
+ exports.createSvmEarnClient = createSvmEarnClient;
890
1233
  exports.decodeBase58 = decodeBase58;
891
1234
  exports.delegatedSignMessage = delegatedSignMessage;
892
1235
  exports.delegatedSignRawMessage = delegatedSignRawMessage;
package/index.esm.js CHANGED
@@ -82,6 +82,29 @@ function decodeBase58(str) {
82
82
  return out;
83
83
  }
84
84
 
85
+ function _object_without_properties_loose(source, excluded) {
86
+ if (source == null) return {};
87
+ var target = {};
88
+ var sourceKeys = Object.keys(source);
89
+ var key, i;
90
+ for(i = 0; i < sourceKeys.length; i++){
91
+ key = sourceKeys[i];
92
+ if (excluded.indexOf(key) >= 0) continue;
93
+ target[key] = source[key];
94
+ }
95
+ return target;
96
+ }
97
+
98
+ const ERROR_EARN_NOT_SVM_WALLET = 'Wallet metadata is not for an SVM wallet';
99
+ const ERROR_EARN_MISSING_TRANSACTIONS = 'Earn signing payload is missing the Solana transaction sequence';
100
+ const ERROR_EARN_NON_CONTIGUOUS_ORDINALS = 'Earn signing payload transaction ordinals must be contiguous from zero';
101
+ /** Default RPC per Dynamic Solana chain id (101 mainnet, 103 devnet); `rpcUrl` overrides. */ const EARN_SOLANA_RPC_BY_CHAIN_ID = {
102
+ '101': 'https://api.mainnet-beta.solana.com',
103
+ '103': 'https://api.devnet.solana.com'
104
+ };
105
+ /** Blockhash checks run at `processed`, the commitment that sees new blockhashes first. */ const EARN_SOLANA_BLOCKHASH_COMMITMENT = 'processed';
106
+ const EARN_CONFIRMATION_POLL_INTERVAL_MS = 1000;
107
+
85
108
  async function getBalance({ address, rpcUrl = SOLANA_RPC_URL }) {
86
109
  const connection = new Connection(rpcUrl != null ? rpcUrl : 'https://api.devnet.solana.com');
87
110
  const balance = await connection.getBalance(new PublicKey(address));
@@ -130,6 +153,300 @@ async function sendTransaction({ signedTransaction, rpcUrl = 'https://api.devnet
130
153
  return txid;
131
154
  }
132
155
 
156
+ /**
157
+ * A transaction in the sequence could not land. Transactions before `ordinal` already
158
+ * confirmed on-chain, so the action may be partially applied.
159
+ */ class EarnSolanaStepError extends Error {
160
+ constructor({ cause, code, ordinal, reason, signature, total }){
161
+ const message = `Transaction ${ordinal + 1} of ${total} ${reason}.`;
162
+ const confirmed = ordinal === 1 ? 'transaction' : `${ordinal} transactions`;
163
+ super(ordinal === 0 ? message : `${message} The earlier ${confirmed} already confirmed on-chain, so the action is partially applied; check your position before retrying.`, {
164
+ cause
165
+ });
166
+ this.name = 'EarnSolanaStepError';
167
+ this.code = code;
168
+ this.ordinal = ordinal;
169
+ this.signature = signature;
170
+ }
171
+ }
172
+ // Versioned transactions are marked by the high bit on the first message byte after the
173
+ // signatures; legacy transactions start the message header there instead.
174
+ const deserializeEarnTransaction = (payload)=>{
175
+ const bytes = new Uint8Array(Buffer.from(payload, 'base64'));
176
+ var _bytes_;
177
+ const signatureCount = (_bytes_ = bytes[0]) != null ? _bytes_ : 0;
178
+ var _bytes_1;
179
+ const firstMessageByte = (_bytes_1 = bytes[1 + signatureCount * 64]) != null ? _bytes_1 : 0;
180
+ return (firstMessageByte & 0x80) !== 0 ? VersionedTransaction.deserialize(bytes) : Transaction.from(bytes);
181
+ };
182
+ const isStepExpired = async ({ connection, expiryBlockHeight, transaction })=>{
183
+ if (expiryBlockHeight !== undefined) {
184
+ return await connection.getBlockHeight('confirmed') > expiryBlockHeight;
185
+ }
186
+ const recentBlockhash = transaction instanceof VersionedTransaction ? transaction.message.recentBlockhash : transaction.recentBlockhash;
187
+ // A transaction with no blockhash cannot be judged; the network rejects it on send anyway.
188
+ if (!recentBlockhash) {
189
+ return false;
190
+ }
191
+ const { value: isBlockhashValid } = await connection.isBlockhashValid(recentBlockhash, {
192
+ commitment: EARN_SOLANA_BLOCKHASH_COMMITMENT
193
+ });
194
+ return !isBlockhashValid;
195
+ };
196
+ // Polls over HTTP rather than a websocket subscription, which some RPC providers refuse.
197
+ const waitForConfirmation = async ({ connection, isExpired, signature })=>{
198
+ const hasExpired = await isExpired();
199
+ // A missing status only means "never landed" once the blockhash lapsed, so the
200
+ // lookup then needs the full transaction history rather than the recent cache.
201
+ const { value: [status] } = await connection.getSignatureStatuses([
202
+ signature
203
+ ], {
204
+ searchTransactionHistory: hasExpired
205
+ });
206
+ if (status == null ? void 0 : status.err) {
207
+ return {
208
+ error: status.err,
209
+ outcome: 'failed'
210
+ };
211
+ }
212
+ if ((status == null ? void 0 : status.confirmationStatus) === 'confirmed' || (status == null ? void 0 : status.confirmationStatus) === 'finalized') {
213
+ return {
214
+ outcome: 'confirmed'
215
+ };
216
+ }
217
+ if (hasExpired && !status) {
218
+ return {
219
+ outcome: 'expired'
220
+ };
221
+ }
222
+ await new Promise((resolve)=>{
223
+ setTimeout(resolve, EARN_CONFIRMATION_POLL_INTERVAL_MS);
224
+ });
225
+ return waitForConfirmation({
226
+ connection,
227
+ isExpired,
228
+ signature
229
+ });
230
+ };
231
+ const serializeSigned = (transaction)=>Buffer.from(transaction instanceof VersionedTransaction ? transaction.serialize() : transaction.serialize({
232
+ verifySignatures: false
233
+ }));
234
+ // The transaction id is its first (fee payer) signature — for sponsored steps that is
235
+ // the sponsor's, not the wallet's — so the send result, not the signature
236
+ // `signTransaction` returned, is what must be confirmed.
237
+ const signAndSendStep = async ({ chainId, connection, context, externalServerKeyShares, ordinal, password, signTransaction, sponsored, total, transaction, walletMetadata })=>{
238
+ try {
239
+ const result = await signTransaction({
240
+ walletMetadata,
241
+ transaction,
242
+ password,
243
+ externalServerKeyShares,
244
+ sponsor: sponsored != null ? sponsored : false,
245
+ chainId,
246
+ context
247
+ });
248
+ const signedTransaction = typeof result === 'string' ? attachSignature({
249
+ transaction,
250
+ signatureBase58: result,
251
+ senderAddress: walletMetadata.accountAddress
252
+ }) : result.transaction;
253
+ return await connection.sendRawTransaction(serializeSigned(signedTransaction), {
254
+ preflightCommitment: EARN_SOLANA_BLOCKHASH_COMMITMENT
255
+ });
256
+ } catch (error) {
257
+ throw new EarnSolanaStepError({
258
+ cause: error instanceof Error ? error : undefined,
259
+ code: 'yield_step_send_failed',
260
+ ordinal,
261
+ reason: 'could not be signed or sent',
262
+ total
263
+ });
264
+ }
265
+ };
266
+ /**
267
+ * Builds a stateless Earn client for Solana vaults: it prepares vault actions through the
268
+ * Dynamic API and executes the returned ordered transaction sequence one transaction at a
269
+ * time — never in parallel, because a later transaction can depend on the state an earlier
270
+ * one wrote. If one fails, the sequence stops and the thrown {@link EarnSolanaStepError}
271
+ * reports how much of the action already landed.
272
+ *
273
+ * Gas sponsorship is opt-in (`sponsored: true`): vault transactions are built with the
274
+ * owner as fee payer, unlike the browser SDK which sponsors automatically.
275
+ */ const createSvmEarnClient = ({ getEarnClient, signTransaction })=>{
276
+ const executeStep = async ({ connection, context, externalServerKeyShares, chainId, onStepChange, onTransactionStepChange, password, sponsored, step, total, walletMetadata })=>{
277
+ const transaction = deserializeEarnTransaction(step.payload);
278
+ const { ordinal } = step;
279
+ const isExpired = ()=>isStepExpired({
280
+ connection,
281
+ expiryBlockHeight: step.expiryBlockHeight,
282
+ transaction
283
+ });
284
+ // The blockhash was fixed when the payload was built, so earlier slow steps can
285
+ // leave later ones unsendable — check before spending a signature.
286
+ if (await isExpired()) {
287
+ throw new EarnSolanaStepError({
288
+ code: 'yield_step_expired',
289
+ ordinal,
290
+ reason: 'expired before it could be signed',
291
+ total
292
+ });
293
+ }
294
+ onStepChange == null ? void 0 : onStepChange('transaction');
295
+ const progress = {
296
+ ordinal,
297
+ total
298
+ };
299
+ if (step.label !== undefined) {
300
+ progress.label = step.label;
301
+ }
302
+ onTransactionStepChange == null ? void 0 : onTransactionStepChange(progress);
303
+ const signature = await signAndSendStep({
304
+ chainId,
305
+ connection,
306
+ context,
307
+ externalServerKeyShares,
308
+ ordinal,
309
+ password,
310
+ signTransaction,
311
+ sponsored,
312
+ total,
313
+ transaction,
314
+ walletMetadata
315
+ });
316
+ // A sent transaction may still land even when the poll itself fails, so wrap any RPC
317
+ // error and keep the send signature on it for reconciliation.
318
+ let confirmation;
319
+ try {
320
+ confirmation = await waitForConfirmation({
321
+ connection,
322
+ isExpired,
323
+ signature
324
+ });
325
+ } catch (error) {
326
+ throw new EarnSolanaStepError({
327
+ cause: error instanceof Error ? error : undefined,
328
+ code: 'yield_step_confirmation_unknown',
329
+ ordinal,
330
+ reason: 'was sent but its confirmation could not be checked',
331
+ signature,
332
+ total
333
+ });
334
+ }
335
+ if (confirmation.outcome === 'failed') {
336
+ throw new EarnSolanaStepError({
337
+ code: 'yield_step_failed',
338
+ ordinal,
339
+ reason: `failed on-chain: ${JSON.stringify(confirmation.error)}`,
340
+ signature,
341
+ total
342
+ });
343
+ }
344
+ if (confirmation.outcome === 'expired') {
345
+ throw new EarnSolanaStepError({
346
+ code: 'yield_step_expired',
347
+ ordinal,
348
+ reason: 'was not confirmed before its blockhash expired',
349
+ signature,
350
+ total
351
+ });
352
+ }
353
+ return signature;
354
+ };
355
+ const executeEarnTransaction = async ({ signingPayload, walletMetadata, password, externalServerKeyShares, sponsored, rpcUrl, chainId, context, onStepChange, onTransactionStepChange })=>{
356
+ if (walletMetadata.chainName !== 'SVM') {
357
+ throw new Error(ERROR_EARN_NOT_SVM_WALLET);
358
+ }
359
+ if (!walletMetadata.accountAddress) {
360
+ throw new Error('Account address is required');
361
+ }
362
+ var _signingPayload_transactions;
363
+ const steps = [
364
+ ...(_signingPayload_transactions = signingPayload.transactions) != null ? _signingPayload_transactions : []
365
+ ].sort((first, second)=>first.ordinal - second.ordinal);
366
+ if (steps.length === 0) {
367
+ throw new Error(ERROR_EARN_MISSING_TRANSACTIONS);
368
+ }
369
+ if (steps.some((step, index)=>step.ordinal !== index)) {
370
+ throw new Error(ERROR_EARN_NON_CONTIGUOUS_ORDINALS);
371
+ }
372
+ var _ref;
373
+ const connection = new Connection((_ref = rpcUrl != null ? rpcUrl : EARN_SOLANA_RPC_BY_CHAIN_ID[chainId != null ? chainId : signingPayload.chainId]) != null ? _ref : SOLANA_RPC_URL);
374
+ // A rejection stops the sequence at the first failure; everything after it is skipped.
375
+ const transactionHash = await steps.reduce(async (previousStep, step)=>{
376
+ await previousStep;
377
+ return executeStep({
378
+ connection,
379
+ context,
380
+ externalServerKeyShares,
381
+ chainId: chainId != null ? chainId : signingPayload.chainId,
382
+ onStepChange,
383
+ onTransactionStepChange,
384
+ password,
385
+ sponsored,
386
+ step,
387
+ total: steps.length,
388
+ walletMetadata
389
+ });
390
+ }, Promise.resolve(''));
391
+ return {
392
+ transactionHash
393
+ };
394
+ };
395
+ return {
396
+ executeEarnTransaction,
397
+ depositToEarnVault: async (_param)=>{
398
+ var { vaultId, amount, receiverAddress, traceContext } = _param, walletParams = _object_without_properties_loose(_param, [
399
+ "vaultId",
400
+ "amount",
401
+ "receiverAddress",
402
+ "traceContext"
403
+ ]);
404
+ const signingPayload = await getEarnClient().prepareDeposit({
405
+ vaultId,
406
+ ownerAddress: walletParams.walletMetadata.accountAddress,
407
+ amount,
408
+ receiverAddress,
409
+ traceContext
410
+ });
411
+ return executeEarnTransaction(_extends({
412
+ signingPayload
413
+ }, walletParams));
414
+ },
415
+ withdrawFromEarnVault: async (_param)=>{
416
+ var { vaultId, amount, receiverAddress, traceContext } = _param, walletParams = _object_without_properties_loose(_param, [
417
+ "vaultId",
418
+ "amount",
419
+ "receiverAddress",
420
+ "traceContext"
421
+ ]);
422
+ const signingPayload = await getEarnClient().prepareWithdrawal({
423
+ vaultId,
424
+ ownerAddress: walletParams.walletMetadata.accountAddress,
425
+ amount,
426
+ receiverAddress,
427
+ traceContext
428
+ });
429
+ return executeEarnTransaction(_extends({
430
+ signingPayload
431
+ }, walletParams));
432
+ },
433
+ claimEarnRewards: async (_param)=>{
434
+ var { vaultId, traceContext } = _param, walletParams = _object_without_properties_loose(_param, [
435
+ "vaultId",
436
+ "traceContext"
437
+ ]);
438
+ const signingPayload = await getEarnClient().prepareClaimRewards({
439
+ vaultId,
440
+ ownerAddress: walletParams.walletMetadata.accountAddress,
441
+ traceContext
442
+ });
443
+ return executeEarnTransaction(_extends({
444
+ signingPayload
445
+ }, walletParams));
446
+ }
447
+ };
448
+ };
449
+
133
450
  const logError$1 = createLogError('node-svm');
134
451
  // Helper: normalize bytes to hex without triggering Buffer.from(string, encoding) overload
135
452
  const toHex = (bytes)=>(Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes)).toString('hex');
@@ -561,6 +878,30 @@ class DynamicSvmWalletClient extends DynamicWalletClient {
561
878
  const svmWallets = wallets.filter((wallet)=>wallet.chainName === 'SVM');
562
879
  return svmWallets;
563
880
  }
881
+ // ---------------------------------------------------------------------------
882
+ // Earn (yield vaults)
883
+ // ---------------------------------------------------------------------------
884
+ get svmEarn() {
885
+ return createSvmEarnClient({
886
+ getEarnClient: ()=>this.earn,
887
+ signTransaction: (args)=>this.signTransaction(args)
888
+ });
889
+ }
890
+ /**
891
+ * Executes an Earn signing payload returned by the prepare* methods, signing and
892
+ * sending its ordered Solana transaction sequence one transaction at a time.
893
+ */ async executeEarnTransaction(params) {
894
+ return this.svmEarn.executeEarnTransaction(params);
895
+ }
896
+ /** Prepares and executes a deposit into an Earn vault. */ async depositToEarnVault(params) {
897
+ return this.svmEarn.depositToEarnVault(params);
898
+ }
899
+ /** Prepares and executes a withdrawal from an Earn vault. */ async withdrawFromEarnVault(params) {
900
+ return this.svmEarn.withdrawFromEarnVault(params);
901
+ }
902
+ /** Prepares and executes a claim of accrued Earn vault rewards. */ async claimEarnRewards(params) {
903
+ return this.svmEarn.claimEarnRewards(params);
904
+ }
564
905
  constructor({ environmentId, baseApiUrl, baseMPCRelayApiUrl, enableMPCAccelerator, logger, debug }){
565
906
  super({
566
907
  environmentId,
@@ -875,4 +1216,4 @@ const delegatedSignTransaction = async (client, { walletId, shareSetId, walletAp
875
1216
  });
876
1217
  };
877
1218
 
878
- export { DynamicSvmWalletClient, ERROR_CREATE_WALLET_ACCOUNT, ERROR_DELEGATED_SIGN_TRANSACTION_CONTEXT_REQUIRED, ERROR_SIGN_RAW_MESSAGE, ERROR_SIGN_TRANSACTION_CONTEXT_REQUIRED, ERROR_SIGN_TRANSACTION_RAW_HEX_CONTEXT_REQUIRED, addSignatureToTransaction, attachSignature, createDelegatedSvmWalletClient, createSolanaTransaction, decodeBase58, delegatedSignMessage, delegatedSignRawMessage, delegatedSignTransaction, delegatedSponsorTransaction, encodeBase58, getBalance, revokeDelegation, sendTransaction };
1219
+ export { DynamicSvmWalletClient, ERROR_CREATE_WALLET_ACCOUNT, ERROR_DELEGATED_SIGN_TRANSACTION_CONTEXT_REQUIRED, ERROR_SIGN_RAW_MESSAGE, ERROR_SIGN_TRANSACTION_CONTEXT_REQUIRED, ERROR_SIGN_TRANSACTION_RAW_HEX_CONTEXT_REQUIRED, EarnSolanaStepError, addSignatureToTransaction, attachSignature, createDelegatedSvmWalletClient, createSolanaTransaction, createSvmEarnClient, decodeBase58, delegatedSignMessage, delegatedSignRawMessage, delegatedSignTransaction, delegatedSponsorTransaction, encodeBase58, getBalance, revokeDelegation, sendTransaction };
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@dynamic-labs-wallet/node-svm",
3
- "version": "1.1.51",
3
+ "version": "1.1.53",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "dependencies": {
7
- "@dynamic-labs-wallet/node": "1.1.51",
7
+ "@dynamic-labs-wallet/node": "1.1.53",
8
8
  "@solana/web3.js": "^1.98.2"
9
9
  },
10
10
  "publishConfig": {
@@ -1,5 +1,6 @@
1
1
  import { type ServerKeyShare, DynamicWalletClient, type Ed25519KeygenResult, type ThresholdSignatureScheme, type TraceContext, type DynamicWalletClientProps, type WalletMetadata, type WalletApiKeyCreationResult, type RawSignPreImage, type SignMessageContext } from '@dynamic-labs-wallet/node';
2
2
  import { type Transaction, VersionedTransaction } from '@solana/web3.js';
3
+ import type { ClaimEarnRewardsParams, DepositToEarnVaultParams, EarnTransactionResult, ExecuteEarnTransactionParams, WithdrawFromEarnVaultParams } from './earn.types.js';
3
4
  type SvmSignableTransaction = VersionedTransaction | Transaction | string;
4
5
  type SignTransactionResult = string | {
5
6
  signature: string;
@@ -168,6 +169,18 @@ export declare class DynamicSvmWalletClient extends DynamicWalletClient {
168
169
  derivationPath: string | undefined;
169
170
  thresholdSignatureScheme: ThresholdSignatureScheme;
170
171
  }[]>;
172
+ private get svmEarn();
173
+ /**
174
+ * Executes an Earn signing payload returned by the prepare* methods, signing and
175
+ * sending its ordered Solana transaction sequence one transaction at a time.
176
+ */
177
+ executeEarnTransaction(params: ExecuteEarnTransactionParams): Promise<EarnTransactionResult>;
178
+ /** Prepares and executes a deposit into an Earn vault. */
179
+ depositToEarnVault(params: DepositToEarnVaultParams): Promise<EarnTransactionResult>;
180
+ /** Prepares and executes a withdrawal from an Earn vault. */
181
+ withdrawFromEarnVault(params: WithdrawFromEarnVaultParams): Promise<EarnTransactionResult>;
182
+ /** Prepares and executes a claim of accrued Earn vault rewards. */
183
+ claimEarnRewards(params: ClaimEarnRewardsParams): Promise<EarnTransactionResult>;
171
184
  }
172
185
  export {};
173
186
  //# sourceMappingURL=client.d.ts.map
@@ -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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"}
@@ -0,0 +1,56 @@
1
+ import type { EarnClient, ServerKeyShare, SignMessageContext, WalletMetadata } from '@dynamic-labs-wallet/node';
2
+ import { Transaction, VersionedTransaction } from '@solana/web3.js';
3
+ import type { ClaimEarnRewardsParams, DepositToEarnVaultParams, EarnTransactionResult, ExecuteEarnTransactionParams, WithdrawFromEarnVaultParams } from './earn.types.js';
4
+ type EarnStepErrorCode = 'yield_step_expired' | 'yield_step_failed' | 'yield_step_send_failed' | 'yield_step_confirmation_unknown';
5
+ /**
6
+ * A transaction in the sequence could not land. Transactions before `ordinal` already
7
+ * confirmed on-chain, so the action may be partially applied.
8
+ */
9
+ export declare class EarnSolanaStepError extends Error {
10
+ readonly code: EarnStepErrorCode;
11
+ readonly ordinal: number;
12
+ readonly signature?: string;
13
+ constructor({ cause, code, ordinal, reason, signature, total, }: {
14
+ cause?: Error;
15
+ code: EarnStepErrorCode;
16
+ ordinal: number;
17
+ reason: string;
18
+ signature?: string;
19
+ total: number;
20
+ });
21
+ }
22
+ type SignEarnTransaction = (args: {
23
+ walletMetadata: WalletMetadata;
24
+ transaction: Transaction | VersionedTransaction;
25
+ password?: string;
26
+ externalServerKeyShares?: ServerKeyShare[];
27
+ sponsor?: boolean;
28
+ chainId?: string;
29
+ context?: SignMessageContext;
30
+ }) => Promise<string | {
31
+ signature: string;
32
+ transaction: Transaction | VersionedTransaction;
33
+ }>;
34
+ /** Primitives the Earn client borrows from whoever owns the wallet. */
35
+ export type SvmEarnClientDeps = {
36
+ getEarnClient: () => EarnClient;
37
+ signTransaction: SignEarnTransaction;
38
+ };
39
+ /**
40
+ * Builds a stateless Earn client for Solana vaults: it prepares vault actions through the
41
+ * Dynamic API and executes the returned ordered transaction sequence one transaction at a
42
+ * time — never in parallel, because a later transaction can depend on the state an earlier
43
+ * one wrote. If one fails, the sequence stops and the thrown {@link EarnSolanaStepError}
44
+ * reports how much of the action already landed.
45
+ *
46
+ * Gas sponsorship is opt-in (`sponsored: true`): vault transactions are built with the
47
+ * owner as fee payer, unlike the browser SDK which sponsors automatically.
48
+ */
49
+ export declare const createSvmEarnClient: ({ getEarnClient, signTransaction }: SvmEarnClientDeps) => {
50
+ executeEarnTransaction: ({ signingPayload, walletMetadata, password, externalServerKeyShares, sponsored, rpcUrl, chainId, context, onStepChange, onTransactionStepChange, }: ExecuteEarnTransactionParams) => Promise<EarnTransactionResult>;
51
+ depositToEarnVault: ({ vaultId, amount, receiverAddress, traceContext, ...walletParams }: DepositToEarnVaultParams) => Promise<EarnTransactionResult>;
52
+ withdrawFromEarnVault: ({ vaultId, amount, receiverAddress, traceContext, ...walletParams }: WithdrawFromEarnVaultParams) => Promise<EarnTransactionResult>;
53
+ claimEarnRewards: ({ vaultId, traceContext, ...walletParams }: ClaimEarnRewardsParams) => Promise<EarnTransactionResult>;
54
+ };
55
+ export {};
56
+ //# sourceMappingURL=earn.client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"earn.client.d.ts","sourceRoot":"","sources":["../../src/client/earn.client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EAEV,cAAc,EACd,kBAAkB,EAClB,cAAc,EACf,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAc,WAAW,EAAE,oBAAoB,EAAyB,MAAM,iBAAiB,CAAC;AASvG,OAAO,KAAK,EACV,sBAAsB,EACtB,wBAAwB,EACxB,qBAAqB,EAErB,4BAA4B,EAC5B,2BAA2B,EAC5B,MAAM,iBAAiB,CAAC;AAGzB,KAAK,iBAAiB,GAClB,oBAAoB,GACpB,mBAAmB,GACnB,wBAAwB,GACxB,iCAAiC,CAAC;AAEtC;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;gBAEhB,EACV,KAAK,EACL,IAAI,EACJ,OAAO,EACP,MAAM,EACN,SAAS,EACT,KAAK,GACN,EAAE;QACD,KAAK,CAAC,EAAE,KAAK,CAAC;QACd,IAAI,EAAE,iBAAiB,CAAC;QACxB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;KACf;CAcF;AAED,KAAK,mBAAmB,GAAG,CAAC,IAAI,EAAE;IAChC,cAAc,EAAE,cAAc,CAAC;IAC/B,WAAW,EAAE,WAAW,GAAG,oBAAoB,CAAC;IAChD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uBAAuB,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,kBAAkB,CAAC;CAC9B,KAAK,OAAO,CAAC,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,WAAW,GAAG,oBAAoB,CAAA;CAAE,CAAC,CAAC;AAE/F,uEAAuE;AACvE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,aAAa,EAAE,MAAM,UAAU,CAAC;IAChC,eAAe,EAAE,mBAAmB,CAAC;CACtC,CAAC;AAsJF;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,uCAAwC,iBAAiB;iLAmHpF,4BAA4B,KAAG,OAAO,CAAC,qBAAqB,CAAC;8FAsD3D,wBAAwB,KAAG,OAAO,CAAC,qBAAqB,CAAC;iGAiBzD,2BAA2B,KAAG,OAAO,CAAC,qBAAqB,CAAC;mEAe5D,sBAAsB,KAAG,OAAO,CAAC,qBAAqB,CAAC;CAS7D,CAAC"}
@@ -0,0 +1,9 @@
1
+ export declare const ERROR_EARN_NOT_SVM_WALLET = "Wallet metadata is not for an SVM wallet";
2
+ export declare const ERROR_EARN_MISSING_TRANSACTIONS = "Earn signing payload is missing the Solana transaction sequence";
3
+ export declare const ERROR_EARN_NON_CONTIGUOUS_ORDINALS = "Earn signing payload transaction ordinals must be contiguous from zero";
4
+ /** Default RPC per Dynamic Solana chain id (101 mainnet, 103 devnet); `rpcUrl` overrides. */
5
+ export declare const EARN_SOLANA_RPC_BY_CHAIN_ID: Record<string, string>;
6
+ /** Blockhash checks run at `processed`, the commitment that sees new blockhashes first. */
7
+ export declare const EARN_SOLANA_BLOCKHASH_COMMITMENT: "processed";
8
+ export declare const EARN_CONFIRMATION_POLL_INTERVAL_MS = 1000;
9
+ //# sourceMappingURL=earn.constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"earn.constants.d.ts","sourceRoot":"","sources":["../../src/client/earn.constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,yBAAyB,6CAA6C,CAAC;AAEpF,eAAO,MAAM,+BAA+B,oEAAoE,CAAC;AAEjH,eAAO,MAAM,kCAAkC,2EAC2B,CAAC;AAE3E,6FAA6F;AAC7F,eAAO,MAAM,2BAA2B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAG9D,CAAC;AAEF,2FAA2F;AAC3F,eAAO,MAAM,gCAAgC,aAAuB,CAAC;AAErE,eAAO,MAAM,kCAAkC,OAAO,CAAC"}
@@ -0,0 +1,41 @@
1
+ import type { EarnExecutionStep, EarnSigningPayload, ServerKeyShare, SignMessageContext, TraceContext, WalletMetadata } from '@dynamic-labs-wallet/node';
2
+ export type { EarnExecutionStep, EarnTransactionResult } from '@dynamic-labs-wallet/node';
3
+ /** Position of the transaction about to be signed within the ordered sequence. */
4
+ export type EarnTransactionStepProgress = {
5
+ label?: string;
6
+ ordinal: number;
7
+ total: number;
8
+ };
9
+ /** Wallet context and chain access every Earn write action needs. */
10
+ export type SvmEarnWalletParams = {
11
+ walletMetadata: WalletMetadata;
12
+ password?: string;
13
+ externalServerKeyShares?: ServerKeyShare[];
14
+ /** Opt in to Dynamic gas sponsorship for every transaction in the sequence. */
15
+ sponsored?: boolean;
16
+ /** Solana RPC endpoint; defaults to the public cluster for the payload's `chainId` (101 mainnet, 103 devnet). */
17
+ rpcUrl?: string;
18
+ chainId?: string;
19
+ context?: SignMessageContext;
20
+ onStepChange?: (step: EarnExecutionStep) => void;
21
+ /** Invoked as each transaction in the sequence is about to be signed. */
22
+ onTransactionStepChange?: (step: EarnTransactionStepProgress) => void;
23
+ traceContext?: TraceContext;
24
+ };
25
+ export type ExecuteEarnTransactionParams = Omit<SvmEarnWalletParams, 'traceContext'> & {
26
+ signingPayload: EarnSigningPayload;
27
+ };
28
+ export type DepositToEarnVaultParams = SvmEarnWalletParams & {
29
+ vaultId: string;
30
+ amount: string;
31
+ receiverAddress?: string;
32
+ };
33
+ export type WithdrawFromEarnVaultParams = SvmEarnWalletParams & {
34
+ vaultId: string;
35
+ amount: string;
36
+ receiverAddress?: string;
37
+ };
38
+ export type ClaimEarnRewardsParams = SvmEarnWalletParams & {
39
+ vaultId: string;
40
+ };
41
+ //# sourceMappingURL=earn.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"earn.types.d.ts","sourceRoot":"","sources":["../../src/client/earn.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACf,MAAM,2BAA2B,CAAC;AAEnC,YAAY,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAE1F,kFAAkF;AAClF,MAAM,MAAM,2BAA2B,GAAG;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,qEAAqE;AACrE,MAAM,MAAM,mBAAmB,GAAG;IAChC,cAAc,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uBAAuB,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3C,+EAA+E;IAC/E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iHAAiH;IACjH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACjD,yEAAyE;IACzE,uBAAuB,CAAC,EAAE,CAAC,IAAI,EAAE,2BAA2B,KAAK,IAAI,CAAC;IACtE,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG,IAAI,CAAC,mBAAmB,EAAE,cAAc,CAAC,GAAG;IACrF,cAAc,EAAE,kBAAkB,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG,mBAAmB,GAAG;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG,mBAAmB,GAAG;IAC9D,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG,mBAAmB,GAAG;IACzD,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC"}
@@ -2,4 +2,7 @@ export * from './client.js';
2
2
  export * from './utils.js';
3
3
  export * from './base58.js';
4
4
  export * from './constants.js';
5
+ export { createSvmEarnClient, EarnSolanaStepError } from './earn.client.js';
6
+ export type { SvmEarnClientDeps } from './earn.client.js';
7
+ export type { ClaimEarnRewardsParams, DepositToEarnVaultParams, EarnTransactionStepProgress, ExecuteEarnTransactionParams, SvmEarnWalletParams, WithdrawFromEarnVaultParams, } from './earn.types.js';
5
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5E,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,YAAY,EACV,sBAAsB,EACtB,wBAAwB,EACxB,2BAA2B,EAC3B,4BAA4B,EAC5B,mBAAmB,EACnB,2BAA2B,GAC5B,MAAM,iBAAiB,CAAC"}