diff --git a/packages/jellyfish-transaction/__tests__/script/defi/dftx_token/TokenUpdate.test.ts b/packages/jellyfish-transaction/__tests__/script/defi/dftx_token/TokenUpdate.test.ts new file mode 100644 index 0000000000..bdc14b2846 --- /dev/null +++ b/packages/jellyfish-transaction/__tests__/script/defi/dftx_token/TokenUpdate.test.ts @@ -0,0 +1,61 @@ +import { SmartBuffer } from 'smart-buffer' +import { OP_DEFI_TX } from '../../../../src/script/defi' +import { CTokenUpdate, TokenUpdate } from '../../../../src/script/defi/dftx_token' +import { OP_CODES } from '../../../../src/script' +import { toBuffer, toOPCodes } from '../../../../src/script/_buffer' + +it('should bi-directional buffer-object-buffer', () => { + const fixtures = [ + // regtest fixtures + // 1 fixture as only 'isDAT' flag modification allowed before Bayfront fork + // and only 'UpdateToken' is triggered while before Bayfront fork + // https://github.com/DeFiCh/ain/blob/c7b13959cc84c6d6210927b0e2377432c0dcadeb/src/masternodes/rpc_tokens.cpp#L278 + '6a26446654784effe50b27cd4325e9a87401e833a9caccf256e0b4ea37b6c4fb038bedc1cb247100' + + // BUG(canonbrother): isDAT is not updated after modified + // Issue is submitted: https://github.com/DeFiCh/ain/issues/440 + ] + + fixtures.forEach(hex => { + const stack: any = 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(0x4e) + }) +}) + +const header = '6a26446654784e' // OP_RETURN, PUSH_DATA(44665478, 4e) +const data = 'ffe50b27cd4325e9a87401e833a9caccf256e0b4ea37b6c4fb038bedc1cb247100' +const tokenUpdate: TokenUpdate = { + isDAT: false, + creationTx: '7124cbc1ed8b03fbc4b637eab4e056f2cccaa933e80174a8e92543cd270be5ff' +} + +it('should craft dftx with OP_CODES._()', () => { + const stack = [ + OP_CODES.OP_RETURN, + OP_CODES.OP_DEFI_TX_TOKEN_UPDATE(tokenUpdate) + ] + + 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 CTokenUpdate(buffer) + + expect(composable.toObject()).toEqual(tokenUpdate) + }) + + it('should compose from composable to buffer', () => { + const composable = new CTokenUpdate(tokenUpdate) + const buffer = new SmartBuffer() + composable.toBuffer(buffer) + + expect(buffer.toBuffer().toString('hex')).toEqual(data) + }) +}) diff --git a/packages/jellyfish-transaction/__tests__/script/defi/dftx_token/TokenUpdateAny.test.ts b/packages/jellyfish-transaction/__tests__/script/defi/dftx_token/TokenUpdateAny.test.ts new file mode 100644 index 0000000000..cbc015cca0 --- /dev/null +++ b/packages/jellyfish-transaction/__tests__/script/defi/dftx_token/TokenUpdateAny.test.ts @@ -0,0 +1,69 @@ +import { SmartBuffer } from 'smart-buffer' +import { OP_DEFI_TX } from '../../../../src/script/defi' +import { CTokenUpdateAny, TokenUpdateAny } from '../../../../src/script/defi/dftx_token' +import { OP_CODES } from '../../../../src/script' +import { toBuffer, toOPCodes } from '../../../../src/script/_buffer' +import BigNumber from 'bignumber.js' + +it('should bi-directional buffer-object-buffer', () => { + const fixtures = [ + // regtest fixtures + // flags + '6a37446654786ed819f622ced3616e3c02e5337b54cbf921c364e182a80925219e1f60461ee5fc034341540343415408000000000000000001', + + // symbol (symbolKey) + '6a38446654786effe50b27cd4325e9a87401e833a9caccf256e0b4ea37b6c4fb038bedc1cb2471044f57574c034f574c08000000000000000007', + + // name + '6a39446654786effe50b27cd4325e9a87401e833a9caccf256e0b4ea37b6c4fb038bedc1cb2471034f574c054e4947485408000000000000000007' + ] + + fixtures.forEach(hex => { + const stack: any = 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(0x6e) + }) +}) + +const header = '6a37446654786e' // OP_RETURN, PUSH_DATA(44665478, 6e) +const data = 'd819f622ced3616e3c02e5337b54cbf921c364e182a80925219e1f60461ee5fc034341540343415408000000000000000007' +const tokenUpdateAny: TokenUpdateAny = { + symbol: 'CAT', + name: 'CAT', + decimal: 8, + limit: new BigNumber('0'), + mintable: true, + tradeable: true, + isDAT: true, + creationTx: 'fce51e46601f9e212509a882e164c321f9cb547b33e5023c6e61d3ce22f619d8' +} + +it('should craft dftx with OP_CODES._()', () => { + const stack = [ + OP_CODES.OP_RETURN, + OP_CODES.OP_DEFI_TX_TOKEN_UPDATE_ANY(tokenUpdateAny) + ] + + 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 CTokenUpdateAny(buffer) + + expect(composable.toObject()).toEqual(tokenUpdateAny) + }) + + it('should compose from composable to buffer', () => { + const composable = new CTokenUpdateAny(tokenUpdateAny) + 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 3ec5979f22..6f08dd2761 100644 --- a/packages/jellyfish-transaction/src/script/defi/dftx.ts +++ b/packages/jellyfish-transaction/src/script/defi/dftx.ts @@ -16,7 +16,7 @@ import { CPoolAddLiquidity, CPoolRemoveLiquidity, CPoolSwap, PoolAddLiquidity, PoolRemoveLiquidity, PoolSwap } from './dftx_pool' -import { CTokenCreate, CTokenMint, TokenCreate, TokenMint } from './dftx_token' +import { CTokenCreate, CTokenMint, CTokenUpdate, CTokenUpdateAny, TokenCreate, TokenMint, TokenUpdate, TokenUpdateAny } from './dftx_token' import { CAppointOracle, AppointOracle, @@ -114,6 +114,10 @@ export class CDfTx extends ComposableBuffer> { return compose(CTokenMint.OP_NAME, d => new CTokenMint(d)) case CTokenCreate.OP_CODE: return compose(CTokenCreate.OP_NAME, d => new CTokenCreate(d)) + case CTokenUpdate.OP_CODE: + return compose(CTokenUpdate.OP_NAME, d => new CTokenUpdate(d)) + case CTokenUpdateAny.OP_CODE: + return compose(CTokenUpdateAny.OP_NAME, d => new CTokenUpdateAny(d)) case CUtxosToAccount.OP_CODE: return compose(CUtxosToAccount.OP_NAME, d => new CUtxosToAccount(d)) case CAccountToUtxos.OP_CODE: diff --git a/packages/jellyfish-transaction/src/script/defi/dftx_token.ts b/packages/jellyfish-transaction/src/script/defi/dftx_token.ts index 0632faef1b..dad8647e35 100644 --- a/packages/jellyfish-transaction/src/script/defi/dftx_token.ts +++ b/packages/jellyfish-transaction/src/script/defi/dftx_token.ts @@ -41,7 +41,7 @@ export interface TokenCreate { } /** - * Composable TokenMint, C stands for Composable. + * Composable TokenCreate, C stands for Composable. * Immutable by design, bi-directional fromBuffer, toBuffer deep composer. */ export class CTokenCreate extends ComposableBuffer { @@ -62,3 +62,61 @@ export class CTokenCreate extends ComposableBuffer { ] } } + +/** + * TokenUpdate DeFi Transaction + * Note(canonbrother): Only 'isDAT' flag modification allowed before Bayfront fork (<10000) + */ +export interface TokenUpdate { + creationTx: string // -----------------| 32 bytes hex string + isDAT: boolean // ---------------------| 1 byte bitmask start, position 0 +} + +/** + * Composable CTokenUpdate, C stands for Composable. + * Immutable by design, bi-directional fromBuffer, toBuffer deep composer. + */ +export class CTokenUpdate extends ComposableBuffer { + static OP_CODE = 0x4e /// 'N' + static OP_NAME = 'OP_DEFI_TX_TOKEN_UPDATE' + + composers (tu: TokenUpdate): BufferComposer[] { + return [ + ComposableBuffer.hexBEBufferLE(32, () => tu.creationTx, v => tu.creationTx = v), + ComposableBuffer.bitmask1Byte(1, () => [tu.isDAT], v => { + tu.isDAT = v[0] + }) + ] + } +} + +/** + * TokenUpdateAny DeFi Transaction + */ +export interface TokenUpdateAny extends TokenCreate { + creationTx: string // -----------------| 32 bytes hex string +} + +/** + * Composable TokenUpdateAny, C stands for Composable. + * Immutable by design, bi-directional fromBuffer, toBuffer deep composer. + */ +export class CTokenUpdateAny extends ComposableBuffer { + static OP_CODE = 0x6e /// 'n' + static OP_NAME = 'OP_DEFI_TX_TOKEN_UPDATE_ANY' + + composers (tua: TokenUpdateAny): BufferComposer[] { + return [ + ComposableBuffer.hexBEBufferLE(32, () => tua.creationTx, v => tua.creationTx = v), + ComposableBuffer.varUIntUtf8BE(() => tua.symbol, v => tua.symbol = v), + ComposableBuffer.varUIntUtf8BE(() => tua.name, v => tua.name = v), + ComposableBuffer.uInt8(() => tua.decimal, v => tua.decimal = v), + ComposableBuffer.bigNumberUInt64(() => tua.limit, v => tua.limit = v), + ComposableBuffer.bitmask1Byte(3, () => [tua.isDAT, tua.tradeable, tua.mintable], v => { + tua.isDAT = v[0] + tua.tradeable = v[1] + tua.mintable = v[2] + }) + ] + } +} diff --git a/packages/jellyfish-transaction/src/script/mapping.ts b/packages/jellyfish-transaction/src/script/mapping.ts index 23630961ef..ca77a76d0e 100644 --- a/packages/jellyfish-transaction/src/script/mapping.ts +++ b/packages/jellyfish-transaction/src/script/mapping.ts @@ -18,7 +18,7 @@ import { PoolRemoveLiquidity, PoolSwap } from './defi/dftx_pool' -import { CTokenCreate, CTokenMint, TokenCreate, TokenMint } from './defi/dftx_token' +import { CTokenCreate, CTokenUpdate, CTokenUpdateAny, CTokenMint, TokenCreate, TokenUpdate, TokenUpdateAny, TokenMint } from './defi/dftx_token' import { AccountToAccount, AccountToUtxos, @@ -170,6 +170,22 @@ export const OP_CODES = { data: tokenCreate }) }, + OP_DEFI_TX_TOKEN_UPDATE: (tokenUpdate: TokenUpdate): OP_DEFI_TX => { + return new OP_DEFI_TX({ + signature: CDfTx.SIGNATURE, + type: CTokenUpdate.OP_CODE, + name: CTokenUpdate.OP_NAME, + data: tokenUpdate + }) + }, + OP_DEFI_TX_TOKEN_UPDATE_ANY: (tokenUpdateAny: TokenUpdateAny): OP_DEFI_TX => { + return new OP_DEFI_TX({ + signature: CDfTx.SIGNATURE, + type: CTokenUpdateAny.OP_CODE, + name: CTokenUpdateAny.OP_NAME, + data: tokenUpdateAny + }) + }, OP_DEFI_TX_UTXOS_TO_ACCOUNT: (utxosToAccount: UtxosToAccount): OP_DEFI_TX => { return new OP_DEFI_TX({ signature: CDfTx.SIGNATURE,