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

Bns v2 feat #2080

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from
106 changes: 106 additions & 0 deletions migrations/20240912154500_add_bns_v2_names_table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/** @param { import("node-pg-migrate").MigrationBuilder } pgm */
exports.up = pgm => {
pgm.createTable('names_v2', {
id: {
type: 'serial',
primaryKey: true,
},
fullName: {
type: 'string',
notNull: true,
},
name: {
type: 'string',
notNull: true,
},
namespace_id: {
type: 'string',
notNull: true,
},
registered_at: {
type: 'integer',
notNull: false,
},
imported_at: {
type: 'integer',
notNull: false,
},
hashed_salted_fqn_preorder: {
type: 'string',
notNull: false,
},
preordered_by: {
type: 'string',
notNull: false,
},
renewal_height: {
type: 'integer',
notNull: true,
},
stx_burn: {
type: 'bigint',
notNull: true,
},
owner: {
type: 'string',
notNull: true,
},
tx_id: {
type: 'bytea',
notNull: true,
},
tx_index: {
type: 'smallint',
notNull: true,
},
event_index: 'integer',
status: {
type: 'string',
notNull: false,
},
canonical: {
type: 'boolean',
notNull: true,
default: true,
},
index_block_hash: {
type: 'bytea',
notNull: true,
},
parent_index_block_hash: {
type: 'bytea',
notNull: true,
},
microblock_hash: {
type: 'bytea',
notNull: true,
},
microblock_sequence: {
type: 'integer',
notNull: true,
},
microblock_canonical: {
type: 'boolean',
notNull: true,
},
});

pgm.createIndex('names_v2', 'namespace_id');
pgm.createIndex('names_v2', 'index_block_hash');
pgm.createIndex('names_v2', [
{ name: 'registered_at', sort: 'DESC' },
{ name: 'microblock_sequence', sort: 'DESC' },
{ name: 'tx_index', sort: 'DESC' },
{ name: 'event_index', sort: 'DESC' },
]);
pgm.addConstraint(
'names_v2',
'unique_name_v2_tx_id_index_block_hash_microblock_hash_event_index',
'UNIQUE(fullName, tx_id, index_block_hash, microblock_hash, event_index)'
);
pgm.addConstraint('names_v2', 'unique_fullname', 'UNIQUE(fullName)');
};

exports.down = pgm => {
pgm.dropTable('names_v2');
};
123 changes: 123 additions & 0 deletions migrations/20240912154500_add_bns_v2_namespaces_table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/** @param { import("node-pg-migrate").MigrationBuilder } pgm */
exports.up = pgm => {
pgm.createTable('namespaces_v2', {
id: {
type: 'serial',
primaryKey: true,
},
namespace_id: {
type: 'string',
notNull: true,
},
namespace_manager: {
type: 'string',
notNull: false,
},
manager_transferable: {
type: 'boolean',
notNull: true,
},
manager_frozen: {
type: 'boolean',
notNull: true,
},
namespace_import: {
type: 'string',
notNull: true,
},
reveal_block: {
type: 'integer',
notNull: true,
},
launched_at: {
type: 'integer',
notNull: false,
},
launch_block: {
type: 'integer',
notNull: true,
},
lifetime: {
type: 'integer',
notNull: true,
},
can_update_price_function: {
type: 'boolean',
notNull: true,
},
buckets: {
type: 'string',
notNull: true,
},
base: {
type: 'numeric',
notNull: true,
},
coeff: {
type: 'numeric',
notNull: true,
},
nonalpha_discount: {
type: 'numeric',
notNull: true,
},
no_vowel_discount: {
type: 'numeric',
notNull: true,
},
status: {
type: 'string',
notNull: false,
},
tx_id: {
type: 'bytea',
notNull: true,
},
tx_index: {
type: 'smallint',
notNull: true,
},
canonical: {
type: 'boolean',
notNull: true,
default: true,
},
index_block_hash: {
type: 'bytea',
notNull: true,
},
parent_index_block_hash: {
type: 'bytea',
notNull: true,
},
microblock_hash: {
type: 'bytea',
notNull: true,
},
microblock_sequence: {
type: 'integer',
notNull: true,
},
microblock_canonical: {
type: 'boolean',
notNull: true,
},
});

pgm.createIndex('namespaces_v2', 'index_block_hash');
pgm.createIndex('namespaces_v2', [
{ name: 'launch_block', sort: 'DESC' },
{ name: 'microblock_sequence', sort: 'DESC' },
{ name: 'tx_index', sort: 'DESC' },
]);
pgm.addConstraint(
'namespaces_v2',
'unique_namespace_v2_id_tx_id_index_block_hash_microblock_hash',
'UNIQUE(namespace_id, tx_id, index_block_hash, microblock_hash)'
);
pgm.addConstraint('namespaces_v2', 'unique_namespace_id', 'UNIQUE(namespace_id)');
};

exports.down = pgm => {
pgm.dropTable('namespaces_v2');
};
10 changes: 10 additions & 0 deletions src/api/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ import FastifyMetrics from 'fastify-metrics';
import FastifyCors from '@fastify/cors';
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox';
import * as promClient from 'prom-client';
import { BnsV2NameRoutes } from './routes/bnsV2/names';
import { BnsV2NamespaceRoutes } from './routes/bnsV2/namespaces';
import { BnsV2AddressRoutes } from './routes/bnsV2/addresses';
import { BnsV2PriceRoutes } from './routes/bnsV2/pricing';
import { BnsV2ReadRoutes } from './routes/bnsV2/reads';

export interface ApiServer {
fastifyApp: FastifyInstance;
Expand Down Expand Up @@ -112,6 +117,11 @@ export const StacksApiRoutes: FastifyPluginAsync<
await fastify.register(BnsNamespaceRoutes, { prefix: '/v1/namespaces' });
await fastify.register(BnsAddressRoutes, { prefix: '/v1/addresses' });
await fastify.register(BnsPriceRoutes, { prefix: '/v2/prices' });
await fastify.register(BnsV2NameRoutes, { prefix: '/v2/names' });
await fastify.register(BnsV2NamespaceRoutes, { prefix: '/v2/namespaces' });
await fastify.register(BnsV2AddressRoutes, { prefix: '/v2/addresses' });
await fastify.register(BnsV2ReadRoutes, { prefix: '/v2/read' });
await fastify.register(BnsV2PriceRoutes, { prefix: '/v3/prices' });

await Promise.resolve();
};
Expand Down
80 changes: 80 additions & 0 deletions src/api/routes/bnsV2/addresses.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { handleChainTipCache } from '../../../api/controllers/cache-controller';
import { FastifyPluginAsync } from 'fastify';
import { Type, TypeBoxTypeProvider } from '@fastify/type-provider-typebox';
import { Server } from 'node:http';
import { UnanchoredParamSchema } from '../../schemas/params';
import { InvalidRequestError, InvalidRequestErrorType } from '../../../errors';

const SUPPORTED_BLOCKCHAINS = ['stacks'];

export const BnsV2AddressRoutes: FastifyPluginAsync<
Record<never, never>,
Server,
TypeBoxTypeProvider
> = async fastify => {
fastify.get(
'/:blockchain/:address',
{
preHandler: handleChainTipCache,
schema: {
operationId: 'get_names_owned_by_address',
summary: 'Get Names Owned by Address',
description: `Retrieves a list of names owned by the address provided.`,
tags: ['Names'],
params: Type.Object({
blockchain: Type.String({
description: 'The layer-1 blockchain for the address',
examples: ['stacks'],
}),
address: Type.String({
description: 'The address to lookup',
examples: ['SP2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKNRV9EJ7'],
}),
}),
querystring: Type.Object({
unanchored: UnanchoredParamSchema,
}),
response: {
200: Type.Object(
{
names: Type.Array(
Type.String({
examples: ['muneeb.id'],
})
),
},
{
title: 'BnsNamesOwnByAddressResponse',
description: 'Retrieves a list of names owned by the address provided.',
}
),
},
},
},
async (req, reply) => {
const { blockchain, address } = req.params;

if (!SUPPORTED_BLOCKCHAINS.includes(blockchain)) {
throw new InvalidRequestError(
'Unsupported blockchain',
InvalidRequestErrorType.bad_request
);
}

const includeUnanchored = req.query.unanchored ?? false;

const namesV2ByAddress = await fastify.db.getNamesV2ByAddressList({
address: address,
includeUnanchored,
chainId: fastify.chainId,
});

if (namesV2ByAddress.found) {
await reply.send({ names: namesV2ByAddress.result });
} else {
await reply.send({ names: [] });
}
}
);
await Promise.resolve();
};
Loading
Loading