Skip to content

Commit

Permalink
v1.3.0: (Revision 2) Added Publish command and Swapable.Registry
Browse files Browse the repository at this point in the history
  • Loading branch information
evias committed Jul 28, 2021
1 parent d22f127 commit 5bcf3b1
Show file tree
Hide file tree
Showing 12 changed files with 816 additions and 31 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ubcdigital/swapable",
"version": "1.2.0",
"version": "1.3.0",
"license": "LGPL-3.0-only",
"description": "Swapable: Automated Liquidity Pools",
"keywords": [
Expand Down
140 changes: 119 additions & 21 deletions src/Swapable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
import {
Reader as ReaderImpl,
} from './adapters/Symbol'
import { PoolService, PoolImpl } from './services/PoolService'

/**
* @type Swapable.CommandFn
Expand Down Expand Up @@ -68,26 +69,96 @@ export const AssetCommands: CommandsList = {
'AddLiquidity': (c, i): Command => new CommandsImpl.AddLiquidity(c, i),
'RemoveLiquidity': (c, i): Command => new CommandsImpl.RemoveLiquidity(c, i),
'Swap': (c, i): Command => new CommandsImpl.Swap(c, i),
'Publish': (c, i): Command => new CommandsImpl.Publish(c, i),
}

/**
* @var Swapable.PoolTargetDerivationPath
* @var Swapable.Revision
* @package Swapable
* @subpackage Standard
* @since v1.0.0
* @description Contains text that describes the derivation path for the
* target account of automated pools.
* @description Object that describes the version of Swapable liquidity pools.
*
* Revision 1: @ubcdigital/[email protected]
* Revision 2: @ubcdigital/[email protected]
*/
export const PoolTargetDerivationPath: string = 'm/4343\'/0\'/0\'/0\''
export const Revision: number = 2

/**
* @var Swapable.Revision
* @package Swapable
* @subpackage Standard
* @since v1.0.0
* @description Object that describes the count of revisions for Swapable digital assets.
* @type Swapable.Registry
* @package standards
* @since v1.2.1
* @description Class that describes a registry for pools.
*
* Registries may list one or more than one liquidity pool(s).
* Each target account hosts one or more than one market pair.
*
* Registries do not own any cryptocurrency. They serve a role
* of public ledger that contains liquidity pools metadata.
*/
export const Revision: number = 1
export class Registry {
/**
* The reader execution context
*
* @var {Context}
*/
public context: Context

/**
* Constructs a pool registry object.
*
* @param {ReaderImpl} reader
* @param {PublicAccount} publicAccount
*/
public constructor(
/**
* @readonly
* @access public
* @description The blockchain network reader configuration.
*
* Our first implementation uses a Symbol blockchain network
* adapter as ReaderImpl. It is possible that other networks
* are implemented in the future.
*/
public readonly reader: ReaderImpl,

/**
* @readonly
* @access public
* @description The deterministic public account representing
* the registry. This account holds incoming transactions and
* is used for listing capacity.
*/
public readonly publicAccount: PublicAccount
) {
this.context = new Context(
Revision,
publicAccount,
this.reader,
new TransactionParameters(),
[],
)
}

/**
* List a registry's liquidity pools. Registries should be used
* for publicly listing liquidity pools. So-called market pairs
* are hosted by the Liquidity Pool target account.
*
* @static
* @param {Registry|PublicAccount} authority
* @return {MosaicId[]}
*/
public async getPools(
revision?: number,
): Promise<PoolImpl[]> {
// initialize pool service
const service = new PoolService(this.context)

// use service to read pools (mosaic+metadata)
return await service.getPools(this.publicAccount, !!revision ? revision : Revision)
}
}

/**
* @class Swapable.AutomatedPool
Expand Down Expand Up @@ -134,13 +205,6 @@ export const Revision: number = 1
*/
export class AutomatedPool implements Market {

/**
* @description The deterministic public account which owns an
* automated liquidity pool. This account is used
* to issue the *automated pool shares* mosaic.
*/
public target: PublicAccount

/**
* @description The source blockchain network of assets paired
* in an automated liquidity pool.
Expand Down Expand Up @@ -207,11 +271,8 @@ export class AutomatedPool implements Market {
* owner of the pool shares mosaic which is created for each
* liquidity pool. This account *should* be multi-signature.
*/
target: PublicAccount,
public readonly target: PublicAccount,
) {
// - Only store the public account in instance
this.target = target

// - Set asset source network configuration
this.source = new AssetSource(this.reader.generationHash)
}
Expand Down Expand Up @@ -308,6 +369,43 @@ export class AutomatedPool implements Market {
return sharesAssetId
}

/**
* Publishes an Automated Liquidity Pool to \a registry and
* uses the target public account. Registries serve as list
* of liquidity pools.
*
* @param {PublicAccount} registry
* @returns {Promise<TransactionURI<Transaction>>}
*/
public async publish(
registry: PublicAccount,
): Promise<TransactionURI<Transaction>> {
// - Reads network information from blockchain "reader"
await this.synchronize()

try {
// - Prepares command parameters
const argv = [
new CommandOption('registry', registry)
]

// - Instanciates a command in a context
const context = this.getContext(this.target, new TransactionParameters(), argv)
const cmdFn = this.getCommand(this.identifier, 'Publish', context) as Executable

// - Populates the synchronized data
cmdFn.mosaicInfo = this.mosaicInfo
cmdFn.reserveInfo = this.reserveInfo

// - Executes the automated pool command
return cmdFn.execute(this.target, argv)
}
catch (f) {
// XXX error notifications / events
throw f
}
}

/**
* Verifies the autorization for \a actor to execute a pool
* command \a command given \a sharesAssetId automated pool
Expand Down
162 changes: 162 additions & 0 deletions src/commands/Publish.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/**
* This file is part of Swapable shared under LGPL-3.0-only.
* Copyright (C) 2021 Using Blockchain Ltd, Reg No.: 12658136, United Kingdom
*
* @package Swapable
* @author Grégory Saive for Using Blockchain Ltd <[email protected]>
* @license LGPL-3.0-only
*/
import {
InnerTransaction,
PublicAccount,
Transaction,
TransferTransaction,
PlainMessage,
} from 'symbol-sdk'

// internal dependencies
import {
AllowanceResult,
CommandOption,
Symbol,
} from '../../index'
import { Executable } from './Executable'

/**
* @class Swapable.Publish
* @package Swapable
* @subpackage Commands
* @since v1.0.0
* @description Class that describes a command for publishing
* automated liquidity pools to a registry.
* @summary
* This automated pool command accepts the following arguments:
*
* | Argument | Description | Example |
* | --- | --- | --- |
* | registry | Liquidity pool registry | `new PublicAccount(...)` |
*
* 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 | Target Account | Creates a registration contract with a signature that contains the token identifier. |
*
*/
export class Publish extends Executable {
/**
* @access public
* @description The list of **required** arguments to execute
* *this* automated pool command.
*/
public arguments: string[] = [
'registry',
]

/**
* 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).
**/
public canExecute(
actor: PublicAccount,
argv?: CommandOption[]
): AllowanceResult {
// - Asserts the presence of mandatory inputs
super.assertHasMandatoryArguments(argv, this.arguments)

// - Allows only the target account of liquidity
// pools to publish a pool to a registry.
return new AllowanceResult(
actor.address.equals(this.target.address)
)
}

// region abstract methods
/**
* This method returns the automated pool command name,
* e.g. "Publish" or "AddLiquidity", etc.
*
* @access public
* @return {string}
**/
public get name(): string {
return 'Publish'
}

/**
* 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}
**/
public get descriptor(): string {
return 'Swapable(v' + this.context.revision + ')' + ':publish:' + 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.
**/
protected get transactions(): Transaction[] {
// - Reads the execution context
const reader = this.context.reader as Symbol.Reader

// - Read external arguments
const registry = this.context.getInput('registry', new PublicAccount())

// - Prepares the response
const transactions: InnerTransaction[] = []
const signers: PublicAccount[] = []

// - Transaction 01: Add execution proof transaction
transactions.push(TransferTransaction.create(
this.context.parameters.deadline,
registry.address,
[], // no mosaics
PlainMessage.create(this.descriptor),
reader.networkType,
undefined, // maxFee 0 for inner
))

// - Transaction 10 is issued by **provider** account ("the actor")
signers.push(this.target)

// - Assigns correct signer to each transaction
return transactions.map(
(transaction, i) => transaction.toAggregate(signers[i])
)
}
// end-region abstract methods
}
4 changes: 4 additions & 0 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { CreatePool as CreatePoolImpl } from './CreatePool'
import { AddLiquidity as AddLiquidityImpl } from './AddLiquidity'
import { RemoveLiquidity as RemoveLiquidityImpl } from './RemoveLiquidity'
import { Swap as SwapImpl } from './Swap'
import { Publish as PublishImpl } from './Publish'

/**
* @namespace Swapable.PoolCommands
Expand All @@ -32,4 +33,7 @@ export namespace PoolCommands {
// - Exports an alias to the `Swap` command implementation
export class Swap extends SwapImpl {}

// - Exports an alias to the `Publish` command implementation
export class Publish extends PublishImpl {}

}
Loading

0 comments on commit 5bcf3b1

Please sign in to comment.