From 36addce6add55ffd4e5180c3c396c964672182e9 Mon Sep 17 00:00:00 2001 From: Rafael Cardenas Date: Wed, 30 Aug 2023 15:10:48 -0600 Subject: [PATCH] fix: remove supply view, calculate supply as we mint --- ...693428793416_brc20-minted-supply-column.ts | 36 +++++++++++++++ src/pg/brc20/brc20-pg-store.ts | 45 ++++++++----------- src/pg/pg-store.ts | 1 - 3 files changed, 55 insertions(+), 27 deletions(-) create mode 100644 migrations/1693428793416_brc20-minted-supply-column.ts diff --git a/migrations/1693428793416_brc20-minted-supply-column.ts b/migrations/1693428793416_brc20-minted-supply-column.ts new file mode 100644 index 00000000..55513825 --- /dev/null +++ b/migrations/1693428793416_brc20-minted-supply-column.ts @@ -0,0 +1,36 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { MigrationBuilder, ColumnDefinitions } from 'node-pg-migrate'; + +export const shorthands: ColumnDefinitions | undefined = undefined; + +export function up(pgm: MigrationBuilder): void { + pgm.addColumn('brc20_deploys', { + minted_supply: { + type: 'numeric', + default: 0, + }, + }); + pgm.sql(` + UPDATE brc20_deploys AS d + SET minted_supply = ( + SELECT COALESCE(SUM(amount), 0) AS minted_supply + FROM brc20_mints + WHERE brc20_deploy_id = d.id + ) + `); + pgm.dropMaterializedView('brc20_supplies'); +} + +export function down(pgm: MigrationBuilder): void { + pgm.dropColumn('brc20_deploys', ['minted_supply']); + pgm.createMaterializedView( + 'brc20_supplies', + { data: true }, + ` + SELECT brc20_deploy_id, SUM(amount) as minted_supply, MAX(block_height) as block_height + FROM brc20_mints + GROUP BY brc20_deploy_id + ` + ); + pgm.createIndex('brc20_supplies', ['brc20_deploy_id'], { unique: true }); +} diff --git a/src/pg/brc20/brc20-pg-store.ts b/src/pg/brc20/brc20-pg-store.ts index 48908e40..716186d8 100644 --- a/src/pg/brc20/brc20-pg-store.ts +++ b/src/pg/brc20/brc20-pg-store.ts @@ -86,12 +86,11 @@ export class Brc20PgStore extends BasePgStoreModule { const results = await this.sql<(DbBrc20Token & { total: number })[]>` SELECT d.id, i.genesis_id, i.number, d.block_height, d.tx_id, d.address, d.ticker, d.max, d.limit, - d.decimals, l.timestamp as deploy_timestamp, COALESCE(s.minted_supply, 0) as minted_supply, COUNT(*) OVER() as total + d.decimals, l.timestamp as deploy_timestamp, d.minted_supply, COUNT(*) OVER() as total FROM brc20_deploys AS d INNER JOIN inscriptions AS i ON i.id = d.inscription_id INNER JOIN genesis_locations AS g ON g.inscription_id = d.inscription_id INNER JOIN locations AS l ON l.id = g.location_id - LEFT JOIN brc20_supplies AS s ON d.id = s.brc20_deploy_id ${tickerPrefixCondition ? this.sql`WHERE ${tickerPrefixCondition}` : this.sql``} OFFSET ${args.offset} LIMIT ${args.limit} @@ -144,13 +143,8 @@ export class Brc20PgStore extends BasePgStoreModule { const deploy = await this.getDeploy(args); if (!deploy) return; - const supplyPromise = sql<{ max: string }[]>` - SELECT max FROM brc20_deploys WHERE id = ${deploy.id} - `; - const mintedPromise = sql<{ minted_supply: string }[]>` - SELECT minted_supply - FROM brc20_supplies - WHERE brc20_deploy_id = ${deploy.id} + const supplyPromise = sql<{ max: string; minted_supply: string }[]>` + SELECT max, minted_supply FROM brc20_deploys WHERE id = ${deploy.id} `; const holdersPromise = sql<{ count: string }[]>` SELECT COUNT(*) AS count @@ -159,11 +153,11 @@ export class Brc20PgStore extends BasePgStoreModule { GROUP BY address HAVING SUM(avail_balance + trans_balance) > 0 `; - const settles = await Promise.allSettled([supplyPromise, holdersPromise, mintedPromise]); - const [supply, holders, minted] = throwOnFirstRejected(settles); + const settles = await Promise.allSettled([supplyPromise, holdersPromise]); + const [supply, holders] = throwOnFirstRejected(settles); return { max_supply: supply[0].max, - minted_supply: minted[0]?.minted_supply ?? '0', + minted_supply: supply[0]?.minted_supply ?? '0', holders: holders[0]?.count ?? '0', }; }); @@ -283,23 +277,17 @@ export class Brc20PgStore extends BasePgStoreModule { // * Does the mint amount exceed remaining supply? const mintRes = await this.sql` WITH mint_data AS ( - SELECT - d.id, d.decimals, d.limit, d.max, - COALESCE(SUM(amount), 0) AS minted_supply - FROM brc20_deploys AS d - LEFT JOIN brc20_mints AS m ON m.brc20_deploy_id = d.id - WHERE d.ticker_lower = LOWER(${mint.op.tick}) - GROUP BY d.id + SELECT id, decimals, "limit", max, minted_supply + FROM brc20_deploys + WHERE ticker_lower = LOWER(${mint.op.tick}) AND minted_supply < max ), validated_mint AS ( SELECT - m.id AS brc20_deploy_id, - LEAST(${mint.op.amt}::numeric, m.max - m.minted_supply) AS real_mint_amount - FROM mint_data AS m - WHERE - (m.minted_supply < m.max) - AND (m.limit IS NULL OR ${mint.op.amt}::numeric <= m.limit) - AND (SCALE(${mint.op.amt}::numeric) <= m.decimals) + id AS brc20_deploy_id, + LEAST(${mint.op.amt}::numeric, max - minted_supply) AS real_mint_amount + FROM mint_data + WHERE ("limit" IS NULL OR ${mint.op.amt}::numeric <= "limit") + AND (SCALE(${mint.op.amt}::numeric) <= decimals) ), mint_insert AS ( INSERT INTO brc20_mints @@ -310,6 +298,11 @@ export class Brc20PgStore extends BasePgStoreModule { FROM validated_mint ) ON CONFLICT (inscription_id) DO NOTHING + ), + supply_update AS ( + UPDATE brc20_deploys + SET minted_supply = minted_supply + (SELECT real_mint_amount FROM validated_mint) + WHERE id = (SELECT brc20_deploy_id FROM validated_mint) ) INSERT INTO brc20_balances (inscription_id, location_id, brc20_deploy_id, address, avail_balance, trans_balance, type) diff --git a/src/pg/pg-store.ts b/src/pg/pg-store.ts index 5b0cd487..69d215b9 100644 --- a/src/pg/pg-store.ts +++ b/src/pg/pg-store.ts @@ -235,7 +235,6 @@ export class PgStore extends BasePgStore { // We'll issue materialized view refreshes in parallel. We will not wait for them to finish so // we can respond to the chainhook node with a `200` HTTP code as soon as possible. const views = [this.normalizeInscriptionCount({ min_block_height: updatedBlockHeightMin })]; - if (ENV.BRC20_BLOCK_SCAN_ENABLED) views.push(this.refreshMaterializedView('brc20_supplies')); const viewRefresh = Promise.allSettled(views); // Only wait for these on tests. if (isTestEnv) await viewRefresh;