@sudobility/contracts 1.14.0 → 1.14.2

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.
@@ -11,22 +11,27 @@ var InstructionType;
11
11
  InstructionType[InstructionType["Initialize"] = 0] = "Initialize";
12
12
  InstructionType[InstructionType["Send"] = 1] = "Send";
13
13
  InstructionType[InstructionType["SendPrepared"] = 2] = "SendPrepared";
14
- InstructionType[InstructionType["SendThroughWebhook"] = 3] = "SendThroughWebhook";
15
- InstructionType[InstructionType["SendToEmail"] = 4] = "SendToEmail";
16
- InstructionType[InstructionType["SendPreparedToEmail"] = 5] = "SendPreparedToEmail";
14
+ InstructionType[InstructionType["SendToEmail"] = 3] = "SendToEmail";
15
+ InstructionType[InstructionType["SendPreparedToEmail"] = 4] = "SendPreparedToEmail";
16
+ InstructionType[InstructionType["SendThroughWebhook"] = 5] = "SendThroughWebhook";
17
17
  InstructionType[InstructionType["ClaimRecipientShare"] = 6] = "ClaimRecipientShare";
18
18
  InstructionType[InstructionType["ClaimOwnerShare"] = 7] = "ClaimOwnerShare";
19
- InstructionType[InstructionType["ClaimExpiredShares"] = 8] = "ClaimExpiredShares";
20
- InstructionType[InstructionType["SetFees"] = 9] = "SetFees";
21
- InstructionType[InstructionType["DelegateTo"] = 10] = "DelegateTo";
22
- InstructionType[InstructionType["RejectDelegation"] = 11] = "RejectDelegation";
19
+ InstructionType[InstructionType["SetFee"] = 8] = "SetFee";
20
+ InstructionType[InstructionType["DelegateTo"] = 9] = "DelegateTo";
21
+ InstructionType[InstructionType["RejectDelegation"] = 10] = "RejectDelegation";
22
+ InstructionType[InstructionType["SetDelegationFee"] = 11] = "SetDelegationFee";
23
23
  InstructionType[InstructionType["SetCustomFeePercentage"] = 12] = "SetCustomFeePercentage";
24
24
  InstructionType[InstructionType["ClearCustomFeePercentage"] = 13] = "ClearCustomFeePercentage";
25
25
  InstructionType[InstructionType["Pause"] = 14] = "Pause";
26
26
  InstructionType[InstructionType["Unpause"] = 15] = "Unpause";
27
- InstructionType[InstructionType["EmergencyUnpause"] = 16] = "EmergencyUnpause";
28
- InstructionType[InstructionType["DistributeClaimableFunds"] = 17] = "DistributeClaimableFunds";
27
+ InstructionType[InstructionType["DistributeClaimableFunds"] = 16] = "DistributeClaimableFunds";
28
+ InstructionType[InstructionType["ClaimExpiredShares"] = 17] = "ClaimExpiredShares";
29
+ InstructionType[InstructionType["EmergencyUnpause"] = 18] = "EmergencyUnpause";
29
30
  })(InstructionType || (InstructionType = {}));
31
+ const CLAIM_PDA_SEED = Buffer.from('claim');
32
+ const DELEGATION_PDA_SEED = Buffer.from('delegation');
33
+ const DISCOUNT_PDA_SEED = Buffer.from('discount');
34
+ const CLAIM_PERIOD_SECONDS = 60 * 24 * 60 * 60;
30
35
  // Instruction data encoding functions
31
36
  function encodeInitialize(usdcMint) {
32
37
  const data = Buffer.alloc(1 + 32);
@@ -73,43 +78,35 @@ function encodeSendPrepared(to, mailId, revenueShareToReceiver, resolveSenderToN
73
78
  data.writeUInt8(resolveSenderToName ? 1 : 0, offset);
74
79
  return data;
75
80
  }
76
- function encodeSendThroughWebhook(to, subject, body, webhookId, revenueShareToReceiver) {
77
- const subjectBytes = Buffer.from(subject, 'utf8');
78
- const bodyBytes = Buffer.from(body, 'utf8');
81
+ function encodeSendThroughWebhook(to, webhookId, revenueShareToReceiver, resolveSenderToName = false) {
79
82
  const webhookIdBytes = Buffer.from(webhookId, 'utf8');
80
- const data = Buffer.alloc(1 + 32 + 4 + subjectBytes.length + 4 + bodyBytes.length + 4 + webhookIdBytes.length + 1);
83
+ const data = Buffer.alloc(1 + 32 + 4 + webhookIdBytes.length + 1 + 1);
81
84
  let offset = 0;
82
85
  data.writeUInt8(InstructionType.SendThroughWebhook, offset);
83
86
  offset += 1;
84
87
  to.toBuffer().copy(data, offset);
85
88
  offset += 32;
86
- data.writeUInt32LE(subjectBytes.length, offset);
87
- offset += 4;
88
- subjectBytes.copy(data, offset);
89
- offset += subjectBytes.length;
90
- data.writeUInt32LE(bodyBytes.length, offset);
91
- offset += 4;
92
- bodyBytes.copy(data, offset);
93
- offset += bodyBytes.length;
94
89
  data.writeUInt32LE(webhookIdBytes.length, offset);
95
90
  offset += 4;
96
91
  webhookIdBytes.copy(data, offset);
97
92
  offset += webhookIdBytes.length;
98
93
  data.writeUInt8(revenueShareToReceiver ? 1 : 0, offset);
94
+ offset += 1;
95
+ data.writeUInt8(resolveSenderToName ? 1 : 0, offset);
99
96
  return data;
100
97
  }
101
- function encodeSendToEmail(emailHash, subject, body, payer, revenueShareToReceiver) {
102
- const emailHashBytes = Buffer.from(emailHash, 'utf8');
98
+ function encodeSendToEmail(toEmail, subject, body) {
99
+ const emailBytes = Buffer.from(toEmail, 'utf8');
103
100
  const subjectBytes = Buffer.from(subject, 'utf8');
104
101
  const bodyBytes = Buffer.from(body, 'utf8');
105
- const data = Buffer.alloc(1 + 4 + emailHashBytes.length + 4 + subjectBytes.length + 4 + bodyBytes.length + 32 + 1);
102
+ const data = Buffer.alloc(1 + 4 + emailBytes.length + 4 + subjectBytes.length + 4 + bodyBytes.length);
106
103
  let offset = 0;
107
104
  data.writeUInt8(InstructionType.SendToEmail, offset);
108
105
  offset += 1;
109
- data.writeUInt32LE(emailHashBytes.length, offset);
106
+ data.writeUInt32LE(emailBytes.length, offset);
110
107
  offset += 4;
111
- emailHashBytes.copy(data, offset);
112
- offset += emailHashBytes.length;
108
+ emailBytes.copy(data, offset);
109
+ offset += emailBytes.length;
113
110
  data.writeUInt32LE(subjectBytes.length, offset);
114
111
  offset += 4;
115
112
  subjectBytes.copy(data, offset);
@@ -118,36 +115,35 @@ function encodeSendToEmail(emailHash, subject, body, payer, revenueShareToReceiv
118
115
  offset += 4;
119
116
  bodyBytes.copy(data, offset);
120
117
  offset += bodyBytes.length;
121
- payer.toBuffer().copy(data, offset);
122
- offset += 32;
123
- data.writeUInt8(revenueShareToReceiver ? 1 : 0, offset);
124
118
  return data;
125
119
  }
126
- function encodeSendPreparedToEmail(emailHash, mailId, payer, revenueShareToReceiver) {
127
- const emailHashBytes = Buffer.from(emailHash, 'utf8');
120
+ function encodeSendPreparedToEmail(toEmail, mailId) {
121
+ const emailBytes = Buffer.from(toEmail, 'utf8');
128
122
  const mailIdBytes = Buffer.from(mailId, 'utf8');
129
- const data = Buffer.alloc(1 + 4 + emailHashBytes.length + 4 + mailIdBytes.length + 32 + 1);
123
+ const data = Buffer.alloc(1 + 4 + emailBytes.length + 4 + mailIdBytes.length);
130
124
  let offset = 0;
131
125
  data.writeUInt8(InstructionType.SendPreparedToEmail, offset);
132
126
  offset += 1;
133
- data.writeUInt32LE(emailHashBytes.length, offset);
127
+ data.writeUInt32LE(emailBytes.length, offset);
134
128
  offset += 4;
135
- emailHashBytes.copy(data, offset);
136
- offset += emailHashBytes.length;
129
+ emailBytes.copy(data, offset);
130
+ offset += emailBytes.length;
137
131
  data.writeUInt32LE(mailIdBytes.length, offset);
138
132
  offset += 4;
139
133
  mailIdBytes.copy(data, offset);
140
134
  offset += mailIdBytes.length;
141
- payer.toBuffer().copy(data, offset);
142
- offset += 32;
143
- data.writeUInt8(revenueShareToReceiver ? 1 : 0, offset);
144
135
  return data;
145
136
  }
146
- function encodeSetFees(sendFee, delegationFee) {
147
- const data = Buffer.alloc(1 + 8 + 8);
148
- data.writeUInt8(InstructionType.SetFees, 0);
137
+ function encodeSetFee(sendFee) {
138
+ const data = Buffer.alloc(1 + 8);
139
+ data.writeUInt8(InstructionType.SetFee, 0);
149
140
  data.writeBigUInt64LE(sendFee, 1);
150
- data.writeBigUInt64LE(delegationFee, 9);
141
+ return data;
142
+ }
143
+ function encodeSetDelegationFee(delegationFee) {
144
+ const data = Buffer.alloc(1 + 8);
145
+ data.writeUInt8(InstructionType.SetDelegationFee, 0);
146
+ data.writeBigUInt64LE(delegationFee, 1);
151
147
  return data;
152
148
  }
153
149
  function encodeDelegateTo(delegate) {
@@ -159,10 +155,9 @@ function encodeDelegateTo(delegate) {
159
155
  }
160
156
  return data;
161
157
  }
162
- function encodeRejectDelegation(delegatingAddress) {
163
- const data = Buffer.alloc(1 + 32);
158
+ function encodeRejectDelegation() {
159
+ const data = Buffer.alloc(1);
164
160
  data.writeUInt8(InstructionType.RejectDelegation, 0);
165
- delegatingAddress.toBuffer().copy(data, 1);
166
161
  return data;
167
162
  }
168
163
  function encodeSetCustomFeePercentage(account, percentage) {
@@ -187,18 +182,10 @@ function encodeClaimExpiredShares(recipient) {
187
182
  recipient.toBuffer().copy(data, 1);
188
183
  return data;
189
184
  }
190
- function encodeDistributeClaimableFunds(recipients) {
191
- const recipientCount = recipients.length;
192
- const data = Buffer.alloc(1 + 4 + recipientCount * 32);
193
- let offset = 0;
194
- data.writeUInt8(InstructionType.DistributeClaimableFunds, offset);
195
- offset += 1;
196
- data.writeUInt32LE(recipientCount, offset);
197
- offset += 4;
198
- for (const recipient of recipients) {
199
- recipient.toBuffer().copy(data, offset);
200
- offset += 32;
201
- }
185
+ function encodeDistributeClaimableFunds(recipient) {
186
+ const data = Buffer.alloc(1 + 32);
187
+ data.writeUInt8(InstructionType.DistributeClaimableFunds, 0);
188
+ recipient.toBuffer().copy(data, 1);
202
189
  return data;
203
190
  }
204
191
  /**
@@ -397,7 +384,7 @@ class SolanaMailerClient {
397
384
  /**
398
385
  * Send a message with optional revenue sharing
399
386
  */
400
- async send(connectedWallet, chainInfo, to, subject, body, revenueShareToReceiver, computeOptions) {
387
+ async send(connectedWallet, chainInfo, to, subject, body, revenueShareToReceiver, resolveSenderToName = false, computeOptions) {
401
388
  const connection = await this.getOrCreateConnection(chainInfo, connectedWallet.connection);
402
389
  const { programId, mailerStatePda } = this.getProgramAddresses(chainInfo);
403
390
  if (!chainInfo.usdcAddress) {
@@ -405,24 +392,22 @@ class SolanaMailerClient {
405
392
  }
406
393
  const usdcMint = new web3_js_1.PublicKey(chainInfo.usdcAddress);
407
394
  const toPubkey = typeof to === 'string' ? new web3_js_1.PublicKey(to) : to;
408
- // Get token accounts
409
395
  const senderTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(usdcMint, connectedWallet.wallet.publicKey, false, spl_token_1.TOKEN_PROGRAM_ID);
410
396
  const mailerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(usdcMint, mailerStatePda, true, spl_token_1.TOKEN_PROGRAM_ID);
411
- const [recipientInfo] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('recipient_info'), toPubkey.toBuffer()], programId);
397
+ const [recipientClaimPda] = web3_js_1.PublicKey.findProgramAddressSync([CLAIM_PDA_SEED, toPubkey.toBuffer()], programId);
412
398
  const keys = [
413
399
  { pubkey: connectedWallet.wallet.publicKey, isSigner: true, isWritable: true },
414
- { pubkey: toPubkey, isSigner: false, isWritable: false },
400
+ { pubkey: recipientClaimPda, isSigner: false, isWritable: true },
415
401
  { pubkey: mailerStatePda, isSigner: false, isWritable: true },
416
402
  { pubkey: senderTokenAccount, isSigner: false, isWritable: true },
417
403
  { pubkey: mailerTokenAccount, isSigner: false, isWritable: true },
418
- { pubkey: recipientInfo, isSigner: false, isWritable: true },
419
404
  { pubkey: spl_token_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
420
405
  { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
421
406
  ];
422
407
  const instruction = new web3_js_1.TransactionInstruction({
423
408
  programId,
424
409
  keys,
425
- data: encodeSend(toPubkey, subject, body, revenueShareToReceiver),
410
+ data: encodeSend(toPubkey, subject, body, revenueShareToReceiver, resolveSenderToName),
426
411
  });
427
412
  const transaction = new web3_js_1.Transaction().add(instruction);
428
413
  // Check if mailer token account exists, create if not
@@ -435,7 +420,7 @@ class SolanaMailerClient {
435
420
  /**
436
421
  * Send a prepared message
437
422
  */
438
- async sendPrepared(connectedWallet, chainInfo, to, mailId, revenueShareToReceiver, computeOptions) {
423
+ async sendPrepared(connectedWallet, chainInfo, to, mailId, revenueShareToReceiver, resolveSenderToName = false, computeOptions) {
439
424
  const connection = await this.getOrCreateConnection(chainInfo, connectedWallet.connection);
440
425
  const { programId, mailerStatePda } = this.getProgramAddresses(chainInfo);
441
426
  if (!chainInfo.usdcAddress) {
@@ -445,21 +430,20 @@ class SolanaMailerClient {
445
430
  const toPubkey = typeof to === 'string' ? new web3_js_1.PublicKey(to) : to;
446
431
  const senderTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(usdcMint, connectedWallet.wallet.publicKey, false, spl_token_1.TOKEN_PROGRAM_ID);
447
432
  const mailerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(usdcMint, mailerStatePda, true, spl_token_1.TOKEN_PROGRAM_ID);
448
- const [recipientInfo] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('recipient_info'), toPubkey.toBuffer()], programId);
433
+ const [recipientClaimPda] = web3_js_1.PublicKey.findProgramAddressSync([CLAIM_PDA_SEED, toPubkey.toBuffer()], programId);
449
434
  const keys = [
450
435
  { pubkey: connectedWallet.wallet.publicKey, isSigner: true, isWritable: true },
451
- { pubkey: toPubkey, isSigner: false, isWritable: false },
436
+ { pubkey: recipientClaimPda, isSigner: false, isWritable: true },
452
437
  { pubkey: mailerStatePda, isSigner: false, isWritable: true },
453
438
  { pubkey: senderTokenAccount, isSigner: false, isWritable: true },
454
439
  { pubkey: mailerTokenAccount, isSigner: false, isWritable: true },
455
- { pubkey: recipientInfo, isSigner: false, isWritable: true },
456
440
  { pubkey: spl_token_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
457
441
  { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
458
442
  ];
459
443
  const instruction = new web3_js_1.TransactionInstruction({
460
444
  programId,
461
445
  keys,
462
- data: encodeSendPrepared(toPubkey, mailId, revenueShareToReceiver),
446
+ data: encodeSendPrepared(toPubkey, mailId, revenueShareToReceiver, resolveSenderToName),
463
447
  });
464
448
  const transaction = new web3_js_1.Transaction().add(instruction);
465
449
  // Check if mailer token account exists, create if not
@@ -472,7 +456,7 @@ class SolanaMailerClient {
472
456
  /**
473
457
  * Send through webhook
474
458
  */
475
- async sendThroughWebhook(connectedWallet, chainInfo, to, subject, body, webhookId, revenueShareToReceiver, computeOptions) {
459
+ async sendThroughWebhook(connectedWallet, chainInfo, to, webhookId, revenueShareToReceiver, resolveSenderToName = false, computeOptions) {
476
460
  const connection = await this.getOrCreateConnection(chainInfo, connectedWallet.connection);
477
461
  const { programId, mailerStatePda } = this.getProgramAddresses(chainInfo);
478
462
  if (!chainInfo.usdcAddress) {
@@ -482,21 +466,20 @@ class SolanaMailerClient {
482
466
  const toPubkey = typeof to === 'string' ? new web3_js_1.PublicKey(to) : to;
483
467
  const senderTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(usdcMint, connectedWallet.wallet.publicKey, false, spl_token_1.TOKEN_PROGRAM_ID);
484
468
  const mailerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(usdcMint, mailerStatePda, true, spl_token_1.TOKEN_PROGRAM_ID);
485
- const [recipientInfo] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('recipient_info'), toPubkey.toBuffer()], programId);
469
+ const [recipientClaimPda] = web3_js_1.PublicKey.findProgramAddressSync([CLAIM_PDA_SEED, toPubkey.toBuffer()], programId);
486
470
  const keys = [
487
471
  { pubkey: connectedWallet.wallet.publicKey, isSigner: true, isWritable: true },
488
- { pubkey: toPubkey, isSigner: false, isWritable: false },
472
+ { pubkey: recipientClaimPda, isSigner: false, isWritable: true },
489
473
  { pubkey: mailerStatePda, isSigner: false, isWritable: true },
490
474
  { pubkey: senderTokenAccount, isSigner: false, isWritable: true },
491
475
  { pubkey: mailerTokenAccount, isSigner: false, isWritable: true },
492
- { pubkey: recipientInfo, isSigner: false, isWritable: true },
493
476
  { pubkey: spl_token_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
494
477
  { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
495
478
  ];
496
479
  const instruction = new web3_js_1.TransactionInstruction({
497
480
  programId,
498
481
  keys,
499
- data: encodeSendThroughWebhook(toPubkey, subject, body, webhookId, revenueShareToReceiver),
482
+ data: encodeSendThroughWebhook(toPubkey, webhookId, revenueShareToReceiver, resolveSenderToName),
500
483
  });
501
484
  const transaction = new web3_js_1.Transaction().add(instruction);
502
485
  const accountInfo = await connection.getAccountInfo(mailerTokenAccount);
@@ -508,14 +491,13 @@ class SolanaMailerClient {
508
491
  /**
509
492
  * Send to email address
510
493
  */
511
- async sendToEmail(emailHash, subject, body, payer, revenueShareToReceiver, connectedWallet, chainInfo, computeOptions) {
494
+ async sendToEmail(emailHash, subject, body, _payer, _revenueShareToReceiver, connectedWallet, chainInfo, computeOptions) {
512
495
  const connection = await this.getOrCreateConnection(chainInfo, connectedWallet.connection);
513
496
  const { programId, mailerStatePda } = this.getProgramAddresses(chainInfo);
514
497
  if (!chainInfo.usdcAddress) {
515
498
  throw new Error(`No USDC mint configured for ${chainInfo.name}`);
516
499
  }
517
500
  const usdcMint = new web3_js_1.PublicKey(chainInfo.usdcAddress);
518
- const payerPubkey = typeof payer === 'string' ? new web3_js_1.PublicKey(payer) : payer;
519
501
  const senderTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(usdcMint, connectedWallet.wallet.publicKey, false, spl_token_1.TOKEN_PROGRAM_ID);
520
502
  const mailerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(usdcMint, mailerStatePda, true, spl_token_1.TOKEN_PROGRAM_ID);
521
503
  const keys = [
@@ -524,12 +506,11 @@ class SolanaMailerClient {
524
506
  { pubkey: senderTokenAccount, isSigner: false, isWritable: true },
525
507
  { pubkey: mailerTokenAccount, isSigner: false, isWritable: true },
526
508
  { pubkey: spl_token_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
527
- { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
528
509
  ];
529
510
  const instruction = new web3_js_1.TransactionInstruction({
530
511
  programId,
531
512
  keys,
532
- data: encodeSendToEmail(emailHash, subject, body, payerPubkey, revenueShareToReceiver),
513
+ data: encodeSendToEmail(emailHash, subject, body),
533
514
  });
534
515
  const transaction = new web3_js_1.Transaction().add(instruction);
535
516
  const accountInfo = await connection.getAccountInfo(mailerTokenAccount);
@@ -541,14 +522,13 @@ class SolanaMailerClient {
541
522
  /**
542
523
  * Send prepared to email address
543
524
  */
544
- async sendPreparedToEmail(emailHash, mailId, payer, revenueShareToReceiver, connectedWallet, chainInfo, computeOptions) {
525
+ async sendPreparedToEmail(emailHash, mailId, _payer, _revenueShareToReceiver, connectedWallet, chainInfo, computeOptions) {
545
526
  const connection = await this.getOrCreateConnection(chainInfo, connectedWallet.connection);
546
527
  const { programId, mailerStatePda } = this.getProgramAddresses(chainInfo);
547
528
  if (!chainInfo.usdcAddress) {
548
529
  throw new Error(`No USDC mint configured for ${chainInfo.name}`);
549
530
  }
550
531
  const usdcMint = new web3_js_1.PublicKey(chainInfo.usdcAddress);
551
- const payerPubkey = typeof payer === 'string' ? new web3_js_1.PublicKey(payer) : payer;
552
532
  const senderTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(usdcMint, connectedWallet.wallet.publicKey, false, spl_token_1.TOKEN_PROGRAM_ID);
553
533
  const mailerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(usdcMint, mailerStatePda, true, spl_token_1.TOKEN_PROGRAM_ID);
554
534
  const keys = [
@@ -557,12 +537,11 @@ class SolanaMailerClient {
557
537
  { pubkey: senderTokenAccount, isSigner: false, isWritable: true },
558
538
  { pubkey: mailerTokenAccount, isSigner: false, isWritable: true },
559
539
  { pubkey: spl_token_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
560
- { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
561
540
  ];
562
541
  const instruction = new web3_js_1.TransactionInstruction({
563
542
  programId,
564
543
  keys,
565
- data: encodeSendPreparedToEmail(emailHash, mailId, payerPubkey, revenueShareToReceiver),
544
+ data: encodeSendPreparedToEmail(emailHash, mailId),
566
545
  });
567
546
  const transaction = new web3_js_1.Transaction().add(instruction);
568
547
  const accountInfo = await connection.getAccountInfo(mailerTokenAccount);
@@ -583,13 +562,13 @@ class SolanaMailerClient {
583
562
  const usdcMint = new web3_js_1.PublicKey(chainInfo.usdcAddress);
584
563
  const recipientTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(usdcMint, connectedWallet.wallet.publicKey, false, spl_token_1.TOKEN_PROGRAM_ID);
585
564
  const mailerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(usdcMint, mailerStatePda, true, spl_token_1.TOKEN_PROGRAM_ID);
586
- const [recipientInfo] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('recipient_info'), connectedWallet.wallet.publicKey.toBuffer()], programId);
565
+ const [recipientClaimPda] = web3_js_1.PublicKey.findProgramAddressSync([CLAIM_PDA_SEED, connectedWallet.wallet.publicKey.toBuffer()], programId);
587
566
  const keys = [
588
567
  { pubkey: connectedWallet.wallet.publicKey, isSigner: true, isWritable: true },
568
+ { pubkey: recipientClaimPda, isSigner: false, isWritable: true },
589
569
  { pubkey: mailerStatePda, isSigner: false, isWritable: true },
590
570
  { pubkey: recipientTokenAccount, isSigner: false, isWritable: true },
591
571
  { pubkey: mailerTokenAccount, isSigner: false, isWritable: true },
592
- { pubkey: recipientInfo, isSigner: false, isWritable: true },
593
572
  { pubkey: spl_token_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
594
573
  ];
595
574
  const instruction = new web3_js_1.TransactionInstruction({
@@ -634,11 +613,11 @@ class SolanaMailerClient {
634
613
  const connection = await this.getOrCreateConnection(chainInfo, connectedWallet.connection);
635
614
  const { programId, mailerStatePda } = this.getProgramAddresses(chainInfo);
636
615
  const recipientPubkey = typeof recipient === 'string' ? new web3_js_1.PublicKey(recipient) : recipient;
637
- const [recipientInfo] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('recipient_info'), recipientPubkey.toBuffer()], programId);
616
+ const [recipientClaimPda] = web3_js_1.PublicKey.findProgramAddressSync([CLAIM_PDA_SEED, recipientPubkey.toBuffer()], programId);
638
617
  const keys = [
639
618
  { pubkey: connectedWallet.wallet.publicKey, isSigner: true, isWritable: true },
640
619
  { pubkey: mailerStatePda, isSigner: false, isWritable: true },
641
- { pubkey: recipientInfo, isSigner: false, isWritable: true },
620
+ { pubkey: recipientClaimPda, isSigner: false, isWritable: true },
642
621
  ];
643
622
  const instruction = new web3_js_1.TransactionInstruction({
644
623
  programId,
@@ -663,13 +642,13 @@ class SolanaMailerClient {
663
642
  : null;
664
643
  const senderTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(usdcMint, connectedWallet.wallet.publicKey, false, spl_token_1.TOKEN_PROGRAM_ID);
665
644
  const mailerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(usdcMint, mailerStatePda, true, spl_token_1.TOKEN_PROGRAM_ID);
666
- const [delegatorInfo] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('delegator_info'), connectedWallet.wallet.publicKey.toBuffer()], programId);
645
+ const [delegationPda] = web3_js_1.PublicKey.findProgramAddressSync([DELEGATION_PDA_SEED, connectedWallet.wallet.publicKey.toBuffer()], programId);
667
646
  const keys = [
668
647
  { pubkey: connectedWallet.wallet.publicKey, isSigner: true, isWritable: true },
648
+ { pubkey: delegationPda, isSigner: false, isWritable: true },
669
649
  { pubkey: mailerStatePda, isSigner: false, isWritable: true },
670
650
  { pubkey: senderTokenAccount, isSigner: false, isWritable: true },
671
651
  { pubkey: mailerTokenAccount, isSigner: false, isWritable: true },
672
- { pubkey: delegatorInfo, isSigner: false, isWritable: true },
673
652
  { pubkey: spl_token_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
674
653
  { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
675
654
  ];
@@ -690,17 +669,18 @@ class SolanaMailerClient {
690
669
  */
691
670
  async rejectDelegation(connectedWallet, chainInfo, delegatingAddress, computeOptions) {
692
671
  const connection = await this.getOrCreateConnection(chainInfo, connectedWallet.connection);
693
- const { programId } = this.getProgramAddresses(chainInfo);
672
+ const { programId, mailerStatePda } = this.getProgramAddresses(chainInfo);
694
673
  const delegatorPubkey = typeof delegatingAddress === 'string' ? new web3_js_1.PublicKey(delegatingAddress) : delegatingAddress;
695
- const [delegatorInfo] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('delegator_info'), delegatorPubkey.toBuffer()], programId);
674
+ const [delegationPda] = web3_js_1.PublicKey.findProgramAddressSync([DELEGATION_PDA_SEED, delegatorPubkey.toBuffer()], programId);
696
675
  const keys = [
697
676
  { pubkey: connectedWallet.wallet.publicKey, isSigner: true, isWritable: false },
698
- { pubkey: delegatorInfo, isSigner: false, isWritable: true },
677
+ { pubkey: delegationPda, isSigner: false, isWritable: true },
678
+ { pubkey: mailerStatePda, isSigner: false, isWritable: false },
699
679
  ];
700
680
  const instruction = new web3_js_1.TransactionInstruction({
701
681
  programId,
702
682
  keys,
703
- data: encodeRejectDelegation(delegatorPubkey),
683
+ data: encodeRejectDelegation(),
704
684
  });
705
685
  const transaction = new web3_js_1.Transaction().add(instruction);
706
686
  return await this.sendTransaction(transaction, connectedWallet.wallet, connection, undefined, computeOptions);
@@ -711,16 +691,24 @@ class SolanaMailerClient {
711
691
  async setFees(connectedWallet, chainInfo, sendFee, delegationFee, computeOptions) {
712
692
  const connection = await this.getOrCreateConnection(chainInfo, connectedWallet.connection);
713
693
  const { programId, mailerStatePda } = this.getProgramAddresses(chainInfo);
714
- const keys = [
715
- { pubkey: connectedWallet.wallet.publicKey, isSigner: true, isWritable: false },
716
- { pubkey: mailerStatePda, isSigner: false, isWritable: true },
717
- ];
718
- const instruction = new web3_js_1.TransactionInstruction({
694
+ const ownerKey = connectedWallet.wallet.publicKey;
695
+ const setSendFeeIx = new web3_js_1.TransactionInstruction({
719
696
  programId,
720
- keys,
721
- data: encodeSetFees(BigInt(sendFee), BigInt(delegationFee)),
697
+ keys: [
698
+ { pubkey: ownerKey, isSigner: true, isWritable: false },
699
+ { pubkey: mailerStatePda, isSigner: false, isWritable: true },
700
+ ],
701
+ data: encodeSetFee(BigInt(sendFee)),
722
702
  });
723
- const transaction = new web3_js_1.Transaction().add(instruction);
703
+ const setDelegationFeeIx = new web3_js_1.TransactionInstruction({
704
+ programId,
705
+ keys: [
706
+ { pubkey: ownerKey, isSigner: true, isWritable: false },
707
+ { pubkey: mailerStatePda, isSigner: false, isWritable: true },
708
+ ],
709
+ data: encodeSetDelegationFee(BigInt(delegationFee)),
710
+ });
711
+ const transaction = new web3_js_1.Transaction().add(setSendFeeIx, setDelegationFeeIx);
724
712
  return await this.sendTransaction(transaction, connectedWallet.wallet, connection, undefined, computeOptions);
725
713
  }
726
714
  /**
@@ -730,11 +718,14 @@ class SolanaMailerClient {
730
718
  const connection = await this.getOrCreateConnection(chainInfo, connectedWallet.connection);
731
719
  const { programId, mailerStatePda } = this.getProgramAddresses(chainInfo);
732
720
  const accountPubkey = typeof account === 'string' ? new web3_js_1.PublicKey(account) : account;
733
- const [customFeeInfo] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('custom_fee'), accountPubkey.toBuffer()], programId);
721
+ const [discountPda] = web3_js_1.PublicKey.findProgramAddressSync([DISCOUNT_PDA_SEED, accountPubkey.toBuffer()], programId);
722
+ const ownerKey = connectedWallet.wallet.publicKey;
734
723
  const keys = [
735
- { pubkey: connectedWallet.wallet.publicKey, isSigner: true, isWritable: true },
724
+ { pubkey: ownerKey, isSigner: true, isWritable: false },
736
725
  { pubkey: mailerStatePda, isSigner: false, isWritable: false },
737
- { pubkey: customFeeInfo, isSigner: false, isWritable: true },
726
+ { pubkey: discountPda, isSigner: false, isWritable: true },
727
+ { pubkey: accountPubkey, isSigner: false, isWritable: false },
728
+ { pubkey: ownerKey, isSigner: true, isWritable: true },
738
729
  { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
739
730
  ];
740
731
  const instruction = new web3_js_1.TransactionInstruction({
@@ -752,11 +743,11 @@ class SolanaMailerClient {
752
743
  const connection = await this.getOrCreateConnection(chainInfo, connectedWallet.connection);
753
744
  const { programId, mailerStatePda } = this.getProgramAddresses(chainInfo);
754
745
  const accountPubkey = typeof account === 'string' ? new web3_js_1.PublicKey(account) : account;
755
- const [customFeeInfo] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('custom_fee'), accountPubkey.toBuffer()], programId);
746
+ const [discountPda] = web3_js_1.PublicKey.findProgramAddressSync([DISCOUNT_PDA_SEED, accountPubkey.toBuffer()], programId);
756
747
  const keys = [
757
748
  { pubkey: connectedWallet.wallet.publicKey, isSigner: true, isWritable: false },
758
749
  { pubkey: mailerStatePda, isSigner: false, isWritable: false },
759
- { pubkey: customFeeInfo, isSigner: false, isWritable: true },
750
+ { pubkey: discountPda, isSigner: false, isWritable: true },
760
751
  ];
761
752
  const instruction = new web3_js_1.TransactionInstruction({
762
753
  programId,
@@ -832,26 +823,31 @@ class SolanaMailerClient {
832
823
  const usdcMint = new web3_js_1.PublicKey(chainInfo.usdcAddress);
833
824
  const recipientPubkeys = recipients.map(r => typeof r === 'string' ? new web3_js_1.PublicKey(r) : r);
834
825
  const mailerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(usdcMint, mailerStatePda, true, spl_token_1.TOKEN_PROGRAM_ID);
835
- // Build keys array
836
- const keys = [
837
- { pubkey: connectedWallet.wallet.publicKey, isSigner: true, isWritable: false },
838
- { pubkey: mailerStatePda, isSigner: false, isWritable: true },
839
- { pubkey: mailerTokenAccount, isSigner: false, isWritable: true },
840
- { pubkey: spl_token_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
841
- ];
842
- // Add recipient info and token accounts
826
+ const transaction = new web3_js_1.Transaction();
827
+ const mailerTokenInfo = await connection.getAccountInfo(mailerTokenAccount);
828
+ if (!mailerTokenInfo) {
829
+ transaction.add((0, spl_token_1.createAssociatedTokenAccountInstruction)(connectedWallet.wallet.publicKey, mailerTokenAccount, mailerStatePda, usdcMint));
830
+ }
843
831
  for (const recipient of recipientPubkeys) {
844
- const [recipientInfo] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('recipient_info'), recipient.toBuffer()], programId);
832
+ const [recipientClaimPda] = web3_js_1.PublicKey.findProgramAddressSync([CLAIM_PDA_SEED, recipient.toBuffer()], programId);
845
833
  const recipientTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(usdcMint, recipient, false, spl_token_1.TOKEN_PROGRAM_ID);
846
- keys.push({ pubkey: recipientInfo, isSigner: false, isWritable: true });
847
- keys.push({ pubkey: recipientTokenAccount, isSigner: false, isWritable: true });
834
+ const recipientTokenInfo = await connection.getAccountInfo(recipientTokenAccount);
835
+ if (!recipientTokenInfo) {
836
+ transaction.add((0, spl_token_1.createAssociatedTokenAccountInstruction)(connectedWallet.wallet.publicKey, recipientTokenAccount, recipient, usdcMint));
837
+ }
838
+ transaction.add(new web3_js_1.TransactionInstruction({
839
+ programId,
840
+ keys: [
841
+ { pubkey: connectedWallet.wallet.publicKey, isSigner: true, isWritable: false },
842
+ { pubkey: mailerStatePda, isSigner: false, isWritable: true },
843
+ { pubkey: recipientClaimPda, isSigner: false, isWritable: true },
844
+ { pubkey: recipientTokenAccount, isSigner: false, isWritable: true },
845
+ { pubkey: mailerTokenAccount, isSigner: false, isWritable: true },
846
+ { pubkey: spl_token_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
847
+ ],
848
+ data: encodeDistributeClaimableFunds(recipient),
849
+ }));
848
850
  }
849
- const instruction = new web3_js_1.TransactionInstruction({
850
- programId,
851
- keys,
852
- data: encodeDistributeClaimableFunds(recipientPubkeys),
853
- });
854
- const transaction = new web3_js_1.Transaction().add(instruction);
855
851
  return await this.sendTransaction(transaction, connectedWallet.wallet, connection, undefined, computeOptions);
856
852
  }
857
853
  // ============= Read Methods =============
@@ -867,8 +863,8 @@ class SolanaMailerClient {
867
863
  }
868
864
  // Parse the state data
869
865
  const data = accountInfo.data;
870
- const sendFee = data.readBigUInt64LE(41); // After discriminator(8) + owner(32) + paused(1)
871
- const delegationFee = data.readBigUInt64LE(49);
866
+ const sendFee = data.readBigUInt64LE(8 + 32 + 32); // After discriminator + owner + mint
867
+ const delegationFee = data.readBigUInt64LE(8 + 32 + 32 + 8);
872
868
  return {
873
869
  sendFee,
874
870
  delegationFee,
@@ -895,22 +891,22 @@ class SolanaMailerClient {
895
891
  const conn = await this.getOrCreateConnection(chainInfo, connection);
896
892
  const { programId } = this.getProgramAddresses(chainInfo);
897
893
  const recipientPubkey = typeof recipient === 'string' ? new web3_js_1.PublicKey(recipient) : recipient;
898
- const [recipientInfo] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('recipient_info'), recipientPubkey.toBuffer()], programId);
899
- const accountInfo = await conn.getAccountInfo(recipientInfo);
894
+ const [recipientClaimPda] = web3_js_1.PublicKey.findProgramAddressSync([CLAIM_PDA_SEED, recipientPubkey.toBuffer()], programId);
895
+ const accountInfo = await conn.getAccountInfo(recipientClaimPda);
900
896
  if (!accountInfo || !accountInfo.data) {
901
897
  return null;
902
898
  }
903
899
  // Parse the recipient info data
904
900
  const data = accountInfo.data;
905
- const amount = data.readBigUInt64LE(8); // After discriminator
906
- const expiresAt = data.readBigInt64LE(16);
907
- // Check if expired
901
+ const amount = data.readBigUInt64LE(8 + 32); // discriminator + recipient
902
+ const timestamp = Number(data.readBigInt64LE(8 + 32 + 8)); // after amount
903
+ const expiresAt = timestamp + CLAIM_PERIOD_SECONDS;
908
904
  const now = Math.floor(Date.now() / 1000);
909
- const isExpired = Number(expiresAt) > 0 && Number(expiresAt) < now;
905
+ const isExpired = timestamp > 0 && now > expiresAt;
910
906
  return {
911
907
  amount: Number(amount),
912
- timestamp: Number(expiresAt), // Using expiresAt as timestamp
913
- expiresAt: Number(expiresAt),
908
+ timestamp,
909
+ expiresAt,
914
910
  recipient: recipient instanceof web3_js_1.PublicKey ? recipient.toBase58() : recipient,
915
911
  isExpired,
916
912
  };
@@ -927,7 +923,7 @@ class SolanaMailerClient {
927
923
  }
928
924
  // Parse the state data
929
925
  const data = accountInfo.data;
930
- const ownerClaimable = data.readBigUInt64LE(57); // After discriminator(8) + owner(32) + paused(1) + sendFee(8) + delegationFee(8)
926
+ const ownerClaimable = data.readBigUInt64LE(8 + 32 + 32 + 8 + 8); // After discriminator + owner + mint + sendFee + delegationFee
931
927
  return Number(ownerClaimable);
932
928
  }
933
929
  /**
@@ -937,19 +933,19 @@ class SolanaMailerClient {
937
933
  const conn = await this.getOrCreateConnection(chainInfo, connection);
938
934
  const { programId } = this.getProgramAddresses(chainInfo);
939
935
  const addressPubkey = typeof address === 'string' ? new web3_js_1.PublicKey(address) : address;
940
- const [delegatorInfo] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('delegator_info'), addressPubkey.toBuffer()], programId);
941
- const accountInfo = await conn.getAccountInfo(delegatorInfo);
936
+ const [delegationPda] = web3_js_1.PublicKey.findProgramAddressSync([DELEGATION_PDA_SEED, addressPubkey.toBuffer()], programId);
937
+ const accountInfo = await conn.getAccountInfo(delegationPda);
942
938
  if (!accountInfo || !accountInfo.data) {
943
939
  return null;
944
940
  }
945
941
  // Parse the delegatingAddress info data
946
942
  const data = accountInfo.data;
947
- const hasDelegate = data.readUInt8(8) === 1; // After discriminator
943
+ const hasDelegate = data.readUInt8(8 + 32) === 1; // After discriminator + delegator
948
944
  if (!hasDelegate) {
949
945
  return null;
950
946
  }
951
947
  // Read delegate pubkey
952
- const delegateBytes = data.slice(9, 41); // 32 bytes for pubkey
948
+ const delegateBytes = data.slice(8 + 32 + 1, 8 + 32 + 1 + 32); // 32 bytes for pubkey
953
949
  return new web3_js_1.PublicKey(delegateBytes);
954
950
  }
955
951
  /**
@@ -959,15 +955,15 @@ class SolanaMailerClient {
959
955
  const conn = await this.getOrCreateConnection(chainInfo, connection);
960
956
  const { programId } = this.getProgramAddresses(chainInfo);
961
957
  const accountPubkey = typeof account === 'string' ? new web3_js_1.PublicKey(account) : account;
962
- const [customFeeInfo] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('custom_fee'), accountPubkey.toBuffer()], programId);
963
- const accountInfo = await conn.getAccountInfo(customFeeInfo);
958
+ const [discountPda] = web3_js_1.PublicKey.findProgramAddressSync([DISCOUNT_PDA_SEED, accountPubkey.toBuffer()], programId);
959
+ const accountInfo = await conn.getAccountInfo(discountPda);
964
960
  if (!accountInfo || !accountInfo.data) {
965
961
  return 100; // Default to 100% if no custom fee set
966
962
  }
967
963
  // Parse the custom fee data
968
964
  const data = accountInfo.data;
969
- const percentage = data.readUInt8(8); // After discriminator
970
- return percentage;
965
+ const discount = data.readUInt8(8 + 32); // After discriminator + account pubkey
966
+ return 100 - discount;
971
967
  }
972
968
  /**
973
969
  * Check if the program is paused
@@ -981,7 +977,7 @@ class SolanaMailerClient {
981
977
  }
982
978
  // Parse the state data
983
979
  const data = accountInfo.data;
984
- const paused = data.readUInt8(40); // After discriminator(8) + owner(32)
980
+ const paused = data.readUInt8(8 + 32 + 32 + 8 + 8 + 8); // After all state fields up to owner_claimable
985
981
  return paused === 1;
986
982
  }
987
983
  /**