diff --git a/packages/jellyfish-transaction/__tests__/script/defi/dftx_oracles/AppointOracle.test.ts b/packages/jellyfish-transaction/__tests__/script/defi/dftx_oracles/AppointOracle.test.ts new file mode 100644 index 0000000000..9f596a6928 --- /dev/null +++ b/packages/jellyfish-transaction/__tests__/script/defi/dftx_oracles/AppointOracle.test.ts @@ -0,0 +1,75 @@ +import { SmartBuffer } from 'smart-buffer' +import { + CAppointOracle, + AppointOracle +} from '../../../../src/script/defi/dftx_oracles' +import { OP_CODES } from '../../../../src/script' +import { toBuffer, toOPCodes } from '../../../../src/script/_buffer' +import { OP_DEFI_TX } from '../../../../src/script/defi' + +it('should bi-directional buffer-object-buffer', () => { + const fixtures = [ + '6a35446654786f1976a914c52fcb3c6dd28e530e5d162fee41f235bf7709cd88ac0102055445534c4103455552055445534c4103555344' + ] + + 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(0x6f) + }) +}) + +const header = '6a35446654786f' // OP_RETURN, PUSH_DATA(44665478, 6f) +const data = '1976a914c52fcb3c6dd28e530e5d162fee41f235bf7709cd88ac0102055445534c4103455552055445534c4103555344' +const appointOracle: AppointOracle = { + script: { + stack: [ + OP_CODES.OP_DUP, + OP_CODES.OP_HASH160, + OP_CODES.OP_PUSHDATA_HEX_LE('c52fcb3c6dd28e530e5d162fee41f235bf7709cd'), + OP_CODES.OP_EQUALVERIFY, + OP_CODES.OP_CHECKSIG + ] + }, + weightage: 1, + priceFeeds: [ + { + token: 'TESLA', + currency: 'EUR' + }, + { + token: 'TESLA', + currency: 'USD' + } + ] +} + +it('should craft dftx with OP_CODES._()', () => { + const stack = [ + OP_CODES.OP_RETURN, + OP_CODES.OP_DEFI_TX_APPOINT_ORACLE(appointOracle) + ] + + 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 CAppointOracle(buffer) + + expect(composable.toObject()).toEqual(appointOracle) + }) + + it('should compose from composable to buffer', () => { + const composable = new CAppointOracle(appointOracle) + const buffer = new SmartBuffer() + composable.toBuffer(buffer) + + expect(buffer.toBuffer().toString('hex')).toEqual(data) + }) +}) diff --git a/packages/jellyfish-transaction/__tests__/script/defi/dftx_oracles/RemoveOracle.test.ts b/packages/jellyfish-transaction/__tests__/script/defi/dftx_oracles/RemoveOracle.test.ts new file mode 100644 index 0000000000..a6e6bb01e3 --- /dev/null +++ b/packages/jellyfish-transaction/__tests__/script/defi/dftx_oracles/RemoveOracle.test.ts @@ -0,0 +1,56 @@ +import { SmartBuffer } from 'smart-buffer' +import { + CRemoveOracle, + RemoveOracle +} from '../../../../src/script/defi/dftx_oracles' +import { OP_CODES } from '../../../../src/script' +import { toBuffer, toOPCodes } from '../../../../src/script/_buffer' +import { OP_DEFI_TX } from '../../../../src/script/defi' + +it('should bi-directional buffer-object-buffer', () => { + const fixtures = [ + '6a254466547868061d35948925528b2025c4b84ea6f4899bab6efbcaf63776258186d7728424d1' + ] + + 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(0x68) + }) +}) + +const header = '6a254466547868' // OP_RETURN, PUSH_DATA(44665478, 68) +const data = '061d35948925528b2025c4b84ea6f4899bab6efbcaf63776258186d7728424d1' +const removeOracle: RemoveOracle = { + oracleId: 'd1248472d78681257637f6cafb6eab9b89f4a64eb8c425208b52258994351d06' +} + +it('should craft dftx with OP_CODES._()', () => { + const stack = [ + OP_CODES.OP_RETURN, + OP_CODES.OP_DEFI_TX_REMOVE_ORACLE(removeOracle) + ] + + 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 CRemoveOracle(buffer) + + expect(composable.toObject()).toEqual(removeOracle) + }) + + it('should compose from composable to buffer', () => { + const composable = new CRemoveOracle(removeOracle) + const buffer = new SmartBuffer() + composable.toBuffer(buffer) + + expect(buffer.toBuffer().toString('hex')).toEqual(data) + }) +}) diff --git a/packages/jellyfish-transaction/__tests__/script/defi/dftx_oracles/SetOracleData.test.ts b/packages/jellyfish-transaction/__tests__/script/defi/dftx_oracles/SetOracleData.test.ts new file mode 100644 index 0000000000..7781495015 --- /dev/null +++ b/packages/jellyfish-transaction/__tests__/script/defi/dftx_oracles/SetOracleData.test.ts @@ -0,0 +1,70 @@ +import { SmartBuffer } from 'smart-buffer' +import { + SetOracleData, + CSetOracleData +} from '../../../../src/script/defi/dftx_oracles' +import BigNumber from 'bignumber.js' +import { OP_CODES } from '../../../../src/script' +import { toBuffer, toOPCodes } from '../../../../src/script/_buffer' +import { OP_DEFI_TX } from '../../../../src/script/defi' + +it('should bi-directional buffer-object-buffer', () => { + const fixtures = [ + '6a414466547879061d35948925528b2025c4b84ea6f4899bab6efbcaf63776258186d7728424d1bc29a7600000000001055445534c41010355534400e1f50500000000' + ] + + 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(0x79) + }) +}) + +const header = '6a414466547879' // OP_RETURN, PUSH_DATA(44665478, 79) +const data = '061d35948925528b2025c4b84ea6f4899bab6efbcaf63776258186d7728424d1bc29a7600000000001055445534c41010355534400e1f50500000000' + +const setOracleData: SetOracleData = { + oracleId: 'd1248472d78681257637f6cafb6eab9b89f4a64eb8c425208b52258994351d06', + timestamp: new BigNumber('1621567932'), + tokens: [ + { + token: 'TESLA', + prices: [ + { + currency: 'USD', + amount: new BigNumber('1.0') + } + ] + } + ] +} + +it('should craft dftx with OP_CODES._()', () => { + const stack = [ + OP_CODES.OP_RETURN, + OP_CODES.OP_DEFI_TX_SET_ORACLE_DATA(setOracleData) + ] + + 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 CSetOracleData(buffer) + + expect(composable.toObject()).toEqual(setOracleData) + }) + + it('should compose from composable to buffer', () => { + const composable = new CSetOracleData(setOracleData) + const buffer = new SmartBuffer() + composable.toBuffer(buffer) + + expect(buffer.toBuffer().toString('hex')).toEqual(data) + }) +}) diff --git a/packages/jellyfish-transaction/__tests__/script/defi/dftx_oracles/UpdateOracle.test.ts b/packages/jellyfish-transaction/__tests__/script/defi/dftx_oracles/UpdateOracle.test.ts new file mode 100644 index 0000000000..d7fda1bfda --- /dev/null +++ b/packages/jellyfish-transaction/__tests__/script/defi/dftx_oracles/UpdateOracle.test.ts @@ -0,0 +1,80 @@ +import { SmartBuffer } from 'smart-buffer' +import { + CUpdateOracle, + UpdateOracle +} from '../../../../src/script/defi/dftx_oracles' +import { OP_CODES } from '../../../../src/script' +import { toBuffer, toOPCodes } from '../../../../src/script/_buffer' +import { OP_DEFI_TX } from '../../../../src/script/defi' + +it('should bi-directional buffer-object-buffer', () => { + const fixtures = [ + '6a4c5f4466547874061d35948925528b2025c4b84ea6f4899bab6efbcaf63776258186d7728424d11976a914ad1eaafdd6edcf2260f28cb31e24117c240681ca88ac0503055445534c4103455552055445534c41034a5059055445534c4103555344' + ] + + 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(0x74) + }) +}) + +const header = '6a4c5f4466547874' // OP_RETURN, PUSH_DATA(5f44665478, 74) +const data = '061d35948925528b2025c4b84ea6f4899bab6efbcaf63776258186d7728424d11976a914ad1eaafdd6edcf2260f28cb31e24117c240681ca88ac0503055445534c4103455552055445534c41034a5059055445534c4103555344' +const updateOracle: UpdateOracle = { + script: { + stack: [ + OP_CODES.OP_DUP, + OP_CODES.OP_HASH160, + OP_CODES.OP_PUSHDATA_HEX_LE('ad1eaafdd6edcf2260f28cb31e24117c240681ca'), + OP_CODES.OP_EQUALVERIFY, + OP_CODES.OP_CHECKSIG + ] + }, + oracleId: 'd1248472d78681257637f6cafb6eab9b89f4a64eb8c425208b52258994351d06', + weightage: 5, + pricefeeds: [ + { + token: 'TESLA', + currency: 'EUR' + }, + { + token: 'TESLA', + currency: 'JPY' + }, + { + token: 'TESLA', + currency: 'USD' + } + ] +} + +it('should craft dftx with OP_CODES._()', () => { + const stack = [ + OP_CODES.OP_RETURN, + OP_CODES.OP_DEFI_TX_UPDATE_ORACLE(updateOracle) + ] + + 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 CUpdateOracle(buffer) + + expect(composable.toObject()).toEqual(updateOracle) + }) + + it('should compose from composable to buffer', () => { + const composable = new CUpdateOracle(updateOracle) + 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 2b5cf030e4..8f1ccef67b 100644 --- a/packages/jellyfish-transaction/src/script/defi/dftx.ts +++ b/packages/jellyfish-transaction/src/script/defi/dftx.ts @@ -16,6 +16,16 @@ import { PoolSwap } from './dftx_pool' import { CTokenMint, TokenMint } from './dftx_token' +import { + CAppointOracle, + AppointOracle, + CUpdateOracle, + UpdateOracle, + CRemoveOracle, + RemoveOracle, + CSetOracleData, + SetOracleData +} from './dftx_oracles' import { CDeFiOpUnmapped, DeFiOpUnmapped } from './dftx_unmapped' // Disabling no-return-assign makes the code cleaner with the setter and getter */ @@ -109,6 +119,14 @@ export class CDfTx extends ComposableBuffer> { return compose(CAccountToAccount.OP_NAME, d => new CAccountToAccount(d)) case CAnyAccountToAccount.OP_CODE: return compose(CAnyAccountToAccount.OP_NAME, d => new CAnyAccountToAccount(d)) + case CAppointOracle.OP_CODE: + return compose(CAppointOracle.OP_NAME, d => new CAppointOracle(d)) + case CRemoveOracle.OP_CODE: + return compose(CRemoveOracle.OP_NAME, d => new CRemoveOracle(d)) + case CUpdateOracle.OP_CODE: + return compose(CUpdateOracle.OP_NAME, d => new CUpdateOracle(d)) + case CSetOracleData.OP_CODE: + return compose(CSetOracleData.OP_NAME, d => new CSetOracleData(d)) case CAutoAuthPrep.OP_CODE: return compose(CAutoAuthPrep.OP_NAME, () => new CAutoAuthPrep()) default: diff --git a/packages/jellyfish-transaction/src/script/defi/dftx_oracles.ts b/packages/jellyfish-transaction/src/script/defi/dftx_oracles.ts new file mode 100644 index 0000000000..286de2454d --- /dev/null +++ b/packages/jellyfish-transaction/src/script/defi/dftx_oracles.ts @@ -0,0 +1,110 @@ +import { BufferComposer, ComposableBuffer } from '../../buffer/buffer_composer' +import { CurrencyPair, CCurrencyPair, CTokenPrice, TokenPrice } from './dftx_price' +import { Script } from '../../tx' +import { CScript } from '../../tx_composer' +import BigNumber from 'bignumber.js' + +// Disabling no-return-assign makes the code cleaner with the setter and getter */ +/* eslint-disable no-return-assign */ + +/** + * AppointOracle DeFi Transaction + */ +export interface AppointOracle { + script: Script // ---------------------| n = VarUInt{1-9 bytes}, + n bytes + weightage: number // ------------------| 1 byte unsigned int + priceFeeds: CurrencyPair[] // ---------| c = VarUInt{1-9 bytes}, + c x CurrencyPair +} + +/** + * Composable AppointOracle, C stands for Composable. + * Immutable by design, bi-directional fromBuffer, toBuffer deep composer. + */ +export class CAppointOracle extends ComposableBuffer { + static OP_CODE = 0x6f + static OP_NAME = 'OP_DEFI_TX_APPOINT_ORACLE' + + composers (ao: AppointOracle): BufferComposer[] { + return [ + ComposableBuffer.single