@symmetry-hq/liquidity-sdk
Version:
Exchange functionality using symmetry funds liquidity
188 lines (187 loc) • 9.08 kB
JavaScript
"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 __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TokenSwap = void 0;
const anchor_1 = require("@coral-xyz/anchor");
const nodewallet_1 = __importDefault(require("@coral-xyz/anchor/dist/cjs/nodewallet"));
const web3_js_1 = require("@solana/web3.js");
const types_1 = require("./types");
const fundsIDL_1 = require("./fundsIDL");
const decoder_1 = require("./decoder");
const executeSwap_1 = require("./executeSwap");
const liquidity_1 = require("./liquidity");
__exportStar(require("./types"), exports);
class TokenSwap {
constructor(accountInfos, connection, wallet) {
let { tokenListAccountInfo, curveDataAccountInfo, oracleDataAccountInfos, fundStateAccountInfos, } = accountInfos;
if (!wallet)
wallet = new nodewallet_1.default(web3_js_1.Keypair.generate());
let provider = new anchor_1.AnchorProvider(connection, wallet, {
skipPreflight: true,
preflightCommitment: "confirmed",
commitment: "confirmed",
});
this.connection = connection;
this.wallet = wallet;
this.program = new anchor_1.Program(fundsIDL_1.IDL, types_1.FUNDS_PROGRAM_ID, provider);
this.tokenListData = (0, decoder_1.decodeTokenList)(this.program, tokenListAccountInfo);
this.curveChainData = (0, decoder_1.decodeCurveData)(this.program, curveDataAccountInfo);
this.funds = (0, decoder_1.decodeFunds)(this.program, fundStateAccountInfos);
this.oraclePrices = (0, decoder_1.decodeOraclePrices)(oracleDataAccountInfos, this.tokenListData);
}
static getAccountInfosForTokenSwap(connection) {
return __awaiter(this, void 0, void 0, function* () {
//@ts-ignore
let [tokenListAccountInfo, curveDataAccountInfo] = yield connection
.getMultipleAccountsInfo([types_1.TOKEN_LIST_ADDRESS, types_1.CURVE_DATA_ADDRESS]);
let oracleDataPubkeys = [];
let numTokens = new anchor_1.BN(tokenListAccountInfo.data.slice(8, 16), 10, "le").toNumber();
for (let i = 0; i < numTokens; i++) {
let start = 199 * i + 16 + 96;
let end = 199 * i + 16 + 96 + 32;
oracleDataPubkeys.push(new web3_js_1.PublicKey(tokenListAccountInfo.data.slice(start, end)));
}
//@ts-ignore
let oracleDataAccountInfos = yield connection
.getMultipleAccountsInfo(oracleDataPubkeys);
let fundStateAccountInfos = yield connection.getProgramAccounts(types_1.FUNDS_PROGRAM_ID, {
commitment: connection.commitment,
filters: [
{ dataSize: 10208 },
{ memcmp: { offset: 112, bytes: "11111111" } }
],
encoding: 'base64'
});
return {
tokenListAccountInfo: tokenListAccountInfo,
curveDataAccountInfo: curveDataAccountInfo,
oracleDataAccountInfos: oracleDataAccountInfos,
fundStateAccountInfos: fundStateAccountInfos,
};
});
}
static init(connection, wallet) {
return __awaiter(this, void 0, void 0, function* () {
return new TokenSwap(yield this.getAccountInfosForTokenSwap(connection), connection, wallet);
});
}
setWallet(wallet) {
this.wallet = wallet;
let provider = new anchor_1.AnchorProvider(this.connection, wallet, {
skipPreflight: true,
preflightCommitment: "confirmed",
commitment: "confirmed",
});
this.program = new anchor_1.Program(fundsIDL_1.IDL, types_1.FUNDS_PROGRAM_ID, provider);
}
getTokenList() {
return this.tokenListData.filter(x => x.isLive == true).map(x => {
return {
tokenId: x.id,
coingeckoId: x.coingeckoId,
tokenMint: x.tokenMint,
};
});
}
getAccountsForUpdate() {
return [
types_1.CURVE_DATA_ADDRESS,
...this.tokenListData.map(token => new web3_js_1.PublicKey(token.oracleAccount)),
...this.funds.map(fund => fund.pubkey),
];
}
update(accountInfos) {
this.curveChainData = (0, decoder_1.decodeCurveData)(this.program, accountInfos[0]);
this.oraclePrices = (0, decoder_1.decodeOraclePrices)(accountInfos.slice(1, 1 + this.tokenListData.length), this.tokenListData);
this.funds = (0, decoder_1.decodeFunds)(this.program, accountInfos.slice(1 + this.tokenListData.length, accountInfos.length)
.map((info, id) => {
return {
pubkey: this.funds[id].pubkey,
account: info,
};
}));
}
updateLiquiditySources() {
return __awaiter(this, void 0, void 0, function* () {
let accounts = this.getAccountsForUpdate();
let results = [];
for (let i = 0; i < accounts.length; i += 100) {
//@ts-ignore
let batch = yield this.connection
.getMultipleAccountsInfo(accounts.slice(i, i + 100));
results = [...results, ...batch];
}
this.update(results);
});
}
findTokenId(tokenMint) {
for (let i = 0; i < this.tokenListData.length; i++)
if (this.tokenListData[i].tokenMint == tokenMint.toBase58())
return this.tokenListData[i].id;
return undefined;
}
getRouteData(tokenFrom, tokenTo, fromAmount) {
let tokenIdFrom = this.findTokenId(tokenFrom);
let tokenIdTo = this.findTokenId(tokenTo);
return (0, liquidity_1.loadRouteData)(this.tokenListData, this.curveChainData, this.funds, this.oraclePrices,
//@ts-ignore
tokenIdFrom, tokenIdTo, fromAmount);
}
getAllRoutes(tokenFrom, tokenTo, fromAmount) {
let tokenIdFrom = this.findTokenId(tokenFrom);
let tokenIdTo = this.findTokenId(tokenTo);
let possibleRoutes = this.funds
.map(fund => (0, liquidity_1.checkForLiquidity)(this.tokenListData, this.curveChainData, fund.pubkey, fund.fund, this.oraclePrices,
//@ts-ignore
tokenIdFrom, tokenIdTo,
//@ts-ignore
Math.floor(fromAmount * 10 ** this.tokenListData[tokenIdFrom].decimals)));
//@ts-ignore
return possibleRoutes.filter(route => route != undefined).sort((a, b) => b.toAmount - a.toAmount);
}
executeSwap(routeData, fromTokenAccount, toTokenAccount, slippage) {
return __awaiter(this, void 0, void 0, function* () {
if (!this.wallet)
throw new Error("Wallet not provided");
return yield (0, executeSwap_1.executeSwap)(this.program, //@ts-ignore
this.wallet, this.connection, this.tokenListData, routeData, fromTokenAccount, toTokenAccount, slippage);
});
}
generateSwapInstruction(routeData, fromTokenAccount, toTokenAccount, user, slippage) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
if (!user && !this.wallet)
throw new Error("Wallet not provided");
if (!user)
user = (_a = this.wallet) === null || _a === void 0 ? void 0 : _a.publicKey;
return yield (0, executeSwap_1.generateSwapInstruction)(this.program, //@ts-ignore
user, this.tokenListData, routeData, fromTokenAccount, toTokenAccount, slippage);
});
}
}
exports.TokenSwap = TokenSwap;