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