swapable
Version:
Swapable: Automated Liquidity Pools
352 lines (351 loc) • 16.4 kB
JavaScript
"use strict";
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());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DigitalMarket = exports.AutomatedPool = exports.Revision = exports.PoolTargetDerivationPath = exports.AssetCommands = void 0;
/**
* 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
*/
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
// internal dependencies
const index_1 = require("./commands/index");
const index_2 = require("../index");
const Symbol_1 = require("./adapters/Symbol");
/**
* @var Swapable.AssetCommands
* @package Swapable
* @subpackage Standard
* @since v1.0.0
* @description Object that describes a list of available automated pool commands.
*/
exports.AssetCommands = {
'CreatePool': (c, i) => new index_1.PoolCommands.CreatePool(c, i),
'AddLiquidity': (c, i) => new index_1.PoolCommands.AddLiquidity(c, i),
'RemoveLiquidity': (c, i) => new index_1.PoolCommands.RemoveLiquidity(c, i),
'Swap': (c, i) => new index_1.PoolCommands.Swap(c, i),
};
/**
* @var Swapable.PoolTargetDerivationPath
* @package Swapable
* @subpackage Standard
* @since v1.0.0
* @description Contains text that describes the derivation path for the
* target account of automated pools.
*/
exports.PoolTargetDerivationPath = 'm/4343\'/0\'/0\'/0\'';
/**
* @var Swapable.Revision
* @package Swapable
* @subpackage Standard
* @since v1.0.0
* @description Object that describes the count of revisions for Swapable digital assets.
*/
exports.Revision = 1;
/**
* @class Swapable.AssetProtocol<__Adapter>
* @package standards
* @since v1.0.0
* @description Generic class to describe Swapable digital assets.
*
* Swapable assets consist in combining two cryptocurrencies in
* a liquidity pool to define an invariant `k` using a constant
* product formula of: `x * y = k` where `x` and `y` represent
* the cryptocurrencies that are paired in said liquidity pool.
*
* A swapable asset may be paired with any other cryptocurrency
* provided that they reside on the same blockchain network.
*
* Liquidity providers add liquidity into pools and Traders can
* swap currencies. A fee is added to each trade at the rate of
* 0.30% which are then added to token reserves. Providers can
* withdraw their shares of the total reserve at any time.
*
* When liquidity is added by a provider, they will be assigned
* some **Pool Shares**. Those shares can then be burned at any
* time by providers in order to take back their part of pooled
* assets.
*/
class AutomatedPool {
/**
* Constructs an automated liquidity pool instance around a
* name of \a name, a network reader \a reader and a signer
* implementation of \a signer. Also \a bip39 mnemonic pass
* phrases are used to derive deterministic child accounts.
*
* @access public
* @param {string} name The name of the automated liquidity pool (e.g.: "XYM:BTC").
* @param {TReader} reader The blockchain network reader configuration.
* @param {SignerImpl} signer The digital signature implementation ("key provider").
* @param {MnemonicPassPhrase} bip39 The mnemonic pass phrase ("24-words"). Caution here.
*/
constructor(
/**
* @readonly
* @access public
* @description The name of the automated pool.
*
* This name is usually best described using the two paired
* cryptocurrencies, e.g.: In a pair with Symbol (XYM) and
* Bitcoin (BTC), the name would be `XYM:BTC`.
*/
name,
/**
* @readonly
* @access public
* @description The blockchain network reader configuration.
*
* Our first implementation uses a Symbol blockchain network
* adapter as ReaderImpl and SignerImpl. It is possible that
* other network adapters are implemented in the future.
*/
reader,
/**
* @readonly
* @access protected
* @description The digital signature implementation ("key provider").
*
* Our first implementation uses a Symbol blockchain network
* adapter as ReaderImpl and SignerImpl. It is possible that
* other network adapters are implemented in the future.
*/
signer,
/**
* @readonly
* @access protected
* @description The mnemonic pass phrase ("24-words"). Caution here.
*
* :warning: This information is highly sensitive. In case you
* you are willing to host/deploy an automated pool, please do
* take caution when handling the aforementioned mnemonic pass
* phrase.
*/
bip39) {
this.name = name;
this.reader = reader;
this.signer = signer;
this.bip39 = bip39;
// - Derive child account in local scope
const account = Symbol_1.Accountable.derive(bip39.toSeed( /* XXX password */), exports.PoolTargetDerivationPath, this.reader.networkType, signer);
// - Only store the public account in instance
this.target = account.publicAccount;
// - Set asset source network configuration
this.source = new index_2.AssetSource(this.reader.generationHash);
}
/**
* Getter for the deterministic asset identifier related to
* the automated pool shares mosaic that will be created on
* the network. At any time, there is always one automated
* pool shares mosaic identifier per automated pool.
*
* @access public
* @return {AssetIdentifier}
*/
get identifier() {
// - Creates the deterministic asset identifier
return index_2.AssetIdentifier.createForSource(this.name, this.target, this.source);
}
/**
* Synchronize the command execution with the network. This
* method shall be used to fetch data required for / before
* the execution of an automated pool command.
*
* @async
* @override {Market.synchronize()}
* @access public
* @return {Promise<boolean>}
*/
synchronize() {
return __awaiter(this, void 0, void 0, function* () {
// - Prepares synchronization (context and endpoints)
const context = this.getContext(this.target, new index_2.TransactionParameters());
const mosaicHttp = context.reader.factoryHttp.createMosaicRepository();
const accountHttp = context.reader.factoryHttp.createAccountRepository();
try {
// - Reads the information about the automated pool shares mosaic of this automated pool
this.mosaicInfo = yield mosaicHttp.getMosaic(this.identifier.toMosaicId()).pipe(operators_1.catchError(e => { console.error(e); return rxjs_1.of(undefined); })).toPromise();
}
catch (e) { }
try {
// - Reads the information about the available reserves of this automated pool
this.reserveInfo = yield accountHttp.getAccountInfo(this.target.address).pipe(operators_1.catchError(e => { console.error(e); return rxjs_1.of(undefined); })).toPromise();
}
catch (e) { }
// - Done synchronizing network information
return true;
});
}
/**
* Creates a new Automated Liquidity Pool given \a provider
* a funded public account, \a x left-side input amount and
* \a y right-side input amount. The transaction parameters
* argument is optional and should be used to override data
* about network configuration.
*
* This method returns the asset identifier that represents
* the automated pool shares, all automated liquidity pools
* will be assigned **one** such asset identifier.
*
* @access public
* @param {PublicAccount} provider The public account of the liquidity provider.
* @param {AssetAmount} x The cryptocurrency amount for `x` in the constant product formula.
* @param {AssetAmount} y The cryptocurrency amount for `y` in the constant product formula.
* @param {TransactionParameters} parameters (Optional) The additional transaction parameters (network specific).
* @return {AssetIdentifier} The automated pool shares asset identifier.
**/
create(provider, x, y, parameters = new index_2.TransactionParameters()) {
// - Generates a deterministic LP Shares asset identifier
const sharesAssetId = this.identifier;
// - Execute digital automated pool command `CreatePool` (synchronize() not needed)
this.result = this.executeOffline(this.target, sharesAssetId, 'CreatePool', parameters, [
new index_2.CommandOption('provider', provider),
new index_2.CommandOption('input_x', x),
new index_2.CommandOption('input_y', y),
]);
// - Returns the LP Shares asset identifier
return sharesAssetId;
}
/**
* Verifies the autorization for \a actor to execute a pool
* command \a command given \a sharesAssetId automated pool
* shares asset identifier.
*
* @access public
* @param {PublicAccount} actor The actor is whom executes the command.
* @param {AssetIdentifier} sharesAssetId The automated pool shares asset identifier.
* @param {string} command The automated pool command.
* @param {Array<CommandOption>} argv The command options (arguments).
* @return {AllowanceResult} Returns whether an actor is authorized to execute said command.
**/
canExecute(actor, sharesAssetId, command, argv) {
// - Instanciates the command and context
const params = new index_2.TransactionParameters();
const context = this.getContext(actor, params, argv);
const cmdFn = this.getCommand(sharesAssetId, command, context);
// - Populate the synchronized data
cmdFn.mosaicInfo = this.mosaicInfo;
cmdFn.reserveInfo = this.reserveInfo;
// - Uses `canExecute` from underlying \a command
return cmdFn.canExecute(actor, argv);
}
/**
* Executes \a command given \a sharesAssetId automated pool
* shares asset identifier, \a actor public account, \a argv
* command execution options and \a parameters to broadcast.
*
* @access public
* @param {PublicAccount} actor The actor is whom executes the command.
* @param {AssetIdentifier} sharesAssetId The automated pool shares asset identifier.
* @param {string} command The automated pool command descriptor (e.g. "CreatePool").
* @param {TransactionParameters} parameters The transaction parameters (network specific).
* @param {Array<CommandOption>} argv The command execution options (arguments).
* @return {Promise<TransactionURI>} A digital contract that must be signed by the actor and possibly by the target account.
**/
execute(actor, sharesAssetId, command, parameters, argv) {
return __awaiter(this, void 0, void 0, function* () {
// - Reads network information from blockchain "reader"
yield this.synchronize();
try {
// - Instanciates a command in a context
const context = this.getContext(actor, parameters, argv);
const cmdFn = this.getCommand(sharesAssetId, command, context);
// - Populates the synchronized data
cmdFn.mosaicInfo = this.mosaicInfo;
cmdFn.reserveInfo = this.reserveInfo;
// - Executes the automated pool command
return cmdFn.execute(actor, argv);
}
catch (f) {
// XXX error notifications / events
throw f;
}
});
}
/**
* Executes \a command given \a sharesAssetId automated pool
* shares asset identifier, \a actor public account, \a argv
* command execution options and \a parameters to broadcast.
*
* This method does **not** call the `synchronize()` method.
*
* @access public
* @param {PublicAccount} actor The actor is whom executes the command.
* @param {AssetIdentifier} sharesAssetId The automated pool shares asset identifier.
* @param {string} command The automated pool command descriptor (e.g. "CreatePool").
* @param {TransactionParameters} parameters The transaction parameters (network specific).
* @param {Array<CommandOption>} argv The command execution options (arguments).
* @return {Promise<TransactionURI>} A digital contract that must be signed by the actor and possibly by the target account.
**/
executeOffline(actor, sharesAssetId, command, parameters, argv) {
// does-not-call-synchronize()
try {
// - Instanciates a command in a context
const context = this.getContext(actor, parameters, argv);
const cmdFn = this.getCommand(sharesAssetId, command, context);
// - Populates the synchronized data
cmdFn.mosaicInfo = this.mosaicInfo;
cmdFn.reserveInfo = this.reserveInfo;
// - Executes the automated pool command
return cmdFn.execute(actor, argv);
}
catch (f) {
// XXX error notifications / events
throw f;
}
}
/// region protected methods
/**
* Returns an execution context around an \a actor, \a argv
* command options and \a parameters transaction parameters.
*
* @access protected
* @param {PublicAccount} actor The actor in said execution context.
* @param {TransactionParameters} parameters The transaction parameters.
* @param {CommandOption[]} argv The execution options.
* @return {Context} The pre-configured *execution context*.
*/
getContext(actor, parameters, argv) {
return new index_2.Context(exports.Revision, actor, this.reader, this.signer, parameters, argv);
}
/**
* Returns a command instance for \a command given \a context
* and \a sharesAssetId.
*
* @see {Swapable.AssetCommands}
* @access protected
* @param {AssetIdentifier} sharesAssetId The automated pool shares asset identifier.
* @param {string} command The automated pool command name (which command).
* @param {Context} context The command execution context (arguments).
* @return {Command} The command instance pre-configured with the execution context.
* @throws {FailureInvalidCommand} On invalid automated pool command.
*/
getCommand(sharesAssetId, command, context) {
// validate digital automated pool command
if (!exports.AssetCommands || !exports.AssetCommands[command]) {
throw new index_2.FailureInvalidCommand('Invalid automated pool command.');
}
return exports.AssetCommands[command](context, sharesAssetId);
}
}
exports.AutomatedPool = AutomatedPool;
/**
* @class Swapable.DigitalMarket
* @package standards
* @since v1.0.0
* @description Class that describes markets for swapable digital assets.
*/
class DigitalMarket extends AutomatedPool {
}
exports.DigitalMarket = DigitalMarket;