Skip to content

Commit

Permalink
Mint / burn (#172)
Browse files Browse the repository at this point in the history
* Mint / burn queries

* update openapi.json

* reformat MintBurnHistoryController and add top level doc

* rebuild openapi.json

---------

Co-authored-by: Enzo Cioppettini <[email protected]>
  • Loading branch information
gostkin and ecioppettini authored Mar 4, 2024
1 parent 6bf19dc commit 577b658
Show file tree
Hide file tree
Showing 7 changed files with 567 additions and 0 deletions.
154 changes: 154 additions & 0 deletions docs/bin/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,100 @@
],
"type": "object"
},
"MintBurnSingleResponse": {
"properties": {
"assets": {
"properties": {},
"additionalProperties": {
"properties": {},
"additionalProperties": {
"$ref": "#/components/schemas/Amount"
},
"type": "object"
},
"type": "object",
"description": "Assets changed in a particular transaction",
"example": {
"b863bc7369f46136ac1048adb2fa7dae3af944c3bbb2be2f216a8d4f": {
"42657272794e617679": "1"
}
}
},
"metadata": {
"type": "string",
"nullable": true,
"description": "Transaction metadata of related mint / burn event"
},
"actionBlockId": {
"type": "string",
"description": "Block id of related mint / burn event",
"example": "4e90f1d14ad742a1c0e094a89ad180b896068f93fc3969614b1c53bac547b374",
"pattern": "[0-9a-fA-F]{64}"
},
"actionTxId": {
"type": "string",
"description": "Transaction id of related mint / burn event",
"example": "28eb069e3e8c13831d431e3b2e35f58525493ab2d77fde83184993e4aa7a0eda",
"pattern": "[0-9a-fA-F]{64}"
},
"actionSlot": {
"type": "number",
"format": "double",
"description": "Slot at which the transaction happened",
"example": 512345
}
},
"required": [
"assets",
"metadata",
"actionBlockId",
"actionTxId",
"actionSlot"
],
"type": "object"
},
"MintBurnHistoryResponse": {
"items": {
"$ref": "#/components/schemas/MintBurnSingleResponse"
},
"type": "array"
},
"MintBurnHistoryRequest": {
"properties": {
"policyIds": {
"items": {
"$ref": "#/components/schemas/PolicyId"
},
"type": "array"
},
"range": {
"properties": {
"maxSlot": {
"type": "number",
"format": "double",
"description": "Maximal slot from which the events should be returned (inclusive)",
"example": 46154860
},
"minSlot": {
"type": "number",
"format": "double",
"description": "Minimal slot from which the events should be returned (not inclusive)",
"example": 46154769
}
},
"required": [
"maxSlot",
"minSlot"
],
"type": "object",
"description": "Mint Burn events in this slot range will be returned"
}
},
"required": [
"range"
],
"type": "object"
},
"ProjectedNftStatus": {
"enum": [
"Lock",
Expand Down Expand Up @@ -1763,6 +1857,66 @@
}
}
},
"/asset/mint-burn-history": {
"post": {
"operationId": "ProjectedNftRange",
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MintBurnHistoryResponse"
}
}
}
},
"400": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorShape"
}
}
}
},
"409": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorShape"
}
}
}
},
"422": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorShape"
}
}
}
}
},
"description": "Gets mint and burn events in the provided slot range, optionally filtering\nby policyId(s). A burn event is a mint with a negative value.",
"security": [],
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MintBurnHistoryRequest"
}
}
}
}
}
},
"/projected-nft/range": {
"post": {
"operationId": "ProjectedNftRange",
Expand Down
155 changes: 155 additions & 0 deletions webserver/server/app/controllers/MintBurnHistoryController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { Body, Controller, TsoaResponse, Res, Post, Route, SuccessResponse } from 'tsoa';
import { StatusCodes } from 'http-status-codes';
import tx from 'pg-tx';
import pool from '../services/PgPoolSingleton';

import type { ErrorShape } from '../../../shared/errors';
import type { EndpointTypes } from '../../../shared/routes';
import { Routes } from '../../../shared/routes';
import { mintBurnRange, mintBurnRangeByPolicyIds } from '../services/MintBurnHistoryService';
import type { MintBurnSingleResponse } from '../../../shared/models/MintBurn';
import type { PolicyId } from '../../../shared/models/PolicyIdAssetMap';
import type {
ISqlMintBurnRangeResult,
ISqlMintBurnRangeByPolicyIdsResult,
} from '../models/asset/mintBurnHistory.queries';

const route = Routes.mintBurnHistory;

@Route('asset/mint-burn-history')
export class MintRangeController extends Controller {
/**
* Gets mint and burn events in the provided slot range, optionally filtering
* by policyId(s). A burn event is a mint with a negative value.
*
*/
@SuccessResponse(`${StatusCodes.OK}`)
@Post()
public async projectedNftRange(
@Body()
requestBody: EndpointTypes[typeof route]['input'],
@Res()
errorResponse: TsoaResponse<
StatusCodes.BAD_REQUEST | StatusCodes.CONFLICT | StatusCodes.UNPROCESSABLE_ENTITY,
ErrorShape
>
): Promise<EndpointTypes[typeof route]['response']> {
if (requestBody.policyIds !== undefined && requestBody.policyIds.length > 0) {
return await this.handle_by_policy_ids_query(requestBody.policyIds, requestBody);
} else {
return await this.handle_general_query(requestBody);
}
}

async handle_general_query(
requestBody: EndpointTypes[typeof route]['input']
): Promise<EndpointTypes[typeof route]['response']> {
const assets = await tx<ISqlMintBurnRangeResult[]>(pool, async dbTx => {
const data = await mintBurnRange({
range: requestBody.range,
dbTx,
});

return data;
});

let mintRangeResponse: MintBurnSingleResponse = {
actionTxId: '',
actionBlockId: '',
metadata: null,
actionSlot: 0,
assets: {},
};

const result: MintBurnSingleResponse[] = [];

for (const entry of assets) {
const policyId = entry.policy_id !== null ? entry.policy_id.toString() : '';
const assetName = entry.asset_name !== null ? entry.asset_name.toString() : '';
const actionTxId = entry.action_tx_id !== null ? entry.action_tx_id.toString() : '';
const actionBlockId = entry.action_block_id !== null ? entry.action_block_id.toString() : '';

if (mintRangeResponse.actionTxId != actionTxId) {
if (mintRangeResponse.actionTxId.length > 0) {
result.push(mintRangeResponse);
}

mintRangeResponse = {
actionSlot: entry.action_slot,
actionTxId: actionTxId,
actionBlockId: actionBlockId,
metadata: entry.action_tx_metadata,
assets: {},
};
}

const for_policy = mintRangeResponse.assets[policyId] ?? {};

for_policy[assetName] = entry.amount;
mintRangeResponse.assets[policyId] = for_policy;
}

if (mintRangeResponse.actionTxId.length > 0) {
result.push(mintRangeResponse);
}

return result;
}

async handle_by_policy_ids_query(
policyIds: PolicyId[],
requestBody: EndpointTypes[typeof route]['input']
): Promise<EndpointTypes[typeof route]['response']> {
const assets = await tx<ISqlMintBurnRangeByPolicyIdsResult[]>(pool, async dbTx => {
const data = await mintBurnRangeByPolicyIds({
range: requestBody.range,
policyIds: policyIds,
dbTx,
});

return data;
});

let mintRangeResponse: MintBurnSingleResponse = {
actionTxId: '',
actionBlockId: '',
metadata: null,
actionSlot: 0,
assets: {},
};

const result: MintBurnSingleResponse[] = [];

for (const entry of assets) {
const policyId = entry.policy_id !== null ? entry.policy_id.toString() : '';
const assetName = entry.asset_name !== null ? entry.asset_name.toString() : '';
const actionTxId = entry.action_tx_id !== null ? entry.action_tx_id.toString() : '';
const actionBlockId = entry.action_block_id !== null ? entry.action_block_id.toString() : '';

if (mintRangeResponse.actionTxId != actionTxId) {
if (mintRangeResponse.actionTxId.length > 0) {
result.push(mintRangeResponse);
}

mintRangeResponse = {
actionSlot: entry.action_slot,
actionTxId: actionTxId,
actionBlockId: actionBlockId,
metadata: entry.action_tx_metadata,
assets: {},
};
}

const for_policy = mintRangeResponse.assets[policyId] ?? {};

for_policy[assetName] = entry.amount;
mintRangeResponse.assets[policyId] = for_policy;
}

if (mintRangeResponse.actionTxId.length > 0) {
result.push(mintRangeResponse);
}

return result;
}
}
Loading

0 comments on commit 577b658

Please sign in to comment.