From 2137497ce8bd8fd50c393eb41fda278afc236f69 Mon Sep 17 00:00:00 2001 From: g0drlc Date: Fri, 2 Feb 2024 10:29:43 -0600 Subject: [PATCH] feat: support transfers by ordinal number instead of inscription id (#296) * feat: transfers by ordinal number * fix: upgrade client --- package-lock.json | 14 +-- package.json | 2 +- src/pg/brc20/brc20-pg-store.ts | 23 ++--- src/pg/brc20/helpers.ts | 4 +- src/pg/counts/counts-pg-store.ts | 10 +- src/pg/helpers.ts | 25 +++-- src/pg/pg-store.ts | 103 +++++++++++++------- src/pg/types.ts | 109 +++++++++++++-------- tests/brc20.test.ts | 158 +++++++++++++++++++++++-------- tests/cache.test.ts | 6 +- tests/helpers.ts | 3 +- tests/inscriptions.test.ts | 28 +++--- tests/server.test.ts | 4 +- 13 files changed, 323 insertions(+), 166 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3c147ab..5681507 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@fastify/swagger": "^8.3.1", "@fastify/type-provider-typebox": "^3.2.0", "@hirosystems/api-toolkit": "^1.3.1", - "@hirosystems/chainhook-client": "^1.5.0", + "@hirosystems/chainhook-client": "^1.6.0", "@semantic-release/changelog": "^6.0.3", "@semantic-release/commit-analyzer": "^10.0.4", "@semantic-release/git": "^10.0.1", @@ -1299,9 +1299,9 @@ } }, "node_modules/@hirosystems/chainhook-client": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@hirosystems/chainhook-client/-/chainhook-client-1.5.0.tgz", - "integrity": "sha512-iAhY3848Y64wiNhNx9PpntlxXGjdVPaxX1JfoivRh5S73Vo2BBEWrwYrcpyJrlYM0PnRNOry7/OUHGN33xwO3Q==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@hirosystems/chainhook-client/-/chainhook-client-1.6.0.tgz", + "integrity": "sha512-8skXF0Hk5XL5LH6enYySyuwEy3Kzn8AAb5+ERG0w9WSfehBNjLkL3zKJFuwg8Ov926YzE7a2ZnzlUpVmvvlulw==", "dependencies": { "@fastify/type-provider-typebox": "^3.2.0", "fastify": "^4.15.0", @@ -19743,9 +19743,9 @@ } }, "@hirosystems/chainhook-client": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@hirosystems/chainhook-client/-/chainhook-client-1.5.0.tgz", - "integrity": "sha512-iAhY3848Y64wiNhNx9PpntlxXGjdVPaxX1JfoivRh5S73Vo2BBEWrwYrcpyJrlYM0PnRNOry7/OUHGN33xwO3Q==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@hirosystems/chainhook-client/-/chainhook-client-1.6.0.tgz", + "integrity": "sha512-8skXF0Hk5XL5LH6enYySyuwEy3Kzn8AAb5+ERG0w9WSfehBNjLkL3zKJFuwg8Ov926YzE7a2ZnzlUpVmvvlulw==", "requires": { "@fastify/type-provider-typebox": "^3.2.0", "fastify": "^4.15.0", diff --git a/package.json b/package.json index 67adef7..17dad3d 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "@fastify/swagger": "^8.3.1", "@fastify/type-provider-typebox": "^3.2.0", "@hirosystems/api-toolkit": "^1.3.1", - "@hirosystems/chainhook-client": "^1.5.0", + "@hirosystems/chainhook-client": "^1.6.0", "@semantic-release/changelog": "^6.0.3", "@semantic-release/commit-analyzer": "^10.0.4", "@semantic-release/git": "^10.0.1", diff --git a/src/pg/brc20/brc20-pg-store.ts b/src/pg/brc20/brc20-pg-store.ts index 0332595..357795a 100644 --- a/src/pg/brc20/brc20-pg-store.ts +++ b/src/pg/brc20/brc20-pg-store.ts @@ -3,12 +3,13 @@ import * as postgres from 'postgres'; import { hexToBuffer } from '../../api/util/helpers'; import { DbInscriptionIndexPaging, - DbInscriptionInsert, - DbLocationInsert, + InscriptionData, DbLocationPointerInsert, DbLocationTransferType, DbPaginatedResult, - DbRevealInsert, + InscriptionEventData, + LocationData, + InscriptionRevealData, } from '../types'; import { BRC20_DEPLOYS_COLUMNS, @@ -39,13 +40,13 @@ export class Brc20PgStore extends BasePgStoreModule { } async insertOperations(args: { - reveals: DbRevealInsert[]; + reveals: InscriptionEventData[]; pointers: DbLocationPointerInsert[]; }): Promise { for (const [i, reveal] of args.reveals.entries()) { const pointer = args.pointers[i]; if (parseInt(pointer.block_height) < BRC20_GENESIS_BLOCK) continue; - if (reveal.inscription) { + if ('inscription' in reveal) { if ( reveal.inscription.classic_number < 0 || reveal.inscription.number < 0 || @@ -75,7 +76,7 @@ export class Brc20PgStore extends BasePgStoreModule { } async applyTransfer(args: { - reveal: DbRevealInsert; + reveal: InscriptionEventData; pointer: DbLocationPointerInsert; }): Promise { await this.sqlWriteTransaction(async sql => { @@ -207,7 +208,7 @@ export class Brc20PgStore extends BasePgStoreModule { private async insertDeploy(deploy: { brc20: Brc20Deploy; - reveal: DbRevealInsert; + reveal: InscriptionEventData; pointer: DbLocationPointerInsert; }): Promise { if (deploy.reveal.location.transfer_type != DbLocationTransferType.transferred) return; @@ -257,7 +258,7 @@ export class Brc20PgStore extends BasePgStoreModule { private async insertMint(mint: { brc20: Brc20Mint; - reveal: DbRevealInsert; + reveal: InscriptionEventData; pointer: DbLocationPointerInsert; }): Promise { if (mint.reveal.location.transfer_type != DbLocationTransferType.transferred) return; @@ -335,7 +336,7 @@ export class Brc20PgStore extends BasePgStoreModule { private async insertTransfer(transfer: { brc20: Brc20Transfer; - reveal: DbRevealInsert; + reveal: InscriptionEventData; pointer: DbLocationPointerInsert; }): Promise { if (transfer.reveal.location.transfer_type != DbLocationTransferType.transferred) return; @@ -406,7 +407,7 @@ export class Brc20PgStore extends BasePgStoreModule { ); } - async rollBackInscription(args: { inscription: DbInscriptionInsert }): Promise { + async rollBackInscription(args: { inscription: InscriptionData }): Promise { const events = await this.sql` SELECT e.* FROM brc20_events AS e INNER JOIN inscriptions AS i ON i.id = e.inscription_id @@ -430,7 +431,7 @@ export class Brc20PgStore extends BasePgStoreModule { } } - async rollBackLocation(args: { location: DbLocationInsert }): Promise { + async rollBackLocation(args: { location: LocationData }): Promise { const events = await this.sql` SELECT e.* FROM brc20_events AS e INNER JOIN locations AS l ON l.id = e.genesis_location_id diff --git a/src/pg/brc20/helpers.ts b/src/pg/brc20/helpers.ts index de9d50a..3b1f195 100644 --- a/src/pg/brc20/helpers.ts +++ b/src/pg/brc20/helpers.ts @@ -2,7 +2,7 @@ import { Static, Type } from '@fastify/type-provider-typebox'; import { TypeCompiler } from '@sinclair/typebox/compiler'; import BigNumber from 'bignumber.js'; import { hexToBuffer } from '../../api/util/helpers'; -import { DbInscriptionInsert } from '../types'; +import { InscriptionData } from '../types'; const Brc20TickerSchema = Type.String({ minLength: 1 }); const Brc20NumberSchema = Type.RegEx(/^((\d+)|(\d*\.?\d+))$/); @@ -51,7 +51,7 @@ const UINT64_MAX = BigNumber('18446744073709551615'); // 20 digits const numExceedsMax = (num: string) => num.length >= 20 && UINT64_MAX.isLessThan(num); // For testing only -export function brc20FromInscription(inscription: DbInscriptionInsert): Brc20 | undefined { +export function brc20FromInscription(inscription: InscriptionData): Brc20 | undefined { if (inscription.number < 0) return; if (inscription.mime_type !== 'text/plain' && inscription.mime_type !== 'application/json') return; diff --git a/src/pg/counts/counts-pg-store.ts b/src/pg/counts/counts-pg-store.ts index 719b1b5..6994959 100644 --- a/src/pg/counts/counts-pg-store.ts +++ b/src/pg/counts/counts-pg-store.ts @@ -3,9 +3,9 @@ import { SatoshiRarity } from '../../api/util/ordinal-satoshi'; import { DbInscription, DbInscriptionIndexFilters, - DbInscriptionInsert, + InscriptionData, DbInscriptionType, - DbLocationInsert, + RevealLocationData, DbLocationPointer, } from '../types'; import { DbInscriptionIndexResultCountType } from './types'; @@ -55,7 +55,7 @@ export class CountsPgStore extends BasePgStoreModule { } } - async applyInscriptions(writes: DbInscriptionInsert[]): Promise { + async applyInscriptions(writes: InscriptionData[]): Promise { if (writes.length === 0) return; const mimeType = new Map(); const rarity = new Map(); @@ -105,8 +105,8 @@ export class CountsPgStore extends BasePgStoreModule { } async rollBackInscription(args: { - inscription: DbInscriptionInsert; - location: DbLocationInsert; + inscription: InscriptionData; + location: RevealLocationData; }): Promise { await this.sql` WITH decrease_mime_type AS ( diff --git a/src/pg/helpers.ts b/src/pg/helpers.ts index 8704135..4cef060 100644 --- a/src/pg/helpers.ts +++ b/src/pg/helpers.ts @@ -7,7 +7,12 @@ import { BitcoinInscriptionTransferred, } from '@hirosystems/chainhook-client'; import { ENV } from '../env'; -import { DbLocationTransferType, DbRevealInsert } from './types'; +import { + DbLocationTransferType, + InscriptionEventData, + InscriptionTransferData, + InscriptionRevealData, +} from './types'; import { OrdinalSatoshi } from '../api/util/ordinal-satoshi'; /** @@ -83,13 +88,13 @@ export function removeNullBytes(input: string): string { return input.replace(/\x00/g, ''); } -function revealInsertFromOrdhookInscriptionRevealed(args: { +function updateFromOrdhookInscriptionRevealed(args: { block_height: number; block_hash: string; tx_id: string; timestamp: number; reveal: BitcoinInscriptionRevealed; -}): DbRevealInsert { +}): InscriptionRevealData { const satoshi = new OrdinalSatoshi(args.reveal.ordinal_number); const satpoint = parseSatPoint(args.reveal.satpoint_post_inscription); const recursive_refs = getInscriptionRecursion(args.reveal.content_bytes); @@ -130,14 +135,14 @@ function revealInsertFromOrdhookInscriptionRevealed(args: { }; } -function revealInsertFromOrdhookInscriptionTransferred(args: { +function updateFromOrdhookInscriptionTransferred(args: { block_height: number; block_hash: string; tx_id: string; timestamp: number; blockTransferIndex: number; transfer: BitcoinInscriptionTransferred; -}): DbRevealInsert { +}): InscriptionTransferData { const satpoint = parseSatPoint(args.transfer.satpoint_post_transfer); const prevSatpoint = parseSatPoint(args.transfer.satpoint_pre_transfer); return { @@ -147,7 +152,7 @@ function revealInsertFromOrdhookInscriptionTransferred(args: { tx_id: args.tx_id, tx_index: args.transfer.tx_index, block_transfer_index: args.blockTransferIndex, - genesis_id: args.transfer.inscription_id, + ordinal_number: args.transfer.ordinal_number.toString(), address: args.transfer.destination.value ?? null, output: `${satpoint.tx_id}:${satpoint.vout}`, offset: satpoint.offset ?? null, @@ -164,18 +169,18 @@ function revealInsertFromOrdhookInscriptionTransferred(args: { }; } -export function revealInsertsFromOrdhookEvent(event: BitcoinEvent): DbRevealInsert[] { +export function revealInsertsFromOrdhookEvent(event: BitcoinEvent): InscriptionEventData[] { // Keep the relative ordering of a transfer within a block for faster future reads. let blockTransferIndex = 0; const block_height = event.block_identifier.index; const block_hash = normalizedHexString(event.block_identifier.hash); - const writes: DbRevealInsert[] = []; + const writes: InscriptionEventData[] = []; for (const tx of event.transactions) { const tx_id = normalizedHexString(tx.transaction_identifier.hash); for (const operation of tx.metadata.ordinal_operations) { if (operation.inscription_revealed) writes.push( - revealInsertFromOrdhookInscriptionRevealed({ + updateFromOrdhookInscriptionRevealed({ block_hash, block_height, tx_id, @@ -185,7 +190,7 @@ export function revealInsertsFromOrdhookEvent(event: BitcoinEvent): DbRevealInse ); if (operation.inscription_transferred) writes.push( - revealInsertFromOrdhookInscriptionTransferred({ + updateFromOrdhookInscriptionTransferred({ block_hash, block_height, tx_id, diff --git a/src/pg/pg-store.ts b/src/pg/pg-store.ts index ab8a6b6..ce4032b 100644 --- a/src/pg/pg-store.ts +++ b/src/pg/pg-store.ts @@ -26,15 +26,19 @@ import { DbInscriptionIndexFilters, DbInscriptionIndexOrder, DbInscriptionIndexPaging, - DbInscriptionInsert, + InscriptionData, DbInscriptionLocationChange, DbLocation, - DbLocationInsert, + RevealLocationData, DbLocationPointer, DbLocationPointerInsert, DbPaginatedResult, - DbRevealInsert, + InscriptionEventData, LOCATIONS_COLUMNS, + InscriptionRevealData, + InscriptionInsert, + LocationInsert, + LocationData, } from './types'; export const MIGRATIONS_DIR = path.join(__dirname, '../../migrations'); @@ -123,8 +127,8 @@ export class PgStore extends BasePgStore { const time = stopwatch(); const writes = revealInsertsFromOrdhookEvent(event); const newBlessedNumbers = writes - .filter(w => w.inscription !== undefined && w.inscription.number >= 0) - .map(w => w.inscription?.number ?? 0); + .filter(w => 'inscription' in w && w.inscription.number >= 0) + .map(w => (w as InscriptionRevealData).inscription.number ?? 0); assertNoBlockInscriptionGap({ currentNumber: currentBlessedNumber, newNumbers: newBlessedNumbers, @@ -484,17 +488,55 @@ export class PgStore extends BasePgStore { `; // roughly 35 days of blocks, assuming 10 minute block times on a full database } - private async insertInscriptions(reveals: DbRevealInsert[]): Promise { + private async insertInscriptions(reveals: InscriptionEventData[]): Promise { if (reveals.length === 0) return; await this.sqlWriteTransaction(async sql => { - const inscriptions: DbInscriptionInsert[] = []; - const transferGenesisIds = new Set(); + const inscriptionInserts: InscriptionInsert[] = []; + const locationInserts: LocationInsert[] = []; + const revealOutputs: InscriptionEventData[] = []; + const transferredOrdinalNumbersSet = new Set(); for (const r of reveals) - if (r.inscription) inscriptions.push(r.inscription); - else transferGenesisIds.add(r.location.genesis_id); - if (inscriptions.length) + if ('inscription' in r) { + revealOutputs.push(r); + inscriptionInserts.push(r.inscription); + locationInserts.push({ + ...r.location, + inscription_id: sql`(SELECT id FROM inscriptions WHERE genesis_id = ${r.location.genesis_id})`, + timestamp: sql`TO_TIMESTAMP(${r.location.timestamp})`, + }); + } else { + transferredOrdinalNumbersSet.add(r.location.ordinal_number); + // Transfers can move multiple inscriptions in the same sat, we must expand all of them so + // we can update their respective locations. + // TODO: This could probably be optimized to use fewer queries. + const inscriptionIds = await sql<{ id: string; genesis_id: string }[]>` + SELECT id, genesis_id FROM inscriptions WHERE sat_ordinal = ${r.location.ordinal_number} + `; + for (const row of inscriptionIds) { + revealOutputs.push(r); + locationInserts.push({ + genesis_id: row.genesis_id, + inscription_id: row.id, + block_height: r.location.block_height, + block_hash: r.location.block_hash, + tx_id: r.location.tx_id, + tx_index: r.location.tx_index, + address: r.location.address, + output: r.location.output, + offset: r.location.offset, + prev_output: r.location.prev_output, + prev_offset: r.location.prev_offset, + value: r.location.value, + transfer_type: r.location.transfer_type, + block_transfer_index: r.location.block_transfer_index, + timestamp: sql`TO_TIMESTAMP(${r.location.timestamp})`, + }); + } + } + const transferredOrdinalNumbers = [...transferredOrdinalNumbersSet]; + if (inscriptionInserts.length) await sql` - INSERT INTO inscriptions ${sql(inscriptions)} + INSERT INTO inscriptions ${sql(inscriptionInserts)} ON CONFLICT ON CONSTRAINT inscriptions_number_unique DO UPDATE SET genesis_id = EXCLUDED.genesis_id, mime_type = EXCLUDED.mime_type, @@ -507,13 +549,8 @@ export class PgStore extends BasePgStore { sat_coinbase_height = EXCLUDED.sat_coinbase_height, updated_at = NOW() `; - const locationData = reveals.map(i => ({ - ...i.location, - inscription_id: sql`(SELECT id FROM inscriptions WHERE genesis_id = ${i.location.genesis_id} LIMIT 1)`, - timestamp: sql`TO_TIMESTAMP(${i.location.timestamp})`, - })); const pointers = await sql` - INSERT INTO locations ${sql(locationData)} + INSERT INTO locations ${sql(locationInserts)} ON CONFLICT ON CONSTRAINT locations_inscription_id_block_height_tx_index_unique DO UPDATE SET genesis_id = EXCLUDED.genesis_id, block_hash = EXCLUDED.block_hash, @@ -526,21 +563,23 @@ export class PgStore extends BasePgStore { RETURNING inscription_id, id AS location_id, block_height, tx_index, address `; await this.updateInscriptionRecursions(reveals); - if (transferGenesisIds.size) + if (transferredOrdinalNumbers.length) await sql` UPDATE inscriptions SET updated_at = NOW() - WHERE genesis_id IN ${sql([...transferGenesisIds])} + WHERE sat_ordinal IN ${sql(transferredOrdinalNumbers)} `; await this.updateInscriptionLocationPointers(pointers); for (const reveal of reveals) { - const action = reveal.inscription ? `reveal #${reveal.inscription.number}` : `transfer`; - logger.info( - `PgStore ${action} (${reveal.location.genesis_id}) at block ${reveal.location.block_height}` - ); + const action = + 'inscription' in reveal + ? `reveal #${reveal.inscription.number} (${reveal.location.genesis_id})` + : `transfer sat ${reveal.location.ordinal_number}`; + logger.info(`PgStore ${action} at block ${reveal.location.block_height}`); } - await this.counts.applyInscriptions(inscriptions); - if (ENV.BRC20_BLOCK_SCAN_ENABLED) await this.brc20.insertOperations({ reveals, pointers }); + await this.counts.applyInscriptions(inscriptionInserts); + if (ENV.BRC20_BLOCK_SCAN_ENABLED) + await this.brc20.insertOperations({ reveals: revealOutputs, pointers }); }); } @@ -584,12 +623,12 @@ export class PgStore extends BasePgStore { }); } - private async rollBackInscriptions(rollbacks: DbRevealInsert[]): Promise { + private async rollBackInscriptions(rollbacks: InscriptionEventData[]): Promise { if (rollbacks.length === 0) return; await this.sqlWriteTransaction(async sql => { // Roll back events in reverse so BRC-20 keeps a sane order. for (const rollback of rollbacks.reverse()) { - if (rollback.inscription) { + if ('inscription' in rollback) { await this.brc20.rollBackInscription({ inscription: rollback.inscription }); await this.counts.rollBackInscription({ inscription: rollback.inscription, @@ -609,7 +648,7 @@ export class PgStore extends BasePgStore { WHERE output = ${rollback.location.output} AND "offset" = ${rollback.location.offset} `; logger.info( - `PgStore rollback transfer for ${rollback.location.genesis_id} at block ${rollback.location.block_height}` + `PgStore rollback transfer for sat ${rollback.location.ordinal_number} at block ${rollback.location.block_height}` ); } } @@ -707,7 +746,7 @@ export class PgStore extends BasePgStore { } private async recalculateCurrentLocationPointerFromLocationRollBack(args: { - location: DbLocationInsert; + location: LocationData; }): Promise { await this.sqlWriteTransaction(async sql => { // Is the location we're rolling back *the* current location? @@ -740,7 +779,7 @@ export class PgStore extends BasePgStore { }); } - private async updateInscriptionRecursions(reveals: DbRevealInsert[]): Promise { + private async updateInscriptionRecursions(reveals: InscriptionEventData[]): Promise { if (reveals.length === 0) return; const inserts: { inscription_id: PgSqlQuery; @@ -748,7 +787,7 @@ export class PgStore extends BasePgStore { ref_inscription_genesis_id: string; }[] = []; for (const i of reveals) - if (i.inscription && i.recursive_refs?.length) { + if ('inscription' in i && i.recursive_refs?.length) { const refSet = new Set(i.recursive_refs); for (const ref of refSet) inserts.push({ diff --git a/src/pg/types.ts b/src/pg/types.ts index a418cfa..d070c91 100644 --- a/src/pg/types.ts +++ b/src/pg/types.ts @@ -1,7 +1,75 @@ -import { PgNumeric, PgBytea } from '@hirosystems/api-toolkit'; +import { PgNumeric, PgBytea, PgSqlQuery } from '@hirosystems/api-toolkit'; import { Order, OrderBy } from '../api/schemas'; import { SatoshiRarity } from '../api/util/ordinal-satoshi'; +/** + * Updates and inserts + */ + +export type InscriptionData = { + genesis_id: string; + number: number; + classic_number: number; + mime_type: string; + content_type: string; + content_length: number; + content: PgBytea; + fee: PgNumeric; + curse_type: string | null; + sat_ordinal: PgNumeric; + sat_rarity: string; + sat_coinbase_height: number; + recursive: boolean; +}; + +export type InscriptionInsert = InscriptionData; + +type AbstractLocationData = { + block_height: number; + block_hash: string; + tx_id: string; + tx_index: number; + address: string | null; + output: string; + offset: PgNumeric | null; + prev_output: string | null; + prev_offset: PgNumeric | null; + value: PgNumeric | null; + transfer_type: DbLocationTransferType; + block_transfer_index: number | null; +}; + +export type RevealLocationData = AbstractLocationData & { genesis_id: string; timestamp: number }; + +export type TransferLocationData = AbstractLocationData & { + ordinal_number: PgNumeric; + timestamp: number; +}; + +export type LocationData = RevealLocationData | TransferLocationData; + +export type LocationInsert = AbstractLocationData & { + timestamp: PgSqlQuery; + genesis_id: string; + inscription_id: PgSqlQuery | string; +}; + +export type InscriptionRevealData = { + inscription: InscriptionData; + recursive_refs: string[]; + location: RevealLocationData; +}; + +export type InscriptionTransferData = { + location: TransferLocationData; +}; + +export type InscriptionEventData = InscriptionRevealData | InscriptionTransferData; + +/** + * Selects + */ + export type DbPaginatedResult = { total: number; results: T[]; @@ -40,23 +108,6 @@ export enum DbLocationTransferType { burnt = 'burnt', } -export type DbLocationInsert = { - genesis_id: string; - block_height: number; - block_hash: string; - tx_id: string; - tx_index: number; - address: string | null; - output: string; - offset: PgNumeric | null; - prev_output: string | null; - prev_offset: PgNumeric | null; - value: PgNumeric | null; - timestamp: number; - transfer_type: DbLocationTransferType; - block_transfer_index: number | null; -}; - export type DbLocation = { id: string; inscription_id: string | null; @@ -134,28 +185,6 @@ export const LOCATIONS_COLUMNS = [ 'timestamp', ]; -export type DbInscriptionInsert = { - genesis_id: string; - number: number; - classic_number: number; - mime_type: string; - content_type: string; - content_length: number; - content: PgBytea; - fee: PgNumeric; - curse_type: string | null; - sat_ordinal: PgNumeric; - sat_rarity: string; - sat_coinbase_height: number; - recursive: boolean; -}; - -export type DbRevealInsert = { - inscription?: DbInscriptionInsert; - recursive_refs?: string[]; - location: DbLocationInsert; -}; - export type DbInscription = { id: string; genesis_id: string; diff --git a/tests/brc20.test.ts b/tests/brc20.test.ts index 2b3302f..bea3bad 100644 --- a/tests/brc20.test.ts +++ b/tests/brc20.test.ts @@ -3,7 +3,7 @@ import { buildApiServer } from '../src/api/init'; import { Brc20ActivityResponse, Brc20TokenResponse } from '../src/api/schemas'; import { brc20FromInscription } from '../src/pg/brc20/helpers'; import { MIGRATIONS_DIR, PgStore } from '../src/pg/pg-store'; -import { DbInscriptionInsert } from '../src/pg/types'; +import { InscriptionData } from '../src/pg/types'; import { TestChainhookPayloadBuilder, TestFastifyServer, @@ -38,6 +38,7 @@ describe('BRC-20', () => { max: '250000', }, number: 0, + ordinal_number: 0, tx_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dc', address: address, }) @@ -63,6 +64,7 @@ describe('BRC-20', () => { amt: '10000', }, number: 1, + ordinal_number: 1, tx_id: '3b55f624eaa4f8de6c42e0c490176b67123a83094384f658611faf7bfb85dd0f', address: address, }) @@ -84,9 +86,9 @@ describe('BRC-20', () => { }); describe('token standard validation', () => { - const testInsert = (json: any): DbInscriptionInsert => { + const testInsert = (json: any): InscriptionData => { const content = Buffer.from(JSON.stringify(json), 'utf-8'); - const insert: DbInscriptionInsert = { + const insert: InscriptionData = { genesis_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dci0', number: 0, classic_number: 0, @@ -114,7 +116,7 @@ describe('BRC-20', () => { }), 'utf-8' ); - const insert: DbInscriptionInsert = { + const insert: InscriptionData = { genesis_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dci0', number: 0, classic_number: 0, @@ -143,7 +145,7 @@ describe('BRC-20', () => { '{"p": "brc-20", "op": "deploy", "tick": "PEPE", "max": "21000000"', 'utf-8' ); - const insert: DbInscriptionInsert = { + const insert: InscriptionData = { genesis_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dci0', number: 0, classic_number: 0, @@ -493,6 +495,7 @@ describe('BRC-20', () => { max: '21000000', }, number: 0, + ordinal_number: 0, tx_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dc', address: 'bc1p3cyx5e2hgh53w7kpxcvm8s4kkega9gv5wfw7c4qxsvxl0u8x834qf0u2td', }) @@ -544,6 +547,7 @@ describe('BRC-20', () => { max: '21000000', }, number: 0, + ordinal_number: 0, tx_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dc', address: 'bc1p3cyx5e2hgh53w7kpxcvm8s4kkega9gv5wfw7c4qxsvxl0u8x834qf0u2td', }) @@ -569,6 +573,7 @@ describe('BRC-20', () => { max: '19000000', }, number: 1, + ordinal_number: 1, tx_id: '3f8067a6e9b45308b5a090c2987feeb2d08cbaf814ef2ffabad7c381b62f5f7e', address: 'bc1p3cyx5e2hgh53w7kpxcvm8s4kkega9gv5wfw7c4qxsvxl0u8x834qf0u2td', }) @@ -620,6 +625,7 @@ describe('BRC-20', () => { max: '21000000', }, number: 0, + ordinal_number: 0, tx_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dc', address: 'bc1p3cyx5e2hgh53w7kpxcvm8s4kkega9gv5wfw7c4qxsvxl0u8x834qf0u2td', }) @@ -645,6 +651,7 @@ describe('BRC-20', () => { max: '19000000', }, number: 1, + ordinal_number: 1, tx_id: '3f8067a6e9b45308b5a090c2987feeb2d08cbaf814ef2ffabad7c381b62f5f7e', address: 'bc1p3cyx5e2hgh53w7kpxcvm8s4kkega9gv5wfw7c4qxsvxl0u8x834qf0u2td', }) @@ -719,6 +726,7 @@ describe('BRC-20', () => { max: '21000000', }, number: 0, + ordinal_number: 0, classic_number: -1, tx_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dc', address: 'bc1p3cyx5e2hgh53w7kpxcvm8s4kkega9gv5wfw7c4qxsvxl0u8x834qf0u2td', @@ -759,6 +767,7 @@ describe('BRC-20', () => { max: '21000000', }, number: 0, + ordinal_number: 0, tx_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dc', address: address, }) @@ -784,6 +793,7 @@ describe('BRC-20', () => { amt: '250000', }, number: 1, + ordinal_number: 1, tx_id: '8aec77f855549d98cb9fb5f35e02a03f9a2354fd05a5f89fc610b32c3b01f99f', address: address, }) @@ -827,6 +837,7 @@ describe('BRC-20', () => { amt: '100000', }, number: 2, + ordinal_number: 2, tx_id: '7a1adbc3e93ddf8d7c4e0ba75aa11c98c431521dd850be8b955feedb716d8bec', address: 'bc1p3cyx5e2hgh53w7kpxcvm8s4kkega9gv5wfw7c4qxsvxl0u8x834qf0u2td', }) @@ -885,6 +896,7 @@ describe('BRC-20', () => { max: '21000000', }, number: 0, + ordinal_number: 0, tx_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dc', address: address, }) @@ -910,6 +922,7 @@ describe('BRC-20', () => { amt: '250000', }, number: 1, + ordinal_number: 1, tx_id: '8aec77f855549d98cb9fb5f35e02a03f9a2354fd05a5f89fc610b32c3b01f99f', address: address, }) @@ -936,6 +949,7 @@ describe('BRC-20', () => { amt: '250000', }, number: 1, + ordinal_number: 1, tx_id: '8aec77f855549d98cb9fb5f35e02a03f9a2354fd05a5f89fc610b32c3b01f99f', address: address, }) @@ -981,6 +995,7 @@ describe('BRC-20', () => { dec: '1', }, number: 0, + ordinal_number: 0, tx_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dc', address: address, }) @@ -1006,6 +1021,7 @@ describe('BRC-20', () => { amt: '250000.000', // Invalid decimal count }, number: 1, + ordinal_number: 1, tx_id: '8aec77f855549d98cb9fb5f35e02a03f9a2354fd05a5f89fc610b32c3b01f99f', address: address, }) @@ -1045,6 +1061,7 @@ describe('BRC-20', () => { dec: '1', }, number: 0, + ordinal_number: 0, tx_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dc', address: address, }) @@ -1070,6 +1087,7 @@ describe('BRC-20', () => { amt: '1000', }, number: 1, + ordinal_number: 1, tx_id: '3b55f624eaa4f8de6c42e0c490176b67123a83094384f658611faf7bfb85dd0f', address: address, }) @@ -1086,6 +1104,7 @@ describe('BRC-20', () => { amt: '1000', }, number: 2, + ordinal_number: 2, tx_id: '7e09bda2cba34bca648cca6d79a074940d39b6137150d3a3edcf80c0e01419a5', address: address, }) @@ -1102,6 +1121,7 @@ describe('BRC-20', () => { amt: '5000000000', // Exceeds supply }, number: 3, + ordinal_number: 3, tx_id: '8aec77f855549d98cb9fb5f35e02a03f9a2354fd05a5f89fc610b32c3b01f99f', address: address, }) @@ -1145,6 +1165,7 @@ describe('BRC-20', () => { amt: '1000', }, number: 4, + ordinal_number: 4, tx_id: 'bf7a3e1a0647ca88f6539119b2defaec302683704ea270b3302e709597643548', address: address, }) @@ -1182,6 +1203,7 @@ describe('BRC-20', () => { amt: '1000', }, number: 0, + ordinal_number: 0, tx_id: '3b55f624eaa4f8de6c42e0c490176b67123a83094384f658611faf7bfb85dd0f', address: address, }) @@ -1222,6 +1244,7 @@ describe('BRC-20', () => { lim: '100', }, number: 0, + ordinal_number: 0, tx_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dc', address: address, }) @@ -1247,6 +1270,7 @@ describe('BRC-20', () => { amt: '1000', // Greater than limit }, number: 1, + ordinal_number: 1, tx_id: '3b55f624eaa4f8de6c42e0c490176b67123a83094384f658611faf7bfb85dd0f', address: address, }) @@ -1288,6 +1312,7 @@ describe('BRC-20', () => { amt: '2000', }, number: 2, + ordinal_number: 2, tx_id: 'eee52b22397ea4a4aefe6a39931315e93a157091f5a994216c0aa9c8c6fef47a', address: address, }) @@ -1342,6 +1367,7 @@ describe('BRC-20', () => { amt: '2000', }, number: 2, + ordinal_number: 2, tx_id: 'eee52b22397ea4a4aefe6a39931315e93a157091f5a994216c0aa9c8c6fef47a', address: address, }) @@ -1388,6 +1414,7 @@ describe('BRC-20', () => { amt: '5000000000', // More than was minted }, number: 2, + ordinal_number: 2, tx_id: 'eee52b22397ea4a4aefe6a39931315e93a157091f5a994216c0aa9c8c6fef47a', address: address, }) @@ -1434,6 +1461,7 @@ describe('BRC-20', () => { amt: '9000', }, number: 2, + ordinal_number: 2, tx_id: 'eee52b22397ea4a4aefe6a39931315e93a157091f5a994216c0aa9c8c6fef47a', address: address, }) @@ -1450,6 +1478,7 @@ describe('BRC-20', () => { amt: '2000', // Will exceed available balance }, number: 3, + ordinal_number: 3, tx_id: '7edaa48337a94da327b6262830505f116775a32db5ad4ad46e87ecea33f21bac', address: address, }) @@ -1497,6 +1526,7 @@ describe('BRC-20', () => { amt: '9000', }, number: 2, + ordinal_number: 2, tx_id: 'eee52b22397ea4a4aefe6a39931315e93a157091f5a994216c0aa9c8c6fef47a', address: address, }) @@ -1514,7 +1544,7 @@ describe('BRC-20', () => { hash: '7edaa48337a94da327b6262830505f116775a32db5ad4ad46e87ecea33f21bac', }) .inscriptionTransferred({ - inscription_id: 'eee52b22397ea4a4aefe6a39931315e93a157091f5a994216c0aa9c8c6fef47ai0', + ordinal_number: 2, destination: { type: 'transferred', value: address2 }, satpoint_pre_transfer: 'eee52b22397ea4a4aefe6a39931315e93a157091f5a994216c0aa9c8c6fef47a:0:0', @@ -1595,6 +1625,7 @@ describe('BRC-20', () => { amt: '9000', }, number: 2, + ordinal_number: 2, tx_id: 'eee52b22397ea4a4aefe6a39931315e93a157091f5a994216c0aa9c8c6fef47a', address: address, }) @@ -1612,7 +1643,7 @@ describe('BRC-20', () => { hash: '7edaa48337a94da327b6262830505f116775a32db5ad4ad46e87ecea33f21bac', }) .inscriptionTransferred({ - inscription_id: 'eee52b22397ea4a4aefe6a39931315e93a157091f5a994216c0aa9c8c6fef47ai0', + ordinal_number: 2, destination: { type: 'spent_in_fees', value: '' }, satpoint_pre_transfer: 'eee52b22397ea4a4aefe6a39931315e93a157091f5a994216c0aa9c8c6fef47a:0:0', @@ -1663,6 +1694,7 @@ describe('BRC-20', () => { amt: '9000', }, number: 2, + ordinal_number: 2, tx_id: 'eee52b22397ea4a4aefe6a39931315e93a157091f5a994216c0aa9c8c6fef47a', address: address, }) @@ -1680,7 +1712,7 @@ describe('BRC-20', () => { hash: '7edaa48337a94da327b6262830505f116775a32db5ad4ad46e87ecea33f21bac', }) .inscriptionTransferred({ - inscription_id: 'eee52b22397ea4a4aefe6a39931315e93a157091f5a994216c0aa9c8c6fef47ai0', + ordinal_number: 2, destination: { type: 'burnt' }, satpoint_pre_transfer: 'eee52b22397ea4a4aefe6a39931315e93a157091f5a994216c0aa9c8c6fef47a:0:0', @@ -1732,6 +1764,7 @@ describe('BRC-20', () => { amt: '9000', }, number: 2, + ordinal_number: 2, tx_id: 'eee52b22397ea4a4aefe6a39931315e93a157091f5a994216c0aa9c8c6fef47a', address: address, }) @@ -1749,7 +1782,7 @@ describe('BRC-20', () => { hash: '7edaa48337a94da327b6262830505f116775a32db5ad4ad46e87ecea33f21bac', }) .inscriptionTransferred({ - inscription_id: 'eee52b22397ea4a4aefe6a39931315e93a157091f5a994216c0aa9c8c6fef47ai0', + ordinal_number: 2, destination: { type: 'transferred', value: address2 }, satpoint_pre_transfer: 'eee52b22397ea4a4aefe6a39931315e93a157091f5a994216c0aa9c8c6fef47a:0:0', @@ -1773,7 +1806,7 @@ describe('BRC-20', () => { hash: '55bec906eadc9f5c120cc39555ba46e85e562eacd6217e4dd0b8552783286d0e', }) .inscriptionTransferred({ - inscription_id: 'eee52b22397ea4a4aefe6a39931315e93a157091f5a994216c0aa9c8c6fef47ai0', + ordinal_number: 2, destination: { type: 'transferred', value: address }, satpoint_pre_transfer: '7edaa48337a94da327b6262830505f116775a32db5ad4ad46e87ecea33f21bac:0:0', @@ -1842,6 +1875,7 @@ describe('BRC-20', () => { amt: '20', }, number: 2, + ordinal_number: 2, tx_id: '825a25b64b5d99ca30e04e53cc9a3020412e1054eb2a7523eb075ddd6d983205', address: address, }) @@ -1859,7 +1893,7 @@ describe('BRC-20', () => { hash: '486815e61723d03af344e1256d7e0c028a8e9e71eb38157f4bf069eb94292ee1', }) .inscriptionTransferred({ - inscription_id: '825a25b64b5d99ca30e04e53cc9a3020412e1054eb2a7523eb075ddd6d983205i0', + ordinal_number: 2, destination: { type: 'transferred', value: address2 }, satpoint_pre_transfer: '825a25b64b5d99ca30e04e53cc9a3020412e1054eb2a7523eb075ddd6d983205:0:0', @@ -1901,6 +1935,7 @@ describe('BRC-20', () => { amt: '20', }, number: 3, + ordinal_number: 3, tx_id: '09a812f72275892b4858880cf3821004a6e8885817159b340639afe9952ac053', address: address2, }) @@ -1930,7 +1965,7 @@ describe('BRC-20', () => { hash: '26c0c3acbb1c87e682ade86220ba06e649d7599ecfc49a71495f1bdd04efbbb4', }) .inscriptionTransferred({ - inscription_id: '09a812f72275892b4858880cf3821004a6e8885817159b340639afe9952ac053i0', + ordinal_number: 3, destination: { type: 'transferred', value: address2 }, satpoint_pre_transfer: '486815e61723d03af344e1256d7e0c028a8e9e71eb38157f4bf069eb94292ee1:0:0', @@ -1975,6 +2010,7 @@ describe('BRC-20', () => { max: '21000000', }, number: 0, + ordinal_number: 0, tx_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dc', address: 'bc1p3cyx5e2hgh53w7kpxcvm8s4kkega9gv5wfw7c4qxsvxl0u8x834qf0u2td', }) @@ -2014,6 +2050,7 @@ describe('BRC-20', () => { const blockHeights = incrementing(BRC20_GENESIS_BLOCK); let transferHash = randomHash(); + let number = inscriptionNumbers.next().value; await db.updateInscriptions( new TestChainhookPayloadBuilder() .apply() @@ -2027,7 +2064,8 @@ describe('BRC-20', () => { tick: 'PEPE', max: '21000000', }, - number: inscriptionNumbers.next().value, + number: number, + ordinal_number: number, tx_id: transferHash, address: 'bc1p3cyx5e2hgh53w7kpxcvm8s4kkega9gv5wfw7c4qxsvxl0u8x834qf0u2td', }) @@ -2036,6 +2074,7 @@ describe('BRC-20', () => { ); transferHash = randomHash(); + number = inscriptionNumbers.next().value; await db.updateInscriptions( new TestChainhookPayloadBuilder() .apply() @@ -2049,7 +2088,8 @@ describe('BRC-20', () => { tick: 'PEER', max: '21000000', }, - number: inscriptionNumbers.next().value, + number: number, + ordinal_number: number, tx_id: transferHash, address: 'bc1p3cyx5e2hgh53w7kpxcvm8s4kkega9gv5wfw7c4qxsvxl0u8x834qf0u2td', }) @@ -2058,6 +2098,7 @@ describe('BRC-20', () => { ); transferHash = randomHash(); + number = inscriptionNumbers.next().value; await db.updateInscriptions( new TestChainhookPayloadBuilder() .apply() @@ -2071,7 +2112,8 @@ describe('BRC-20', () => { tick: 'ABCD', max: '21000000', }, - number: inscriptionNumbers.next().value, + number: number, + ordinal_number: number, tx_id: transferHash, address: 'bc1p3cyx5e2hgh53w7kpxcvm8s4kkega9gv5wfw7c4qxsvxl0u8x834qf0u2td', }) @@ -2080,6 +2122,7 @@ describe('BRC-20', () => { ); transferHash = randomHash(); + number = inscriptionNumbers.next().value; await db.updateInscriptions( new TestChainhookPayloadBuilder() .apply() @@ -2093,7 +2136,8 @@ describe('BRC-20', () => { tick: 'DCBA', max: '21000000', }, - number: inscriptionNumbers.next().value, + number: number, + ordinal_number: number, tx_id: transferHash, address: 'bc1p3cyx5e2hgh53w7kpxcvm8s4kkega9gv5wfw7c4qxsvxl0u8x834qf0u2td', }) @@ -2124,6 +2168,7 @@ describe('BRC-20', () => { const addressB = 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4'; // A deploys PEPE + let number = inscriptionNumbers.next().value; await db.updateInscriptions( new TestChainhookPayloadBuilder() .apply() @@ -2137,7 +2182,8 @@ describe('BRC-20', () => { tick: 'PEPE', max: '21000000', }, - number: inscriptionNumbers.next().value, + number: number, + ordinal_number: number, tx_id: randomHash(), address: addressA, }) @@ -2149,6 +2195,7 @@ describe('BRC-20', () => { const pepeMints = []; for (let i = 0; i < 10; i++) { const txHash = randomHash(); + number = inscriptionNumbers.next().value; const payload = new TestChainhookPayloadBuilder() .apply() .block({ height: blockHeights.next().value }) @@ -2161,7 +2208,8 @@ describe('BRC-20', () => { tick: 'PEPE', amt: '10000', }, - number: inscriptionNumbers.next().value, + number: number, + ordinal_number: number, tx_id: txHash, address: addressA, }) @@ -2172,6 +2220,7 @@ describe('BRC-20', () => { } // B deploys ABCD + number = inscriptionNumbers.next().value; await db.updateInscriptions( new TestChainhookPayloadBuilder() .apply() @@ -2185,7 +2234,8 @@ describe('BRC-20', () => { tick: 'ABCD', max: '21000000', }, - number: inscriptionNumbers.next().value, + number: number, + ordinal_number: number, tx_id: randomHash(), address: addressB, }) @@ -2194,6 +2244,7 @@ describe('BRC-20', () => { ); // B mints 10000 ABCD + number = inscriptionNumbers.next().value; await db.updateInscriptions( new TestChainhookPayloadBuilder() .apply() @@ -2207,7 +2258,8 @@ describe('BRC-20', () => { tick: 'ABCD', amt: '10000', }, - number: inscriptionNumbers.next().value, + number, + ordinal_number: number, tx_id: randomHash(), address: addressB, }) @@ -2218,6 +2270,7 @@ describe('BRC-20', () => { // B send 1000 ABCD to A // (create inscription, transfer) const txHashTransfer = randomHash(); + number = inscriptionNumbers.next().value; const payloadTransfer = new TestChainhookPayloadBuilder() .apply() .block({ height: blockHeights.next().value }) @@ -2230,7 +2283,8 @@ describe('BRC-20', () => { tick: 'ABCD', amt: '1000', }, - number: inscriptionNumbers.next().value, + number, + ordinal_number: number, tx_id: txHashTransfer, address: addressB, }) @@ -2244,7 +2298,7 @@ describe('BRC-20', () => { .block({ height: blockHeights.next().value }) .transaction({ hash: txHashTransferSend }) .inscriptionTransferred({ - inscription_id: `${txHashTransfer}i0`, + ordinal_number: number, destination: { type: 'transferred', value: addressA }, satpoint_pre_transfer: `${txHashTransfer}:0:0`, satpoint_post_transfer: `${txHashTransferSend}:0:0`, @@ -2365,6 +2419,7 @@ describe('BRC-20', () => { const addressB = 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4'; // A deploys PEPE + let number = inscriptionNumbers.next().value; await db.updateInscriptions( new TestChainhookPayloadBuilder() .apply() @@ -2378,7 +2433,8 @@ describe('BRC-20', () => { tick: 'PEPE', max: '21000000', }, - number: inscriptionNumbers.next().value, + number, + ordinal_number: number, tx_id: randomHash(), address: addressA, }) @@ -2408,6 +2464,7 @@ describe('BRC-20', () => { ); // A mints 10000 PEPE + number = inscriptionNumbers.next().value; await db.updateInscriptions( new TestChainhookPayloadBuilder() .apply() @@ -2421,7 +2478,8 @@ describe('BRC-20', () => { tick: 'PEPE', amt: '10000', }, - number: inscriptionNumbers.next().value, + number, + ordinal_number: number, tx_id: randomHash(), address: addressA, }) @@ -2454,6 +2512,7 @@ describe('BRC-20', () => { ); // B mints 10000 PEPE + number = inscriptionNumbers.next().value; await db.updateInscriptions( new TestChainhookPayloadBuilder() .apply() @@ -2467,7 +2526,8 @@ describe('BRC-20', () => { tick: 'PEPE', amt: '10000', }, - number: inscriptionNumbers.next().value, + number, + ordinal_number: number, tx_id: randomHash(), address: addressB, }) @@ -2497,6 +2557,7 @@ describe('BRC-20', () => { // A creates transfer of 9000 PEPE const transferHash = randomHash(); + number = inscriptionNumbers.next().value; await db.updateInscriptions( new TestChainhookPayloadBuilder() .apply() @@ -2510,7 +2571,8 @@ describe('BRC-20', () => { tick: 'PEPE', amt: '9000', }, - number: inscriptionNumbers.next().value, + number, + ordinal_number: number, tx_id: transferHash, address: addressA, }) @@ -2549,7 +2611,7 @@ describe('BRC-20', () => { .inscriptionTransferred({ destination: { type: 'transferred', value: addressB }, tx_index: 0, - inscription_id: `${transferHash}i0`, + ordinal_number: number, post_transfer_output_value: null, satpoint_pre_transfer: `${transferHash}:0:0`, satpoint_post_transfer: @@ -2622,6 +2684,7 @@ describe('BRC-20', () => { const addressC = 'bc1q9d80h0q5d3f54w7w8c3l2sguf9uset4ydw9xj2'; // Step 1: A deploys a token + let number = inscriptionNumbers.next().value; await db.updateInscriptions( new TestChainhookPayloadBuilder() .apply() @@ -2635,7 +2698,8 @@ describe('BRC-20', () => { tick: 'PEPE', max: '21000000', }, - number: inscriptionNumbers.next().value, + number, + ordinal_number: number, tx_id: randomHash(), address: addressA, }) @@ -2665,6 +2729,7 @@ describe('BRC-20', () => { ); // Step 2: A mints 1000 of the token + number = inscriptionNumbers.next().value; await db.updateInscriptions( new TestChainhookPayloadBuilder() .apply() @@ -2678,7 +2743,8 @@ describe('BRC-20', () => { tick: 'PEPE', amt: '1000', }, - number: inscriptionNumbers.next().value, + number, + ordinal_number: number, tx_id: randomHash(), address: addressA, }) @@ -2735,6 +2801,7 @@ describe('BRC-20', () => { ); // Step 3: B mints 2000 of the token + number = inscriptionNumbers.next().value; await db.updateInscriptions( new TestChainhookPayloadBuilder() .apply() @@ -2748,7 +2815,8 @@ describe('BRC-20', () => { tick: 'PEPE', amt: '2000', }, - number: inscriptionNumbers.next().value, + number, + ordinal_number: number, tx_id: randomHash(), address: addressB, }) @@ -2779,6 +2847,7 @@ describe('BRC-20', () => { // Step 4: A creates a transfer to B const transferHashAB = randomHash(); + const numberAB = inscriptionNumbers.next().value; await db.updateInscriptions( new TestChainhookPayloadBuilder() .apply() @@ -2792,7 +2861,8 @@ describe('BRC-20', () => { tick: 'PEPE', amt: '1000', }, - number: inscriptionNumbers.next().value, + number: numberAB, + ordinal_number: numberAB, tx_id: transferHashAB, address: addressA, }) @@ -2846,6 +2916,7 @@ describe('BRC-20', () => { // Step 5: B creates a transfer to C const transferHashBC = randomHash(); + const numberBC = inscriptionNumbers.next().value; await db.updateInscriptions( new TestChainhookPayloadBuilder() .apply() @@ -2859,7 +2930,8 @@ describe('BRC-20', () => { tick: 'PEPE', amt: '2000', }, - number: inscriptionNumbers.next().value, + number: numberBC, + ordinal_number: numberBC, tx_id: transferHashBC, address: addressB, }) @@ -2900,7 +2972,7 @@ describe('BRC-20', () => { .inscriptionTransferred({ destination: { type: 'transferred', value: addressB }, tx_index: 0, - inscription_id: `${transferHashAB}i0`, + ordinal_number: numberAB, post_transfer_output_value: null, satpoint_pre_transfer: `${transferHashAB}:0:0`, satpoint_post_transfer: `${transferHashABSend}:0:0`, @@ -2988,7 +3060,7 @@ describe('BRC-20', () => { .inscriptionTransferred({ destination: { type: 'transferred', value: addressC }, tx_index: 0, - inscription_id: `${transferHashBC}i0`, + ordinal_number: numberBC, post_transfer_output_value: null, satpoint_pre_transfer: `${transferHashBC}:0:0`, satpoint_post_transfer: `${transferHashBCSend}:0:0`, @@ -3073,6 +3145,7 @@ describe('BRC-20', () => { const addressA = 'bc1q6uwuet65rm6xvlz7ztw2gvdmmay5uaycu03mqz'; // Step 1: Create a token PEPE + let number = inscriptionNumbers.next().value; await db.updateInscriptions( new TestChainhookPayloadBuilder() .apply() @@ -3086,7 +3159,8 @@ describe('BRC-20', () => { tick: 'PEPE', max: '21000000', }, - number: inscriptionNumbers.next().value, + number, + ordinal_number: number, tx_id: randomHash(), address: addressA, }) @@ -3116,6 +3190,7 @@ describe('BRC-20', () => { ); // Step 2: Create a token PEER + number = inscriptionNumbers.next().value; await db.updateInscriptions( new TestChainhookPayloadBuilder() .apply() @@ -3129,7 +3204,8 @@ describe('BRC-20', () => { tick: 'PEER', max: '21000000', }, - number: inscriptionNumbers.next().value, + number, + ordinal_number: number, tx_id: randomHash(), address: addressA, }) @@ -3222,6 +3298,7 @@ describe('BRC-20', () => { amt: '2000', }, number: 2, + ordinal_number: 2, tx_id: '633648e0e1ddcab8dea0496a561f2b08c486ae619b5634d7bb55d7f0cd32ef16', address: 'bc1qp9jgp9qtlhgvwjnxclj6kav6nr2fq09c206pyl', }) @@ -3268,6 +3345,7 @@ describe('BRC-20', () => { max: '250000', }, number: 0, + ordinal_number: 0, tx_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dc', address: 'bc1qp9jgp9qtlhgvwjnxclj6kav6nr2fq09c206pyl', }) @@ -3319,6 +3397,7 @@ describe('BRC-20', () => { amt: '9000', }, number: 2, + ordinal_number: 2, tx_id: 'eee52b22397ea4a4aefe6a39931315e93a157091f5a994216c0aa9c8c6fef47a', address: address, }) @@ -3335,7 +3414,7 @@ describe('BRC-20', () => { hash: '7edaa48337a94da327b6262830505f116775a32db5ad4ad46e87ecea33f21bac', }) .inscriptionTransferred({ - inscription_id: 'eee52b22397ea4a4aefe6a39931315e93a157091f5a994216c0aa9c8c6fef47ai0', + ordinal_number: 2, destination: { type: 'transferred', value: address2 }, satpoint_pre_transfer: 'eee52b22397ea4a4aefe6a39931315e93a157091f5a994216c0aa9c8c6fef47a:0:0', @@ -3365,6 +3444,7 @@ describe('BRC-20', () => { max: '1000', }, number: 3, + ordinal_number: 3, tx_id: '8354e85e87fa2df8b3a06ec0b9d395559b95174530cb19447fc4df5f6d4ca84d', address: address, }) @@ -3389,6 +3469,7 @@ describe('BRC-20', () => { amt: '500', }, number: 4, + ordinal_number: 4, tx_id: '81f4ee2c247c5f5c0d3a6753fef706df410ea61c2aa6d370003b98beb041b887', address: address, }) @@ -3414,6 +3495,7 @@ describe('BRC-20', () => { amt: '100', }, number: 5, + ordinal_number: 5, tx_id: 'c1c7f1d5c10a30605a8a5285ca3465a4f75758ed9b7f201e5ef62727e179966f', address: address, }) @@ -3430,7 +3512,7 @@ describe('BRC-20', () => { hash: 'a00d01a3e772ce2219ddf3fe2fe4053be071262d9594f11f018fdada7179ae2d', }) .inscriptionTransferred({ - inscription_id: 'c1c7f1d5c10a30605a8a5285ca3465a4f75758ed9b7f201e5ef62727e179966fi0', + ordinal_number: 5, destination: { type: 'transferred', value: address }, // To self satpoint_pre_transfer: 'c1c7f1d5c10a30605a8a5285ca3465a4f75758ed9b7f201e5ef62727e179966f:0:0', diff --git a/tests/cache.test.ts b/tests/cache.test.ts index a8a7803..fa5a9f9 100644 --- a/tests/cache.test.ts +++ b/tests/cache.test.ts @@ -89,7 +89,7 @@ describe('ETag cache', () => { hash: '0xbdda0d240132bab2af7f797d1507beb1acab6ad43e2c0ef7f96291aea5cc3444', }) .inscriptionTransferred({ - inscription_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dci0', + ordinal_number: 257418248345364, destination: { type: 'transferred', value: 'bc1p3xqwzmddceqrd6x9yxplqzkl5vucta2gqm5szpkmpuvcvgs7g8psjf8htd', @@ -126,7 +126,7 @@ describe('ETag cache', () => { hash: 'bebb1357c97d2348eb8ef24e1d8639ff79c8847bf12999ca7fef463489b40f0f', }) .inscriptionTransferred({ - inscription_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dci0', + ordinal_number: 257418248345364, destination: { type: 'transferred', value: 'bc1p3xqwzmddceqrd6x9yxplqzkl5vucta2gqm5szpkmpuvcvgs7g8psjf8htd', @@ -235,7 +235,7 @@ describe('ETag cache', () => { .block({ height: 778577 }) .transaction({ hash: 'ae9d273a10e899f0d2cad47ee2b0e77ab8a9addd9dd5bb5e4b03d6971c060d52' }) .inscriptionTransferred({ - inscription_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dci0', + ordinal_number: 257418248345364, destination: { type: 'transferred', value: 'bc1p3cyx5e2hgh53w7kpxcvm8s4kkega9gv5wfw7c4qxsvxl0u8x834qf0u2td', diff --git a/tests/helpers.ts b/tests/helpers.ts index 569b6ed..e1f0087 100644 --- a/tests/helpers.ts +++ b/tests/helpers.ts @@ -114,6 +114,7 @@ export function brc20Reveal(args: { classic_number?: number; address: string; tx_id: string; + ordinal_number: number; }): BitcoinInscriptionRevealed { const content = Buffer.from(JSON.stringify(args.json), 'utf-8'); const reveal: BitcoinInscriptionRevealed = { @@ -128,7 +129,7 @@ export function brc20Reveal(args: { inscription_id: `${args.tx_id}i0`, inscription_output_value: 10000, inscriber_address: args.address, - ordinal_number: 0, + ordinal_number: args.ordinal_number, ordinal_block_height: 0, ordinal_offset: 0, satpoint_post_inscription: `${args.tx_id}:0:0`, diff --git a/tests/inscriptions.test.ts b/tests/inscriptions.test.ts index e89a8b3..5b6d6e1 100644 --- a/tests/inscriptions.test.ts +++ b/tests/inscriptions.test.ts @@ -470,7 +470,7 @@ describe('/inscriptions', () => { hash: '0xbdda0d240132bab2af7f797d1507beb1acab6ad43e2c0ef7f96291aea5cc3444', }) .inscriptionTransferred({ - inscription_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dci0', + ordinal_number: 257418248345364, destination: { type: 'transferred', value: 'bc1p3xqwzmddceqrd6x9yxplqzkl5vucta2gqm5szpkmpuvcvgs7g8psjf8htd', @@ -525,7 +525,7 @@ describe('/inscriptions', () => { hash: '0xe3af144354367de58c675e987febcb49f17d6c19e645728b833fe95408feab85', }) .inscriptionTransferred({ - inscription_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dci0', + ordinal_number: 257418248345364, destination: { type: 'transferred', value: 'bc1pkjq7cerr6h53qm86k9t3dq0gqg8lcfz5jx7z4aj2mpqrjggrnass0u7qqj', @@ -616,7 +616,7 @@ describe('/inscriptions', () => { }) // Transfer 1 .inscriptionTransferred({ - inscription_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dci0', + ordinal_number: 257418248345364, destination: { type: 'transferred', value: 'bc1qv7d2dgyvtctv7ya4t3ysy4c2s8qz4nm8t6dvm3', @@ -630,7 +630,7 @@ describe('/inscriptions', () => { }) // Transfer 2 .inscriptionTransferred({ - inscription_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dci0', + ordinal_number: 257418248345364, destination: { type: 'transferred', value: 'bc1p3xqwzmddceqrd6x9yxplqzkl5vucta2gqm5szpkmpuvcvgs7g8psjf8htd', @@ -720,7 +720,7 @@ describe('/inscriptions', () => { hash: '0xbdda0d240132bab2af7f797d1507beb1acab6ad43e2c0ef7f96291aea5cc3444', }) .inscriptionTransferred({ - inscription_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dci0', + ordinal_number: 257418248345364, destination: { type: 'transferred', value: 'bc1p3xqwzmddceqrd6x9yxplqzkl5vucta2gqm5szpkmpuvcvgs7g8psjf8htd', @@ -775,7 +775,7 @@ describe('/inscriptions', () => { hash: '0xe3af144354367de58c675e987febcb49f17d6c19e645728b833fe95408feab85', }) .inscriptionTransferred({ - inscription_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dci0', + ordinal_number: 257418248345364, destination: { type: 'transferred', value: 'bc1pkjq7cerr6h53qm86k9t3dq0gqg8lcfz5jx7z4aj2mpqrjggrnass0u7qqj', @@ -871,7 +871,7 @@ describe('/inscriptions', () => { hash: '0xbdda0d240132bab2af7f797d1507beb1acab6ad43e2c0ef7f96291aea5cc3444', }) .inscriptionTransferred({ - inscription_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dci0', + ordinal_number: 257418248345364, destination: { type: 'transferred', value: 'bc1p3xqwzmddceqrd6x9yxplqzkl5vucta2gqm5szpkmpuvcvgs7g8psjf8htd', @@ -930,7 +930,7 @@ describe('/inscriptions', () => { hash: '0xe3af144354367de58c675e987febcb49f17d6c19e645728b833fe95408feab85', }) .inscriptionTransferred({ - inscription_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dci0', + ordinal_number: 257418248345364, destination: { type: 'transferred', value: 'bc1pkjq7cerr6h53qm86k9t3dq0gqg8lcfz5jx7z4aj2mpqrjggrnass0u7qqj', @@ -1031,7 +1031,7 @@ describe('/inscriptions', () => { inscription_id: '7ac73ecd01b9da4a7eab904655416dbfe8e03f193e091761b5a63ad0963570cdi0', inscription_output_value: 10000, inscriber_address: 'bc1ptrehxtus25xx8jp5pchljxg2aps7mdemc4264zzzsdcvs6q25hhsf3rrph', - ordinal_number: 257418248345364, + ordinal_number: 257418248340000, ordinal_block_height: 650000, ordinal_offset: 0, satpoint_post_inscription: @@ -1067,7 +1067,7 @@ describe('/inscriptions', () => { hash: '0xbdda0d240132bab2af7f797d1507beb1acab6ad43e2c0ef7f96291aea5cc3444', }) .inscriptionTransferred({ - inscription_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dci0', + ordinal_number: 257418248345364, destination: { type: 'transferred', value: 'bc1p3xqwzmddceqrd6x9yxplqzkl5vucta2gqm5szpkmpuvcvgs7g8psjf8htd', @@ -1083,7 +1083,7 @@ describe('/inscriptions', () => { hash: 'abe7deebd0c6bacc9b1ddd234f9442db0530180448e934f34b9cbf3d7e6d91cb', }) .inscriptionTransferred({ - inscription_id: '7ac73ecd01b9da4a7eab904655416dbfe8e03f193e091761b5a63ad0963570cdi0', + ordinal_number: 257418248340000, destination: { type: 'transferred', value: 'bc1p3xqwzmddceqrd6x9yxplqzkl5vucta2gqm5szpkmpuvcvgs7g8psjf8htd', @@ -1172,7 +1172,7 @@ describe('/inscriptions', () => { hash: '5cabafe04aaf98b1f325b0c3ffcbff904dbdb6f3d2e9e451102fda36f1056b5e', }) .inscriptionTransferred({ - inscription_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dci0', + ordinal_number: 257418248345364, destination: { type: 'transferred', value: 'bc1pkx5me775s748lzchytzdsw4f0lq04wssxnyk27g8fn3gee8zhjjqsn9tfp', @@ -1186,7 +1186,7 @@ describe('/inscriptions', () => { }) // Transfer for same inscription in same block .inscriptionTransferred({ - inscription_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dci0', + ordinal_number: 257418248345364, destination: { type: 'transferred', value: 'bc1pkx5me775s748lzchytzdsw4f0lq04wssxnyk27g8fn3gee8zhjjqsn9tfp', @@ -2634,7 +2634,7 @@ describe('/inscriptions', () => { // Transfers affect result totals correctly const transfer2: BitcoinInscriptionTransferred = { - inscription_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dci0', + ordinal_number: 1000000000000, destination: { type: 'transferred', value: 'bc1pscktlmn99gyzlvymvrezh6vwd0l4kg06tg5rvssw0czg8873gz5sdkteqj', diff --git a/tests/server.test.ts b/tests/server.test.ts index 5f00ffd..878c5e9 100644 --- a/tests/server.test.ts +++ b/tests/server.test.ts @@ -175,7 +175,7 @@ describe('EventServer', () => { await expect(db.getChainTipBlockHeight()).resolves.toBe(775617); const transfer: BitcoinInscriptionTransferred = { - inscription_id: '38c46a8bf7ec90bc7f6b797e7dc84baa97f4e5fd4286b92fe1b50176d03b18dci0', + ordinal_number: 5, destination: { type: 'transferred', value: 'bc1p3cyx5e2hgh53w7kpxcvm8s4kkega9gv5wfw7c4qxsvxl0u8x834qf00000', @@ -311,7 +311,7 @@ describe('EventServer', () => { hash: '7edaa48337a94da327b6262830505f116775a32db5ad4ad46e87ecea33f21bac', }) .inscriptionTransferred({ - inscription_id: '6046f17804eb8396285567a20c09598ae1273b6f744b23700ba95593c380ce02i0', + ordinal_number: 5, destination: { type: 'transferred', value: address2 }, satpoint_pre_transfer: '6046f17804eb8396285567a20c09598ae1273b6f744b23700ba95593c380ce02:0:0',