Skip to content

Commit

Permalink
Add query by address
Browse files Browse the repository at this point in the history
  • Loading branch information
gostkin committed Dec 18, 2023
1 parent 1e22d98 commit 9cc2979
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 3 deletions.
59 changes: 58 additions & 1 deletion webserver/server/app/controllers/ProjectedNftRangeController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import pool from '../services/PgPoolSingleton';
import type { ErrorShape } from '../../../shared/errors';
import type { EndpointTypes } from '../../../shared/routes';
import { Routes } from '../../../shared/routes';
import { projectedNftRange } from '../services/ProjectedNftRange';
import { projectedNftRange, projectedNftRangeByAddress } from '../services/ProjectedNftRange';
import type {ProjectedNftRangeResponse} from '../../../shared/models/ProjectedNftRange';
import {PROJECTED_NFT_LIMIT} from "../../../shared/constants";
import {Errors, genErrorMessage} from "../../../shared/errors";

const route = Routes.projectedNftEventsRange;

Expand All @@ -22,6 +24,28 @@ export class ProjectedNftRangeController extends Controller {
StatusCodes.BAD_REQUEST | StatusCodes.CONFLICT | StatusCodes.UNPROCESSABLE_ENTITY,
ErrorShape
>
): Promise<EndpointTypes[typeof route]['response']> {
const slotRangeSize = requestBody.range.maxSlot - requestBody.range.minSlot;
if (slotRangeSize > PROJECTED_NFT_LIMIT.SLOT_RANGE) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return errorResponse(
StatusCodes.BAD_REQUEST,
genErrorMessage(Errors.SlotRangeLimitExceeded, {
limit: PROJECTED_NFT_LIMIT.SLOT_RANGE,
found: slotRangeSize,
})
);
}

if (requestBody.address !== null) {
return await this.handle_by_address_query(requestBody.address, requestBody);
} else {
return await this.handle_general_query(requestBody);
}
}

async handle_general_query(
requestBody: EndpointTypes[typeof route]['input'],
): Promise<EndpointTypes[typeof route]['response']> {
const response = await tx<
ProjectedNftRangeResponse
Expand Down Expand Up @@ -50,4 +74,37 @@ export class ProjectedNftRangeController extends Controller {

return response;
}

async handle_by_address_query(
address: string,
requestBody: EndpointTypes[typeof route]['input'],
): Promise<EndpointTypes[typeof route]['response']> {
const response = await tx<
ProjectedNftRangeResponse
>(pool, async dbTx => {
const data = await projectedNftRangeByAddress({
address: address,
range: requestBody.range,
dbTx
});

return data.map(data => ({
ownerAddress: data.owner_address,
previousTxHash: data.previous_tx_hash,
previousTxOutputIndex: data.previous_tx_output_index != null ? parseInt(data.previous_tx_output_index) : null,
actionTxId: data.action_tx_id,
actionOutputIndex: data.action_output_index,
asset: `${data.policy_id}.${data.asset_name}`,
policyId: data.policy_id,
assetName: data.asset_name,
amount: data.amount,
status: data.status,
plutusDatum: data.plutus_datum,
actionSlot: data.action_slot,
forHowLong: data.for_how_long,
}));
});

return response;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/** Types generated for queries found in "app/models/projected_nft/projectedNftRangeByAddress.sql" */
import { PreparedQuery } from '@pgtyped/query';

/** 'SqlProjectedNftRangeByAddress' parameters type */
export interface ISqlProjectedNftRangeByAddressParams {
max_slot: number;
min_slot: number;
owner_address: string;
}

/** 'SqlProjectedNftRangeByAddress' return type */
export interface ISqlProjectedNftRangeByAddressResult {
action_output_index: number | null;
action_slot: number;
action_tx_id: string | null;
amount: string;
asset_name: string;
for_how_long: string | null;
owner_address: string | null;
plutus_datum: string | null;
policy_id: string;
previous_tx_hash: string | null;
previous_tx_output_index: string | null;
status: string | null;
}

/** 'SqlProjectedNftRangeByAddress' query type */
export interface ISqlProjectedNftRangeByAddressQuery {
params: ISqlProjectedNftRangeByAddressParams;
result: ISqlProjectedNftRangeByAddressResult;
}

const sqlProjectedNftRangeByAddressIR: any = {"usedParamSet":{"owner_address":true,"min_slot":true,"max_slot":true},"params":[{"name":"owner_address","required":true,"transform":{"type":"scalar"},"locs":[{"a":1250,"b":1264}]},{"name":"min_slot","required":true,"transform":{"type":"scalar"},"locs":[{"a":1289,"b":1298}]},{"name":"max_slot","required":true,"transform":{"type":"scalar"},"locs":[{"a":1324,"b":1333}]}],"statement":"SELECT\n encode(\"ProjectedNFT\".owner_address, 'hex') as owner_address,\n\n encode(\"ProjectedNFT\".previous_utxo_tx_hash, 'hex') as previous_tx_hash,\n \"ProjectedNFT\".previous_utxo_tx_output_index as previous_tx_output_index,\n\n CASE\n WHEN \"TransactionOutput\".output_index = NULL THEN NULL\n ELSE \"TransactionOutput\".output_index\n END AS action_output_index,\n\n encode(\"Transaction\".hash, 'hex') as action_tx_id,\n\n \"ProjectedNFT\".policy_id as policy_id,\n \"ProjectedNFT\".asset_name as asset_name,\n \"ProjectedNFT\".amount as amount,\n\n CASE\n WHEN \"ProjectedNFT\".operation = 0 THEN 'Lock'\n WHEN \"ProjectedNFT\".operation = 1 THEN 'Unlocking'\n WHEN \"ProjectedNFT\".operation = 2 THEN 'Claim'\n ELSE 'Invalid'\n END AS status,\n\n encode(\"ProjectedNFT\".plutus_datum, 'hex') as plutus_datum,\n \"ProjectedNFT\".for_how_long as for_how_long,\n\n \"Block\".slot as action_slot\nFROM \"ProjectedNFT\"\n LEFT JOIN \"TransactionOutput\" ON \"TransactionOutput\".id = \"ProjectedNFT\".hololocker_utxo_id\n JOIN \"Transaction\" ON \"Transaction\".id = \"ProjectedNFT\".tx_id\n JOIN \"Block\" ON \"Transaction\".block_id = \"Block\".id\nWHERE\n encode(\"ProjectedNFT\".owner_address, 'hex') = :owner_address!\n AND \"Block\".slot > :min_slot!\n AND \"Block\".slot <= :max_slot!\nORDER BY (\"Block\".height, \"Transaction\".tx_index) ASC"};

/**
* Query generated from SQL:
* ```
* SELECT
* encode("ProjectedNFT".owner_address, 'hex') as owner_address,
*
* encode("ProjectedNFT".previous_utxo_tx_hash, 'hex') as previous_tx_hash,
* "ProjectedNFT".previous_utxo_tx_output_index as previous_tx_output_index,
*
* CASE
* WHEN "TransactionOutput".output_index = NULL THEN NULL
* ELSE "TransactionOutput".output_index
* END AS action_output_index,
*
* encode("Transaction".hash, 'hex') as action_tx_id,
*
* "ProjectedNFT".policy_id as policy_id,
* "ProjectedNFT".asset_name as asset_name,
* "ProjectedNFT".amount as amount,
*
* CASE
* WHEN "ProjectedNFT".operation = 0 THEN 'Lock'
* WHEN "ProjectedNFT".operation = 1 THEN 'Unlocking'
* WHEN "ProjectedNFT".operation = 2 THEN 'Claim'
* ELSE 'Invalid'
* END AS status,
*
* encode("ProjectedNFT".plutus_datum, 'hex') as plutus_datum,
* "ProjectedNFT".for_how_long as for_how_long,
*
* "Block".slot as action_slot
* FROM "ProjectedNFT"
* LEFT JOIN "TransactionOutput" ON "TransactionOutput".id = "ProjectedNFT".hololocker_utxo_id
* JOIN "Transaction" ON "Transaction".id = "ProjectedNFT".tx_id
* JOIN "Block" ON "Transaction".block_id = "Block".id
* WHERE
* encode("ProjectedNFT".owner_address, 'hex') = :owner_address!
* AND "Block".slot > :min_slot!
* AND "Block".slot <= :max_slot!
* ORDER BY ("Block".height, "Transaction".tx_index) ASC
* ```
*/
export const sqlProjectedNftRangeByAddress = new PreparedQuery<ISqlProjectedNftRangeByAddressParams,ISqlProjectedNftRangeByAddressResult>(sqlProjectedNftRangeByAddressIR);


Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
@name sqlProjectedNftRangeByAddress
*/
SELECT
encode("ProjectedNFT".owner_address, 'hex') as owner_address,

encode("ProjectedNFT".previous_utxo_tx_hash, 'hex') as previous_tx_hash,
"ProjectedNFT".previous_utxo_tx_output_index as previous_tx_output_index,

CASE
WHEN "TransactionOutput".output_index = NULL THEN NULL
ELSE "TransactionOutput".output_index
END AS action_output_index,

encode("Transaction".hash, 'hex') as action_tx_id,

"ProjectedNFT".policy_id as policy_id,
"ProjectedNFT".asset_name as asset_name,
"ProjectedNFT".amount as amount,

CASE
WHEN "ProjectedNFT".operation = 0 THEN 'Lock'
WHEN "ProjectedNFT".operation = 1 THEN 'Unlocking'
WHEN "ProjectedNFT".operation = 2 THEN 'Claim'
ELSE 'Invalid'
END AS status,

encode("ProjectedNFT".plutus_datum, 'hex') as plutus_datum,
"ProjectedNFT".for_how_long as for_how_long,

"Block".slot as action_slot
FROM "ProjectedNFT"
LEFT JOIN "TransactionOutput" ON "TransactionOutput".id = "ProjectedNFT".hololocker_utxo_id
JOIN "Transaction" ON "Transaction".id = "ProjectedNFT".tx_id
JOIN "Block" ON "Transaction".block_id = "Block".id
WHERE
encode("ProjectedNFT".owner_address, 'hex') = :owner_address!
AND "Block".slot > :min_slot!
AND "Block".slot <= :max_slot!
ORDER BY ("Block".height, "Transaction".tx_index) ASC;
13 changes: 13 additions & 0 deletions webserver/server/app/services/ProjectedNftRange.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { PoolClient } from 'pg';
import type { ISqlProjectedNftRangeResult} from '../models/projected_nft/projectedNftRange.queries';
import { sqlProjectedNftRange } from '../models/projected_nft/projectedNftRange.queries';
import { sqlProjectedNftRangeByAddress } from '../models/projected_nft/projectedNftRangeByAddress.queries';

export async function projectedNftRange(request: {
range: { minSlot: number, maxSlot: number },
Expand All @@ -11,3 +12,15 @@ export async function projectedNftRange(request: {
max_slot: request.range.maxSlot,
}, request.dbTx));
}

export async function projectedNftRangeByAddress(request: {
address: string,
range: { minSlot: number, maxSlot: number },
dbTx: PoolClient,
}): Promise<ISqlProjectedNftRangeResult[]> {
return (await sqlProjectedNftRangeByAddress.run({
owner_address: request.address,
min_slot: request.range.minSlot,
max_slot: request.range.maxSlot,
}, request.dbTx));
}
6 changes: 5 additions & 1 deletion webserver/shared/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,9 @@ export const DEX_PRICE_LIMIT = {

export const POOL_DELEGATION_LIMIT = {
POOLS: 50,
SLOT_RANGE: 200,
SLOT_RANGE: 20000,
};

export const PROJECTED_NFT_LIMIT = {
SLOT_RANGE: 100000,
};
3 changes: 2 additions & 1 deletion webserver/shared/models/ProjectedNftRange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ export type ProjectedNftRangeRequest = {
* @example 46154860
*/
maxSlot: number
}
},
address: string | null
};

export type ProjectedNftRangeResponse = {
Expand Down

0 comments on commit 9cc2979

Please sign in to comment.