@jet-lab/margin
Advanced tools
Comparing version 0.1.34 to 0.1.35
@@ -44,4 +44,4 @@ import { PublicKey } from "@solana/web3.js"; | ||
calculateValue(): void; | ||
collateralValue(): BN; | ||
requiredCollateralValue(): BN; | ||
collateralValue(): any; | ||
requiredCollateralValue(): any; | ||
setBalance(balance: BN): void; | ||
@@ -48,0 +48,0 @@ setPrice(price: PriceInfo): void; |
@@ -1,2 +0,1 @@ | ||
/// <reference types="bn.js" /> | ||
import { Address, AnchorProvider, BN } from "@project-serum/anchor"; | ||
@@ -3,0 +2,0 @@ import { GetProgramAccountsFilter, PublicKey, TransactionInstruction, TransactionSignature } from "@solana/web3.js"; |
@@ -1,2 +0,1 @@ | ||
/// <reference types="bn.js" /> | ||
import { Address, BN } from "@project-serum/anchor"; | ||
@@ -6,3 +5,3 @@ import { PriceData } from "@pythnetwork/client"; | ||
import { PublicKey, TransactionInstruction } from "@solana/web3.js"; | ||
import { AssociatedToken } from "../../token"; | ||
import { AssociatedToken, TokenAddress } from "../../token"; | ||
import { TokenAmount } from "../../token/tokenAmount"; | ||
@@ -90,4 +89,4 @@ import { MarginAccount } from "../marginAccount"; | ||
calculatePrices(pythPrice: PriceData | undefined): PriceResult; | ||
depositNoteExchangeRate(): BN; | ||
loanNoteExchangeRate(): BN; | ||
depositNoteExchangeRate(): any; | ||
loanNoteExchangeRate(): any; | ||
/** | ||
@@ -145,15 +144,16 @@ * Linear interpolation between (x0, y0) and (x1, y1) | ||
amount: BN; | ||
source?: Address; | ||
source?: TokenAddress; | ||
}): Promise<string>; | ||
withDeposit({ instructions, depositor, source, destination, amount }: { | ||
withDeposit({ instructions, marginAccount, source, destination, amount }: { | ||
instructions: TransactionInstruction[]; | ||
depositor: Address; | ||
source: Address; | ||
marginAccount: MarginAccount; | ||
source?: TokenAddress; | ||
destination: Address; | ||
amount: BN; | ||
}): Promise<void>; | ||
marginBorrow({ marginAccount, pools, amount }: { | ||
marginBorrow({ marginAccount, pools, amount, destination }: { | ||
marginAccount: MarginAccount; | ||
pools: Pool[]; | ||
amount: BN; | ||
destination?: TokenAddress; | ||
}): Promise<void>; | ||
@@ -168,13 +168,14 @@ withGetOrCreateLoanPosition(instructions: TransactionInstruction[], marginAccount: MarginAccount): Promise<Address>; | ||
}): Promise<void>; | ||
marginRepay({ marginAccount, pools, amount }: { | ||
marginRepay({ marginAccount, pools, source, amount }: { | ||
marginAccount: MarginAccount; | ||
pools: Pool[]; | ||
amount: PoolAmount; | ||
source?: TokenAddress; | ||
amount: BN; | ||
}): Promise<void>; | ||
makeMarginRepayInstruction({ instructions, marginAccount, deposit_account, loan_account, amount }: { | ||
withMarginRepay({ instructions, marginAccount, depositPosition, loanPosition, amount }: { | ||
instructions: TransactionInstruction[]; | ||
marginAccount: MarginAccount; | ||
deposit_account: Address; | ||
loan_account: Address; | ||
amount: PoolAmount; | ||
depositPosition: Address; | ||
loanPosition: Address; | ||
amount: BN; | ||
}): Promise<void>; | ||
@@ -185,3 +186,3 @@ withdraw({ marginAccount, pools, amount, destination }: { | ||
amount: PoolAmount; | ||
destination?: Address; | ||
destination?: TokenAddress; | ||
}): Promise<void>; | ||
@@ -191,4 +192,4 @@ withWithdraw({ instructions, marginAccount, source, destination, amount }: { | ||
marginAccount: MarginAccount; | ||
source: Address; | ||
destination: Address; | ||
source: PublicKey; | ||
destination?: TokenAddress; | ||
amount: PoolAmount; | ||
@@ -195,0 +196,0 @@ }): Promise<void>; |
@@ -320,3 +320,3 @@ "use strict"; | ||
*/ | ||
async deposit({ marginAccount, amount, source }) { | ||
async deposit({ marginAccount, amount, source = token_1.TokenFormat.unwrappedSol }) { | ||
(0, chai_1.assert)(marginAccount); | ||
@@ -330,6 +330,5 @@ (0, chai_1.assert)(amount); | ||
const instructions = []; | ||
source !== null && source !== void 0 ? source : (source = await token_1.AssociatedToken.withCreateOrWrapIfNativeMint(instructions, marginAccount.provider, this.tokenMint, amount)); | ||
await this.withDeposit({ | ||
instructions: instructions, | ||
depositor: marginAccount.owner, | ||
marginAccount, | ||
source, | ||
@@ -342,3 +341,12 @@ destination: position.address, | ||
} | ||
async withDeposit({ instructions, depositor, source, destination, amount }) { | ||
async withDeposit({ instructions, marginAccount, source = token_1.TokenFormat.unwrappedSol, destination, amount }) { | ||
const provider = marginAccount.provider; | ||
const mint = this.tokenMint; | ||
source = await token_1.AssociatedToken.withBeginTransferFromSource({ | ||
instructions, | ||
provider, | ||
mint, | ||
amount, | ||
source | ||
}); | ||
const ix = await this.programs.marginPool.methods | ||
@@ -350,3 +358,3 @@ .deposit(amount) | ||
depositNoteMint: this.addresses.depositNoteMint, | ||
depositor, | ||
depositor: marginAccount.owner, | ||
source, | ||
@@ -358,14 +366,20 @@ destination, | ||
instructions.push(ix); | ||
token_1.AssociatedToken.withEndTransfer({ | ||
instructions, | ||
provider, | ||
mint, | ||
destination | ||
}); | ||
} | ||
async marginBorrow({ marginAccount, pools, amount }) { | ||
async marginBorrow({ marginAccount, pools, amount, destination }) { | ||
const lamports = poolAmount_1.PoolAmount.tokens(amount); | ||
await marginAccount.refresh(); | ||
const preInstructons = []; | ||
const refreshInstructions = []; | ||
const postInstructions = []; | ||
const instructionsInstructions = []; | ||
const depositPosition = await marginAccount.getOrCreatePosition(this.addresses.depositNoteMint); | ||
(0, chai_1.assert)(depositPosition); | ||
const loanNoteAccount = await this.withGetOrCreateLoanPosition(preInstructons, marginAccount); | ||
await this.withMarginRefreshAllPositionPrices({ instructions: refreshInstructions, pools, marginAccount }); | ||
const loanNoteAccount = await this.withGetOrCreateLoanPosition(instructionsInstructions, marginAccount); | ||
await this.withMarginBorrow({ | ||
instructions: postInstructions, | ||
instructions: instructionsInstructions, | ||
marginAccount, | ||
@@ -376,4 +390,13 @@ depositPosition, | ||
}); | ||
if (destination !== undefined) { | ||
await this.withWithdraw({ | ||
instructions: instructionsInstructions, | ||
marginAccount, | ||
source: depositPosition.address, | ||
destination, | ||
amount: lamports | ||
}); | ||
} | ||
try { | ||
return await (0, utils_1.sendAll)(marginAccount.provider, [preInstructons, (0, utils_1.chunks)(11, refreshInstructions), postInstructions]); | ||
return await (0, utils_1.sendAll)(marginAccount.provider, [(0, utils_1.chunks)(11, refreshInstructions), instructionsInstructions]); | ||
} | ||
@@ -432,6 +455,6 @@ catch (err) { | ||
/// `amount` - The amount to be repaid | ||
async marginRepay({ marginAccount, pools, amount }) { | ||
async marginRepay({ marginAccount, pools, source, amount }) { | ||
await marginAccount.refresh(); | ||
const deposit_position = await marginAccount.getOrCreatePosition(this.addresses.depositNoteMint); | ||
(0, chai_1.assert)(deposit_position); | ||
const depositPosition = await marginAccount.getOrCreatePosition(this.addresses.depositNoteMint); | ||
(0, chai_1.assert)(depositPosition); | ||
const refreshInstructions = []; | ||
@@ -441,7 +464,16 @@ const instructions = []; | ||
const loanNoteAccount = await this.withGetOrCreateLoanPosition(instructions, marginAccount); | ||
await this.makeMarginRepayInstruction({ | ||
if (source !== undefined) { | ||
await this.withDeposit({ | ||
instructions, | ||
marginAccount, | ||
source, | ||
destination: depositPosition.address, | ||
amount | ||
}); | ||
} | ||
await this.withMarginRepay({ | ||
instructions, | ||
marginAccount: marginAccount, | ||
deposit_account: deposit_position.address, | ||
loan_account: loanNoteAccount, | ||
depositPosition: depositPosition.address, | ||
loanPosition: loanNoteAccount, | ||
amount | ||
@@ -463,3 +495,3 @@ }); | ||
} | ||
async makeMarginRepayInstruction({ instructions, marginAccount, deposit_account, loan_account, amount }) { | ||
async withMarginRepay({ instructions, marginAccount, depositPosition, loanPosition, amount }) { | ||
await marginAccount.withAdapterInvoke({ | ||
@@ -470,3 +502,3 @@ instructions, | ||
adapterInstruction: await this.programs.marginPool.methods | ||
.marginRepay(amount.toRpcArg()) | ||
.marginRepay(poolAmount_1.PoolAmount.tokens(amount).toRpcArg()) | ||
.accounts({ | ||
@@ -477,4 +509,4 @@ marginAccount: marginAccount.address, | ||
depositNoteMint: this.addresses.depositNoteMint, | ||
loanAccount: loan_account, | ||
depositAccount: deposit_account, | ||
loanAccount: loanPosition, | ||
depositAccount: depositPosition, | ||
tokenProgram: spl_token_1.TOKEN_PROGRAM_ID | ||
@@ -492,4 +524,4 @@ }) | ||
/// `destination` - (Optional) The token account to send the withdrawn deposit | ||
async withdraw({ marginAccount, pools, amount, destination }) { | ||
// FIXME: can be getPosition | ||
async withdraw({ marginAccount, pools, amount, destination = token_1.TokenFormat.unwrappedSol }) { | ||
// FIXME: can source be calculated in withdraw? | ||
const { address: source } = await marginAccount.getOrCreatePosition(this.addresses.depositNoteMint); | ||
@@ -499,4 +531,2 @@ const preInstructions = []; | ||
const instructions = []; | ||
const postInstructions = []; | ||
let marginWithdrawDestination = destination !== null && destination !== void 0 ? destination : (await token_1.AssociatedToken.withCreateOrUnwrapIfNativeMint(preInstructions, postInstructions, marginAccount.provider, this.tokenMint)); | ||
await this.withMarginRefreshAllPositionPrices({ instructions: refreshInstructions, pools, marginAccount }); | ||
@@ -508,29 +538,41 @@ await marginAccount.withUpdateAllPositionBalances({ instructions: refreshInstructions }); | ||
source, | ||
destination: marginWithdrawDestination, | ||
destination, | ||
amount | ||
}); | ||
return await (0, utils_1.sendAll)(marginAccount.provider, [ | ||
preInstructions, | ||
(0, utils_1.chunks)(11, refreshInstructions), | ||
[...instructions, ...postInstructions] | ||
]); | ||
return await (0, utils_1.sendAll)(marginAccount.provider, [preInstructions, (0, utils_1.chunks)(11, refreshInstructions), instructions]); | ||
} | ||
async withWithdraw({ instructions, marginAccount, source, destination, amount }) { | ||
await marginAccount.withAdapterInvoke({ | ||
async withWithdraw({ instructions, marginAccount, source, destination = token_1.TokenFormat.unwrappedSol, amount }) { | ||
const provider = marginAccount.provider; | ||
const mint = this.tokenMint; | ||
destination = await token_1.AssociatedToken.withBeginTransferToDestination({ | ||
instructions, | ||
adapterProgram: this.programs.config.marginPoolProgramId, | ||
adapterMetadata: this.addresses.marginPoolAdapterMetadata, | ||
adapterInstruction: await this.programs.marginPool.methods | ||
.withdraw(amount.toRpcArg()) | ||
.accounts({ | ||
depositor: marginAccount.address, | ||
marginPool: this.address, | ||
vault: this.addresses.vault, | ||
depositNoteMint: this.addresses.depositNoteMint, | ||
source, | ||
destination, | ||
tokenProgram: spl_token_1.TOKEN_PROGRAM_ID | ||
}) | ||
.instruction() | ||
provider, | ||
mint, | ||
destination | ||
}); | ||
if (destination) { | ||
await marginAccount.withAdapterInvoke({ | ||
instructions, | ||
adapterProgram: this.programs.config.marginPoolProgramId, | ||
adapterMetadata: this.addresses.marginPoolAdapterMetadata, | ||
adapterInstruction: await this.programs.marginPool.methods | ||
.withdraw(amount.toRpcArg()) | ||
.accounts({ | ||
depositor: marginAccount.address, | ||
marginPool: this.address, | ||
vault: this.addresses.vault, | ||
depositNoteMint: this.addresses.depositNoteMint, | ||
source, | ||
destination, | ||
tokenProgram: spl_token_1.TOKEN_PROGRAM_ID | ||
}) | ||
.instruction() | ||
}); | ||
} | ||
token_1.AssociatedToken.withEndTransfer({ | ||
instructions, | ||
provider, | ||
mint, | ||
destination | ||
}); | ||
} | ||
@@ -537,0 +579,0 @@ async withRegisterLoan(instructions, marginAccount) { |
@@ -1,2 +0,1 @@ | ||
/// <reference types="bn.js" /> | ||
import { BN } from "@project-serum/anchor"; | ||
@@ -3,0 +2,0 @@ declare type PoolAmountKindTokens = { |
@@ -24,5 +24,5 @@ /// <reference types="node" /> | ||
getExposure(value: BN): BN; | ||
getCollateralValue(value: BN): BN; | ||
getRequiredCollateralValue(value: BN): BN; | ||
getCollateralValue(value: BN): any; | ||
getRequiredCollateralValue(value: BN): any; | ||
} | ||
//# sourceMappingURL=positionTokenMetadata.d.ts.map |
/// <reference types="node" /> | ||
/// <reference types="bn.js" /> | ||
import { BN } from "@project-serum/anchor"; | ||
@@ -4,0 +3,0 @@ import { Market as SerumMarket, OpenOrders } from "@project-serum/serum"; |
@@ -1,2 +0,1 @@ | ||
/// <reference types="bn.js" /> | ||
import { BN, Idl } from "@project-serum/anchor"; | ||
@@ -3,0 +2,0 @@ import { IdlTypeDef } from "@project-serum/anchor/dist/cjs/idl"; |
@@ -1,2 +0,1 @@ | ||
/// <reference types="bn.js" /> | ||
import { BN } from "@project-serum/anchor"; | ||
@@ -3,0 +2,0 @@ import { Account, Connection, PublicKey, Signer, TransactionInstruction } from "@solana/web3.js"; |
/// <reference types="node" /> | ||
/// <reference types="bn.js" /> | ||
import { BN, Address, AnchorProvider } from "@project-serum/anchor"; | ||
@@ -7,2 +6,9 @@ import { Mint, Account } from "@solana/spl-token"; | ||
import { TokenAmount } from "./tokenAmount"; | ||
export declare type TokenAddress = Address | TokenFormat; | ||
export declare enum TokenFormat { | ||
/** The users associated token account will be used, and sol will be unwrapped. */ | ||
unwrappedSol = 0, | ||
/** The users associated token account will be used, and sol will be wrapped. */ | ||
wrappedSol = 1 | ||
} | ||
export declare class AssociatedToken { | ||
@@ -40,2 +46,3 @@ address: PublicKey; | ||
static exists(connection: Connection, mint: Address, owner: Address): Promise<boolean>; | ||
static existsAux(connection: Connection, mint: Address, owner: Address, address: Address): Promise<boolean>; | ||
static loadAux(connection: Connection, address: Address, decimals: number): Promise<AssociatedToken>; | ||
@@ -128,3 +135,3 @@ static zero(mint: Address, owner: Address, decimals: number): AssociatedToken; | ||
/** | ||
* If the native wrapped token account does not exist, add instruction to create the token account. If ATA exists, do nothing. | ||
* If the token account does not exist, add instructions to create and initialize the token account. If the account exists do nothing. | ||
* @static | ||
@@ -134,6 +141,7 @@ * @param {TransactionInstruction[]} instructions | ||
* @param {Address} owner | ||
* @param {Address} mint | ||
* @returns {Promise<PublicKey>} returns the public key of the token account | ||
* @memberof AssociatedToken | ||
*/ | ||
static withCreateNative(instructions: TransactionInstruction[], provider: AnchorProvider, owner: Address): Promise<PublicKey>; | ||
static withCreateAux(instructions: TransactionInstruction[], provider: AnchorProvider, owner: Address, mint: Address, address: Address): Promise<void>; | ||
/** | ||
@@ -170,3 +178,3 @@ * Add close associated token account IX | ||
*/ | ||
static withWrapIfNativeMint(instructions: TransactionInstruction[], provider: AnchorProvider, mint: Address, tokenAccountOrNative: Address, amount: BN): Promise<PublicKey>; | ||
static withWrapIfNativeMint(instructions: TransactionInstruction[], provider: AnchorProvider, mint: Address, amount: BN): Promise<PublicKey>; | ||
/** | ||
@@ -180,5 +188,5 @@ * Unwraps all SOL if the mint is native and the tokenAccount is the owner | ||
*/ | ||
static withUnwrapIfNative(instructions: TransactionInstruction[], owner: Address, mint: Address, tokenAccountOrNative: Address): void; | ||
static withUnwrapIfNative(instructions: TransactionInstruction[], owner: Address, mint: Address): void; | ||
/** | ||
* Create the associated token account. Funds it if natve. | ||
* Create the associated token account. Funds it if native. | ||
* | ||
@@ -189,6 +197,6 @@ * @static | ||
* @param {Address} mint | ||
* @param {BN} initialAmount | ||
* @param {BN} wrapAmount | ||
* @memberof AssociatedToken | ||
*/ | ||
static withCreateOrWrapIfNativeMint(instructions: TransactionInstruction[], provider: AnchorProvider, mint: Address, initialAmount: BN): Promise<PublicKey>; | ||
static withCreateOrWrapIfNativeMint(instructions: TransactionInstruction[], provider: AnchorProvider, mint: Address, wrapAmount: BN): Promise<PublicKey>; | ||
/** | ||
@@ -206,2 +214,22 @@ * Create the associated token account as a pre-instruction. | ||
static withCreateOrUnwrapIfNativeMint(preInstructions: TransactionInstruction[], postInstructions: TransactionInstruction[], provider: AnchorProvider, mint: Address): Promise<PublicKey>; | ||
static withBeginTransferFromSource({ instructions, provider, mint, amount, source }: { | ||
instructions: TransactionInstruction[]; | ||
provider: AnchorProvider; | ||
mint: Address; | ||
amount: BN; | ||
source: Address | TokenFormat; | ||
}): Promise<PublicKey>; | ||
static withBeginTransferToDestination({ instructions, provider, mint, destination }: { | ||
instructions: TransactionInstruction[]; | ||
provider: AnchorProvider; | ||
mint: Address; | ||
destination: Address | TokenFormat; | ||
}): Promise<PublicKey>; | ||
/** Ends the transfer by unwraps the token if it is the native mint. */ | ||
static withEndTransfer({ instructions, provider, mint, destination }: { | ||
instructions: TransactionInstruction[]; | ||
provider: AnchorProvider; | ||
mint: Address; | ||
destination: Address | TokenFormat; | ||
}): void; | ||
} | ||
@@ -208,0 +236,0 @@ /** |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.numberToBigInt = exports.bigIntToNumber = exports.bigIntToBn = exports.bnToNumber = exports.numberToBn = exports.AssociatedToken = void 0; | ||
exports.numberToBigInt = exports.bigIntToNumber = exports.bigIntToBn = exports.bnToNumber = exports.numberToBn = exports.AssociatedToken = exports.TokenFormat = void 0; | ||
const anchor_1 = require("@project-serum/anchor"); | ||
@@ -11,2 +11,9 @@ const token_instructions_1 = require("@project-serum/serum/lib/token-instructions"); | ||
const tokenAmount_1 = require("./tokenAmount"); | ||
var TokenFormat; | ||
(function (TokenFormat) { | ||
/** The users associated token account will be used, and sol will be unwrapped. */ | ||
TokenFormat[TokenFormat["unwrappedSol"] = 0] = "unwrappedSol"; | ||
/** The users associated token account will be used, and sol will be wrapped. */ | ||
TokenFormat[TokenFormat["wrappedSol"] = 1] = "wrappedSol"; | ||
})(TokenFormat = exports.TokenFormat || (exports.TokenFormat = {})); | ||
class AssociatedToken { | ||
@@ -56,3 +63,3 @@ /** | ||
if (token.info && !token.info.owner.equals(ownerAddress)) { | ||
throw new Error("Unexpected owner of the associated token"); | ||
throw new spl_token_1.TokenInvalidOwnerError("The owner of a token account doesn't match the expected owner"); | ||
} | ||
@@ -65,5 +72,25 @@ return token; | ||
const address = this.derive(mintAddress, ownerAddress); | ||
const account = await connection.getAccountInfo(address); | ||
return !!account; | ||
return await AssociatedToken.existsAux(connection, mint, owner, address); | ||
} | ||
static async existsAux(connection, mint, owner, address) { | ||
const mintAddress = (0, anchor_1.translateAddress)(mint); | ||
const ownerAddress = (0, anchor_1.translateAddress)(owner); | ||
const tokenAddress = (0, anchor_1.translateAddress)(address); | ||
const info = await connection.getAccountInfo(tokenAddress); | ||
if (info) { | ||
const fakeDecimals = 0; | ||
const account = this.decodeAccount(info, address, fakeDecimals); | ||
if (!account.info) { | ||
throw new spl_token_1.TokenInvalidAccountSizeError(); | ||
} | ||
if (!account.info.owner.equals(ownerAddress)) { | ||
throw new spl_token_1.TokenInvalidOwnerError("The owner of a token account doesn't match the expected owner"); | ||
} | ||
if (!account.info.mint.equals(mintAddress)) { | ||
throw new spl_token_1.TokenInvalidMintError("The mint of a token account doesn't match the expected mint"); | ||
} | ||
return true; | ||
} | ||
return false; | ||
} | ||
static async loadAux(connection, address, decimals) { | ||
@@ -337,3 +364,3 @@ const pubkey = (0, anchor_1.translateAddress)(address); | ||
/** | ||
* If the native wrapped token account does not exist, add instruction to create the token account. If ATA exists, do nothing. | ||
* If the token account does not exist, add instructions to create and initialize the token account. If the account exists do nothing. | ||
* @static | ||
@@ -343,13 +370,22 @@ * @param {TransactionInstruction[]} instructions | ||
* @param {Address} owner | ||
* @param {Address} mint | ||
* @returns {Promise<PublicKey>} returns the public key of the token account | ||
* @memberof AssociatedToken | ||
*/ | ||
static async withCreateNative(instructions, provider, owner) { | ||
static async withCreateAux(instructions, provider, owner, mint, address) { | ||
const ownerAddress = (0, anchor_1.translateAddress)(owner); | ||
const tokenAddress = this.derive(spl_token_1.NATIVE_MINT, ownerAddress); | ||
if (!(await AssociatedToken.exists(provider.connection, spl_token_1.NATIVE_MINT, ownerAddress))) { | ||
const ix = (0, spl_token_1.createAssociatedTokenAccountInstruction)(provider.wallet.publicKey, tokenAddress, ownerAddress, spl_token_1.NATIVE_MINT); | ||
instructions.push(ix); | ||
const mintAddress = (0, anchor_1.translateAddress)(mint); | ||
const tokenAddress = (0, anchor_1.translateAddress)(address); | ||
if (!(await AssociatedToken.existsAux(provider.connection, mintAddress, ownerAddress, address))) { | ||
let rent = await (0, spl_token_1.getMinimumBalanceForRentExemptAccount)(provider.connection); | ||
let createIx = web3_js_1.SystemProgram.createAccount({ | ||
fromPubkey: provider.wallet.publicKey, | ||
newAccountPubkey: tokenAddress, | ||
lamports: rent, | ||
space: spl_token_1.ACCOUNT_SIZE, | ||
programId: token_instructions_1.TOKEN_PROGRAM_ID | ||
}); | ||
let initIx = (0, spl_token_1.createInitializeAccountInstruction)(tokenAddress, mintAddress, ownerAddress); | ||
instructions.push(createIx, initIx); | ||
} | ||
return tokenAddress; | ||
} | ||
@@ -410,11 +446,9 @@ /** | ||
*/ | ||
static async withWrapIfNativeMint(instructions, provider, mint, tokenAccountOrNative, amount) { | ||
const owner = provider.wallet.publicKey; | ||
static async withWrapIfNativeMint(instructions, provider, mint, amount) { | ||
const mintPubkey = (0, anchor_1.translateAddress)(mint); | ||
const tokenAccountOrNativePubkey = (0, anchor_1.translateAddress)(tokenAccountOrNative); | ||
//only run if mint is wrapped sol mint, and the token account is actually the native wallet | ||
if (this.isNative(owner, mintPubkey, tokenAccountOrNativePubkey)) { | ||
//only run if mint is wrapped sol mint | ||
if (mintPubkey.equals(spl_token_1.NATIVE_MINT)) { | ||
return this.withWrapNative(instructions, provider, amount); | ||
} | ||
return tokenAccountOrNativePubkey; | ||
return AssociatedToken.derive(mint, provider.wallet.publicKey); | ||
} | ||
@@ -429,7 +463,6 @@ /** | ||
*/ | ||
static withUnwrapIfNative(instructions, owner, mint, tokenAccountOrNative) { | ||
static withUnwrapIfNative(instructions, owner, mint) { | ||
const ownerPubkey = (0, anchor_1.translateAddress)(owner); | ||
const mintPubkey = (0, anchor_1.translateAddress)(mint); | ||
const tokenAccountOrNativePubkey = (0, anchor_1.translateAddress)(tokenAccountOrNative); | ||
if (this.isNative(ownerPubkey, mintPubkey, tokenAccountOrNativePubkey)) { | ||
if (mintPubkey.equals(spl_token_1.NATIVE_MINT)) { | ||
//add close account IX | ||
@@ -440,3 +473,3 @@ this.withUnwrapNative(instructions, owner); | ||
/** | ||
* Create the associated token account. Funds it if natve. | ||
* Create the associated token account. Funds it if native. | ||
* | ||
@@ -447,6 +480,6 @@ * @static | ||
* @param {Address} mint | ||
* @param {BN} initialAmount | ||
* @param {BN} wrapAmount | ||
* @memberof AssociatedToken | ||
*/ | ||
static async withCreateOrWrapIfNativeMint(instructions, provider, mint, initialAmount) { | ||
static async withCreateOrWrapIfNativeMint(instructions, provider, mint, wrapAmount) { | ||
const owner = provider.wallet.publicKey; | ||
@@ -456,3 +489,3 @@ const mintPubkey = (0, anchor_1.translateAddress)(mint); | ||
// Only run if mint is wrapped sol mint. Create the wrapped sol account and return its pubkey | ||
return await this.withWrapNative(instructions, provider, initialAmount); | ||
return await this.withWrapNative(instructions, provider, wrapAmount); | ||
} | ||
@@ -485,2 +518,55 @@ else { | ||
} | ||
static async withBeginTransferFromSource({ instructions, provider, mint, amount, source = TokenFormat.unwrappedSol }) { | ||
let sourceAddress; | ||
if (source instanceof web3_js_1.PublicKey || typeof source === "string") { | ||
sourceAddress = (0, anchor_1.translateAddress)(source); | ||
} | ||
let owner = provider.wallet.publicKey; | ||
let isSourceOwner = sourceAddress && sourceAddress.equals(owner); | ||
let isSourceAssociatedAddress = sourceAddress && AssociatedToken.derive(mint, owner).equals(sourceAddress); | ||
if (source === TokenFormat.unwrappedSol || isSourceOwner || isSourceAssociatedAddress) { | ||
return await AssociatedToken.withCreateOrWrapIfNativeMint(instructions, provider, mint, amount); | ||
} | ||
else if (source === TokenFormat.wrappedSol) { | ||
return await AssociatedToken.withCreate(instructions, provider, owner, mint); | ||
} | ||
else if (sourceAddress) { | ||
await AssociatedToken.withCreateAux(instructions, provider, owner, mint, sourceAddress); | ||
return sourceAddress; | ||
} | ||
throw new Error("Unexpected argument 'source' or there are multiple versions of @solana/web3.js PublicKey installed"); | ||
} | ||
static async withBeginTransferToDestination({ instructions, provider, mint, destination = TokenFormat.unwrappedSol }) { | ||
let destinationAddress; | ||
if (destination instanceof web3_js_1.PublicKey || typeof destination === "string") { | ||
destinationAddress = (0, anchor_1.translateAddress)(destination); | ||
} | ||
let owner = provider.wallet.publicKey; | ||
let isDestinationOwner = destinationAddress && destinationAddress.equals(owner); | ||
let isDestinationAssociatedAddress = destinationAddress && AssociatedToken.derive(mint, owner).equals(destinationAddress); | ||
if (destination === TokenFormat.wrappedSol || | ||
destination === TokenFormat.unwrappedSol || | ||
isDestinationOwner || | ||
isDestinationAssociatedAddress) { | ||
return await AssociatedToken.withCreate(instructions, provider, owner, mint); | ||
} | ||
else if (destinationAddress) { | ||
await AssociatedToken.withCreateAux(instructions, provider, owner, mint, destinationAddress); | ||
return destinationAddress; | ||
} | ||
throw new Error("Unexpected argument 'destination' or there are multiple versions of @solana/web3.js PublicKey installed"); | ||
} | ||
/** Ends the transfer by unwraps the token if it is the native mint. */ | ||
static withEndTransfer({ instructions, provider, mint, destination = TokenFormat.unwrappedSol }) { | ||
let destinationAddress; | ||
if (destination instanceof web3_js_1.PublicKey || typeof destination === "string") { | ||
destinationAddress = (0, anchor_1.translateAddress)(destination); | ||
} | ||
let owner = provider.wallet.publicKey; | ||
let isDestinationOwner = destinationAddress && destinationAddress.equals(owner); | ||
if ((0, anchor_1.translateAddress)(mint).equals(spl_token_1.NATIVE_MINT) && | ||
(destination === TokenFormat.unwrappedSol || isDestinationOwner)) { | ||
AssociatedToken.withUnwrapNative(instructions, owner); | ||
} | ||
} | ||
} | ||
@@ -487,0 +573,0 @@ exports.AssociatedToken = AssociatedToken; |
@@ -1,2 +0,1 @@ | ||
/// <reference types="bn.js" /> | ||
import { BN } from "@project-serum/anchor"; | ||
@@ -3,0 +2,0 @@ import { Account, Mint } from "@solana/spl-token"; |
@@ -1,2 +0,1 @@ | ||
/// <reference types="bn.js" /> | ||
import { PublicKey } from "@solana/web3.js"; | ||
@@ -3,0 +2,0 @@ import { Address, BN, AnchorProvider } from "@project-serum/anchor"; |
@@ -1,11 +0,10 @@ | ||
/// <reference types="bn.js" /> | ||
import { BN } from "@project-serum/anchor"; | ||
export declare class Number128 { | ||
static readonly PRECISION = 10; | ||
static readonly ONE: BN; | ||
static readonly ZERO: BN; | ||
static readonly MAX: BN; | ||
static readonly U64_MAX: BN; | ||
static readonly ONE: any; | ||
static readonly ZERO: any; | ||
static readonly MAX: any; | ||
static readonly U64_MAX: any; | ||
static readonly BPS_EXPONENT = -4; | ||
static readonly POWERS_OF_TEN: BN[]; | ||
static readonly POWERS_OF_TEN: any[]; | ||
private constructor(); | ||
@@ -16,7 +15,7 @@ /** Removes the fractional component from the number.*/ | ||
static asU64(value: BN, exponent: number): BN; | ||
static from(value: BN): BN; | ||
static fromDecimal(value: BN, exponent: number): BN; | ||
static from(value: BN): any; | ||
static fromDecimal(value: BN, exponent: number): any; | ||
/** Convert from basis points */ | ||
static fromBps(basisPoints: BN): BN; | ||
static fromBps(basisPoints: BN): any; | ||
} | ||
//# sourceMappingURL=number128.d.ts.map |
@@ -1,9 +0,8 @@ | ||
/// <reference types="bn.js" /> | ||
import { BN } from "@project-serum/anchor"; | ||
export declare class Number192 { | ||
static readonly PRECISION = 15; | ||
static readonly ONE: BN; | ||
static readonly ZERO: BN; | ||
static readonly U64_MAX: BN; | ||
static readonly POWERS_OF_TEN: BN[]; | ||
static readonly ONE: any; | ||
static readonly ZERO: any; | ||
static readonly U64_MAX: any; | ||
static readonly POWERS_OF_TEN: any[]; | ||
private constructor(); | ||
@@ -15,5 +14,5 @@ /** Removes the fractional component from the number. */ | ||
static asU64Rounded(value: BN, exponent: number): BN; | ||
static from(value: BN): BN; | ||
static fromDecimal(value: BN, exponent: number): BN; | ||
static from(value: BN): any; | ||
static fromDecimal(value: BN, exponent: number): any; | ||
} | ||
//# sourceMappingURL=number192.d.ts.map |
@@ -1,4 +0,2 @@ | ||
/// <reference types="bn.js" /> | ||
import { BN } from "@project-serum/anchor"; | ||
export declare function getTimestamp(): BN; | ||
export declare function getTimestamp(): any; | ||
//# sourceMappingURL=timestamp.d.ts.map |
@@ -44,4 +44,4 @@ import { PublicKey } from "@solana/web3.js"; | ||
calculateValue(): void; | ||
collateralValue(): BN; | ||
requiredCollateralValue(): BN; | ||
collateralValue(): any; | ||
requiredCollateralValue(): any; | ||
setBalance(balance: BN): void; | ||
@@ -48,0 +48,0 @@ setPrice(price: PriceInfo): void; |
@@ -1,2 +0,1 @@ | ||
/// <reference types="bn.js" /> | ||
import { Address, AnchorProvider, BN } from "@project-serum/anchor"; | ||
@@ -3,0 +2,0 @@ import { GetProgramAccountsFilter, PublicKey, TransactionInstruction, TransactionSignature } from "@solana/web3.js"; |
@@ -1,2 +0,1 @@ | ||
/// <reference types="bn.js" /> | ||
import { Address, BN } from "@project-serum/anchor"; | ||
@@ -6,3 +5,3 @@ import { PriceData } from "@pythnetwork/client"; | ||
import { PublicKey, TransactionInstruction } from "@solana/web3.js"; | ||
import { AssociatedToken } from "../../token"; | ||
import { AssociatedToken, TokenAddress } from "../../token"; | ||
import { TokenAmount } from "../../token/tokenAmount"; | ||
@@ -90,4 +89,4 @@ import { MarginAccount } from "../marginAccount"; | ||
calculatePrices(pythPrice: PriceData | undefined): PriceResult; | ||
depositNoteExchangeRate(): BN; | ||
loanNoteExchangeRate(): BN; | ||
depositNoteExchangeRate(): any; | ||
loanNoteExchangeRate(): any; | ||
/** | ||
@@ -145,15 +144,16 @@ * Linear interpolation between (x0, y0) and (x1, y1) | ||
amount: BN; | ||
source?: Address; | ||
source?: TokenAddress; | ||
}): Promise<string>; | ||
withDeposit({ instructions, depositor, source, destination, amount }: { | ||
withDeposit({ instructions, marginAccount, source, destination, amount }: { | ||
instructions: TransactionInstruction[]; | ||
depositor: Address; | ||
source: Address; | ||
marginAccount: MarginAccount; | ||
source?: TokenAddress; | ||
destination: Address; | ||
amount: BN; | ||
}): Promise<void>; | ||
marginBorrow({ marginAccount, pools, amount }: { | ||
marginBorrow({ marginAccount, pools, amount, destination }: { | ||
marginAccount: MarginAccount; | ||
pools: Pool[]; | ||
amount: BN; | ||
destination?: TokenAddress; | ||
}): Promise<void>; | ||
@@ -168,13 +168,14 @@ withGetOrCreateLoanPosition(instructions: TransactionInstruction[], marginAccount: MarginAccount): Promise<Address>; | ||
}): Promise<void>; | ||
marginRepay({ marginAccount, pools, amount }: { | ||
marginRepay({ marginAccount, pools, source, amount }: { | ||
marginAccount: MarginAccount; | ||
pools: Pool[]; | ||
amount: PoolAmount; | ||
source?: TokenAddress; | ||
amount: BN; | ||
}): Promise<void>; | ||
makeMarginRepayInstruction({ instructions, marginAccount, deposit_account, loan_account, amount }: { | ||
withMarginRepay({ instructions, marginAccount, depositPosition, loanPosition, amount }: { | ||
instructions: TransactionInstruction[]; | ||
marginAccount: MarginAccount; | ||
deposit_account: Address; | ||
loan_account: Address; | ||
amount: PoolAmount; | ||
depositPosition: Address; | ||
loanPosition: Address; | ||
amount: BN; | ||
}): Promise<void>; | ||
@@ -185,3 +186,3 @@ withdraw({ marginAccount, pools, amount, destination }: { | ||
amount: PoolAmount; | ||
destination?: Address; | ||
destination?: TokenAddress; | ||
}): Promise<void>; | ||
@@ -191,4 +192,4 @@ withWithdraw({ instructions, marginAccount, source, destination, amount }: { | ||
marginAccount: MarginAccount; | ||
source: Address; | ||
destination: Address; | ||
source: PublicKey; | ||
destination?: TokenAddress; | ||
amount: PoolAmount; | ||
@@ -195,0 +196,0 @@ }): Promise<void>; |
@@ -6,3 +6,3 @@ import { BN, translateAddress } from "@project-serum/anchor"; | ||
import { assert } from "chai"; | ||
import { AssociatedToken, bigIntToBn } from "../../token"; | ||
import { AssociatedToken, bigIntToBn, TokenFormat } from "../../token"; | ||
import { TokenAmount } from "../../token/tokenAmount"; | ||
@@ -318,3 +318,3 @@ import { PoolAmount } from "./poolAmount"; | ||
*/ | ||
async deposit({ marginAccount, amount, source }) { | ||
async deposit({ marginAccount, amount, source = TokenFormat.unwrappedSol }) { | ||
assert(marginAccount); | ||
@@ -328,6 +328,5 @@ assert(amount); | ||
const instructions = []; | ||
source !== null && source !== void 0 ? source : (source = await AssociatedToken.withCreateOrWrapIfNativeMint(instructions, marginAccount.provider, this.tokenMint, amount)); | ||
await this.withDeposit({ | ||
instructions: instructions, | ||
depositor: marginAccount.owner, | ||
marginAccount, | ||
source, | ||
@@ -340,3 +339,12 @@ destination: position.address, | ||
} | ||
async withDeposit({ instructions, depositor, source, destination, amount }) { | ||
async withDeposit({ instructions, marginAccount, source = TokenFormat.unwrappedSol, destination, amount }) { | ||
const provider = marginAccount.provider; | ||
const mint = this.tokenMint; | ||
source = await AssociatedToken.withBeginTransferFromSource({ | ||
instructions, | ||
provider, | ||
mint, | ||
amount, | ||
source | ||
}); | ||
const ix = await this.programs.marginPool.methods | ||
@@ -348,3 +356,3 @@ .deposit(amount) | ||
depositNoteMint: this.addresses.depositNoteMint, | ||
depositor, | ||
depositor: marginAccount.owner, | ||
source, | ||
@@ -356,14 +364,20 @@ destination, | ||
instructions.push(ix); | ||
AssociatedToken.withEndTransfer({ | ||
instructions, | ||
provider, | ||
mint, | ||
destination | ||
}); | ||
} | ||
async marginBorrow({ marginAccount, pools, amount }) { | ||
async marginBorrow({ marginAccount, pools, amount, destination }) { | ||
const lamports = PoolAmount.tokens(amount); | ||
await marginAccount.refresh(); | ||
const preInstructons = []; | ||
const refreshInstructions = []; | ||
const postInstructions = []; | ||
const instructionsInstructions = []; | ||
const depositPosition = await marginAccount.getOrCreatePosition(this.addresses.depositNoteMint); | ||
assert(depositPosition); | ||
const loanNoteAccount = await this.withGetOrCreateLoanPosition(preInstructons, marginAccount); | ||
await this.withMarginRefreshAllPositionPrices({ instructions: refreshInstructions, pools, marginAccount }); | ||
const loanNoteAccount = await this.withGetOrCreateLoanPosition(instructionsInstructions, marginAccount); | ||
await this.withMarginBorrow({ | ||
instructions: postInstructions, | ||
instructions: instructionsInstructions, | ||
marginAccount, | ||
@@ -374,4 +388,13 @@ depositPosition, | ||
}); | ||
if (destination !== undefined) { | ||
await this.withWithdraw({ | ||
instructions: instructionsInstructions, | ||
marginAccount, | ||
source: depositPosition.address, | ||
destination, | ||
amount: lamports | ||
}); | ||
} | ||
try { | ||
return await sendAll(marginAccount.provider, [preInstructons, chunks(11, refreshInstructions), postInstructions]); | ||
return await sendAll(marginAccount.provider, [chunks(11, refreshInstructions), instructionsInstructions]); | ||
} | ||
@@ -430,6 +453,6 @@ catch (err) { | ||
/// `amount` - The amount to be repaid | ||
async marginRepay({ marginAccount, pools, amount }) { | ||
async marginRepay({ marginAccount, pools, source, amount }) { | ||
await marginAccount.refresh(); | ||
const deposit_position = await marginAccount.getOrCreatePosition(this.addresses.depositNoteMint); | ||
assert(deposit_position); | ||
const depositPosition = await marginAccount.getOrCreatePosition(this.addresses.depositNoteMint); | ||
assert(depositPosition); | ||
const refreshInstructions = []; | ||
@@ -439,7 +462,16 @@ const instructions = []; | ||
const loanNoteAccount = await this.withGetOrCreateLoanPosition(instructions, marginAccount); | ||
await this.makeMarginRepayInstruction({ | ||
if (source !== undefined) { | ||
await this.withDeposit({ | ||
instructions, | ||
marginAccount, | ||
source, | ||
destination: depositPosition.address, | ||
amount | ||
}); | ||
} | ||
await this.withMarginRepay({ | ||
instructions, | ||
marginAccount: marginAccount, | ||
deposit_account: deposit_position.address, | ||
loan_account: loanNoteAccount, | ||
depositPosition: depositPosition.address, | ||
loanPosition: loanNoteAccount, | ||
amount | ||
@@ -461,3 +493,3 @@ }); | ||
} | ||
async makeMarginRepayInstruction({ instructions, marginAccount, deposit_account, loan_account, amount }) { | ||
async withMarginRepay({ instructions, marginAccount, depositPosition, loanPosition, amount }) { | ||
await marginAccount.withAdapterInvoke({ | ||
@@ -468,3 +500,3 @@ instructions, | ||
adapterInstruction: await this.programs.marginPool.methods | ||
.marginRepay(amount.toRpcArg()) | ||
.marginRepay(PoolAmount.tokens(amount).toRpcArg()) | ||
.accounts({ | ||
@@ -475,4 +507,4 @@ marginAccount: marginAccount.address, | ||
depositNoteMint: this.addresses.depositNoteMint, | ||
loanAccount: loan_account, | ||
depositAccount: deposit_account, | ||
loanAccount: loanPosition, | ||
depositAccount: depositPosition, | ||
tokenProgram: TOKEN_PROGRAM_ID | ||
@@ -490,4 +522,4 @@ }) | ||
/// `destination` - (Optional) The token account to send the withdrawn deposit | ||
async withdraw({ marginAccount, pools, amount, destination }) { | ||
// FIXME: can be getPosition | ||
async withdraw({ marginAccount, pools, amount, destination = TokenFormat.unwrappedSol }) { | ||
// FIXME: can source be calculated in withdraw? | ||
const { address: source } = await marginAccount.getOrCreatePosition(this.addresses.depositNoteMint); | ||
@@ -497,4 +529,2 @@ const preInstructions = []; | ||
const instructions = []; | ||
const postInstructions = []; | ||
let marginWithdrawDestination = destination !== null && destination !== void 0 ? destination : (await AssociatedToken.withCreateOrUnwrapIfNativeMint(preInstructions, postInstructions, marginAccount.provider, this.tokenMint)); | ||
await this.withMarginRefreshAllPositionPrices({ instructions: refreshInstructions, pools, marginAccount }); | ||
@@ -506,29 +536,41 @@ await marginAccount.withUpdateAllPositionBalances({ instructions: refreshInstructions }); | ||
source, | ||
destination: marginWithdrawDestination, | ||
destination, | ||
amount | ||
}); | ||
return await sendAll(marginAccount.provider, [ | ||
preInstructions, | ||
chunks(11, refreshInstructions), | ||
[...instructions, ...postInstructions] | ||
]); | ||
return await sendAll(marginAccount.provider, [preInstructions, chunks(11, refreshInstructions), instructions]); | ||
} | ||
async withWithdraw({ instructions, marginAccount, source, destination, amount }) { | ||
await marginAccount.withAdapterInvoke({ | ||
async withWithdraw({ instructions, marginAccount, source, destination = TokenFormat.unwrappedSol, amount }) { | ||
const provider = marginAccount.provider; | ||
const mint = this.tokenMint; | ||
destination = await AssociatedToken.withBeginTransferToDestination({ | ||
instructions, | ||
adapterProgram: this.programs.config.marginPoolProgramId, | ||
adapterMetadata: this.addresses.marginPoolAdapterMetadata, | ||
adapterInstruction: await this.programs.marginPool.methods | ||
.withdraw(amount.toRpcArg()) | ||
.accounts({ | ||
depositor: marginAccount.address, | ||
marginPool: this.address, | ||
vault: this.addresses.vault, | ||
depositNoteMint: this.addresses.depositNoteMint, | ||
source, | ||
destination, | ||
tokenProgram: TOKEN_PROGRAM_ID | ||
}) | ||
.instruction() | ||
provider, | ||
mint, | ||
destination | ||
}); | ||
if (destination) { | ||
await marginAccount.withAdapterInvoke({ | ||
instructions, | ||
adapterProgram: this.programs.config.marginPoolProgramId, | ||
adapterMetadata: this.addresses.marginPoolAdapterMetadata, | ||
adapterInstruction: await this.programs.marginPool.methods | ||
.withdraw(amount.toRpcArg()) | ||
.accounts({ | ||
depositor: marginAccount.address, | ||
marginPool: this.address, | ||
vault: this.addresses.vault, | ||
depositNoteMint: this.addresses.depositNoteMint, | ||
source, | ||
destination, | ||
tokenProgram: TOKEN_PROGRAM_ID | ||
}) | ||
.instruction() | ||
}); | ||
} | ||
AssociatedToken.withEndTransfer({ | ||
instructions, | ||
provider, | ||
mint, | ||
destination | ||
}); | ||
} | ||
@@ -535,0 +577,0 @@ async withRegisterLoan(instructions, marginAccount) { |
@@ -1,2 +0,1 @@ | ||
/// <reference types="bn.js" /> | ||
import { BN } from "@project-serum/anchor"; | ||
@@ -3,0 +2,0 @@ declare type PoolAmountKindTokens = { |
@@ -24,5 +24,5 @@ /// <reference types="node" /> | ||
getExposure(value: BN): BN; | ||
getCollateralValue(value: BN): BN; | ||
getRequiredCollateralValue(value: BN): BN; | ||
getCollateralValue(value: BN): any; | ||
getRequiredCollateralValue(value: BN): any; | ||
} | ||
//# sourceMappingURL=positionTokenMetadata.d.ts.map |
/// <reference types="node" /> | ||
/// <reference types="bn.js" /> | ||
import { BN } from "@project-serum/anchor"; | ||
@@ -4,0 +3,0 @@ import { Market as SerumMarket, OpenOrders } from "@project-serum/serum"; |
@@ -1,2 +0,1 @@ | ||
/// <reference types="bn.js" /> | ||
import { BN, Idl } from "@project-serum/anchor"; | ||
@@ -3,0 +2,0 @@ import { IdlTypeDef } from "@project-serum/anchor/dist/cjs/idl"; |
@@ -1,2 +0,1 @@ | ||
/// <reference types="bn.js" /> | ||
import { BN } from "@project-serum/anchor"; | ||
@@ -3,0 +2,0 @@ import { Account, Connection, PublicKey, Signer, TransactionInstruction } from "@solana/web3.js"; |
/// <reference types="node" /> | ||
/// <reference types="bn.js" /> | ||
import { BN, Address, AnchorProvider } from "@project-serum/anchor"; | ||
@@ -7,2 +6,9 @@ import { Mint, Account } from "@solana/spl-token"; | ||
import { TokenAmount } from "./tokenAmount"; | ||
export declare type TokenAddress = Address | TokenFormat; | ||
export declare enum TokenFormat { | ||
/** The users associated token account will be used, and sol will be unwrapped. */ | ||
unwrappedSol = 0, | ||
/** The users associated token account will be used, and sol will be wrapped. */ | ||
wrappedSol = 1 | ||
} | ||
export declare class AssociatedToken { | ||
@@ -40,2 +46,3 @@ address: PublicKey; | ||
static exists(connection: Connection, mint: Address, owner: Address): Promise<boolean>; | ||
static existsAux(connection: Connection, mint: Address, owner: Address, address: Address): Promise<boolean>; | ||
static loadAux(connection: Connection, address: Address, decimals: number): Promise<AssociatedToken>; | ||
@@ -128,3 +135,3 @@ static zero(mint: Address, owner: Address, decimals: number): AssociatedToken; | ||
/** | ||
* If the native wrapped token account does not exist, add instruction to create the token account. If ATA exists, do nothing. | ||
* If the token account does not exist, add instructions to create and initialize the token account. If the account exists do nothing. | ||
* @static | ||
@@ -134,6 +141,7 @@ * @param {TransactionInstruction[]} instructions | ||
* @param {Address} owner | ||
* @param {Address} mint | ||
* @returns {Promise<PublicKey>} returns the public key of the token account | ||
* @memberof AssociatedToken | ||
*/ | ||
static withCreateNative(instructions: TransactionInstruction[], provider: AnchorProvider, owner: Address): Promise<PublicKey>; | ||
static withCreateAux(instructions: TransactionInstruction[], provider: AnchorProvider, owner: Address, mint: Address, address: Address): Promise<void>; | ||
/** | ||
@@ -170,3 +178,3 @@ * Add close associated token account IX | ||
*/ | ||
static withWrapIfNativeMint(instructions: TransactionInstruction[], provider: AnchorProvider, mint: Address, tokenAccountOrNative: Address, amount: BN): Promise<PublicKey>; | ||
static withWrapIfNativeMint(instructions: TransactionInstruction[], provider: AnchorProvider, mint: Address, amount: BN): Promise<PublicKey>; | ||
/** | ||
@@ -180,5 +188,5 @@ * Unwraps all SOL if the mint is native and the tokenAccount is the owner | ||
*/ | ||
static withUnwrapIfNative(instructions: TransactionInstruction[], owner: Address, mint: Address, tokenAccountOrNative: Address): void; | ||
static withUnwrapIfNative(instructions: TransactionInstruction[], owner: Address, mint: Address): void; | ||
/** | ||
* Create the associated token account. Funds it if natve. | ||
* Create the associated token account. Funds it if native. | ||
* | ||
@@ -189,6 +197,6 @@ * @static | ||
* @param {Address} mint | ||
* @param {BN} initialAmount | ||
* @param {BN} wrapAmount | ||
* @memberof AssociatedToken | ||
*/ | ||
static withCreateOrWrapIfNativeMint(instructions: TransactionInstruction[], provider: AnchorProvider, mint: Address, initialAmount: BN): Promise<PublicKey>; | ||
static withCreateOrWrapIfNativeMint(instructions: TransactionInstruction[], provider: AnchorProvider, mint: Address, wrapAmount: BN): Promise<PublicKey>; | ||
/** | ||
@@ -206,2 +214,22 @@ * Create the associated token account as a pre-instruction. | ||
static withCreateOrUnwrapIfNativeMint(preInstructions: TransactionInstruction[], postInstructions: TransactionInstruction[], provider: AnchorProvider, mint: Address): Promise<PublicKey>; | ||
static withBeginTransferFromSource({ instructions, provider, mint, amount, source }: { | ||
instructions: TransactionInstruction[]; | ||
provider: AnchorProvider; | ||
mint: Address; | ||
amount: BN; | ||
source: Address | TokenFormat; | ||
}): Promise<PublicKey>; | ||
static withBeginTransferToDestination({ instructions, provider, mint, destination }: { | ||
instructions: TransactionInstruction[]; | ||
provider: AnchorProvider; | ||
mint: Address; | ||
destination: Address | TokenFormat; | ||
}): Promise<PublicKey>; | ||
/** Ends the transfer by unwraps the token if it is the native mint. */ | ||
static withEndTransfer({ instructions, provider, mint, destination }: { | ||
instructions: TransactionInstruction[]; | ||
provider: AnchorProvider; | ||
mint: Address; | ||
destination: Address | TokenFormat; | ||
}): void; | ||
} | ||
@@ -208,0 +236,0 @@ /** |
import { BN, translateAddress } from "@project-serum/anchor"; | ||
import { TOKEN_PROGRAM_ID } from "@project-serum/serum/lib/token-instructions"; | ||
import { ASSOCIATED_TOKEN_PROGRAM_ID, NATIVE_MINT, createAssociatedTokenAccountInstruction, createCloseAccountInstruction, createSyncNativeInstruction, TokenAccountNotFoundError, TokenInvalidAccountOwnerError, TokenInvalidAccountSizeError, ACCOUNT_SIZE, AccountLayout, AccountState, MINT_SIZE, MintLayout } from "@solana/spl-token"; | ||
import { ASSOCIATED_TOKEN_PROGRAM_ID, NATIVE_MINT, createAssociatedTokenAccountInstruction, createCloseAccountInstruction, createSyncNativeInstruction, TokenAccountNotFoundError, TokenInvalidAccountOwnerError, TokenInvalidAccountSizeError, ACCOUNT_SIZE, AccountLayout, AccountState, MINT_SIZE, MintLayout, TokenInvalidOwnerError, createInitializeAccountInstruction, getMinimumBalanceForRentExemptAccount, TokenInvalidMintError } from "@solana/spl-token"; | ||
import { PublicKey, SystemProgram } from "@solana/web3.js"; | ||
@@ -8,2 +8,9 @@ import { Number192 } from "../utils/number192"; | ||
import { TokenAmount } from "./tokenAmount"; | ||
export var TokenFormat; | ||
(function (TokenFormat) { | ||
/** The users associated token account will be used, and sol will be unwrapped. */ | ||
TokenFormat[TokenFormat["unwrappedSol"] = 0] = "unwrappedSol"; | ||
/** The users associated token account will be used, and sol will be wrapped. */ | ||
TokenFormat[TokenFormat["wrappedSol"] = 1] = "wrappedSol"; | ||
})(TokenFormat || (TokenFormat = {})); | ||
export class AssociatedToken { | ||
@@ -53,3 +60,3 @@ /** | ||
if (token.info && !token.info.owner.equals(ownerAddress)) { | ||
throw new Error("Unexpected owner of the associated token"); | ||
throw new TokenInvalidOwnerError("The owner of a token account doesn't match the expected owner"); | ||
} | ||
@@ -62,5 +69,25 @@ return token; | ||
const address = this.derive(mintAddress, ownerAddress); | ||
const account = await connection.getAccountInfo(address); | ||
return !!account; | ||
return await AssociatedToken.existsAux(connection, mint, owner, address); | ||
} | ||
static async existsAux(connection, mint, owner, address) { | ||
const mintAddress = translateAddress(mint); | ||
const ownerAddress = translateAddress(owner); | ||
const tokenAddress = translateAddress(address); | ||
const info = await connection.getAccountInfo(tokenAddress); | ||
if (info) { | ||
const fakeDecimals = 0; | ||
const account = this.decodeAccount(info, address, fakeDecimals); | ||
if (!account.info) { | ||
throw new TokenInvalidAccountSizeError(); | ||
} | ||
if (!account.info.owner.equals(ownerAddress)) { | ||
throw new TokenInvalidOwnerError("The owner of a token account doesn't match the expected owner"); | ||
} | ||
if (!account.info.mint.equals(mintAddress)) { | ||
throw new TokenInvalidMintError("The mint of a token account doesn't match the expected mint"); | ||
} | ||
return true; | ||
} | ||
return false; | ||
} | ||
static async loadAux(connection, address, decimals) { | ||
@@ -334,3 +361,3 @@ const pubkey = translateAddress(address); | ||
/** | ||
* If the native wrapped token account does not exist, add instruction to create the token account. If ATA exists, do nothing. | ||
* If the token account does not exist, add instructions to create and initialize the token account. If the account exists do nothing. | ||
* @static | ||
@@ -340,13 +367,22 @@ * @param {TransactionInstruction[]} instructions | ||
* @param {Address} owner | ||
* @param {Address} mint | ||
* @returns {Promise<PublicKey>} returns the public key of the token account | ||
* @memberof AssociatedToken | ||
*/ | ||
static async withCreateNative(instructions, provider, owner) { | ||
static async withCreateAux(instructions, provider, owner, mint, address) { | ||
const ownerAddress = translateAddress(owner); | ||
const tokenAddress = this.derive(NATIVE_MINT, ownerAddress); | ||
if (!(await AssociatedToken.exists(provider.connection, NATIVE_MINT, ownerAddress))) { | ||
const ix = createAssociatedTokenAccountInstruction(provider.wallet.publicKey, tokenAddress, ownerAddress, NATIVE_MINT); | ||
instructions.push(ix); | ||
const mintAddress = translateAddress(mint); | ||
const tokenAddress = translateAddress(address); | ||
if (!(await AssociatedToken.existsAux(provider.connection, mintAddress, ownerAddress, address))) { | ||
let rent = await getMinimumBalanceForRentExemptAccount(provider.connection); | ||
let createIx = SystemProgram.createAccount({ | ||
fromPubkey: provider.wallet.publicKey, | ||
newAccountPubkey: tokenAddress, | ||
lamports: rent, | ||
space: ACCOUNT_SIZE, | ||
programId: TOKEN_PROGRAM_ID | ||
}); | ||
let initIx = createInitializeAccountInstruction(tokenAddress, mintAddress, ownerAddress); | ||
instructions.push(createIx, initIx); | ||
} | ||
return tokenAddress; | ||
} | ||
@@ -407,11 +443,9 @@ /** | ||
*/ | ||
static async withWrapIfNativeMint(instructions, provider, mint, tokenAccountOrNative, amount) { | ||
const owner = provider.wallet.publicKey; | ||
static async withWrapIfNativeMint(instructions, provider, mint, amount) { | ||
const mintPubkey = translateAddress(mint); | ||
const tokenAccountOrNativePubkey = translateAddress(tokenAccountOrNative); | ||
//only run if mint is wrapped sol mint, and the token account is actually the native wallet | ||
if (this.isNative(owner, mintPubkey, tokenAccountOrNativePubkey)) { | ||
//only run if mint is wrapped sol mint | ||
if (mintPubkey.equals(NATIVE_MINT)) { | ||
return this.withWrapNative(instructions, provider, amount); | ||
} | ||
return tokenAccountOrNativePubkey; | ||
return AssociatedToken.derive(mint, provider.wallet.publicKey); | ||
} | ||
@@ -426,7 +460,6 @@ /** | ||
*/ | ||
static withUnwrapIfNative(instructions, owner, mint, tokenAccountOrNative) { | ||
static withUnwrapIfNative(instructions, owner, mint) { | ||
const ownerPubkey = translateAddress(owner); | ||
const mintPubkey = translateAddress(mint); | ||
const tokenAccountOrNativePubkey = translateAddress(tokenAccountOrNative); | ||
if (this.isNative(ownerPubkey, mintPubkey, tokenAccountOrNativePubkey)) { | ||
if (mintPubkey.equals(NATIVE_MINT)) { | ||
//add close account IX | ||
@@ -437,3 +470,3 @@ this.withUnwrapNative(instructions, owner); | ||
/** | ||
* Create the associated token account. Funds it if natve. | ||
* Create the associated token account. Funds it if native. | ||
* | ||
@@ -444,6 +477,6 @@ * @static | ||
* @param {Address} mint | ||
* @param {BN} initialAmount | ||
* @param {BN} wrapAmount | ||
* @memberof AssociatedToken | ||
*/ | ||
static async withCreateOrWrapIfNativeMint(instructions, provider, mint, initialAmount) { | ||
static async withCreateOrWrapIfNativeMint(instructions, provider, mint, wrapAmount) { | ||
const owner = provider.wallet.publicKey; | ||
@@ -453,3 +486,3 @@ const mintPubkey = translateAddress(mint); | ||
// Only run if mint is wrapped sol mint. Create the wrapped sol account and return its pubkey | ||
return await this.withWrapNative(instructions, provider, initialAmount); | ||
return await this.withWrapNative(instructions, provider, wrapAmount); | ||
} | ||
@@ -482,2 +515,55 @@ else { | ||
} | ||
static async withBeginTransferFromSource({ instructions, provider, mint, amount, source = TokenFormat.unwrappedSol }) { | ||
let sourceAddress; | ||
if (source instanceof PublicKey || typeof source === "string") { | ||
sourceAddress = translateAddress(source); | ||
} | ||
let owner = provider.wallet.publicKey; | ||
let isSourceOwner = sourceAddress && sourceAddress.equals(owner); | ||
let isSourceAssociatedAddress = sourceAddress && AssociatedToken.derive(mint, owner).equals(sourceAddress); | ||
if (source === TokenFormat.unwrappedSol || isSourceOwner || isSourceAssociatedAddress) { | ||
return await AssociatedToken.withCreateOrWrapIfNativeMint(instructions, provider, mint, amount); | ||
} | ||
else if (source === TokenFormat.wrappedSol) { | ||
return await AssociatedToken.withCreate(instructions, provider, owner, mint); | ||
} | ||
else if (sourceAddress) { | ||
await AssociatedToken.withCreateAux(instructions, provider, owner, mint, sourceAddress); | ||
return sourceAddress; | ||
} | ||
throw new Error("Unexpected argument 'source' or there are multiple versions of @solana/web3.js PublicKey installed"); | ||
} | ||
static async withBeginTransferToDestination({ instructions, provider, mint, destination = TokenFormat.unwrappedSol }) { | ||
let destinationAddress; | ||
if (destination instanceof PublicKey || typeof destination === "string") { | ||
destinationAddress = translateAddress(destination); | ||
} | ||
let owner = provider.wallet.publicKey; | ||
let isDestinationOwner = destinationAddress && destinationAddress.equals(owner); | ||
let isDestinationAssociatedAddress = destinationAddress && AssociatedToken.derive(mint, owner).equals(destinationAddress); | ||
if (destination === TokenFormat.wrappedSol || | ||
destination === TokenFormat.unwrappedSol || | ||
isDestinationOwner || | ||
isDestinationAssociatedAddress) { | ||
return await AssociatedToken.withCreate(instructions, provider, owner, mint); | ||
} | ||
else if (destinationAddress) { | ||
await AssociatedToken.withCreateAux(instructions, provider, owner, mint, destinationAddress); | ||
return destinationAddress; | ||
} | ||
throw new Error("Unexpected argument 'destination' or there are multiple versions of @solana/web3.js PublicKey installed"); | ||
} | ||
/** Ends the transfer by unwraps the token if it is the native mint. */ | ||
static withEndTransfer({ instructions, provider, mint, destination = TokenFormat.unwrappedSol }) { | ||
let destinationAddress; | ||
if (destination instanceof PublicKey || typeof destination === "string") { | ||
destinationAddress = translateAddress(destination); | ||
} | ||
let owner = provider.wallet.publicKey; | ||
let isDestinationOwner = destinationAddress && destinationAddress.equals(owner); | ||
if (translateAddress(mint).equals(NATIVE_MINT) && | ||
(destination === TokenFormat.unwrappedSol || isDestinationOwner)) { | ||
AssociatedToken.withUnwrapNative(instructions, owner); | ||
} | ||
} | ||
} | ||
@@ -484,0 +570,0 @@ AssociatedToken.NATIVE_DECIMALS = 9; |
@@ -1,2 +0,1 @@ | ||
/// <reference types="bn.js" /> | ||
import { BN } from "@project-serum/anchor"; | ||
@@ -3,0 +2,0 @@ import { Account, Mint } from "@solana/spl-token"; |
@@ -1,2 +0,1 @@ | ||
/// <reference types="bn.js" /> | ||
import { PublicKey } from "@solana/web3.js"; | ||
@@ -3,0 +2,0 @@ import { Address, BN, AnchorProvider } from "@project-serum/anchor"; |
@@ -1,11 +0,10 @@ | ||
/// <reference types="bn.js" /> | ||
import { BN } from "@project-serum/anchor"; | ||
export declare class Number128 { | ||
static readonly PRECISION = 10; | ||
static readonly ONE: BN; | ||
static readonly ZERO: BN; | ||
static readonly MAX: BN; | ||
static readonly U64_MAX: BN; | ||
static readonly ONE: any; | ||
static readonly ZERO: any; | ||
static readonly MAX: any; | ||
static readonly U64_MAX: any; | ||
static readonly BPS_EXPONENT = -4; | ||
static readonly POWERS_OF_TEN: BN[]; | ||
static readonly POWERS_OF_TEN: any[]; | ||
private constructor(); | ||
@@ -16,7 +15,7 @@ /** Removes the fractional component from the number.*/ | ||
static asU64(value: BN, exponent: number): BN; | ||
static from(value: BN): BN; | ||
static fromDecimal(value: BN, exponent: number): BN; | ||
static from(value: BN): any; | ||
static fromDecimal(value: BN, exponent: number): any; | ||
/** Convert from basis points */ | ||
static fromBps(basisPoints: BN): BN; | ||
static fromBps(basisPoints: BN): any; | ||
} | ||
//# sourceMappingURL=number128.d.ts.map |
@@ -1,9 +0,8 @@ | ||
/// <reference types="bn.js" /> | ||
import { BN } from "@project-serum/anchor"; | ||
export declare class Number192 { | ||
static readonly PRECISION = 15; | ||
static readonly ONE: BN; | ||
static readonly ZERO: BN; | ||
static readonly U64_MAX: BN; | ||
static readonly POWERS_OF_TEN: BN[]; | ||
static readonly ONE: any; | ||
static readonly ZERO: any; | ||
static readonly U64_MAX: any; | ||
static readonly POWERS_OF_TEN: any[]; | ||
private constructor(); | ||
@@ -15,5 +14,5 @@ /** Removes the fractional component from the number. */ | ||
static asU64Rounded(value: BN, exponent: number): BN; | ||
static from(value: BN): BN; | ||
static fromDecimal(value: BN, exponent: number): BN; | ||
static from(value: BN): any; | ||
static fromDecimal(value: BN, exponent: number): any; | ||
} | ||
//# sourceMappingURL=number192.d.ts.map |
@@ -1,4 +0,2 @@ | ||
/// <reference types="bn.js" /> | ||
import { BN } from "@project-serum/anchor"; | ||
export declare function getTimestamp(): BN; | ||
export declare function getTimestamp(): any; | ||
//# sourceMappingURL=timestamp.d.ts.map |
{ | ||
"name": "@jet-lab/margin", | ||
"version": "0.1.34", | ||
"version": "0.1.35", | ||
"description": "Library for interacting with the Jet margin on-chain programs", | ||
@@ -48,3 +48,4 @@ "keywords": [ | ||
"typescript": "^4.5.4" | ||
} | ||
}, | ||
"type": "module" | ||
} |
@@ -6,3 +6,3 @@ import { Address, BN, translateAddress } from "@project-serum/anchor" | ||
import { assert } from "chai" | ||
import { AssociatedToken, bigIntToBn } from "../../token" | ||
import { AssociatedToken, bigIntToBn, TokenAddress, TokenFormat } from "../../token" | ||
import { TokenAmount } from "../../token/tokenAmount" | ||
@@ -444,3 +444,11 @@ import { MarginAccount } from "../marginAccount" | ||
*/ | ||
async deposit({ marginAccount, amount, source }: { marginAccount: MarginAccount; amount: BN; source?: Address }) { | ||
async deposit({ | ||
marginAccount, | ||
amount, | ||
source = TokenFormat.unwrappedSol | ||
}: { | ||
marginAccount: MarginAccount | ||
amount: BN | ||
source?: TokenAddress | ||
}) { | ||
assert(marginAccount) | ||
@@ -456,12 +464,6 @@ assert(amount) | ||
const instructions: TransactionInstruction[] = [] | ||
source ??= await AssociatedToken.withCreateOrWrapIfNativeMint( | ||
instructions, | ||
marginAccount.provider, | ||
this.tokenMint, | ||
amount | ||
) | ||
await this.withDeposit({ | ||
instructions: instructions, | ||
depositor: marginAccount.owner, | ||
marginAccount, | ||
source, | ||
@@ -477,4 +479,4 @@ destination: position.address, | ||
instructions, | ||
depositor, | ||
source, | ||
marginAccount, | ||
source = TokenFormat.unwrappedSol, | ||
destination, | ||
@@ -484,7 +486,18 @@ amount | ||
instructions: TransactionInstruction[] | ||
depositor: Address | ||
source: Address | ||
marginAccount: MarginAccount | ||
source?: TokenAddress | ||
destination: Address | ||
amount: BN | ||
}): Promise<void> { | ||
const provider = marginAccount.provider | ||
const mint = this.tokenMint | ||
source = await AssociatedToken.withBeginTransferFromSource({ | ||
instructions, | ||
provider, | ||
mint, | ||
amount, | ||
source | ||
}) | ||
const ix = await this.programs.marginPool.methods | ||
@@ -496,3 +509,3 @@ .deposit(amount) | ||
depositNoteMint: this.addresses.depositNoteMint, | ||
depositor, | ||
depositor: marginAccount.owner, | ||
source, | ||
@@ -504,9 +517,26 @@ destination, | ||
instructions.push(ix) | ||
AssociatedToken.withEndTransfer({ | ||
instructions, | ||
provider, | ||
mint, | ||
destination | ||
}) | ||
} | ||
async marginBorrow({ marginAccount, pools, amount }: { marginAccount: MarginAccount; pools: Pool[]; amount: BN }) { | ||
async marginBorrow({ | ||
marginAccount, | ||
pools, | ||
amount, | ||
destination | ||
}: { | ||
marginAccount: MarginAccount | ||
pools: Pool[] | ||
amount: BN | ||
destination?: TokenAddress | ||
}) { | ||
const lamports = PoolAmount.tokens(amount) | ||
await marginAccount.refresh() | ||
const preInstructons: TransactionInstruction[] = [] | ||
const refreshInstructions: TransactionInstruction[] = [] | ||
const postInstructions: TransactionInstruction[] = [] | ||
const instructionsInstructions: TransactionInstruction[] = [] | ||
@@ -516,8 +546,8 @@ const depositPosition = await marginAccount.getOrCreatePosition(this.addresses.depositNoteMint) | ||
const loanNoteAccount = await this.withGetOrCreateLoanPosition(preInstructons, marginAccount) | ||
await this.withMarginRefreshAllPositionPrices({ instructions: refreshInstructions, pools, marginAccount }) | ||
const loanNoteAccount = await this.withGetOrCreateLoanPosition(instructionsInstructions, marginAccount) | ||
await this.withMarginBorrow({ | ||
instructions: postInstructions, | ||
instructions: instructionsInstructions, | ||
marginAccount, | ||
@@ -528,4 +558,15 @@ depositPosition, | ||
}) | ||
if (destination !== undefined) { | ||
await this.withWithdraw({ | ||
instructions: instructionsInstructions, | ||
marginAccount, | ||
source: depositPosition.address, | ||
destination, | ||
amount: lamports | ||
}) | ||
} | ||
try { | ||
return await sendAll(marginAccount.provider, [preInstructons, chunks(11, refreshInstructions), postInstructions]) | ||
return await sendAll(marginAccount.provider, [chunks(11, refreshInstructions), instructionsInstructions]) | ||
} catch (err) { | ||
@@ -604,2 +645,3 @@ console.log(err) | ||
pools, | ||
source, | ||
amount | ||
@@ -609,7 +651,8 @@ }: { | ||
pools: Pool[] | ||
amount: PoolAmount | ||
source?: TokenAddress | ||
amount: BN | ||
}) { | ||
await marginAccount.refresh() | ||
const deposit_position = await marginAccount.getOrCreatePosition(this.addresses.depositNoteMint) | ||
assert(deposit_position) | ||
const depositPosition = await marginAccount.getOrCreatePosition(this.addresses.depositNoteMint) | ||
assert(depositPosition) | ||
@@ -622,7 +665,18 @@ const refreshInstructions: TransactionInstruction[] = [] | ||
const loanNoteAccount = await this.withGetOrCreateLoanPosition(instructions, marginAccount) | ||
await this.makeMarginRepayInstruction({ | ||
if (source !== undefined) { | ||
await this.withDeposit({ | ||
instructions, | ||
marginAccount, | ||
source, | ||
destination: depositPosition.address, | ||
amount | ||
}) | ||
} | ||
await this.withMarginRepay({ | ||
instructions, | ||
marginAccount: marginAccount, | ||
deposit_account: deposit_position.address, | ||
loan_account: loanNoteAccount, | ||
depositPosition: depositPosition.address, | ||
loanPosition: loanNoteAccount, | ||
amount | ||
@@ -646,7 +700,7 @@ }) | ||
async makeMarginRepayInstruction({ | ||
async withMarginRepay({ | ||
instructions, | ||
marginAccount, | ||
deposit_account, | ||
loan_account, | ||
depositPosition, | ||
loanPosition, | ||
amount | ||
@@ -656,5 +710,5 @@ }: { | ||
marginAccount: MarginAccount | ||
deposit_account: Address | ||
loan_account: Address | ||
amount: PoolAmount | ||
depositPosition: Address | ||
loanPosition: Address | ||
amount: BN | ||
}): Promise<void> { | ||
@@ -666,3 +720,3 @@ await marginAccount.withAdapterInvoke({ | ||
adapterInstruction: await this.programs.marginPool.methods | ||
.marginRepay(amount.toRpcArg()) | ||
.marginRepay(PoolAmount.tokens(amount).toRpcArg()) | ||
.accounts({ | ||
@@ -673,4 +727,4 @@ marginAccount: marginAccount.address, | ||
depositNoteMint: this.addresses.depositNoteMint, | ||
loanAccount: loan_account, | ||
depositAccount: deposit_account, | ||
loanAccount: loanPosition, | ||
depositAccount: depositPosition, | ||
tokenProgram: TOKEN_PROGRAM_ID | ||
@@ -693,3 +747,3 @@ }) | ||
amount, | ||
destination | ||
destination = TokenFormat.unwrappedSol | ||
}: { | ||
@@ -699,5 +753,5 @@ marginAccount: MarginAccount | ||
amount: PoolAmount | ||
destination?: Address | ||
destination?: TokenAddress | ||
}) { | ||
// FIXME: can be getPosition | ||
// FIXME: can source be calculated in withdraw? | ||
const { address: source } = await marginAccount.getOrCreatePosition(this.addresses.depositNoteMint) | ||
@@ -708,13 +762,3 @@ | ||
const instructions: TransactionInstruction[] = [] | ||
const postInstructions: TransactionInstruction[] = [] | ||
let marginWithdrawDestination = | ||
destination ?? | ||
(await AssociatedToken.withCreateOrUnwrapIfNativeMint( | ||
preInstructions, | ||
postInstructions, | ||
marginAccount.provider, | ||
this.tokenMint | ||
)) | ||
await this.withMarginRefreshAllPositionPrices({ instructions: refreshInstructions, pools, marginAccount }) | ||
@@ -726,11 +770,7 @@ await marginAccount.withUpdateAllPositionBalances({ instructions: refreshInstructions }) | ||
source, | ||
destination: marginWithdrawDestination, | ||
destination, | ||
amount | ||
}) | ||
return await sendAll(marginAccount.provider, [ | ||
preInstructions, | ||
chunks(11, refreshInstructions), | ||
[...instructions, ...postInstructions] | ||
]) | ||
return await sendAll(marginAccount.provider, [preInstructions, chunks(11, refreshInstructions), instructions]) | ||
} | ||
@@ -742,3 +782,3 @@ | ||
source, | ||
destination, | ||
destination = TokenFormat.unwrappedSol, | ||
amount | ||
@@ -748,23 +788,42 @@ }: { | ||
marginAccount: MarginAccount | ||
source: Address | ||
destination: Address | ||
source: PublicKey | ||
destination?: TokenAddress | ||
amount: PoolAmount | ||
}): Promise<void> { | ||
await marginAccount.withAdapterInvoke({ | ||
const provider = marginAccount.provider | ||
const mint = this.tokenMint | ||
destination = await AssociatedToken.withBeginTransferToDestination({ | ||
instructions, | ||
adapterProgram: this.programs.config.marginPoolProgramId, | ||
adapterMetadata: this.addresses.marginPoolAdapterMetadata, | ||
adapterInstruction: await this.programs.marginPool.methods | ||
.withdraw(amount.toRpcArg()) | ||
.accounts({ | ||
depositor: marginAccount.address, | ||
marginPool: this.address, | ||
vault: this.addresses.vault, | ||
depositNoteMint: this.addresses.depositNoteMint, | ||
source, | ||
destination, | ||
tokenProgram: TOKEN_PROGRAM_ID | ||
}) | ||
.instruction() | ||
provider, | ||
mint, | ||
destination | ||
}) | ||
if (destination) { | ||
await marginAccount.withAdapterInvoke({ | ||
instructions, | ||
adapterProgram: this.programs.config.marginPoolProgramId, | ||
adapterMetadata: this.addresses.marginPoolAdapterMetadata, | ||
adapterInstruction: await this.programs.marginPool.methods | ||
.withdraw(amount.toRpcArg()) | ||
.accounts({ | ||
depositor: marginAccount.address, | ||
marginPool: this.address, | ||
vault: this.addresses.vault, | ||
depositNoteMint: this.addresses.depositNoteMint, | ||
source, | ||
destination, | ||
tokenProgram: TOKEN_PROGRAM_ID | ||
}) | ||
.instruction() | ||
}) | ||
} | ||
AssociatedToken.withEndTransfer({ | ||
instructions, | ||
provider, | ||
mint, | ||
destination | ||
}) | ||
} | ||
@@ -771,0 +830,0 @@ |
@@ -18,3 +18,7 @@ import { BN, Address, translateAddress, AnchorProvider, Provider } from "@project-serum/anchor" | ||
MINT_SIZE, | ||
MintLayout | ||
MintLayout, | ||
TokenInvalidOwnerError, | ||
createInitializeAccountInstruction, | ||
getMinimumBalanceForRentExemptAccount, | ||
TokenInvalidMintError | ||
} from "@solana/spl-token" | ||
@@ -26,2 +30,11 @@ import { Connection, PublicKey, TransactionInstruction, SystemProgram, AccountInfo } from "@solana/web3.js" | ||
export type TokenAddress = Address | TokenFormat | ||
export enum TokenFormat { | ||
/** The users associated token account will be used, and sol will be unwrapped. */ | ||
unwrappedSol, | ||
/** The users associated token account will be used, and sol will be wrapped. */ | ||
wrappedSol | ||
} | ||
export class AssociatedToken { | ||
@@ -70,3 +83,3 @@ static readonly NATIVE_DECIMALS = 9 | ||
if (token.info && !token.info.owner.equals(ownerAddress)) { | ||
throw new Error("Unexpected owner of the associated token") | ||
throw new TokenInvalidOwnerError("The owner of a token account doesn't match the expected owner") | ||
} | ||
@@ -80,6 +93,28 @@ return token | ||
const address = this.derive(mintAddress, ownerAddress) | ||
const account = await connection.getAccountInfo(address) | ||
return !!account | ||
return await AssociatedToken.existsAux(connection, mint, owner, address) | ||
} | ||
static async existsAux(connection: Connection, mint: Address, owner: Address, address: Address) { | ||
const mintAddress = translateAddress(mint) | ||
const ownerAddress = translateAddress(owner) | ||
const tokenAddress = translateAddress(address) | ||
const info = await connection.getAccountInfo(tokenAddress) | ||
if (info) { | ||
const fakeDecimals = 0 | ||
const account = this.decodeAccount(info, address, fakeDecimals) | ||
if (!account.info) { | ||
throw new TokenInvalidAccountSizeError() | ||
} | ||
if (!account.info.owner.equals(ownerAddress)) { | ||
throw new TokenInvalidOwnerError("The owner of a token account doesn't match the expected owner") | ||
} | ||
if (!account.info.mint.equals(mintAddress)) { | ||
throw new TokenInvalidMintError("The mint of a token account doesn't match the expected mint") | ||
} | ||
return true | ||
} | ||
return false | ||
} | ||
static async loadAux(connection: Connection, address: Address, decimals: number) { | ||
@@ -421,3 +456,3 @@ const pubkey = translateAddress(address) | ||
/** | ||
* If the native wrapped token account does not exist, add instruction to create the token account. If ATA exists, do nothing. | ||
* If the token account does not exist, add instructions to create and initialize the token account. If the account exists do nothing. | ||
* @static | ||
@@ -427,23 +462,29 @@ * @param {TransactionInstruction[]} instructions | ||
* @param {Address} owner | ||
* @param {Address} mint | ||
* @returns {Promise<PublicKey>} returns the public key of the token account | ||
* @memberof AssociatedToken | ||
*/ | ||
static async withCreateNative( | ||
static async withCreateAux( | ||
instructions: TransactionInstruction[], | ||
provider: AnchorProvider, | ||
owner: Address | ||
): Promise<PublicKey> { | ||
owner: Address, | ||
mint: Address, | ||
address: Address | ||
): Promise<void> { | ||
const ownerAddress = translateAddress(owner) | ||
const tokenAddress = this.derive(NATIVE_MINT, ownerAddress) | ||
const mintAddress = translateAddress(mint) | ||
const tokenAddress = translateAddress(address) | ||
if (!(await AssociatedToken.exists(provider.connection, NATIVE_MINT, ownerAddress))) { | ||
const ix = createAssociatedTokenAccountInstruction( | ||
provider.wallet.publicKey, | ||
tokenAddress, | ||
ownerAddress, | ||
NATIVE_MINT | ||
) | ||
instructions.push(ix) | ||
if (!(await AssociatedToken.existsAux(provider.connection, mintAddress, ownerAddress, address))) { | ||
let rent = await getMinimumBalanceForRentExemptAccount(provider.connection) | ||
let createIx = SystemProgram.createAccount({ | ||
fromPubkey: provider.wallet.publicKey, | ||
newAccountPubkey: tokenAddress, | ||
lamports: rent, | ||
space: ACCOUNT_SIZE, | ||
programId: TOKEN_PROGRAM_ID | ||
}) | ||
let initIx = createInitializeAccountInstruction(tokenAddress, mintAddress, ownerAddress) | ||
instructions.push(createIx, initIx) | ||
} | ||
return tokenAddress | ||
} | ||
@@ -518,14 +559,11 @@ | ||
mint: Address, | ||
tokenAccountOrNative: Address, | ||
amount: BN | ||
): Promise<PublicKey> { | ||
const owner = provider.wallet.publicKey | ||
const mintPubkey = translateAddress(mint) | ||
const tokenAccountOrNativePubkey = translateAddress(tokenAccountOrNative) | ||
//only run if mint is wrapped sol mint, and the token account is actually the native wallet | ||
if (this.isNative(owner, mintPubkey, tokenAccountOrNativePubkey)) { | ||
//only run if mint is wrapped sol mint | ||
if (mintPubkey.equals(NATIVE_MINT)) { | ||
return this.withWrapNative(instructions, provider, amount) | ||
} | ||
return tokenAccountOrNativePubkey | ||
return AssociatedToken.derive(mint, provider.wallet.publicKey) | ||
} | ||
@@ -541,13 +579,7 @@ | ||
*/ | ||
static withUnwrapIfNative( | ||
instructions: TransactionInstruction[], | ||
owner: Address, | ||
mint: Address, | ||
tokenAccountOrNative: Address | ||
): void { | ||
static withUnwrapIfNative(instructions: TransactionInstruction[], owner: Address, mint: Address): void { | ||
const ownerPubkey = translateAddress(owner) | ||
const mintPubkey = translateAddress(mint) | ||
const tokenAccountOrNativePubkey = translateAddress(tokenAccountOrNative) | ||
if (this.isNative(ownerPubkey, mintPubkey, tokenAccountOrNativePubkey)) { | ||
if (mintPubkey.equals(NATIVE_MINT)) { | ||
//add close account IX | ||
@@ -559,3 +591,3 @@ this.withUnwrapNative(instructions, owner) | ||
/** | ||
* Create the associated token account. Funds it if natve. | ||
* Create the associated token account. Funds it if native. | ||
* | ||
@@ -566,3 +598,3 @@ * @static | ||
* @param {Address} mint | ||
* @param {BN} initialAmount | ||
* @param {BN} wrapAmount | ||
* @memberof AssociatedToken | ||
@@ -574,3 +606,3 @@ */ | ||
mint: Address, | ||
initialAmount: BN | ||
wrapAmount: BN | ||
): Promise<PublicKey> { | ||
@@ -582,3 +614,3 @@ const owner = provider.wallet.publicKey | ||
// Only run if mint is wrapped sol mint. Create the wrapped sol account and return its pubkey | ||
return await this.withWrapNative(instructions, provider, initialAmount) | ||
return await this.withWrapNative(instructions, provider, wrapAmount) | ||
} else { | ||
@@ -619,2 +651,99 @@ // Return the associated token | ||
} | ||
static async withBeginTransferFromSource({ | ||
instructions, | ||
provider, | ||
mint, | ||
amount, | ||
source = TokenFormat.unwrappedSol | ||
}: { | ||
instructions: TransactionInstruction[] | ||
provider: AnchorProvider | ||
mint: Address | ||
amount: BN | ||
source: Address | TokenFormat | ||
}): Promise<PublicKey> { | ||
let sourceAddress: PublicKey | undefined | ||
if (source instanceof PublicKey || typeof source === "string") { | ||
sourceAddress = translateAddress(source) | ||
} | ||
let owner = provider.wallet.publicKey | ||
let isSourceOwner = sourceAddress && sourceAddress.equals(owner) | ||
let isSourceAssociatedAddress = sourceAddress && AssociatedToken.derive(mint, owner).equals(sourceAddress) | ||
if (source === TokenFormat.unwrappedSol || isSourceOwner || isSourceAssociatedAddress) { | ||
return await AssociatedToken.withCreateOrWrapIfNativeMint(instructions, provider, mint, amount) | ||
} else if (source === TokenFormat.wrappedSol) { | ||
return await AssociatedToken.withCreate(instructions, provider, owner, mint) | ||
} else if (sourceAddress) { | ||
await AssociatedToken.withCreateAux(instructions, provider, owner, mint, sourceAddress) | ||
return sourceAddress | ||
} | ||
throw new Error( | ||
"Unexpected argument 'source' or there are multiple versions of @solana/web3.js PublicKey installed" | ||
) | ||
} | ||
static async withBeginTransferToDestination({ | ||
instructions, | ||
provider, | ||
mint, | ||
destination = TokenFormat.unwrappedSol | ||
}: { | ||
instructions: TransactionInstruction[] | ||
provider: AnchorProvider | ||
mint: Address | ||
destination: Address | TokenFormat | ||
}): Promise<PublicKey> { | ||
let destinationAddress: PublicKey | undefined | ||
if (destination instanceof PublicKey || typeof destination === "string") { | ||
destinationAddress = translateAddress(destination) | ||
} | ||
let owner = provider.wallet.publicKey | ||
let isDestinationOwner = destinationAddress && destinationAddress.equals(owner) | ||
let isDestinationAssociatedAddress = | ||
destinationAddress && AssociatedToken.derive(mint, owner).equals(destinationAddress) | ||
if ( | ||
destination === TokenFormat.wrappedSol || | ||
destination === TokenFormat.unwrappedSol || | ||
isDestinationOwner || | ||
isDestinationAssociatedAddress | ||
) { | ||
return await AssociatedToken.withCreate(instructions, provider, owner, mint) | ||
} else if (destinationAddress) { | ||
await AssociatedToken.withCreateAux(instructions, provider, owner, mint, destinationAddress) | ||
return destinationAddress | ||
} | ||
throw new Error( | ||
"Unexpected argument 'destination' or there are multiple versions of @solana/web3.js PublicKey installed" | ||
) | ||
} | ||
/** Ends the transfer by unwraps the token if it is the native mint. */ | ||
static withEndTransfer({ | ||
instructions, | ||
provider, | ||
mint, | ||
destination = TokenFormat.unwrappedSol | ||
}: { | ||
instructions: TransactionInstruction[] | ||
provider: AnchorProvider | ||
mint: Address | ||
destination: Address | TokenFormat | ||
}) { | ||
let destinationAddress: PublicKey | undefined | ||
if (destination instanceof PublicKey || typeof destination === "string") { | ||
destinationAddress = translateAddress(destination) | ||
} | ||
let owner = provider.wallet.publicKey | ||
let isDestinationOwner = destinationAddress && destinationAddress.equals(owner) | ||
if ( | ||
translateAddress(mint).equals(NATIVE_MINT) && | ||
(destination === TokenFormat.unwrappedSol || isDestinationOwner) | ||
) { | ||
AssociatedToken.withUnwrapNative(instructions, owner) | ||
} | ||
} | ||
} | ||
@@ -621,0 +750,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
2124258
40399
Yes