@sudobility/contracts 0.14.0 → 1.9.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.
- package/README.md +2 -3
- package/artifacts/contracts/Mailer.sol/Mailer.dbg.json +1 -1
- package/artifacts/contracts/Mailer.sol/Mailer.json +2 -278
- package/artifacts/contracts/MockUSDC.sol/MockUSDC.dbg.json +1 -1
- package/artifacts/contracts/interfaces/IERC20.sol/IERC20.dbg.json +1 -1
- package/dist/evm/src/evm/index.d.ts +1 -1
- package/dist/evm/src/evm/index.d.ts.map +1 -1
- package/dist/evm/src/evm/index.js +1 -1
- package/dist/evm/src/evm/index.js.map +1 -1
- package/dist/evm/src/evm/mailer-client.d.ts +180 -936
- package/dist/evm/src/evm/mailer-client.d.ts.map +1 -1
- package/dist/evm/src/evm/mailer-client.js +249 -451
- package/dist/evm/src/evm/mailer-client.js.map +1 -1
- package/dist/evm/typechain-types/Mailer.d.ts +10 -177
- package/dist/evm/typechain-types/Mailer.d.ts.map +1 -1
- package/dist/evm/typechain-types/factories/Mailer__factory.d.ts +1 -216
- package/dist/evm/typechain-types/factories/Mailer__factory.d.ts.map +1 -1
- package/dist/evm/typechain-types/factories/Mailer__factory.js +1 -277
- package/dist/evm/typechain-types/factories/Mailer__factory.js.map +1 -1
- package/dist/solana/solana/index.d.ts +1 -1
- package/dist/solana/solana/index.d.ts.map +1 -1
- package/dist/solana/solana/index.js +1 -3
- package/dist/solana/solana/index.js.map +1 -1
- package/dist/solana/solana/mailer-client.d.ts +18 -91
- package/dist/solana/solana/mailer-client.d.ts.map +1 -1
- package/dist/solana/solana/mailer-client.js +55 -316
- package/dist/solana/solana/mailer-client.js.map +1 -1
- package/dist/unified/src/evm/index.d.ts +1 -1
- package/dist/unified/src/evm/index.d.ts.map +1 -1
- package/dist/unified/src/evm/index.js +1 -1
- package/dist/unified/src/evm/index.js.map +1 -1
- package/dist/unified/src/evm/mailer-client.d.ts +180 -936
- package/dist/unified/src/evm/mailer-client.d.ts.map +1 -1
- package/dist/unified/src/evm/mailer-client.js +249 -451
- package/dist/unified/src/evm/mailer-client.js.map +1 -1
- package/dist/unified/src/react/hooks/useMailerMutations.d.ts +1 -1
- package/dist/unified/src/react/hooks/useMailerMutations.js +1 -1
- package/dist/unified/src/solana/index.d.ts +1 -1
- package/dist/unified/src/solana/index.d.ts.map +1 -1
- package/dist/unified/src/solana/index.js +1 -3
- package/dist/unified/src/solana/index.js.map +1 -1
- package/dist/unified/src/solana/mailer-client.d.ts +18 -91
- package/dist/unified/src/solana/mailer-client.d.ts.map +1 -1
- package/dist/unified/src/solana/mailer-client.js +55 -316
- package/dist/unified/src/solana/mailer-client.js.map +1 -1
- package/dist/unified/src/unified/index.d.ts +1 -1
- package/dist/unified/src/unified/index.d.ts.map +1 -1
- package/dist/unified/src/unified/onchain-mailer-client.d.ts +251 -111
- package/dist/unified/src/unified/onchain-mailer-client.d.ts.map +1 -1
- package/dist/unified/src/unified/onchain-mailer-client.js +1375 -744
- package/dist/unified/src/unified/onchain-mailer-client.js.map +1 -1
- package/dist/unified/src/unified/types.d.ts +6 -16
- package/dist/unified/src/unified/types.d.ts.map +1 -1
- package/dist/unified/src/utils/chain-config.d.ts +2 -4
- package/dist/unified/src/utils/chain-config.d.ts.map +1 -1
- package/dist/unified/src/utils/chain-config.js +36 -46
- package/dist/unified/src/utils/chain-config.js.map +1 -1
- package/dist/unified/typechain-types/Mailer.d.ts +10 -177
- package/dist/unified/typechain-types/Mailer.d.ts.map +1 -1
- package/dist/unified/typechain-types/factories/Mailer__factory.d.ts +1 -216
- package/dist/unified/typechain-types/factories/Mailer__factory.d.ts.map +1 -1
- package/dist/unified/typechain-types/factories/Mailer__factory.js +1 -277
- package/dist/unified/typechain-types/factories/Mailer__factory.js.map +1 -1
- package/dist/unified-esm/src/evm/index.d.ts +1 -1
- package/dist/unified-esm/src/evm/index.d.ts.map +1 -1
- package/dist/unified-esm/src/evm/index.js +1 -1
- package/dist/unified-esm/src/evm/index.js.map +1 -1
- package/dist/unified-esm/src/evm/mailer-client.d.ts +180 -936
- package/dist/unified-esm/src/evm/mailer-client.d.ts.map +1 -1
- package/dist/unified-esm/src/evm/mailer-client.js +251 -453
- package/dist/unified-esm/src/evm/mailer-client.js.map +1 -1
- package/dist/unified-esm/src/react/hooks/useMailerMutations.d.ts +1 -1
- package/dist/unified-esm/src/react/hooks/useMailerMutations.js +1 -1
- package/dist/unified-esm/src/solana/index.d.ts +1 -1
- package/dist/unified-esm/src/solana/index.d.ts.map +1 -1
- package/dist/unified-esm/src/solana/index.js +1 -1
- package/dist/unified-esm/src/solana/index.js.map +1 -1
- package/dist/unified-esm/src/solana/mailer-client.d.ts +18 -91
- package/dist/unified-esm/src/solana/mailer-client.d.ts.map +1 -1
- package/dist/unified-esm/src/solana/mailer-client.js +56 -317
- package/dist/unified-esm/src/solana/mailer-client.js.map +1 -1
- package/dist/unified-esm/src/unified/index.d.ts +1 -1
- package/dist/unified-esm/src/unified/index.d.ts.map +1 -1
- package/dist/unified-esm/src/unified/onchain-mailer-client.d.ts +251 -111
- package/dist/unified-esm/src/unified/onchain-mailer-client.d.ts.map +1 -1
- package/dist/unified-esm/src/unified/onchain-mailer-client.js +1379 -748
- package/dist/unified-esm/src/unified/onchain-mailer-client.js.map +1 -1
- package/dist/unified-esm/src/unified/types.d.ts +6 -16
- package/dist/unified-esm/src/unified/types.d.ts.map +1 -1
- package/dist/unified-esm/src/utils/chain-config.d.ts +2 -4
- package/dist/unified-esm/src/utils/chain-config.d.ts.map +1 -1
- package/dist/unified-esm/src/utils/chain-config.js +35 -46
- package/dist/unified-esm/src/utils/chain-config.js.map +1 -1
- package/dist/unified-esm/typechain-types/Mailer.d.ts +10 -177
- package/dist/unified-esm/typechain-types/Mailer.d.ts.map +1 -1
- package/dist/unified-esm/typechain-types/factories/Mailer__factory.d.ts +1 -216
- package/dist/unified-esm/typechain-types/factories/Mailer__factory.d.ts.map +1 -1
- package/dist/unified-esm/typechain-types/factories/Mailer__factory.js +1 -277
- package/dist/unified-esm/typechain-types/factories/Mailer__factory.js.map +1 -1
- package/package.json +9 -20
- package/programs/mailer/src/lib.rs +171 -1026
- package/programs/mailer/tests/integration_tests.rs +65 -586
- package/typechain-types/Mailer.ts +8 -319
- package/typechain-types/factories/Mailer__factory.ts +1 -277
- package/artifacts/contracts/Mailer.sol/Mailer.d.ts +0 -1146
- package/artifacts/contracts/Mailer.sol/artifacts.d.ts +0 -21
- package/artifacts/contracts/MockUSDC.sol/MockUSDC.d.ts +0 -284
- package/artifacts/contracts/MockUSDC.sol/artifacts.d.ts +0 -21
- package/artifacts/contracts/interfaces/IERC20.sol/IERC20.d.ts +0 -157
- package/artifacts/contracts/interfaces/IERC20.sol/artifacts.d.ts +0 -21
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//! # Native Solana Mailer Program
|
|
2
2
|
//!
|
|
3
|
-
//! A native Solana program for decentralized messaging with delegation management,
|
|
3
|
+
//! A native Solana program for decentralized messaging with delegation management,
|
|
4
4
|
//! USDC fees and revenue sharing - no Anchor dependencies.
|
|
5
5
|
//!
|
|
6
6
|
//! ## Key Features
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
//! - Standard: Sender pays 10% fee only
|
|
26
26
|
//! - Owner gets 10% of all fees
|
|
27
27
|
|
|
28
|
+
|
|
28
29
|
use borsh::{BorshDeserialize, BorshSerialize};
|
|
29
30
|
use solana_program::{
|
|
30
31
|
account_info::{next_account_info, AccountInfo},
|
|
@@ -33,13 +34,11 @@ use solana_program::{
|
|
|
33
34
|
msg,
|
|
34
35
|
program::{invoke, invoke_signed},
|
|
35
36
|
program_error::ProgramError,
|
|
36
|
-
program_pack::Pack,
|
|
37
37
|
pubkey::Pubkey,
|
|
38
38
|
rent::Rent,
|
|
39
39
|
system_instruction,
|
|
40
40
|
sysvar::Sysvar,
|
|
41
41
|
};
|
|
42
|
-
use spl_token::state::Account as TokenAccount;
|
|
43
42
|
use thiserror::Error;
|
|
44
43
|
|
|
45
44
|
// Program ID for the Native Mailer program
|
|
@@ -98,20 +97,6 @@ impl Delegation {
|
|
|
98
97
|
pub const LEN: usize = 32 + 1 + 32 + 1; // 66 bytes (max with Some(Pubkey))
|
|
99
98
|
}
|
|
100
99
|
|
|
101
|
-
/// Fee discount account for custom fee percentages
|
|
102
|
-
/// Stores discount (0-100) instead of percentage for cleaner default behavior
|
|
103
|
-
/// 0 = no discount (100% fee), 100 = full discount (0% fee, free)
|
|
104
|
-
#[derive(BorshSerialize, BorshDeserialize, Debug, Clone)]
|
|
105
|
-
pub struct FeeDiscount {
|
|
106
|
-
pub account: Pubkey,
|
|
107
|
-
pub discount: u8, // 0-100: 0 = no discount (full fee), 100 = full discount (free)
|
|
108
|
-
pub bump: u8,
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
impl FeeDiscount {
|
|
112
|
-
pub const LEN: usize = 32 + 1 + 1; // 34 bytes
|
|
113
|
-
}
|
|
114
|
-
|
|
115
100
|
/// Instructions
|
|
116
101
|
#[derive(BorshSerialize, BorshDeserialize, Debug)]
|
|
117
102
|
pub enum MailerInstruction {
|
|
@@ -139,22 +124,6 @@ pub enum MailerInstruction {
|
|
|
139
124
|
resolve_sender_to_name: bool,
|
|
140
125
|
},
|
|
141
126
|
|
|
142
|
-
/// Send prepared message with optional revenue sharing (references off-chain content via mailId)
|
|
143
|
-
/// Accounts:
|
|
144
|
-
/// 0. `[signer]` Sender
|
|
145
|
-
/// 1. `[writable]` Recipient claim account (PDA)
|
|
146
|
-
/// 2. `[]` Mailer state account (PDA)
|
|
147
|
-
/// 3. `[writable]` Sender USDC account
|
|
148
|
-
/// 4. `[writable]` Mailer USDC account
|
|
149
|
-
/// 5. `[]` Token program
|
|
150
|
-
/// 6. `[]` System program
|
|
151
|
-
SendPrepared {
|
|
152
|
-
to: Pubkey,
|
|
153
|
-
mail_id: String,
|
|
154
|
-
revenue_share_to_receiver: bool,
|
|
155
|
-
resolve_sender_to_name: bool,
|
|
156
|
-
},
|
|
157
|
-
|
|
158
127
|
/// Send message to email address (no wallet address known)
|
|
159
128
|
/// Charges only 10% owner fee since recipient wallet is unknown
|
|
160
129
|
/// Accounts:
|
|
@@ -177,22 +146,9 @@ pub enum MailerInstruction {
|
|
|
177
146
|
/// 2. `[writable]` Sender USDC account
|
|
178
147
|
/// 3. `[writable]` Mailer USDC account
|
|
179
148
|
/// 4. `[]` Token program
|
|
180
|
-
SendPreparedToEmail {
|
|
181
|
-
|
|
182
|
-
|
|
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,
|
|
149
|
+
SendPreparedToEmail {
|
|
150
|
+
to_email: String,
|
|
151
|
+
mail_id: String,
|
|
196
152
|
},
|
|
197
153
|
|
|
198
154
|
/// Claim recipient share
|
|
@@ -235,7 +191,6 @@ pub enum MailerInstruction {
|
|
|
235
191
|
/// Accounts:
|
|
236
192
|
/// 0. `[signer]` Rejector
|
|
237
193
|
/// 1. `[writable]` Delegation account (PDA)
|
|
238
|
-
/// 2. `[]` Mailer state account (PDA)
|
|
239
194
|
RejectDelegation,
|
|
240
195
|
|
|
241
196
|
/// Set delegation fee (owner only)
|
|
@@ -243,27 +198,7 @@ pub enum MailerInstruction {
|
|
|
243
198
|
/// 0. `[signer]` Owner
|
|
244
199
|
/// 1. `[writable]` Mailer state account (PDA)
|
|
245
200
|
SetDelegationFee { new_fee: u64 },
|
|
246
|
-
|
|
247
|
-
/// Set custom fee percentage for a specific address (owner only)
|
|
248
|
-
/// Accounts:
|
|
249
|
-
/// 0. `[signer]` Owner
|
|
250
|
-
/// 1. `[]` Mailer state account (PDA)
|
|
251
|
-
/// 2. `[writable]` Fee discount account (PDA)
|
|
252
|
-
/// 3. `[]` Account to set custom fee for
|
|
253
|
-
/// 4. `[signer]` Payer for account creation
|
|
254
|
-
/// 5. `[]` System program
|
|
255
|
-
SetCustomFeePercentage {
|
|
256
|
-
account: Pubkey,
|
|
257
|
-
percentage: u8, // 0-100: 0 = free, 100 = full fee
|
|
258
|
-
},
|
|
259
|
-
|
|
260
|
-
/// Clear custom fee percentage for a specific address (owner only)
|
|
261
|
-
/// Accounts:
|
|
262
|
-
/// 0. `[signer]` Owner
|
|
263
|
-
/// 1. `[]` Mailer state account (PDA)
|
|
264
|
-
/// 2. `[writable]` Fee discount account (PDA)
|
|
265
|
-
ClearCustomFeePercentage { account: Pubkey },
|
|
266
|
-
|
|
201
|
+
|
|
267
202
|
/// Pause the contract (owner only)
|
|
268
203
|
/// Accounts:
|
|
269
204
|
/// 0. `[signer]` Owner
|
|
@@ -272,13 +207,13 @@ pub enum MailerInstruction {
|
|
|
272
207
|
/// 3. `[writable]` Mailer USDC account
|
|
273
208
|
/// 4. `[]` Token program
|
|
274
209
|
Pause,
|
|
275
|
-
|
|
210
|
+
|
|
276
211
|
/// Unpause the contract (owner only)
|
|
277
212
|
/// Accounts:
|
|
278
213
|
/// 0. `[signer]` Owner
|
|
279
214
|
/// 1. `[writable]` Mailer state account (PDA)
|
|
280
215
|
Unpause,
|
|
281
|
-
|
|
216
|
+
|
|
282
217
|
/// Distribute claimable funds (when paused)
|
|
283
218
|
/// Accounts:
|
|
284
219
|
/// 0. `[signer]` Anyone can call
|
|
@@ -289,13 +224,6 @@ pub enum MailerInstruction {
|
|
|
289
224
|
/// 5. `[]` Token program
|
|
290
225
|
DistributeClaimableFunds { recipient: Pubkey },
|
|
291
226
|
|
|
292
|
-
/// Claim expired recipient shares (owner only)
|
|
293
|
-
/// Accounts:
|
|
294
|
-
/// 0. `[signer]` Owner
|
|
295
|
-
/// 1. `[writable]` Mailer state account (PDA)
|
|
296
|
-
/// 2. `[writable]` Recipient claim account (PDA)
|
|
297
|
-
ClaimExpiredShares { recipient: Pubkey },
|
|
298
|
-
|
|
299
227
|
/// Emergency unpause without fund distribution (owner only)
|
|
300
228
|
/// Accounts:
|
|
301
229
|
/// 0. `[signer]` Owner
|
|
@@ -328,16 +256,10 @@ pub enum MailerError {
|
|
|
328
256
|
InvalidPDA,
|
|
329
257
|
#[error("Invalid account owner")]
|
|
330
258
|
InvalidAccountOwner,
|
|
331
|
-
#[error("Invalid token mint")]
|
|
332
|
-
InvalidMint,
|
|
333
|
-
#[error("Invalid token program")]
|
|
334
|
-
InvalidTokenProgram,
|
|
335
259
|
#[error("Contract is paused")]
|
|
336
260
|
ContractPaused,
|
|
337
261
|
#[error("Contract is not paused")]
|
|
338
262
|
ContractNotPaused,
|
|
339
|
-
#[error("Invalid percentage (must be 0-100)")]
|
|
340
|
-
InvalidPercentage,
|
|
341
263
|
}
|
|
342
264
|
|
|
343
265
|
impl From<MailerError> for ProgramError {
|
|
@@ -358,55 +280,15 @@ pub fn process_instruction(
|
|
|
358
280
|
MailerInstruction::Initialize { usdc_mint } => {
|
|
359
281
|
process_initialize(program_id, accounts, usdc_mint)
|
|
360
282
|
}
|
|
361
|
-
MailerInstruction::Send {
|
|
362
|
-
to,
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
} => process_send(
|
|
368
|
-
program_id,
|
|
369
|
-
accounts,
|
|
370
|
-
to,
|
|
371
|
-
subject,
|
|
372
|
-
_body,
|
|
373
|
-
revenue_share_to_receiver,
|
|
374
|
-
resolve_sender_to_name,
|
|
375
|
-
),
|
|
376
|
-
MailerInstruction::SendPrepared {
|
|
377
|
-
to,
|
|
378
|
-
mail_id,
|
|
379
|
-
revenue_share_to_receiver,
|
|
380
|
-
resolve_sender_to_name,
|
|
381
|
-
} => process_send_prepared(
|
|
382
|
-
program_id,
|
|
383
|
-
accounts,
|
|
384
|
-
to,
|
|
385
|
-
mail_id,
|
|
386
|
-
revenue_share_to_receiver,
|
|
387
|
-
resolve_sender_to_name,
|
|
388
|
-
),
|
|
389
|
-
MailerInstruction::SendToEmail {
|
|
390
|
-
to_email,
|
|
391
|
-
subject,
|
|
392
|
-
_body,
|
|
393
|
-
} => process_send_to_email(program_id, accounts, to_email, subject, _body),
|
|
283
|
+
MailerInstruction::Send { to, subject, _body, revenue_share_to_receiver, resolve_sender_to_name } => {
|
|
284
|
+
process_send(program_id, accounts, to, subject, _body, revenue_share_to_receiver, resolve_sender_to_name)
|
|
285
|
+
}
|
|
286
|
+
MailerInstruction::SendToEmail { to_email, subject, _body } => {
|
|
287
|
+
process_send_to_email(program_id, accounts, to_email, subject, _body)
|
|
288
|
+
}
|
|
394
289
|
MailerInstruction::SendPreparedToEmail { to_email, mail_id } => {
|
|
395
290
|
process_send_prepared_to_email(program_id, accounts, to_email, mail_id)
|
|
396
291
|
}
|
|
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
|
-
),
|
|
410
292
|
MailerInstruction::ClaimRecipientShare => {
|
|
411
293
|
process_claim_recipient_share(program_id, accounts)
|
|
412
294
|
}
|
|
@@ -419,22 +301,18 @@ pub fn process_instruction(
|
|
|
419
301
|
MailerInstruction::SetDelegationFee { new_fee } => {
|
|
420
302
|
process_set_delegation_fee(program_id, accounts, new_fee)
|
|
421
303
|
}
|
|
422
|
-
MailerInstruction::
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
process_clear_custom_fee_percentage(program_id, accounts, account)
|
|
304
|
+
MailerInstruction::Pause => {
|
|
305
|
+
process_pause(program_id, accounts)
|
|
306
|
+
}
|
|
307
|
+
MailerInstruction::Unpause => {
|
|
308
|
+
process_unpause(program_id, accounts)
|
|
428
309
|
}
|
|
429
|
-
MailerInstruction::Pause => process_pause(program_id, accounts),
|
|
430
|
-
MailerInstruction::Unpause => process_unpause(program_id, accounts),
|
|
431
310
|
MailerInstruction::DistributeClaimableFunds { recipient } => {
|
|
432
311
|
process_distribute_claimable_funds(program_id, accounts, recipient)
|
|
433
312
|
}
|
|
434
|
-
MailerInstruction::
|
|
435
|
-
|
|
313
|
+
MailerInstruction::EmergencyUnpause => {
|
|
314
|
+
process_emergency_unpause(program_id, accounts)
|
|
436
315
|
}
|
|
437
|
-
MailerInstruction::EmergencyUnpause => process_emergency_unpause(program_id, accounts),
|
|
438
316
|
}
|
|
439
317
|
}
|
|
440
318
|
|
|
@@ -472,11 +350,7 @@ fn process_initialize(
|
|
|
472
350
|
space as u64,
|
|
473
351
|
program_id,
|
|
474
352
|
),
|
|
475
|
-
&[
|
|
476
|
-
owner.clone(),
|
|
477
|
-
mailer_account.clone(),
|
|
478
|
-
system_program.clone(),
|
|
479
|
-
],
|
|
353
|
+
&[owner.clone(), mailer_account.clone(), system_program.clone()],
|
|
480
354
|
&[&[b"mailer", &[bump]]],
|
|
481
355
|
)?;
|
|
482
356
|
|
|
@@ -524,30 +398,23 @@ fn process_send(
|
|
|
524
398
|
}
|
|
525
399
|
|
|
526
400
|
// Load mailer state
|
|
527
|
-
let (mailer_pda, _) = assert_mailer_account(program_id, mailer_account)?;
|
|
528
401
|
let mailer_data = mailer_account.try_borrow_data()?;
|
|
529
402
|
let mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
|
|
530
403
|
drop(mailer_data);
|
|
531
404
|
|
|
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
|
-
|
|
536
405
|
// Check if contract is paused
|
|
537
406
|
if mailer_state.paused {
|
|
538
407
|
return Err(MailerError::ContractPaused.into());
|
|
539
408
|
}
|
|
540
409
|
|
|
541
|
-
// Calculate effective fee based on custom discount (if any)
|
|
542
|
-
let effective_fee =
|
|
543
|
-
calculate_fee_with_discount(program_id, sender.key, accounts, mailer_state.send_fee)?;
|
|
544
|
-
|
|
545
410
|
if revenue_share_to_receiver {
|
|
546
411
|
// Priority mode: full fee with revenue sharing
|
|
547
412
|
|
|
548
413
|
// Create or load recipient claim account
|
|
549
|
-
let (claim_pda, claim_bump) =
|
|
550
|
-
|
|
414
|
+
let (claim_pda, claim_bump) = Pubkey::find_program_address(
|
|
415
|
+
&[b"claim", to.as_ref()],
|
|
416
|
+
program_id
|
|
417
|
+
);
|
|
551
418
|
|
|
552
419
|
if recipient_claim.key != &claim_pda {
|
|
553
420
|
return Err(MailerError::InvalidPDA.into());
|
|
@@ -567,18 +434,13 @@ fn process_send(
|
|
|
567
434
|
space as u64,
|
|
568
435
|
program_id,
|
|
569
436
|
),
|
|
570
|
-
&[
|
|
571
|
-
sender.clone(),
|
|
572
|
-
recipient_claim.clone(),
|
|
573
|
-
system_program.clone(),
|
|
574
|
-
],
|
|
437
|
+
&[sender.clone(), recipient_claim.clone(), system_program.clone()],
|
|
575
438
|
&[&[b"claim", to.as_ref(), &[claim_bump]]],
|
|
576
439
|
)?;
|
|
577
440
|
|
|
578
441
|
// Initialize claim account
|
|
579
442
|
let mut claim_data = recipient_claim.try_borrow_mut_data()?;
|
|
580
|
-
claim_data[0..8]
|
|
581
|
-
.copy_from_slice(&hash_discriminator("account:RecipientClaim").to_le_bytes());
|
|
443
|
+
claim_data[0..8].copy_from_slice(&hash_discriminator("account:RecipientClaim").to_le_bytes());
|
|
582
444
|
|
|
583
445
|
let claim_state = RecipientClaim {
|
|
584
446
|
recipient: to,
|
|
@@ -591,53 +453,50 @@ fn process_send(
|
|
|
591
453
|
drop(claim_data);
|
|
592
454
|
}
|
|
593
455
|
|
|
594
|
-
// Transfer
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
)?;
|
|
456
|
+
// Transfer full send fee
|
|
457
|
+
invoke(
|
|
458
|
+
&spl_token::instruction::transfer(
|
|
459
|
+
token_program.key,
|
|
460
|
+
sender_usdc.key,
|
|
461
|
+
mailer_usdc.key,
|
|
462
|
+
sender.key,
|
|
463
|
+
&[],
|
|
464
|
+
mailer_state.send_fee,
|
|
465
|
+
)?,
|
|
466
|
+
&[
|
|
467
|
+
sender_usdc.clone(),
|
|
468
|
+
mailer_usdc.clone(),
|
|
469
|
+
sender.clone(),
|
|
470
|
+
token_program.clone(),
|
|
471
|
+
],
|
|
472
|
+
)?;
|
|
612
473
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
}
|
|
474
|
+
// Record revenue shares
|
|
475
|
+
record_shares(recipient_claim, mailer_account, to, mailer_state.send_fee)?;
|
|
616
476
|
|
|
617
|
-
msg!("Priority mail sent from {} to {}: {} (revenue share enabled, resolve sender: {}
|
|
477
|
+
msg!("Priority mail sent from {} to {}: {} (revenue share enabled, resolve sender: {})", sender.key, to, subject, _resolve_sender_to_name);
|
|
618
478
|
} else {
|
|
619
479
|
// Standard mode: 10% fee only, no revenue sharing
|
|
620
|
-
|
|
480
|
+
|
|
481
|
+
let owner_fee = mailer_state.send_fee / 10; // 10% of send_fee
|
|
621
482
|
|
|
622
483
|
// Transfer only owner fee (10%)
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
)?;
|
|
640
|
-
}
|
|
484
|
+
invoke(
|
|
485
|
+
&spl_token::instruction::transfer(
|
|
486
|
+
token_program.key,
|
|
487
|
+
sender_usdc.key,
|
|
488
|
+
mailer_usdc.key,
|
|
489
|
+
sender.key,
|
|
490
|
+
&[],
|
|
491
|
+
owner_fee,
|
|
492
|
+
)?,
|
|
493
|
+
&[
|
|
494
|
+
sender_usdc.clone(),
|
|
495
|
+
mailer_usdc.clone(),
|
|
496
|
+
sender.clone(),
|
|
497
|
+
token_program.clone(),
|
|
498
|
+
],
|
|
499
|
+
)?;
|
|
641
500
|
|
|
642
501
|
// Update owner claimable
|
|
643
502
|
let mut mailer_data = mailer_account.try_borrow_mut_data()?;
|
|
@@ -645,183 +504,81 @@ fn process_send(
|
|
|
645
504
|
mailer_state.owner_claimable += owner_fee;
|
|
646
505
|
mailer_state.serialize(&mut &mut mailer_data[8..])?;
|
|
647
506
|
|
|
648
|
-
msg!(
|
|
649
|
-
"Standard mail sent from {} to {}: {} (resolve sender: {}, effective fee: {})",
|
|
650
|
-
sender.key,
|
|
651
|
-
to,
|
|
652
|
-
subject,
|
|
653
|
-
_resolve_sender_to_name,
|
|
654
|
-
effective_fee
|
|
655
|
-
);
|
|
507
|
+
msg!("Standard mail sent from {} to {}: {} (resolve sender: {})", sender.key, to, subject, _resolve_sender_to_name);
|
|
656
508
|
}
|
|
657
509
|
|
|
658
510
|
Ok(())
|
|
659
511
|
}
|
|
660
512
|
|
|
661
|
-
///
|
|
662
|
-
fn
|
|
663
|
-
|
|
513
|
+
/// Process send to email address (no wallet known, only owner fee)
|
|
514
|
+
fn process_send_to_email(
|
|
515
|
+
_program_id: &Pubkey,
|
|
664
516
|
accounts: &[AccountInfo],
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
_resolve_sender_to_name: bool,
|
|
517
|
+
to_email: String,
|
|
518
|
+
subject: String,
|
|
519
|
+
_body: String,
|
|
669
520
|
) -> ProgramResult {
|
|
670
521
|
let account_iter = &mut accounts.iter();
|
|
671
522
|
let sender = next_account_info(account_iter)?;
|
|
672
|
-
let recipient_claim = next_account_info(account_iter)?;
|
|
673
523
|
let mailer_account = next_account_info(account_iter)?;
|
|
674
524
|
let sender_usdc = next_account_info(account_iter)?;
|
|
675
525
|
let mailer_usdc = next_account_info(account_iter)?;
|
|
676
526
|
let token_program = next_account_info(account_iter)?;
|
|
677
|
-
let system_program = next_account_info(account_iter)?;
|
|
678
527
|
|
|
679
528
|
if !sender.is_signer {
|
|
680
529
|
return Err(ProgramError::MissingRequiredSignature);
|
|
681
530
|
}
|
|
682
531
|
|
|
683
532
|
// Load mailer state
|
|
684
|
-
let (mailer_pda, _) = assert_mailer_account(program_id, mailer_account)?;
|
|
685
533
|
let mailer_data = mailer_account.try_borrow_data()?;
|
|
686
534
|
let mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
|
|
687
535
|
drop(mailer_data);
|
|
688
536
|
|
|
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
|
-
|
|
693
537
|
// Check if contract is paused
|
|
694
538
|
if mailer_state.paused {
|
|
695
539
|
return Err(MailerError::ContractPaused.into());
|
|
696
540
|
}
|
|
697
541
|
|
|
698
|
-
// Calculate
|
|
699
|
-
let
|
|
700
|
-
calculate_fee_with_discount(program_id, sender.key, accounts, mailer_state.send_fee)?;
|
|
701
|
-
|
|
702
|
-
if revenue_share_to_receiver {
|
|
703
|
-
// Priority mode: full fee with revenue sharing
|
|
704
|
-
|
|
705
|
-
// Create or load recipient claim account
|
|
706
|
-
let (claim_pda, claim_bump) =
|
|
707
|
-
Pubkey::find_program_address(&[b"claim", to.as_ref()], program_id);
|
|
708
|
-
|
|
709
|
-
if recipient_claim.key != &claim_pda {
|
|
710
|
-
return Err(MailerError::InvalidPDA.into());
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
// Create claim account if needed
|
|
714
|
-
if recipient_claim.lamports() == 0 {
|
|
715
|
-
let rent = Rent::get()?;
|
|
716
|
-
let space = 8 + RecipientClaim::LEN;
|
|
717
|
-
let lamports = rent.minimum_balance(space);
|
|
718
|
-
|
|
719
|
-
invoke_signed(
|
|
720
|
-
&system_instruction::create_account(
|
|
721
|
-
sender.key,
|
|
722
|
-
recipient_claim.key,
|
|
723
|
-
lamports,
|
|
724
|
-
space as u64,
|
|
725
|
-
program_id,
|
|
726
|
-
),
|
|
727
|
-
&[
|
|
728
|
-
sender.clone(),
|
|
729
|
-
recipient_claim.clone(),
|
|
730
|
-
system_program.clone(),
|
|
731
|
-
],
|
|
732
|
-
&[&[b"claim", to.as_ref(), &[claim_bump]]],
|
|
733
|
-
)?;
|
|
734
|
-
|
|
735
|
-
// Initialize claim account
|
|
736
|
-
let mut claim_data = recipient_claim.try_borrow_mut_data()?;
|
|
737
|
-
claim_data[0..8]
|
|
738
|
-
.copy_from_slice(&hash_discriminator("account:RecipientClaim").to_le_bytes());
|
|
739
|
-
|
|
740
|
-
let claim_state = RecipientClaim {
|
|
741
|
-
recipient: to,
|
|
742
|
-
amount: 0,
|
|
743
|
-
timestamp: 0,
|
|
744
|
-
bump: claim_bump,
|
|
745
|
-
};
|
|
746
|
-
|
|
747
|
-
claim_state.serialize(&mut &mut claim_data[8..])?;
|
|
748
|
-
drop(claim_data);
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
// Transfer effective fee (may be discounted)
|
|
752
|
-
if effective_fee > 0 {
|
|
753
|
-
invoke(
|
|
754
|
-
&spl_token::instruction::transfer(
|
|
755
|
-
token_program.key,
|
|
756
|
-
sender_usdc.key,
|
|
757
|
-
mailer_usdc.key,
|
|
758
|
-
sender.key,
|
|
759
|
-
&[],
|
|
760
|
-
effective_fee,
|
|
761
|
-
)?,
|
|
762
|
-
&[
|
|
763
|
-
sender_usdc.clone(),
|
|
764
|
-
mailer_usdc.clone(),
|
|
765
|
-
sender.clone(),
|
|
766
|
-
token_program.clone(),
|
|
767
|
-
],
|
|
768
|
-
)?;
|
|
769
|
-
|
|
770
|
-
// Record revenue shares (only if fee > 0)
|
|
771
|
-
record_shares(recipient_claim, mailer_account, to, effective_fee)?;
|
|
772
|
-
}
|
|
542
|
+
// Calculate 10% owner fee (no revenue share since no wallet address)
|
|
543
|
+
let owner_fee = (mailer_state.send_fee * 10) / 100;
|
|
773
544
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
545
|
+
// Transfer fee from sender to mailer
|
|
546
|
+
let transfer_ix = spl_token::instruction::transfer(
|
|
547
|
+
token_program.key,
|
|
548
|
+
sender_usdc.key,
|
|
549
|
+
mailer_usdc.key,
|
|
550
|
+
sender.key,
|
|
551
|
+
&[],
|
|
552
|
+
owner_fee,
|
|
553
|
+
)?;
|
|
778
554
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
owner_fee,
|
|
789
|
-
)?,
|
|
790
|
-
&[
|
|
791
|
-
sender_usdc.clone(),
|
|
792
|
-
mailer_usdc.clone(),
|
|
793
|
-
sender.clone(),
|
|
794
|
-
token_program.clone(),
|
|
795
|
-
],
|
|
796
|
-
)?;
|
|
797
|
-
}
|
|
555
|
+
invoke(
|
|
556
|
+
&transfer_ix,
|
|
557
|
+
&[
|
|
558
|
+
sender_usdc.clone(),
|
|
559
|
+
mailer_usdc.clone(),
|
|
560
|
+
sender.clone(),
|
|
561
|
+
token_program.clone(),
|
|
562
|
+
],
|
|
563
|
+
)?;
|
|
798
564
|
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
565
|
+
// Update owner claimable
|
|
566
|
+
let mut mailer_data = mailer_account.try_borrow_mut_data()?;
|
|
567
|
+
let mut mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
|
|
568
|
+
mailer_state.owner_claimable += owner_fee;
|
|
569
|
+
mailer_state.serialize(&mut &mut mailer_data[8..])?;
|
|
804
570
|
|
|
805
|
-
|
|
806
|
-
"Standard prepared mail sent from {} to {} (mailId: {}, resolve sender: {}, effective fee: {})",
|
|
807
|
-
sender.key,
|
|
808
|
-
to,
|
|
809
|
-
mail_id,
|
|
810
|
-
_resolve_sender_to_name,
|
|
811
|
-
effective_fee
|
|
812
|
-
);
|
|
813
|
-
}
|
|
571
|
+
msg!("Mail sent from {} to email {}: {}", sender.key, to_email, subject);
|
|
814
572
|
|
|
815
573
|
Ok(())
|
|
816
574
|
}
|
|
817
575
|
|
|
818
|
-
/// Process send to email address (no wallet known, only owner fee)
|
|
819
|
-
fn
|
|
576
|
+
/// Process send prepared to email address (no wallet known, only owner fee)
|
|
577
|
+
fn process_send_prepared_to_email(
|
|
820
578
|
_program_id: &Pubkey,
|
|
821
579
|
accounts: &[AccountInfo],
|
|
822
580
|
to_email: String,
|
|
823
|
-
|
|
824
|
-
_body: String,
|
|
581
|
+
mail_id: String,
|
|
825
582
|
) -> ProgramResult {
|
|
826
583
|
let account_iter = &mut accounts.iter();
|
|
827
584
|
let sender = next_account_info(account_iter)?;
|
|
@@ -835,127 +592,37 @@ fn process_send_to_email(
|
|
|
835
592
|
}
|
|
836
593
|
|
|
837
594
|
// Load mailer state
|
|
838
|
-
let (mailer_pda, _) = assert_mailer_account(_program_id, mailer_account)?;
|
|
839
595
|
let mailer_data = mailer_account.try_borrow_data()?;
|
|
840
596
|
let mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
|
|
841
597
|
drop(mailer_data);
|
|
842
598
|
|
|
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
|
-
|
|
847
599
|
// Check if contract is paused
|
|
848
600
|
if mailer_state.paused {
|
|
849
601
|
return Err(MailerError::ContractPaused.into());
|
|
850
602
|
}
|
|
851
603
|
|
|
852
|
-
// Calculate effective fee based on custom discount (if any)
|
|
853
|
-
let effective_fee =
|
|
854
|
-
calculate_fee_with_discount(_program_id, sender.key, accounts, mailer_state.send_fee)?;
|
|
855
|
-
|
|
856
604
|
// Calculate 10% owner fee (no revenue share since no wallet address)
|
|
857
|
-
let owner_fee = (
|
|
605
|
+
let owner_fee = (mailer_state.send_fee * 10) / 100;
|
|
858
606
|
|
|
859
607
|
// Transfer fee from sender to mailer
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
)?;
|
|
608
|
+
let transfer_ix = spl_token::instruction::transfer(
|
|
609
|
+
token_program.key,
|
|
610
|
+
sender_usdc.key,
|
|
611
|
+
mailer_usdc.key,
|
|
612
|
+
sender.key,
|
|
613
|
+
&[],
|
|
614
|
+
owner_fee,
|
|
615
|
+
)?;
|
|
869
616
|
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
// Update owner claimable
|
|
882
|
-
let mut mailer_data = mailer_account.try_borrow_mut_data()?;
|
|
883
|
-
let mut mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
|
|
884
|
-
mailer_state.owner_claimable += owner_fee;
|
|
885
|
-
mailer_state.serialize(&mut &mut mailer_data[8..])?;
|
|
886
|
-
|
|
887
|
-
msg!(
|
|
888
|
-
"Mail sent from {} to email {}: {} (effective fee: {})",
|
|
889
|
-
sender.key,
|
|
890
|
-
to_email,
|
|
891
|
-
subject,
|
|
892
|
-
effective_fee
|
|
893
|
-
);
|
|
894
|
-
|
|
895
|
-
Ok(())
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
/// Process send prepared to email address (no wallet known, only owner fee)
|
|
899
|
-
fn process_send_prepared_to_email(
|
|
900
|
-
_program_id: &Pubkey,
|
|
901
|
-
accounts: &[AccountInfo],
|
|
902
|
-
to_email: String,
|
|
903
|
-
mail_id: String,
|
|
904
|
-
) -> ProgramResult {
|
|
905
|
-
let account_iter = &mut accounts.iter();
|
|
906
|
-
let sender = next_account_info(account_iter)?;
|
|
907
|
-
let mailer_account = next_account_info(account_iter)?;
|
|
908
|
-
let sender_usdc = next_account_info(account_iter)?;
|
|
909
|
-
let mailer_usdc = next_account_info(account_iter)?;
|
|
910
|
-
let token_program = next_account_info(account_iter)?;
|
|
911
|
-
|
|
912
|
-
if !sender.is_signer {
|
|
913
|
-
return Err(ProgramError::MissingRequiredSignature);
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
// Load mailer state
|
|
917
|
-
let (mailer_pda, _) = assert_mailer_account(_program_id, mailer_account)?;
|
|
918
|
-
let mailer_data = mailer_account.try_borrow_data()?;
|
|
919
|
-
let mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
|
|
920
|
-
drop(mailer_data);
|
|
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
|
-
|
|
926
|
-
// Check if contract is paused
|
|
927
|
-
if mailer_state.paused {
|
|
928
|
-
return Err(MailerError::ContractPaused.into());
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
// Calculate effective fee based on custom discount (if any)
|
|
932
|
-
let effective_fee =
|
|
933
|
-
calculate_fee_with_discount(_program_id, sender.key, accounts, mailer_state.send_fee)?;
|
|
934
|
-
|
|
935
|
-
// Calculate 10% owner fee (no revenue share since no wallet address)
|
|
936
|
-
let owner_fee = (effective_fee * 10) / 100;
|
|
937
|
-
|
|
938
|
-
// Transfer fee from sender to mailer
|
|
939
|
-
if owner_fee > 0 {
|
|
940
|
-
let transfer_ix = spl_token::instruction::transfer(
|
|
941
|
-
token_program.key,
|
|
942
|
-
sender_usdc.key,
|
|
943
|
-
mailer_usdc.key,
|
|
944
|
-
sender.key,
|
|
945
|
-
&[],
|
|
946
|
-
owner_fee,
|
|
947
|
-
)?;
|
|
948
|
-
|
|
949
|
-
invoke(
|
|
950
|
-
&transfer_ix,
|
|
951
|
-
&[
|
|
952
|
-
sender_usdc.clone(),
|
|
953
|
-
mailer_usdc.clone(),
|
|
954
|
-
sender.clone(),
|
|
955
|
-
token_program.clone(),
|
|
956
|
-
],
|
|
957
|
-
)?;
|
|
958
|
-
}
|
|
617
|
+
invoke(
|
|
618
|
+
&transfer_ix,
|
|
619
|
+
&[
|
|
620
|
+
sender_usdc.clone(),
|
|
621
|
+
mailer_usdc.clone(),
|
|
622
|
+
sender.clone(),
|
|
623
|
+
token_program.clone(),
|
|
624
|
+
],
|
|
625
|
+
)?;
|
|
959
626
|
|
|
960
627
|
// Update owner claimable
|
|
961
628
|
let mut mailer_data = mailer_account.try_borrow_mut_data()?;
|
|
@@ -963,170 +630,7 @@ fn process_send_prepared_to_email(
|
|
|
963
630
|
mailer_state.owner_claimable += owner_fee;
|
|
964
631
|
mailer_state.serialize(&mut &mut mailer_data[8..])?;
|
|
965
632
|
|
|
966
|
-
msg!(
|
|
967
|
-
"Prepared mail sent from {} to email {} (mailId: {}, effective fee: {})",
|
|
968
|
-
sender.key,
|
|
969
|
-
to_email,
|
|
970
|
-
mail_id,
|
|
971
|
-
effective_fee
|
|
972
|
-
);
|
|
973
|
-
|
|
974
|
-
Ok(())
|
|
975
|
-
}
|
|
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
|
-
}
|
|
633
|
+
msg!("Prepared mail sent from {} to email {} (mailId: {})", sender.key, to_email, mail_id);
|
|
1130
634
|
|
|
1131
635
|
Ok(())
|
|
1132
636
|
}
|
|
@@ -1145,13 +649,6 @@ fn process_claim_recipient_share(_program_id: &Pubkey, accounts: &[AccountInfo])
|
|
|
1145
649
|
return Err(ProgramError::MissingRequiredSignature);
|
|
1146
650
|
}
|
|
1147
651
|
|
|
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
|
-
|
|
1155
652
|
// Load claim state
|
|
1156
653
|
let mut claim_data = recipient_claim.try_borrow_mut_data()?;
|
|
1157
654
|
let mut claim_state: RecipientClaim = BorshDeserialize::deserialize(&mut &claim_data[8..])?;
|
|
@@ -1178,11 +675,6 @@ fn process_claim_recipient_share(_program_id: &Pubkey, accounts: &[AccountInfo])
|
|
|
1178
675
|
// Load mailer state for PDA signing
|
|
1179
676
|
let mailer_data = mailer_account.try_borrow_data()?;
|
|
1180
677
|
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)?;
|
|
1186
678
|
|
|
1187
679
|
// Transfer USDC from mailer to recipient
|
|
1188
680
|
invoke_signed(
|
|
@@ -1220,8 +712,6 @@ fn process_claim_owner_share(_program_id: &Pubkey, accounts: &[AccountInfo]) ->
|
|
|
1220
712
|
return Err(ProgramError::MissingRequiredSignature);
|
|
1221
713
|
}
|
|
1222
714
|
|
|
1223
|
-
let (mailer_pda, _) = assert_mailer_account(_program_id, mailer_account)?;
|
|
1224
|
-
|
|
1225
715
|
// Load and update mailer state
|
|
1226
716
|
let mut mailer_data = mailer_account.try_borrow_mut_data()?;
|
|
1227
717
|
let mut mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
|
|
@@ -1239,10 +729,6 @@ fn process_claim_owner_share(_program_id: &Pubkey, accounts: &[AccountInfo]) ->
|
|
|
1239
729
|
mailer_state.serialize(&mut &mut mailer_data[8..])?;
|
|
1240
730
|
drop(mailer_data);
|
|
1241
731
|
|
|
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
|
-
|
|
1246
732
|
// Transfer USDC from mailer to owner
|
|
1247
733
|
invoke_signed(
|
|
1248
734
|
&spl_token::instruction::transfer(
|
|
@@ -1276,8 +762,6 @@ fn process_set_fee(_program_id: &Pubkey, accounts: &[AccountInfo], new_fee: u64)
|
|
|
1276
762
|
return Err(ProgramError::MissingRequiredSignature);
|
|
1277
763
|
}
|
|
1278
764
|
|
|
1279
|
-
assert_mailer_account(_program_id, mailer_account)?;
|
|
1280
|
-
|
|
1281
765
|
// Load and update mailer state
|
|
1282
766
|
let mut mailer_data = mailer_account.try_borrow_mut_data()?;
|
|
1283
767
|
let mut mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
|
|
@@ -1318,25 +802,21 @@ fn process_delegate_to(
|
|
|
1318
802
|
return Err(ProgramError::MissingRequiredSignature);
|
|
1319
803
|
}
|
|
1320
804
|
|
|
1321
|
-
let (mailer_pda, _) = assert_mailer_account(program_id, mailer_account)?;
|
|
1322
|
-
|
|
1323
805
|
// Load mailer state
|
|
1324
806
|
let mailer_data = mailer_account.try_borrow_data()?;
|
|
1325
807
|
let mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
|
|
1326
808
|
drop(mailer_data);
|
|
1327
809
|
|
|
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
|
-
|
|
1332
810
|
// Check if contract is paused
|
|
1333
811
|
if mailer_state.paused {
|
|
1334
812
|
return Err(MailerError::ContractPaused.into());
|
|
1335
813
|
}
|
|
1336
814
|
|
|
1337
815
|
// Verify delegation account PDA
|
|
1338
|
-
let (delegation_pda, delegation_bump) =
|
|
1339
|
-
|
|
816
|
+
let (delegation_pda, delegation_bump) = Pubkey::find_program_address(
|
|
817
|
+
&[b"delegation", delegator.key.as_ref()],
|
|
818
|
+
program_id
|
|
819
|
+
);
|
|
1340
820
|
|
|
1341
821
|
if delegation_account.key != &delegation_pda {
|
|
1342
822
|
return Err(MailerError::InvalidPDA.into());
|
|
@@ -1366,8 +846,7 @@ fn process_delegate_to(
|
|
|
1366
846
|
|
|
1367
847
|
// Initialize delegation account
|
|
1368
848
|
let mut delegation_data = delegation_account.try_borrow_mut_data()?;
|
|
1369
|
-
delegation_data[0..8]
|
|
1370
|
-
.copy_from_slice(&hash_discriminator("account:Delegation").to_le_bytes());
|
|
849
|
+
delegation_data[0..8].copy_from_slice(&hash_discriminator("account:Delegation").to_le_bytes());
|
|
1371
850
|
|
|
1372
851
|
let delegation_state = Delegation {
|
|
1373
852
|
delegator: *delegator.key,
|
|
@@ -1403,8 +882,7 @@ fn process_delegate_to(
|
|
|
1403
882
|
|
|
1404
883
|
// Update delegation
|
|
1405
884
|
let mut delegation_data = delegation_account.try_borrow_mut_data()?;
|
|
1406
|
-
let mut delegation_state: Delegation =
|
|
1407
|
-
BorshDeserialize::deserialize(&mut &delegation_data[8..])?;
|
|
885
|
+
let mut delegation_state: Delegation = BorshDeserialize::deserialize(&mut &delegation_data[8..])?;
|
|
1408
886
|
delegation_state.delegate = delegate;
|
|
1409
887
|
delegation_state.serialize(&mut &mut delegation_data[8..])?;
|
|
1410
888
|
|
|
@@ -1413,31 +891,18 @@ fn process_delegate_to(
|
|
|
1413
891
|
}
|
|
1414
892
|
|
|
1415
893
|
/// Reject delegation
|
|
1416
|
-
fn process_reject_delegation(
|
|
894
|
+
fn process_reject_delegation(_program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
|
|
1417
895
|
let account_iter = &mut accounts.iter();
|
|
1418
896
|
let rejector = next_account_info(account_iter)?;
|
|
1419
897
|
let delegation_account = next_account_info(account_iter)?;
|
|
1420
|
-
let mailer_account = next_account_info(account_iter)?;
|
|
1421
898
|
|
|
1422
899
|
if !rejector.is_signer {
|
|
1423
900
|
return Err(ProgramError::MissingRequiredSignature);
|
|
1424
901
|
}
|
|
1425
902
|
|
|
1426
|
-
// Verify mailer state PDA and ensure contract is not paused
|
|
1427
|
-
let (_mailer_pda, _) = assert_mailer_account(program_id, mailer_account)?;
|
|
1428
|
-
|
|
1429
|
-
let mailer_data = mailer_account.try_borrow_data()?;
|
|
1430
|
-
let mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
|
|
1431
|
-
drop(mailer_data);
|
|
1432
|
-
|
|
1433
|
-
if mailer_state.paused {
|
|
1434
|
-
return Err(MailerError::ContractPaused.into());
|
|
1435
|
-
}
|
|
1436
|
-
|
|
1437
903
|
// Load and update delegation state
|
|
1438
904
|
let mut delegation_data = delegation_account.try_borrow_mut_data()?;
|
|
1439
|
-
let mut delegation_state: Delegation =
|
|
1440
|
-
BorshDeserialize::deserialize(&mut &delegation_data[8..])?;
|
|
905
|
+
let mut delegation_state: Delegation = BorshDeserialize::deserialize(&mut &delegation_data[8..])?;
|
|
1441
906
|
|
|
1442
907
|
// Verify the rejector is the current delegate
|
|
1443
908
|
if delegation_state.delegate != Some(*rejector.key) {
|
|
@@ -1465,8 +930,6 @@ fn process_set_delegation_fee(
|
|
|
1465
930
|
return Err(ProgramError::MissingRequiredSignature);
|
|
1466
931
|
}
|
|
1467
932
|
|
|
1468
|
-
assert_mailer_account(_program_id, mailer_account)?;
|
|
1469
|
-
|
|
1470
933
|
// Load and update mailer state
|
|
1471
934
|
let mut mailer_data = mailer_account.try_borrow_mut_data()?;
|
|
1472
935
|
let mut mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
|
|
@@ -1488,194 +951,6 @@ fn process_set_delegation_fee(
|
|
|
1488
951
|
Ok(())
|
|
1489
952
|
}
|
|
1490
953
|
|
|
1491
|
-
/// Set custom fee percentage for a specific address (owner only)
|
|
1492
|
-
fn process_set_custom_fee_percentage(
|
|
1493
|
-
program_id: &Pubkey,
|
|
1494
|
-
accounts: &[AccountInfo],
|
|
1495
|
-
account: Pubkey,
|
|
1496
|
-
percentage: u8,
|
|
1497
|
-
) -> ProgramResult {
|
|
1498
|
-
let account_iter = &mut accounts.iter();
|
|
1499
|
-
let owner = next_account_info(account_iter)?;
|
|
1500
|
-
let mailer_account = next_account_info(account_iter)?;
|
|
1501
|
-
let fee_discount_account = next_account_info(account_iter)?;
|
|
1502
|
-
let _target_account = next_account_info(account_iter)?;
|
|
1503
|
-
let payer = next_account_info(account_iter)?;
|
|
1504
|
-
let system_program = next_account_info(account_iter)?;
|
|
1505
|
-
|
|
1506
|
-
if !owner.is_signer {
|
|
1507
|
-
return Err(ProgramError::MissingRequiredSignature);
|
|
1508
|
-
}
|
|
1509
|
-
|
|
1510
|
-
assert_mailer_account(program_id, mailer_account)?;
|
|
1511
|
-
|
|
1512
|
-
// Load mailer state and verify owner
|
|
1513
|
-
let mailer_data = mailer_account.try_borrow_data()?;
|
|
1514
|
-
let mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
|
|
1515
|
-
drop(mailer_data);
|
|
1516
|
-
|
|
1517
|
-
if mailer_state.owner != *owner.key {
|
|
1518
|
-
return Err(MailerError::OnlyOwner.into());
|
|
1519
|
-
}
|
|
1520
|
-
|
|
1521
|
-
// Check if contract is paused
|
|
1522
|
-
if mailer_state.paused {
|
|
1523
|
-
return Err(MailerError::ContractPaused.into());
|
|
1524
|
-
}
|
|
1525
|
-
|
|
1526
|
-
// Validate percentage
|
|
1527
|
-
if percentage > 100 {
|
|
1528
|
-
return Err(MailerError::InvalidPercentage.into());
|
|
1529
|
-
}
|
|
1530
|
-
|
|
1531
|
-
// Verify fee discount account PDA
|
|
1532
|
-
let (discount_pda, bump) =
|
|
1533
|
-
Pubkey::find_program_address(&[b"discount", account.as_ref()], program_id);
|
|
1534
|
-
|
|
1535
|
-
if fee_discount_account.key != &discount_pda {
|
|
1536
|
-
return Err(MailerError::InvalidPDA.into());
|
|
1537
|
-
}
|
|
1538
|
-
|
|
1539
|
-
// Create or update fee discount account
|
|
1540
|
-
if fee_discount_account.lamports() == 0 {
|
|
1541
|
-
let rent = Rent::get()?;
|
|
1542
|
-
let space = 8 + FeeDiscount::LEN;
|
|
1543
|
-
let lamports = rent.minimum_balance(space);
|
|
1544
|
-
|
|
1545
|
-
invoke_signed(
|
|
1546
|
-
&system_instruction::create_account(
|
|
1547
|
-
payer.key,
|
|
1548
|
-
fee_discount_account.key,
|
|
1549
|
-
lamports,
|
|
1550
|
-
space as u64,
|
|
1551
|
-
program_id,
|
|
1552
|
-
),
|
|
1553
|
-
&[
|
|
1554
|
-
payer.clone(),
|
|
1555
|
-
fee_discount_account.clone(),
|
|
1556
|
-
system_program.clone(),
|
|
1557
|
-
],
|
|
1558
|
-
&[&[b"discount", account.as_ref(), &[bump]]],
|
|
1559
|
-
)?;
|
|
1560
|
-
|
|
1561
|
-
// Initialize discount account
|
|
1562
|
-
let mut discount_data = fee_discount_account.try_borrow_mut_data()?;
|
|
1563
|
-
discount_data[0..8]
|
|
1564
|
-
.copy_from_slice(&hash_discriminator("account:FeeDiscount").to_le_bytes());
|
|
1565
|
-
|
|
1566
|
-
let fee_discount = FeeDiscount {
|
|
1567
|
-
account,
|
|
1568
|
-
discount: 100 - percentage, // Store as discount: 0% fee = 100 discount, 100% fee = 0 discount
|
|
1569
|
-
bump,
|
|
1570
|
-
};
|
|
1571
|
-
|
|
1572
|
-
fee_discount.serialize(&mut &mut discount_data[8..])?;
|
|
1573
|
-
} else {
|
|
1574
|
-
// Update existing discount account
|
|
1575
|
-
let mut discount_data = fee_discount_account.try_borrow_mut_data()?;
|
|
1576
|
-
let mut fee_discount: FeeDiscount =
|
|
1577
|
-
BorshDeserialize::deserialize(&mut &discount_data[8..])?;
|
|
1578
|
-
fee_discount.discount = 100 - percentage; // Store as discount
|
|
1579
|
-
fee_discount.serialize(&mut &mut discount_data[8..])?;
|
|
1580
|
-
}
|
|
1581
|
-
|
|
1582
|
-
msg!("Custom fee percentage set for {}: {}%", account, percentage);
|
|
1583
|
-
Ok(())
|
|
1584
|
-
}
|
|
1585
|
-
|
|
1586
|
-
/// Clear custom fee percentage for a specific address (owner only)
|
|
1587
|
-
fn process_clear_custom_fee_percentage(
|
|
1588
|
-
program_id: &Pubkey,
|
|
1589
|
-
accounts: &[AccountInfo],
|
|
1590
|
-
account: Pubkey,
|
|
1591
|
-
) -> ProgramResult {
|
|
1592
|
-
let account_iter = &mut accounts.iter();
|
|
1593
|
-
let owner = next_account_info(account_iter)?;
|
|
1594
|
-
let mailer_account = next_account_info(account_iter)?;
|
|
1595
|
-
let fee_discount_account = next_account_info(account_iter)?;
|
|
1596
|
-
|
|
1597
|
-
if !owner.is_signer {
|
|
1598
|
-
return Err(ProgramError::MissingRequiredSignature);
|
|
1599
|
-
}
|
|
1600
|
-
|
|
1601
|
-
assert_mailer_account(program_id, mailer_account)?;
|
|
1602
|
-
|
|
1603
|
-
// Load mailer state and verify owner
|
|
1604
|
-
let mailer_data = mailer_account.try_borrow_data()?;
|
|
1605
|
-
let mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
|
|
1606
|
-
drop(mailer_data);
|
|
1607
|
-
|
|
1608
|
-
if mailer_state.owner != *owner.key {
|
|
1609
|
-
return Err(MailerError::OnlyOwner.into());
|
|
1610
|
-
}
|
|
1611
|
-
|
|
1612
|
-
// Check if contract is paused
|
|
1613
|
-
if mailer_state.paused {
|
|
1614
|
-
return Err(MailerError::ContractPaused.into());
|
|
1615
|
-
}
|
|
1616
|
-
|
|
1617
|
-
// Verify fee discount account PDA
|
|
1618
|
-
let (discount_pda, _) =
|
|
1619
|
-
Pubkey::find_program_address(&[b"discount", account.as_ref()], program_id);
|
|
1620
|
-
|
|
1621
|
-
if fee_discount_account.key != &discount_pda {
|
|
1622
|
-
return Err(MailerError::InvalidPDA.into());
|
|
1623
|
-
}
|
|
1624
|
-
|
|
1625
|
-
// Clear by setting discount to 0 (no discount = 100% fee = default behavior)
|
|
1626
|
-
if fee_discount_account.lamports() > 0 {
|
|
1627
|
-
let mut discount_data = fee_discount_account.try_borrow_mut_data()?;
|
|
1628
|
-
let mut fee_discount: FeeDiscount =
|
|
1629
|
-
BorshDeserialize::deserialize(&mut &discount_data[8..])?;
|
|
1630
|
-
fee_discount.discount = 0; // 0 discount = 100% fee = default
|
|
1631
|
-
fee_discount.serialize(&mut &mut discount_data[8..])?;
|
|
1632
|
-
}
|
|
1633
|
-
|
|
1634
|
-
msg!(
|
|
1635
|
-
"Custom fee percentage cleared for {} (reset to 100%)",
|
|
1636
|
-
account
|
|
1637
|
-
);
|
|
1638
|
-
Ok(())
|
|
1639
|
-
}
|
|
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
|
-
|
|
1679
954
|
/// Record revenue shares for priority messages
|
|
1680
955
|
fn record_shares(
|
|
1681
956
|
recipient_claim: &AccountInfo,
|
|
@@ -1702,51 +977,10 @@ fn record_shares(
|
|
|
1702
977
|
mailer_state.owner_claimable += owner_amount;
|
|
1703
978
|
mailer_state.serialize(&mut &mut mailer_data[8..])?;
|
|
1704
979
|
|
|
1705
|
-
msg!(
|
|
1706
|
-
"Shares recorded: recipient {}, owner {}",
|
|
1707
|
-
recipient_amount,
|
|
1708
|
-
owner_amount
|
|
1709
|
-
);
|
|
980
|
+
msg!("Shares recorded: recipient {}, owner {}", recipient_amount, owner_amount);
|
|
1710
981
|
Ok(())
|
|
1711
982
|
}
|
|
1712
983
|
|
|
1713
|
-
/// Calculate the effective fee for an account based on custom discount
|
|
1714
|
-
/// If no discount account exists, returns full base_fee (default behavior)
|
|
1715
|
-
/// Otherwise applies discount: fee = base_fee * (100 - discount) / 100
|
|
1716
|
-
fn calculate_fee_with_discount(
|
|
1717
|
-
program_id: &Pubkey,
|
|
1718
|
-
account: &Pubkey,
|
|
1719
|
-
accounts: &[AccountInfo],
|
|
1720
|
-
base_fee: u64,
|
|
1721
|
-
) -> Result<u64, ProgramError> {
|
|
1722
|
-
// Try to find fee discount account
|
|
1723
|
-
let (discount_pda, _) =
|
|
1724
|
-
Pubkey::find_program_address(&[b"discount", account.as_ref()], program_id);
|
|
1725
|
-
|
|
1726
|
-
// Check if any account in the accounts slice matches the discount PDA
|
|
1727
|
-
let discount_account = accounts.iter().find(|acc| acc.key == &discount_pda);
|
|
1728
|
-
|
|
1729
|
-
if let Some(discount_acc) = discount_account {
|
|
1730
|
-
// Account exists and has lamports - load the discount
|
|
1731
|
-
if discount_acc.lamports() > 0 {
|
|
1732
|
-
let discount_data = discount_acc.try_borrow_data()?;
|
|
1733
|
-
if discount_data.len() >= 8 + FeeDiscount::LEN {
|
|
1734
|
-
let fee_discount: FeeDiscount =
|
|
1735
|
-
BorshDeserialize::deserialize(&mut &discount_data[8..])?;
|
|
1736
|
-
let discount = fee_discount.discount as u64;
|
|
1737
|
-
|
|
1738
|
-
// Apply discount: fee = base_fee * (100 - discount) / 100
|
|
1739
|
-
// Examples: discount=0 → 100% fee, discount=50 → 50% fee, discount=100 → 0% fee (free)
|
|
1740
|
-
let effective_fee = (base_fee * (100 - discount)) / 100;
|
|
1741
|
-
return Ok(effective_fee);
|
|
1742
|
-
}
|
|
1743
|
-
}
|
|
1744
|
-
}
|
|
1745
|
-
|
|
1746
|
-
// No discount account or uninitialized - use full fee (default behavior)
|
|
1747
|
-
Ok(base_fee)
|
|
1748
|
-
}
|
|
1749
|
-
|
|
1750
984
|
/// Pause the contract and distribute owner claimable funds
|
|
1751
985
|
fn process_pause(_program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
|
|
1752
986
|
let account_iter = &mut accounts.iter();
|
|
@@ -1760,8 +994,6 @@ fn process_pause(_program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResul
|
|
|
1760
994
|
return Err(ProgramError::MissingRequiredSignature);
|
|
1761
995
|
}
|
|
1762
996
|
|
|
1763
|
-
let (mailer_pda, _) = assert_mailer_account(_program_id, mailer_account)?;
|
|
1764
|
-
|
|
1765
997
|
// Load and update mailer state
|
|
1766
998
|
let mut mailer_data = mailer_account.try_borrow_mut_data()?;
|
|
1767
999
|
let mut mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
|
|
@@ -1779,17 +1011,13 @@ fn process_pause(_program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResul
|
|
|
1779
1011
|
// Set paused state
|
|
1780
1012
|
mailer_state.paused = true;
|
|
1781
1013
|
|
|
1782
|
-
assert_token_program(token_program)?;
|
|
1783
|
-
|
|
1784
1014
|
// Distribute owner claimable funds if any
|
|
1785
1015
|
if mailer_state.owner_claimable > 0 {
|
|
1786
1016
|
let amount = mailer_state.owner_claimable;
|
|
1787
1017
|
mailer_state.owner_claimable = 0;
|
|
1788
1018
|
|
|
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
|
-
|
|
1792
1019
|
// Transfer USDC from mailer to owner
|
|
1020
|
+
let (mailer_pda, bump) = Pubkey::find_program_address(&[b"mailer"], _program_id);
|
|
1793
1021
|
invoke_signed(
|
|
1794
1022
|
&spl_token::instruction::transfer(
|
|
1795
1023
|
token_program.key,
|
|
@@ -1799,13 +1027,8 @@ fn process_pause(_program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResul
|
|
|
1799
1027
|
&[],
|
|
1800
1028
|
amount,
|
|
1801
1029
|
)?,
|
|
1802
|
-
&[
|
|
1803
|
-
|
|
1804
|
-
owner_usdc.clone(),
|
|
1805
|
-
mailer_account.clone(),
|
|
1806
|
-
token_program.clone(),
|
|
1807
|
-
],
|
|
1808
|
-
&[&[b"mailer", &[mailer_state.bump]]],
|
|
1030
|
+
&[mailer_usdc.clone(), owner_usdc.clone(), token_program.clone()],
|
|
1031
|
+
&[&[b"mailer", &[bump]]],
|
|
1809
1032
|
)?;
|
|
1810
1033
|
|
|
1811
1034
|
msg!("Distributed owner funds during pause: {}", amount);
|
|
@@ -1813,7 +1036,7 @@ fn process_pause(_program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResul
|
|
|
1813
1036
|
|
|
1814
1037
|
// Save updated state
|
|
1815
1038
|
mailer_state.serialize(&mut &mut mailer_data[8..])?;
|
|
1816
|
-
|
|
1039
|
+
|
|
1817
1040
|
msg!("Contract paused by owner: {}", owner.key);
|
|
1818
1041
|
Ok(())
|
|
1819
1042
|
}
|
|
@@ -1828,8 +1051,6 @@ fn process_unpause(_program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramRes
|
|
|
1828
1051
|
return Err(ProgramError::MissingRequiredSignature);
|
|
1829
1052
|
}
|
|
1830
1053
|
|
|
1831
|
-
assert_mailer_account(_program_id, mailer_account)?;
|
|
1832
|
-
|
|
1833
1054
|
// Load and update mailer state
|
|
1834
1055
|
let mut mailer_data = mailer_account.try_borrow_mut_data()?;
|
|
1835
1056
|
let mut mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
|
|
@@ -1847,11 +1068,43 @@ fn process_unpause(_program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramRes
|
|
|
1847
1068
|
// Set unpaused state
|
|
1848
1069
|
mailer_state.paused = false;
|
|
1849
1070
|
mailer_state.serialize(&mut &mut mailer_data[8..])?;
|
|
1850
|
-
|
|
1071
|
+
|
|
1851
1072
|
msg!("Contract unpaused by owner: {}", owner.key);
|
|
1852
1073
|
Ok(())
|
|
1853
1074
|
}
|
|
1854
1075
|
|
|
1076
|
+
/// Emergency unpause without fund distribution (owner only)
|
|
1077
|
+
fn process_emergency_unpause(_program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
|
|
1078
|
+
let account_iter = &mut accounts.iter();
|
|
1079
|
+
let owner = next_account_info(account_iter)?;
|
|
1080
|
+
let mailer_account = next_account_info(account_iter)?;
|
|
1081
|
+
|
|
1082
|
+
if !owner.is_signer {
|
|
1083
|
+
return Err(ProgramError::MissingRequiredSignature);
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
// Load and update mailer state
|
|
1087
|
+
let mut mailer_data = mailer_account.try_borrow_mut_data()?;
|
|
1088
|
+
let mut mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
|
|
1089
|
+
|
|
1090
|
+
// Verify owner
|
|
1091
|
+
if mailer_state.owner != *owner.key {
|
|
1092
|
+
return Err(MailerError::OnlyOwner.into());
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
// Check if not paused
|
|
1096
|
+
if !mailer_state.paused {
|
|
1097
|
+
return Err(MailerError::ContractNotPaused.into());
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
// Set unpaused state without fund distribution
|
|
1101
|
+
mailer_state.paused = false;
|
|
1102
|
+
mailer_state.serialize(&mut &mut mailer_data[8..])?;
|
|
1103
|
+
|
|
1104
|
+
msg!("Contract emergency unpaused by owner: {} - funds can be claimed manually", owner.key);
|
|
1105
|
+
Ok(())
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1855
1108
|
/// Distribute claimable funds when contract is paused
|
|
1856
1109
|
fn process_distribute_claimable_funds(
|
|
1857
1110
|
_program_id: &Pubkey,
|
|
@@ -1866,8 +1119,6 @@ fn process_distribute_claimable_funds(
|
|
|
1866
1119
|
let mailer_usdc = next_account_info(account_iter)?;
|
|
1867
1120
|
let token_program = next_account_info(account_iter)?;
|
|
1868
1121
|
|
|
1869
|
-
let (mailer_pda, _) = assert_mailer_account(_program_id, mailer_account)?;
|
|
1870
|
-
|
|
1871
1122
|
// Load mailer state to check if paused
|
|
1872
1123
|
let mailer_data = mailer_account.try_borrow_data()?;
|
|
1873
1124
|
let mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
|
|
@@ -1884,8 +1135,6 @@ fn process_distribute_claimable_funds(
|
|
|
1884
1135
|
return Err(MailerError::InvalidPDA.into());
|
|
1885
1136
|
}
|
|
1886
1137
|
|
|
1887
|
-
assert_token_program(token_program)?;
|
|
1888
|
-
|
|
1889
1138
|
// Load and update recipient claim
|
|
1890
1139
|
let mut claim_data = recipient_claim_account.try_borrow_mut_data()?;
|
|
1891
1140
|
let mut claim_state: RecipientClaim = BorshDeserialize::deserialize(&mut &claim_data[8..])?;
|
|
@@ -1898,10 +1147,8 @@ fn process_distribute_claimable_funds(
|
|
|
1898
1147
|
claim_state.amount = 0;
|
|
1899
1148
|
claim_state.timestamp = 0;
|
|
1900
1149
|
|
|
1901
|
-
assert_token_account(recipient_usdc, &recipient, &mailer_state.usdc_mint)?;
|
|
1902
|
-
assert_token_account(mailer_usdc, &mailer_pda, &mailer_state.usdc_mint)?;
|
|
1903
|
-
|
|
1904
1150
|
// Transfer USDC from mailer to recipient
|
|
1151
|
+
let (mailer_pda, bump) = Pubkey::find_program_address(&[b"mailer"], _program_id);
|
|
1905
1152
|
invoke_signed(
|
|
1906
1153
|
&spl_token::instruction::transfer(
|
|
1907
1154
|
token_program.key,
|
|
@@ -1911,118 +1158,16 @@ fn process_distribute_claimable_funds(
|
|
|
1911
1158
|
&[],
|
|
1912
1159
|
amount,
|
|
1913
1160
|
)?,
|
|
1914
|
-
&[
|
|
1915
|
-
|
|
1916
|
-
recipient_usdc.clone(),
|
|
1917
|
-
mailer_account.clone(),
|
|
1918
|
-
token_program.clone(),
|
|
1919
|
-
],
|
|
1920
|
-
&[&[b"mailer", &[mailer_state.bump]]],
|
|
1161
|
+
&[mailer_usdc.clone(), recipient_usdc.clone(), token_program.clone()],
|
|
1162
|
+
&[&[b"mailer", &[bump]]],
|
|
1921
1163
|
)?;
|
|
1922
1164
|
|
|
1923
1165
|
claim_state.serialize(&mut &mut claim_data[8..])?;
|
|
1924
|
-
|
|
1166
|
+
|
|
1925
1167
|
msg!("Distributed claimable funds to {}: {}", recipient, amount);
|
|
1926
1168
|
Ok(())
|
|
1927
1169
|
}
|
|
1928
1170
|
|
|
1929
|
-
/// Claim expired shares and move them under owner control (owner only)
|
|
1930
|
-
fn process_claim_expired_shares(
|
|
1931
|
-
program_id: &Pubkey,
|
|
1932
|
-
accounts: &[AccountInfo],
|
|
1933
|
-
recipient: Pubkey,
|
|
1934
|
-
) -> ProgramResult {
|
|
1935
|
-
let account_iter = &mut accounts.iter();
|
|
1936
|
-
let owner = next_account_info(account_iter)?;
|
|
1937
|
-
let mailer_account = next_account_info(account_iter)?;
|
|
1938
|
-
let recipient_claim_account = next_account_info(account_iter)?;
|
|
1939
|
-
|
|
1940
|
-
if !owner.is_signer {
|
|
1941
|
-
return Err(ProgramError::MissingRequiredSignature);
|
|
1942
|
-
}
|
|
1943
|
-
|
|
1944
|
-
let (_mailer_pda, _) = assert_mailer_account(program_id, mailer_account)?;
|
|
1945
|
-
|
|
1946
|
-
// Load and verify mailer state
|
|
1947
|
-
let mut mailer_data = mailer_account.try_borrow_mut_data()?;
|
|
1948
|
-
let mut mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
|
|
1949
|
-
|
|
1950
|
-
if mailer_state.owner != *owner.key {
|
|
1951
|
-
return Err(MailerError::OnlyOwner.into());
|
|
1952
|
-
}
|
|
1953
|
-
|
|
1954
|
-
// Verify recipient claim PDA
|
|
1955
|
-
let (claim_pda, _) = Pubkey::find_program_address(&[b"claim", recipient.as_ref()], program_id);
|
|
1956
|
-
if recipient_claim_account.key != &claim_pda {
|
|
1957
|
-
return Err(MailerError::InvalidPDA.into());
|
|
1958
|
-
}
|
|
1959
|
-
|
|
1960
|
-
// Load and validate claim state
|
|
1961
|
-
let mut claim_data = recipient_claim_account.try_borrow_mut_data()?;
|
|
1962
|
-
let mut claim_state: RecipientClaim = BorshDeserialize::deserialize(&mut &claim_data[8..])?;
|
|
1963
|
-
|
|
1964
|
-
if claim_state.recipient != recipient {
|
|
1965
|
-
return Err(MailerError::InvalidRecipient.into());
|
|
1966
|
-
}
|
|
1967
|
-
if claim_state.amount == 0 {
|
|
1968
|
-
return Err(MailerError::NoClaimableAmount.into());
|
|
1969
|
-
}
|
|
1970
|
-
|
|
1971
|
-
let current_time = Clock::get()?.unix_timestamp;
|
|
1972
|
-
if current_time <= claim_state.timestamp + CLAIM_PERIOD {
|
|
1973
|
-
return Err(MailerError::ClaimPeriodNotExpired.into());
|
|
1974
|
-
}
|
|
1975
|
-
|
|
1976
|
-
let amount = claim_state.amount;
|
|
1977
|
-
claim_state.amount = 0;
|
|
1978
|
-
claim_state.timestamp = 0;
|
|
1979
|
-
claim_state.serialize(&mut &mut claim_data[8..])?;
|
|
1980
|
-
drop(claim_data);
|
|
1981
|
-
|
|
1982
|
-
mailer_state.owner_claimable += amount;
|
|
1983
|
-
mailer_state.serialize(&mut &mut mailer_data[8..])?;
|
|
1984
|
-
|
|
1985
|
-
msg!("Expired shares claimed for {}: {}", recipient, amount);
|
|
1986
|
-
Ok(())
|
|
1987
|
-
}
|
|
1988
|
-
|
|
1989
|
-
/// Emergency unpause without fund distribution (owner only)
|
|
1990
|
-
fn process_emergency_unpause(_program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
|
|
1991
|
-
let account_iter = &mut accounts.iter();
|
|
1992
|
-
let owner = next_account_info(account_iter)?;
|
|
1993
|
-
let mailer_account = next_account_info(account_iter)?;
|
|
1994
|
-
|
|
1995
|
-
if !owner.is_signer {
|
|
1996
|
-
return Err(ProgramError::MissingRequiredSignature);
|
|
1997
|
-
}
|
|
1998
|
-
|
|
1999
|
-
assert_mailer_account(_program_id, mailer_account)?;
|
|
2000
|
-
|
|
2001
|
-
// Load and update mailer state
|
|
2002
|
-
let mut mailer_data = mailer_account.try_borrow_mut_data()?;
|
|
2003
|
-
let mut mailer_state: MailerState = BorshDeserialize::deserialize(&mut &mailer_data[8..])?;
|
|
2004
|
-
|
|
2005
|
-
// Verify owner
|
|
2006
|
-
if mailer_state.owner != *owner.key {
|
|
2007
|
-
return Err(MailerError::OnlyOwner.into());
|
|
2008
|
-
}
|
|
2009
|
-
|
|
2010
|
-
// Check if not paused
|
|
2011
|
-
if !mailer_state.paused {
|
|
2012
|
-
return Err(MailerError::ContractNotPaused.into());
|
|
2013
|
-
}
|
|
2014
|
-
|
|
2015
|
-
// Set unpaused state without fund distribution
|
|
2016
|
-
mailer_state.paused = false;
|
|
2017
|
-
mailer_state.serialize(&mut &mut mailer_data[8..])?;
|
|
2018
|
-
|
|
2019
|
-
msg!(
|
|
2020
|
-
"Contract emergency unpaused by owner: {} - funds can be claimed manually",
|
|
2021
|
-
owner.key
|
|
2022
|
-
);
|
|
2023
|
-
Ok(())
|
|
2024
|
-
}
|
|
2025
|
-
|
|
2026
1171
|
/// Simple hash function for account discriminators
|
|
2027
1172
|
fn hash_discriminator(name: &str) -> u64 {
|
|
2028
1173
|
use std::collections::hash_map::DefaultHasher;
|