From ba6d746c503f17b263dbfc04b3cfa6546fcc89ab Mon Sep 17 00:00:00 2001 From: Hardhat Chad Date: Sun, 12 May 2024 19:28:43 +0000 Subject: [PATCH] claim rate limiter --- src/consts.rs | 3 +++ src/instruction.rs | 1 + src/processor/claim.rs | 49 ++++++++++++++++++++++++++++++++++----- src/processor/register.rs | 17 ++++++++++---- src/state/proof.rs | 3 +++ 5 files changed, 63 insertions(+), 10 deletions(-) diff --git a/src/consts.rs b/src/consts.rs index 53e55a8..cbe8987 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -21,6 +21,9 @@ pub const ONE_ORE: u64 = 10u64.pow(TOKEN_DECIMALS as u32); /// The duration of one minute, in seconds. pub const ONE_MINUTE: i64 = 60; +/// The duration of one day, in seconds. +pub const ONE_DAY: i64 = 86400; + /// The number of minutes in an Ore epoch. pub const EPOCH_MINUTES: i64 = 5; diff --git a/src/instruction.rs b/src/instruction.rs index 29d7ec4..949e87a 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -295,6 +295,7 @@ pub fn claim(signer: Pubkey, beneficiary: Pubkey, amount: u64) -> Instruction { accounts: vec![ AccountMeta::new(signer, true), AccountMeta::new(beneficiary, false), + AccountMeta::new(MINT_ADDRESS, false), AccountMeta::new(proof, false), AccountMeta::new_readonly(TREASURY_ADDRESS, false), AccountMeta::new(treasury_tokens, false), diff --git a/src/processor/claim.rs b/src/processor/claim.rs index 593bdf1..67456f0 100644 --- a/src/processor/claim.rs +++ b/src/processor/claim.rs @@ -1,11 +1,11 @@ use solana_program::{ - account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, - pubkey::Pubkey, + account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, + program_error::ProgramError, pubkey::Pubkey, sysvar::Sysvar, }; use crate::{ error::OreError, instruction::ClaimArgs, loaders::*, state::Proof, utils::AccountDeserialize, - MINT_ADDRESS, TREASURY, TREASURY_BUMP, + MINT_ADDRESS, ONE_DAY, TREASURY, TREASURY_BUMP, }; /// Claim distributes Ore from the treasury to a miner. Its responsibilies include: @@ -26,13 +26,14 @@ pub fn process_claim<'a, 'info>( let amount = u64::from_le_bytes(args.amount); // Load accounts - let [signer, beneficiary_info, proof_info, treasury_info, treasury_tokens_info, token_program] = + let [signer, beneficiary_info, mint_info, proof_info, treasury_info, treasury_tokens_info, token_program] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; load_signer(signer)?; load_token_account(beneficiary_info, None, &MINT_ADDRESS, true)?; + load_mint(mint_info, MINT_ADDRESS, true)?; load_treasury(treasury_info, false)?; load_token_account( treasury_tokens_info, @@ -42,14 +43,50 @@ pub fn process_claim<'a, 'info>( )?; load_program(token_program, spl_token::id())?; - // Update miner balance + // If last claim was less than 1 day ago, burn some of the claim amount + let mut claim_amount = amount; let mut proof_data = proof_info.data.borrow_mut(); let proof = Proof::try_from_bytes_mut(&mut proof_data)?; + let clock = Clock::get().or(Err(ProgramError::InvalidAccountData))?; + let t = proof.last_claim_at.saturating_add(ONE_DAY); + if clock.unix_timestamp.lt(&t) { + // Calculate burn amount + let burn_amount = amount + .saturating_mul(t.saturating_sub(clock.unix_timestamp) as u64) + .saturating_div(ONE_DAY as u64); + + // Burn tokens from treasury + solana_program::program::invoke_signed( + &spl_token::instruction::burn( + &spl_token::id(), + treasury_tokens_info.key, + mint_info.key, + treasury_info.key, + &[treasury_info.key], + burn_amount, + )?, + &[ + token_program.clone(), + treasury_tokens_info.clone(), + mint_info.clone(), + treasury_info.clone(), + ], + &[&[TREASURY, &[TREASURY_BUMP]]], + )?; + + // Update claim amount + claim_amount = amount.saturating_sub(burn_amount); + } + + // Update miner balance proof.balance = proof .balance .checked_sub(amount) .ok_or(OreError::ClaimTooLarge)?; + // Update timestamp + proof.last_claim_at = clock.unix_timestamp; + // Distribute tokens from treasury to beneficiary solana_program::program::invoke_signed( &spl_token::instruction::transfer( @@ -58,7 +95,7 @@ pub fn process_claim<'a, 'info>( beneficiary_info.key, treasury_info.key, &[treasury_info.key], - amount, + claim_amount, )?, &[ token_program.clone(), diff --git a/src/processor/register.rs b/src/processor/register.rs index cfed8ed..8dbb760 100644 --- a/src/processor/register.rs +++ b/src/processor/register.rs @@ -1,8 +1,15 @@ use std::mem::size_of; use solana_program::{ - account_info::AccountInfo, entrypoint::ProgramResult, keccak::hashv, - program_error::ProgramError, pubkey::Pubkey, slot_hashes::SlotHash, system_program, sysvar, + account_info::AccountInfo, + clock::Clock, + entrypoint::ProgramResult, + keccak::hashv, + program_error::ProgramError, + pubkey::Pubkey, + slot_hashes::SlotHash, + system_program, + sysvar::{self, Sysvar}, }; use crate::{ @@ -54,6 +61,7 @@ pub fn process_register<'a, 'info>( system_program, signer, )?; + let clock = Clock::get().or(Err(ProgramError::InvalidAccountData))?; let mut proof_data = proof_info.data.borrow_mut(); proof_data[0] = Proof::discriminator() as u8; let proof = Proof::try_from_bytes_mut(&mut proof_data)?; @@ -64,8 +72,9 @@ pub fn process_register<'a, 'info>( &slot_hashes_info.data.borrow()[0..size_of::()], ]) .0; - proof.last_hash_at = 0; - proof.last_stake_at = 0; + proof.last_claim_at = clock.unix_timestamp; + proof.last_hash_at = clock.unix_timestamp; + proof.last_stake_at = clock.unix_timestamp; proof.total_hashes = 0; proof.total_rewards = 0; diff --git a/src/state/proof.rs b/src/state/proof.rs index 902fc14..78c819c 100644 --- a/src/state/proof.rs +++ b/src/state/proof.rs @@ -21,6 +21,9 @@ pub struct Proof { /// The current mining challenge. pub challenge: [u8; 32], + /// The last time this account claimed rewards. + pub last_claim_at: i64, + /// The last time this account provided a hash. pub last_hash_at: i64,