Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DfTx TokenCreate #208

Merged
merged 13 commits into from
May 25, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -1007,3 +1007,122 @@ describe('ComposableBuffer.varUInt', () => {
}).toThrow('out of Number.MAX_SAFE_INTEGER range')
})
})

describe('ComposableBuffer.bitmask1Byte', () => {
describe('bitmask 1 byte with 3 positional bits', () => {
const obj = { isA: false, isB: true, isC: false }

const composer = ComposableBuffer.bitmask1Byte(
3,
() => [obj.isA, obj.isB, obj.isC],
v => {
obj.isA = v[0]
obj.isB = v[1]
obj.isC = v[2]
}
)

it('should fromBuffer', () => {
shouldFromBuffer(composer, '02', [false, true, false], () => [obj.isA, obj.isB, obj.isC])
})

it('should toBuffer', () => {
shouldToBuffer(composer, '02', [false, true, false], v => {
obj.isA = v[0]
obj.isB = v[1]
obj.isC = v[2]
})
})
})

describe('bitmask 1 byte with 8 positional bits', () => {
const obj = {
isA: true,
isB: true,
isC: true,
isD: true,
isE: true,
isF: true,
isG: true,
isH: true
}

const composer = ComposableBuffer.bitmask1Byte(
8, () => Object.values(obj), v => {
obj.isA = v[0]
obj.isB = v[1]
obj.isC = v[2]
obj.isD = v[3]
obj.isE = v[4]
obj.isF = v[5]
obj.isG = v[6]
obj.isH = v[7]
})

it('should fromBuffer', () => {
shouldFromBuffer(composer, 'FF',
[true, true, true, true, true, true, true, true],
() => Object.values(obj))
})

it('should toBuffer', () => {
shouldToBuffer(
composer, 'FF',
[true, true, true, true, true, true, true, true],
v => {
obj.isA = v[0]
obj.isB = v[1]
obj.isC = v[2]
obj.isD = v[3]
obj.isE = v[4]
obj.isF = v[5]
obj.isG = v[6]
obj.isH = v[7]
})
})
})

describe('bitmask 1 byte with 7 positional bits', () => {
const obj = {
isA: false,
isB: true,
isC: false,
isD: true,
isE: false,
isF: true,
isG: false
}

const composer = ComposableBuffer.bitmask1Byte(
7, () => Object.values(obj), v => {
obj.isA = v[0]
obj.isB = v[1]
obj.isC = v[2]
obj.isD = v[3]
obj.isE = v[4]
obj.isF = v[5]
obj.isG = v[6]
})

it('should fromBuffer', () => {
shouldFromBuffer(composer, '2A',
[false, true, false, true, false, true, false],
() => Object.values(obj))
})

it('should toBuffer', () => {
shouldToBuffer(
composer, '2A',
[false, true, false, true, false, true, false],
v => {
obj.isA = v[0]
obj.isB = v[1]
obj.isC = v[2]
obj.isD = v[3]
obj.isE = v[4]
obj.isF = v[5]
obj.isG = v[6]
})
})
})
})
fuxingloh marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { SmartBuffer } from 'smart-buffer'
import { OP_DEFI_TX } from '../../../../src/script/defi'
import { CTokenCreate, TokenCreate } from '../../../../src/script/defi/dftx_token'
import { OP_CODES } from '../../../../src/script'
import { toBuffer, toOPCodes } from '../../../../src/script/_buffer'
import BigNumber from 'bignumber.js'

/**
* using createToken sample from
* https://explorer.defichain.com/#/DFI/mainnet/tx/8a5066b4ea77c8d0b705ba94f47585f944ae587700f0f43f8655d01f38921f40
* https://explorer.defichain.com/#/DFI/mainnet/tx/baeddea27199a9e9001133f18942353dc79a765f0c437a1eda550f7675dc6b8b
* https://explorer.defichain.com/#/DFI/mainnet/tx/11fb90953bbd2a8f8649c116e6071dc73d428c5eba97c5a4a6dac550df2ab78c
*/
it('should bi-directional buffer-object-buffer', () => {
const fixtures = [
'6a1b44665478540342544307426974636f696e08000000000000000003',
'6a19446654785404474f4c4404476f6c6408000000000000000003',
'6a224466547854035753420e77616c6c7374726565746265747308000000000000000003',

// regtest fixtures
'6a174466547854034748490347484908000000000000000004',
'6a174466547854034a4b4c034a4b4c08000000000000000000',
'6a174466547854034d4e4f034d4e4f08000000000000000001',
'6a174466547854035051520350515208000000000000000007',
'6a174466547854034142430341424308000000000000000005'
]

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(0x54)
})
})

const header = '6a224466547854' // OP_RETURN, PUSH_DATA(44665478, 54)
const data = '035753420e77616c6c7374726565746265747308000000000000000003'
const tokenCreate: TokenCreate = {
symbol: 'WSB',
name: 'wallstreetbets',
decimal: 8,
limit: new BigNumber('0'),
mintable: true,
tradeable: true,
isDAT: false
}

it('should craft dftx with OP_CODES._()', () => {
const stack = [
OP_CODES.OP_RETURN,
OP_CODES.OP_DEFI_TX_TOKEN_CREATE(tokenCreate)
]

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 CTokenCreate(buffer)

expect(composable.toObject()).toEqual(tokenCreate)
})

it('should compose from composable to buffer', () => {
const composable = new CTokenCreate(tokenCreate)
const buffer = new SmartBuffer()
composable.toBuffer(buffer)

expect(buffer.toBuffer().toString('hex')).toEqual(data)
})
})
12 changes: 12 additions & 0 deletions packages/jellyfish-transaction/src/buffer/buffer_bitmask.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Extracts the "truthiness" of a bit given a position
* @param {number} binaryNum - The number to query from
* @param {number} position - This is the zero-indexed position of the bit from the right
* @returns {boolean} - "Truthiness" of the bit we're interested in
*/
export function getBitsFrom (binaryNum: number, position: number): boolean {
// Bit-shifts according to zero-indexed position
const mask = 1 << position
const query = binaryNum & mask
return Boolean(query)
}
30 changes: 30 additions & 0 deletions packages/jellyfish-transaction/src/buffer/buffer_composer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import BigNumber from 'bignumber.js'
import { SmartBuffer } from 'smart-buffer'
import { writeVarUInt, readVarUInt } from './buffer_varuint'
import { getBitsFrom } from './buffer_bitmask'
import { ONE_HUNDRED_MILLION, readBigNumberUInt64, writeBigNumberUInt64 } from './buffer_bignumber'

export interface BufferComposer {
Expand Down Expand Up @@ -438,4 +439,33 @@ export abstract class ComposableBuffer<T> implements BufferComposer {
}
}
}

/**
* Imposing mask over bits method, 1 byte
*
* @param length of the input array to read/set
* @param getter to read from to buffer
* @param setter to set to from buffer
*/
static bitmask1Byte (
length: number,
getter: () => boolean[],
setter: (data: boolean[]) => void
): BufferComposer {
return {
fromBuffer: (buffer: SmartBuffer): void => {
const num = buffer.readUInt8()
const array: boolean[] = []
for (let i = 0; i < length; i += 1) {
array.unshift(getBitsFrom(num, i))
}
setter(array)
},
toBuffer: (buffer: SmartBuffer): void => {
const bools = getter().map(bool => bool.toString().toLowerCase() === 'true' ? 1 : 0)
const num = parseInt(bools.join(''), 2)
buffer.writeBuffer(Buffer.from([num]))
}
}
}
}
4 changes: 3 additions & 1 deletion packages/jellyfish-transaction/src/script/defi/dftx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
CPoolAddLiquidity, CPoolRemoveLiquidity, CPoolSwap, PoolAddLiquidity, PoolRemoveLiquidity,
PoolSwap
} from './dftx_pool'
import { CTokenMint, TokenMint } from './dftx_token'
import { CTokenCreate, CTokenMint, TokenCreate, TokenMint } from './dftx_token'
import { CDeFiOpUnmapped, DeFiOpUnmapped } from './dftx_unmapped'

// Disabling no-return-assign makes the code cleaner with the setter and getter */
Expand Down Expand Up @@ -100,6 +100,8 @@ export class CDfTx extends ComposableBuffer<DfTx<any>> {
return compose<PoolRemoveLiquidity>(CPoolRemoveLiquidity.OP_NAME, d => new CPoolRemoveLiquidity(d))
case CTokenMint.OP_CODE:
return compose<TokenMint>(CTokenMint.OP_NAME, d => new CTokenMint(d))
case CTokenCreate.OP_CODE:
return compose<TokenCreate>(CTokenCreate.OP_NAME, d => new CTokenCreate(d))
case CUtxosToAccount.OP_CODE:
return compose<UtxosToAccount>(CUtxosToAccount.OP_NAME, d => new CUtxosToAccount(d))
case CAccountToUtxos.OP_CODE:
Expand Down
37 changes: 37 additions & 0 deletions packages/jellyfish-transaction/src/script/defi/dftx_token.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { BufferComposer, ComposableBuffer } from '../../buffer/buffer_composer'
import { TokenBalance, CTokenBalance } from './dftx_balance'
import BigNumber from 'bignumber.js'

// Disabling no-return-assign makes the code cleaner with the setter and getter */
/* eslint-disable no-return-assign */
Expand All @@ -25,3 +26,39 @@ export class CTokenMint extends ComposableBuffer<TokenMint> {
]
}
}

/**
* TokenCreate DeFi Transaction
*/
export interface TokenCreate {
symbol: string // ---------------------| VarUInt{1-9 bytes}, + n bytes
name: string // -----------------------| VarUInt{1-9 bytes}, + n bytes
decimal: number // --------------------| 1 byte
limit: BigNumber // -------------------| 8 bytes
isDAT: boolean // ---------------------| 1 byte bitmask start, position 0
tradeable: boolean // -----------------| 1 byte bitmask, position 1
mintable: boolean // ------------------| 1 byte bitmask end, position 2
}

/**
* Composable TokenMint, C stands for Composable.
* Immutable by design, bi-directional fromBuffer, toBuffer deep composer.
*/
export class CTokenCreate extends ComposableBuffer<TokenCreate> {
static OP_CODE = 0x54 /// 'T'
static OP_NAME = 'OP_DEFI_TX_TOKEN_CREATE'

composers (tc: TokenCreate): BufferComposer[] {
return [
ComposableBuffer.varUIntUtf8BE(() => tc.symbol, v => tc.symbol = v),
ComposableBuffer.varUIntUtf8BE(() => tc.name, v => tc.name = v),
ComposableBuffer.uInt8(() => tc.decimal, v => tc.decimal = v),
ComposableBuffer.bigNumberUInt64(() => tc.limit, v => tc.limit = v),
ComposableBuffer.bitmask1Byte(3, () => [tc.isDAT, tc.tradeable, tc.mintable], v => {
tc.isDAT = v[0]
tc.tradeable = v[1]
tc.mintable = v[2]
})
]
}
}
10 changes: 9 additions & 1 deletion packages/jellyfish-transaction/src/script/mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
PoolRemoveLiquidity,
PoolSwap
} from './defi/dftx_pool'
import { CTokenMint, TokenMint } from './defi/dftx_token'
import { CTokenCreate, CTokenMint, TokenCreate, TokenMint } from './defi/dftx_token'
import {
AccountToAccount,
AccountToUtxos,
Expand Down Expand Up @@ -150,6 +150,14 @@ export const OP_CODES = {
data: tokenMint
})
},
OP_DEFI_TX_TOKEN_CREATE: (tokenCreate: TokenCreate): OP_DEFI_TX => {
return new OP_DEFI_TX({
signature: CDfTx.SIGNATURE,
type: CTokenCreate.OP_CODE,
name: CTokenCreate.OP_NAME,
data: tokenCreate
})
},
OP_DEFI_TX_UTXOS_TO_ACCOUNT: (utxosToAccount: UtxosToAccount): OP_DEFI_TX => {
return new OP_DEFI_TX({
signature: CDfTx.SIGNATURE,
Expand Down