@sudobility/contracts 1.10.1 → 1.11.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 (28) hide show
  1. package/artifacts/contracts/Mailer.sol/Mailer.d.ts +67 -2
  2. package/artifacts/contracts/Mailer.sol/Mailer.dbg.json +1 -1
  3. package/artifacts/contracts/Mailer.sol/Mailer.json +67 -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/typechain-types/Mailer.d.ts +50 -2
  7. package/dist/evm/typechain-types/Mailer.d.ts.map +1 -1
  8. package/dist/evm/typechain-types/factories/Mailer__factory.d.ts +53 -1
  9. package/dist/evm/typechain-types/factories/Mailer__factory.d.ts.map +1 -1
  10. package/dist/evm/typechain-types/factories/Mailer__factory.js +66 -1
  11. package/dist/evm/typechain-types/factories/Mailer__factory.js.map +1 -1
  12. package/dist/unified/typechain-types/Mailer.d.ts +50 -2
  13. package/dist/unified/typechain-types/Mailer.d.ts.map +1 -1
  14. package/dist/unified/typechain-types/factories/Mailer__factory.d.ts +53 -1
  15. package/dist/unified/typechain-types/factories/Mailer__factory.d.ts.map +1 -1
  16. package/dist/unified/typechain-types/factories/Mailer__factory.js +66 -1
  17. package/dist/unified/typechain-types/factories/Mailer__factory.js.map +1 -1
  18. package/dist/unified-esm/typechain-types/Mailer.d.ts +50 -2
  19. package/dist/unified-esm/typechain-types/Mailer.d.ts.map +1 -1
  20. package/dist/unified-esm/typechain-types/factories/Mailer__factory.d.ts +53 -1
  21. package/dist/unified-esm/typechain-types/factories/Mailer__factory.d.ts.map +1 -1
  22. package/dist/unified-esm/typechain-types/factories/Mailer__factory.js +66 -1
  23. package/dist/unified-esm/typechain-types/factories/Mailer__factory.js.map +1 -1
  24. package/package.json +6 -1
  25. package/programs/mailer/src/lib.rs +305 -13
  26. package/programs/mailer/tests/integration_tests.rs +206 -3
  27. package/typechain-types/Mailer.ts +79 -0
  28. package/typechain-types/factories/Mailer__factory.ts +66 -1
@@ -33,11 +33,13 @@ use solana_program::{
33
33
  msg,
34
34
  program::{invoke, invoke_signed},
35
35
  program_error::ProgramError,
36
+ program_pack::Pack,
36
37
  pubkey::Pubkey,
37
38
  rent::Rent,
38
39
  system_instruction,
39
40
  sysvar::Sysvar,
40
41
  };
42
+ use spl_token::state::Account as TokenAccount;
41
43
  use thiserror::Error;
42
44
 
43
45
  // Program ID for the Native Mailer program
@@ -177,6 +179,22 @@ pub enum MailerInstruction {
177
179
  /// 4. `[]` Token program
178
180
  SendPreparedToEmail { to_email: String, mail_id: String },
179
181
 
182
+ /// Send message through webhook (referenced by webhookId)
183
+ /// Accounts:
184
+ /// 0. `[signer]` Sender
185
+ /// 1. `[writable]` Recipient claim account (PDA)
186
+ /// 2. `[]` Mailer state account (PDA)
187
+ /// 3. `[writable]` Sender USDC account
188
+ /// 4. `[writable]` Mailer USDC account
189
+ /// 5. `[]` Token program
190
+ /// 6. `[]` System program
191
+ SendThroughWebhook {
192
+ to: Pubkey,
193
+ webhook_id: String,
194
+ revenue_share_to_receiver: bool,
195
+ resolve_sender_to_name: bool,
196
+ },
197
+
180
198
  /// Claim recipient share
181
199
  /// Accounts:
182
200
  /// 0. `[signer]` Recipient
@@ -310,6 +328,10 @@ pub enum MailerError {
310
328
  InvalidPDA,
311
329
  #[error("Invalid account owner")]
312
330
  InvalidAccountOwner,
331
+ #[error("Invalid token mint")]
332
+ InvalidMint,
333
+ #[error("Invalid token program")]
334
+ InvalidTokenProgram,
313
335
  #[error("Contract is paused")]
314
336
  ContractPaused,
315
337
  #[error("Contract is not paused")]
@@ -372,6 +394,19 @@ pub fn process_instruction(
372
394
  MailerInstruction::SendPreparedToEmail { to_email, mail_id } => {
373
395
  process_send_prepared_to_email(program_id, accounts, to_email, mail_id)
374
396
  }
397
+ MailerInstruction::SendThroughWebhook {
398
+ to,
399
+ webhook_id,
400
+ revenue_share_to_receiver,
401
+ resolve_sender_to_name,
402
+ } => process_send_through_webhook(
403
+ program_id,
404
+ accounts,
405
+ to,
406
+ webhook_id,
407
+ revenue_share_to_receiver,
408
+ resolve_sender_to_name,
409
+ ),
375
410
  MailerInstruction::ClaimRecipientShare => {
376
411
  process_claim_recipient_share(program_id, accounts)
377
412
  }
@@ -489,15 +524,15 @@ fn process_send(
489
524
  }
490
525
 
491
526
  // Load mailer state
492
- let (mailer_pda, _) = Pubkey::find_program_address(&[b"mailer"], program_id);
493
- if mailer_account.key != &mailer_pda {
494
- return Err(MailerError::InvalidPDA.into());
495
- }
496
-
527
+ let (mailer_pda, _) = assert_mailer_account(program_id, mailer_account)?;
497
528
  let mailer_data = mailer_account.try_borrow_data()?;
498
529
  let mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
499
530
  drop(mailer_data);
500
531
 
532
+ assert_token_program(token_program)?;
533
+ assert_token_account(sender_usdc, sender.key, &mailer_state.usdc_mint)?;
534
+ assert_token_account(mailer_usdc, &mailer_pda, &mailer_state.usdc_mint)?;
535
+
501
536
  // Check if contract is paused
502
537
  if mailer_state.paused {
503
538
  return Err(MailerError::ContractPaused.into());
@@ -646,10 +681,15 @@ fn process_send_prepared(
646
681
  }
647
682
 
648
683
  // Load mailer state
684
+ let (mailer_pda, _) = assert_mailer_account(program_id, mailer_account)?;
649
685
  let mailer_data = mailer_account.try_borrow_data()?;
650
686
  let mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
651
687
  drop(mailer_data);
652
688
 
689
+ assert_token_program(token_program)?;
690
+ assert_token_account(sender_usdc, sender.key, &mailer_state.usdc_mint)?;
691
+ assert_token_account(mailer_usdc, &mailer_pda, &mailer_state.usdc_mint)?;
692
+
653
693
  // Check if contract is paused
654
694
  if mailer_state.paused {
655
695
  return Err(MailerError::ContractPaused.into());
@@ -795,10 +835,15 @@ fn process_send_to_email(
795
835
  }
796
836
 
797
837
  // Load mailer state
838
+ let (mailer_pda, _) = assert_mailer_account(_program_id, mailer_account)?;
798
839
  let mailer_data = mailer_account.try_borrow_data()?;
799
840
  let mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
800
841
  drop(mailer_data);
801
842
 
843
+ assert_token_program(token_program)?;
844
+ assert_token_account(sender_usdc, sender.key, &mailer_state.usdc_mint)?;
845
+ assert_token_account(mailer_usdc, &mailer_pda, &mailer_state.usdc_mint)?;
846
+
802
847
  // Check if contract is paused
803
848
  if mailer_state.paused {
804
849
  return Err(MailerError::ContractPaused.into());
@@ -869,10 +914,15 @@ fn process_send_prepared_to_email(
869
914
  }
870
915
 
871
916
  // Load mailer state
917
+ let (mailer_pda, _) = assert_mailer_account(_program_id, mailer_account)?;
872
918
  let mailer_data = mailer_account.try_borrow_data()?;
873
919
  let mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
874
920
  drop(mailer_data);
875
921
 
922
+ assert_token_program(token_program)?;
923
+ assert_token_account(sender_usdc, sender.key, &mailer_state.usdc_mint)?;
924
+ assert_token_account(mailer_usdc, &mailer_pda, &mailer_state.usdc_mint)?;
925
+
876
926
  // Check if contract is paused
877
927
  if mailer_state.paused {
878
928
  return Err(MailerError::ContractPaused.into());
@@ -924,6 +974,163 @@ fn process_send_prepared_to_email(
924
974
  Ok(())
925
975
  }
926
976
 
977
+ /// Send message through webhook (references webhook by webhookId)
978
+ fn process_send_through_webhook(
979
+ program_id: &Pubkey,
980
+ accounts: &[AccountInfo],
981
+ to: Pubkey,
982
+ webhook_id: String,
983
+ revenue_share_to_receiver: bool,
984
+ _resolve_sender_to_name: bool,
985
+ ) -> ProgramResult {
986
+ let account_iter = &mut accounts.iter();
987
+ let sender = next_account_info(account_iter)?;
988
+ let recipient_claim = next_account_info(account_iter)?;
989
+ let mailer_account = next_account_info(account_iter)?;
990
+ let sender_usdc = next_account_info(account_iter)?;
991
+ let mailer_usdc = next_account_info(account_iter)?;
992
+ let token_program = next_account_info(account_iter)?;
993
+ let system_program = next_account_info(account_iter)?;
994
+
995
+ if !sender.is_signer {
996
+ return Err(ProgramError::MissingRequiredSignature);
997
+ }
998
+
999
+ // Load mailer state
1000
+ let (mailer_pda, _) = assert_mailer_account(program_id, mailer_account)?;
1001
+ let mailer_data = mailer_account.try_borrow_data()?;
1002
+ let mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
1003
+ drop(mailer_data);
1004
+
1005
+ assert_token_program(token_program)?;
1006
+ assert_token_account(sender_usdc, sender.key, &mailer_state.usdc_mint)?;
1007
+ assert_token_account(mailer_usdc, &mailer_pda, &mailer_state.usdc_mint)?;
1008
+
1009
+ // Check if contract is paused
1010
+ if mailer_state.paused {
1011
+ return Err(MailerError::ContractPaused.into());
1012
+ }
1013
+
1014
+ // Calculate effective fee based on custom discount (if any)
1015
+ let effective_fee =
1016
+ calculate_fee_with_discount(program_id, sender.key, accounts, mailer_state.send_fee)?;
1017
+
1018
+ if revenue_share_to_receiver {
1019
+ // Priority mode: full fee with revenue sharing
1020
+
1021
+ // Create or load recipient claim account
1022
+ let (claim_pda, claim_bump) =
1023
+ Pubkey::find_program_address(&[b"claim", to.as_ref()], program_id);
1024
+
1025
+ if recipient_claim.key != &claim_pda {
1026
+ return Err(MailerError::InvalidPDA.into());
1027
+ }
1028
+
1029
+ // Create claim account if needed
1030
+ if recipient_claim.lamports() == 0 {
1031
+ let rent = Rent::get()?;
1032
+ let space = 8 + RecipientClaim::LEN;
1033
+ let lamports = rent.minimum_balance(space);
1034
+
1035
+ invoke_signed(
1036
+ &system_instruction::create_account(
1037
+ sender.key,
1038
+ recipient_claim.key,
1039
+ lamports,
1040
+ space as u64,
1041
+ program_id,
1042
+ ),
1043
+ &[
1044
+ sender.clone(),
1045
+ recipient_claim.clone(),
1046
+ system_program.clone(),
1047
+ ],
1048
+ &[&[b"claim", to.as_ref(), &[claim_bump]]],
1049
+ )?;
1050
+
1051
+ // Initialize claim account
1052
+ let mut claim_data = recipient_claim.try_borrow_mut_data()?;
1053
+ claim_data[0..8]
1054
+ .copy_from_slice(&hash_discriminator("account:RecipientClaim").to_le_bytes());
1055
+
1056
+ let claim_state = RecipientClaim {
1057
+ recipient: to,
1058
+ amount: 0,
1059
+ timestamp: 0,
1060
+ bump: claim_bump,
1061
+ };
1062
+
1063
+ claim_state.serialize(&mut &mut claim_data[8..])?;
1064
+ drop(claim_data);
1065
+ }
1066
+
1067
+ // Transfer effective fee (may be discounted)
1068
+ if effective_fee > 0 {
1069
+ invoke(
1070
+ &spl_token::instruction::transfer(
1071
+ token_program.key,
1072
+ sender_usdc.key,
1073
+ mailer_usdc.key,
1074
+ sender.key,
1075
+ &[],
1076
+ effective_fee,
1077
+ )?,
1078
+ &[
1079
+ sender_usdc.clone(),
1080
+ mailer_usdc.clone(),
1081
+ sender.clone(),
1082
+ token_program.clone(),
1083
+ ],
1084
+ )?;
1085
+
1086
+ // Record revenue shares (only if fee > 0)
1087
+ record_shares(recipient_claim, mailer_account, to, effective_fee)?;
1088
+ }
1089
+
1090
+ msg!("Webhook mail sent from {} to {} (webhookId: {}, revenue share enabled, resolve sender: {}, effective fee: {})", sender.key, to, webhook_id, _resolve_sender_to_name, effective_fee);
1091
+ } else {
1092
+ // Standard mode: 10% fee only, no revenue sharing
1093
+ let owner_fee = (effective_fee * 10) / 100; // 10% of effective fee
1094
+
1095
+ // Transfer only owner fee (10%)
1096
+ if owner_fee > 0 {
1097
+ invoke(
1098
+ &spl_token::instruction::transfer(
1099
+ token_program.key,
1100
+ sender_usdc.key,
1101
+ mailer_usdc.key,
1102
+ sender.key,
1103
+ &[],
1104
+ owner_fee,
1105
+ )?,
1106
+ &[
1107
+ sender_usdc.clone(),
1108
+ mailer_usdc.clone(),
1109
+ sender.clone(),
1110
+ token_program.clone(),
1111
+ ],
1112
+ )?;
1113
+ }
1114
+
1115
+ // Update owner claimable
1116
+ let mut mailer_data = mailer_account.try_borrow_mut_data()?;
1117
+ let mut mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
1118
+ mailer_state.owner_claimable += owner_fee;
1119
+ mailer_state.serialize(&mut &mut mailer_data[8..])?;
1120
+
1121
+ msg!(
1122
+ "Webhook mail sent from {} to {} (webhookId: {}, resolve sender: {}, effective fee: {})",
1123
+ sender.key,
1124
+ to,
1125
+ webhook_id,
1126
+ _resolve_sender_to_name,
1127
+ effective_fee
1128
+ );
1129
+ }
1130
+
1131
+ Ok(())
1132
+ }
1133
+
927
1134
  /// Process claim recipient share
928
1135
  fn process_claim_recipient_share(_program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
929
1136
  let account_iter = &mut accounts.iter();
@@ -938,6 +1145,13 @@ fn process_claim_recipient_share(_program_id: &Pubkey, accounts: &[AccountInfo])
938
1145
  return Err(ProgramError::MissingRequiredSignature);
939
1146
  }
940
1147
 
1148
+ let (mailer_pda, _) = assert_mailer_account(_program_id, mailer_account)?;
1149
+ let (claim_pda, _) =
1150
+ Pubkey::find_program_address(&[b"claim", recipient.key.as_ref()], _program_id);
1151
+ if recipient_claim.key != &claim_pda {
1152
+ return Err(MailerError::InvalidPDA.into());
1153
+ }
1154
+
941
1155
  // Load claim state
942
1156
  let mut claim_data = recipient_claim.try_borrow_mut_data()?;
943
1157
  let mut claim_state: RecipientClaim = BorshDeserialize::deserialize(&mut &claim_data[8..])?;
@@ -964,6 +1178,11 @@ fn process_claim_recipient_share(_program_id: &Pubkey, accounts: &[AccountInfo])
964
1178
  // Load mailer state for PDA signing
965
1179
  let mailer_data = mailer_account.try_borrow_data()?;
966
1180
  let mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
1181
+ drop(mailer_data);
1182
+
1183
+ assert_token_program(token_program)?;
1184
+ assert_token_account(recipient_usdc, recipient.key, &mailer_state.usdc_mint)?;
1185
+ assert_token_account(mailer_usdc, &mailer_pda, &mailer_state.usdc_mint)?;
967
1186
 
968
1187
  // Transfer USDC from mailer to recipient
969
1188
  invoke_signed(
@@ -1001,6 +1220,8 @@ fn process_claim_owner_share(_program_id: &Pubkey, accounts: &[AccountInfo]) ->
1001
1220
  return Err(ProgramError::MissingRequiredSignature);
1002
1221
  }
1003
1222
 
1223
+ let (mailer_pda, _) = assert_mailer_account(_program_id, mailer_account)?;
1224
+
1004
1225
  // Load and update mailer state
1005
1226
  let mut mailer_data = mailer_account.try_borrow_mut_data()?;
1006
1227
  let mut mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
@@ -1018,6 +1239,10 @@ fn process_claim_owner_share(_program_id: &Pubkey, accounts: &[AccountInfo]) ->
1018
1239
  mailer_state.serialize(&mut &mut mailer_data[8..])?;
1019
1240
  drop(mailer_data);
1020
1241
 
1242
+ assert_token_program(token_program)?;
1243
+ assert_token_account(owner_usdc, owner.key, &mailer_state.usdc_mint)?;
1244
+ assert_token_account(mailer_usdc, &mailer_pda, &mailer_state.usdc_mint)?;
1245
+
1021
1246
  // Transfer USDC from mailer to owner
1022
1247
  invoke_signed(
1023
1248
  &spl_token::instruction::transfer(
@@ -1051,6 +1276,8 @@ fn process_set_fee(_program_id: &Pubkey, accounts: &[AccountInfo], new_fee: u64)
1051
1276
  return Err(ProgramError::MissingRequiredSignature);
1052
1277
  }
1053
1278
 
1279
+ assert_mailer_account(_program_id, mailer_account)?;
1280
+
1054
1281
  // Load and update mailer state
1055
1282
  let mut mailer_data = mailer_account.try_borrow_mut_data()?;
1056
1283
  let mut mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
@@ -1091,11 +1318,17 @@ fn process_delegate_to(
1091
1318
  return Err(ProgramError::MissingRequiredSignature);
1092
1319
  }
1093
1320
 
1321
+ let (mailer_pda, _) = assert_mailer_account(program_id, mailer_account)?;
1322
+
1094
1323
  // Load mailer state
1095
1324
  let mailer_data = mailer_account.try_borrow_data()?;
1096
1325
  let mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
1097
1326
  drop(mailer_data);
1098
1327
 
1328
+ assert_token_program(token_program)?;
1329
+ assert_token_account(delegator_usdc, delegator.key, &mailer_state.usdc_mint)?;
1330
+ assert_token_account(mailer_usdc, &mailer_pda, &mailer_state.usdc_mint)?;
1331
+
1099
1332
  // Check if contract is paused
1100
1333
  if mailer_state.paused {
1101
1334
  return Err(MailerError::ContractPaused.into());
@@ -1191,10 +1424,7 @@ fn process_reject_delegation(program_id: &Pubkey, accounts: &[AccountInfo]) -> P
1191
1424
  }
1192
1425
 
1193
1426
  // Verify mailer state PDA and ensure contract is not paused
1194
- let (mailer_pda, _) = Pubkey::find_program_address(&[b"mailer"], program_id);
1195
- if mailer_account.key != &mailer_pda {
1196
- return Err(MailerError::InvalidPDA.into());
1197
- }
1427
+ let (_mailer_pda, _) = assert_mailer_account(program_id, mailer_account)?;
1198
1428
 
1199
1429
  let mailer_data = mailer_account.try_borrow_data()?;
1200
1430
  let mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
@@ -1235,6 +1465,8 @@ fn process_set_delegation_fee(
1235
1465
  return Err(ProgramError::MissingRequiredSignature);
1236
1466
  }
1237
1467
 
1468
+ assert_mailer_account(_program_id, mailer_account)?;
1469
+
1238
1470
  // Load and update mailer state
1239
1471
  let mut mailer_data = mailer_account.try_borrow_mut_data()?;
1240
1472
  let mut mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
@@ -1275,6 +1507,8 @@ fn process_set_custom_fee_percentage(
1275
1507
  return Err(ProgramError::MissingRequiredSignature);
1276
1508
  }
1277
1509
 
1510
+ assert_mailer_account(program_id, mailer_account)?;
1511
+
1278
1512
  // Load mailer state and verify owner
1279
1513
  let mailer_data = mailer_account.try_borrow_data()?;
1280
1514
  let mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
@@ -1364,6 +1598,8 @@ fn process_clear_custom_fee_percentage(
1364
1598
  return Err(ProgramError::MissingRequiredSignature);
1365
1599
  }
1366
1600
 
1601
+ assert_mailer_account(program_id, mailer_account)?;
1602
+
1367
1603
  // Load mailer state and verify owner
1368
1604
  let mailer_data = mailer_account.try_borrow_data()?;
1369
1605
  let mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
@@ -1402,6 +1638,44 @@ fn process_clear_custom_fee_percentage(
1402
1638
  Ok(())
1403
1639
  }
1404
1640
 
1641
+ fn assert_token_program(token_program: &AccountInfo) -> Result<(), ProgramError> {
1642
+ if token_program.key != &spl_token::id() {
1643
+ return Err(MailerError::InvalidTokenProgram.into());
1644
+ }
1645
+ Ok(())
1646
+ }
1647
+
1648
+ fn assert_token_account(
1649
+ token_account_info: &AccountInfo,
1650
+ expected_owner: &Pubkey,
1651
+ expected_mint: &Pubkey,
1652
+ ) -> Result<(), ProgramError> {
1653
+ let data = token_account_info.try_borrow_data()?;
1654
+ let token_account = TokenAccount::unpack(&data)?;
1655
+ drop(data);
1656
+
1657
+ if token_account.owner != *expected_owner {
1658
+ return Err(MailerError::InvalidAccountOwner.into());
1659
+ }
1660
+
1661
+ if token_account.mint != *expected_mint {
1662
+ return Err(MailerError::InvalidMint.into());
1663
+ }
1664
+
1665
+ Ok(())
1666
+ }
1667
+
1668
+ fn assert_mailer_account(
1669
+ program_id: &Pubkey,
1670
+ mailer_account: &AccountInfo,
1671
+ ) -> Result<(Pubkey, u8), ProgramError> {
1672
+ let (mailer_pda, bump) = Pubkey::find_program_address(&[b"mailer"], program_id);
1673
+ if mailer_account.key != &mailer_pda {
1674
+ return Err(MailerError::InvalidPDA.into());
1675
+ }
1676
+ Ok((mailer_pda, bump))
1677
+ }
1678
+
1405
1679
  /// Record revenue shares for priority messages
1406
1680
  fn record_shares(
1407
1681
  recipient_claim: &AccountInfo,
@@ -1486,6 +1760,8 @@ fn process_pause(_program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResul
1486
1760
  return Err(ProgramError::MissingRequiredSignature);
1487
1761
  }
1488
1762
 
1763
+ let (mailer_pda, _) = assert_mailer_account(_program_id, mailer_account)?;
1764
+
1489
1765
  // Load and update mailer state
1490
1766
  let mut mailer_data = mailer_account.try_borrow_mut_data()?;
1491
1767
  let mut mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
@@ -1503,13 +1779,17 @@ fn process_pause(_program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResul
1503
1779
  // Set paused state
1504
1780
  mailer_state.paused = true;
1505
1781
 
1782
+ assert_token_program(token_program)?;
1783
+
1506
1784
  // Distribute owner claimable funds if any
1507
1785
  if mailer_state.owner_claimable > 0 {
1508
1786
  let amount = mailer_state.owner_claimable;
1509
1787
  mailer_state.owner_claimable = 0;
1510
1788
 
1789
+ assert_token_account(owner_usdc, owner.key, &mailer_state.usdc_mint)?;
1790
+ assert_token_account(mailer_usdc, &mailer_pda, &mailer_state.usdc_mint)?;
1791
+
1511
1792
  // Transfer USDC from mailer to owner
1512
- let (mailer_pda, bump) = Pubkey::find_program_address(&[b"mailer"], _program_id);
1513
1793
  invoke_signed(
1514
1794
  &spl_token::instruction::transfer(
1515
1795
  token_program.key,
@@ -1525,7 +1805,7 @@ fn process_pause(_program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResul
1525
1805
  mailer_account.clone(),
1526
1806
  token_program.clone(),
1527
1807
  ],
1528
- &[&[b"mailer", &[bump]]],
1808
+ &[&[b"mailer", &[mailer_state.bump]]],
1529
1809
  )?;
1530
1810
 
1531
1811
  msg!("Distributed owner funds during pause: {}", amount);
@@ -1548,6 +1828,8 @@ fn process_unpause(_program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramRes
1548
1828
  return Err(ProgramError::MissingRequiredSignature);
1549
1829
  }
1550
1830
 
1831
+ assert_mailer_account(_program_id, mailer_account)?;
1832
+
1551
1833
  // Load and update mailer state
1552
1834
  let mut mailer_data = mailer_account.try_borrow_mut_data()?;
1553
1835
  let mut mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
@@ -1584,6 +1866,8 @@ fn process_distribute_claimable_funds(
1584
1866
  let mailer_usdc = next_account_info(account_iter)?;
1585
1867
  let token_program = next_account_info(account_iter)?;
1586
1868
 
1869
+ let (mailer_pda, _) = assert_mailer_account(_program_id, mailer_account)?;
1870
+
1587
1871
  // Load mailer state to check if paused
1588
1872
  let mailer_data = mailer_account.try_borrow_data()?;
1589
1873
  let mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
@@ -1600,6 +1884,8 @@ fn process_distribute_claimable_funds(
1600
1884
  return Err(MailerError::InvalidPDA.into());
1601
1885
  }
1602
1886
 
1887
+ assert_token_program(token_program)?;
1888
+
1603
1889
  // Load and update recipient claim
1604
1890
  let mut claim_data = recipient_claim_account.try_borrow_mut_data()?;
1605
1891
  let mut claim_state: RecipientClaim = BorshDeserialize::deserialize(&mut &claim_data[8..])?;
@@ -1612,8 +1898,10 @@ fn process_distribute_claimable_funds(
1612
1898
  claim_state.amount = 0;
1613
1899
  claim_state.timestamp = 0;
1614
1900
 
1901
+ assert_token_account(recipient_usdc, &recipient, &mailer_state.usdc_mint)?;
1902
+ assert_token_account(mailer_usdc, &mailer_pda, &mailer_state.usdc_mint)?;
1903
+
1615
1904
  // Transfer USDC from mailer to recipient
1616
- let (mailer_pda, bump) = Pubkey::find_program_address(&[b"mailer"], _program_id);
1617
1905
  invoke_signed(
1618
1906
  &spl_token::instruction::transfer(
1619
1907
  token_program.key,
@@ -1629,7 +1917,7 @@ fn process_distribute_claimable_funds(
1629
1917
  mailer_account.clone(),
1630
1918
  token_program.clone(),
1631
1919
  ],
1632
- &[&[b"mailer", &[bump]]],
1920
+ &[&[b"mailer", &[mailer_state.bump]]],
1633
1921
  )?;
1634
1922
 
1635
1923
  claim_state.serialize(&mut &mut claim_data[8..])?;
@@ -1653,6 +1941,8 @@ fn process_claim_expired_shares(
1653
1941
  return Err(ProgramError::MissingRequiredSignature);
1654
1942
  }
1655
1943
 
1944
+ let (_mailer_pda, _) = assert_mailer_account(program_id, mailer_account)?;
1945
+
1656
1946
  // Load and verify mailer state
1657
1947
  let mut mailer_data = mailer_account.try_borrow_mut_data()?;
1658
1948
  let mut mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
@@ -1706,6 +1996,8 @@ fn process_emergency_unpause(_program_id: &Pubkey, accounts: &[AccountInfo]) ->
1706
1996
  return Err(ProgramError::MissingRequiredSignature);
1707
1997
  }
1708
1998
 
1999
+ assert_mailer_account(_program_id, mailer_account)?;
2000
+
1709
2001
  // Load and update mailer state
1710
2002
  let mut mailer_data = mailer_account.try_borrow_mut_data()?;
1711
2003
  let mut mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;