Merge pull request #389 from blockworks-foundation/feature/close-mango-account
Feature/close mango account
This commit is contained in:
commit
6206bbb953
|
@ -56,6 +56,7 @@ import {
|
||||||
createAssociatedTokenAccountIdempotentInstruction,
|
createAssociatedTokenAccountIdempotentInstruction,
|
||||||
getAssociatedTokenAddress,
|
getAssociatedTokenAddress,
|
||||||
toNative,
|
toNative,
|
||||||
|
U64_MAX_BN,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
import { sendTransaction } from './utils/rpc';
|
import { sendTransaction } from './utils/rpc';
|
||||||
|
|
||||||
|
@ -809,6 +810,81 @@ export class MangoClient {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async emptyAndCloseMangoAccount(
|
||||||
|
group: Group,
|
||||||
|
mangoAccount: MangoAccount,
|
||||||
|
): Promise<TransactionSignature> {
|
||||||
|
const instructions: TransactionInstruction[] = [];
|
||||||
|
const healthAccountsToExclude: PublicKey[] = [];
|
||||||
|
|
||||||
|
for (const serum3Account of mangoAccount.serum3Active()) {
|
||||||
|
const serum3Market = group.serum3MarketsMapByMarketIndex.get(
|
||||||
|
serum3Account.marketIndex,
|
||||||
|
)!;
|
||||||
|
|
||||||
|
const closeOOIx = await this.serum3CloseOpenOrdersIx(
|
||||||
|
group,
|
||||||
|
mangoAccount,
|
||||||
|
serum3Market.serumMarketExternal,
|
||||||
|
);
|
||||||
|
healthAccountsToExclude.push(serum3Account.openOrders);
|
||||||
|
instructions.push(closeOOIx);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const perp of mangoAccount.perpActive()) {
|
||||||
|
const perpMarketIndex = perp.marketIndex;
|
||||||
|
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
||||||
|
const deactivatingPositionIx = await this.perpDeactivatePositionIx(
|
||||||
|
group,
|
||||||
|
mangoAccount,
|
||||||
|
perpMarketIndex,
|
||||||
|
);
|
||||||
|
healthAccountsToExclude.push(perpMarket.publicKey);
|
||||||
|
instructions.push(deactivatingPositionIx);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const index in mangoAccount.tokensActive()) {
|
||||||
|
const indexNum = Number(index);
|
||||||
|
const accountsToExclude = [...healthAccountsToExclude];
|
||||||
|
const token = mangoAccount.tokensActive()[indexNum];
|
||||||
|
const bank = group.getFirstBankByTokenIndex(token.tokenIndex);
|
||||||
|
//to withdraw from all token accounts we need to exclude previous tokens pubkeys
|
||||||
|
//used to build health remaining accounts
|
||||||
|
if (indexNum !== 0) {
|
||||||
|
for (let i = indexNum; i--; i >= 0) {
|
||||||
|
const prevToken = mangoAccount.tokensActive()[i];
|
||||||
|
const prevBank = group.getFirstBankByTokenIndex(prevToken.tokenIndex);
|
||||||
|
accountsToExclude.push(prevBank.publicKey, prevBank.oracle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const withdrawIx = await this.tokenWithdrawNativeIx(
|
||||||
|
group,
|
||||||
|
mangoAccount,
|
||||||
|
bank.mint,
|
||||||
|
U64_MAX_BN,
|
||||||
|
false,
|
||||||
|
[...accountsToExclude],
|
||||||
|
);
|
||||||
|
instructions.push(...withdrawIx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeIx = await this.program.methods
|
||||||
|
.accountClose(false)
|
||||||
|
.accounts({
|
||||||
|
group: group.publicKey,
|
||||||
|
account: mangoAccount.publicKey,
|
||||||
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
||||||
|
solDestination: mangoAccount.owner,
|
||||||
|
})
|
||||||
|
.instruction();
|
||||||
|
instructions.push(closeIx);
|
||||||
|
|
||||||
|
return await this.sendAndConfirmTransaction(
|
||||||
|
[...instructions],
|
||||||
|
group.addressLookupTablesList,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public async tokenDeposit(
|
public async tokenDeposit(
|
||||||
group: Group,
|
group: Group,
|
||||||
mangoAccount: MangoAccount,
|
mangoAccount: MangoAccount,
|
||||||
|
@ -917,22 +993,28 @@ export class MangoClient {
|
||||||
allowBorrow: boolean,
|
allowBorrow: boolean,
|
||||||
): Promise<TransactionSignature> {
|
): Promise<TransactionSignature> {
|
||||||
const nativeAmount = toNative(amount, group.getMintDecimals(mintPk));
|
const nativeAmount = toNative(amount, group.getMintDecimals(mintPk));
|
||||||
return await this.tokenWithdrawNative(
|
const ixes = await this.tokenWithdrawNativeIx(
|
||||||
group,
|
group,
|
||||||
mangoAccount,
|
mangoAccount,
|
||||||
mintPk,
|
mintPk,
|
||||||
nativeAmount,
|
nativeAmount,
|
||||||
allowBorrow,
|
allowBorrow,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return await this.sendAndConfirmTransaction(
|
||||||
|
[...ixes],
|
||||||
|
group.addressLookupTablesList,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async tokenWithdrawNative(
|
public async tokenWithdrawNativeIx(
|
||||||
group: Group,
|
group: Group,
|
||||||
mangoAccount: MangoAccount,
|
mangoAccount: MangoAccount,
|
||||||
mintPk: PublicKey,
|
mintPk: PublicKey,
|
||||||
nativeAmount: BN,
|
nativeAmount: BN,
|
||||||
allowBorrow: boolean,
|
allowBorrow: boolean,
|
||||||
): Promise<TransactionSignature> {
|
healthAccountsToExclude: PublicKey[] = [],
|
||||||
|
): Promise<TransactionInstruction[]> {
|
||||||
const bank = group.getFirstBankByMint(mintPk);
|
const bank = group.getFirstBankByMint(mintPk);
|
||||||
|
|
||||||
const tokenAccountPk = await getAssociatedTokenAddress(
|
const tokenAccountPk = await getAssociatedTokenAddress(
|
||||||
|
@ -981,17 +1063,25 @@ export class MangoClient {
|
||||||
tokenAccount: tokenAccountPk,
|
tokenAccount: tokenAccountPk,
|
||||||
})
|
})
|
||||||
.remainingAccounts(
|
.remainingAccounts(
|
||||||
healthRemainingAccounts.map(
|
healthRemainingAccounts
|
||||||
(pk) =>
|
.filter(
|
||||||
({ pubkey: pk, isWritable: false, isSigner: false } as AccountMeta),
|
(accounts) =>
|
||||||
),
|
!healthAccountsToExclude.find((accountsToExclude) =>
|
||||||
|
accounts.equals(accountsToExclude),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.map(
|
||||||
|
(pk) =>
|
||||||
|
({
|
||||||
|
pubkey: pk,
|
||||||
|
isWritable: false,
|
||||||
|
isSigner: false,
|
||||||
|
} as AccountMeta),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.instruction();
|
.instruction();
|
||||||
|
|
||||||
return await this.sendAndConfirmTransaction(
|
return [...preInstructions, ix, ...postInstructions];
|
||||||
[...preInstructions, ix, ...postInstructions],
|
|
||||||
group.addressLookupTablesList,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serum
|
// Serum
|
||||||
|
@ -1718,11 +1808,11 @@ export class MangoClient {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async perpDeactivatePosition(
|
public async perpDeactivatePositionIx(
|
||||||
group: Group,
|
group: Group,
|
||||||
mangoAccount: MangoAccount,
|
mangoAccount: MangoAccount,
|
||||||
perpMarketIndex: PerpMarketIndex,
|
perpMarketIndex: PerpMarketIndex,
|
||||||
): Promise<TransactionSignature> {
|
): Promise<TransactionInstruction> {
|
||||||
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
||||||
const healthRemainingAccounts: PublicKey[] =
|
const healthRemainingAccounts: PublicKey[] =
|
||||||
this.buildHealthRemainingAccounts(
|
this.buildHealthRemainingAccounts(
|
||||||
|
@ -1746,7 +1836,23 @@ export class MangoClient {
|
||||||
({ pubkey: pk, isWritable: false, isSigner: false } as AccountMeta),
|
({ pubkey: pk, isWritable: false, isSigner: false } as AccountMeta),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.rpc();
|
.instruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async perpDeactivatePosition(
|
||||||
|
group: Group,
|
||||||
|
mangoAccount: MangoAccount,
|
||||||
|
perpMarketIndex: PerpMarketIndex,
|
||||||
|
): Promise<TransactionSignature> {
|
||||||
|
const ix = await this.perpDeactivatePositionIx(
|
||||||
|
group,
|
||||||
|
mangoAccount,
|
||||||
|
perpMarketIndex,
|
||||||
|
);
|
||||||
|
return await this.sendAndConfirmTransaction(
|
||||||
|
[ix],
|
||||||
|
group.addressLookupTablesList,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async perpPlaceOrder(
|
public async perpPlaceOrder(
|
||||||
|
@ -2592,7 +2698,6 @@ export class MangoClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mintInfos = tokenPositionIndices
|
const mintInfos = tokenPositionIndices
|
||||||
.filter((tokenIndex) => tokenIndex !== TokenPosition.TokenIndexUnset)
|
.filter((tokenIndex) => tokenIndex !== TokenPosition.TokenIndexUnset)
|
||||||
.map((tokenIndex) => group.mintInfosMapByTokenIndex.get(tokenIndex)!);
|
.map((tokenIndex) => group.mintInfosMapByTokenIndex.get(tokenIndex)!);
|
||||||
|
|
|
@ -114,7 +114,7 @@ async function main() {
|
||||||
} native amount ${nativeFlooredNumber} `,
|
} native amount ${nativeFlooredNumber} `,
|
||||||
);
|
);
|
||||||
|
|
||||||
await client.tokenWithdrawNative(
|
const withdrawIx = await client.tokenWithdrawNativeIx(
|
||||||
group,
|
group,
|
||||||
mangoAccount,
|
mangoAccount,
|
||||||
group.getFirstBankByTokenIndex(token.tokenIndex).mint,
|
group.getFirstBankByTokenIndex(token.tokenIndex).mint,
|
||||||
|
@ -123,6 +123,10 @@ async function main() {
|
||||||
) /* see comment in token_withdraw in program */,
|
) /* see comment in token_withdraw in program */,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
await this.sendAndConfirmTransaction(
|
||||||
|
[...withdrawIx],
|
||||||
|
group.addressLookupTablesList,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// reload and print current positions
|
// reload and print current positions
|
||||||
|
|
|
@ -99,7 +99,7 @@ async function closeUserAccount(userKeypairFile: string) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
await client.tokenWithdrawNative(
|
const withdrawIx = await client.tokenWithdrawNativeIx(
|
||||||
group,
|
group,
|
||||||
mangoAccount,
|
mangoAccount,
|
||||||
group.getFirstBankByTokenIndex(token.tokenIndex)!.mint,
|
group.getFirstBankByTokenIndex(token.tokenIndex)!.mint,
|
||||||
|
@ -110,6 +110,10 @@ async function closeUserAccount(userKeypairFile: string) {
|
||||||
),
|
),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
await this.sendAndConfirmTransaction(
|
||||||
|
[...withdrawIx],
|
||||||
|
group.addressLookupTablesList,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|
|
@ -152,13 +152,17 @@ async function main() {
|
||||||
try {
|
try {
|
||||||
await setBankPrice(bank, PRICES[liabName] / 2);
|
await setBankPrice(bank, PRICES[liabName] / 2);
|
||||||
|
|
||||||
await client.tokenWithdrawNative(
|
const withdrawIx = await client.tokenWithdrawNativeIx(
|
||||||
group,
|
group,
|
||||||
mangoAccount,
|
mangoAccount,
|
||||||
liabMint,
|
liabMint,
|
||||||
new BN(liabAmount),
|
new BN(liabAmount),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
await this.sendAndConfirmTransaction(
|
||||||
|
[...withdrawIx],
|
||||||
|
group.addressLookupTablesList,
|
||||||
|
);
|
||||||
} finally {
|
} finally {
|
||||||
// restore the oracle
|
// restore the oracle
|
||||||
await setBankPrice(bank, PRICES[liabName]);
|
await setBankPrice(bank, PRICES[liabName]);
|
||||||
|
@ -373,13 +377,17 @@ async function main() {
|
||||||
await setBankPrice(collateralBank, PRICES['SOL'] * 10);
|
await setBankPrice(collateralBank, PRICES['SOL'] * 10);
|
||||||
|
|
||||||
// Spot-borrow more than the collateral is worth
|
// Spot-borrow more than the collateral is worth
|
||||||
await client.tokenWithdrawNative(
|
const withdrawIx = await client.tokenWithdrawNativeIx(
|
||||||
group,
|
group,
|
||||||
mangoAccount,
|
mangoAccount,
|
||||||
liabMint,
|
liabMint,
|
||||||
new BN(-5000),
|
new BN(-5000),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
await this.sendAndConfirmTransaction(
|
||||||
|
[...withdrawIx],
|
||||||
|
group.addressLookupTablesList,
|
||||||
|
);
|
||||||
await mangoAccount.reload(client);
|
await mangoAccount.reload(client);
|
||||||
|
|
||||||
// Execute two trades that leave the account with +$0.011 positive pnl
|
// Execute two trades that leave the account with +$0.011 positive pnl
|
||||||
|
|
Loading…
Reference in New Issue