diff --git a/Anchor.toml b/Anchor.toml index f397419..54a4ce1 100644 --- a/Anchor.toml +++ b/Anchor.toml @@ -10,6 +10,7 @@ adapter_katana = "ADPTwDKJTizC3V8gZXDxt5uLjJv4pBnh1nTTf9dZJnS2" adapter_tulip = "ADPT9nhC1asRcEB13FKymLTatqWGCuZHDznGgnakWKxW" adapter_friktion = "ADPTzbsaBdXA3FqXoPHjaTjPfh9kadxxFKxonZihP1Ji" adapter_nft_finance = "ADPTyBr92sBCE1hdYBRvXbMpF4hKs17xyDjFPxopcsrh" +adapter_lido = "ADPTPxbHbEBo9A8E53P2PZnmw3ZYJuwc8ArQQkbJtqhx" [registry] url = "https://anchor.projectserum.com" diff --git a/Cargo.lock b/Cargo.lock index c643f19..15fc658 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,6 +34,14 @@ dependencies = [ "anchor-spl", ] +[[package]] +name = "adapter-lido" +version = "0.1.0" +dependencies = [ + "anchor-lang", + "anchor-spl", +] + [[package]] name = "adapter-lifinity" version = "0.1.0" diff --git a/programs/adapter-lido/Cargo.toml b/programs/adapter-lido/Cargo.toml new file mode 100644 index 0000000..eaa3996 --- /dev/null +++ b/programs/adapter-lido/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "adapter-lido" +version = "0.1.0" +description = "Created with Anchor" +edition = "2021" + +[lib] +crate-type = ["cdylib", "lib"] +name = "adapter_lido" + +[features] +no-entrypoint = [] +no-idl = [] +no-log-ix-name = [] +cpi = ["no-entrypoint"] + +[dependencies] +anchor-lang = "0.24.2" +anchor-spl = "0.24.2" diff --git a/programs/adapter-lido/Xargo.toml b/programs/adapter-lido/Xargo.toml new file mode 100644 index 0000000..475fb71 --- /dev/null +++ b/programs/adapter-lido/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] diff --git a/programs/adapter-lido/src/lib.rs b/programs/adapter-lido/src/lib.rs new file mode 100644 index 0000000..b319333 --- /dev/null +++ b/programs/adapter-lido/src/lib.rs @@ -0,0 +1,208 @@ +use anchor_lang::prelude::*; +use anchor_lang::solana_program::{ + instruction::{AccountMeta, Instruction}, + program::invoke, + pubkey::Pubkey, + stake, +}; +use anchor_spl::token::TokenAccount; + +declare_id!("4XnyVwkN5dR7Rinnys59qMqLjhxytpv4JbFLJuXe5mKk"); + +#[program] +pub mod adapter_lido { + use anchor_lang::solana_program::program::invoke_signed; + + use super::*; + + pub fn deposit<'a, 'b, 'c, 'info>( + ctx: Context<'a, 'b, 'c, 'info, Action<'info>>, + input: Vec, + ) -> Result<()> { + // Get Input + let mut input_bytes = &input[..]; + let input_struct = DepositInputWrapper::deserialize(&mut input_bytes)?; + + msg!("Input: {:?}", input_struct); + + let mut recipient_st_sol_account = + Account::::try_from(&ctx.remaining_accounts[2])?; + + let token_amount = recipient_st_sol_account.amount; + + let deposit_accounts = vec![ + AccountMeta::new(ctx.remaining_accounts[0].key(), false), + AccountMeta::new(ctx.remaining_accounts[1].key(), true), + AccountMeta::new(recipient_st_sol_account.key(), false), + AccountMeta::new(ctx.remaining_accounts[3].key(), false), + AccountMeta::new(ctx.remaining_accounts[4].key(), false), + AccountMeta::new_readonly(ctx.remaining_accounts[5].key(), false), + AccountMeta::new_readonly(ctx.remaining_accounts[6].key(), false), + AccountMeta::new_readonly(ctx.remaining_accounts[7].key(), false), + ]; + + // Prepend instruction byte to Solido Input + let mut data = vec![1u8]; + data.extend(input); + + let ix = Instruction { + program_id: ctx.accounts.base_program_id.key(), + accounts: deposit_accounts, + data, + }; + + invoke(&ix, ctx.remaining_accounts)?; + + recipient_st_sol_account.reload()?; + + let share_amount = recipient_st_sol_account + .amount + .checked_sub(token_amount) + .unwrap(); + + // Wrap Output + let output_struct = DepositOutputWrapper { + share_amount, + ..Default::default() + }; + let mut output: Vec = Vec::new(); + output_struct.serialize(&mut output).unwrap(); + + anchor_lang::solana_program::program::set_return_data(&output); + + msg!("Output: {:?}", output_struct); + + Ok(()) + } + + pub fn withdraw<'a, 'b, 'c, 'info>( + ctx: Context<'a, 'b, 'c, 'info, Action<'info>>, + input: Vec, + ) -> Result<()> { + // Get Input + let mut input_bytes = &input[..]; + let input_struct = WithdrawInputWrapper::deserialize(&mut input_bytes)?; + + msg!("Input: {:?}", input_struct); + + let user_account = ctx.remaining_accounts[1].clone(); + let account_balance = user_account.lamports(); + + let withdraw_accounts = vec![ + AccountMeta::new(ctx.remaining_accounts[0].key(), false), + AccountMeta::new_readonly(user_account.key(), true), + AccountMeta::new(ctx.remaining_accounts[2].key(), false), + AccountMeta::new(ctx.remaining_accounts[3].key(), false), + AccountMeta::new_readonly(ctx.remaining_accounts[4].key(), false), + AccountMeta::new(ctx.remaining_accounts[5].key(), false), + AccountMeta::new(ctx.remaining_accounts[6].key(), true), + AccountMeta::new_readonly(ctx.remaining_accounts[7].key(), false), + AccountMeta::new(ctx.remaining_accounts[8].key(), false), + AccountMeta::new_readonly(ctx.remaining_accounts[9].key(), false), + AccountMeta::new_readonly(ctx.remaining_accounts[10].key(), false), + AccountMeta::new_readonly(ctx.remaining_accounts[11].key(), false), + AccountMeta::new_readonly(ctx.remaining_accounts[12].key(), false), + ]; + + let mut data = vec![23u8]; + data.extend(input); + + let withdraw_ix = Instruction { + program_id: ctx.accounts.base_program_id.key(), + accounts: withdraw_accounts, + data, + }; + + invoke(&withdraw_ix, ctx.remaining_accounts)?; + + let deactivate_ix = stake::instruction::deactivate_stake( + &user_account.key(), + &ctx.remaining_accounts[6].key(), + ); + invoke_signed(&deactivate_ix, &vec![user_account.clone()], &[&[&user_account.key().to_bytes(), &input_struct.gateway_key.to_bytes()]])?; + + let lp_amount = user_account + .lamports() + .checked_sub(account_balance) + .unwrap(); + + // Wrap Output + let output_struct = WithdrawOutputWrapper { + lp_amount, + ..Default::default() + }; + let mut output: Vec = Vec::new(); + output_struct.serialize(&mut output).unwrap(); + + anchor_lang::solana_program::program::set_return_data(&output); + + Ok(()) + } +} + +#[derive(Accounts)] +pub struct Action<'info> { + pub gateway_authority: Signer<'info>, + /// CHECK: Safe + pub base_program_id: AccountInfo<'info>, +} + +#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Default)] +pub struct DepositInputWrapper { + /// Amount to deposit. + pub amount: u64, +} + +#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Default)] +pub struct WithdrawInputWrapper { + /// Amount to withdraw. + pub amount: u64, + /// Index of the Heaviest Validator. + pub validator_index: u32, + pub gateway_key: Pubkey, +} + +// OutputWrapper needs to take up all the space of 32 bytes +#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Default)] +pub struct DepositOutputWrapper { + pub share_amount: u64, + pub dummy_2: u64, + pub dummy_3: u64, + pub dummy_4: u64, +} + +// OutputWrapper needs to take up all the space of 32 bytes +#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Default)] +pub struct WithdrawOutputWrapper { + pub lp_amount: u64, + pub dummy_2: u64, + pub dummy_3: u64, + pub dummy_4: u64, +} + +pub type DepositOutputTuple = (u64, u64, u64, u64); +pub type WithdrawOutputTuple = (u64, u64, u64, u64); + +impl From for DepositOutputTuple { + fn from(result: DepositOutputWrapper) -> DepositOutputTuple { + let DepositOutputWrapper { + share_amount, + dummy_2, + dummy_3, + dummy_4, + } = result; + (share_amount, dummy_2, dummy_3, dummy_4) + } +} + +impl From for WithdrawOutputTuple { + fn from(result: WithdrawOutputWrapper) -> WithdrawOutputTuple { + let WithdrawOutputWrapper { + lp_amount, + dummy_2, + dummy_3, + dummy_4, + } = result; + (lp_amount, dummy_2, dummy_3, dummy_4) + } +} diff --git a/target/idl/adapter_lido.json b/target/idl/adapter_lido.json new file mode 100644 index 0000000..d0b11e6 --- /dev/null +++ b/target/idl/adapter_lido.json @@ -0,0 +1,99 @@ +{ + "version": "0.1.0", + "name": "adapter_lido", + "instructions": [ + { + "name": "deposit", + "accounts": [ + { + "name": "gatewayAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "gatewayStateInfo", + "isMut": false, + "isSigner": false + }, + { + "name": "baseProgramId", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "input", + "type": "bytes" + } + ] + }, + { + "name": "withdraw", + "accounts": [ + { + "name": "gatewayAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "gatewayStateInfo", + "isMut": false, + "isSigner": false + }, + { + "name": "baseProgramId", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "input", + "type": "bytes" + } + ] + } + ], + "types": [ + { + "name": "DepositInputWrapper", + "type": { + "kind": "struct", + "fields": [ + { + "name": "instruction", + "type": "u8" + }, + { + "name": "amount", + "type": "u64" + } + ] + } + }, + { + "name": "WithdrawInputWrapper", + "type": { + "kind": "struct", + "fields": [ + { + "name": "instruction", + "type": "u8" + }, + { + "name": "amount", + "type": "u64" + }, + { + "name": "validatorIndex", + "type": "u32" + } + ] + } + } + ], + "metadata": { + "address": "4XnyVwkN5dR7Rinnys59qMqLjhxytpv4JbFLJuXe5mKk" + } +} \ No newline at end of file diff --git a/target/types/adapter_lido.ts b/target/types/adapter_lido.ts new file mode 100644 index 0000000..0e423c9 --- /dev/null +++ b/target/types/adapter_lido.ts @@ -0,0 +1,193 @@ +export type AdapterLido = { + "version": "0.1.0", + "name": "adapter_lido", + "instructions": [ + { + "name": "deposit", + "accounts": [ + { + "name": "gatewayAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "gatewayStateInfo", + "isMut": false, + "isSigner": false + }, + { + "name": "baseProgramId", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "input", + "type": "bytes" + } + ] + }, + { + "name": "withdraw", + "accounts": [ + { + "name": "gatewayAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "gatewayStateInfo", + "isMut": false, + "isSigner": false + }, + { + "name": "baseProgramId", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "input", + "type": "bytes" + } + ] + } + ], + "types": [ + { + "name": "DepositInputWrapper", + "type": { + "kind": "struct", + "fields": [ + { + "name": "instruction", + "type": "u8" + }, + { + "name": "amount", + "type": "u64" + } + ] + } + }, + { + "name": "WithdrawInputWrapper", + "type": { + "kind": "struct", + "fields": [ + { + "name": "instruction", + "type": "u8" + }, + { + "name": "amount", + "type": "u64" + }, + { + "name": "validatorIndex", + "type": "u32" + } + ] + } + } + ] +}; + +export const IDL: AdapterLido = { + "version": "0.1.0", + "name": "adapter_lido", + "instructions": [ + { + "name": "deposit", + "accounts": [ + { + "name": "gatewayAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "gatewayStateInfo", + "isMut": false, + "isSigner": false + }, + { + "name": "baseProgramId", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "input", + "type": "bytes" + } + ] + }, + { + "name": "withdraw", + "accounts": [ + { + "name": "gatewayAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "gatewayStateInfo", + "isMut": false, + "isSigner": false + }, + { + "name": "baseProgramId", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "input", + "type": "bytes" + } + ] + } + ], + "types": [ + { + "name": "DepositInputWrapper", + "type": { + "kind": "struct", + "fields": [ + { + "name": "instruction", + "type": "u8" + }, + { + "name": "amount", + "type": "u64" + } + ] + } + }, + { + "name": "WithdrawInputWrapper", + "type": { + "kind": "struct", + "fields": [ + { + "name": "instruction", + "type": "u8" + }, + { + "name": "amount", + "type": "u64" + }, + { + "name": "validatorIndex", + "type": "u32" + } + ] + } + } + ] +};