DeFiCh/jellyfish

View on GitHub
packages/jellyfish-transaction/src/script/dftx/dftx_pool.ts

Summary

Maintainability
B
4 hrs
Test Coverage
import BigNumber from 'bignumber.js'
import { BufferComposer, ComposableBuffer, readCompactSize, writeCompactSize } from '@defichain/jellyfish-buffer'
import { Script } from '../../tx'
import { CScript } from '../../tx_composer'
import { SmartBuffer } from 'smart-buffer'
import { CScriptBalances, CTokenBalance, ScriptBalances, TokenBalanceUInt32 } from './dftx_balance'

/**
 * PoolSwap DeFi Transaction
 */
export interface PoolSwap {
  fromScript: Script // ----------------| n = VarUInt{1-9 bytes}, + n bytes
  fromTokenId: number // ---------------| VarInt{MSB-b128}
  fromAmount: BigNumber // -------------| 8 bytes
  toScript: Script // ------------------| n = VarUInt{1-9 bytes}, + n bytes
  toTokenId: number // -----------------| VarInt{MSB-b128}
  maxPrice: BigNumber // ---------------| 8 bytes integer + 8 bytes for fraction
}

/**
 * Composable PoolSwap, C stands for Composable.
 * Immutable by design, bi-directional fromBuffer, toBuffer deep composer.
 * @throws Error if more than 8 decimals
 */
export class CPoolSwap extends ComposableBuffer<PoolSwap> {
  static OP_CODE = 0x73
  static OP_NAME = 'OP_DEFI_TX_POOL_SWAP'

  composers (ps: PoolSwap): BufferComposer[] {
    return [
      ComposableBuffer.single<Script>(() => ps.fromScript, v => ps.fromScript = v, v => new CScript(v)),
      ComposableBuffer.varInt(() => ps.fromTokenId, v => ps.fromTokenId = v),
      ComposableBuffer.satoshiAsBigNumber(() => ps.fromAmount, v => ps.fromAmount = v),
      ComposableBuffer.single<Script>(() => ps.toScript, v => ps.toScript = v, v => new CScript(v)),
      ComposableBuffer.varInt(() => ps.toTokenId, v => ps.toTokenId = v),
      ComposableBuffer.maxPriceAsBigNumber(() => ps.maxPrice, v => ps.maxPrice = v)
    ]
  }
}

export interface PoolId {
  id: number // ------------------------| VarInt{MSB-b128}
}

/**
 * Composable PoolId, C stands for Composable.
 * Immutable by design, bi-directional fromBuffer, toBuffer deep composer.
 */
export class CPoolId extends ComposableBuffer<PoolId> {
  composers (pi: PoolId): BufferComposer[] {
    return [
      ComposableBuffer.varInt(() => pi.id, v => pi.id = v)
    ]
  }
}

/**
 * CompositeSwap DeFi Transaction
 */
export interface CompositeSwap {
  poolSwap: PoolSwap // ----------------| 1 x PoolSwap
  pools: PoolId[] // -------------------| n = VarUInt{1-9 bytes}, + n bytes
}

/**
 * Composable CompositeSwap, C stands for Composable.
 * Extends from CPoolSwap as it contains same data structure but with different DfTx OP_CODE.
 * Immutable by design, bi-directional fromBuffer, toBuffer deep composer.
 * @throws Error if more than 8 decimals
 */
export class CCompositeSwap extends ComposableBuffer<CompositeSwap> {
  static OP_CODE = 0x69 // 'i'
  static OP_NAME = 'OP_DEFI_TX_COMPOSITE_SWAP'

  composers (cs: CompositeSwap): BufferComposer[] {
    return [
      ComposableBuffer.single<PoolSwap>(() => cs.poolSwap, v => cs.poolSwap = v, v => new CPoolSwap(v)),
      ComposableBuffer.compactSizeArray<PoolId>(() => cs.pools, v => cs.pools = v, v => new CPoolId(v))
    ]
  }
}

/**
 * PoolAddLiquidity DeFi Transaction
 */
export interface PoolAddLiquidity {
  from: ScriptBalances[] // ------------| c = VarUInt{1-9 bytes}, + c x ScriptBalances
  shareAddress: Script // --------------| n = VarUInt{1-9 bytes}, + n bytes
}

/**
 * Composable PoolAddLiquidity, C stands for Composable.
 * Immutable by design, bi-directional fromBuffer, toBuffer deep composer.
 */
export class CPoolAddLiquidity extends ComposableBuffer<PoolAddLiquidity> {
  static OP_CODE = 0x6c
  static OP_NAME = 'OP_DEFI_TX_POOL_ADD_LIQUIDITY'

  composers (p: PoolAddLiquidity): BufferComposer[] {
    return [
      ComposableBuffer.compactSizeArray(() => p.from, v => p.from = v, v => new CScriptBalances(v)),
      ComposableBuffer.single<Script>(() => p.shareAddress, v => p.shareAddress = v, v => new CScript(v))
    ]
  }
}

/**
 * PoolRemoveLiquidity DeFi Transaction
 */
export interface PoolRemoveLiquidity {
  script: Script // --------------------| n = VarUInt{1-9 bytes}, + n bytes
  tokenId: number // -------------------| VarInt{MSB-b128}
  amount: BigNumber // -----------------| 8 bytes
}

/**
 * Composable PoolRemoveLiquidity, C stands for Composable.
 * Immutable by design, bi-directional fromBuffer, toBuffer deep composer.
 */
export class CPoolRemoveLiquidity extends ComposableBuffer<PoolRemoveLiquidity> {
  static OP_CODE = 0x72
  static OP_NAME = 'OP_DEFI_TX_POOL_REMOVE_LIQUIDITY'

  composers (p: PoolRemoveLiquidity): BufferComposer[] {
    return [
      ComposableBuffer.single<Script>(() => p.script, v => p.script = v, v => new CScript(v)),
      ComposableBuffer.varInt(() => p.tokenId, v => p.tokenId = v),
      ComposableBuffer.satoshiAsBigNumber(() => p.amount, v => p.amount = v)
    ]
  }
}

/**
 * PoolCreatePair DeFi Transaction
 */
export interface PoolCreatePair {
  tokenA: number // -----------------------| VarInt{MSB-b128}
  tokenB: number // -----------------------| VarInt{MSB-b128}
  commission: BigNumber // ----------------| 8 bytes
  ownerAddress: Script // -----------------| n = VarUInt{1-9 bytes}, + n bytes
  status: boolean // ----------------------| 1 byte
  pairSymbol: string // -------------------| VarUInt{1-9 bytes}, + c bytes UTF encoded string
  customRewards: TokenBalanceUInt32[] // --| c = VarUInt{1-9 bytes}, + c x TokenBalance
}

/**
 * Composable PoolCreatePair, C stands for Composable.
 * Immutable by design, bi-directional fromBuffer, toBuffer deep composer.
 */
export class CPoolCreatePair extends ComposableBuffer<PoolCreatePair> {
  static OP_CODE = 0x70
  static OP_NAME = 'OP_DEFI_TX_POOL_CREATE_PAIR'

  composers (p: PoolCreatePair): BufferComposer[] {
    return [
      ComposableBuffer.varInt(() => p.tokenA, v => p.tokenA = v),
      ComposableBuffer.varInt(() => p.tokenB, v => p.tokenB = v),
      ComposableBuffer.satoshiAsBigNumber(() => p.commission, v => p.commission = v),
      ComposableBuffer.single<Script>(() => p.ownerAddress, v => p.ownerAddress = v, v => new CScript(v)),
      ComposableBuffer.uBool8(() => p.status, v => p.status = v),
      ComposableBuffer.compactSizeUtf8BE(() => p.pairSymbol, v => p.pairSymbol = v),
      // Note(canonbrother): special fix for inconsistent bytes in "block height >= ClarkeQuayHeight" condition
      // https://github.com/DeFiCh/ain/blob/4b70ecd8ee32d00c75be04a786dc75ec4a3c91dd/src/masternodes/rpc_poolpair.cpp#L571-L573
      {
        fromBuffer: (buffer: SmartBuffer): void => {
          if (buffer.remaining() > 0) {
            const length = readCompactSize(buffer)
            p.customRewards = []
            for (let i = 0; i < length; i++) {
              p.customRewards.push(new CTokenBalance(buffer).toObject())
            }
          }
        },
        toBuffer: (buffer: SmartBuffer): void => {
          if (p.customRewards !== undefined) {
            writeCompactSize(p.customRewards.length, buffer)
            p.customRewards.forEach(data => new CTokenBalance(data).toBuffer(buffer))
          }
        }
      }
    ]
  }
}

/**
 * PoolUpdatePair DeFi Transaction
 */
export interface PoolUpdatePair {
  poolId: number // -----------------------| VarInt{MSB-b128}
  status: boolean // ----------------------| 4 bytes
  commission: BigNumber // ----------------| 8 bytes
  ownerAddress: Script // -----------------| n = VarUInt{1-9 bytes}, + n bytes
  customRewards: TokenBalanceUInt32[] // --| c = VarUInt{1-9 bytes}, + c x TokenBalance
}

/**
 * Composable PoolUpdatePair, C stands for Composable.
 * Immutable by design, bi-directional fromBuffer, toBuffer deep composer.
 */
export class CPoolUpdatePair extends ComposableBuffer<PoolUpdatePair> {
  static OP_CODE = 0x75
  static OP_NAME = 'OP_DEFI_TX_POOL_UPDATE_PAIR'

  composers (p: PoolUpdatePair): BufferComposer[] {
    return [
      ComposableBuffer.varInt(() => p.poolId, v => p.poolId = v),
      ComposableBuffer.uBool32(() => p.status, v => p.status = v),
      ComposableBuffer.satoshiAsBigNumber(() => p.commission, v => p.commission = v),
      ComposableBuffer.single<Script>(() => p.ownerAddress, v => p.ownerAddress = v, v => new CScript(v)),
      // Note(canonbrother): special fix for inconsistent bytes in "block height >= ClarkeQuayHeight" condition
      // https://github.com/DeFiCh/ain/blob/4b70ecd8ee32d00c75be04a786dc75ec4a3c91dd/src/masternodes/rpc_poolpair.cpp#L719-721
      {
        fromBuffer: (buffer: SmartBuffer): void => {
          if (buffer.remaining() > 0) {
            const length = readCompactSize(buffer)
            p.customRewards = []
            for (let i = 0; i < length; i++) {
              p.customRewards.push(new CTokenBalance(buffer).toObject())
            }
          }
        },
        toBuffer: (buffer: SmartBuffer): void => {
          if (p.customRewards !== undefined) {
            writeCompactSize(p.customRewards.length, buffer)
            p.customRewards.forEach(data => new CTokenBalance(data).toBuffer(buffer))
          }
        }
      }
    ]
  }
}