UNPKG

goosefx-ssl-sdk

Version:

SDK for interacting with GooseFX Single-sided Liquidity pools

290 lines 14.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Swap = exports.wasmData = void 0; const anchor_1 = require("@project-serum/anchor"); const layouts_1 = require("../layouts"); const spl_token_1 = require("@solana/spl-token"); const web3_js_1 = require("@solana/web3.js"); const buffer_1 = require("buffer"); const constants_1 = require("../constants"); const SwapIDL = __importStar(require("../idl/gfx_ssl_idl.json")); const utils_1 = require("./utils"); const gfx_ssl_wasm_data_1 = __importDefault(require("../wasm/gfx_ssl_wasm_data")); const gfx_ssl_wasm_1 = __importStar(require("../wasm/gfx_ssl_wasm")), wasm = gfx_ssl_wasm_1; const spl_token_2 = require("@solana/spl-token"); const ssl_1 = require("./ssl"); const client_1 = require("@pythnetwork/client"); var gfx_ssl_wasm_data_2 = require("../wasm/gfx_ssl_wasm_data"); Object.defineProperty(exports, "wasmData", { enumerable: true, get: function () { return __importDefault(gfx_ssl_wasm_data_2).default; } }); let wasmInited = false; class Swap { constructor(connection, controller = constants_1.ADDRESSES["MAINNET"].GFX_CONTROLLER, programId = constants_1.ADDRESSES["MAINNET"].SSL_PROGRAM_ID) { this.connection = connection; this.controller = controller; this.programId = programId; this.getPairAddress = (tokenA, tokenB) => { const addresses = [tokenA.toBuffer(), tokenB.toBuffer()].sort(buffer_1.Buffer.compare); const pairArr = web3_js_1.PublicKey.findProgramAddressSync([ buffer_1.Buffer.from("GFX-SSL-Pair", "utf-8"), this.controller.toBuffer(), addresses[0], addresses[1], ], this.programId); return pairArr[0]; }; this.createAssociatedTokenAccountIx = (mint, associatedAccount, owner) => (0, spl_token_1.createAssociatedTokenAccountInstruction)(owner, associatedAccount, owner, mint); this.getQuoter = async (tokenIn, tokenOut) => { let wasm = await this.getWasm(); return new Quoter(this.connection, this.programId, this.controller, tokenIn, tokenOut, wasm); }; this.getQuote = async (tokenIn, tokenOut, inTokenAmount, silent = true) => { const quoter = await this.getQuoter(tokenIn, tokenOut); await quoter.prepare(); return quoter.quote(inTokenAmount, silent); }; this.getMinimumQuote = async (tokenA, tokenB, inTokenAmount, slippage) => { const result = await this.getQuote(tokenA, tokenB, inTokenAmount); const minAmountOut = (result.out * (10000n - BigInt(slippage * 10000))) / 10000n; return minAmountOut; }; this.createSwapIx = async (tokenA, tokenB, inTokenAmount, minOut, wallet, referrerTokenAccount) => { let ixs = []; const addedComputeBudgetIX = web3_js_1.ComputeBudgetProgram.requestUnits({ units: 1000000, additionalFee: 0, }); ixs.push(addedComputeBudgetIX); const program = new anchor_1.Program(SwapIDL, this.programId.toBase58(), new anchor_1.Provider(this.connection, wallet, { commitment: "processed", })); const inst = program.instruction; const pair = this.getPairAddress(tokenA, tokenB); const [inTokenAtaUser, outTokenAtaUser] = [ (0, utils_1.findAssociatedTokenAddress)(wallet, tokenA), (0, utils_1.findAssociatedTokenAddress)(wallet, tokenB), ]; const sslIn = ssl_1.SSL.findAddress(this.controller, tokenA, this.programId); const sslOut = ssl_1.SSL.findAddress(this.controller, tokenB, this.programId); if (!(await this.connection.getAccountInfo(outTokenAtaUser))) { ixs.push(this.createAssociatedTokenAccountIx(tokenB, outTokenAtaUser, wallet)); } const pairData = await this.connection.getAccountInfo(pair); if (!pairData || !pairData.data) throw new Error("Token Pair do not exist yet."); const tokenAccountB = (0, utils_1.findAssociatedTokenAddress)(wallet, tokenB); if (tokenB.toBase58() !== spl_token_1.NATIVE_MINT.toBase58() && !(await this.connection.getParsedAccountInfo(tokenAccountB)).value) { ixs.push(this.createAssociatedTokenAccountIx(tokenB, tokenAccountB, wallet)); } const decoded = layouts_1.PAIR_LAYOUT.decode(pairData.data); const { oracles, nOracle, feeCollector } = decoded; const n = Number(nOracle.toString()); const remainingAccounts = []; if (referrerTokenAccount !== undefined) { remainingAccounts.push({ isSigner: false, isWritable: true, pubkey: referrerTokenAccount }); } for (const oracle of oracles.slice(0, n)) { for (const elem of oracle.elements.slice(0, Number(oracle.n))) { remainingAccounts.push({ isSigner: false, isWritable: false, pubkey: elem.address, }); } } const accounts = { controller: this.controller, pair, sslIn: sslIn, sslOut: sslOut, liabilityVaultIn: (0, utils_1.findAssociatedTokenAddress)(sslIn, tokenA), liabilityVaultOut: (0, utils_1.findAssociatedTokenAddress)(sslOut, tokenB), swappedLiabilityVaultIn: (0, utils_1.findAssociatedTokenAddress)(sslIn, tokenB), swappedLiabilityVaultOut: (0, utils_1.findAssociatedTokenAddress)(sslOut, tokenA), userInAta: inTokenAtaUser, userOutAta: outTokenAtaUser, feeCollectorAta: (0, utils_1.findAssociatedTokenAddress)(feeCollector, tokenA), userWallet: wallet, feeCollector: feeCollector, tokenProgram: spl_token_1.TOKEN_PROGRAM_ID, }; ixs.push(await inst.swap(new anchor_1.BN(inTokenAmount.toString()), new anchor_1.BN(minOut.toString()), { accounts, remainingAccounts })); return ixs; }; } async getWasm() { if (!wasmInited) { await (0, gfx_ssl_wasm_1.default)(buffer_1.Buffer.from(gfx_ssl_wasm_data_1.default, "base64")); wasmInited = true; } return wasm; } } exports.Swap = Swap; class Quoter { constructor(connection, programId, controller, tokenIn, tokenOut, wasm) { this.connection = connection; this.programId = programId; this.controller = controller; this.tokenIn = tokenIn; this.tokenOut = tokenOut; this.wasm = wasm; this.prepared = undefined; this.getPairAddress = (tokenA, tokenB) => { const addresses = [tokenA.toBuffer(), tokenB.toBuffer()].sort(buffer_1.Buffer.compare); const pairArr = web3_js_1.PublicKey.findProgramAddressSync([ buffer_1.Buffer.from("GFX-SSL-Pair", "utf-8"), this.controller.toBuffer(), addresses[0], addresses[1], ], this.programId); return pairArr[0]; }; } async prepare() { const pair = this.getPairAddress(this.tokenIn, this.tokenOut); const pairData = await this.connection.getAccountInfo(pair); if (!pairData) throw "Cannot get Pair"; const sslIn = ssl_1.SSL.findAddress(this.controller, this.tokenIn, this.programId); const sslInData = await this.connection.getAccountInfo(sslIn); if (!sslInData) throw "Cannot get SSL for tokenIn"; const sslOut = ssl_1.SSL.findAddress(this.controller, this.tokenOut, this.programId); const sslOutData = await this.connection.getAccountInfo(sslOut); if (!sslOutData) throw "Cannot get SSL for tokenOut"; const liabilityVaultIn = await (0, spl_token_2.getAccount)(this.connection, (0, utils_1.findAssociatedTokenAddress)(sslIn, this.tokenIn)); const swappedLiabilityVaultIn = await (0, spl_token_2.getAccount)(this.connection, (0, utils_1.findAssociatedTokenAddress)(sslIn, this.tokenOut)); const liabilityVaultOut = await (0, spl_token_2.getAccount)(this.connection, (0, utils_1.findAssociatedTokenAddress)(sslOut, this.tokenOut)); const swappedLiabilityVaultOut = await (0, spl_token_2.getAccount)(this.connection, (0, utils_1.findAssociatedTokenAddress)(sslOut, this.tokenIn)); const OracleRegistry = wasm.OracleRegistry; const decoded = layouts_1.PAIR_LAYOUT.decode(pairData.data); const { maxDelay, oracles, nOracle } = decoded; const n = Number(nOracle.toString()); let publishedSlots = []; let pythStatus = []; let pythConfs = []; const registry = new OracleRegistry(); for (const oracle of oracles.slice(0, n)) { const n = Number(oracle.n); for (const elem of oracle.elements.slice(0, n)) { const acctInfo = await this.connection.getAccountInfo(elem.address); if (acctInfo?.data) { registry.add_oracle(elem.address.toBuffer(), acctInfo.data); let priceData = (0, client_1.parsePriceData)(acctInfo.data); publishedSlots.push(priceData.aggregate.publishSlot); pythStatus.push(priceData.aggregate.status); let price = priceData.aggregate.price / Math.pow(10, -priceData.exponent); let std = priceData.aggregate.confidence / Math.pow(10, -priceData.exponent); pythConfs.push(price / std); } } } this.prepared = { pairData: pairData.data, sslInData: sslInData.data, sslOutData: sslOutData.data, liabilityIn: liabilityVaultIn.amount, swappedLiabilityIn: swappedLiabilityVaultIn.amount, liabilityOut: liabilityVaultOut.amount, swappedLiabilityOut: swappedLiabilityVaultOut.amount, registry: registry, suspended: (new ssl_1.SSL(sslInData)).isSuspended() || (new ssl_1.SSL(sslOutData)).isSuspended(), publishedSlots: publishedSlots.map((val) => BigInt(val)), pythStatus, pythConfs, confidence: decoded.confidence, maxDelay: maxDelay }; } isSuspended(currentSlot) { if (this.prepared === undefined) throw "Run prepare first"; let suspended = this.prepared.suspended; if (currentSlot !== undefined) { for (const pubSlot of this.prepared.publishedSlots) { suspended || (suspended = pubSlot + this.prepared.maxDelay <= currentSlot); } } for (const pythStatus of this.prepared.pythStatus) { suspended || (suspended = pythStatus !== client_1.PriceStatus.Trading); } for (const conf of this.prepared.pythConfs) { suspended || (suspended = this.prepared.confidence > conf); } return suspended; } quote(inTokenAmount, silent = true, niter = 64) { const swapWASM = wasm.swap; if (inTokenAmount === 0n) return { amountIn: 0n, fee: 0n, amountOut: 0n, impact: 0, swapPrice: 0, instantPrice: 0, oraclePrice: 0, }; if (this.prepared === undefined) throw "Run prepare first"; const prepared = this.prepared; let out; try { out = swapWASM(prepared.sslInData.slice(), prepared.sslOutData.slice(), prepared.pairData.slice(), prepared.liabilityIn, prepared.liabilityOut, prepared.swappedLiabilityIn, prepared.swappedLiabilityOut, prepared.registry, inTokenAmount, niter); } catch (e) { if (silent) { return { amountIn: inTokenAmount, fee: 0n, amountOut: 0n, impact: 1, swapPrice: 0, instantPrice: 0, oraclePrice: 0, }; } else { throw e; } } return { amountIn: out.amount_in, fee: out.fee_paid, amountOut: out.amount_out, impact: out.price_impact, swapPrice: out.swap_price, instantPrice: out.insta_price, oraclePrice: out.oracle_price, }; } } //# sourceMappingURL=swap.js.map