swapable
Version:
Swapable: Automated Liquidity Pools
188 lines (187 loc) • 9.72 kB
JavaScript
"use strict";
/**
* This file is part of Swapable shared under AGPL-3.0
* Copyright (C) 2021 Using Blockchain Ltd, Reg No.: 12658136, United Kingdom
*
* @package Swapable
* @author Grégory Saive for Using Blockchain Ltd <greg@ubc.digital>
* @license AGPL-3.0
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.RemoveLiquidity = void 0;
const symbol_sdk_1 = require("symbol-sdk");
// internal dependencies
const index_1 = require("../../index");
const Executable_1 = require("./Executable");
//XXX remove this, used only for type-discovery in command options below.
const Symbol_Testnet_SWP = new index_1.AssetIdentifier('00000001', new symbol_sdk_1.PublicAccount());
const Symbol_Testnet_XYM = new index_1.AssetIdentifier('00000002', new symbol_sdk_1.PublicAccount());
/**
* @class Swapable.RemoveLiquidity
* @package Swapable
* @subpackage Commands
* @since v1.0.0
* @description Class that describes a command for removing assets
* in automated liquidity pools (i.e. "withdrawal").
* @summary
* This automated pool command accepts the following arguments:
*
* | Argument | Description | Example |
* | --- | --- | --- |
* | provider | Liquidity provider | `new PublicAccount(...)` |
* | input_x | Amount and asset identifier of `x` (first in pair) | `new AssetAmount(...)` |
* | input_y | Amount and asset identifier of `y` (second in pair) | `new AssetAmount(...)` |
*
* The execution of this command results in the creation of
* the following list of transactions with their respective
* *signer* and a description:
*
* | Sequence | Type | Signer | Description |
* | --- | --- | --- | --- |
* | 01 | TransferTransaction | Provider Account | Transfers a proportional amount of automated pool shares to the **target** account, where they will be burned. |
* | 02 | MosaicSupplyChangeTransaction | Target Account | Removes the amount of automated pool shares that is proportional to the amount of liquidity removed from the pool. |
* | 03 | TransferTransaction | Target Account | Transfers the **removed liquidity** of `x` and `y` to the provider account. |
* | 04 | TransferTransaction | Provider Account | Adds an execution proof message sent to the **target** account. |
*
*/
class RemoveLiquidity extends Executable_1.Executable {
constructor() {
super(...arguments);
/**
* @access public
* @description The list of **required** arguments to execute
* *this* automated pool command.
*/
this.arguments = [
'provider',
'input_x',
'input_y',
];
// end-region abstract methods
}
/**
* Verifies **allowance** of \a actor to execute a command
* with arguments \a argv. This method returns true if all
* required arguments are present.
*
* This method asserts the presence of mandatory arguments.
*
* @access public
* @param {PublicAccount} actor The actor is whom executes the command.
* @param {Array<CommandOption>} argv The command options (arguments).
* @return {AllowanceResult} Returns whether an actor is authorized to execute this command.
* @throws {FailureMissingArgument} On missing mandatory argument(s).
**/
canExecute(actor, argv) {
// - Asserts the presence of mandatory inputs
super.assertHasMandatoryArguments(argv, this.arguments);
// - Allows anyone to add liquidity to automated pools
// given a connected command execution (read-only).
return new index_1.AllowanceResult(!!this.reserveInfo && !!this.mosaicInfo);
}
// region abstract methods
/**
* This method returns the automated pool command name,
* e.g. "CreatePool" or "AddLiquidity", etc.
*
* @access public
* @return {string}
**/
get name() {
return 'RemoveLiquidity';
}
/**
* This method MUST return a unique automated pool command
* descriptor which includes:
*
* - the open standard descriptor (e.g.: "Swapable") ;
* - the open standard *revision* (e.g.: 1) ;
* - the kebab-case command name (e.g.: "create-pool") ;
* - and the automated pool shares asset identifier.
*
* Items are joined with the `:` operator and attached to a
* so-called execution proof transaction.
*
* @access public
* @return {string}
**/
get descriptor() {
return 'Swapable(v' + this.context.revision + ')' + ':remove-liquidity:' + this.identifier.id;
}
/**
* This method returns a list of unsigned transactions in a
* sequencial order of execution. The resulting transaction
* array is later wrapped inside a digital contract that is
* executed atomically such that either all transactions do
* succeed or all transactions are cancelled.
*
* :warning: This method creates at least one- or more than
* one - network-wide **account restriction**. Restrictions
* can potentially lock you out of your account, please use
* this only with caution and if you understand the risks.
*
* @see {execute()}
* @access public
* @return {Transaction[]} Given the execution of a command, returns a list of unsigned transactions.
**/
get transactions() {
var _a, _b, _c, _d, _e;
// - Reads the execution context
const reader = this.context.reader;
// - Reads external arguments
const provider = this.context.getInput('provider', new symbol_sdk_1.PublicAccount());
const input_x = this.context.getInput('input_x', new index_1.AssetAmount(Symbol_Testnet_SWP, 10));
const input_y = this.context.getInput('input_y', new index_1.AssetAmount(Symbol_Testnet_XYM, 10));
// - Reads shares and reserves information
const supply_lp = (_c = (_b = (_a = this.mosaicInfo) === null || _a === void 0 ? void 0 : _a.supply) === null || _b === void 0 ? void 0 : _b.compact()) !== null && _c !== void 0 ? _c : 0;
const reserve_x = this.reserveOf(input_x.identifier);
const reserve_y = this.reserveOf(input_y.identifier);
// MINT FEE principle (0.05 % of shares issued),
// https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2Pair.sol#L117
// if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)
//XXX if (pool_exists)
//XXX rootK = Math.sqrt(reserve0 * reserve1)
//XXX rootKLast = Math.sqrt(kLast)
//XXX if (rootK > rootKLast)
//XXX numerator = totalSupply.mul(rootK.sub(rootKLast));
//XXX denominator = rootK.mul(5).add(rootKLast);
//XXX liquidity = numerator / denominator;
//XXX if (liquidity > 0) _mint(feeTo, liquidity);
//XXX else kLast = 0
// - Calculate liquidity proportions ("contribution of provider")
const liquidity = Math.min((input_x.amount * supply_lp) / reserve_x, (input_y.amount * supply_lp) / reserve_y);
// - Prepares the response
const transactions = [];
const signers = [];
// - Transaction 01: Transfers proportional automated pool shares to liquidity provider
transactions.push(symbol_sdk_1.TransferTransaction.create(this.context.parameters.deadline, this.target.address, [
new symbol_sdk_1.Mosaic(this.identifier.toMosaicId(), symbol_sdk_1.UInt64.fromUint(liquidity))
], symbol_sdk_1.EmptyMessage, reader.networkType, undefined));
// - Transaction 01 is issued by **provider** account
signers.push(provider);
// - Transaction 02: MosaicSupplyChangeTransaction
// :note: The canExecute() method verifies that `mosaicInfo` is set.
// :warning: This transaction effectively **burns** mosaics.
transactions.push(symbol_sdk_1.MosaicSupplyChangeTransaction.create(this.context.parameters.deadline, (_d = this.mosaicInfo) === null || _d === void 0 ? void 0 : _d.id, symbol_sdk_1.MosaicSupplyChangeAction.Decrease, symbol_sdk_1.UInt64.fromUint(liquidity), reader.networkType, undefined));
// - Transaction 02 is issued by **target** account
signers.push(this.target);
// - Transaction 03: Transfers the removed liquidity of `x` and `y` to the provider account.
transactions.push(symbol_sdk_1.TransferTransaction.create(this.context.parameters.deadline, provider.address, [
new symbol_sdk_1.Mosaic(input_x.identifier.toMosaicId(), symbol_sdk_1.UInt64.fromUint(input_x.amount)),
new symbol_sdk_1.Mosaic(input_y.identifier.toMosaicId(), symbol_sdk_1.UInt64.fromUint(input_y.amount))
], symbol_sdk_1.EmptyMessage, reader.networkType, undefined));
// - Transaction 03 is issued by **target** account
signers.push(this.target);
// - Transaction 04: Add execution proof transaction
transactions.push(symbol_sdk_1.TransferTransaction.create(this.context.parameters.deadline, this.target.address, [], // no mosaics
symbol_sdk_1.PlainMessage.create(this.descriptor
+ ':' + ((_e = this.mosaicInfo) === null || _e === void 0 ? void 0 : _e.id.toHex())
+ ':' + input_x.identifier.toMosaicId().toHex()
+ ':' + input_y.identifier.toMosaicId().toHex()), reader.networkType, undefined));
// - Transaction 04 is issued by **provider** account ("the actor")
signers.push(provider);
// - Assigns correct signer to each transaction
return transactions.map((transaction, i) => transaction.toAggregate(signers[i]));
}
}
exports.RemoveLiquidity = RemoveLiquidity;