diff --git a/samples/hook-schemas/evernode-reputation/hook_state.ts b/samples/hook-schemas/evernode-reputation/hook_state.ts index 81dae2c..afa9aaa 100644 --- a/samples/hook-schemas/evernode-reputation/hook_state.ts +++ b/samples/hook-schemas/evernode-reputation/hook_state.ts @@ -10,10 +10,9 @@ export const EvernodeReputationHookStateDefinition: Definition['hook_states'] = byte_length: 24, }, { - type: 'VarString', + type: 'HexBinary', name: 'Special', pattern: 'FFFFFFFFFFFFFFFF', - binary: true, byte_length: 8, }, ], diff --git a/samples/hook-schemas/evernode/common/configuration.ts b/samples/hook-schemas/evernode/common/configuration.ts index c75abc0..623df06 100644 --- a/samples/hook-schemas/evernode/common/configuration.ts +++ b/samples/hook-schemas/evernode/common/configuration.ts @@ -436,7 +436,6 @@ export const CONF_MOMENT_TRANSIT_INFO: State = { { type: 'VarString', name: 'Key', - pattern: 'EVR', byte_length: 3, exclude: true, @@ -480,7 +479,6 @@ export const CONF_MAX_EMIT_TRX_FEE: State = { { type: 'VarString', name: 'Key', - pattern: 'EVR', byte_length: 3, exclude: true, diff --git a/samples/hook-schemas/evernode/common/repetitive-state-keys.ts b/samples/hook-schemas/evernode/common/repetitive-state-keys.ts index e769b45..6c24eb2 100644 --- a/samples/hook-schemas/evernode/common/repetitive-state-keys.ts +++ b/samples/hook-schemas/evernode/common/repetitive-state-keys.ts @@ -32,10 +32,9 @@ export const STP_TOKEN_ID: State = { exclude: true, }, { - type: 'VarString', + type: 'HexBinary', name: 'TokenID', byte_length: 28, - binary: true, }, ], // @@ -110,10 +109,8 @@ export const STP_HOST_ADDR: State = { // hookstate_data: [ { - type: 'VarString', + type: 'LedgerEntryID', name: 'TokenID', - byte_length: 32, - binary: true, }, { type: 'VarString', @@ -172,8 +169,9 @@ export const STP_HOST_ADDR: State = { delimiter: '.', }, { - type: 'UInt64', + type: 'DateTime64', name: 'Registration Timestamp', + epoch: 'unix', }, { type: 'UInt8', @@ -184,8 +182,9 @@ export const STP_HOST_ADDR: State = { name: 'Last Vote Candidate Idx', }, { - type: 'UInt64', + type: 'DateTime64', name: 'Last Vote Timestamp', + epoch: 'unix', }, { type: 'UInt8', @@ -200,8 +199,9 @@ export const STP_HOST_ADDR: State = { name: 'Flags', }, { - type: 'UInt64', + type: 'DateTime64', name: 'Transfer Timestamp', + epoch: 'unix', }, { type: 'XFL', @@ -239,10 +239,9 @@ export const STP_HOST_ADDR_081: State = { // hookstate_data: [ { - type: 'VarString', + type: 'LedgerEntryID', name: 'TokenID', byte_length: 32, - binary: true, }, { type: 'VarString', @@ -301,8 +300,9 @@ export const STP_HOST_ADDR_081: State = { delimiter: '.', }, { - type: 'UInt64', + type: 'DateTime64', name: 'Registration Timestamp', + epoch: 'unix', }, { type: 'UInt8', @@ -313,8 +313,9 @@ export const STP_HOST_ADDR_081: State = { name: 'Last Vote Candidate Idx', }, { - type: 'UInt64', + type: 'DateTime64', name: 'Last Vote Timestamp', + epoch: 'unix', }, { type: 'UInt8', @@ -329,8 +330,9 @@ export const STP_HOST_ADDR_081: State = { name: 'Flags', }, { - type: 'UInt64', + type: 'DateTime64', name: 'Transfer Timestamp', + epoch: 'unix', }, ], } @@ -364,10 +366,8 @@ export const STP_HOST_ADDR_080: State = { // hookstate_data: [ { - type: 'VarString', + type: 'LedgerEntryID', name: 'TokenID', - byte_length: 32, - binary: true, }, { type: 'VarString', @@ -426,8 +426,9 @@ export const STP_HOST_ADDR_080: State = { delimiter: '.', }, { - type: 'UInt64', + type: 'DateTime64', name: 'Registration Timestamp', + epoch: 'unix', }, { type: 'UInt8', @@ -438,8 +439,9 @@ export const STP_HOST_ADDR_080: State = { name: 'Last Vote Candidate Idx', }, { - type: 'UInt64', + type: 'DateTime64', name: 'Last Vote Timestamp', + epoch: 'unix', }, { type: 'UInt8', @@ -492,10 +494,8 @@ export const STP_TRANSFEREE_ADDR: State = { name: 'Registration Ledger', }, { - type: 'VarString', + type: 'LedgerEntryID', name: 'TokenID', - byte_length: 32, - binary: true, }, ], } @@ -585,11 +585,12 @@ export const STP_CANDIDATE_ID: State = { byte_length: 20, }, { - type: 'UInt64', + type: 'DateTime64', name: 'Created Timestamp', + epoch: 'unix', }, { - type: 'UInt64', + type: 'XFL', name: 'Proposal Fee', }, { @@ -597,16 +598,18 @@ export const STP_CANDIDATE_ID: State = { name: 'Positive Vote Count', }, { - type: 'UInt64', + type: 'DateTime64', name: 'Last Vote Timestamp', + epoch: 'unix', }, { type: 'UInt8', name: 'Status', }, { - type: 'UInt64', + type: 'DateTime64', name: 'Status Change Timestamp', + epoch: 'unix', }, { type: 'UInt8', diff --git a/samples/hook-schemas/evernode/common/singleton-keys.ts b/samples/hook-schemas/evernode/common/singleton-keys.ts index 3d9e8b5..f563a82 100644 --- a/samples/hook-schemas/evernode/common/singleton-keys.ts +++ b/samples/hook-schemas/evernode/common/singleton-keys.ts @@ -257,24 +257,27 @@ export const STK_GOVERNANCE_INFO: State = { name: 'Voter Base Count', }, { - type: 'UInt64', + type: 'DateTime64', name: 'Voter Base Count Changed Timestamp', + epoch: 'unix', }, { type: 'UInt32', name: 'Foundation Last Voted Candidate Idx', }, { - type: 'UInt64', + type: 'DateTime64', name: 'Foundation Last Voted Timestamp', + epoch: 'unix', }, { type: 'Hash256', name: 'Elected Proposal Unique Id', }, { - type: 'UInt64', + type: 'DateTime64', name: 'Proposal Elected Timestamp', + epoch: 'unix', }, { type: 'UInt8', diff --git a/samples/hook-schemas/oracle/invoke_blob.ts b/samples/hook-schemas/oracle/invoke_blob.ts index 011dbde..70fe38e 100644 --- a/samples/hook-schemas/oracle/invoke_blob.ts +++ b/samples/hook-schemas/oracle/invoke_blob.ts @@ -1,6 +1,3 @@ -import { currencyToHex } from '@transia/hooks-toolkit/dist/npm/src/libs/binary-models' -import { decodeAccountID } from '@transia/xrpl' -import sha512h from '@transia/xrpl/dist/npm/utils/hashes/sha512Half' import type { Definition } from '../../../schema' export const OracleInvokeDefinition: Definition['invoke_blobs'] = { @@ -17,7 +14,7 @@ export const OracleInvokeDefinition: Definition['invoke_blobs'] = { name: 'issuer', }, { - type: 'VarString', + type: 'Currency', byte_length: 20, name: 'currency', }, diff --git a/samples/hook-schemas/xahau-governance/hook_state.ts b/samples/hook-schemas/xahau-governance/hook_state.ts index 7eecb21..eab9e51 100644 --- a/samples/hook-schemas/xahau-governance/hook_state.ts +++ b/samples/hook-schemas/xahau-governance/hook_state.ts @@ -133,7 +133,7 @@ export const GovernanceHookStateDefinition: Definition['hook_states'] = { ], hookstate_data: [ { - type: 'Hash256', + type: 'HookHash', name: 'HookHash', }, ], @@ -247,9 +247,8 @@ export const GovernanceHookStateDefinition: Definition['hook_states'] = { name: 'Layer', }, { - type: 'VarString', + type: 'HexBinary', byte_length: 28, - binary: true, name: 'HookHash', }, ], @@ -274,11 +273,8 @@ export const GovernanceHookStateDefinition: Definition['hook_states'] = { name: 'Layer', }, { - type: 'VarString', - name: 'padding', + type: 'Null', byte_length: 20, - binary: true, - exclude: true, }, { type: 'XFL', @@ -306,11 +302,8 @@ export const GovernanceHookStateDefinition: Definition['hook_states'] = { name: 'Layer', }, { - type: 'VarString', - name: 'padding', + type: 'Null', byte_length: 20, - binary: true, - exclude: true, }, { type: 'XFL', diff --git a/samples/parser.ts b/samples/parser.ts index 9084bc1..95b8add 100644 --- a/samples/parser.ts +++ b/samples/parser.ts @@ -1,8 +1,15 @@ import { floatToXfl } from '@transia/hooks-toolkit' -import { hexToXfl } from '@transia/hooks-toolkit/dist/npm/src/libs/binary-models' +import { hexToCurrency, hexToXfl } from '@transia/hooks-toolkit/dist/npm/src/libs/binary-models' import type { HookParameter } from '@transia/xrpl/dist/npm/models/common' import type { HookState } from '@transia/xrpl/dist/npm/models/ledger' -import { decodeAccountID, encodeAccountID } from '@transia/xrpl/dist/npm/utils' +import { + decodeAccountID, + encodeAccountID, + isoTimeToRippleTime, + rippleTimeToISOTime, + rippleTimeToUnixTime, + unixTimeToRippleTime, +} from '@transia/xrpl/dist/npm/utils' import sha512Half from '@transia/xrpl/dist/npm/utils/hashes/sha512Half' import type { HookParameterDefinition } from 'schema/HookParameter' import type { OperationDefinition } from 'schema/Operation' @@ -27,9 +34,16 @@ const getTypeByteLength = (type: Field['type']) => { UInt32: 4, UInt64: 8, XFL: 8, + Currency: 20, Hash256: 32, + DateTime: 4, + DateTime64: 8, + TxHash: 32, + HookHash: 32, + LedgerEntryID: 32, Array: undefined, VarString: undefined, + HexBinary: undefined, Null: undefined, } return typeToLength[type] @@ -57,16 +71,31 @@ const bufferToReadableData = ( return buffer.readUInt16LE() case 'UInt32': return buffer.readUint32LE() + case 'DateTime': { + if (state.epoch === 'unix') return rippleTimeToISOTime(unixTimeToRippleTime(buffer.readUint32LE())) + return rippleTimeToISOTime(buffer.readUint32LE()) + } case 'UInt64': return buffer.readBigUint64LE() + case 'DateTime64': { + if (state.epoch === 'unix') return rippleTimeToISOTime(unixTimeToRippleTime(Number(buffer.readBigUint64LE()))) + return rippleTimeToISOTime(Number(buffer.readBigUint64LE())) + } case 'XFL': return hexToXfl(buffer.toString('hex')) + case 'Currency': + return hexToCurrency(buffer.toString('hex')) case 'VarString': if (state.binary === true) return buffer.toString('hex').toUpperCase() return buffer.toString('utf-8').replace(/\0/g, nullReplaceTo) + case 'HexBinary': + return buffer.toString('hex').toUpperCase() case 'Null': return nullReplaceTo.repeat(state.byte_length) case 'Hash256': + case 'TxHash': + case 'HookHash': + case 'LedgerEntryID': return buffer.toString('hex').toUpperCase() case 'Array': { let from = 0 @@ -107,6 +136,24 @@ const toHex = (state: Field, value: null | string | number | bigint | Array diff --git a/schema.json b/schema.json index f8f366d..f696b12 100644 --- a/schema.json +++ b/schema.json @@ -25,14 +25,35 @@ { "$ref": "#/definitions/XFL" }, + { + "$ref": "#/definitions/Currency" + }, { "$ref": "#/definitions/VarString" }, + { + "$ref": "#/definitions/HexBinary" + }, { "$ref": "#/definitions/Null" }, { "$ref": "#/definitions/Hash256" + }, + { + "$ref": "#/definitions/DateTime" + }, + { + "$ref": "#/definitions/DateTime64" + }, + { + "$ref": "#/definitions/TxHash" + }, + { + "$ref": "#/definitions/HookHash" + }, + { + "$ref": "#/definitions/LedgerEntryID" } ] }, @@ -57,14 +78,35 @@ { "$ref": "#/definitions/XFL" }, + { + "$ref": "#/definitions/Currency" + }, { "$ref": "#/definitions/VarString" }, + { + "$ref": "#/definitions/HexBinary" + }, { "$ref": "#/definitions/Null" }, { "$ref": "#/definitions/Hash256" + }, + { + "$ref": "#/definitions/DateTime" + }, + { + "$ref": "#/definitions/DateTime64" + }, + { + "$ref": "#/definitions/TxHash" + }, + { + "$ref": "#/definitions/HookHash" + }, + { + "$ref": "#/definitions/LedgerEntryID" } ] }, @@ -283,6 +325,41 @@ "additionalProperties": false, "required": ["name", "type"] }, + "Currency": { + "title": "Currency", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["Currency"], + "title": "type" + }, + "byte_length": { + "default": 20, + "type": "number", + "title": "byte_length" + }, + "name": { + "type": "string", + "title": "name" + }, + "field": { + "type": "string", + "title": "field" + }, + "pattern": { + "type": "string", + "title": "pattern" + }, + "exclude": { + "default": false, + "type": "boolean", + "title": "exclude" + } + }, + "additionalProperties": false, + "required": ["name", "type"] + }, "XFL": { "title": "XFL", "type": "object", @@ -331,14 +408,56 @@ "type": "number", "title": "byte_length" }, + "binary": { + "default": false, + "type": "boolean", + "title": "binary" + }, + "length_prefix": { + "description": "The length prefix consists of either two or four bytes\n(depending on the length of the string)\nand indicates the number of raw bytes in the string", + "default": false, + "type": "boolean", + "title": "length_prefix" + }, + "to_last": { + "description": "All remaining data in Buffer is considered to be the value", + "default": false, + "type": "boolean", + "title": "to_last" + }, + "name": { + "type": "string", + "title": "name" + }, + "field": { + "type": "string", + "title": "field" + }, "pattern": { "type": "string", "title": "pattern" }, - "binary": { + "exclude": { "default": false, "type": "boolean", - "title": "binary" + "title": "exclude" + } + }, + "additionalProperties": false, + "required": ["name", "type"] + }, + "HexBinary": { + "title": "HexBinary", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["HexBinary"], + "title": "type" + }, + "byte_length": { + "type": "number", + "title": "byte_length" }, "length_prefix": { "description": "The length prefix consists of either two or four bytes\n(depending on the length of the string)\nand indicates the number of raw bytes in the string", @@ -360,6 +479,10 @@ "type": "string", "title": "field" }, + "pattern": { + "type": "string", + "title": "pattern" + }, "exclude": { "default": false, "type": "boolean", @@ -430,6 +553,193 @@ "additionalProperties": false, "required": ["name", "type"] }, + "DateTime": { + "description": "DateTime(uint32) as unix timestamp (seconds) or ripple timestamp", + "title": "DateTime", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["DateTime"], + "title": "type" + }, + "byte_length": { + "default": 4, + "type": "number", + "title": "byte_length" + }, + "epoch": { + "enum": ["ripple", "unix"], + "type": "string", + "title": "epoch" + }, + "name": { + "type": "string", + "title": "name" + }, + "field": { + "type": "string", + "title": "field" + }, + "pattern": { + "type": "string", + "title": "pattern" + }, + "exclude": { + "default": false, + "type": "boolean", + "title": "exclude" + } + }, + "additionalProperties": false, + "required": ["epoch", "name", "type"] + }, + "DateTime64": { + "description": "DateTime(uint64) as unix timestamp (seconds) or ripple timestamp\n\nAs default RippleEpoch is uint32, but ledger_last_time api return as uint64.\nSo we need to define another type for DateTime as DateTime64.", + "title": "DateTime64", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["DateTime64"], + "title": "type" + }, + "byte_length": { + "default": 8, + "type": "number", + "title": "byte_length" + }, + "epoch": { + "enum": ["ripple", "unix"], + "type": "string", + "title": "epoch" + }, + "name": { + "type": "string", + "title": "name" + }, + "field": { + "type": "string", + "title": "field" + }, + "pattern": { + "type": "string", + "title": "pattern" + }, + "exclude": { + "default": false, + "type": "boolean", + "title": "exclude" + } + }, + "additionalProperties": false, + "required": ["epoch", "name", "type"] + }, + "TxHash": { + "title": "TxHash", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["TxHash"], + "title": "type" + }, + "byte_length": { + "default": 32, + "type": "number", + "title": "byte_length" + }, + "name": { + "type": "string", + "title": "name" + }, + "field": { + "type": "string", + "title": "field" + }, + "pattern": { + "type": "string", + "title": "pattern" + }, + "exclude": { + "default": false, + "type": "boolean", + "title": "exclude" + } + }, + "additionalProperties": false, + "required": ["name", "type"] + }, + "HookHash": { + "title": "HookHash", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["HookHash"], + "title": "type" + }, + "byte_length": { + "default": 32, + "type": "number", + "title": "byte_length" + }, + "name": { + "type": "string", + "title": "name" + }, + "field": { + "type": "string", + "title": "field" + }, + "pattern": { + "type": "string", + "title": "pattern" + }, + "exclude": { + "default": false, + "type": "boolean", + "title": "exclude" + } + }, + "additionalProperties": false, + "required": ["name", "type"] + }, + "LedgerEntryID": { + "title": "LedgerEntryID", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["LedgerEntryID"], + "title": "type" + }, + "byte_length": { + "default": 32, + "type": "number", + "title": "byte_length" + }, + "name": { + "type": "string", + "title": "name" + }, + "field": { + "type": "string", + "title": "field" + }, + "pattern": { + "type": "string", + "title": "pattern" + }, + "exclude": { + "default": false, + "type": "boolean", + "title": "exclude" + } + }, + "additionalProperties": false, + "required": ["name", "type"] + }, "HookParameterDefinition": { "title": "HookParameterDefinition", "type": "object", @@ -585,7 +895,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/definitions/Record", + "$ref": "#/definitions/Record", "title": "data" }, "txn_parameter_definition": { @@ -645,8 +955,15 @@ "enum": [ "AccountID", "Array", + "Currency", + "DateTime", + "DateTime64", "Hash256", + "HexBinary", + "HookHash", + "LedgerEntryID", "Null", + "TxHash", "UInt16", "UInt32", "UInt64", @@ -664,8 +981,15 @@ "enum": [ "AccountID", "Array", + "Currency", + "DateTime", + "DateTime64", "Hash256", + "HexBinary", + "HookHash", + "LedgerEntryID", "Null", + "TxHash", "UInt16", "UInt32", "UInt64", @@ -934,7 +1258,24 @@ "type": "object", "properties": { "type": { - "enum": ["AccountID", "Hash256", "Null", "UInt16", "UInt32", "UInt64", "UInt8", "VarString", "XFL"], + "enum": [ + "AccountID", + "Currency", + "DateTime", + "DateTime64", + "Hash256", + "HexBinary", + "HookHash", + "LedgerEntryID", + "Null", + "TxHash", + "UInt16", + "UInt32", + "UInt64", + "UInt8", + "VarString", + "XFL" + ], "type": "string", "title": "type" }, @@ -956,8 +1297,8 @@ "additionalProperties": false, "required": ["type"] }, - "Record": { - "title": "Record", + "Record": { + "title": "Record", "type": "object", "additionalProperties": false } diff --git a/schema/Field/index.ts b/schema/Field/index.ts index ff4860f..0a1f138 100644 --- a/schema/Field/index.ts +++ b/schema/Field/index.ts @@ -1,6 +1,22 @@ export type Field = ArrayField | NonArrayField -type NonArrayField = AccountID | UInt8 | UInt16 | UInt32 | UInt64 | XFL | VarString | Null | Hash256 +type NonArrayField = + | AccountID + | UInt8 + | UInt16 + | UInt32 + | UInt64 + | XFL + | Currency + | VarString + | HexBinary + | Null + | Hash256 + | DateTime + | DateTime64 + | TxHash + | HookHash + | LedgerEntryID export interface ArrayField extends FieldBase { type: 'Array' @@ -78,6 +94,14 @@ export interface UInt64 extends FieldBase { byte_length?: number } +export interface Currency extends FieldBase { + type: 'Currency' + /** + * @default 20 + */ + byte_length?: number +} + export interface XFL extends FieldBase { type: 'XFL' /** @@ -86,14 +110,33 @@ export interface XFL extends FieldBase { byte_length?: number } -export interface VarString extends Omit { +export interface VarString extends FieldBase { type: 'VarString' byte_length?: number - pattern?: string /** * @default false + * @deprecated use instead HexBinary type */ binary?: boolean + /** + * The length prefix consists of either two or four bytes + * (depending on the length of the string) + * and indicates the number of raw bytes in the string + * @default false + * @deprecated use instead HexBinary type + */ + length_prefix?: boolean + /** + * All remaining data in Buffer is considered to be the value + * @default false + * @deprecated use instead HexBinary type + */ + to_last?: boolean +} + +export interface HexBinary extends FieldBase { + type: 'HexBinary' + byte_length?: number /** * The length prefix consists of either two or four bytes * (depending on the length of the string) @@ -120,3 +163,54 @@ export interface Hash256 extends FieldBase { */ byte_length?: number } + +/** + * DateTime(uint32) as unix timestamp (seconds) or ripple timestamp + */ +export interface DateTime extends FieldBase { + type: 'DateTime' + /** + * @default 4 + */ + byte_length?: number + epoch: 'unix' | 'ripple' +} + +/** + * DateTime(uint64) as unix timestamp (seconds) or ripple timestamp + * + * As default RippleEpoch is uint32, but ledger_last_time api return as uint64. + * So we need to define another type for DateTime as DateTime64. + */ +export interface DateTime64 extends FieldBase { + type: 'DateTime64' + /** + * @default 8 + */ + byte_length?: number + epoch: 'unix' | 'ripple' +} + +export interface TxHash extends FieldBase { + type: 'TxHash' + /** + * @default 32 + */ + byte_length?: number +} + +export interface HookHash extends FieldBase { + type: 'HookHash' + /** + * @default 32 + */ + byte_length?: number +} + +export interface LedgerEntryID extends FieldBase { + type: 'LedgerEntryID' + /** + * @default 32 + */ + byte_length?: number +}