mc/flash loan for delegate (#271)
* flash loan for delegates Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * Fix rust client Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * prettier Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
parent
7da421ea1c
commit
e8ba511c45
|
@ -1275,6 +1275,8 @@ impl MangoClient {
|
||||||
accounts: {
|
accounts: {
|
||||||
let mut ams = anchor_lang::ToAccountMetas::to_account_metas(
|
let mut ams = anchor_lang::ToAccountMetas::to_account_metas(
|
||||||
&mango_v4::accounts::FlashLoanBegin {
|
&mango_v4::accounts::FlashLoanBegin {
|
||||||
|
account: self.mango_account_address,
|
||||||
|
owner: self.owner(),
|
||||||
token_program: Token::id(),
|
token_program: Token::id(),
|
||||||
instructions: solana_sdk::sysvar::instructions::id(),
|
instructions: solana_sdk::sysvar::instructions::id(),
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,9 +11,19 @@ use crate::util::checked_math as cm;
|
||||||
use anchor_lang::prelude::*;
|
use anchor_lang::prelude::*;
|
||||||
use anchor_lang::solana_program::sysvar::instructions as tx_instructions;
|
use anchor_lang::solana_program::sysvar::instructions as tx_instructions;
|
||||||
use anchor_lang::Discriminator;
|
use anchor_lang::Discriminator;
|
||||||
|
use anchor_spl::associated_token::AssociatedToken;
|
||||||
use anchor_spl::token::{self, Token, TokenAccount};
|
use anchor_spl::token::{self, Token, TokenAccount};
|
||||||
use fixed::types::I80F48;
|
use fixed::types::I80F48;
|
||||||
|
|
||||||
|
pub mod jupiter_mainnet_4 {
|
||||||
|
use solana_program::declare_id;
|
||||||
|
declare_id!("JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB");
|
||||||
|
}
|
||||||
|
pub mod jupiter_mainnet_3 {
|
||||||
|
use solana_program::declare_id;
|
||||||
|
declare_id!("JUP3c2Uh3WA4Ng34tw6kPd2G4C5BB21Xo36Je1s32Ph");
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets up mango vaults for flash loan
|
/// Sets up mango vaults for flash loan
|
||||||
///
|
///
|
||||||
/// In addition to these accounts, there must be remaining_accounts:
|
/// In addition to these accounts, there must be remaining_accounts:
|
||||||
|
@ -24,6 +34,10 @@ use fixed::types::I80F48;
|
||||||
/// 4. the mango group
|
/// 4. the mango group
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
pub struct FlashLoanBegin<'info> {
|
pub struct FlashLoanBegin<'info> {
|
||||||
|
pub account: AccountLoaderDynamic<'info, MangoAccount>,
|
||||||
|
// owner is checked at #1
|
||||||
|
pub owner: Signer<'info>,
|
||||||
|
|
||||||
pub token_program: Program<'info, Token>,
|
pub token_program: Program<'info, Token>,
|
||||||
|
|
||||||
/// Instructions Sysvar for instruction introspection
|
/// Instructions Sysvar for instruction introspection
|
||||||
|
@ -42,11 +56,9 @@ pub struct FlashLoanBegin<'info> {
|
||||||
/// 4. the mango group
|
/// 4. the mango group
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
pub struct FlashLoanEnd<'info> {
|
pub struct FlashLoanEnd<'info> {
|
||||||
#[account(
|
#[account(mut)]
|
||||||
mut,
|
|
||||||
has_one = owner
|
|
||||||
)]
|
|
||||||
pub account: AccountLoaderDynamic<'info, MangoAccount>,
|
pub account: AccountLoaderDynamic<'info, MangoAccount>,
|
||||||
|
// owner is checked at #1
|
||||||
pub owner: Signer<'info>,
|
pub owner: Signer<'info>,
|
||||||
|
|
||||||
pub token_program: Program<'info, Token>,
|
pub token_program: Program<'info, Token>,
|
||||||
|
@ -65,6 +77,14 @@ pub fn flash_loan_begin<'key, 'accounts, 'remaining, 'info>(
|
||||||
ctx: Context<'key, 'accounts, 'remaining, 'info, FlashLoanBegin<'info>>,
|
ctx: Context<'key, 'accounts, 'remaining, 'info, FlashLoanBegin<'info>>,
|
||||||
loan_amounts: Vec<u64>,
|
loan_amounts: Vec<u64>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
let account = ctx.accounts.account.load_mut()?;
|
||||||
|
|
||||||
|
// account constraint #1
|
||||||
|
require!(
|
||||||
|
account.fixed.is_owner_or_delegate(ctx.accounts.owner.key()),
|
||||||
|
MangoError::SomeError
|
||||||
|
);
|
||||||
|
|
||||||
let num_loans = loan_amounts.len();
|
let num_loans = loan_amounts.len();
|
||||||
require_eq!(ctx.remaining_accounts.len(), 3 * num_loans + 1);
|
require_eq!(ctx.remaining_accounts.len(), 3 * num_loans + 1);
|
||||||
let banks = &ctx.remaining_accounts[..num_loans];
|
let banks = &ctx.remaining_accounts[..num_loans];
|
||||||
|
@ -156,6 +176,15 @@ pub fn flash_loan_begin<'key, 'accounts, 'remaining, 'info>(
|
||||||
Err(e) => return Err(e.into()),
|
Err(e) => return Err(e.into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if account.fixed.is_delegate(ctx.accounts.owner.key()) {
|
||||||
|
require_msg!(
|
||||||
|
ix.program_id == AssociatedToken::id()
|
||||||
|
|| ix.program_id == jupiter_mainnet_3::ID
|
||||||
|
|| ix.program_id == jupiter_mainnet_4::ID,
|
||||||
|
"delegate is only allowed to pass in ixs to ATA or Jupiter v3 or v4 programs"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Check that the mango program key is not used
|
// Check that the mango program key is not used
|
||||||
if ix.program_id == crate::id() {
|
if ix.program_id == crate::id() {
|
||||||
// must be the last mango ix -- this could possibly be relaxed, but right now
|
// must be the last mango ix -- this could possibly be relaxed, but right now
|
||||||
|
@ -172,6 +201,11 @@ pub fn flash_loan_begin<'key, 'accounts, 'remaining, 'info>(
|
||||||
MangoError::SomeError
|
MangoError::SomeError
|
||||||
);
|
);
|
||||||
|
|
||||||
|
require_msg!(
|
||||||
|
ctx.accounts.account.key() == ix.accounts[0].pubkey,
|
||||||
|
"the mango account passed to FlashLoanBegin and End must match"
|
||||||
|
);
|
||||||
|
|
||||||
// check that the same vaults and token accounts are passed
|
// check that the same vaults and token accounts are passed
|
||||||
let begin_accounts = &ctx.remaining_accounts[num_loans..];
|
let begin_accounts = &ctx.remaining_accounts[num_loans..];
|
||||||
let end_accounts = &ix.accounts[ix.accounts.len() - begin_accounts.len()..];
|
let end_accounts = &ix.accounts[ix.accounts.len() - begin_accounts.len()..];
|
||||||
|
@ -208,6 +242,13 @@ pub fn flash_loan_end<'key, 'accounts, 'remaining, 'info>(
|
||||||
flash_loan_type: FlashLoanType,
|
flash_loan_type: FlashLoanType,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut account = ctx.accounts.account.load_mut()?;
|
let mut account = ctx.accounts.account.load_mut()?;
|
||||||
|
|
||||||
|
// account constraint #1
|
||||||
|
require!(
|
||||||
|
account.fixed.is_owner_or_delegate(ctx.accounts.owner.key()),
|
||||||
|
MangoError::SomeError
|
||||||
|
);
|
||||||
|
|
||||||
let group = account.fixed.group;
|
let group = account.fixed.group;
|
||||||
|
|
||||||
let remaining_len = ctx.remaining_accounts.len();
|
let remaining_len = ctx.remaining_accounts.len();
|
||||||
|
|
|
@ -220,6 +220,10 @@ impl MangoAccountFixed {
|
||||||
self.owner == ix_signer || self.delegate == ix_signer
|
self.owner == ix_signer || self.delegate == ix_signer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_delegate(&self, ix_signer: Pubkey) -> bool {
|
||||||
|
self.delegate == ix_signer
|
||||||
|
}
|
||||||
|
|
||||||
pub fn being_liquidated(&self) -> bool {
|
pub fn being_liquidated(&self) -> bool {
|
||||||
self.being_liquidated == 1
|
self.being_liquidated == 1
|
||||||
}
|
}
|
||||||
|
|
|
@ -367,7 +367,9 @@ pub async fn check_prev_instruction_post_health(solana: &SolanaCookie, account:
|
||||||
//
|
//
|
||||||
|
|
||||||
pub struct FlashLoanBeginInstruction {
|
pub struct FlashLoanBeginInstruction {
|
||||||
|
pub account: Pubkey,
|
||||||
pub group: Pubkey,
|
pub group: Pubkey,
|
||||||
|
pub owner: TestKeypair,
|
||||||
pub mango_token_bank: Pubkey,
|
pub mango_token_bank: Pubkey,
|
||||||
pub mango_token_vault: Pubkey,
|
pub mango_token_vault: Pubkey,
|
||||||
pub target_token_account: Pubkey,
|
pub target_token_account: Pubkey,
|
||||||
|
@ -384,6 +386,8 @@ impl ClientInstruction for FlashLoanBeginInstruction {
|
||||||
let program_id = mango_v4::id();
|
let program_id = mango_v4::id();
|
||||||
|
|
||||||
let accounts = Self::Accounts {
|
let accounts = Self::Accounts {
|
||||||
|
account: self.account,
|
||||||
|
owner: self.owner.pubkey(),
|
||||||
token_program: Token::id(),
|
token_program: Token::id(),
|
||||||
instructions: solana_program::sysvar::instructions::id(),
|
instructions: solana_program::sysvar::instructions::id(),
|
||||||
};
|
};
|
||||||
|
|
|
@ -123,6 +123,8 @@ async fn test_margin_trade() -> Result<(), BanksClientError> {
|
||||||
let send_flash_loan_tx = |solana, withdraw_amount, deposit_amount| async move {
|
let send_flash_loan_tx = |solana, withdraw_amount, deposit_amount| async move {
|
||||||
let mut tx = ClientTransaction::new(solana);
|
let mut tx = ClientTransaction::new(solana);
|
||||||
tx.add_instruction(FlashLoanBeginInstruction {
|
tx.add_instruction(FlashLoanBeginInstruction {
|
||||||
|
account,
|
||||||
|
owner,
|
||||||
group,
|
group,
|
||||||
mango_token_bank: bank,
|
mango_token_bank: bank,
|
||||||
mango_token_vault: vault,
|
mango_token_vault: vault,
|
||||||
|
|
|
@ -2052,7 +2052,6 @@ export class MangoClient {
|
||||||
.flashLoanEnd(flashLoanType)
|
.flashLoanEnd(flashLoanType)
|
||||||
.accounts({
|
.accounts({
|
||||||
account: mangoAccount.publicKey,
|
account: mangoAccount.publicKey,
|
||||||
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
||||||
})
|
})
|
||||||
.remainingAccounts([
|
.remainingAccounts([
|
||||||
...parsedHealthAccounts,
|
...parsedHealthAccounts,
|
||||||
|
@ -2076,6 +2075,8 @@ export class MangoClient {
|
||||||
) /* we don't care about borrowing the target amount, this is just a dummy */,
|
) /* we don't care about borrowing the target amount, this is just a dummy */,
|
||||||
])
|
])
|
||||||
.accounts({
|
.accounts({
|
||||||
|
account: mangoAccount.publicKey,
|
||||||
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
||||||
instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
|
instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
|
||||||
})
|
})
|
||||||
.remainingAccounts([
|
.remainingAccounts([
|
||||||
|
|
|
@ -1239,6 +1239,16 @@ export type MangoV4 = {
|
||||||
{
|
{
|
||||||
"name": "flashLoanBegin",
|
"name": "flashLoanBegin",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
|
{
|
||||||
|
"name": "account",
|
||||||
|
"isMut": false,
|
||||||
|
"isSigner": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "owner",
|
||||||
|
"isMut": false,
|
||||||
|
"isSigner": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "tokenProgram",
|
"name": "tokenProgram",
|
||||||
"isMut": false,
|
"isMut": false,
|
||||||
|
@ -8058,6 +8068,16 @@ export const IDL: MangoV4 = {
|
||||||
{
|
{
|
||||||
"name": "flashLoanBegin",
|
"name": "flashLoanBegin",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
|
{
|
||||||
|
"name": "account",
|
||||||
|
"isMut": false,
|
||||||
|
"isSigner": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "owner",
|
||||||
|
"isMut": false,
|
||||||
|
"isSigner": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "tokenProgram",
|
"name": "tokenProgram",
|
||||||
"isMut": false,
|
"isMut": false,
|
||||||
|
|
Loading…
Reference in New Issue