tip_payment/
lib.rs

1use anchor_lang::prelude::*;
2
3use crate::TipPaymentError::ArithmeticError;
4
5declare_id!("T1pyyaTNZsKv2WcRAB8oVnk93mLJw2XzjtVYqCsaHqt");
6
7/// We've decided to hardcode the seeds, effectively meaning
8/// the following PDAs owned by this program are singleton.
9/// This ensures that `initialize` can only be invoked once,
10/// otherwise the tx would fail since the accounts would have
11/// already been initialized on subsequent calls.
12pub const CONFIG_ACCOUNT_SEED: &[u8] = b"CONFIG_ACCOUNT";
13pub const TIP_ACCOUNT_SEED_0: &[u8] = b"TIP_ACCOUNT_0";
14pub const TIP_ACCOUNT_SEED_1: &[u8] = b"TIP_ACCOUNT_1";
15pub const TIP_ACCOUNT_SEED_2: &[u8] = b"TIP_ACCOUNT_2";
16pub const TIP_ACCOUNT_SEED_3: &[u8] = b"TIP_ACCOUNT_3";
17pub const TIP_ACCOUNT_SEED_4: &[u8] = b"TIP_ACCOUNT_4";
18pub const TIP_ACCOUNT_SEED_5: &[u8] = b"TIP_ACCOUNT_5";
19pub const TIP_ACCOUNT_SEED_6: &[u8] = b"TIP_ACCOUNT_6";
20pub const TIP_ACCOUNT_SEED_7: &[u8] = b"TIP_ACCOUNT_7";
21
22pub const HEADER: usize = 8;
23
24#[program]
25pub mod tip_payment {
26    use super::*;
27
28    pub fn initialize(ctx: Context<Initialize>, _bumps: InitBumps) -> Result<()> {
29        let cfg = &mut ctx.accounts.config;
30        cfg.tip_receiver = ctx.accounts.payer.key();
31        cfg.block_builder = ctx.accounts.payer.key();
32
33        let bumps = InitBumps {
34            config: *ctx.bumps.get("config").unwrap(),
35            tip_payment_account_0: *ctx.bumps.get("tip_payment_account_0").unwrap(),
36            tip_payment_account_1: *ctx.bumps.get("tip_payment_account_1").unwrap(),
37            tip_payment_account_2: *ctx.bumps.get("tip_payment_account_2").unwrap(),
38            tip_payment_account_3: *ctx.bumps.get("tip_payment_account_3").unwrap(),
39            tip_payment_account_4: *ctx.bumps.get("tip_payment_account_4").unwrap(),
40            tip_payment_account_5: *ctx.bumps.get("tip_payment_account_5").unwrap(),
41            tip_payment_account_6: *ctx.bumps.get("tip_payment_account_6").unwrap(),
42            tip_payment_account_7: *ctx.bumps.get("tip_payment_account_7").unwrap(),
43        };
44        cfg.bumps = bumps;
45        cfg.block_builder_commission_pct = 0;
46
47        Ok(())
48    }
49
50    pub fn claim_tips(ctx: Context<ClaimTips>) -> Result<()> {
51        let total_tips = TipPaymentAccount::drain_accounts(ctx.accounts.get_tip_accounts())?;
52
53        let block_builder_fee = total_tips
54            .checked_mul(ctx.accounts.config.block_builder_commission_pct)
55            .ok_or(ArithmeticError)?
56            .checked_div(100)
57            .ok_or(ArithmeticError)?;
58
59        let tip_receiver_fee = total_tips
60            .checked_sub(block_builder_fee)
61            .ok_or(ArithmeticError)?;
62
63        if tip_receiver_fee > 0 {
64            **ctx.accounts.tip_receiver.try_borrow_mut_lamports()? = ctx
65                .accounts
66                .tip_receiver
67                .lamports()
68                .checked_add(tip_receiver_fee)
69                .ok_or(ArithmeticError)?;
70        }
71
72        if block_builder_fee > 0 {
73            **ctx.accounts.block_builder.try_borrow_mut_lamports()? = ctx
74                .accounts
75                .block_builder
76                .lamports()
77                .checked_add(block_builder_fee)
78                .ok_or(ArithmeticError)?;
79        }
80
81        if block_builder_fee > 0 || tip_receiver_fee > 0 {
82            emit!(TipsClaimed {
83                tip_receiver: ctx.accounts.tip_receiver.key(),
84                tip_receiver_amount: tip_receiver_fee,
85                block_builder: ctx.accounts.block_builder.key(),
86                block_builder_amount: block_builder_fee,
87            });
88        }
89
90        Ok(())
91    }
92
93    /// Validator should invoke this instruction before executing any transactions that contain tips.
94    /// Validator should also ensure it calls it if there's a fork detected.
95    pub fn change_tip_receiver(ctx: Context<ChangeTipReceiver>) -> Result<()> {
96        let total_tips = TipPaymentAccount::drain_accounts(ctx.accounts.get_tip_accounts())?;
97
98        let block_builder_fee = total_tips
99            .checked_mul(ctx.accounts.config.block_builder_commission_pct)
100            .ok_or(ArithmeticError)?
101            .checked_div(100)
102            .ok_or(ArithmeticError)?;
103
104        let tip_receiver_fee = total_tips
105            .checked_sub(block_builder_fee)
106            .ok_or(ArithmeticError)?;
107
108        if tip_receiver_fee > 0 {
109            **ctx.accounts.old_tip_receiver.try_borrow_mut_lamports()? = ctx
110                .accounts
111                .old_tip_receiver
112                .lamports()
113                .checked_add(tip_receiver_fee)
114                .ok_or(ArithmeticError)?;
115        }
116
117        if block_builder_fee > 0 {
118            **ctx.accounts.block_builder.try_borrow_mut_lamports()? = ctx
119                .accounts
120                .block_builder
121                .lamports()
122                .checked_add(block_builder_fee)
123                .ok_or(ArithmeticError)?;
124        }
125
126        if block_builder_fee > 0 || tip_receiver_fee > 0 {
127            emit!(TipsClaimed {
128                tip_receiver: ctx.accounts.old_tip_receiver.key(),
129                tip_receiver_amount: tip_receiver_fee,
130                block_builder: ctx.accounts.block_builder.key(),
131                block_builder_amount: block_builder_fee,
132            });
133        }
134
135        // set new funding account
136        ctx.accounts.config.tip_receiver = ctx.accounts.new_tip_receiver.key();
137        Ok(())
138    }
139
140    /// Changes the block builder. The block builder takes a cut on tips transferred out by
141    /// this program. In order for the block builder to be changed, all previous tips must have been
142    /// drained.
143    pub fn change_block_builder(
144        ctx: Context<ChangeBlockBuilder>,
145        block_builder_commission: u64,
146    ) -> Result<()> {
147        require_gte!(100, block_builder_commission, TipPaymentError::InvalidFee);
148
149        let total_tips = TipPaymentAccount::drain_accounts(ctx.accounts.get_tip_accounts())?;
150
151        let block_builder_fee = total_tips
152            .checked_mul(ctx.accounts.config.block_builder_commission_pct)
153            .ok_or(ArithmeticError)?
154            .checked_div(100)
155            .ok_or(ArithmeticError)?;
156
157        let tip_receiver_fee = total_tips
158            .checked_sub(block_builder_fee)
159            .ok_or(ArithmeticError)?;
160
161        if tip_receiver_fee > 0 {
162            **ctx.accounts.tip_receiver.try_borrow_mut_lamports()? = ctx
163                .accounts
164                .tip_receiver
165                .lamports()
166                .checked_add(tip_receiver_fee)
167                .ok_or(ArithmeticError)?;
168        }
169
170        if block_builder_fee > 0 {
171            **ctx.accounts.old_block_builder.try_borrow_mut_lamports()? = ctx
172                .accounts
173                .old_block_builder
174                .lamports()
175                .checked_add(block_builder_fee)
176                .ok_or(ArithmeticError)?;
177        }
178
179        if block_builder_fee > 0 || tip_receiver_fee > 0 {
180            emit!(TipsClaimed {
181                tip_receiver: ctx.accounts.tip_receiver.key(),
182                tip_receiver_amount: tip_receiver_fee,
183                block_builder: ctx.accounts.old_block_builder.key(),
184                block_builder_amount: block_builder_fee,
185            });
186        }
187
188        // set new funding account
189        ctx.accounts.config.block_builder = ctx.accounts.new_block_builder.key();
190        ctx.accounts.config.block_builder_commission_pct = block_builder_commission;
191        Ok(())
192    }
193}
194
195#[error_code]
196pub enum TipPaymentError {
197    ArithmeticError,
198    InvalidFee,
199}
200
201/// Bumps used during initialization
202#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)]
203pub struct InitBumps {
204    pub config: u8,
205    pub tip_payment_account_0: u8,
206    pub tip_payment_account_1: u8,
207    pub tip_payment_account_2: u8,
208    pub tip_payment_account_3: u8,
209    pub tip_payment_account_4: u8,
210    pub tip_payment_account_5: u8,
211    pub tip_payment_account_6: u8,
212    pub tip_payment_account_7: u8,
213}
214
215impl InitBumps {
216    const SIZE: usize = 9;
217}
218
219#[derive(Accounts)]
220#[instruction(bumps: InitBumps)]
221pub struct Initialize<'info> {
222    /// singleton account
223    #[account(
224        init,
225        seeds = [CONFIG_ACCOUNT_SEED],
226        bump,
227        payer = payer,
228        space = Config::SIZE,
229        rent_exempt = enforce
230    )]
231    pub config: Account<'info, Config>,
232    #[account(
233        init,
234        seeds = [TIP_ACCOUNT_SEED_0],
235        bump,
236        payer = payer,
237        space = TipPaymentAccount::SIZE,
238        rent_exempt = enforce
239    )]
240    pub tip_payment_account_0: Account<'info, TipPaymentAccount>,
241    #[account(
242        init,
243        seeds = [TIP_ACCOUNT_SEED_1],
244        bump,
245        payer = payer,
246        space = TipPaymentAccount::SIZE,
247        rent_exempt = enforce
248    )]
249    pub tip_payment_account_1: Account<'info, TipPaymentAccount>,
250    #[account(
251        init,
252        seeds = [TIP_ACCOUNT_SEED_2],
253        bump,
254        payer = payer,
255        space = TipPaymentAccount::SIZE,
256        rent_exempt = enforce
257    )]
258    pub tip_payment_account_2: Account<'info, TipPaymentAccount>,
259    #[account(
260        init,
261        seeds = [TIP_ACCOUNT_SEED_3],
262        bump,
263        payer = payer,
264        space = TipPaymentAccount::SIZE,
265        rent_exempt = enforce
266    )]
267    pub tip_payment_account_3: Account<'info, TipPaymentAccount>,
268    #[account(
269        init,
270        seeds = [TIP_ACCOUNT_SEED_4],
271        bump,
272        payer = payer,
273        space = TipPaymentAccount::SIZE,
274        rent_exempt = enforce
275    )]
276    pub tip_payment_account_4: Account<'info, TipPaymentAccount>,
277    #[account(
278        init,
279        seeds = [TIP_ACCOUNT_SEED_5],
280        bump,
281        payer = payer,
282        space = TipPaymentAccount::SIZE,
283        rent_exempt = enforce
284    )]
285    pub tip_payment_account_5: Account<'info, TipPaymentAccount>,
286    #[account(
287        init,
288        seeds = [TIP_ACCOUNT_SEED_6],
289        bump,
290        payer = payer,
291        space = TipPaymentAccount::SIZE,
292        rent_exempt = enforce
293    )]
294    pub tip_payment_account_6: Account<'info, TipPaymentAccount>,
295    #[account(
296        init,
297        seeds = [TIP_ACCOUNT_SEED_7],
298        bump,
299        payer = payer,
300        space = TipPaymentAccount::SIZE,
301        rent_exempt = enforce
302    )]
303    pub tip_payment_account_7: Account<'info, TipPaymentAccount>,
304
305    pub system_program: Program<'info, System>,
306    #[account(mut)]
307    pub payer: Signer<'info>,
308}
309
310#[derive(Accounts)]
311pub struct ClaimTips<'info> {
312    #[account(
313        mut,
314        seeds = [CONFIG_ACCOUNT_SEED],
315        bump = config.bumps.config,
316        rent_exempt = enforce
317    )]
318    pub config: Account<'info, Config>,
319    #[account(
320        mut,
321        seeds = [TIP_ACCOUNT_SEED_0],
322        bump = config.bumps.tip_payment_account_0,
323        rent_exempt = enforce
324    )]
325    pub tip_payment_account_0: Account<'info, TipPaymentAccount>,
326    #[account(
327        mut,
328        seeds = [TIP_ACCOUNT_SEED_1],
329        bump = config.bumps.tip_payment_account_1,
330        rent_exempt = enforce
331    )]
332    pub tip_payment_account_1: Account<'info, TipPaymentAccount>,
333    #[account(
334        mut,
335        seeds = [TIP_ACCOUNT_SEED_2],
336        bump = config.bumps.tip_payment_account_2,
337        rent_exempt = enforce
338    )]
339    pub tip_payment_account_2: Account<'info, TipPaymentAccount>,
340    #[account(
341        mut,
342        seeds = [TIP_ACCOUNT_SEED_3],
343        bump = config.bumps.tip_payment_account_3,
344        rent_exempt = enforce
345    )]
346    pub tip_payment_account_3: Account<'info, TipPaymentAccount>,
347    #[account(
348        mut,
349        seeds = [TIP_ACCOUNT_SEED_4],
350        bump = config.bumps.tip_payment_account_4,
351        rent_exempt = enforce
352    )]
353    pub tip_payment_account_4: Account<'info, TipPaymentAccount>,
354    #[account(
355        mut,
356        seeds = [TIP_ACCOUNT_SEED_5],
357        bump = config.bumps.tip_payment_account_5,
358        rent_exempt = enforce
359    )]
360    pub tip_payment_account_5: Account<'info, TipPaymentAccount>,
361    #[account(
362        mut,
363        seeds = [TIP_ACCOUNT_SEED_6],
364        bump = config.bumps.tip_payment_account_6,
365        rent_exempt = enforce
366    )]
367    pub tip_payment_account_6: Account<'info, TipPaymentAccount>,
368    #[account(
369        mut,
370        seeds = [TIP_ACCOUNT_SEED_7],
371        bump = config.bumps.tip_payment_account_7,
372        rent_exempt = enforce
373    )]
374    pub tip_payment_account_7: Account<'info, TipPaymentAccount>,
375
376    /// CHECK: this is the account that is configured to receive tips, which is constantly rotating and
377    /// can be an account with a private key to a PDA owned by some other program.
378    #[account(
379        mut,
380        constraint = config.tip_receiver == tip_receiver.key(),
381    )]
382    pub tip_receiver: AccountInfo<'info>,
383
384    /// CHECK: only the current block builder can get tips
385    #[account(
386        mut,
387        constraint = config.block_builder == block_builder.key(),
388    )]
389    pub block_builder: AccountInfo<'info>,
390
391    #[account(mut)]
392    pub signer: Signer<'info>,
393}
394
395impl<'info> ClaimTips<'info> {
396    fn get_tip_accounts(&self) -> Vec<AccountInfo<'info>> {
397        vec![
398            self.tip_payment_account_0.to_account_info(),
399            self.tip_payment_account_1.to_account_info(),
400            self.tip_payment_account_2.to_account_info(),
401            self.tip_payment_account_3.to_account_info(),
402            self.tip_payment_account_4.to_account_info(),
403            self.tip_payment_account_5.to_account_info(),
404            self.tip_payment_account_6.to_account_info(),
405            self.tip_payment_account_7.to_account_info(),
406        ]
407    }
408}
409
410#[derive(Accounts)]
411pub struct ChangeTipReceiver<'info> {
412    #[account(mut)]
413    pub config: Account<'info, Config>,
414
415    /// CHECK: old_tip_receiver receives the funds in the TipPaymentAccount accounts, so
416    /// ensure its the one that's expected
417    #[account(mut, constraint = old_tip_receiver.key() == config.tip_receiver)]
418    pub old_tip_receiver: AccountInfo<'info>,
419
420    /// CHECK: any new, writable account is allowed as a tip receiver.
421    #[account(mut)]
422    pub new_tip_receiver: AccountInfo<'info>,
423
424    /// CHECK: old_block_builder receives a % of funds in the TipPaymentAccount accounts, so
425    /// ensure it's the account that's expected
426    #[account(mut, constraint = block_builder.key() == config.block_builder)]
427    pub block_builder: AccountInfo<'info>,
428
429    #[account(
430        mut,
431        seeds = [TIP_ACCOUNT_SEED_0],
432        bump = config.bumps.tip_payment_account_0,
433        rent_exempt = enforce
434    )]
435    pub tip_payment_account_0: Account<'info, TipPaymentAccount>,
436
437    #[account(
438        mut,
439        seeds = [TIP_ACCOUNT_SEED_1],
440        bump = config.bumps.tip_payment_account_1,
441        rent_exempt = enforce
442    )]
443    pub tip_payment_account_1: Account<'info, TipPaymentAccount>,
444
445    #[account(
446        mut,
447        seeds = [TIP_ACCOUNT_SEED_2],
448        bump = config.bumps.tip_payment_account_2,
449        rent_exempt = enforce
450    )]
451    pub tip_payment_account_2: Account<'info, TipPaymentAccount>,
452
453    #[account(
454        mut,
455        seeds = [TIP_ACCOUNT_SEED_3],
456        bump = config.bumps.tip_payment_account_3,
457        rent_exempt = enforce
458    )]
459    pub tip_payment_account_3: Account<'info, TipPaymentAccount>,
460
461    #[account(
462        mut,
463        seeds = [TIP_ACCOUNT_SEED_4],
464        bump = config.bumps.tip_payment_account_4,
465        rent_exempt = enforce
466    )]
467    pub tip_payment_account_4: Account<'info, TipPaymentAccount>,
468
469    #[account(
470        mut,
471        seeds = [TIP_ACCOUNT_SEED_5],
472        bump = config.bumps.tip_payment_account_5,
473        rent_exempt = enforce
474    )]
475    pub tip_payment_account_5: Account<'info, TipPaymentAccount>,
476
477    #[account(
478        mut,
479        seeds = [TIP_ACCOUNT_SEED_6],
480        bump = config.bumps.tip_payment_account_6,
481        rent_exempt = enforce
482    )]
483    pub tip_payment_account_6: Account<'info, TipPaymentAccount>,
484
485    #[account(
486        mut,
487        seeds = [TIP_ACCOUNT_SEED_7],
488        bump = config.bumps.tip_payment_account_7,
489        rent_exempt = enforce
490    )]
491    pub tip_payment_account_7: Account<'info, TipPaymentAccount>,
492
493    #[account(mut)]
494    pub signer: Signer<'info>,
495}
496
497impl<'info> ChangeTipReceiver<'info> {
498    fn get_tip_accounts(&self) -> Vec<AccountInfo<'info>> {
499        vec![
500            self.tip_payment_account_0.to_account_info(),
501            self.tip_payment_account_1.to_account_info(),
502            self.tip_payment_account_2.to_account_info(),
503            self.tip_payment_account_3.to_account_info(),
504            self.tip_payment_account_4.to_account_info(),
505            self.tip_payment_account_5.to_account_info(),
506            self.tip_payment_account_6.to_account_info(),
507            self.tip_payment_account_7.to_account_info(),
508        ]
509    }
510}
511
512#[derive(Accounts)]
513pub struct ChangeBlockBuilder<'info> {
514    #[account(mut)]
515    pub config: Account<'info, Config>,
516
517    /// CHECK: old_tip_receiver receives the funds in the TipPaymentAccount accounts, so
518    /// ensure its the one that's expected
519    #[account(mut, constraint = tip_receiver.key() == config.tip_receiver)]
520    pub tip_receiver: AccountInfo<'info>,
521
522    /// CHECK: old_block_builder receives a % of funds in the TipPaymentAccount accounts, so
523    /// ensure it's the account that's expected
524    #[account(mut, constraint = old_block_builder.key() == config.block_builder)]
525    pub old_block_builder: AccountInfo<'info>,
526
527    /// CHECK: any new, writable account is allowed as block builder
528    #[account(mut)]
529    pub new_block_builder: AccountInfo<'info>,
530
531    #[account(
532        mut,
533        seeds = [TIP_ACCOUNT_SEED_0],
534        bump = config.bumps.tip_payment_account_0,
535        rent_exempt = enforce
536    )]
537    pub tip_payment_account_0: Account<'info, TipPaymentAccount>,
538
539    #[account(
540        mut,
541        seeds = [TIP_ACCOUNT_SEED_1],
542        bump = config.bumps.tip_payment_account_1,
543        rent_exempt = enforce
544    )]
545    pub tip_payment_account_1: Account<'info, TipPaymentAccount>,
546
547    #[account(
548        mut,
549        seeds = [TIP_ACCOUNT_SEED_2],
550        bump = config.bumps.tip_payment_account_2,
551        rent_exempt = enforce
552    )]
553    pub tip_payment_account_2: Account<'info, TipPaymentAccount>,
554
555    #[account(
556        mut,
557        seeds = [TIP_ACCOUNT_SEED_3],
558        bump = config.bumps.tip_payment_account_3,
559        rent_exempt = enforce
560    )]
561    pub tip_payment_account_3: Account<'info, TipPaymentAccount>,
562
563    #[account(
564        mut,
565        seeds = [TIP_ACCOUNT_SEED_4],
566        bump = config.bumps.tip_payment_account_4,
567        rent_exempt = enforce
568    )]
569    pub tip_payment_account_4: Account<'info, TipPaymentAccount>,
570
571    #[account(
572        mut,
573        seeds = [TIP_ACCOUNT_SEED_5],
574        bump = config.bumps.tip_payment_account_5,
575        rent_exempt = enforce
576    )]
577    pub tip_payment_account_5: Account<'info, TipPaymentAccount>,
578
579    #[account(
580        mut,
581        seeds = [TIP_ACCOUNT_SEED_6],
582        bump = config.bumps.tip_payment_account_6,
583        rent_exempt = enforce
584    )]
585    pub tip_payment_account_6: Account<'info, TipPaymentAccount>,
586
587    #[account(
588        mut,
589        seeds = [TIP_ACCOUNT_SEED_7],
590        bump = config.bumps.tip_payment_account_7,
591        rent_exempt = enforce
592    )]
593    pub tip_payment_account_7: Account<'info, TipPaymentAccount>,
594
595    #[account(mut)]
596    pub signer: Signer<'info>,
597}
598
599impl<'info> ChangeBlockBuilder<'info> {
600    fn get_tip_accounts(&self) -> Vec<AccountInfo<'info>> {
601        vec![
602            self.tip_payment_account_0.to_account_info(),
603            self.tip_payment_account_1.to_account_info(),
604            self.tip_payment_account_2.to_account_info(),
605            self.tip_payment_account_3.to_account_info(),
606            self.tip_payment_account_4.to_account_info(),
607            self.tip_payment_account_5.to_account_info(),
608            self.tip_payment_account_6.to_account_info(),
609            self.tip_payment_account_7.to_account_info(),
610        ]
611    }
612}
613
614/// Stores program config metadata.
615#[account]
616#[derive(Default)]
617pub struct Config {
618    /// The account claiming tips from the mev_payment accounts.
619    pub tip_receiver: Pubkey,
620
621    /// Block builder that receives a % of fees
622    pub block_builder: Pubkey,
623    pub block_builder_commission_pct: u64,
624
625    /// Bumps used to derive PDAs
626    pub bumps: InitBumps,
627}
628
629impl Config {
630    // header, fields, and InitBumps
631    pub const SIZE: usize = 8 + 32 + 32 + 8 + InitBumps::SIZE;
632}
633
634/// Account that searchers will need to tip for their bundles to be accepted.
635/// There will be 8 accounts of this type initialized in order to parallelize bundles.
636#[account]
637#[derive(Default)]
638pub struct TipPaymentAccount {}
639
640impl TipPaymentAccount {
641    pub const SIZE: usize = 8;
642
643    fn drain_accounts(accs: Vec<AccountInfo>) -> Result<u64> {
644        let mut total_tips: u64 = 0;
645        for acc in accs {
646            total_tips = total_tips
647                .checked_add(Self::drain_account(&acc)?)
648                .ok_or(ArithmeticError)?;
649        }
650
651        Ok(total_tips)
652    }
653
654    fn drain_account(acc: &AccountInfo) -> Result<u64> {
655        let tips = Self::calc_tips(acc.lamports())?;
656        if tips > 0 {
657            let pre_lamports = acc.lamports();
658            **acc.try_borrow_mut_lamports()? =
659                pre_lamports.checked_sub(tips).ok_or(ArithmeticError)?;
660        }
661        Ok(tips)
662    }
663
664    fn calc_tips(total_balance: u64) -> Result<u64> {
665        let rent = Rent::get()?;
666        let min_rent = rent.minimum_balance(Self::SIZE);
667
668        Ok(total_balance.checked_sub(min_rent).ok_or(ArithmeticError)?)
669    }
670}
671
672/// events
673#[event]
674pub struct TipsClaimed {
675    tip_receiver: Pubkey,
676    tip_receiver_amount: u64,
677    block_builder: Pubkey,
678    block_builder_amount: u64,
679}