Skip to content
Open
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@dappio-wonderland/adapter-idls",
"version": "0.2.1",
"version": "0.2.1-test.2",
"description": "Dappio Adapter IDLs: The IDL files of Dappio Adapters",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
Expand Down
22 changes: 22 additions & 0 deletions programs/adapter-genopets-staking/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "adapter-genopets-staking"
version = "0.1.0"
description = "Created with Anchor"
edition = "2021"

[lib]
crate-type = ["cdylib", "lib"]
name = "adapter_genopets_staking"

[features]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]

[profile.release]
overflow-checks = true

[dependencies]
anchor-lang = {version = "0.24.2", features = [ "init-if-needed" ] }
anchor-spl = "0.24.2"
2 changes: 2 additions & 0 deletions programs/adapter-genopets-staking/Xargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []
311 changes: 311 additions & 0 deletions programs/adapter-genopets-staking/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,311 @@
use anchor_lang::prelude::*;
use anchor_lang::solana_program::{
hash::hash,
instruction::{AccountMeta, Instruction},
program::invoke,
pubkey::Pubkey,
};
use anchor_spl::token::TokenAccount;
declare_id!("ADPTR3wPKDCZ8HNBBpY3GGXB8hu6DZDqyPJMimyHjKNk");

#[program]
pub mod adapter_genopets_staking {
use super::*;

pub fn stake<'a, 'b, 'c, 'info>(
ctx: Context<'a, 'b, 'c, 'info, Action<'info>>,
input: Vec<u8>,
) -> Result<()> {
// Get Input
let mut input_bytes = &input[..];
let input_struct = StakeInputWrapper::deserialize(&mut input_bytes)?;

let mut stake_data = sighash("global", "stake").to_vec();
stake_data.append(&mut input_struct.amount.to_le_bytes().to_vec());
stake_data.append(&mut input_struct.lock_for_months.to_le_bytes().to_vec());
stake_data.push(0); // default False cuz it's deprecated

let stake_accounts = load_remaining_accounts(
ctx.remaining_accounts,
vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
);

let mut stake_token_account_and_balance =
load_token_account_and_balance(ctx.remaining_accounts, 5);

let stake_ix = Instruction {
program_id: ctx.accounts.base_program_id.key(),
accounts: stake_accounts,
data: stake_data,
};
invoke(&stake_ix, ctx.remaining_accounts)?;

// Wrap Output
let output_struct = StakeOutputWrapper {
token_in_amount: stake_token_account_and_balance.get_balance_change(),
..Default::default()
};
let mut output: Vec<u8> = Vec::new();
output_struct.serialize(&mut output).unwrap();

// Return Result
anchor_lang::solana_program::program::set_return_data(&output);

msg!("Output: {:?}", output_struct);
Ok(())
}
pub fn unstake<'a, 'b, 'c, 'info>(
ctx: Context<'a, 'b, 'c, 'info, Action<'info>>,
input: Vec<u8>,
) -> Result<()> {
// Get Input
let mut input_bytes = &input[..];
let input_struct = UnstakeInputWrapper::deserialize(&mut input_bytes)?;

let mut unstake_data = sighash("global", "withdraw").to_vec(); // Instruction data
unstake_data.push(0); // default False cuz it's deprecated
let unstake_accout_index_array: Vec<usize> =
vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]; // Remaining accounts

let unstake_ix_accounts =
load_remaining_accounts(ctx.remaining_accounts, unstake_accout_index_array);

let mut unstake_token_account_and_balance =
load_token_account_and_balance(ctx.remaining_accounts, 5);

let unstake_ix = Instruction {
program_id: ctx.accounts.base_program_id.key(),
accounts: unstake_ix_accounts,
data: unstake_data,
};
invoke(&unstake_ix, ctx.remaining_accounts)?;

// Wrap Output
let output_struct = UnstakeOutputWrapper {
token_out_amount: unstake_token_account_and_balance.get_balance_change(),
..Default::default()
};
let mut output: Vec<u8> = Vec::new();
output_struct.serialize(&mut output).unwrap();

// Return Result
anchor_lang::solana_program::program::set_return_data(&output);

msg!("Output: {:?}", output_struct);

Ok(())
}
pub fn harvest<'a, 'b, 'c, 'info>(
ctx: Context<'a, 'b, 'c, 'info, Action<'info>>,
input: Vec<u8>,
) -> Result<()> {
// Get Input
let mut input_bytes = &input[..];
let input_struct = HarvestInputWrapper::deserialize(&mut input_bytes)?;

let mut harvest_data = vec![]; // Instruction data
let mut harvest_accout_index_array: Vec<usize> = vec![]; // Remaining accounts
let mut harvest_token_account_index: usize = 0;
match input_struct.harvest_type {
// the type index is to dispatch different type of harvest
// 0 is for initialize
0 => {
harvest_data = sighash("global", "claim_rewards").to_vec();
harvest_data.push(0); // default False cuz it's deprecated
harvest_accout_index_array = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
harvest_token_account_index = 5;
}
// 1 is for completeAsGene
1 => {
harvest_data = sighash("global", "withdraw").to_vec();
harvest_data.push(0); // default False cuz it's deprecated
harvest_accout_index_array =
vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17];
harvest_token_account_index = 5;
}
// 2 is for completeAsSGene
2 => {
harvest_data = sighash("global", "withdraw_as_sgene").to_vec();
harvest_accout_index_array = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
harvest_token_account_index = 4;
}

_ => return Err(ErrorCode::UnsupportedAction.into()),
}

let harvest_ix_accounts =
load_remaining_accounts(ctx.remaining_accounts, harvest_accout_index_array);

let mut harvest_token_account_and_balance =
load_token_account_and_balance(ctx.remaining_accounts, harvest_token_account_index);

let harvest_ix = Instruction {
program_id: ctx.accounts.base_program_id.key(),
accounts: harvest_ix_accounts,
data: harvest_data,
};
invoke(&harvest_ix, ctx.remaining_accounts)?;

// Wrap Output
let output_struct = HarvestOutputWrapper {
reward_amount: harvest_token_account_and_balance.get_balance_change(),
..Default::default()
};
let mut output: Vec<u8> = Vec::new();
output_struct.serialize(&mut output).unwrap();

// Return Result
anchor_lang::solana_program::program::set_return_data(&output);

msg!("Output: {:?}", output_struct);
Ok(())
}
}
#[derive(Accounts)]
pub struct Action<'info> {
pub gateway_authority: Signer<'info>,
/// CHECK: Safe
pub base_program_id: AccountInfo<'info>,
}
#[error_code]
pub enum ErrorCode {
#[msg("Unsupported PoolDirection")]
UnsupportedPoolDirection,
#[msg("Unsupported Action")]
UnsupportedAction,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Default)]
pub struct StakeInputWrapper {
pub amount: u64,
pub lock_for_months: u8,
}

#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Default)]
pub struct StakeOutputWrapper {
pub token_in_amount: u64,
pub dummy_2: u64,
pub dummy_3: u64,
pub dummy_4: u64,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Default)]
pub struct UnstakeInputWrapper {}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Default)]
pub struct UnstakeOutputWrapper {
pub token_out_amount: u64,
pub dummy_2: u64,
pub dummy_3: u64,
pub dummy_4: u64,
}

#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Default)]
pub struct HarvestInputWrapper {
pub harvest_type: u8,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Default)]
pub struct HarvestOutputWrapper {
pub reward_amount: u64,
pub dummy_2: u64,
pub dummy_3: u64,
pub dummy_4: u64,
}

pub type StakeOutputTuple = (u64, u64, u64, u64);
pub type UnstakeOutputTuple = (u64, u64, u64, u64);
pub type HarvestOutputTuple = (u64, u64, u64, u64);

impl From<StakeOutputWrapper> for StakeOutputTuple {
fn from(result: StakeOutputWrapper) -> StakeOutputTuple {
let StakeOutputWrapper {
token_in_amount,
dummy_2,
dummy_3,
dummy_4,
} = result;
(token_in_amount, dummy_2, dummy_3, dummy_4)
}
}

impl From<UnstakeOutputWrapper> for UnstakeOutputTuple {
fn from(result: UnstakeOutputWrapper) -> UnstakeOutputTuple {
let UnstakeOutputWrapper {
token_out_amount,
dummy_2,
dummy_3,
dummy_4,
} = result;
(token_out_amount, dummy_2, dummy_3, dummy_4)
}
}
impl From<HarvestOutputWrapper> for HarvestOutputTuple {
fn from(result: HarvestOutputWrapper) -> HarvestOutputTuple {
let HarvestOutputWrapper {
reward_amount,
dummy_2,
dummy_3,
dummy_4,
} = result;
(reward_amount, dummy_2, dummy_3, dummy_4)
}
}

pub fn sighash(namespace: &str, name: &str) -> [u8; 8] {
let preimage = format!("{}:{}", namespace, name);
let mut sighash = [0u8; 8];

sighash.copy_from_slice(&hash(preimage.as_bytes()).to_bytes()[..8]);
sighash
}

pub fn load_token_account_and_balance<'info>(
remaining_accounts: &[AccountInfo<'info>],
account_index: usize,
) -> TokenAccountAndBalance<'info> {
let token_account_info = &remaining_accounts[account_index];
let token_account = Account::<TokenAccount>::try_from(token_account_info).unwrap();
let balance_before = token_account.amount.clone();
return TokenAccountAndBalance {
token_accout: token_account,
balance_before: balance_before,
};
}

pub struct TokenAccountAndBalance<'info> {
token_accout: Account<'info, TokenAccount>,
balance_before: u64,
}

impl<'info> TokenAccountAndBalance<'info> {
pub fn get_balance_change(&mut self) -> u64 {
self.token_accout.reload().unwrap();
let balance_before = self.balance_before;
let balance_after = self.token_accout.amount;
if balance_after > balance_before {
balance_after.checked_sub(balance_before).unwrap()
} else if balance_after == balance_before {
0_u64
} else {
balance_before.checked_sub(balance_after).unwrap()
}
}
}

pub fn load_remaining_accounts<'info>(
remaining_accounts: &[AccountInfo<'info>],
index_array: Vec<usize>,
) -> Vec<AccountMeta> {
let mut accounts: Vec<AccountMeta> = vec![];
for index in index_array.iter() {
if remaining_accounts[*index].is_writable {
accounts.push(AccountMeta::new(
remaining_accounts[*index].key(),
remaining_accounts[*index].is_signer,
))
} else {
accounts.push(AccountMeta::new_readonly(
remaining_accounts[*index].key(),
remaining_accounts[*index].is_signer,
))
}
}
return accounts;
}
Loading