UNPKG

nomadex-client

Version:

Client library to programmatically interact with https://voi.nomadex.app

232 lines (231 loc) 9.54 kB
import { ABIType, AtomicTransactionComposer, getApplicationAddress, makeAssetTransferTxnWithSuggestedParamsFromObject, makeEmptyTransactionSigner, makePaymentTxnWithSuggestedParamsFromObject, waitForConfirmation, } from 'algosdk'; import { PoolClient } from './PoolClient'; import { MySmartAsset } from './MySmartAsset'; import { SmartAssetClient } from './SmartAssetClient'; import { contracts } from '../constants'; import { populateAppCallResources } from '@algorandfoundation/algokit-utils'; export var TokenType; (function (TokenType) { TokenType[TokenType["ALGO"] = 0] = "ALGO"; TokenType[TokenType["ASA"] = 1] = "ASA"; TokenType[TokenType["SMART"] = 2] = "SMART"; })(TokenType || (TokenType = {})); export class MyPool extends MySmartAsset { constructor(id, network, nodeClient, signer) { super(id, nodeClient, signer); this.id = id; this.poolClient = new PoolClient({ id: id, resolveBy: 'id', sender: signer ?? { addr: 'DYX2V5XF4IKOHE55Z63XAHVBJTMYM723HK5WJZ72BDZ5AFEFKJ5YP4DOQQ', signer: makeEmptyTransactionSigner(), }, }, nodeClient); this.network = network; this.algod = nodeClient; this.signer = signer; } async buildDepositTxn(token, amount) { if (token.type === 0) { return makePaymentTxnWithSuggestedParamsFromObject({ from: this.signer.addr, to: getApplicationAddress(this.id), amount: amount, suggestedParams: await this.algod.getTransactionParams().do(), }); } else if (token.type === 1) { return makeAssetTransferTxnWithSuggestedParamsFromObject({ assetIndex: token.id, from: this.signer.addr, to: getApplicationAddress(this.id), amount: amount, suggestedParams: await this.algod.getTransactionParams().do(), }); } else if (token.type === 2) { const smartAssetClient = new SmartAssetClient({ id: token.id, resolveBy: 'id', sender: this.signer, }, this.algod); const { transaction } = await smartAssetClient.arc200Transfer({ to: getApplicationAddress(this.id), value: amount, }, { sendParams: { populateAppCallResources: true, skipSending: true, }, }); return transaction; } throw Error(''); } async makeOptinTxns(token) { const params = await this.algod.getTransactionParams().do(); const txns = []; if (token.type === TokenType.ASA) { let isOptedIn = false; try { const info = await this.algod.accountAssetInformation(this.signer.addr, token.id).do(); if (typeof info?.['asset-holding']?.amount !== 'undefined') isOptedIn = true; } catch (e) { if (e) { // } } if (!isOptedIn) { txns.push(makeAssetTransferTxnWithSuggestedParamsFromObject({ assetIndex: token.id, from: this.signer.addr, to: this.signer.addr, amount: 0, suggestedParams: params, })); } } else if (token.type === TokenType.SMART) { const factoryAddress = getApplicationAddress(contracts[this.network].poolFcatory); const factoryBalance = await MySmartAsset.from(token.id, this.algod).arc200BalanceOf(factoryAddress); const userBalance = await MySmartAsset.from(token.id, this.algod).arc200BalanceOf(this.signer.addr); const getTxns = async (address) => { const client = new SmartAssetClient({ id: token.id, resolveBy: 'id', sender: { addr: this.signer.addr, signer: makeEmptyTransactionSigner(), }, }, this.algod); return [ makePaymentTxnWithSuggestedParamsFromObject({ from: this.signer.addr, to: getApplicationAddress(token.id), amount: 28500, suggestedParams: params, note: crypto.getRandomValues(new Uint8Array(4)) }), (await client.arc200Transfer({ to: address, value: 0 }, { sendParams: { skipSending: true } })) .transaction, ]; }; if (factoryBalance < 1n) { txns.push(...(await getTxns(factoryAddress))); } if (userBalance < 1n) { txns.push(...(await getTxns(this.signer.addr))); } } return txns; } async addLiquidityHandler(tokenA, tokenB, assetAAmount, assetBAmount, send, populate) { const opts = { id: this.id, resolveBy: 'id', sender: { ...this.signer }, }; const alphaTxn = await this.buildDepositTxn(tokenA, assetAAmount); const betaTxn = await this.buildDepositTxn(tokenB, assetBAmount); const args = { alphaTxn, betaTxn }; const callOpts = { sendParams: { populateAppCallResources: populate, skipSending: !send, }, }; if (!send) opts.sender.signer = makeEmptyTransactionSigner(); const poolClient = new PoolClient(opts, this.algod); const resp = await poolClient.addLiquidity(args, callOpts); return resp; } async addLiquidity(tokenA, tokenB, assetAAmount, assetBAmount) { const result = await this.addLiquidityHandler(tokenA, tokenB, assetAAmount, assetBAmount, true, true); return result.return; } async buildAddLiquidityTxns(tokenA, tokenB, assetAAmount, assetBAmount, populate) { const result = await this.addLiquidityHandler(tokenA, tokenB, assetAAmount, assetBAmount, false, populate); return result.transactions; } async removeLiquidityHandler(lptAmount, send, populate) { const opts = { id: this.id, resolveBy: 'id', sender: { ...this.signer }, }; const args = { lptAmount: lptAmount }; const callOpts = { sendParams: { populateAppCallResources: true, skipSending: !send }, }; if (!send) opts.sender.signer = makeEmptyTransactionSigner(); const poolClient = new PoolClient(opts, this.algod); const resp = await poolClient.removeLiquidity(args, callOpts); return resp; } async removeLiquidity(lptAmount) { const result = await this.removeLiquidityHandler(lptAmount, true, true); return result.return; } async buildRemoveLiquidityTxns(lptAmount, populate) { const result = await this.removeLiquidityHandler(lptAmount, false, populate); return result.transactions; } async swap(fromToken, toToken, fromAmount, toAmountMin, isDirectionAlphaToBeta) { const finalTxns = await this.buildSwapTxns(fromToken, toToken, fromAmount, toAmountMin, isDirectionAlphaToBeta, true); const signed = await this.signer.signer(finalTxns, []); await this.algod.sendRawTransaction(signed).do(); const result = await waitForConfirmation(this.algod, finalTxns.at(-1)?.txID() ?? '', 3); const log = result.logs.find((log) => log.length === 36); if (log) { return ABIType.from('uint256').decode(log.slice(4)); } } async buildSwapTxns(fromToken, toToken, fromAmount, toAmountMin, isDirectionAlphaToBeta, populate) { const poolClient = new PoolClient({ id: this.id, resolveBy: 'id', sender: this.signer, }, this.algod); const args = { txn: await this.buildDepositTxn(fromToken, fromAmount), minAmount: toAmountMin, }; const txns = []; const optinTxns = await this.makeOptinTxns(toToken); txns.push(...optinTxns); if (isDirectionAlphaToBeta) { const { transactions } = await poolClient.swapAlphaToBeta({ alphaTxn: args.txn, minBetaAmount: args.minAmount, }, { sendParams: { skipSending: true }, }); txns.push(...transactions); } else { const { transactions } = await poolClient.swapBetaToAlpha({ betaTxn: args.txn, minAlphaAmount: args.minAmount, }, { sendParams: { skipSending: true }, }); txns.push(...transactions); } let atc = new AtomicTransactionComposer(); const mpt = makeEmptyTransactionSigner(); for (const txn of txns) { txn.group = undefined; atc.addTransaction({ txn: txn, signer: mpt }); } atc.buildGroup(); if (populate) { atc = await populateAppCallResources(atc, this.algod); } return atc.buildGroup().map(({ txn }) => txn); } }