Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Program structure, initial operation #1

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
5,891 changes: 413 additions & 5,478 deletions program/Cargo.lock

Large diffs are not rendered by default.

16 changes: 9 additions & 7 deletions program/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@ license = "WTFPL"
publish = false

[dependencies]
solana-program = "1.6.7"
solana-program = "1.6.9"
arrayref = "0.3.6"
uint = "0.8"

# solana-logger = "1.6.7"
borsh = "0.7.1"
borsh-derive = "0.8.1"
thiserror = "1.0.24"
spl-token = {version = "3.1.1", features = ["no-entrypoint"]}

[features]
test-bpf = []

[dev-dependencies]
assert_matches = "1.4.0"
solana-program-test = "1.5.0"
solana-sdk = "1.6.7"
solana-validator = "1.6.7"

[lib]
crate-type = ["cdylib", "lib"]
20 changes: 20 additions & 0 deletions program/src/entrypoint.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use solana_program::{
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, pubkey::Pubkey,
};

use crate::processor::Processor;

entrypoint!(process_instruction);
fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
msg!(
"process_instruction: {}: {:?} accounts, data={:?}",
program_id,
accounts,
instruction_data
);
Processor::process(program_id, accounts, instruction_data)
}
44 changes: 44 additions & 0 deletions program/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use thiserror::Error;

use solana_program::program_error::ProgramError;

#[derive(Error, Debug, Copy, Clone)]
pub enum TroveError {
/// Invalid instruction
#[error("Invalid Instruction")]
InvalidInstruction,
/// Not Rent Exempt
#[error("Not Rent Exempt")]
NotRentExempt,
/// ExpectedAmountMismatch
#[error("ExpectedAmountMismatch")]
ExpectedAmountMismatch,
/// AmountOverflow
#[error("AmountOverflow")]
AmountOverflow,
/// AmountOverflow
#[error("AccountParsingError")]
AccountParsingError,

/// The owner of the input isn't set to the program address generated by the program.
#[error("Input account owner is not the program address")]
InvalidAccountOwner,

/// Account is not a signer
#[error("Input account must be a signer")]
InvalidSigner,

/// The account cannot be initialized because it is already in use.
#[error("Account is already initialized")]
AlreadyInitialized,

/// Math operation overflow
#[error("Math operation overflow")]
MathOverflow,
}

impl From<TroveError> for ProgramError {
fn from(e: TroveError) -> Self {
ProgramError::Custom(e as u32)
}
}
70 changes: 70 additions & 0 deletions program/src/instruction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use solana_program::program_error::ProgramError;
use std::convert::TryInto;

use crate::error::TroveError::InvalidInstruction;

pub enum TroveInstruction {
/// 0
/// Starts the trade by creating and populating a trove account and transferring ownership of the
/// given SOL token account to the PDA

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PDA?

///
///
/// Accounts expected:
///
/// 0. `[signer]` (Borrower) The account of the person initializing the borrowing
/// 1. `[writable]` (Trove SOL Account) Trove SOL token account that should be created prior to this instruction and owned by the initializer
/// 2. `[writable]` (Trove USD Account) The initializer's USD token account for the USD token they will receive should the trade go through

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use ROKS instead of USD token?

/// 3. `[writable]` (Trove Data Account) The associated trove account, it will hold all necessary info about the trade.
/// 4. `[writable]` (Temp Data Account) The associated trove account, it will hold all necessary info about the trade.
/// 5. `[]` The rent sysvar
/// 6. `[]` The token program
ForgetInitTrove {
/// The amount of SOL deposited
amountSol: u64,
/// The amount of USD borrowed
amountUsd: u64,
},

/// 1
/// Initializes a new trove (lending market obligation).
///
/// Accounts expected by this instruction:
///
/// 0. `[writable]` Trove account - uninitialized.
/// 1. `[signer]` Trove owner.
/// 2. `[]` Rent sysvar.
InitTrove,
}

impl TroveInstruction {
/// Unpacks a byte buffer into a [TroveInstruction](enum.TroveInstruction.html).
pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
let (tag, rest) = input.split_first().ok_or(InvalidInstruction)?;

Ok(match tag {
0 => Self::ForgetInitTrove {
amountSol: Self::unpack_amount_sol(rest)?,
amountUsd: Self::unpack_amount_usd(rest)?,
},
1 => Self::InitTrove,
_ => return Err(InvalidInstruction.into()),
})
}

fn unpack_amount_sol(input: &[u8]) -> Result<u64, ProgramError> {
let amount = input
.get(0..8)
.and_then(|slice| slice.try_into().ok())
.map(u64::from_le_bytes)
.ok_or(InvalidInstruction)?;
Ok(amount)
}
fn unpack_amount_usd(input: &[u8]) -> Result<u64, ProgramError> {
let amount = input
.get(8..16)
.and_then(|slice| slice.try_into().ok())
.map(u64::from_le_bytes)
.ok_or(InvalidInstruction)?;
Ok(amount)
}
}
63 changes: 7 additions & 56 deletions program/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,56 +1,7 @@
use solana_program::{
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, pubkey::Pubkey,
};

solana_program::declare_id!("BpfProgram1111111111111111111111111111111111");

entrypoint!(process_instruction);
fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
msg!(
"process_instruction: {}: {} accounts, data={:?}",
program_id,
accounts.len(),
instruction_data
);
Ok(())
}

#[cfg(test)]
mod test {
use {
super::*,
assert_matches::*,
solana_program::instruction::{AccountMeta, Instruction},
solana_program_test::*,
solana_sdk::{signature::Signer, transaction::Transaction},
};

#[tokio::test]
async fn test_transaction() {
let program_id = Pubkey::new_unique();

let (mut banks_client, payer, recent_blockhash) = ProgramTest::new(
"bpf_program_template",
program_id,
processor!(process_instruction),
)
.start()
.await;

let mut transaction = Transaction::new_with_payer(
&[Instruction {
program_id,
accounts: vec![AccountMeta::new(payer.pubkey(), false)],
data: vec![1, 2, 3],
}],
Some(&payer.pubkey()),
);
transaction.sign(&[&payer], recent_blockhash);

assert_matches!(banks_client.process_transaction(transaction).await, Ok(()));
}
}
pub mod entrypoint;
pub mod error;
pub mod instruction;
pub mod math;
pub mod processor;
pub mod state;
pub mod state_utils;
36 changes: 36 additions & 0 deletions program/src/math/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//! Common module for Decimal and Rate

use solana_program::program_error::ProgramError;

/// Scale of precision
pub const SCALE: usize = 18;
/// Identity
pub const WAD: u64 = 1_000_000_000_000_000_000;
/// Half of identity
pub const HALF_WAD: u64 = 500_000_000_000_000_000;
/// Scale for percentages
pub const PERCENT_SCALER: u64 = 10_000_000_000_000_000;

/// Try to subtract, return an error on underflow
pub trait TrySub: Sized {
/// Subtract
fn try_sub(self, rhs: Self) -> Result<Self, ProgramError>;
}

/// Try to subtract, return an error on overflow
pub trait TryAdd: Sized {
/// Add
fn try_add(self, rhs: Self) -> Result<Self, ProgramError>;
}

/// Try to divide, return an error on overflow or divide by zero
pub trait TryDiv<RHS>: Sized {
/// Divide
fn try_div(self, rhs: RHS) -> Result<Self, ProgramError>;
}

/// Try to multiply, return an error on overflow
pub trait TryMul<RHS>: Sized {
/// Multiply
fn try_mul(self, rhs: RHS) -> Result<Self, ProgramError>;
}
Loading