@sudobility/contracts 1.11.1 → 1.12.0

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 (82) hide show
  1. package/artifacts/contracts/Mailer.sol/Mailer.d.ts +120 -2
  2. package/artifacts/contracts/Mailer.sol/Mailer.dbg.json +1 -1
  3. package/artifacts/contracts/Mailer.sol/Mailer.json +120 -2
  4. package/artifacts/contracts/MockUSDC.sol/MockUSDC.dbg.json +1 -1
  5. package/artifacts/contracts/interfaces/IERC20.sol/IERC20.dbg.json +1 -1
  6. package/dist/evm/src/evm/index.d.ts +1 -1
  7. package/dist/evm/src/evm/index.d.ts.map +1 -1
  8. package/dist/evm/src/evm/index.js +1 -1
  9. package/dist/evm/src/evm/index.js.map +1 -1
  10. package/dist/evm/src/evm/mailer-client.d.ts +936 -180
  11. package/dist/evm/src/evm/mailer-client.d.ts.map +1 -1
  12. package/dist/evm/src/evm/mailer-client.js +451 -249
  13. package/dist/evm/src/evm/mailer-client.js.map +1 -1
  14. package/dist/evm/typechain-types/Mailer.d.ts +81 -11
  15. package/dist/evm/typechain-types/Mailer.d.ts.map +1 -1
  16. package/dist/evm/typechain-types/factories/Mailer__factory.d.ts +93 -1
  17. package/dist/evm/typechain-types/factories/Mailer__factory.d.ts.map +1 -1
  18. package/dist/evm/typechain-types/factories/Mailer__factory.js +119 -1
  19. package/dist/evm/typechain-types/factories/Mailer__factory.js.map +1 -1
  20. package/dist/solana/solana/index.d.ts +1 -1
  21. package/dist/solana/solana/index.d.ts.map +1 -1
  22. package/dist/solana/solana/index.js +3 -1
  23. package/dist/solana/solana/index.js.map +1 -1
  24. package/dist/solana/solana/mailer-client.d.ts +86 -20
  25. package/dist/solana/solana/mailer-client.d.ts.map +1 -1
  26. package/dist/solana/solana/mailer-client.js +290 -58
  27. package/dist/solana/solana/mailer-client.js.map +1 -1
  28. package/dist/unified/src/evm/index.d.ts +1 -1
  29. package/dist/unified/src/evm/index.d.ts.map +1 -1
  30. package/dist/unified/src/evm/index.js +1 -1
  31. package/dist/unified/src/evm/index.js.map +1 -1
  32. package/dist/unified/src/evm/mailer-client.d.ts +936 -180
  33. package/dist/unified/src/evm/mailer-client.d.ts.map +1 -1
  34. package/dist/unified/src/evm/mailer-client.js +451 -249
  35. package/dist/unified/src/evm/mailer-client.js.map +1 -1
  36. package/dist/unified/src/solana/index.d.ts +1 -1
  37. package/dist/unified/src/solana/index.d.ts.map +1 -1
  38. package/dist/unified/src/solana/index.js +3 -1
  39. package/dist/unified/src/solana/index.js.map +1 -1
  40. package/dist/unified/src/solana/mailer-client.d.ts +86 -20
  41. package/dist/unified/src/solana/mailer-client.d.ts.map +1 -1
  42. package/dist/unified/src/solana/mailer-client.js +290 -58
  43. package/dist/unified/src/solana/mailer-client.js.map +1 -1
  44. package/dist/unified/src/unified/onchain-mailer-client.d.ts +25 -0
  45. package/dist/unified/src/unified/onchain-mailer-client.d.ts.map +1 -1
  46. package/dist/unified/src/unified/onchain-mailer-client.js +147 -4
  47. package/dist/unified/src/unified/onchain-mailer-client.js.map +1 -1
  48. package/dist/unified/typechain-types/Mailer.d.ts +81 -11
  49. package/dist/unified/typechain-types/Mailer.d.ts.map +1 -1
  50. package/dist/unified/typechain-types/factories/Mailer__factory.d.ts +93 -1
  51. package/dist/unified/typechain-types/factories/Mailer__factory.d.ts.map +1 -1
  52. package/dist/unified/typechain-types/factories/Mailer__factory.js +119 -1
  53. package/dist/unified/typechain-types/factories/Mailer__factory.js.map +1 -1
  54. package/dist/unified-esm/src/evm/index.d.ts +1 -1
  55. package/dist/unified-esm/src/evm/index.d.ts.map +1 -1
  56. package/dist/unified-esm/src/evm/index.js +1 -1
  57. package/dist/unified-esm/src/evm/index.js.map +1 -1
  58. package/dist/unified-esm/src/evm/mailer-client.d.ts +936 -180
  59. package/dist/unified-esm/src/evm/mailer-client.d.ts.map +1 -1
  60. package/dist/unified-esm/src/evm/mailer-client.js +453 -251
  61. package/dist/unified-esm/src/evm/mailer-client.js.map +1 -1
  62. package/dist/unified-esm/src/solana/index.d.ts +1 -1
  63. package/dist/unified-esm/src/solana/index.d.ts.map +1 -1
  64. package/dist/unified-esm/src/solana/index.js +1 -1
  65. package/dist/unified-esm/src/solana/index.js.map +1 -1
  66. package/dist/unified-esm/src/solana/mailer-client.d.ts +86 -20
  67. package/dist/unified-esm/src/solana/mailer-client.d.ts.map +1 -1
  68. package/dist/unified-esm/src/solana/mailer-client.js +291 -59
  69. package/dist/unified-esm/src/solana/mailer-client.js.map +1 -1
  70. package/dist/unified-esm/src/unified/onchain-mailer-client.d.ts +25 -0
  71. package/dist/unified-esm/src/unified/onchain-mailer-client.d.ts.map +1 -1
  72. package/dist/unified-esm/src/unified/onchain-mailer-client.js +147 -4
  73. package/dist/unified-esm/src/unified/onchain-mailer-client.js.map +1 -1
  74. package/dist/unified-esm/typechain-types/Mailer.d.ts +81 -11
  75. package/dist/unified-esm/typechain-types/Mailer.d.ts.map +1 -1
  76. package/dist/unified-esm/typechain-types/factories/Mailer__factory.d.ts +93 -1
  77. package/dist/unified-esm/typechain-types/factories/Mailer__factory.d.ts.map +1 -1
  78. package/dist/unified-esm/typechain-types/factories/Mailer__factory.js +119 -1
  79. package/dist/unified-esm/typechain-types/factories/Mailer__factory.js.map +1 -1
  80. package/package.json +1 -1
  81. package/typechain-types/Mailer.ts +137 -9
  82. package/typechain-types/factories/Mailer__factory.ts +119 -1
@@ -35,6 +35,23 @@ function encodeSend(to, subject, body, revenueShareToReceiver, resolveSenderToNa
35
35
  data.writeUInt8(resolveSenderToName ? 1 : 0, offset);
36
36
  return data;
37
37
  }
38
+ function encodeSendPrepared(to, mailId, revenueShareToReceiver, resolveSenderToName = false) {
39
+ const mailIdBytes = Buffer.from(mailId, 'utf8');
40
+ const data = Buffer.alloc(1 + 32 + 4 + mailIdBytes.length + 1 + 1);
41
+ let offset = 0;
42
+ data.writeUInt8(InstructionType.SendPrepared, offset);
43
+ offset += 1;
44
+ to.toBuffer().copy(data, offset);
45
+ offset += 32;
46
+ data.writeUInt32LE(mailIdBytes.length, offset);
47
+ offset += 4;
48
+ mailIdBytes.copy(data, offset);
49
+ offset += mailIdBytes.length;
50
+ data.writeUInt8(revenueShareToReceiver ? 1 : 0, offset);
51
+ offset += 1;
52
+ data.writeUInt8(resolveSenderToName ? 1 : 0, offset);
53
+ return data;
54
+ }
38
55
  function encodeSendToEmail(toEmail, subject, body) {
39
56
  const emailBytes = Buffer.from(toEmail, 'utf8');
40
57
  const subjectBytes = Buffer.from(subject, 'utf8');
@@ -72,6 +89,23 @@ function encodeSendPreparedToEmail(toEmail, mailId) {
72
89
  mailIdBytes.copy(data, offset);
73
90
  return data;
74
91
  }
92
+ function encodeSendThroughWebhook(to, webhookId, revenueShareToReceiver, resolveSenderToName = false) {
93
+ const webhookBytes = Buffer.from(webhookId, 'utf8');
94
+ const data = Buffer.alloc(1 + 32 + 4 + webhookBytes.length + 1 + 1);
95
+ let offset = 0;
96
+ data.writeUInt8(InstructionType.SendThroughWebhook, offset);
97
+ offset += 1;
98
+ to.toBuffer().copy(data, offset);
99
+ offset += 32;
100
+ data.writeUInt32LE(webhookBytes.length, offset);
101
+ offset += 4;
102
+ webhookBytes.copy(data, offset);
103
+ offset += webhookBytes.length;
104
+ data.writeUInt8(revenueShareToReceiver ? 1 : 0, offset);
105
+ offset += 1;
106
+ data.writeUInt8(resolveSenderToName ? 1 : 0, offset);
107
+ return data;
108
+ }
75
109
  function encodeSimpleInstruction(instructionType) {
76
110
  const data = Buffer.alloc(1);
77
111
  data.writeUInt8(instructionType, 0);
@@ -89,6 +123,19 @@ function encodeSetDelegationFee(newFee) {
89
123
  data.writeBigUInt64LE(newFee, 1);
90
124
  return data;
91
125
  }
126
+ function encodeSetCustomFeePercentage(account, percentage) {
127
+ const data = Buffer.alloc(1 + 32 + 1);
128
+ data.writeUInt8(InstructionType.SetCustomFeePercentage, 0);
129
+ account.toBuffer().copy(data, 1);
130
+ data.writeUInt8(percentage, 33);
131
+ return data;
132
+ }
133
+ function encodeClearCustomFeePercentage(account) {
134
+ const data = Buffer.alloc(1 + 32);
135
+ data.writeUInt8(InstructionType.ClearCustomFeePercentage, 0);
136
+ account.toBuffer().copy(data, 1);
137
+ return data;
138
+ }
92
139
  function encodeDelegateTo(delegate) {
93
140
  if (!delegate) {
94
141
  // Clear delegation - just send the instruction type and a null option
@@ -172,26 +219,40 @@ function parseDelegation(data) {
172
219
  bump,
173
220
  };
174
221
  }
175
- /**
176
- * Instruction types for the native Solana program
177
- */
222
+ function parseFeeDiscount(data) {
223
+ let offset = 0;
224
+ const account = new web3_js_1.PublicKey(data.slice(offset, offset + 32));
225
+ offset += 32;
226
+ const discount = data.readUInt8(offset);
227
+ offset += 1;
228
+ const bump = data.readUInt8(offset);
229
+ return {
230
+ account,
231
+ discount,
232
+ bump,
233
+ };
234
+ }
178
235
  var InstructionType;
179
236
  (function (InstructionType) {
180
237
  InstructionType[InstructionType["Initialize"] = 0] = "Initialize";
181
238
  InstructionType[InstructionType["Send"] = 1] = "Send";
182
- InstructionType[InstructionType["SendToEmail"] = 2] = "SendToEmail";
183
- InstructionType[InstructionType["SendPreparedToEmail"] = 3] = "SendPreparedToEmail";
184
- InstructionType[InstructionType["ClaimRecipientShare"] = 4] = "ClaimRecipientShare";
185
- InstructionType[InstructionType["ClaimOwnerShare"] = 5] = "ClaimOwnerShare";
186
- InstructionType[InstructionType["SetFee"] = 6] = "SetFee";
187
- InstructionType[InstructionType["DelegateTo"] = 7] = "DelegateTo";
188
- InstructionType[InstructionType["RejectDelegation"] = 8] = "RejectDelegation";
189
- InstructionType[InstructionType["SetDelegationFee"] = 9] = "SetDelegationFee";
190
- InstructionType[InstructionType["Pause"] = 10] = "Pause";
191
- InstructionType[InstructionType["Unpause"] = 11] = "Unpause";
192
- InstructionType[InstructionType["DistributeClaimableFunds"] = 12] = "DistributeClaimableFunds";
193
- InstructionType[InstructionType["ClaimExpiredShares"] = 13] = "ClaimExpiredShares";
194
- InstructionType[InstructionType["EmergencyUnpause"] = 14] = "EmergencyUnpause";
239
+ InstructionType[InstructionType["SendPrepared"] = 2] = "SendPrepared";
240
+ InstructionType[InstructionType["SendToEmail"] = 3] = "SendToEmail";
241
+ InstructionType[InstructionType["SendPreparedToEmail"] = 4] = "SendPreparedToEmail";
242
+ InstructionType[InstructionType["SendThroughWebhook"] = 5] = "SendThroughWebhook";
243
+ InstructionType[InstructionType["ClaimRecipientShare"] = 6] = "ClaimRecipientShare";
244
+ InstructionType[InstructionType["ClaimOwnerShare"] = 7] = "ClaimOwnerShare";
245
+ InstructionType[InstructionType["SetFee"] = 8] = "SetFee";
246
+ InstructionType[InstructionType["DelegateTo"] = 9] = "DelegateTo";
247
+ InstructionType[InstructionType["RejectDelegation"] = 10] = "RejectDelegation";
248
+ InstructionType[InstructionType["SetDelegationFee"] = 11] = "SetDelegationFee";
249
+ InstructionType[InstructionType["SetCustomFeePercentage"] = 12] = "SetCustomFeePercentage";
250
+ InstructionType[InstructionType["ClearCustomFeePercentage"] = 13] = "ClearCustomFeePercentage";
251
+ InstructionType[InstructionType["Pause"] = 14] = "Pause";
252
+ InstructionType[InstructionType["Unpause"] = 15] = "Unpause";
253
+ InstructionType[InstructionType["DistributeClaimableFunds"] = 16] = "DistributeClaimableFunds";
254
+ InstructionType[InstructionType["ClaimExpiredShares"] = 17] = "ClaimExpiredShares";
255
+ InstructionType[InstructionType["EmergencyUnpause"] = 18] = "EmergencyUnpause";
195
256
  })(InstructionType || (InstructionType = {}));
196
257
  /**
197
258
  * @class MailerClient
@@ -229,6 +290,7 @@ var InstructionType;
229
290
  */
230
291
  class MailerClient {
231
292
  constructor(connection, wallet, programId, usdcMint) {
293
+ this.defaultComputeUnitMultiplier = 1.2; // 20% buffer by default
232
294
  this.connection = connection;
233
295
  this.wallet = wallet;
234
296
  this.programId = programId;
@@ -238,10 +300,69 @@ class MailerClient {
238
300
  this.mailerStatePda = mailerPda;
239
301
  this.mailerBump = bump;
240
302
  }
303
+ /**
304
+ * Optimize compute units for a transaction
305
+ * @param transaction Transaction to optimize
306
+ * @param options Compute unit options
307
+ * @returns Optimized transaction with compute budget instructions
308
+ */
309
+ async optimizeComputeUnits(transaction, options) {
310
+ // Skip if explicitly disabled
311
+ if (options?.skipComputeUnits) {
312
+ return { transaction };
313
+ }
314
+ let simulatedUnits;
315
+ let computeUnitLimit = options?.computeUnitLimit;
316
+ // Auto-optimize by simulating transaction
317
+ if (options?.autoOptimize && !computeUnitLimit) {
318
+ try {
319
+ // Set a high limit for simulation
320
+ const simTransaction = new web3_js_1.Transaction().add(...transaction.instructions);
321
+ simTransaction.add(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({
322
+ units: 1400000, // Max for simulation
323
+ }));
324
+ simTransaction.recentBlockhash = (await this.connection.getLatestBlockhash()).blockhash;
325
+ simTransaction.feePayer = this.wallet.publicKey;
326
+ const simulation = await this.connection.simulateTransaction(simTransaction);
327
+ if (simulation.value.err === null && simulation.value.unitsConsumed) {
328
+ simulatedUnits = simulation.value.unitsConsumed;
329
+ const multiplier = options.computeUnitMultiplier ?? this.defaultComputeUnitMultiplier;
330
+ computeUnitLimit = Math.min(Math.ceil(simulatedUnits * multiplier), 1400000 // Max compute units
331
+ );
332
+ }
333
+ }
334
+ catch (error) {
335
+ console.warn('Failed to simulate transaction for compute unit optimization:', error);
336
+ // Fall back to default or specified limit
337
+ computeUnitLimit = computeUnitLimit ?? 200000;
338
+ }
339
+ }
340
+ // Create new transaction with compute budget instructions prepended
341
+ const optimizedTx = new web3_js_1.Transaction();
342
+ // Add compute unit limit if specified or auto-optimized
343
+ if (computeUnitLimit) {
344
+ optimizedTx.add(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({
345
+ units: computeUnitLimit,
346
+ }));
347
+ }
348
+ // Add priority fee if specified
349
+ if (options?.computeUnitPrice) {
350
+ optimizedTx.add(web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({
351
+ microLamports: options.computeUnitPrice,
352
+ }));
353
+ }
354
+ // Add original instructions
355
+ optimizedTx.add(...transaction.instructions);
356
+ return {
357
+ transaction: optimizedTx,
358
+ simulatedUnits,
359
+ };
360
+ }
241
361
  /**
242
362
  * Initialize the mailer program (owner only)
363
+ * @param computeOptions Compute unit optimization options
243
364
  */
244
- async initialize() {
365
+ async initialize(computeOptions) {
245
366
  const instruction = new web3_js_1.TransactionInstruction({
246
367
  keys: [
247
368
  { pubkey: this.wallet.publicKey, isSigner: true, isWritable: false },
@@ -252,7 +373,7 @@ class MailerClient {
252
373
  data: encodeInitialize(this.usdcMint),
253
374
  });
254
375
  const transaction = new web3_js_1.Transaction().add(instruction);
255
- return await this.sendTransaction(transaction);
376
+ return await this.sendTransaction(transaction, undefined, computeOptions);
256
377
  }
257
378
  /**
258
379
  * Send a message with optional revenue sharing
@@ -261,9 +382,10 @@ class MailerClient {
261
382
  * @param body Message body
262
383
  * @param revenueShareToReceiver If true, recipient gets 90% revenue share; if false, no revenue share
263
384
  * @param resolveSenderToName If true, resolve sender address to name via off-chain service
264
- * @returns Transaction signature
385
+ * @param computeOptions Compute unit optimization options
386
+ * @returns Transaction result with signature and compute details
265
387
  */
266
- async send(to, subject, body, revenueShareToReceiver = false, resolveSenderToName = false) {
388
+ async send(to, subject, body, revenueShareToReceiver = false, resolveSenderToName = false, computeOptions) {
267
389
  const recipientKey = typeof to === 'string' ? new web3_js_1.PublicKey(to) : to;
268
390
  // Derive recipient claim PDA
269
391
  const [recipientClaimPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('claim'), recipientKey.toBuffer()], this.programId);
@@ -293,13 +415,13 @@ class MailerClient {
293
415
  });
294
416
  instructions.push(sendInstruction);
295
417
  const transaction = new web3_js_1.Transaction().add(...instructions);
296
- return await this.sendTransaction(transaction);
418
+ return await this.sendTransaction(transaction, undefined, computeOptions);
297
419
  }
298
420
  /**
299
421
  * Claim recipient share of revenue
300
- * @returns Transaction signature
422
+ * @returns Transaction result
301
423
  */
302
- async claimRecipientShare() {
424
+ async claimRecipientShare(computeOptions) {
303
425
  const [recipientClaimPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('claim'), this.wallet.publicKey.toBuffer()], this.programId);
304
426
  const recipientTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(this.usdcMint, this.wallet.publicKey);
305
427
  const mailerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(this.usdcMint, this.mailerStatePda, true);
@@ -316,13 +438,13 @@ class MailerClient {
316
438
  data: encodeSimpleInstruction(InstructionType.ClaimRecipientShare),
317
439
  });
318
440
  const transaction = new web3_js_1.Transaction().add(instruction);
319
- return await this.sendTransaction(transaction);
441
+ return await this.sendTransaction(transaction, undefined, computeOptions);
320
442
  }
321
443
  /**
322
444
  * Claim owner share of fees (owner only)
323
- * @returns Transaction signature
445
+ * @returns Transaction result
324
446
  */
325
- async claimOwnerShare() {
447
+ async claimOwnerShare(computeOptions) {
326
448
  const ownerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(this.usdcMint, this.wallet.publicKey);
327
449
  const mailerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(this.usdcMint, this.mailerStatePda, true);
328
450
  const instruction = new web3_js_1.TransactionInstruction({
@@ -337,7 +459,7 @@ class MailerClient {
337
459
  data: encodeSimpleInstruction(InstructionType.ClaimOwnerShare),
338
460
  });
339
461
  const transaction = new web3_js_1.Transaction().add(instruction);
340
- return await this.sendTransaction(transaction);
462
+ return await this.sendTransaction(transaction, undefined, computeOptions);
341
463
  }
342
464
  /**
343
465
  * Claim expired recipient shares and move them under owner control
@@ -345,7 +467,7 @@ class MailerClient {
345
467
  * @param options Transaction confirm options
346
468
  * @returns Transaction signature
347
469
  */
348
- async claimExpiredShares(recipient, options) {
470
+ async claimExpiredShares(recipient, options, computeOptions) {
349
471
  const recipientKey = typeof recipient === 'string' ? new web3_js_1.PublicKey(recipient) : recipient;
350
472
  const [recipientClaimPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('claim'), recipientKey.toBuffer()], this.programId);
351
473
  const instruction = new web3_js_1.TransactionInstruction({
@@ -358,14 +480,14 @@ class MailerClient {
358
480
  data: encodeClaimExpiredShares(recipientKey),
359
481
  });
360
482
  const transaction = new web3_js_1.Transaction().add(instruction);
361
- return await this.sendTransaction(transaction, options);
483
+ return await this.sendTransaction(transaction, options, computeOptions);
362
484
  }
363
485
  /**
364
486
  * Delegate message handling to another address
365
487
  * @param delegate Address to delegate to, or null to clear delegation
366
488
  * @returns Transaction signature
367
489
  */
368
- async delegateTo(delegate) {
490
+ async delegateTo(delegate, computeOptions) {
369
491
  const delegateKey = delegate
370
492
  ? typeof delegate === 'string'
371
493
  ? new web3_js_1.PublicKey(delegate)
@@ -388,14 +510,14 @@ class MailerClient {
388
510
  data: encodeDelegateTo(delegateKey),
389
511
  });
390
512
  const transaction = new web3_js_1.Transaction().add(instruction);
391
- return await this.sendTransaction(transaction);
513
+ return await this.sendTransaction(transaction, undefined, computeOptions);
392
514
  }
393
515
  /**
394
516
  * Reject a delegation made to you
395
517
  * @param delegator Address that delegated to you
396
518
  * @returns Transaction signature
397
519
  */
398
- async rejectDelegation(delegator) {
520
+ async rejectDelegation(delegator, computeOptions) {
399
521
  const delegatorKey = typeof delegator === 'string' ? new web3_js_1.PublicKey(delegator) : delegator;
400
522
  const [delegationPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('delegation'), delegatorKey.toBuffer()], this.programId);
401
523
  const instruction = new web3_js_1.TransactionInstruction({
@@ -408,14 +530,15 @@ class MailerClient {
408
530
  data: encodeSimpleInstruction(InstructionType.RejectDelegation),
409
531
  });
410
532
  const transaction = new web3_js_1.Transaction().add(instruction);
411
- return await this.sendTransaction(transaction);
533
+ return await this.sendTransaction(transaction, undefined, computeOptions);
412
534
  }
413
535
  /**
414
536
  * Set the send fee (owner only)
415
537
  * @param newFee New fee in USDC micro-units (6 decimals)
416
- * @returns Transaction signature
538
+ * @param computeOptions Compute unit optimization options
539
+ * @returns Transaction result
417
540
  */
418
- async setFee(newFee) {
541
+ async setFee(newFee, computeOptions) {
419
542
  const instruction = new web3_js_1.TransactionInstruction({
420
543
  keys: [
421
544
  { pubkey: this.wallet.publicKey, isSigner: true, isWritable: false },
@@ -425,14 +548,14 @@ class MailerClient {
425
548
  data: encodeSetFee(typeof newFee === 'bigint' ? newFee : BigInt(newFee)),
426
549
  });
427
550
  const transaction = new web3_js_1.Transaction().add(instruction);
428
- return await this.sendTransaction(transaction);
551
+ return await this.sendTransaction(transaction, undefined, computeOptions);
429
552
  }
430
553
  /**
431
554
  * Set the delegation fee (owner only)
432
555
  * @param newFee New delegation fee in USDC micro-units (6 decimals)
433
556
  * @returns Transaction signature
434
557
  */
435
- async setDelegationFee(newFee) {
558
+ async setDelegationFee(newFee, computeOptions) {
436
559
  const instruction = new web3_js_1.TransactionInstruction({
437
560
  keys: [
438
561
  { pubkey: this.wallet.publicKey, isSigner: true, isWritable: false },
@@ -442,7 +565,59 @@ class MailerClient {
442
565
  data: encodeSetDelegationFee(typeof newFee === 'bigint' ? newFee : BigInt(newFee)),
443
566
  });
444
567
  const transaction = new web3_js_1.Transaction().add(instruction);
445
- return await this.sendTransaction(transaction);
568
+ return await this.sendTransaction(transaction, undefined, computeOptions);
569
+ }
570
+ async setCustomFeePercentage(account, percentage, payer, computeOptions) {
571
+ const normalizedPercentage = Math.trunc(percentage);
572
+ if (normalizedPercentage < 0 || normalizedPercentage > 100) {
573
+ throw new Error('Percentage must be between 0 and 100');
574
+ }
575
+ const accountKey = typeof account === 'string' ? new web3_js_1.PublicKey(account) : account;
576
+ const payerKey = payer
577
+ ? typeof payer === 'string'
578
+ ? new web3_js_1.PublicKey(payer)
579
+ : payer
580
+ : this.wallet.publicKey;
581
+ const [discountPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('discount'), accountKey.toBuffer()], this.programId);
582
+ const instruction = new web3_js_1.TransactionInstruction({
583
+ keys: [
584
+ { pubkey: this.wallet.publicKey, isSigner: true, isWritable: false },
585
+ { pubkey: this.mailerStatePda, isSigner: false, isWritable: false },
586
+ { pubkey: discountPda, isSigner: false, isWritable: true },
587
+ { pubkey: accountKey, isSigner: false, isWritable: false },
588
+ { pubkey: payerKey, isSigner: true, isWritable: true },
589
+ { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
590
+ ],
591
+ programId: this.programId,
592
+ data: encodeSetCustomFeePercentage(accountKey, normalizedPercentage),
593
+ });
594
+ const transaction = new web3_js_1.Transaction().add(instruction);
595
+ return this.sendTransaction(transaction, undefined, computeOptions);
596
+ }
597
+ async clearCustomFeePercentage(account, computeOptions) {
598
+ const accountKey = typeof account === 'string' ? new web3_js_1.PublicKey(account) : account;
599
+ const [discountPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('discount'), accountKey.toBuffer()], this.programId);
600
+ const instruction = new web3_js_1.TransactionInstruction({
601
+ keys: [
602
+ { pubkey: this.wallet.publicKey, isSigner: true, isWritable: false },
603
+ { pubkey: this.mailerStatePda, isSigner: false, isWritable: false },
604
+ { pubkey: discountPda, isSigner: false, isWritable: true },
605
+ ],
606
+ programId: this.programId,
607
+ data: encodeClearCustomFeePercentage(accountKey),
608
+ });
609
+ const transaction = new web3_js_1.Transaction().add(instruction);
610
+ return this.sendTransaction(transaction, undefined, computeOptions);
611
+ }
612
+ async getCustomFeePercentage(account) {
613
+ const accountKey = typeof account === 'string' ? new web3_js_1.PublicKey(account) : account;
614
+ const [discountPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('discount'), accountKey.toBuffer()], this.programId);
615
+ const accountInfo = await this.connection.getAccountInfo(discountPda);
616
+ if (!accountInfo) {
617
+ return 100;
618
+ }
619
+ const discountState = parseFeeDiscount(accountInfo.data);
620
+ return 100 - discountState.discount;
446
621
  }
447
622
  /**
448
623
  * Get current fees from the mailer state
@@ -530,7 +705,7 @@ class MailerClient {
530
705
  * @param options Transaction confirm options
531
706
  * @returns Transaction signature
532
707
  */
533
- async sendToEmail(toEmail, subject, body, options) {
708
+ async sendToEmail(toEmail, subject, body, options, computeOptions) {
534
709
  // Get associated token accounts
535
710
  const senderTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(this.usdcMint, this.wallet.publicKey);
536
711
  const mailerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(this.usdcMint, this.mailerStatePda, true);
@@ -556,7 +731,7 @@ class MailerClient {
556
731
  data: instructionData,
557
732
  }));
558
733
  const transaction = new web3_js_1.Transaction().add(...instructions);
559
- return this.sendTransaction(transaction, options);
734
+ return this.sendTransaction(transaction, options, computeOptions);
560
735
  }
561
736
  /**
562
737
  * Send a prepared message to an email address (no wallet known)
@@ -566,7 +741,7 @@ class MailerClient {
566
741
  * @param options Transaction confirm options
567
742
  * @returns Transaction signature
568
743
  */
569
- async sendPreparedToEmail(toEmail, mailId, options) {
744
+ async sendPreparedToEmail(toEmail, mailId, options, computeOptions) {
570
745
  // Get associated token accounts
571
746
  const senderTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(this.usdcMint, this.wallet.publicKey);
572
747
  const mailerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(this.usdcMint, this.mailerStatePda, true);
@@ -592,7 +767,7 @@ class MailerClient {
592
767
  data: instructionData,
593
768
  }));
594
769
  const transaction = new web3_js_1.Transaction().add(...instructions);
595
- return this.sendTransaction(transaction, options);
770
+ return this.sendTransaction(transaction, options, computeOptions);
596
771
  }
597
772
  /**
598
773
  * Send a prepared message using a mailId (to match EVM behavior)
@@ -602,16 +777,66 @@ class MailerClient {
602
777
  * @param resolveSenderToName If true, resolve sender address to name
603
778
  * @returns Transaction signature
604
779
  */
605
- async sendPrepared(to, mailId, revenueShareToReceiver = false, resolveSenderToName = false) {
606
- // For Solana, we send the mailId as both subject and body to indicate it's a prepared message
607
- return this.send(to, mailId, '', revenueShareToReceiver, resolveSenderToName);
780
+ async sendPrepared(to, mailId, revenueShareToReceiver = false, resolveSenderToName = false, computeOptions) {
781
+ const recipientKey = typeof to === 'string' ? new web3_js_1.PublicKey(to) : to;
782
+ const [recipientClaimPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('claim'), recipientKey.toBuffer()], this.programId);
783
+ const senderTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(this.usdcMint, this.wallet.publicKey);
784
+ const mailerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(this.usdcMint, this.mailerStatePda, true);
785
+ const instructions = [];
786
+ const mailerTokenInfo = await this.connection.getAccountInfo(mailerTokenAccount);
787
+ if (!mailerTokenInfo) {
788
+ instructions.push((0, spl_token_1.createAssociatedTokenAccountInstruction)(this.wallet.publicKey, mailerTokenAccount, this.mailerStatePda, this.usdcMint));
789
+ }
790
+ const sendInstruction = new web3_js_1.TransactionInstruction({
791
+ keys: [
792
+ { pubkey: this.wallet.publicKey, isSigner: true, isWritable: false },
793
+ { pubkey: recipientClaimPda, isSigner: false, isWritable: true },
794
+ { pubkey: this.mailerStatePda, isSigner: false, isWritable: false },
795
+ { pubkey: senderTokenAccount, isSigner: false, isWritable: true },
796
+ { pubkey: mailerTokenAccount, isSigner: false, isWritable: true },
797
+ { pubkey: spl_token_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
798
+ { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
799
+ ],
800
+ programId: this.programId,
801
+ data: encodeSendPrepared(recipientKey, mailId, revenueShareToReceiver, resolveSenderToName),
802
+ });
803
+ instructions.push(sendInstruction);
804
+ const transaction = new web3_js_1.Transaction().add(...instructions);
805
+ return this.sendTransaction(transaction, undefined, computeOptions);
806
+ }
807
+ async sendThroughWebhook(to, webhookId, revenueShareToReceiver = false, resolveSenderToName = false, computeOptions) {
808
+ const recipientKey = typeof to === 'string' ? new web3_js_1.PublicKey(to) : to;
809
+ const [recipientClaimPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('claim'), recipientKey.toBuffer()], this.programId);
810
+ const senderTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(this.usdcMint, this.wallet.publicKey);
811
+ const mailerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(this.usdcMint, this.mailerStatePda, true);
812
+ const instructions = [];
813
+ const mailerTokenInfo = await this.connection.getAccountInfo(mailerTokenAccount);
814
+ if (!mailerTokenInfo) {
815
+ instructions.push((0, spl_token_1.createAssociatedTokenAccountInstruction)(this.wallet.publicKey, mailerTokenAccount, this.mailerStatePda, this.usdcMint));
816
+ }
817
+ const sendInstruction = new web3_js_1.TransactionInstruction({
818
+ keys: [
819
+ { pubkey: this.wallet.publicKey, isSigner: true, isWritable: false },
820
+ { pubkey: recipientClaimPda, isSigner: false, isWritable: true },
821
+ { pubkey: this.mailerStatePda, isSigner: false, isWritable: false },
822
+ { pubkey: senderTokenAccount, isSigner: false, isWritable: true },
823
+ { pubkey: mailerTokenAccount, isSigner: false, isWritable: true },
824
+ { pubkey: spl_token_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
825
+ { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
826
+ ],
827
+ programId: this.programId,
828
+ data: encodeSendThroughWebhook(recipientKey, webhookId, revenueShareToReceiver, resolveSenderToName),
829
+ });
830
+ instructions.push(sendInstruction);
831
+ const transaction = new web3_js_1.Transaction().add(...instructions);
832
+ return this.sendTransaction(transaction, undefined, computeOptions);
608
833
  }
609
834
  /**
610
835
  * Pause the contract and distribute owner claimable funds (owner only)
611
836
  * @param options Transaction confirm options
612
837
  * @returns Transaction signature
613
838
  */
614
- async pause(options) {
839
+ async pause(options, computeOptions) {
615
840
  const ownerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(this.usdcMint, this.wallet.publicKey);
616
841
  const mailerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(this.usdcMint, this.mailerStatePda, true);
617
842
  const instruction = new web3_js_1.TransactionInstruction({
@@ -626,14 +851,14 @@ class MailerClient {
626
851
  data: encodeSimpleInstruction(InstructionType.Pause),
627
852
  });
628
853
  const transaction = new web3_js_1.Transaction().add(instruction);
629
- return await this.sendTransaction(transaction, options);
854
+ return await this.sendTransaction(transaction, options, computeOptions);
630
855
  }
631
856
  /**
632
857
  * Unpause the contract (owner only)
633
858
  * @param options Transaction confirm options
634
859
  * @returns Transaction signature
635
860
  */
636
- async unpause(options) {
861
+ async unpause(options, computeOptions) {
637
862
  const instruction = new web3_js_1.TransactionInstruction({
638
863
  keys: [
639
864
  { pubkey: this.wallet.publicKey, isSigner: true, isWritable: false },
@@ -643,14 +868,14 @@ class MailerClient {
643
868
  data: encodeSimpleInstruction(InstructionType.Unpause),
644
869
  });
645
870
  const transaction = new web3_js_1.Transaction().add(instruction);
646
- return await this.sendTransaction(transaction, options);
871
+ return await this.sendTransaction(transaction, options, computeOptions);
647
872
  }
648
873
  /**
649
874
  * Emergency unpause without fund distribution (owner only)
650
875
  * @param options Transaction confirm options
651
876
  * @returns Transaction signature
652
877
  */
653
- async emergencyUnpause(options) {
878
+ async emergencyUnpause(options, computeOptions) {
654
879
  const instruction = new web3_js_1.TransactionInstruction({
655
880
  keys: [
656
881
  { pubkey: this.wallet.publicKey, isSigner: true, isWritable: false },
@@ -660,7 +885,7 @@ class MailerClient {
660
885
  data: encodeSimpleInstruction(InstructionType.EmergencyUnpause),
661
886
  });
662
887
  const transaction = new web3_js_1.Transaction().add(instruction);
663
- return await this.sendTransaction(transaction, options);
888
+ return await this.sendTransaction(transaction, options, computeOptions);
664
889
  }
665
890
  /**
666
891
  * Distribute claimable funds to a recipient when contract is paused
@@ -668,7 +893,7 @@ class MailerClient {
668
893
  * @param options Transaction confirm options
669
894
  * @returns Transaction signature
670
895
  */
671
- async distributeClaimableFunds(recipient, options) {
896
+ async distributeClaimableFunds(recipient, options, computeOptions) {
672
897
  const recipientKey = typeof recipient === 'string' ? new web3_js_1.PublicKey(recipient) : recipient;
673
898
  const [recipientClaimPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('claim'), recipientKey.toBuffer()], this.programId);
674
899
  const recipientTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(this.usdcMint, recipientKey);
@@ -690,7 +915,7 @@ class MailerClient {
690
915
  data,
691
916
  });
692
917
  const transaction = new web3_js_1.Transaction().add(instruction);
693
- return await this.sendTransaction(transaction, options);
918
+ return await this.sendTransaction(transaction, options, computeOptions);
694
919
  }
695
920
  /**
696
921
  * Get the current send fee
@@ -719,17 +944,24 @@ class MailerClient {
719
944
  * @param options Confirm options
720
945
  * @returns Transaction signature
721
946
  */
722
- async sendTransaction(transaction, options) {
947
+ async sendTransaction(transaction, options, computeOptions) {
948
+ // Optimize compute units if requested
949
+ const { transaction: optimizedTx, simulatedUnits } = await this.optimizeComputeUnits(transaction, computeOptions);
723
950
  // Get recent blockhash
724
951
  const { blockhash } = await this.connection.getLatestBlockhash();
725
- transaction.recentBlockhash = blockhash;
726
- transaction.feePayer = this.wallet.publicKey;
952
+ optimizedTx.recentBlockhash = blockhash;
953
+ optimizedTx.feePayer = this.wallet.publicKey;
727
954
  // Sign transaction
728
- const signedTx = await this.wallet.signTransaction(transaction);
955
+ const signedTx = await this.wallet.signTransaction(optimizedTx);
729
956
  // Send and confirm
730
957
  const signature = await this.connection.sendRawTransaction(signedTx.serialize());
731
958
  await this.connection.confirmTransaction(signature, options?.commitment || 'confirmed');
732
- return signature;
959
+ return {
960
+ signature,
961
+ simulatedUnits,
962
+ computeUnitLimit: computeOptions?.computeUnitLimit,
963
+ computeUnitPrice: computeOptions?.computeUnitPrice,
964
+ };
733
965
  }
734
966
  /**
735
967
  * Create a simple wallet from a keypair for testing