Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add tx_count to tokens #215

Merged
merged 3 commits into from
Sep 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions migrations/1694081119000_brc20-counts-by-tx-count.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* 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', {
tx_count: {
type: 'bigint',
default: 1,
},
});
pgm.sql(`
UPDATE brc20_deploys AS d
SET tx_count = (
SELECT COALESCE(COUNT(*), 0) AS tx_count
FROM brc20_events
WHERE brc20_deploy_id = d.id
)
`);
}

export function down(pgm: MigrationBuilder): void {
pgm.dropColumn('brc20_deploys', 'tx_count');
}
4 changes: 4 additions & 0 deletions src/api/routes/brc20.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Server } from 'http';
import {
AddressParam,
BlockHeightParam,
Brc20TokensOrderByParam,
Brc20ActivityResponseSchema,
Brc20BalanceResponseSchema,
Brc20HolderResponseSchema,
Expand Down Expand Up @@ -46,6 +47,8 @@ export const Brc20Routes: FastifyPluginCallback<
tags: ['BRC-20'],
querystring: Type.Object({
ticker: Type.Optional(Brc20TickersParam),
// Sorting
order_by: Type.Optional(Brc20TokensOrderByParam),
// Pagination
offset: Type.Optional(OffsetParam),
limit: Type.Optional(LimitParam),
Expand All @@ -62,6 +65,7 @@ export const Brc20Routes: FastifyPluginCallback<
limit,
offset,
ticker: request.query.ticker,
order_by: request.query.order_by,
});
await reply.send({
limit,
Expand Down
10 changes: 10 additions & 0 deletions src/api/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,15 @@ export const Brc20OperationParam = Type.Union(

export const Brc20OperationsParam = Type.Array(Brc20OperationParam);

export enum Brc20TokenOrderBy {
tx_count = 'tx_count',
index = 'index',
}
export const Brc20TokensOrderByParam = Type.Enum(Brc20TokenOrderBy, {
title: 'Order By',
description: 'Parameter to order results by',
});

export enum OrderBy {
number = 'number',
genesis_block_height = 'genesis_block_height',
Expand Down Expand Up @@ -463,6 +472,7 @@ export const Brc20TokenResponseSchema = Type.Object(
decimals: Type.Integer({ examples: [18] }),
deploy_timestamp: Type.Integer({ examples: [1677733170000] }),
minted_supply: Type.String({ examples: ['1000000'] }),
tx_count: Type.String({ examples: ['300000'] }),
},
{ title: 'BRC-20 Token Response' }
);
Expand Down
1 change: 1 addition & 0 deletions src/api/util/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export function parseBrc20Tokens(items: DbBrc20Token[]): Brc20TokenResponse[] {
decimals: i.decimals,
deploy_timestamp: i.timestamp.valueOf(),
minted_supply: decimals(i.minted_supply, i.decimals),
tx_count: i.tx_count,
}));
}

Expand Down
83 changes: 62 additions & 21 deletions src/pg/brc20/brc20-pg-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,21 @@ import * as postgres from 'postgres';
import { hexToBuffer } from '../../api/util/helpers';
import { DbInscription, DbInscriptionIndexPaging, DbPaginatedResult } from '../types';
import {
DbBrc20Token,
DbBrc20Balance,
DbBrc20Holder,
BRC20_DEPLOYS_COLUMNS,
DbBrc20ScannedInscription,
DbBrc20Activity,
DbBrc20Balance,
DbBrc20BalanceTypeId,
DbBrc20DeployInsert,
DbBrc20Event,
DbBrc20Holder,
DbBrc20Location,
DbBrc20Activity,
DbBrc20MintEvent,
DbBrc20ScannedInscription,
DbBrc20Token,
DbBrc20TokenWithSupply,
DbBrc20BalanceTypeId,
DbBrc20MintActivity,
DbBrc20TransferEvent,
} from './types';

import {
Brc20Deploy,
Brc20Mint,
Expand All @@ -23,6 +26,8 @@ import {
isAddressSentAsFee,
} from './helpers';

import { Brc20TokenOrderBy } from '../../api/schemas';

export class Brc20PgStore extends BasePgStoreModule {
sqlOr(partials: postgres.PendingQuery<postgres.Row[]>[] | undefined) {
return partials?.reduce((acc, curr) => this.sql`${acc} OR ${curr}`);
Expand Down Expand Up @@ -160,9 +165,13 @@ export class Brc20PgStore extends BasePgStoreModule {
total_balance = brc20_total_balances.total_balance + EXCLUDED.total_balance
)
`
}
}, deploy_update AS (
UPDATE brc20_deploys
SET tx_count = tx_count + 1
WHERE id = (SELECT brc20_deploy_id FROM validated_transfer)
)
INSERT INTO brc20_events (operation, inscription_id, genesis_location_id, brc20_deploy_id, transfer_id) (
SELECT 'transfer_send', id, ${location.id}, brc20_deploy_id, id
Copy link
Contributor Author

@janniks janniks Sep 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rafaelcr I had a bug here that will have caused incorrect DB values. Should I add a migration to mitigate (rewrite the inscription_id column of transfer_sends?)

SELECT 'transfer_send', ${location.inscription_id}, ${location.id}, brc20_deploy_id, id
FROM validated_transfer
)
`;
Expand All @@ -184,6 +193,7 @@ export class Brc20PgStore extends BasePgStoreModule {
max: deploy.op.max,
limit: deploy.op.lim ?? null,
decimals: deploy.op.dec ?? '18',
tx_count: 1,
};
const deployRes = await this.sql`
WITH deploy_insert AS (
Expand Down Expand Up @@ -231,9 +241,11 @@ export class Brc20PgStore extends BasePgStoreModule {
ON CONFLICT (inscription_id) DO NOTHING
RETURNING id, brc20_deploy_id
),
supply_update AS (
deploy_update AS (
UPDATE brc20_deploys
SET minted_supply = minted_supply + (SELECT real_mint_amount FROM validated_mint)
SET
minted_supply = minted_supply + (SELECT real_mint_amount FROM validated_mint),
tx_count = tx_count + 1
WHERE id = (SELECT brc20_deploy_id FROM validated_mint)
),
balance_insert AS (
Expand Down Expand Up @@ -309,6 +321,11 @@ export class Brc20PgStore extends BasePgStoreModule {
trans_balance = trans_balance + ${transfer.op.amt}::numeric
WHERE brc20_deploy_id = (SELECT brc20_deploy_id FROM validated_transfer)
AND address = ${transfer.location.address}
),
deploy_update AS (
UPDATE brc20_deploys
SET tx_count = tx_count + 1
WHERE id = (SELECT brc20_deploy_id FROM validated_transfer)
)
INSERT INTO brc20_events (operation, inscription_id, genesis_location_id, brc20_deploy_id, transfer_id) (
SELECT 'transfer', ${transfer.location.inscription_id}, ${transfer.location.id}, brc20_deploy_id, id
Expand All @@ -322,32 +339,42 @@ export class Brc20PgStore extends BasePgStoreModule {
}

async rollBackInscription(args: { inscription: DbInscription }): Promise<void> {
const activities = await this.sql<DbBrc20Activity[]>`
const events = await this.sql<DbBrc20Event[]>`
SELECT * FROM brc20_events WHERE inscription_id = ${args.inscription.id}
`;
if (activities.count === 0) return;
if (events.count === 0) return;
// Traverse all activities generated by this inscription and roll back actions that are NOT
// otherwise handled by the ON DELETE CASCADE postgres constraint.
for (const activity of activities) {
switch (activity.operation) {
for (const event of events) {
switch (event.operation) {
case 'deploy':
// Deploy events are handled by the ON DELETE CASCADE postgres constraint.
// - tx_count is lost successfully, since the deploy will be deleted.
break;
case 'mint':
await this.rollBackMint(activity);
await this.rollBackMint(event);
break;
case 'transfer':
case 'transfer_send':
await this.rollBackTransfer(event);
break;
}
}
}

private async rollBackMint(activity: DbBrc20MintActivity): Promise<void> {
private async rollBackMint(activity: DbBrc20MintEvent): Promise<void> {
// Get real minted amount and substract from places.
await this.sql`
WITH minted_balance AS (
SELECT address, avail_balance
FROM brc20_balances
WHERE inscription_id = ${activity.inscription_id} AND type = ${DbBrc20BalanceTypeId.mint}
),
token_supply_update AS (
deploy_update AS (
UPDATE brc20_deploys
SET minted_supply = minted_supply - (SELECT avail_balance FROM minted_balance)
SET
minted_supply = minted_supply - (SELECT avail_balance FROM minted_balance),
tx_count = tx_count - 1
WHERE id = ${activity.brc20_deploy_id}
)
UPDATE brc20_total_balances SET
Expand All @@ -357,12 +384,26 @@ export class Brc20PgStore extends BasePgStoreModule {
`;
}

private async rollBackTransfer(activity: DbBrc20TransferEvent): Promise<void> {
// Subtract tx_count per transfer event (transfer and transfer_send are
// separate events, so they will both be counted).
await this.sql`
UPDATE brc20_deploys
SET tx_count = tx_count - 1
WHERE id = ${activity.brc20_deploy_id}
`;
}

async getTokens(
args: { ticker?: string[] } & DbInscriptionIndexPaging
args: { ticker?: string[]; order_by?: Brc20TokenOrderBy } & DbInscriptionIndexPaging
): Promise<DbPaginatedResult<DbBrc20Token>> {
const tickerPrefixCondition = this.sqlOr(
args.ticker?.map(t => this.sql`d.ticker_lower LIKE LOWER(${t}) || '%'`)
);
const orderBy =
args.order_by === Brc20TokenOrderBy.tx_count
? this.sql`tx_count DESC` // tx_count
: this.sql`l.block_height DESC, l.tx_index DESC`; // default: `index`
const results = await this.sql<(DbBrc20Token & { total: number })[]>`
SELECT
${this.sql(BRC20_DEPLOYS_COLUMNS.map(c => `d.${c}`))},
Expand All @@ -372,7 +413,7 @@ export class Brc20PgStore extends BasePgStoreModule {
INNER JOIN genesis_locations AS g ON g.inscription_id = d.inscription_id
INNER JOIN locations AS l ON l.id = g.location_id
${tickerPrefixCondition ? this.sql`WHERE ${tickerPrefixCondition}` : this.sql``}
ORDER BY l.block_height DESC, l.tx_index DESC
ORDER BY ${orderBy}
OFFSET ${args.offset}
LIMIT ${args.limit}
`;
Expand Down
32 changes: 27 additions & 5 deletions src/pg/brc20/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export type DbBrc20DeployInsert = {
max: string;
decimals: string;
limit: string | null;
tx_count: number;
};

export type DbBrc20MintInsert = {
Expand Down Expand Up @@ -77,6 +78,7 @@ export type DbBrc20Token = {
decimals: number;
timestamp: number;
minted_supply: string;
tx_count: string;
};

export type DbBrc20TokenWithSupply = DbBrc20Token & {
Expand Down Expand Up @@ -107,16 +109,35 @@ export enum DbBrc20BalanceTypeId {

export type DbBrc20EventOperation = 'deploy' | 'mint' | 'transfer' | 'transfer_send';

export type DbBrc20EventInsert = {
operation: DbBrc20EventOperation;
type BaseEvent = {
inscription_id: string;
genesis_location_id: string;
brc20_deploy_id: string;
deploy_id: string | null;
mint_id: string | null;
transfer_id: string | null;
};

export type DbBrc20DeployEvent = BaseEvent & {
operation: 'deploy';
deploy_id: string;
mint_id: null;
transfer_id: null;
};

export type DbBrc20MintEvent = BaseEvent & {
operation: 'mint';
deploy_id: null;
mint_id: string;
transfer_id: null;
};

export type DbBrc20TransferEvent = BaseEvent & {
operation: 'transfer' | 'transfer_send';
deploy_id: null;
mint_id: null;
transfer_id: string;
};

export type DbBrc20Event = DbBrc20DeployEvent | DbBrc20MintEvent | DbBrc20TransferEvent;

type BaseActivity = {
ticker: string;
deploy_decimals: number;
Expand Down Expand Up @@ -159,6 +180,7 @@ export const BRC20_DEPLOYS_COLUMNS = [
'decimals',
'limit',
'minted_supply',
'tx_count',
];

export const BRC20_TRANSFERS_COLUMNS = [
Expand Down
Loading