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