From 66ba744f96a101c6d0e23122ae7aa41fb1933ba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20C=C3=A1rdenas?= Date: Mon, 4 Mar 2024 11:59:29 -0600 Subject: [PATCH] fix: transfers for inscriptions created immediately before (#320) * fix: transfers for inscriptions created immediately before * chore: comment style --- src/pg/pg-store.ts | 45 ++++++++++-------- tests/ordhook/server.test.ts | 90 ++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 20 deletions(-) diff --git a/src/pg/pg-store.ts b/src/pg/pg-store.ts index c392ead9..5f643294 100644 --- a/src/pg/pg-store.ts +++ b/src/pg/pg-store.ts @@ -493,14 +493,32 @@ export class PgStore extends BasePgStore { ): Promise { if (reveals.length === 0) return; await this.sqlWriteTransaction(async sql => { + // 1. Write inscription reveals const inscriptionInserts: InscriptionInsert[] = []; + for (const r of reveals) if ('inscription' in r) inscriptionInserts.push(r.inscription); + if (inscriptionInserts.length) + await sql` + 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, + content_type = EXCLUDED.content_type, + content_length = EXCLUDED.content_length, + content = EXCLUDED.content, + fee = EXCLUDED.fee, + sat_ordinal = EXCLUDED.sat_ordinal, + sat_rarity = EXCLUDED.sat_rarity, + sat_coinbase_height = EXCLUDED.sat_coinbase_height, + updated_at = NOW() + `; + + // 2. Write locations and transfers const locationInserts: LocationInsert[] = []; const revealOutputs: InscriptionEventData[] = []; const transferredOrdinalNumbersSet = new Set(); for (const r of reveals) 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})`, @@ -535,22 +553,6 @@ export class PgStore extends BasePgStore { }); } } - const transferredOrdinalNumbers = [...transferredOrdinalNumbersSet]; - if (inscriptionInserts.length) - await sql` - 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, - content_type = EXCLUDED.content_type, - content_length = EXCLUDED.content_length, - content = EXCLUDED.content, - fee = EXCLUDED.fee, - sat_ordinal = EXCLUDED.sat_ordinal, - sat_rarity = EXCLUDED.sat_rarity, - sat_coinbase_height = EXCLUDED.sat_coinbase_height, - updated_at = NOW() - `; const pointers: DbLocationPointerInsert[] = []; for (const batch of batchIterate(locationInserts, INSERT_BATCH_SIZE)) { const pointerBatch = await sql` @@ -569,13 +571,13 @@ export class PgStore extends BasePgStore { await this.updateInscriptionLocationPointers(pointerBatch); pointers.push(...pointerBatch); } - await this.updateInscriptionRecursions(reveals); - if (streamed && transferredOrdinalNumbers.length) + if (streamed && transferredOrdinalNumbersSet.size) await sql` UPDATE inscriptions SET updated_at = NOW() - WHERE sat_ordinal IN ${sql(transferredOrdinalNumbers)} + WHERE sat_ordinal IN ${sql([...transferredOrdinalNumbersSet])} `; + for (const reveal of reveals) { const action = 'inscription' in reveal @@ -583,6 +585,9 @@ export class PgStore extends BasePgStore { : `transfer sat ${reveal.location.ordinal_number}`; logger.info(`PgStore ${action} at block ${reveal.location.block_height}`); } + + // 3. Recursions, Counts and BRC-20 + await this.updateInscriptionRecursions(reveals); await this.counts.applyInscriptions(inscriptionInserts); if (ENV.BRC20_BLOCK_SCAN_ENABLED) await this.brc20.insertOperations({ reveals: revealOutputs, pointers }); diff --git a/tests/ordhook/server.test.ts b/tests/ordhook/server.test.ts index 30bfdd2e..09c96a88 100644 --- a/tests/ordhook/server.test.ts +++ b/tests/ordhook/server.test.ts @@ -345,6 +345,96 @@ describe('EventServer', () => { const json = response.json(); expect(json.genesis_address).toBe(address); }); + + test('inscriptions revealed and immediately transferred in the same block', async () => { + await db.updateInscriptions( + new TestChainhookPayloadBuilder() + .apply() + .block({ + height: 832574, + hash: '000000000000000000020c8145de25b1e1e0a6312e377827a3015e15fdd574cd', + }) + .transaction({ + hash: '53957f47697096cef4ad24dae6357b3d7ffe1e3eb9216ce0bb01d6b6a2c8cf4a', + }) + .inscriptionRevealed({ + content_bytes: + '0x7b2270223a226272632d3230222c226f70223a227472616e73666572222c227469636b223a224d544d54222c22616d74223a2231303030227d', + content_length: 57, + content_type: 'text/plain;charset=utf-8', + curse_type: null, + delegate: '', + inscriber_address: 'bc1pgfkgsz2gv8cy42csdfgnuepx5g2sm0y3nsccvehjpjnev8990pms7jp9n5', + inscription_fee: 11322, + inscription_id: '53957f47697096cef4ad24dae6357b3d7ffe1e3eb9216ce0bb01d6b6a2c8cf4ai0', + inscription_input_index: 0, + inscription_number: { + classic: 0, + jubilee: 0, + }, + inscription_output_value: 8834, + inscription_pointer: 0, + metadata: null, + metaprotocol: '', + ordinal_block_height: 149412, + ordinal_number: 747064132806533, + ordinal_offset: 4132806533, + parent: '', + satpoint_post_inscription: + '53957f47697096cef4ad24dae6357b3d7ffe1e3eb9216ce0bb01d6b6a2c8cf4a:0:0', + transfers_pre_inscription: 0, + tx_index: 2613, + }) + .transaction({ + hash: '5252157e270d1d405fa5d58249832ca3aa706b84e4dad2a31e7f52373aec2b7b', + }) + .inscriptionTransferred({ + destination: { + type: 'transferred', + value: 'bc1p80sw4ug55q7p4ha5gsk40d2tszqy7cendt9yksmf4nswzzrq58msp6t7qe', + }, + ordinal_number: 747064132806533, + post_transfer_output_value: 546, + satpoint_post_transfer: + '5252157e270d1d405fa5d58249832ca3aa706b84e4dad2a31e7f52373aec2b7b:0:0', + satpoint_pre_transfer: + '53957f47697096cef4ad24dae6357b3d7ffe1e3eb9216ce0bb01d6b6a2c8cf4a:0:0', + tx_index: 2614, + }) + .build() + ); + const response = await fastify.inject({ + method: 'GET', + url: `/ordinals/inscriptions/53957f47697096cef4ad24dae6357b3d7ffe1e3eb9216ce0bb01d6b6a2c8cf4ai0/transfers`, + }); + expect(response.statusCode).toBe(200); + const json = response.json(); + expect(json.total).toBe(2); + expect(json.results).toStrictEqual([ + { + address: 'bc1p80sw4ug55q7p4ha5gsk40d2tszqy7cendt9yksmf4nswzzrq58msp6t7qe', + block_hash: '000000000000000000020c8145de25b1e1e0a6312e377827a3015e15fdd574cd', + block_height: 832574, + location: '5252157e270d1d405fa5d58249832ca3aa706b84e4dad2a31e7f52373aec2b7b:0:0', + offset: '0', + output: '5252157e270d1d405fa5d58249832ca3aa706b84e4dad2a31e7f52373aec2b7b:0', + timestamp: 1677803510000, + tx_id: '5252157e270d1d405fa5d58249832ca3aa706b84e4dad2a31e7f52373aec2b7b', + value: '546', + }, + { + address: 'bc1pgfkgsz2gv8cy42csdfgnuepx5g2sm0y3nsccvehjpjnev8990pms7jp9n5', + block_hash: '000000000000000000020c8145de25b1e1e0a6312e377827a3015e15fdd574cd', + block_height: 832574, + location: '53957f47697096cef4ad24dae6357b3d7ffe1e3eb9216ce0bb01d6b6a2c8cf4a:0:0', + offset: '0', + output: '53957f47697096cef4ad24dae6357b3d7ffe1e3eb9216ce0bb01d6b6a2c8cf4a:0', + timestamp: 1677803510000, + tx_id: '53957f47697096cef4ad24dae6357b3d7ffe1e3eb9216ce0bb01d6b6a2c8cf4a', + value: '8834', + }, + ]); + }); }); describe('gap detection', () => {