diff --git a/programs/mango-v4/src/instructions/flash_loan.rs b/programs/mango-v4/src/instructions/flash_loan.rs index f2663338b..c738aa500 100644 --- a/programs/mango-v4/src/instructions/flash_loan.rs +++ b/programs/mango-v4/src/instructions/flash_loan.rs @@ -36,6 +36,7 @@ pub struct FlashLoan<'info> { pub token_program: Program<'info, Token>, } +#[derive(Debug)] struct AllowedVault { /// index of the vault in cpi_ais vault_cpi_ai_index: usize, @@ -51,7 +52,7 @@ struct AllowedVault { loan_amount: I80F48, } -#[derive(AnchorDeserialize, AnchorSerialize, Clone, Copy)] +#[derive(AnchorDeserialize, AnchorSerialize, Clone, Copy, Debug)] pub struct FlashLoanWithdraw { /// Account index of the vault to withdraw from in the target_accounts section. /// Index is counted after health accounts. @@ -60,7 +61,7 @@ pub struct FlashLoanWithdraw { pub amount: u64, } -#[derive(AnchorDeserialize, AnchorSerialize)] +#[derive(AnchorDeserialize, AnchorSerialize, Debug)] pub struct CpiData { pub account_start: u8, pub data: Vec, @@ -232,6 +233,11 @@ pub fn flash_loan<'key, 'accounts, 'remaining, 'info>( } } + msg!("withdraws {:#?}", withdraws); + msg!("cpi_datas {:#?}", cpi_datas); + msg!("allowed_vaults {:#?}", allowed_vaults); + msg!("used_vaults {:#?}", used_vaults); + // get rid of Ref<> to avoid limiting the cpi call drop(allowed_banks); drop(group); diff --git a/release-to-devnet.sh b/release-to-devnet.sh index e34b7414b..6d6743cb2 100755 --- a/release-to-devnet.sh +++ b/release-to-devnet.sh @@ -22,13 +22,13 @@ if [[ -z "${NO_DEPLOY}" ]]; then solana --url https://mango.devnet.rpcpool.com program deploy --program-id $PROGRAM_ID \ -k $WALLET_WITH_FUNDS target/deploy/mango_v4.so - # publish idl - anchor idl upgrade --provider.cluster https://mango.devnet.rpcpool.com --provider.wallet $WALLET_WITH_FUNDS \ - --filepath target/idl/mango_v4.json $PROGRAM_ID + # # publish idl + # anchor idl upgrade --provider.cluster https://mango.devnet.rpcpool.com --provider.wallet $WALLET_WITH_FUNDS \ + # --filepath target/idl/mango_v4.json $PROGRAM_ID else echo "Skipping deployment..." fi -# build npm package -(cd ./ts/client && tsc) +# # build npm package +# (cd ./ts/client && tsc) diff --git a/ts/client/src/client.ts b/ts/client/src/client.ts index 9f8a1d560..0db8a6e32 100644 --- a/ts/client/src/client.ts +++ b/ts/client/src/client.ts @@ -1212,11 +1212,17 @@ export class MangoClient { ]) .flat(); - const keys = instructions.map((ix) => ix.keys).flat(); - const vaultIndex = keys.findIndex((k) => k.pubkey.equals(inputBank.vault)); + const vaultIndex = targetRemainingAccounts + .reverse() + .findIndex((k) => k.pubkey.equals(inputBank.vault)); + + targetRemainingAccounts.reverse(); const withdraws: FlashLoanWithdraw[] = [ - { index: vaultIndex, amount: toU64(amountIn, inputBank.mintDecimals) }, + { + index: targetRemainingAccounts.length - vaultIndex - 1, + amount: toU64(amountIn, inputBank.mintDecimals), + }, ]; let cpiDatas = []; diff --git a/ts/client/src/scripts/example1-flash-loan.ts b/ts/client/src/scripts/example1-flash-loan.ts new file mode 100644 index 000000000..e7bea42a2 --- /dev/null +++ b/ts/client/src/scripts/example1-flash-loan.ts @@ -0,0 +1,77 @@ +import { AnchorProvider, Wallet } from '@project-serum/anchor'; +import { Connection, Keypair } from '@solana/web3.js'; +import fs from 'fs'; +import { MangoClient } from '../client'; +import { MANGO_V4_ID } from '../constants'; + +async function main() { + const options = AnchorProvider.defaultOptions(); + const connection = new Connection( + 'https://mango.devnet.rpcpool.com', + options, + ); + + const user = Keypair.fromSecretKey( + Buffer.from( + JSON.parse(fs.readFileSync(process.env.USER_KEYPAIR!, 'utf-8')), + ), + ); + const userWallet = new Wallet(user); + const userProvider = new AnchorProvider(connection, userWallet, options); + const client = await MangoClient.connect( + userProvider, + 'devnet', + MANGO_V4_ID['devnet'], + ); + console.log(`User ${userWallet.publicKey.toBase58()}`); + + // fetch group + const admin = Keypair.fromSecretKey( + Buffer.from( + JSON.parse(fs.readFileSync(process.env.ADMIN_KEYPAIR!, 'utf-8')), + ), + ); + const group = await client.getGroupForAdmin(admin.publicKey, 0); + console.log(`Found group ${group.publicKey.toBase58()}`); + + // create + fetch account + console.log(`Creating mangoaccount...`); + const mangoAccount = await client.getOrCreateMangoAccount( + group, + user.publicKey, + 0, + 'my_mango_account', + ); + console.log(`...created/found mangoAccount ${mangoAccount.publicKey}`); + console.log(mangoAccount.toString()); + + if (false) { + // deposit and withdraw + console.log(`Depositing...50 USDC`); + await client.tokenDeposit(group, mangoAccount, 'USDC', 50); + await mangoAccount.reload(client); + + console.log(`Depositing...0.0005 BTC`); + await client.tokenDeposit(group, mangoAccount, 'BTC', 0.0005); + await mangoAccount.reload(client); + } + try { + const sig = await client.marginTrade({ + group: group, + mangoAccount: mangoAccount, + inputToken: 'USDC', + amountIn: 0.001, + outputToken: 'SOL', + minimumAmountOut: 0.1, + }); + console.log( + `sig https://explorer.solana.com/address/${sig}?cluster=devnet`, + ); + } catch (error) { + console.log(error); + } + + process.exit(); +} + +main();