diff --git a/packages/jellyfish-transaction/__tests__/script/defi/dftx_balance/AccountToAccount.test.ts b/packages/jellyfish-transaction/__tests__/script/defi/dftx_balance/AccountToAccount.test.ts new file mode 100644 index 0000000000..d6db12d054 --- /dev/null +++ b/packages/jellyfish-transaction/__tests__/script/defi/dftx_balance/AccountToAccount.test.ts @@ -0,0 +1,83 @@ +import { SmartBuffer } from 'smart-buffer' +import { AccountToAccount, CAccountToAccount } from '../../../../src/script/defi/dftx_account' +import { OP_CODES, toBuffer, toOPCodes } from '../../../../src/script' +import BigNumber from 'bignumber.js' +import { OP_DEFI_TX } from '../../../../src/script/defi' + +it('should bi-directional buffer-object-buffer', () => { + const fixtures = [ + '6a4544665478421976a914078084bb24a623d99521a2d6a53de6c558811de888ac0117a9146db59cbbcb1c7eeb0550c21669966c18c47028ba8701000000003da5a50300000000', + '6a43446654784217a914b76160c26b3e25fffe1a0fcf0107ace1cc586db0870117a914990b4f2d928f6dfb98c812b485c54dc73434ac248701070000007203000000000000', + '6a4544665478421976a9147d9ec663ae7c31a7f26990529d63fda2b565c38e88ac0117a9140ae6eab32e282e09dd929519ad674d96cbb202e687010000000095382a2e00000000', + '6a43446654784217a9146a75e3dbdee2de17134e891b3a408050fa44eac1870117a91463277879e3992a30a94671b4f61d1ca634d95442870103000000bd7c0d0000000000', + '6a43446654784217a914b76160c26b3e25fffe1a0fcf0107ace1cc586db0870117a914990b4f2d928f6dfb98c812b485c54dc73434ac248701070000009319000000000000' + ] + + fixtures.forEach(hex => { + const stack = toOPCodes( + SmartBuffer.fromBuffer(Buffer.from(hex, 'hex')) + ) + const buffer = toBuffer(stack) + expect(buffer.toString('hex')).toBe(hex) + expect((stack[1] as OP_DEFI_TX).tx.type).toBe(0x42) + }) +}) + +/** + * using accountToUtxos sample from + * https://explorer.defichain.io/#/DFI/mainnet/tx/1788a0d8345808efaf03c0d1f5a7936fffb3b25b6c68218da4f9ec978dd6b4d4 + */ +const header = '6a454466547842' // OP_RETURN 44665478 42 +const data = '1976a914078084bb24a623d99521a2d6a53de6c558811de888ac0117a9146db59cbbcb1c7eeb0550c21669966c18c47028ba8701000000003da5a50300000000' +const accountToAccount: AccountToAccount = { + from: { + stack: [ + OP_CODES.OP_DUP, + OP_CODES.OP_HASH160, + OP_CODES.OP_PUSHDATA_HEX_LE('078084bb24a623d99521a2d6a53de6c558811de8'), + OP_CODES.OP_EQUALVERIFY, + OP_CODES.OP_CHECKSIG + ] + }, + to: [{ + balances: [ + { + // 0.07431308 moved, total output = 0.61187389 + amount: new BigNumber('0.61187389'), token: 0 + } + ], + script: { + stack: [ + OP_CODES.OP_HASH160, + OP_CODES.OP_PUSHDATA_HEX_LE('6db59cbbcb1c7eeb0550c21669966c18c47028ba'), + OP_CODES.OP_EQUAL + ] + } + }] +} + +it('should craft dftx with OP_CODES._()', () => { + const stack = [ + OP_CODES.OP_RETURN, + OP_CODES.DEFI_OP_ACCOUNT_TO_ACCOUNT(accountToAccount) + ] + + const buffer = toBuffer(stack) + expect(buffer.toString('hex')).toBe(header + data) +}) + +describe('Composable', () => { + it('should compose from buffer to composable', () => { + const buffer = SmartBuffer.fromBuffer(Buffer.from(data, 'hex')) + const composable = new CAccountToAccount(buffer) + expect(composable.toObject()).toEqual(accountToAccount) + }) + + it('should compose from composable to buffer', () => { + const composable = new CAccountToAccount(accountToAccount) + const buffer = new SmartBuffer() + composable.toBuffer(buffer) + + expect(buffer.toBuffer().toString('hex')).toEqual(data) + }) +}) diff --git a/packages/jellyfish-transaction/__tests__/script/defi/dftx_balance/AccountToUtxos.test.ts b/packages/jellyfish-transaction/__tests__/script/defi/dftx_balance/AccountToUtxos.test.ts new file mode 100644 index 0000000000..d619538f5d --- /dev/null +++ b/packages/jellyfish-transaction/__tests__/script/defi/dftx_balance/AccountToUtxos.test.ts @@ -0,0 +1,70 @@ +import { SmartBuffer } from 'smart-buffer' +import { AccountToUtxos, CAccountToUtxos } from '../../../../src/script/defi/dftx_account' +import { OP_CODES, toBuffer, toOPCodes } from '../../../../src/script' +import BigNumber from 'bignumber.js' +import { OP_DEFI_TX } from '../../../../src/script/defi' + +it('should bi-directional buffer-object-buffer', () => { + const fixtures = [ + '6a2b446654786217a914d6e3de1c51f22e580944bb6a1647f1d22f0159c78701000000007030e6550200000002', + '6a2b446654786217a914fad0d4ab78412ec38e7a0b118e51e147e947e02d870100000000f2664e750200000002', + '6a2b446654786217a91462f401bfbe944884f07b489eb97fd3b001d5303287010000000000a81dd50f00000002' + ] + + fixtures.forEach(hex => { + const stack = toOPCodes( + SmartBuffer.fromBuffer(Buffer.from(hex, 'hex')) + ) + const buffer = toBuffer(stack) + expect(buffer.toString('hex')).toBe(hex) + expect((stack[1] as OP_DEFI_TX).tx.type).toBe(0x62) + }) +}) + +/** + * using accountToUtxos sample from + * https://explorer.defichain.io/#/DFI/mainnet/tx/8acd31c4ba08a31adefa0af5816e52ac5d61ad184a397bcc114590ecf2b08ed6 + */ +const header = '6a2b4466547862' // OP_RETURN 44665478 62 +const data = '17a914d6e3de1c51f22e580944bb6a1647f1d22f0159c78701000000007030e6550200000002' +const accountToUtxos: AccountToUtxos = { + from: { + stack: [ + OP_CODES.OP_HASH160, + OP_CODES.OP_PUSHDATA_HEX_LE('d6e3de1c51f22e580944bb6a1647f1d22f0159c7'), + OP_CODES.OP_EQUAL + ] + }, + balances: [ + { + amount: new BigNumber('100.31083632'), token: 0 + } + ], + mintingOutputsStart: 2 +} + +it('should craft dftx with OP_CODES._()', () => { + const stack = [ + OP_CODES.OP_RETURN, + OP_CODES.DEFI_OP_ACCOUNT_TO_UTXOS(accountToUtxos) + ] + + const buffer = toBuffer(stack) + expect(buffer.toString('hex')).toBe(header + data) +}) + +describe('Composable', () => { + it('should compose from buffer to composable', () => { + const buffer = SmartBuffer.fromBuffer(Buffer.from(data, 'hex')) + const composable = new CAccountToUtxos(buffer) + expect(composable.toObject()).toEqual(accountToUtxos) + }) + + it('should compose from composable to buffer', () => { + const composable = new CAccountToUtxos(accountToUtxos) + const buffer = new SmartBuffer() + composable.toBuffer(buffer) + + expect(buffer.toBuffer().toString('hex')).toEqual(data) + }) +}) diff --git a/packages/jellyfish-transaction/__tests__/script/defi/dftx_balance/ScriptBalances.test.ts b/packages/jellyfish-transaction/__tests__/script/defi/dftx_balance/ScriptBalances.test.ts new file mode 100644 index 0000000000..ab10a30dad --- /dev/null +++ b/packages/jellyfish-transaction/__tests__/script/defi/dftx_balance/ScriptBalances.test.ts @@ -0,0 +1,35 @@ +import BigNumber from 'bignumber.js' +import { SmartBuffer } from 'smart-buffer' +import { CScriptBalances, ScriptBalances } from '../../../../src/script/defi/dftx_balance' +import { OP_CODES } from '../../../../src/script' + +const data = '17a914d6e3de1c51f22e580944bb6a1647f1d22f0159c78701000000007030e65502000000' +const scriptBalances: ScriptBalances = { + script: { + stack: [ + OP_CODES.OP_HASH160, + OP_CODES.OP_PUSHDATA_HEX_LE('d6e3de1c51f22e580944bb6a1647f1d22f0159c7'), + OP_CODES.OP_EQUAL + ] + }, + balances: [ + { + amount: new BigNumber('100.31083632'), token: 0 + } + ] +} + +describe('Composable', () => { + it('should compose from buffer to composable', () => { + const buffer = SmartBuffer.fromBuffer(Buffer.from(data, 'hex')) + const composable = new CScriptBalances(buffer) + expect(composable.toObject()).toEqual(scriptBalances) + }) + + it('should compose from composable to buffer', () => { + const composable = new CScriptBalances(scriptBalances) + const buffer = new SmartBuffer() + composable.toBuffer(buffer) + expect(buffer.toBuffer().toString('hex')).toEqual(data) + }) +}) diff --git a/packages/jellyfish-transaction/__tests__/script/defi/dftx_balance/TokenBalance.test.ts b/packages/jellyfish-transaction/__tests__/script/defi/dftx_balance/TokenBalance.test.ts new file mode 100644 index 0000000000..ad8afc90d5 --- /dev/null +++ b/packages/jellyfish-transaction/__tests__/script/defi/dftx_balance/TokenBalance.test.ts @@ -0,0 +1,24 @@ +import BigNumber from 'bignumber.js' +import { SmartBuffer } from 'smart-buffer' +import { CTokenBalance, TokenBalance } from '../../../../src/script/defi/dftx_balance' + +const data = '000000003da5a50300000000' +const tokenBalance: TokenBalance = { + token: 0, + amount: new BigNumber('0.61187389') +} + +describe('Composable', () => { + it('should compose from buffer to composable', () => { + const buffer = SmartBuffer.fromBuffer(Buffer.from(data, 'hex')) + const composable = new CTokenBalance(buffer) + expect(composable.toObject()).toEqual(tokenBalance) + }) + + it('should compose from composable to buffer', () => { + const composable = new CTokenBalance(tokenBalance) + const buffer = new SmartBuffer() + composable.toBuffer(buffer) + expect(buffer.toBuffer().toString('hex')).toEqual(data) + }) +}) diff --git a/packages/jellyfish-transaction/__tests__/script/defi/dftx_balance/UtxosToAccount.test.ts b/packages/jellyfish-transaction/__tests__/script/defi/dftx_balance/UtxosToAccount.test.ts new file mode 100644 index 0000000000..20655bc19c --- /dev/null +++ b/packages/jellyfish-transaction/__tests__/script/defi/dftx_balance/UtxosToAccount.test.ts @@ -0,0 +1,74 @@ +import { SmartBuffer } from 'smart-buffer' +import { CUtxosToAccount, UtxosToAccount } from '../../../../src/script/defi/dftx_account' +import { OP_CODES, toBuffer, toOPCodes } from '../../../../src/script' +import BigNumber from 'bignumber.js' +import { OP_DEFI_TX } from '../../../../src/script/defi' + +it('should bi-directional buffer-object-buffer', () => { + const fixtures = [ + '6a2b44665478550117a91445903c2015cce2e8c3ac5fc13db388ccfd23d56387010000000059b8fa4004000000', + '6a2b44665478550117a91445903c2015cce2e8c3ac5fc13db388ccfd23d5638701000000004e32624204000000', + '6a2b44665478550117a914ea0e3fde9a9d281e348b3d05bc83d7da95eb631e8701000000004b44140000000000', + '6a2d4466547855011976a9142d34be7852e3741b974bec9c49e3b99ee08b89d888ac0100000000d705140b00000000', + '6a2b44665478550117a914462633e915dc1670f353568b8774da38c555bc2b87010000000074cab42901000000', + '6a2b44665478550117a914f5fe3eba6fc3fb484eadf4ee6d3364a7c9bec194870100000000afce935200000000', + '6a2b44665478550117a91469d2048d15b991eeebdd1e6b8391b5414da3ece58701000000009c81a20100000000', + '6a2b44665478550117a91491b76ba9a57e3821ab4b623265f20ef551e6c4cd8701000000006fd1190000000000', + '6a2b44665478550117a914a6adde29e5ebe8592cbf83fca196c0b363ad9278870100000000bcdc41eb04000000', + '6a2b44665478550117a914a6adde29e5ebe8592cbf83fca196c0b363ad92788701000000005fa6480000000000' + ] + + fixtures.forEach(hex => { + const stack = toOPCodes( + SmartBuffer.fromBuffer(Buffer.from(hex, 'hex')) + ) + const buffer = toBuffer(stack) + expect(buffer.toString('hex')).toBe(hex) + expect((stack[1] as OP_DEFI_TX).tx.type).toBe(0x55) + }) +}) + +const header = '6a2b4466547855' // OP_RETURN 44665478 55 +const data = '0117a91445903c2015cce2e8c3ac5fc13db388ccfd23d56387010000000059b8fa4004000000' +const utxosToAccount: UtxosToAccount = { + to: [{ + balances: [ + { + amount: new BigNumber('182.70042201'), token: 0 + } + ], + script: { + stack: [ + OP_CODES.OP_HASH160, + OP_CODES.OP_PUSHDATA_HEX_LE('45903c2015cce2e8c3ac5fc13db388ccfd23d563'), + OP_CODES.OP_EQUAL + ] + } + }] +} + +it('should craft dftx with OP_CODES._()', () => { + const stack = [ + OP_CODES.OP_RETURN, + OP_CODES.DEFI_OP_UTXOS_TO_ACCOUNT(utxosToAccount) + ] + + const buffer = toBuffer(stack) + expect(buffer.toString('hex')).toBe(header + data) +}) + +describe('Composable', () => { + it('should compose from buffer to composable', () => { + const buffer = SmartBuffer.fromBuffer(Buffer.from(data, 'hex')) + const composable = new CUtxosToAccount(buffer) + expect(composable.toObject()).toEqual(utxosToAccount) + }) + + it('should compose from composable to buffer', () => { + const composable = new CUtxosToAccount(utxosToAccount) + const buffer = new SmartBuffer() + composable.toBuffer(buffer) + + expect(buffer.toBuffer().toString('hex')).toEqual(data) + }) +}) diff --git a/packages/jellyfish-transaction/src/script/defi/dftx.ts b/packages/jellyfish-transaction/src/script/defi/dftx.ts index 39e031f3d4..8c9f452fae 100644 --- a/packages/jellyfish-transaction/src/script/defi/dftx.ts +++ b/packages/jellyfish-transaction/src/script/defi/dftx.ts @@ -1,5 +1,13 @@ import { SmartBuffer } from 'smart-buffer' import { BufferComposer, ComposableBuffer } from '../../buffer/buffer_composer' +import { + AccountToAccount, + AccountToUtxos, + CAccountToAccount, + CAccountToUtxos, + CUtxosToAccount, + UtxosToAccount +} from './dftx_account' import { CPoolAddLiquidity, CPoolRemoveLiquidity, CPoolSwap, PoolAddLiquidity, PoolRemoveLiquidity, PoolSwap @@ -90,6 +98,12 @@ export class CDfTx extends ComposableBuffer> { return compose(CPoolRemoveLiquidity.OP_NAME, d => new CPoolRemoveLiquidity(d)) case CTokenMint.OP_CODE: return compose(CTokenMint.OP_NAME, d => new CTokenMint(d)) + case CUtxosToAccount.OP_CODE: + return compose(CUtxosToAccount.OP_NAME, d => new CUtxosToAccount(d)) + case CAccountToUtxos.OP_CODE: + return compose(CAccountToUtxos.OP_NAME, d => new CAccountToUtxos(d)) + case CAccountToAccount.OP_CODE: + return compose(CAccountToAccount.OP_NAME, d => new CAccountToAccount(d)) default: return compose(CDeFiOpUnmapped.OP_NAME, d => new CDeFiOpUnmapped(d)) } diff --git a/packages/jellyfish-transaction/src/script/defi/dftx_account.ts b/packages/jellyfish-transaction/src/script/defi/dftx_account.ts new file mode 100644 index 0000000000..85ca209c09 --- /dev/null +++ b/packages/jellyfish-transaction/src/script/defi/dftx_account.ts @@ -0,0 +1,79 @@ +import { BufferComposer, ComposableBuffer } from '../../buffer/buffer_composer' +import { Script } from '../../tx' +import { CScript } from '../../tx_composer' +import { CScriptBalances, CTokenBalance, ScriptBalances, TokenBalance } from './dftx_balance' + +// Disabling no-return-assign makes the code cleaner with the setter and getter */ +/* eslint-disable no-return-assign */ + +/** + * UtxosToAccount DeFi Transaction + */ +export interface UtxosToAccount { + to: ScriptBalances[] // --------------| n = VarUInt{1-9 bytes}, + n bytes +} + +/** + * Composable UtxosToAccount, C stands for Composable. + * Immutable by design, bi-directional fromBuffer, toBuffer deep composer. + */ +export class CUtxosToAccount extends ComposableBuffer { + static OP_CODE = 0x55 // 'U' + static OP_NAME = 'DEFI_OP_UTXOS_TO_ACCOUNT' + + composers (u2a: UtxosToAccount): BufferComposer[] { + return [ + ComposableBuffer.varUIntArray(() => u2a.to, v => u2a.to = v, v => new CScriptBalances(v)) + ] + } +} + +/** + * AccountToUtxos DeFi Transaction + */ +export interface AccountToUtxos { + from: Script // -----------------------| n = VarUInt{1-9 bytes}, + n bytes + balances: TokenBalance[] // -----------| c = VarUInt{1-9 bytes}, + c x TokenBalance + mintingOutputsStart: number // --------| 4 bytes unsigned +} + +/** + * Composable UtxosToAccount, C stands for Composable. + * Immutable by design, bi-directional fromBuffer, toBuffer deep composer. + */ +export class CAccountToUtxos extends ComposableBuffer { + static OP_CODE = 0x62 // 'b' + static OP_NAME = 'DEFI_OP_ACCOUNT_TO_UTXOS' + + composers (a2u: AccountToUtxos): BufferComposer[] { + return [ + ComposableBuffer.single