Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions api/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,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 a program epoch.
pub const EPOCH_MINUTES: i64 = 15;

Expand Down Expand Up @@ -67,6 +70,9 @@ pub const PROOF: &[u8] = b"proof";
/// The seed of the treasury account PDA.
pub const TREASURY: &[u8] = b"treasury";

/// The seed of the vesting account PDA.
pub const VESTING: &[u8] = b"vesting";

/// Noise for deriving the mint pda
pub const MINT_NOISE: [u8; 16] = [
89, 157, 88, 232, 243, 249, 197, 132, 199, 49, 19, 234, 91, 94, 150, 41,
Expand Down
4 changes: 3 additions & 1 deletion api/src/sdk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use steel::*;
use crate::{
consts::*,
instruction::*,
state::{bus_pda, config_pda, proof_pda, treasury_pda},
state::{bus_pda, config_pda, proof_pda, treasury_pda, vesting_pda},
};

/// Builds an auth instruction.
Expand All @@ -19,6 +19,7 @@ pub fn auth(proof: Pubkey) -> Instruction {
/// Builds a claim instruction.
pub fn claim(signer: Pubkey, beneficiary: Pubkey, amount: u64) -> Instruction {
let proof = proof_pda(signer).0;
let vesting = vesting_pda(proof).0;
Instruction {
program_id: crate::ID,
accounts: vec![
Expand All @@ -27,6 +28,7 @@ pub fn claim(signer: Pubkey, beneficiary: Pubkey, amount: u64) -> Instruction {
AccountMeta::new(proof, false),
AccountMeta::new_readonly(TREASURY_ADDRESS, false),
AccountMeta::new(TREASURY_TOKENS_ADDRESS, false),
AccountMeta::new(vesting, false),
AccountMeta::new_readonly(spl_token::ID, false),
],
data: Claim {
Expand Down
8 changes: 8 additions & 0 deletions api/src/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ mod bus;
mod config;
mod proof;
mod treasury;
mod vesting;

pub use bus::*;
pub use config::*;
pub use proof::*;
pub use treasury::*;
pub use vesting::*;

use steel::*;

Expand All @@ -19,6 +21,7 @@ pub enum OreAccount {
Config = 101,
Proof = 102,
Treasury = 103,
Vesting = 104,
}

/// Fetch the PDA of a bus account.
Expand All @@ -40,3 +43,8 @@ pub fn proof_pda(authority: Pubkey) -> (Pubkey, u8) {
pub fn treasury_pda() -> (Pubkey, u8) {
Pubkey::find_program_address(&[TREASURY], &crate::id())
}

/// Derive the PDA of a vesting account.
pub fn vesting_pda(proof: Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(&[VESTING, proof.as_ref()], &crate::id())
}
23 changes: 23 additions & 0 deletions api/src/state/vesting.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use steel::*;

use super::OreAccount;

/// Vesting accounts track a miner's current vesting schedule.
/// Miners are allowed to claim 1% of their earnings every 24 hours.
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
pub struct Vesting {
/// The proof assocaited with this vesting schedule.
pub proof: Pubkey,

/// The amount of tokens that have been claimed in the current window.
pub window_claim_amount: u64,

/// The high water mark of the proof balance.
pub window_proof_balance: u64,

/// The start of the vesting window.
pub window_start_at: i64,
}

account!(OreAccount, Vesting);
61 changes: 58 additions & 3 deletions program/src/claim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub fn process_claim(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult

// Load accounts.
let clock = Clock::get()?;
let [signer_info, beneficiary_info, proof_info, treasury_info, treasury_tokens_info, token_program] =
let [signer_info, beneficiary_info, proof_info, treasury_info, treasury_tokens_info, vesting_info, token_program, system_program] =
accounts
else {
return Err(ProgramError::NotEnoughAccountKeys);
Expand All @@ -27,12 +27,67 @@ pub fn process_claim(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult
)?;
treasury_info.is_treasury()?;
treasury_tokens_info.is_writable()?.is_treasury_tokens()?;
system_program.is_program(&system_program::ID)?;
token_program.is_program(&spl_token::ID)?;

// Create vesting account if it doesn't exist.
let vesting = if vesting_info.data_is_empty() {
// Verify seeds
vesting_info
.is_empty()?
.is_writable()?
.has_seeds(&[VESTING, proof_info.key.as_ref()], &ore_api::ID)?;

// Initialize vesting.
create_program_account::<Vesting>(
vesting_info,
system_program,
signer_info,
&ore_api::ID,
&[VESTING, proof_info.key.as_ref()],
)?;
let vesting = vesting_info
.as_account_mut::<Vesting>(&ore_api::ID)?
.assert_mut(|v| v.proof == *proof_info.key)?;
vesting.proof = *proof_info.key;
vesting.window_claim_amount = 0;
vesting.window_proof_balance = proof.balance;
vesting.window_start_at = clock.unix_timestamp;
vesting
} else {
// Load vesting account.
vesting_info
.as_account_mut::<Vesting>(&ore_api::ID)?
.assert_mut(|v| v.proof == *proof_info.key)?
};

// Update vesting window.
if clock.unix_timestamp > vesting.window_start_at + ONE_DAY {
vesting.window_claim_amount = 0;
vesting.window_start_at = clock.unix_timestamp;
};

// Update the high water mark.
vesting.window_proof_balance = vesting.window_proof_balance.max(proof.balance);

// Calculate claim amount.
let max_claim_amount = vesting.window_proof_balance.checked_div(100).unwrap();
let remaining_claim_amount = max_claim_amount
.checked_sub(vesting.window_claim_amount)
.unwrap();

// Exempt boost proof from vesting.
let boost_config_address = ore_boost_api::state::config_pda().0;
let claim_amount = if proof.authority == boost_config_address {
amount.min(proof.balance)
} else {
amount.min(remaining_claim_amount).min(proof.balance)
};

// Update miner balance.
proof.balance = proof
.balance
.checked_sub(amount)
.checked_sub(claim_amount)
.ok_or(OreError::ClaimTooLarge)?;

// Update last claim timestamp.
Expand All @@ -44,7 +99,7 @@ pub fn process_claim(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult
treasury_tokens_info,
beneficiary_info,
token_program,
amount,
claim_amount,
&[TREASURY],
)?;

Expand Down