diff --git a/contracts/main/TTDex.ligo b/contracts/main/TTDex.ligo index 691ff562..21c2a630 100644 --- a/contracts/main/TTDex.ligo +++ b/contracts/main/TTDex.ligo @@ -14,6 +14,6 @@ function main (const p : full_action; const s : full_dex_storage) : full_return | Update_operators(params) -> call_token(IUpdate_operators(params), this, 1n, s) | Get_reserves(params) -> get_reserves(params, s) | Close -> ((nil:list(operation)), close(s)) - | SetDexFunction(params) -> ((nil:list(operation)), if params.index > 4n then (failwith("Dex/wrong-index") : full_dex_storage) else set_dex_function(params.index, params.func, s)) + | SetDexFunction(params) -> ((nil:list(operation)), if params.index > 3n then (failwith("Dex/wrong-index") : full_dex_storage) else set_dex_function(params.index, params.func, s)) | SetTokenFunction(params) -> ((nil:list(operation)), if params.index > 2n then (failwith("Dex/wrong-index") : full_dex_storage) else set_token_function(params.index, params.func, s)) end diff --git a/contracts/partials/ITTDex.ligo b/contracts/partials/ITTDex.ligo index 96954cd8..099d5c7d 100644 --- a/contracts/partials/ITTDex.ligo +++ b/contracts/partials/ITTDex.ligo @@ -87,17 +87,6 @@ type token_to_token_route_params is receiver : address; (* tokens receiver *) ] -(* Entrypoint arguments *) -type token_to_token_payment_params is - [@layout:comb] - record [ - pair : tokens_info; - operation : swap_type; - amount_in : nat; (* amount of tokens to be exchanged *) - min_amount_out : nat; (* min amount of XTZ received to accept exchange *) - receiver : address; (* tokens receiver *) - ] - type invest_liquidity_params is [@layout:comb] record [ @@ -118,7 +107,6 @@ type divest_liquidity_params is type dex_action is | InitializeExchange of invest_liquidity_params (* sets initial liquidity *) | TokenToTokenRoutePayment of token_to_token_route_params (* exchanges XTZ to tokens and sends them to receiver *) -| TokenToTokenPayment of token_to_token_payment_params (* exchanges XTZ to tokens and sends them to receiver *) | InvestLiquidity of invest_liquidity_params (* mints min shares after investing tokens and XTZ *) | DivestLiquidity of divest_liquidity_params (* burns shares and sends tokens and XTZ to the owner *) diff --git a/contracts/partials/TTDex.ligo b/contracts/partials/TTDex.ligo index 1c74ef70..3bbfdd4a 100644 --- a/contracts/partials/TTDex.ligo +++ b/contracts/partials/TTDex.ligo @@ -15,10 +15,9 @@ based on the argument type. block { const idx : nat = case p of | InitializeExchange(n) -> 0n - | TokenToTokenPayment(n) -> 1n - | TokenToTokenRoutePayment(n) -> 2n - | InvestLiquidity(n) -> 3n - | DivestLiquidity(n) -> 4n + | TokenToTokenRoutePayment(n) -> 1n + | InvestLiquidity(n) -> 2n + | DivestLiquidity(n) -> 3n end; const res : return = case s.dex_lambdas[idx] of Some(f) -> f(p, s.storage, this) diff --git a/contracts/partials/TTMethodDex.ligo b/contracts/partials/TTMethodDex.ligo index a4dec575..470994b5 100644 --- a/contracts/partials/TTMethodDex.ligo +++ b/contracts/partials/TTMethodDex.ligo @@ -271,105 +271,12 @@ function initialize_exchange( params.pair.token_b_type ) # operations; } - | TokenToTokenPayment(n) -> skip | TokenToTokenRoutePayment(n) -> skip | InvestLiquidity(n) -> skip | DivestLiquidity(n) -> skip end } with (operations, s) -(* Exchange tokens to tokens, -note: tokens should be approved before the operation *) -function token_to_token( - const p : dex_action; - const s : dex_storage; - const this : address) : return is - block { - var operations: list(operation) := list[ - Tezos.transaction( - unit, - 0mutez, - get_close_entrypoint(this) - )]; - case p of - InitializeExchange(n) -> skip - | TokenToTokenRoutePayment(n) -> skip - | TokenToTokenPayment(params) -> { - if s.entered - then failwith("Dex/reentrancy") - else s.entered := True; - - (* check preconditions *) - if params.pair.token_a_address = params.pair.token_b_address - and params.pair.token_a_id >= params.pair.token_b_id - then failwith("Dex/wrong-token-id") else skip; - if params.pair.token_a_address > params.pair.token_b_address - then failwith("Dex/wrong-pair") else skip; - - (* get par info*) - const res : (pair_info * nat) = get_pair(params.pair, s); - const pair : pair_info = res.0; - const token_id : nat = res.1; - - (* ensure there is liquidity *) - if pair.token_a_pool * pair.token_b_pool = 0n - then failwith("Dex/not-launched") else skip; - if params.amount_in = 0n (* non-zero amount of tokens exchanged *) - then failwith ("Dex/zero-amount-in") else skip; - if params.min_amount_out = 0n (* non-zero amount of tokens exchanged *) - then failwith ("Dex/zero-min-amount-out") else skip; - - - (* calculate amount out *) - const swap: swap_data = form_swap_data( - pair, - params.pair, - params.operation); - const from_in_with_fee : nat = params.amount_in * 997n; - const numerator : nat = from_in_with_fee * swap.to_.pool; - const denominator : nat = swap.from_.pool * 1000n + from_in_with_fee; - - (* calculate swapped token amount *) - const out : nat = numerator / denominator; - - (* ensure requirements *) - if out < params.min_amount_out (* minimal XTZ amount out is sutisfied *) - then failwith("Dex/wrong-min-out") else skip; - if out > swap.to_.pool / 3n (* the price impact isn't too high *) - then failwith("Dex/high-out") else skip; - - (* update XTZ pool *) - swap.to_.pool := abs(swap.to_.pool - out); - swap.from_.pool := swap.from_.pool + params.amount_in; - - const updated_pair : pair_info = form_pools( - swap.from_.pool, - swap.to_.pool, - pair.total_supply, - params.operation); - s.pairs[token_id] := updated_pair; - - (* prepare operations to withdraw user's tokens and transfer XTZ *) - operations := - (* from *) - typed_transfer(Tezos.sender, this, params.amount_in, - swap.from_.id, - swap.from_.token, - swap.from_.standard - ) # operations; - operations := - (* to *) - typed_transfer(this, params.receiver, out, - swap.to_.id, - swap.to_.token, - swap.to_.standard - ) # operations; - } - | InvestLiquidity(n) -> skip - | DivestLiquidity(n) -> skip - end - } with (operations, s) - (* Intrenal functions for swap hops *) function internal_token_to_token_swap( const tmp : internal_swap_type; @@ -449,14 +356,13 @@ function token_to_token_route( )]; case p of | InitializeExchange(n) -> skip - | TokenToTokenPayment(n) -> skip | TokenToTokenRoutePayment(params) -> { if s.entered then failwith("Dex/reentrancy") else s.entered := True; (* validate input params *) - if List.size(params.swaps) < 2n (* non-zero amount of tokens exchanged *) + if List.size(params.swaps) < 1n (* non-zero amount of tokens exchanged *) then failwith ("Dex/too-few-swaps") else skip; if params.amount_in = 0n (* non-zero amount of tokens exchanged *) then failwith ("Dex/zero-amount-in") else skip; @@ -550,7 +456,6 @@ function invest_liquidity( case p of | InitializeExchange(n) -> skip | TokenToTokenRoutePayment(n) -> skip - | TokenToTokenPayment(n) -> skip | InvestLiquidity(params) -> { if s.entered then failwith("Dex/reentrancy") @@ -660,7 +565,6 @@ function divest_liquidity( )]; case p of | InitializeExchange(token_amount) -> skip - | TokenToTokenPayment(n) -> skip | TokenToTokenRoutePayment(n) -> skip | InvestLiquidity(n) -> skip | DivestLiquidity(params) -> { diff --git a/storage/TTFunctions.js b/storage/TTFunctions.js index 190c4807..104d256d 100644 --- a/storage/TTFunctions.js +++ b/storage/TTFunctions.js @@ -5,18 +5,14 @@ module.exports.dexFunctions = [ }, { index: 1, - name: "token_to_token", - }, - { - index: 2, name: "token_to_token_route", }, { - index: 3, + index: 2, name: "invest_liquidity", }, { - index: 4, + index: 3, name: "divest_liquidity", }, ]; diff --git a/test/helpers/ttdexFA2.ts b/test/helpers/ttdexFA2.ts index bd6c4f08..3f486421 100644 --- a/test/helpers/ttdexFA2.ts +++ b/test/helpers/ttdexFA2.ts @@ -224,23 +224,29 @@ export class TTDex extends TokenFA2 { ); } } + const swaps = [ + { + pair: { + token_a_address: tokenAAddress, + token_b_address: tokenBAddress, + token_a_id: tokenAid, + token_b_id: tokenBid, + token_a_type: { + [standard.toLowerCase() == "mixed" + ? "fa2" + : standard.toLowerCase()]: null, + }, + token_b_type: { + [standard.toLowerCase() == "mixed" + ? "fa12" + : standard.toLowerCase()]: null, + }, + }, + operation: { [opType]: null }, + }, + ]; const operation = await this.contract.methods - .use( - "tokenToTokenPayment", - tokenAAddress, - tokenAid, - standard.toLowerCase() == "mixed" ? "fa2" : standard.toLowerCase(), - null, - tokenBAddress, - tokenBid, - standard.toLowerCase() == "mixed" ? "fa12" : standard.toLowerCase(), - null, - opType, - null, - amountIn, - minAmountOut, - receiver - ) + .use("tokenToTokenRoutePayment", swaps, amountIn, minAmountOut, receiver) .send(); await confirmOperation(tezos, operation.hash); return operation; diff --git a/test/helpers/ttdexFA2FA12.ts b/test/helpers/ttdexFA2FA12.ts deleted file mode 100644 index ba7d8fc7..00000000 --- a/test/helpers/ttdexFA2FA12.ts +++ /dev/null @@ -1,280 +0,0 @@ -import { ContractAbstraction, ContractProvider } from "@taquito/taquito"; -import { BatchOperation } from "@taquito/taquito/dist/types/operations/batch-operation"; -import { TransactionOperation } from "@taquito/taquito/dist/types/operations/transaction-operation"; -import { BigNumber } from "bignumber.js"; -import { TokenFA2 } from "./tokenFA2"; -import { TTDexStorage } from "./types"; -import { getLigo } from "./utils"; -import { execSync } from "child_process"; -import { confirmOperation } from "./confirmation"; - -const standard = process.env.EXCHANGE_TOKEN_STANDARD; - -export class TTDex extends TokenFA2 { - public contract: ContractAbstraction; - public storage: TTDexStorage; - - constructor(contract: ContractAbstraction) { - super(contract); - } - - static async init(dexAddress: string): Promise { - return new TTDex(await tezos.contract.at(dexAddress)); - } - - async updateStorage( - maps: { - tokens?: string[]; - token_to_id?: string[]; - pairs?: string[]; - ledger?: any[]; - dex_lambdas?: number[]; - token_lambdas?: number[]; - } = {} - ): Promise { - const storage: any = await this.contract.storage(); - this.storage = { - pairs_count: storage.storage.pairs_count, - tokens: {}, - token_to_id: {}, - pairs: {}, - ledger: {}, - dex_lambdas: {}, - token_lambdas: {}, - }; - for (let key in maps) { - if (["dex_lambdas", "token_lambdas"].includes(key)) continue; - this.storage[key] = await maps[key].reduce(async (prev, current) => { - try { - return { - ...(await prev), - [key == "ledger" ? current[0] : current]: await storage.storage[ - key - ].get(current), - }; - } catch (ex) { - console.log(ex); - return { - ...(await prev), - }; - } - }, Promise.resolve({})); - } - for (let key in maps) { - if (!["dex_lambdas", "token_lambdas"].includes(key)) continue; - this[key] = await maps[key].reduce(async (prev, current) => { - try { - return { - ...(await prev), - [current]: await storage[key].get(current.toString()), - }; - } catch (ex) { - return { - ...(await prev), - }; - } - }, Promise.resolve({})); - } - } - - async initializeExchange( - tokenAAddress: string, - tokenBAddress: string, - tokenAAmount: number, - tokenBAmount: number, - tokenAid: BigNumber = new BigNumber(0), - approve: boolean = true - ): Promise { - if (approve) { - await this.approveFA2Token( - tokenAAddress, - tokenAid, - tokenAAmount, - this.contract.address - ); - await this.approveFA12Token( - tokenBAddress, - tokenBAmount, - this.contract.address - ); - } - const operation = await this.contract.methods - .use( - "initializeExchange", - tokenAAddress, - tokenAid, - tokenBAddress, - tokenAAmount, - tokenBAmount - ) - .send(); - await confirmOperation(tezos, operation.hash); - return operation; - } - - async tokenToTokenPayment( - tokenAAddress: string, - tokenBAddress: string, - opType: string, - amountIn: number, - minAmountOut: number, - receiver: string, - tokenAid: BigNumber = new BigNumber(0) - ): Promise { - if (opType == "buy") { - await this.approveFA12Token( - tokenBAddress, - amountIn, - this.contract.address - ); - } else { - await this.approveFA2Token( - tokenAAddress, - tokenAid, - amountIn, - this.contract.address - ); - } - const operation = await this.contract.methods - .use( - "tokenToTokenPayment", - tokenAAddress, - tokenAid, - tokenBAddress, - opType, - null, - amountIn, - minAmountOut, - receiver - ) - .send(); - await confirmOperation(tezos, operation.hash); - return operation; - } - - async investLiquidity( - pairId: string, - tokenAAmount: number, - tokenBAmount: number, - minShares: number - ): Promise { - await this.updateStorage({ tokens: [pairId] }); - let pair = this.storage.tokens[pairId]; - await this.approveFA2Token( - pair.token_a_address, - pair.token_a_id, - tokenAAmount, - this.contract.address - ); - await this.approveFA12Token( - pair.token_b_address, - tokenBAmount, - this.contract.address - ); - const operation = await this.contract.methods - .use( - "investLiquidity", - pair.token_a_address, - pair.token_a_id, - pair.token_b_address, - tokenAAmount, - tokenBAmount, - minShares - ) - .send(); - await confirmOperation(tezos, operation.hash); - return operation; - } - - async divestLiquidity( - pairId: string, - tokenAAmount: number, - tokenBAmount: number, - sharesBurned: number - ): Promise { - await this.updateStorage({ tokens: [pairId] }); - let pair = this.storage.tokens[pairId]; - const operation = await this.contract.methods - .use( - "divestLiquidity", - pair.token_a_address, - pair.token_a_id, - pair.token_b_address, - tokenAAmount, - tokenBAmount, - sharesBurned - ) - .send(); - await confirmOperation(tezos, operation.hash); - return operation; - } - - async approveFA2Token( - tokenAddress: string, - tokenId: BigNumber, - tokenAmount: number, - address: string - ): Promise { - await this.updateStorage(); - let token = await tezos.contract.at(tokenAddress); - let operation = await token.methods - .update_operators([ - { - [tokenAmount ? "add_operator" : "remove_operator"]: { - owner: await tezos.signer.publicKeyHash(), - operator: address, - token_id: tokenId, - }, - }, - ]) - .send(); - await confirmOperation(tezos, operation.hash); - return operation; - } - - async approveFA12Token( - tokenAddress: string, - tokenAmount: number, - address: string - ): Promise { - await this.updateStorage(); - let token = await tezos.contract.at(tokenAddress); - let operation = await token.methods.approve(address, tokenAmount).send(); - await confirmOperation(tezos, operation.hash); - return operation; - } - - async setDexFunction(index: number, lambdaName: string): Promise { - let ligo = getLigo(true); - const stdout = execSync( - `${ligo} compile-parameter --michelson-format=json $PWD/contracts/main/TTDex${standard}.ligo main 'SetDexFunction(record index =${index}n; func = ${lambdaName}; end)'`, - { maxBuffer: 1024 * 500 } - ); - const operation = await tezos.contract.transfer({ - to: this.contract.address, - amount: 0, - parameter: { - entrypoint: "setDexFunction", - value: JSON.parse(stdout.toString()).args[0].args[0].args[0], - }, - }); - await confirmOperation(tezos, operation.hash); - } - - async setTokenFunction(index: number, lambdaName: string): Promise { - let ligo = getLigo(true); - const stdout = execSync( - `${ligo} compile-parameter --michelson-format=json $PWD/contracts/main/TTDex${standard}.ligo main 'SetTokenFunction(record index =${index}n; func = ${lambdaName}; end)'`, - { maxBuffer: 1024 * 500 } - ); - const operation = await tezos.contract.transfer({ - to: this.contract.address, - amount: 0, - parameter: { - entrypoint: "setTokenFunction", - value: JSON.parse(stdout.toString()).args[0].args[0].args[0], - }, - }); - await confirmOperation(tezos, operation.hash); - } -} diff --git a/test/storage/TTFunctions.ts b/test/storage/TTFunctions.ts index 7aab2093..8cceb0f3 100644 --- a/test/storage/TTFunctions.ts +++ b/test/storage/TTFunctions.ts @@ -5,18 +5,14 @@ export let dexFunctions = [ }, { index: 1, - name: "token_to_token", - }, - { - index: 2, name: "token_to_token_route", }, { - index: 3, + index: 2, name: "invest_liquidity", }, { - index: 4, + index: 3, name: "divest_liquidity", }, ];