Files
stacks-blockchain-api/src/tests/api-tests.ts

8643 lines
316 KiB
TypeScript

import * as supertest from 'supertest';
import {
makeContractCall,
NonFungibleConditionCode,
FungibleConditionCode,
bufferCVFromString,
ClarityAbi,
ClarityType,
makeContractDeploy,
serializeCV,
sponsorTransaction,
createNonFungiblePostCondition,
createFungiblePostCondition,
createSTXPostCondition,
BufferReader,
ChainID,
AnchorMode,
intCV,
uintCV,
stringAsciiCV,
} from '@stacks/transactions';
import * as BN from 'bn.js';
import { createClarityValueArray, readTransaction } from '../p2p/tx';
import { getTxFromDataStore, getBlockFromDataStore } from '../api/controllers/db-controller';
import {
createDbTxFromCoreMsg,
DbBlock,
DbTx,
DbTxTypeId,
DbStxEvent,
DbEventTypeId,
DbAssetEventTypeId,
DbFtEvent,
DbNftEvent,
DbMempoolTx,
DbSmartContract,
DbSmartContractEvent,
DbTxStatus,
DbBurnchainReward,
DataStoreBlockUpdateData,
DbRewardSlotHolder,
DbMinerReward,
DbTokenOfferingLocked,
DataStoreTxEventData,
DbTxAnchorMode,
} from '../datastore/common';
import { startApiServer, ApiServer } from '../api/init';
import { PgDataStore, cycleMigrations, runMigrations } from '../datastore/postgres-store';
import { PoolClient } from 'pg';
import { bufferToHexPrefixString, I32_MAX, microStxToStx, STACKS_DECIMAL_PLACES } from '../helpers';
import { FEE_RATE } from './../api/routes/fee-rate';
import { Block, FeeRateRequest } from 'docs/generated';
import { TestBlockBuilder, TestMempoolTxBuilder } from './test-helpers';
describe('api tests', () => {
let db: PgDataStore;
let client: PoolClient;
let api: ApiServer;
beforeEach(async () => {
process.env.PG_DATABASE = 'postgres';
await cycleMigrations();
db = await PgDataStore.connect();
client = await db.pool.connect();
api = await startApiServer({ datastore: db, chainId: ChainID.Testnet, httpLogLevel: 'silly' });
});
test('info block time', async () => {
const query1 = await supertest(api.server).get(`/extended/v1/info/network_block_times`);
expect(query1.status).toBe(200);
expect(query1.type).toBe('application/json');
expect(JSON.parse(query1.text)).toEqual({
testnet: { target_block_time: 120 },
mainnet: { target_block_time: 600 },
});
const query2 = await supertest(api.server).get(`/extended/v1/info/network_block_time/mainnet`);
expect(query2.status).toBe(200);
expect(query2.type).toBe('application/json');
expect(JSON.parse(query2.text)).toEqual({ target_block_time: 600 });
const query3 = await supertest(api.server).get(`/extended/v1/info/network_block_time/testnet`);
expect(query3.status).toBe(200);
expect(query3.type).toBe('application/json');
expect(JSON.parse(query3.text)).toEqual({ target_block_time: 120 });
const query4 = await supertest(api.server).get(`/extended/v1/info/network_block_time/badnet`);
expect(query4.status).toBe(400);
expect(query4.type).toBe('application/json');
expect(JSON.parse(query4.text)).toEqual({
error: '`network` param must be `testnet` or `mainnet`',
});
});
test('stx-supply', async () => {
const testAddr1 = 'testAddr1';
const dbBlock1: DbBlock = {
block_hash: '0x0123',
index_block_hash: '0x1234',
parent_index_block_hash: '0x00',
parent_block_hash: '0x5678',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 39486,
burn_block_hash: '0x1234',
burn_block_height: 123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const tx: DbTx = {
tx_id: '0x1234',
tx_index: 4,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.alloc(0),
index_block_hash: dbBlock1.index_block_hash,
block_hash: dbBlock1.block_hash,
block_height: dbBlock1.block_height,
burn_block_time: dbBlock1.burn_block_time,
parent_burn_block_time: 0,
type_id: DbTxTypeId.Coinbase,
coinbase_payload: Buffer.from('coinbase hi'),
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: '',
parent_block_hash: '',
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: testAddr1,
origin_hash_mode: 1,
event_count: 5,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const stxMintEvent1: DbStxEvent = {
event_index: 0,
tx_id: tx.tx_id,
tx_index: tx.tx_index,
block_height: tx.block_height,
canonical: true,
asset_event_type_id: DbAssetEventTypeId.Mint,
recipient: tx.sender_address,
event_type: DbEventTypeId.StxAsset,
amount: 230_000_000_000_000n,
};
const stxMintEvent2: DbStxEvent = {
...stxMintEvent1,
amount: 5_000_000_000_000n,
};
await db.updateBlock(client, dbBlock1);
await db.updateTx(client, tx);
await db.updateStxEvent(client, tx, stxMintEvent1);
await db.updateStxEvent(client, tx, stxMintEvent2);
const expectedTotalStx1 = stxMintEvent1.amount + stxMintEvent2.amount;
const result1 = await supertest(api.server).get(`/extended/v1/stx_supply`);
expect(result1.status).toBe(200);
expect(result1.type).toBe('application/json');
const expectedResp1 = {
unlocked_percent: '17.38',
total_stx: '1352464600.000000',
unlocked_stx: microStxToStx(expectedTotalStx1),
block_height: dbBlock1.block_height,
};
expect(JSON.parse(result1.text)).toEqual(expectedResp1);
// ensure burned STX reduce the unlocked stx supply
const stxBurnEvent1: DbStxEvent = {
event_index: 0,
tx_id: tx.tx_id,
tx_index: tx.tx_index,
block_height: tx.block_height,
canonical: true,
asset_event_type_id: DbAssetEventTypeId.Burn,
sender: tx.sender_address,
event_type: DbEventTypeId.StxAsset,
amount: 10_000_000_000_000n,
};
await db.updateStxEvent(client, tx, stxBurnEvent1);
const expectedTotalStx2 = stxMintEvent1.amount + stxMintEvent2.amount - stxBurnEvent1.amount;
const result2 = await supertest(api.server).get(`/extended/v1/stx_supply`);
expect(result2.status).toBe(200);
expect(result2.type).toBe('application/json');
const expectedResp2 = {
unlocked_percent: '16.64',
total_stx: '1352464600.000000',
unlocked_stx: microStxToStx(expectedTotalStx2),
block_height: dbBlock1.block_height,
};
expect(JSON.parse(result2.text)).toEqual(expectedResp2);
// ensure miner coinbase rewards are included
const minerReward1: DbMinerReward = {
block_hash: dbBlock1.block_hash,
index_block_hash: dbBlock1.index_block_hash,
from_index_block_hash: dbBlock1.index_block_hash,
mature_block_height: dbBlock1.block_height,
canonical: true,
recipient: testAddr1,
coinbase_amount: 15_000_000_000_000n,
tx_fees_anchored: 1_000_000_000_000n,
tx_fees_streamed_confirmed: 2_000_000_000_000n,
tx_fees_streamed_produced: 3_000_000_000_000n,
};
await db.updateMinerReward(client, minerReward1);
const expectedTotalStx3 =
stxMintEvent1.amount +
stxMintEvent2.amount -
stxBurnEvent1.amount +
minerReward1.coinbase_amount;
const result3 = await supertest(api.server).get(`/extended/v1/stx_supply`);
expect(result3.status).toBe(200);
expect(result3.type).toBe('application/json');
const expectedResp3 = {
unlocked_percent: '17.75',
total_stx: '1352464600.000000',
unlocked_stx: microStxToStx(expectedTotalStx3),
block_height: dbBlock1.block_height,
};
expect(JSON.parse(result3.text)).toEqual(expectedResp3);
const result4 = await supertest(api.server).get(`/extended/v1/stx_supply/total/plain`);
expect(result4.status).toBe(200);
expect(result4.type).toBe('text/plain');
expect(result4.text).toEqual('1352464600.000000');
const result5 = await supertest(api.server).get(`/extended/v1/stx_supply/circulating/plain`);
expect(result5.status).toBe(200);
expect(result5.type).toBe('text/plain');
expect(result5.text).toEqual(microStxToStx(expectedTotalStx3));
// test legacy endpoint response formatting
const result6 = await supertest(api.server).get(`/extended/v1/stx_supply/legacy_format`);
expect(result6.status).toBe(200);
expect(result6.type).toBe('application/json');
const expectedResp6 = {
unlockedPercent: '17.75',
totalStacks: '1352464600.000000',
totalStacksFormatted: '1,352,464,600.000000',
unlockedSupply: microStxToStx(expectedTotalStx3),
unlockedSupplyFormatted: new Intl.NumberFormat('en', {
minimumFractionDigits: STACKS_DECIMAL_PLACES,
}).format(parseInt(microStxToStx(expectedTotalStx3))),
blockHeight: dbBlock1.block_height.toString(),
};
expect(JSON.parse(result6.text)).toEqual(expectedResp6);
});
test('fetch reward slot holders', async () => {
const slotHolder1: DbRewardSlotHolder = {
canonical: true,
burn_block_hash: '0x1234',
burn_block_height: 2,
address: '1G4ayBXJvxZMoZpaNdZG6VyWwWq2mHpMjQ',
slot_index: 0,
};
const slotHolder2: DbRewardSlotHolder = {
canonical: true,
burn_block_hash: '0x1234',
burn_block_height: 2,
address: '1DDUAqoyXvhF4cxznN9uL6j9ok1oncsT2z',
slot_index: 1,
};
await db.updateBurnchainRewardSlotHolders({
burnchainBlockHash: '0x1234',
burnchainBlockHeight: 2,
slotHolders: [slotHolder1, slotHolder2],
});
const result = await supertest(api.server).get(`/extended/v1/burnchain/reward_slot_holders`);
expect(result.status).toBe(200);
expect(result.type).toBe('application/json');
const expectedResp1 = {
limit: 96,
offset: 0,
total: 2,
results: [
{
canonical: true,
burn_block_hash: '0x1234',
burn_block_height: 2,
address: '1DDUAqoyXvhF4cxznN9uL6j9ok1oncsT2z',
slot_index: 1,
},
{
canonical: true,
burn_block_hash: '0x1234',
burn_block_height: 2,
address: '1G4ayBXJvxZMoZpaNdZG6VyWwWq2mHpMjQ',
slot_index: 0,
},
],
};
expect(JSON.parse(result.text)).toEqual(expectedResp1);
});
test('fetch reward slot holder entries for BTC address', async () => {
const slotHolder1: DbRewardSlotHolder = {
canonical: true,
burn_block_hash: '0x1234',
burn_block_height: 2,
address: '1G4ayBXJvxZMoZpaNdZG6VyWwWq2mHpMjQ',
slot_index: 0,
};
const slotHolder2: DbRewardSlotHolder = {
canonical: true,
burn_block_hash: '0x1234',
burn_block_height: 2,
address: '1DDUAqoyXvhF4cxznN9uL6j9ok1oncsT2z',
slot_index: 1,
};
await db.updateBurnchainRewardSlotHolders({
burnchainBlockHash: '0x1234',
burnchainBlockHeight: 2,
slotHolders: [slotHolder1, slotHolder2],
});
const result = await supertest(api.server).get(
`/extended/v1/burnchain/reward_slot_holders/${slotHolder1.address}`
);
expect(result.status).toBe(200);
expect(result.type).toBe('application/json');
const expectedResp1 = {
limit: 96,
offset: 0,
total: 1,
results: [
{
canonical: true,
burn_block_hash: '0x1234',
burn_block_height: 2,
address: '1G4ayBXJvxZMoZpaNdZG6VyWwWq2mHpMjQ',
slot_index: 0,
},
],
};
expect(JSON.parse(result.text)).toEqual(expectedResp1);
});
test('fetch reward slot holder entries for mainnet STX address', async () => {
const mainnetStxAddr = 'SP2JKEZC09WVMR33NBSCWQAJC5GS590RP1FR9CK55';
const mainnetBtcAddr = '1G4ayBXJvxZMoZpaNdZG6VyWwWq2mHpMjQ';
const slotHolder1: DbRewardSlotHolder = {
canonical: true,
burn_block_hash: '0x1234',
burn_block_height: 2,
address: mainnetBtcAddr,
slot_index: 0,
};
const slotHolder2: DbRewardSlotHolder = {
canonical: true,
burn_block_hash: '0x1234',
burn_block_height: 2,
address: '1DDUAqoyXvhF4cxznN9uL6j9ok1oncsT2z',
slot_index: 1,
};
await db.updateBurnchainRewardSlotHolders({
burnchainBlockHash: '0x1234',
burnchainBlockHeight: 2,
slotHolders: [slotHolder1, slotHolder2],
});
const result = await supertest(api.server).get(
`/extended/v1/burnchain/reward_slot_holders/${mainnetStxAddr}`
);
expect(result.status).toBe(200);
expect(result.type).toBe('application/json');
const expectedResp1 = {
limit: 96,
offset: 0,
total: 1,
results: [
{
canonical: true,
burn_block_hash: '0x1234',
burn_block_height: 2,
address: '1G4ayBXJvxZMoZpaNdZG6VyWwWq2mHpMjQ',
slot_index: 0,
},
],
};
expect(JSON.parse(result.text)).toEqual(expectedResp1);
});
test('fetch burnchain rewards', async () => {
const addr1 = '1G4ayBXJvxZMoZpaNdZG6VyWwWq2mHpMjQ';
const addr2 = '1DDUAqoyXvhF4cxznN9uL6j9ok1oncsT2z';
const reward1: DbBurnchainReward = {
canonical: true,
burn_block_hash: '0x1234',
burn_block_height: 200,
burn_amount: 2000n,
reward_recipient: addr1,
reward_amount: 900n,
reward_index: 0,
};
const reward2: DbBurnchainReward = {
canonical: true,
burn_block_hash: '0x1234',
burn_block_height: 200,
burn_amount: 2001n,
reward_recipient: addr1,
reward_amount: 901n,
reward_index: 1,
};
const reward3: DbBurnchainReward = {
canonical: true,
burn_block_hash: '0x2345',
burn_block_height: 201,
burn_amount: 3001n,
reward_recipient: addr2,
reward_amount: 902n,
reward_index: 0,
};
const reward4: DbBurnchainReward = {
...reward3,
reward_index: 1,
};
const reward5: DbBurnchainReward = {
...reward3,
reward_index: 2,
};
await db.updateBurnchainRewards({
burnchainBlockHash: reward1.burn_block_hash,
burnchainBlockHeight: reward1.burn_block_height,
rewards: [reward1, reward2],
});
await db.updateBurnchainRewards({
burnchainBlockHash: reward3.burn_block_hash,
burnchainBlockHeight: reward3.burn_block_height,
rewards: [reward3, reward4, reward5],
});
const rewardResult = await supertest(api.server).get(
`/extended/v1/burnchain/rewards?limit=3&offset=0`
);
expect(rewardResult.status).toBe(200);
expect(rewardResult.type).toBe('application/json');
const expectedResp1 = {
limit: 3,
offset: 0,
results: [
{
canonical: true,
burn_block_hash: '0x2345',
burn_block_height: 201,
burn_amount: '3001',
reward_recipient: '1DDUAqoyXvhF4cxznN9uL6j9ok1oncsT2z',
reward_amount: '902',
reward_index: 2,
},
{
canonical: true,
burn_block_hash: '0x2345',
burn_block_height: 201,
burn_amount: '3001',
reward_recipient: '1DDUAqoyXvhF4cxznN9uL6j9ok1oncsT2z',
reward_amount: '902',
reward_index: 1,
},
{
canonical: true,
burn_block_hash: '0x2345',
burn_block_height: 201,
burn_amount: '3001',
reward_recipient: '1DDUAqoyXvhF4cxznN9uL6j9ok1oncsT2z',
reward_amount: '902',
reward_index: 0,
},
],
};
expect(JSON.parse(rewardResult.text)).toEqual(expectedResp1);
});
test('fetch burnchain total rewards for BTC address', async () => {
const addr = '1G4ayBXJvxZMoZpaNdZG6VyWwWq2mHpMjQ';
const reward1: DbBurnchainReward = {
canonical: true,
burn_block_hash: '0x1234',
burn_block_height: 200,
burn_amount: 2000n,
reward_recipient: addr,
reward_amount: 1000n,
reward_index: 0,
};
const reward2: DbBurnchainReward = {
canonical: true,
burn_block_hash: '0x2234',
burn_block_height: 201,
burn_amount: 2000n,
reward_recipient: addr,
reward_amount: 1001n,
reward_index: 0,
};
const reward3: DbBurnchainReward = {
canonical: true,
burn_block_hash: '0x3234',
burn_block_height: 202,
burn_amount: 2000n,
reward_recipient: addr,
reward_amount: 1002n,
reward_index: 0,
};
await db.updateBurnchainRewards({
burnchainBlockHash: reward1.burn_block_hash,
burnchainBlockHeight: reward1.burn_block_height,
rewards: [reward1],
});
await db.updateBurnchainRewards({
burnchainBlockHash: reward2.burn_block_hash,
burnchainBlockHeight: reward2.burn_block_height,
rewards: [reward2],
});
await db.updateBurnchainRewards({
burnchainBlockHash: reward3.burn_block_hash,
burnchainBlockHeight: reward3.burn_block_height,
rewards: [reward3],
});
const rewardResult = await supertest(api.server).get(
`/extended/v1/burnchain/rewards/${addr}/total`
);
expect(rewardResult.status).toBe(200);
expect(rewardResult.type).toBe('application/json');
const expectedResp1 = {
reward_recipient: '1G4ayBXJvxZMoZpaNdZG6VyWwWq2mHpMjQ',
reward_amount: '3003',
};
expect(JSON.parse(rewardResult.text)).toEqual(expectedResp1);
});
test('fetch burnchain rewards for BTC address', async () => {
const addr1 = '1G4ayBXJvxZMoZpaNdZG6VyWwWq2mHpMjQ';
const reward1: DbBurnchainReward = {
canonical: true,
burn_block_hash: '0x1234',
burn_block_height: 200,
burn_amount: 2000n,
reward_recipient: addr1,
reward_amount: 900n,
reward_index: 0,
};
await db.updateBurnchainRewards({
burnchainBlockHash: reward1.burn_block_hash,
burnchainBlockHeight: reward1.burn_block_height,
rewards: [reward1],
});
const rewardResult = await supertest(api.server).get(`/extended/v1/burnchain/rewards/${addr1}`);
expect(rewardResult.status).toBe(200);
expect(rewardResult.type).toBe('application/json');
const expectedResp1 = {
limit: 96,
offset: 0,
results: [
{
canonical: true,
burn_block_hash: '0x1234',
burn_block_height: 200,
burn_amount: '2000',
reward_recipient: '1G4ayBXJvxZMoZpaNdZG6VyWwWq2mHpMjQ',
reward_amount: '900',
reward_index: 0,
},
],
};
expect(JSON.parse(rewardResult.text)).toEqual(expectedResp1);
});
test('fetch burnchain rewards for mainnet STX address', async () => {
const mainnetStxAddr = 'SP2JKEZC09WVMR33NBSCWQAJC5GS590RP1FR9CK55';
const mainnetBtcAddr = '1G4ayBXJvxZMoZpaNdZG6VyWwWq2mHpMjQ';
const reward1: DbBurnchainReward = {
canonical: true,
burn_block_hash: '0x1234',
burn_block_height: 200,
burn_amount: 2000n,
reward_recipient: mainnetBtcAddr,
reward_amount: 900n,
reward_index: 0,
};
await db.updateBurnchainRewards({
burnchainBlockHash: reward1.burn_block_hash,
burnchainBlockHeight: reward1.burn_block_height,
rewards: [reward1],
});
const rewardResult = await supertest(api.server).get(
`/extended/v1/burnchain/rewards/${mainnetStxAddr}`
);
expect(rewardResult.status).toBe(200);
expect(rewardResult.type).toBe('application/json');
const expectedResp1 = {
limit: 96,
offset: 0,
results: [
{
canonical: true,
burn_block_hash: '0x1234',
burn_block_height: 200,
burn_amount: '2000',
reward_recipient: mainnetBtcAddr,
reward_amount: '900',
reward_index: 0,
},
],
};
expect(JSON.parse(rewardResult.text)).toEqual(expectedResp1);
});
test('fetch burnchain rewards for testnet STX address', async () => {
const testnetStxAddr = 'STDFV22FCWGHB7B5563BHXVMCSYM183PRB9DH090';
const testnetBtcAddr = 'mhyfanXuwsCMrixyQcCDzh28iHEdtQzZEm';
const reward1: DbBurnchainReward = {
canonical: true,
burn_block_hash: '0x1234',
burn_block_height: 200,
burn_amount: 2000n,
reward_recipient: testnetBtcAddr,
reward_amount: 900n,
reward_index: 0,
};
await db.updateBurnchainRewards({
burnchainBlockHash: reward1.burn_block_hash,
burnchainBlockHeight: reward1.burn_block_height,
rewards: [reward1],
});
const rewardResult = await supertest(api.server).get(
`/extended/v1/burnchain/rewards/${testnetStxAddr}`
);
expect(rewardResult.status).toBe(200);
expect(rewardResult.type).toBe('application/json');
const expectedResp1 = {
limit: 96,
offset: 0,
results: [
{
canonical: true,
burn_block_hash: '0x1234',
burn_block_height: 200,
burn_amount: '2000',
reward_recipient: testnetBtcAddr,
reward_amount: '900',
reward_index: 0,
},
],
};
expect(JSON.parse(rewardResult.text)).toEqual(expectedResp1);
});
test('fetch burnchain rewards for testnet STX address', async () => {
const testnetStxAddr = 'STDFV22FCWGHB7B5563BHXVMCSYM183PRB9DH090';
const testnetBtcAddr = 'mhyfanXuwsCMrixyQcCDzh28iHEdtQzZEm';
const reward1: DbBurnchainReward = {
canonical: true,
burn_block_hash: '0x1234',
burn_block_height: 200,
burn_amount: 2000n,
reward_recipient: testnetBtcAddr,
reward_amount: 900n,
reward_index: 0,
};
await db.updateBurnchainRewards({
burnchainBlockHash: reward1.burn_block_hash,
burnchainBlockHeight: reward1.burn_block_height,
rewards: [reward1],
});
const rewardResult = await supertest(api.server).get(
`/extended/v1/burnchain/rewards/${testnetStxAddr}`
);
expect(rewardResult.status).toBe(200);
expect(rewardResult.type).toBe('application/json');
const expectedResp1 = {
limit: 96,
offset: 0,
results: [
{
canonical: true,
burn_block_hash: '0x1234',
burn_block_height: 200,
burn_amount: '2000',
reward_recipient: testnetBtcAddr,
reward_amount: '900',
reward_index: 0,
},
],
};
expect(JSON.parse(rewardResult.text)).toEqual(expectedResp1);
});
test('fetch tx list details', async () => {
const mempoolTx: DbMempoolTx = {
pruned: false,
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000000',
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.from('test-raw-tx'),
type_id: DbTxTypeId.Coinbase,
status: DbTxStatus.Pending,
receipt_time: 1594307695,
coinbase_payload: Buffer.from('coinbase hi'),
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'sender-addr',
origin_hash_mode: 1,
};
await db.updateMempoolTxs({ mempoolTxs: [mempoolTx] });
const source_code = `;; pg-mdomains-v1\n;;\n;; Decentralized domain names manager for Paradigma\n;; To facilitate acquisition of Stacks decentralized domain names\n(use-trait sip-010-trait 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.sip-010-trait )\n(use-trait token-trait 'SP3YK7KWMYRCDMV5M4792T0T7DERQXHJJGGEPV1N8.paradigma-token-trait-v1.paradigma-token-trait)\n\n\n;; constants\n(define-constant ERR_INSUFFICIENT_FUNDS 101)\n(define-constant ERR_UNAUTHORIZED 109)\n(define-constant ERR_NAME_PREORDER_FUNDS_INSUFFICIENT 203) ;; transfer to sponsored \n(define-constant ERR_DOMAINNAME_MANAGER_NOT_FOUND 501)\n\n;; set constant for contract owner, used for updating token-uri\n(define-constant CONTRACT_OWNER tx-sender)\n\n;; initial value for domain wallet, set to this contract until initialized\n(define-data-var domainWallet principal 'SP3YK7KWMYRCDMV5M4792T0T7DERQXHJJGGEPV1N8)\n\n(define-data-var platformDomainWallet principal 'SPRK2JVQ988PYT19JSAJNR3K9YZAZGVY04XMC2Z7) ;; Wallet where to transfer share fee services\n\n;; Manage domain name service fees\n;; by accepted tokens\n(define-map DomainServiceFeeIndex\n {\n serviceId: uint\n }\n {\n tokenSymbol: (string-ascii 32),\n } \n)\n\n(define-read-only (get-domain-service-fee-index (id uint))\n (map-get? DomainServiceFeeIndex\n {\n serviceId: id\n }\n ) \n)\n\n(define-map DomainServiceFee\n {\n tokenSymbol: (string-ascii 32),\n }\n {\n fee: uint\n }\n)\n(define-read-only (get-domain-service-fee (tokenSymbol (string-ascii 32)))\n (unwrap-panic (get fee \n (map-get? DomainServiceFee\n {tokenSymbol: tokenSymbol}\n )\n )\n )\n)\n(define-data-var domainServiceFeeCount uint u0)\n(define-read-only (get-domain-service-fee-count)\n (var-get domainServiceFeeCount)\n)\n\n;; Set reference info for domain service fee\n;; protected function to update domain service fee variable\n(define-public (create-domain-service-fee \n (tokenSymbol (string-ascii 32))\n (fee uint) \n )\n (begin\n (if (is-authorized-domain) \n (if\n (is-none \n (map-get? DomainServiceFee\n {\n tokenSymbol: tokenSymbol\n }\n ) \n )\n (begin\n (var-set domainServiceFeeCount (+ (var-get domainServiceFeeCount) u1))\n (map-insert DomainServiceFeeIndex\n { \n serviceId: (var-get domainServiceFeeCount)\n }\n {\n tokenSymbol: tokenSymbol\n } \n )\n (map-insert DomainServiceFee \n {\n tokenSymbol: tokenSymbol\n } \n {\n fee: fee\n }\n ) \n (ok true)\n )\n (begin\n (ok \n (map-set DomainServiceFee \n {\n tokenSymbol: tokenSymbol\n } \n {\n fee: fee\n }\n )\n )\n )\n )\n (err ERR_UNAUTHORIZED)\n )\n )\n)\n\n;; check if contract caller is contract owner\n(define-private (is-authorized-owner)\n (is-eq contract-caller CONTRACT_OWNER)\n)\n\n;; Token flow management\n\n;; Stores participants DomainName service sell\n\n;; (define-data-var domainNameManagerCount -list (list 2000 uint) (list))\n\n(define-data-var domainNameManagerCount uint u0)\n\n(define-read-only (get-domain-name-manager-count)\n (var-get domainNameManagerCount)\n)\n(define-map DomainNameManagersIndex\n { domainNMId: uint }\n {\n nameSpace: (buff 48), ;; domain namespace defined in Blockchain Name Service (BNS) like .app\n domainName: (buff 48) ;; domain name under a namespace like xck in xck.app\n }\n)\n\n(define-read-only (get-domain-name-managers-index (id uint))\n (map-get? DomainNameManagersIndex\n {\n domainNMId: id\n }\n ) \n)\n\n(define-map DomainNameManagers\n {\n nameSpace: (buff 48), ;; domain namespace defined in Blockchain Name Service (BNS) like .app\n domainName: (buff 48) ;; domain name under a namespace like xck in xck.app\n }\n {\n domainNameWallet: principal, ;; DomainName manager account - branding and domainName token\n domainNameFeePerc: uint, ;; DomainName share percentage of fee (ie u10)\n domainNameFeeTokenMint: uint, ;; Tokens considered reciprocity to domainName token\n domainNameTokenSymbol: (string-utf8 5), ;; Token Symbol used to mint domainName token\n sponsoredWallet: principal, ;; Sponsored institution account\n sponsoredFeePerc: uint, ;; Sponsored share percentage of fee (ie u10)\n sponsoredDID: (string-utf8 256), ;; Sponsored Stacks ID\n sponsoredUri: (string-utf8 256), ;; Sponsored website Uri\n referencerFeeTokenMint: uint ;; Tokens for promoters references as reciprocity \n }\n)\n\n;; returns set domain wallet principal\n(define-read-only (get-domain-wallet)\n (var-get domainWallet)\n)\n\n;; checks if caller is Auth contract\n(define-private (is-authorized-auth) \n (is-eq contract-caller 'SP3YK7KWMYRCDMV5M4792T0T7DERQXHJJGGEPV1N8)\n) \n\n;; protected function to update domain wallet variable\n(define-public (set-domain-wallet (newDomainWallet principal))\n (begin\n (asserts! (is-authorized-auth) (err ERR_UNAUTHORIZED)) \n (ok (var-set domainWallet newDomainWallet))\n )\n)\n\n;; check if contract caller is domain wallet\n(define-private (is-authorized-domain)\n (is-eq contract-caller (var-get domainWallet))\n)\n\n;; Set reference info for domainName managers\n(define-public (create-domainname-manager \n (nameSpace (buff 48))\n (domainName (buff 48)) \n (domainNameWallet principal) \n (domainNameFeePerc uint) \n (domainNameFeeTokenMint uint) \n (tokenSymbol (string-utf8 5))\n (sponsoredWallet principal) \n (sponsoredFeePerc uint)\n (sponsoredDID (string-utf8 256))\n (sponsoredUri (string-utf8 256))\n (referencerFeeTokenMint uint)\n )\n (begin\n (if (is-authorized-domain) \n (if\n (is-none \n (map-get? DomainNameManagers \n {\n nameSpace: nameSpace,\n domainName: domainName\n }\n ) \n )\n (begin\n (var-set domainNameManagerCount (+ (var-get domainNameManagerCount) u1))\n (map-insert DomainNameManagersIndex\n { \n domainNMId: (var-get domainNameManagerCount)\n }\n {\n nameSpace: nameSpace,\n domainName: domainName\n } \n )\n (map-insert DomainNameManagers \n {\n nameSpace: nameSpace,\n domainName: domainName\n } \n {\n domainNameWallet: domainNameWallet,\n domainNameFeePerc: domainNameFeePerc,\n domainNameFeeTokenMint: domainNameFeeTokenMint,\n domainNameTokenSymbol: tokenSymbol,\n sponsoredWallet: sponsoredWallet,\n sponsoredFeePerc: sponsoredFeePerc,\n sponsoredDID: sponsoredDID,\n sponsoredUri: sponsoredUri,\n referencerFeeTokenMint: referencerFeeTokenMint\n }\n ) \n (ok true)\n )\n (begin\n (ok \n (map-set DomainNameManagers \n {\n nameSpace: nameSpace,\n domainName: domainName\n } \n {\n domainNameWallet: domainNameWallet,\n domainNameFeePerc: domainNameFeePerc,\n domainNameFeeTokenMint: domainNameFeeTokenMint,\n domainNameTokenSymbol: tokenSymbol,\n sponsoredWallet: sponsoredWallet,\n sponsoredFeePerc: sponsoredFeePerc,\n sponsoredDID: sponsoredDID,\n sponsoredUri: sponsoredUri,\n referencerFeeTokenMint: referencerFeeTokenMint\n }\n )\n )\n )\n )\n (err ERR_UNAUTHORIZED)\n )\n )\n)\n\n;; Gets the principal for domainName managers\n(define-read-only (get-ref-domainname-manager (nameSpace (buff 48)) (domainName (buff 48)))\n (ok (unwrap! (map-get? DomainNameManagers \n {\n nameSpace: nameSpace,\n domainName: domainName\n }\n )\n (err ERR_DOMAINNAME_MANAGER_NOT_FOUND)\n )\n )\n)\n\n\n;; Makes the name-preorder\n(define-public (bns-name-preorder (hashedSaltedFqn (buff 20)) (stxToBurn uint) (paymentSIP010Trait <sip-010-trait>) (reciprocityTokenTrait <token-trait>) (referencerWallet principal))\n (begin\n (asserts! (> (stx-get-balance tx-sender) stxToBurn) (err ERR_NAME_PREORDER_FUNDS_INSUFFICIENT))\n (let \n (\n (symbol (unwrap-panic (contract-call? paymentSIP010Trait get-symbol)))\n (fee (get-domain-service-fee symbol))\n (toBurn (- stxToBurn fee))\n (tr (order-to-register-domain tx-sender fee 0x616c6c 0x616c6c 0x737461636b73 paymentSIP010Trait reciprocityTokenTrait referencerWallet)) ;; Includes subdomain:all namespace:all name:stacks as domainnames\n )\n (ok (try! (contract-call? 'SP000000000000000000002Q6VF78.bns name-preorder hashedSaltedFqn toBurn)))\n ) \n )\n)\n\n;;
Gives the order to register a domain and subdomain associated to a domainName and transfers to the domain managers\n(define-public (order-to-register-domain (sender principal) (fee uint) (nameSpace (buff 48)) (domainName (buff 48)) (subDomain (buff 48)) \n (paymentSIP010Trait <sip-010-trait>) (reciprocityTokenTrait <token-trait>) (referencerWallet principal))\n (begin\n (asserts! (is-eq tx-sender sender) (err ERR_UNAUTHORIZED))\n (asserts! (> (unwrap-panic (contract-call? paymentSIP010Trait get-balance tx-sender)) fee) (err ERR_INSUFFICIENT_FUNDS))\n (let \n (\n (domainNameRef \n (unwrap-panic (map-get? DomainNameManagers \n {\n nameSpace: nameSpace,\n domainName: domainName\n }\n )\n )\n )\n (sponsoredFeePerc \n (get sponsoredFeePerc domainNameRef)\n )\n (sponsoredWallet \n (get sponsoredWallet domainNameRef)\n )\n (domainNameFeePerc \n (get domainNameFeePerc domainNameRef)\n ) \n (domainNameWallet \n (get domainNameWallet domainNameRef)\n )\n (domainNameFeeTokenMint \n (get domainNameFeeTokenMint domainNameRef)\n )\n (referencerFeeTokenMint\n (get referencerFeeTokenMint domainNameRef))\n (transferToSponsored (/ (* sponsoredFeePerc fee) u100) )\n (transferToDomainManager (/ (* domainNameFeePerc fee) u100))\n (transferToPlatform (/ (* (- u100 (+ domainNameFeePerc sponsoredFeePerc ) ) fee) u100))\n (platformDWallet (get-platform-domain-wallet))\n ) \n ;; transfer to sponsored \n (if (> transferToSponsored u0)\n (unwrap-panic (contract-call? paymentSIP010Trait transfer \n transferToSponsored \n sender \n sponsoredWallet\n none\n )\n )\n true\n )\n ;; transfer to domain name manager\n (if (> transferToDomainManager u0)\n (unwrap-panic (contract-call? paymentSIP010Trait transfer\n transferToDomainManager\n sender\n domainNameWallet\n none\n )\n )\n true\n )\n ;; transfer to platform manager\n (if (> transferToPlatform u0)\n (unwrap-panic (contract-call? paymentSIP010Trait transfer\n transferToPlatform\n sender \n platformDWallet\n none\n )\n )\n true\n )\n ;; mint token to sender as reciprocity\n (if (> domainNameFeeTokenMint u0)\n (unwrap-panic (as-contract (contract-call? reciprocityTokenTrait \n mint \n domainNameFeeTokenMint\n sender\n )\n )\n )\n true\n )\n ;; mint token for referencer (if there is) as reciprocity\n (if (> referencerFeeTokenMint u0)\n (unwrap-panic (as-contract (contract-call? reciprocityTokenTrait \n mint \n referencerFeeTokenMint\n referencerWallet\n )\n )\n )\n true\n )\n )\n (ok true)\n )\n)\n\n;; returns set domain wallet principal\n(define-read-only (get-platform-domain-wallet)\n (var-get platformDomainWallet)\n)\n;; protected function to update domain wallet variable\n(define-public (set-platform-domain-wallet (newPDomainWallet principal))\n (begin\n (asserts! (is-authorized-auth) (err ERR_UNAUTHORIZED)) \n (ok (var-set platformDomainWallet newPDomainWallet))\n )\n)`;
const abi = `{\"maps\":[{\"key\":{\"tuple\":[{\"name\":\"domainName\",\"type\":{\"buffer\":{\"length\":48}}},{\"name\":\"nameSpace\",\"type\":{\"buffer\":{\"length\":48}}}]},\"name\":\"DomainNameManagers\",\"value\":{\"tuple\":[{\"name\":\"domainNameFeePerc\",\"type\":\"uint128\"},{\"name\":\"domainNameFeeTokenMint\",\"type\":\"uint128\"},{\"name\":\"domainNameTokenSymbol\",\"type\":{\"string-utf8\":{\"length\":5}}},{\"name\":\"domainNameWallet\",\"type\":\"principal\"},{\"name\":\"referencerFeeTokenMint\",\"type\":\"uint128\"},{\"name\":\"sponsoredDID\",\"type\":{\"string-utf8\":{\"length\":256}}},{\"name\":\"sponsoredFeePerc\",\"type\":\"uint128\"},{\"name\":\"sponsoredUri\",\"type\":{\"string-utf8\":{\"length\":256}}},{\"name\":\"sponsoredWallet\",\"type\":\"principal\"}]}},{\"key\":{\"tuple\":[{\"name\":\"domainNMId\",\"type\":\"uint128\"}]},\"name\":\"DomainNameManagersIndex\",\"value\":{\"tuple\":[{\"name\":\"domainName\",\"type\":{\"buffer\":{\"length\":48}}},{\"name\":\"nameSpace\",\"type\":{\"buffer\":{\"length\":48}}}]}},{\"key\":{\"tuple\":[{\"name\":\"tokenSymbol\",\"type\":{\"string-ascii\":{\"length\":32}}}]},\"name\":\"DomainServiceFee\",\"value\":{\"tuple\":[{\"name\":\"fee\",\"type\":\"uint128\"}]}},{\"key\":{\"tuple\":[{\"name\":\"serviceId\",\"type\":\"uint128\"}]},\"name\":\"DomainServiceFeeIndex\",\"value\":{\"tuple\":[{\"name\":\"tokenSymbol\",\"type\":{\"string-ascii\":{\"length\":32}}}]}}],\"functions\":[{\"args\":[],\"name\":\"is-authorized-auth\",\"access\":\"private\",\"outputs\":{\"type\":\"bool\"}},{\"args\":[],\"name\":\"is-authorized-domain\",\"access\":\"private\",\"outputs\":{\"type\":\"bool\"}},{\"args\":[],\"name\":\"is-authorized-owner\",\"access\":\"private\",\"outputs\":{\"type\":\"bool\"}},{\"args\":[{\"name\":\"hashedSaltedFqn\",\"type\":{\"buffer\":{\"length\":20}}},{\"name\":\"stxToBurn\",\"type\":\"uint128\"},{\"name\":\"paymentSIP010Trait\",\"type\":\"trait_reference\"},{\"name\":\"reciprocityTokenTrait\",\"type\":\"trait_reference\"},{\"name\":\"referencerWallet\",\"type\":\"principal\"}],\"name\":\"bns-name-preorder\",\"access\":\"public\",\"outputs\":{\"type\":{\"response\":{\"ok\":\"uint128\",\"error\":\"int128\"}}}},{\"args\":[{\"name\":\"tokenSymbol\",\"type\":{\"string-ascii\":{\"length\":32}}},{\"name\":\"fee\",\"type\":\"uint128\"}],\"name\":\"create-domain-service-fee\",\"access\":\"public\",\"outputs\":{\"type\":{\"response\":{\"ok\":\"bool\",\"error\":\"int128\"}}}},{\"args\":[{\"name\":\"nameSpace\",\"type\":{\"buffer\":{\"length\":48}}},{\"name\":\"domainName\",\"type\":{\"buffer\":{\"length\":48}}},{\"name\":\"domainNameWallet\",\"type\":\"principal\"},{\"name\":\"domainNameFeePerc\",\"type\":\"uint128\"},{\"name\":\"domainNameFeeTokenMint\",\"type\":\"uint128\"},{\"name\":\"tokenSymbol\",\"type\":{\"string-utf8\":{\"length\":5}}},{\"name\":\"sponsoredWallet\",\"type\":\"principal\"},{\"name\":\"sponsoredFeePerc\",\"type\":\"uint128\"},{\"name\":\"sponsoredDID\",\"type\":{\"string-utf8\":{\"length\":256}}},{\"name\":\"sponsoredUri\",\"type\":{\"string-utf8\":{\"length\":256}}},{\"name\":\"referencerFeeTokenMint\",\"type\":\"uint128\"}],\"name\":\"create-domainname-manager\",\"access\":\"public\",\"outputs\":{\"type\":{\"response\":{\"ok\":\"bool\",\"error\":\"int128\"}}}},{\"args\":[{\"name\":\"sender\",\"type\":\"principal\"},{\"name\":\"fee\",\"type\":\"uint128\"},{\"name\":\"nameSpace\",\"type\":{\"buffer\":{\"length\":48}}},{\"name\":\"domainName\",\"type\":{\"buffer\":{\"length\":48}}},{\"name\":\"subDomain\",\"type\":{\"buffer\":{\"length\":48}}},{\"name\":\"paymentSIP010Trait\",\"type\":\"trait_reference\"},{\"name\":\"reciprocityTokenTrait\",\"type\":\"trait_reference\"},{\"name\":\"referencerWallet\",\"type\":\"principal\"}],\"name\":\"order-to-register-domain\",\"access\":\"public\",\"outputs\":{\"type\":{\"response\":{\"ok\":\"bool\",\"error\":\"int128\"}}}},{\"args\":[{\"name\":\"newDomainWallet\",\"type\":\"principal\"}],\"name\":\"set-domain-wallet\",\"access\":\"public\",\"outputs\":{\"type\":{\"response\":{\"ok\":\"bool\",\"error\":\"int128\"}}}},{\"args\":[{\"name\":\"newPDomainWallet\",\"type\":\"principal\"}],\"name\":\"set-platform-domain-wallet\",\"access\":\"public\",\"outputs\":{\"type\":{\"response\":{\"ok\":\"bool\",\"error\":\"int128\"}}}},{\"args\":[],\"name\":\"get-domain-name-manager-count\",\"access\":\"read_only\",\"outputs\":{\"type\":\"uint128\"}},{\"args\":[{\"name\":\"id\",\"type\":\"uint128\"}],\"name\":\"get-domain-name-managers-index\",\"access\":\"read_only\",\"outputs\":{\"type\":{\"optional\":{\"tuple\":[{\"name\":\"domainName\",\"type\":{\"buffer\":{\"length\":48}}},{\"name\":\"nameSpace\",\"type\":{\"buffer\":{\"length\":48}}}]}}}},{\"args\":[{\"name\":\"tokenSymbol\",\"type\":{\"string-ascii\":{\"length\":32}}}],\"name\":\"get-domain-service-fee\",\"access\":\"read_only\",\"outputs\":{\"type\":\"uint128\"}},{\"args\":[],\"name\":\"get-domain-service-fee-count\",\"access\":\"read_only\",\"outputs\":{\"type\":\"uint128\"}},{\"args\":[{\"name\":\"id\",\"type\":\"uint128\"}],\"name\":\"get-domain-service-fee-index\",\"access\":\"read_only\",\"outputs\":{\"type\":{\"optional\":{\"tuple\":[{\"name\":\"tokenSymbol\",\"type\":{\"string-ascii\":{\"length\":32}}}]}}}},{\"args\":[],\"name\":\"get-domain-wallet\",\"access\":\"read_only\",\"outputs\":{\"type\":\"principal\"}},{\"args\":[],\"name\":\"get-platform-domain-wallet\",\"access\":\"read_only\",\"outputs\":{\"type\":\"principal\"}},{\"args\":[{\"name\":\"nameSpace\",\"type\":{\"buffer\":{\"length\":48}}},{\"name\":\"domainName\",\"type\":{\"buffer\":{\"length\":48}}}],\"name\":\"get-ref-domainname-manager\",\"access\":\"read_only\",\"outputs\":{\"type\":{\"response\":{\"ok\":{\"tuple\":[{\"name\":\"domainNameFeePerc\",\"type\":\"uint128\"},{\"name\":\"domainNameFeeTokenMint\",\"type\":\"uint128\"},{\"name\":\"domainNameTokenSymbol\",\"type\":{\"string-utf8\":{\"length\":5}}},{\"name\":\"domainNameWallet\",\"type\":\"principal\"},{\"name\":\"referencerFeeTokenMint\",\"type\":\"uint128\"},{\"name\":\"sponsoredDID\",\"type\":{\"string-utf8\":{\"length\":256}}},{\"name\":\"sponsoredFeePerc\",\"type\":\"uint128\"},{\"name\":\"sponsoredUri\",\"type\":{\"string-utf8\":{\"length\":256}}},{\"name\":\"sponsoredWallet\",\"type\":\"principal\"}]},\"error\":\"int128\"}}}}],\"variables\":[{\"name\":\"CONTRACT_OWNER\",\"type\":\"principal\",\"access\":\"constant\"},{\"name\":\"ERR_DOMAINNAME_MANAGER_NOT_FOUND\",\"type\":\"int128\",\"access\":\"constant\"},{\"name\":\"ERR_INSUFFICIENT_FUNDS\",\"type\":\"int128\",\"access\":\"constant\"},{\"name\":\"ERR_NAME_PREORDER_FUNDS_INSUFFICIENT\",\"type\":\"int128\",\"access\":\"constant\"},{\"name\":\"ERR_UNAUTHORIZED\",\"type\":\"int128\",\"access\":\"constant\"},{\"name\":\"domainNameManagerCount\",\"type\":\"uint128\",\"access\":\"variable\"},{\"name\":\"domainServiceFeeCount\",\"type\":\"uint128\",\"access\":\"variable\"},{\"name\":\"domainWallet\",\"type\":\"principal\",\"access\":\"variable\"},{\"name\":\"platformDomainWallet\",\"type\":\"principal\",\"access\":\"variable\"}],\"fungible_tokens\":[],\"non_fungible_tokens\":[]}`;
const tx1: DbTx = {
type_id: DbTxTypeId.ContractCall,
tx_id: '0x8407751d1a8d11ee986aca32a6459d9cd798283a12e048ebafcd4cc7dadb29af',
anchor_mode: DbTxAnchorMode.Any,
nonce: 0,
raw_tx: Buffer.alloc(0),
canonical: true,
microblock_canonical: true,
microblock_sequence: 2147483647,
microblock_hash: '',
parent_index_block_hash: '',
parent_block_hash: '',
block_height: 1,
tx_index: 33,
index_block_hash: '0xd10ccecfd7ac9e5f8a10de0532fac028559b31a6ff494d82147f6297fb663139',
block_hash: '0xd10ccecfd7ac9e5f8a10de0532fac028559b31a6ff494d82147f6297fb663139',
burn_block_time: 1637003433,
parent_burn_block_time: 1637002470,
status: DbTxStatus.Success,
raw_result: '0x0100000000000000000000000000000001', // u1
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 139200n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'SPX3DV9X9CGA8P14B3CMP2X8DBW6ZDXEAXDNPTER',
origin_hash_mode: 1,
event_count: 6,
execution_cost_read_count: 55,
execution_cost_read_length: 88420,
execution_cost_runtime: 116256000,
execution_cost_write_count: 9,
execution_cost_write_length: 339,
contract_call_contract_id: 'SP3YK7KWMYRCDMV5M4792T0T7DERQXHJJGGEPV1N8.pg-mdomains-v1',
contract_call_function_name: 'bns-name-preorder',
};
const contractCall: DbSmartContract = {
tx_id: '0x668142abbcabb846e3f83183325325071a8b4882dcf5476a38148cb5b738fc83',
canonical: true,
contract_id: 'SP3YK7KWMYRCDMV5M4792T0T7DERQXHJJGGEPV1N8.pg-mdomains-v1',
block_height: 1,
source_code,
abi,
};
const dbBlock: DbBlock = {
block_hash: '0xd10ccecfd7ac9e5f8a10de0532fac028559b31a6ff494d82147f6297fb663139',
index_block_hash: '0xd10ccecfd7ac9e5f8a10de0532fac028559b31a6ff494d82147f6297fb663139',
parent_index_block_hash: '',
parent_block_hash: '',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 1637003433,
burn_block_hash: '0x0000000000000000000342c6f7e9313ffa6f0a92618edaf86351ca265aee1c7a',
burn_block_height: 1,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 1210,
execution_cost_read_length: 1919542,
execution_cost_runtime: 2480886000,
execution_cost_write_count: 138,
execution_cost_write_length: 91116,
};
await db.updateBlock(client, dbBlock);
await db.updateTx(client, tx1);
await db.updateSmartContract(client, tx1, contractCall);
const dbTx2: DbTx = {
tx_id: '0x8915000000000000000000000000000000000000000000000000000000000000',
anchor_mode: 3,
nonce: 1000,
raw_tx: Buffer.from('test-raw-tx'),
type_id: DbTxTypeId.Coinbase,
coinbase_payload: Buffer.from('coinbase hi'),
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: true,
sender_address: 'sender-addr',
sponsor_address: 'sponsor-addr',
origin_hash_mode: 1,
block_hash: '0x0123',
index_block_hash: '0x1234',
parent_block_hash: '0x5678',
block_height: 0,
burn_block_time: 39486,
parent_burn_block_time: 1626122935,
tx_index: 4,
status: DbTxStatus.Success,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: '',
event_count: 0,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateTx(client, dbTx2);
const notFoundTxId = '0x8914000000000000000000000000000000000000000000000000000000000000';
const txsListDetail = await supertest(api.server).get(
`/extended/v1/tx/multiple?tx_id=${mempoolTx.tx_id}&tx_id=${tx1.tx_id}&tx_id=${notFoundTxId}&tx_id=${dbTx2.tx_id}`
);
const jsonRes = txsListDetail.body;
// tx comparison
expect(jsonRes[mempoolTx.tx_id].result.tx_id).toEqual(mempoolTx.tx_id);
expect(jsonRes[tx1.tx_id].result.tx_id).toEqual(tx1.tx_id);
// mempool tx comparison
expect(jsonRes[notFoundTxId].result.tx_id).toEqual(notFoundTxId);
// not found comparison
expect(jsonRes[dbTx2.tx_id].result.tx_id).toEqual(dbTx2.tx_id);
});
test('fetch mempool-tx', async () => {
const mempoolTx: DbMempoolTx = {
pruned: false,
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000000',
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.from('test-raw-tx'),
type_id: DbTxTypeId.Coinbase,
status: DbTxStatus.Pending,
receipt_time: 1594307695,
coinbase_payload: Buffer.from('coinbase hi'),
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'sender-addr',
origin_hash_mode: 1,
};
await db.updateMempoolTxs({ mempoolTxs: [mempoolTx] });
const searchResult1 = await supertest(api.server).get(`/extended/v1/tx/${mempoolTx.tx_id}`);
expect(searchResult1.status).toBe(200);
expect(searchResult1.type).toBe('application/json');
const expectedResp1 = {
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000000',
tx_status: 'pending',
tx_type: 'coinbase',
fee_rate: '1234',
nonce: 0,
anchor_mode: 'any',
sender_address: 'sender-addr',
sponsored: false,
post_condition_mode: 'allow',
post_conditions: [],
receipt_time: 1594307695,
receipt_time_iso: '2020-07-09T15:14:55.000Z',
coinbase_payload: { data: '0x636f696e62617365206869' },
};
expect(JSON.parse(searchResult1.text)).toEqual(expectedResp1);
});
test('fetch mempool-tx - sponsored', async () => {
const dbBlock: DbBlock = {
block_hash: '0xff',
index_block_hash: '0x1234',
parent_index_block_hash: '0x5678',
parent_block_hash: '0x5678',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 1594647995,
burn_block_hash: '0x1234',
burn_block_height: 123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateBlock(client, dbBlock);
const mempoolTx: DbMempoolTx = {
pruned: false,
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000000',
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.from('test-raw-tx'),
type_id: DbTxTypeId.Coinbase,
status: DbTxStatus.Pending,
receipt_time: 1594307695,
coinbase_payload: Buffer.from('coinbase hi'),
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: true,
sender_address: 'sender-addr',
sponsor_address: 'sponsor-addr',
origin_hash_mode: 1,
};
await db.updateMempoolTxs({ mempoolTxs: [mempoolTx] });
const searchResult1 = await supertest(api.server).get(`/extended/v1/tx/${mempoolTx.tx_id}`);
expect(searchResult1.status).toBe(200);
expect(searchResult1.type).toBe('application/json');
const expectedResp1 = {
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000000',
tx_status: 'pending',
tx_type: 'coinbase',
fee_rate: '1234',
nonce: 0,
anchor_mode: 'any',
sender_address: 'sender-addr',
sponsor_address: 'sponsor-addr',
sponsored: true,
post_condition_mode: 'allow',
post_conditions: [],
receipt_time: 1594307695,
receipt_time_iso: '2020-07-09T15:14:55.000Z',
coinbase_payload: { data: '0x636f696e62617365206869' },
};
expect(JSON.parse(searchResult1.text)).toEqual(expectedResp1);
});
test('fetch mempool-tx - dropped', async () => {
const mempoolTx1: DbMempoolTx = {
pruned: false,
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000000',
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.from('test-raw-tx'),
type_id: DbTxTypeId.Coinbase,
status: DbTxStatus.Pending,
receipt_time: 1594307695,
coinbase_payload: Buffer.from('coinbase hi'),
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: true,
sender_address: 'sender-addr',
sponsor_address: 'sponsor-addr',
origin_hash_mode: 1,
};
const mempoolTx2: DbMempoolTx = {
...mempoolTx1,
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000001',
receipt_time: 1594307702,
};
const mempoolTx3: DbMempoolTx = {
...mempoolTx1,
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000003',
receipt_time: 1594307703,
};
const mempoolTx4: DbMempoolTx = {
...mempoolTx1,
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000004',
receipt_time: 1594307704,
};
const mempoolTx5: DbMempoolTx = {
...mempoolTx1,
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000005',
receipt_time: 1594307705,
};
const mempoolTx6: DbMempoolTx = {
...mempoolTx1,
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000006',
receipt_time: 1594307706,
};
await db.updateMempoolTxs({
mempoolTxs: [mempoolTx1, mempoolTx2, mempoolTx3, mempoolTx4, mempoolTx5],
});
await db.dropMempoolTxs({
status: DbTxStatus.DroppedReplaceAcrossFork,
txIds: [mempoolTx1.tx_id, mempoolTx2.tx_id],
});
const searchResult1 = await supertest(api.server).get(`/extended/v1/tx/${mempoolTx1.tx_id}`);
expect(searchResult1.status).toBe(200);
expect(searchResult1.type).toBe('application/json');
const expectedResp1 = {
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000000',
tx_status: 'dropped_replace_across_fork',
tx_type: 'coinbase',
fee_rate: '1234',
nonce: 0,
anchor_mode: 'any',
sender_address: 'sender-addr',
sponsor_address: 'sponsor-addr',
sponsored: true,
post_condition_mode: 'allow',
post_conditions: [],
receipt_time: 1594307695,
receipt_time_iso: '2020-07-09T15:14:55.000Z',
coinbase_payload: { data: '0x636f696e62617365206869' },
};
expect(JSON.parse(searchResult1.text)).toEqual(expectedResp1);
const searchResult2 = await supertest(api.server).get(`/extended/v1/tx/${mempoolTx2.tx_id}`);
expect(searchResult2.status).toBe(200);
expect(searchResult2.type).toBe('application/json');
const expectedResp2 = {
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000001',
tx_status: 'dropped_replace_across_fork',
tx_type: 'coinbase',
fee_rate: '1234',
nonce: 0,
anchor_mode: 'any',
sender_address: 'sender-addr',
sponsor_address: 'sponsor-addr',
sponsored: true,
post_condition_mode: 'allow',
post_conditions: [],
receipt_time: 1594307702,
receipt_time_iso: '2020-07-09T15:15:02.000Z',
coinbase_payload: { data: '0x636f696e62617365206869' },
};
expect(JSON.parse(searchResult2.text)).toEqual(expectedResp2);
await db.dropMempoolTxs({
status: DbTxStatus.DroppedReplaceByFee,
txIds: [mempoolTx3.tx_id],
});
const searchResult3 = await supertest(api.server).get(`/extended/v1/tx/${mempoolTx3.tx_id}`);
expect(searchResult3.status).toBe(200);
expect(searchResult3.type).toBe('application/json');
const expectedResp3 = {
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000003',
tx_status: 'dropped_replace_by_fee',
tx_type: 'coinbase',
fee_rate: '1234',
nonce: 0,
anchor_mode: 'any',
sender_address: 'sender-addr',
sponsor_address: 'sponsor-addr',
sponsored: true,
post_condition_mode: 'allow',
post_conditions: [],
receipt_time: 1594307703,
receipt_time_iso: '2020-07-09T15:15:03.000Z',
coinbase_payload: { data: '0x636f696e62617365206869' },
};
expect(JSON.parse(searchResult3.text)).toEqual(expectedResp3);
await db.dropMempoolTxs({
status: DbTxStatus.DroppedTooExpensive,
txIds: [mempoolTx4.tx_id],
});
const searchResult4 = await supertest(api.server).get(`/extended/v1/tx/${mempoolTx4.tx_id}`);
expect(searchResult4.status).toBe(200);
expect(searchResult4.type).toBe('application/json');
const expectedResp4 = {
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000004',
tx_status: 'dropped_too_expensive',
tx_type: 'coinbase',
fee_rate: '1234',
nonce: 0,
anchor_mode: 'any',
sender_address: 'sender-addr',
sponsor_address: 'sponsor-addr',
sponsored: true,
post_condition_mode: 'allow',
post_conditions: [],
receipt_time: 1594307704,
receipt_time_iso: '2020-07-09T15:15:04.000Z',
coinbase_payload: { data: '0x636f696e62617365206869' },
};
expect(JSON.parse(searchResult4.text)).toEqual(expectedResp4);
await db.dropMempoolTxs({
status: DbTxStatus.DroppedStaleGarbageCollect,
txIds: [mempoolTx5.tx_id],
});
const searchResult5 = await supertest(api.server).get(`/extended/v1/tx/${mempoolTx5.tx_id}`);
expect(searchResult5.status).toBe(200);
expect(searchResult5.type).toBe('application/json');
const expectedResp5 = {
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000005',
tx_status: 'dropped_stale_garbage_collect',
tx_type: 'coinbase',
fee_rate: '1234',
nonce: 0,
anchor_mode: 'any',
sender_address: 'sender-addr',
sponsor_address: 'sponsor-addr',
sponsored: true,
post_condition_mode: 'allow',
post_conditions: [],
receipt_time: 1594307705,
receipt_time_iso: '2020-07-09T15:15:05.000Z',
coinbase_payload: { data: '0x636f696e62617365206869' },
};
expect(JSON.parse(searchResult5.text)).toEqual(expectedResp5);
const mempoolDroppedResult1 = await supertest(api.server).get(
'/extended/v1/tx/mempool/dropped'
);
expect(mempoolDroppedResult1.status).toBe(200);
expect(mempoolDroppedResult1.type).toBe('application/json');
expect(mempoolDroppedResult1.body).toEqual(
expect.objectContaining({
results: expect.arrayContaining([
expect.objectContaining({
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000005',
tx_status: 'dropped_stale_garbage_collect',
}),
expect.objectContaining({
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000004',
tx_status: 'dropped_too_expensive',
}),
expect.objectContaining({
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000003',
tx_status: 'dropped_replace_by_fee',
}),
expect.objectContaining({
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000001',
tx_status: 'dropped_replace_across_fork',
}),
expect.objectContaining({
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000000',
tx_status: 'dropped_replace_across_fork',
}),
]),
})
);
const dbBlock1: DbBlock = {
block_hash: '0x0123',
index_block_hash: '0x1234',
parent_index_block_hash: '0x00',
parent_block_hash: '0x5678',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 39486,
burn_block_hash: '0x1234',
burn_block_height: 123,
miner_txid: '0x4321',
canonical: false,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const dbTx1: DbTx = {
...mempoolTx1,
...dbBlock1,
parent_burn_block_time: 1626122935,
tx_index: 4,
status: DbTxStatus.Success,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: '',
event_count: 0,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const dataStoreUpdate1: DataStoreBlockUpdateData = {
block: dbBlock1,
microblocks: [],
minerRewards: [],
txs: [
{
tx: dbTx1,
stxEvents: [],
stxLockEvents: [],
ftEvents: [],
nftEvents: [],
contractLogEvents: [],
smartContracts: [],
names: [],
namespaces: [],
},
],
};
await db.update(dataStoreUpdate1);
const mempoolDroppedResult2 = await supertest(api.server).get(
'/extended/v1/tx/mempool/dropped'
);
expect(mempoolDroppedResult2.status).toBe(200);
expect(mempoolDroppedResult2.type).toBe('application/json');
expect(mempoolDroppedResult2.body.results).toHaveLength(4);
expect(mempoolDroppedResult2.body).toEqual(
expect.objectContaining({
results: expect.arrayContaining([
expect.objectContaining({
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000005',
tx_status: 'dropped_stale_garbage_collect',
}),
expect.objectContaining({
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000004',
tx_status: 'dropped_too_expensive',
}),
expect.objectContaining({
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000003',
tx_status: 'dropped_replace_by_fee',
}),
expect.objectContaining({
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000001',
tx_status: 'dropped_replace_across_fork',
}),
]),
})
);
});
test('fetch mempool-tx list', async () => {
for (let i = 0; i < 10; i++) {
const mempoolTx: DbMempoolTx = {
pruned: false,
tx_id: `0x891200000000000000000000000000000000000000000000000000000000000${i}`,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.from('test-raw-tx'),
type_id: DbTxTypeId.Coinbase,
receipt_time: (new Date(`2020-07-09T15:14:0${i}Z`).getTime() / 1000) | 0,
coinbase_payload: Buffer.from('coinbase hi'),
status: 1,
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'sender-addr',
origin_hash_mode: 1,
};
await db.updateMempoolTxs({ mempoolTxs: [mempoolTx] });
}
const searchResult1 = await supertest(api.server).get(
'/extended/v1/tx/mempool?limit=3&offset=2'
);
expect(searchResult1.status).toBe(200);
expect(searchResult1.type).toBe('application/json');
const expectedResp1 = {
limit: 3,
offset: 2,
total: 10,
results: [
{
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000007',
tx_status: 'pending',
tx_type: 'coinbase',
receipt_time: 1594307647,
receipt_time_iso: '2020-07-09T15:14:07.000Z',
fee_rate: '1234',
nonce: 0,
anchor_mode: 'any',
sender_address: 'sender-addr',
sponsored: false,
post_condition_mode: 'allow',
post_conditions: [],
coinbase_payload: { data: '0x636f696e62617365206869' },
},
{
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000006',
tx_status: 'pending',
tx_type: 'coinbase',
receipt_time: 1594307646,
receipt_time_iso: '2020-07-09T15:14:06.000Z',
fee_rate: '1234',
nonce: 0,
anchor_mode: 'any',
sender_address: 'sender-addr',
sponsored: false,
post_condition_mode: 'allow',
post_conditions: [],
coinbase_payload: { data: '0x636f696e62617365206869' },
},
{
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000005',
tx_status: 'pending',
tx_type: 'coinbase',
receipt_time: 1594307645,
receipt_time_iso: '2020-07-09T15:14:05.000Z',
fee_rate: '1234',
nonce: 0,
anchor_mode: 'any',
sender_address: 'sender-addr',
sponsored: false,
post_condition_mode: 'allow',
post_conditions: [],
coinbase_payload: { data: '0x636f696e62617365206869' },
},
],
};
expect(JSON.parse(searchResult1.text)).toEqual(expectedResp1);
});
test('fetch mempool-tx list filtered', async () => {
const sendAddr = 'SP25YGP221F01S9SSCGN114MKDAK9VRK8P3KXGEMB';
const recvAddr = 'SP10EZK56MB87JYF5A704K7N18YAT6G6M09HY22GC';
const contractAddr = 'SP466FNC0P7JWTNM2R9T199QRZN1MYEDTAR0KP27';
const contractCallId = 'SP32AEEF6WW5Y0NMJ1S8SBSZDAY8R5J32NBZFPKKZ.free-punks-v0';
const stxTransfers: {
sender: string;
receiver: string;
smart_contract_id?: string;
smart_contract_source?: string;
contract_call_id?: string;
contract_call_function_name?: string;
type_id: DbTxTypeId;
}[] = new Array(5).fill({
sender: 'sender-addr',
receiver: 'receiver-addr',
type_id: DbTxTypeId.TokenTransfer,
});
stxTransfers.push(
{
sender: sendAddr,
receiver: recvAddr,
type_id: DbTxTypeId.TokenTransfer,
},
{
sender: sendAddr,
receiver: 'testRecv1',
type_id: DbTxTypeId.TokenTransfer,
},
{
sender: 'testSend1',
receiver: recvAddr,
type_id: DbTxTypeId.TokenTransfer,
},
{
sender: 'testSend1',
receiver: 'testRecv1',
contract_call_id: contractCallId,
contract_call_function_name: 'mint',
type_id: DbTxTypeId.ContractCall,
},
{
sender: 'testSend1',
receiver: 'testRecv1',
smart_contract_id: contractAddr,
smart_contract_source: '(define-public (say-hi) (ok "hello world"))',
type_id: DbTxTypeId.SmartContract,
},
{
sender: 'testSend1',
receiver: contractCallId,
type_id: DbTxTypeId.TokenTransfer,
}
);
let index = 0;
for (const xfer of stxTransfers) {
const paddedIndex = ('00' + index).slice(-2);
const mempoolTx: DbMempoolTx = {
pruned: false,
tx_id: `0x89120000000000000000000000000000000000000000000000000000000000${paddedIndex}`,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.from('test-raw-tx'),
type_id: xfer.type_id,
receipt_time: (new Date(`2020-07-09T15:14:${paddedIndex}Z`).getTime() / 1000) | 0,
status: 1,
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
origin_hash_mode: 1,
sender_address: xfer.sender,
token_transfer_recipient_address: xfer.receiver,
token_transfer_amount: 1234n,
token_transfer_memo: Buffer.alloc(0),
contract_call_contract_id: xfer.contract_call_id,
contract_call_function_name: xfer.contract_call_function_name,
smart_contract_contract_id: xfer.smart_contract_id,
smart_contract_source_code: xfer.smart_contract_source,
};
await db.updateMempoolTxs({ mempoolTxs: [mempoolTx] });
index++;
}
const searchResult1 = await supertest(api.server).get(
`/extended/v1/tx/mempool?sender_address=${sendAddr}`
);
expect(searchResult1.status).toBe(200);
expect(searchResult1.type).toBe('application/json');
const expectedResp1 = {
limit: 96,
offset: 0,
total: 2,
results: [
{
fee_rate: '1234',
nonce: 0,
anchor_mode: 'any',
post_condition_mode: 'allow',
post_conditions: [],
receipt_time: 1594307646,
receipt_time_iso: '2020-07-09T15:14:06.000Z',
sender_address: 'SP25YGP221F01S9SSCGN114MKDAK9VRK8P3KXGEMB',
sponsored: false,
token_transfer: {
amount: '1234',
memo: '',
recipient_address: 'testRecv1',
},
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000006',
tx_status: 'pending',
tx_type: 'token_transfer',
},
{
fee_rate: '1234',
nonce: 0,
anchor_mode: 'any',
post_condition_mode: 'allow',
post_conditions: [],
receipt_time: 1594307645,
receipt_time_iso: '2020-07-09T15:14:05.000Z',
sender_address: 'SP25YGP221F01S9SSCGN114MKDAK9VRK8P3KXGEMB',
sponsored: false,
token_transfer: {
amount: '1234',
memo: '',
recipient_address: 'SP10EZK56MB87JYF5A704K7N18YAT6G6M09HY22GC',
},
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000005',
tx_status: 'pending',
tx_type: 'token_transfer',
},
],
};
expect(JSON.parse(searchResult1.text)).toEqual(expectedResp1);
const searchResult2 = await supertest(api.server).get(
`/extended/v1/tx/mempool?recipient_address=${recvAddr}`
);
expect(searchResult2.status).toBe(200);
expect(searchResult2.type).toBe('application/json');
const expectedResp2 = {
limit: 96,
offset: 0,
total: 2,
results: [
{
fee_rate: '1234',
nonce: 0,
anchor_mode: 'any',
post_condition_mode: 'allow',
post_conditions: [],
receipt_time: 1594307647,
receipt_time_iso: '2020-07-09T15:14:07.000Z',
sender_address: 'testSend1',
sponsored: false,
token_transfer: {
amount: '1234',
memo: '',
recipient_address: 'SP10EZK56MB87JYF5A704K7N18YAT6G6M09HY22GC',
},
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000007',
tx_status: 'pending',
tx_type: 'token_transfer',
},
{
fee_rate: '1234',
nonce: 0,
anchor_mode: 'any',
post_condition_mode: 'allow',
post_conditions: [],
receipt_time: 1594307645,
receipt_time_iso: '2020-07-09T15:14:05.000Z',
sender_address: 'SP25YGP221F01S9SSCGN114MKDAK9VRK8P3KXGEMB',
sponsored: false,
token_transfer: {
amount: '1234',
memo: '',
recipient_address: 'SP10EZK56MB87JYF5A704K7N18YAT6G6M09HY22GC',
},
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000005',
tx_status: 'pending',
tx_type: 'token_transfer',
},
],
};
expect(JSON.parse(searchResult2.text)).toEqual(expectedResp2);
const searchResult3 = await supertest(api.server).get(
`/extended/v1/tx/mempool?sender_address=${sendAddr}&recipient_address=${recvAddr}&`
);
expect(searchResult3.status).toBe(200);
expect(searchResult3.type).toBe('application/json');
const expectedResp3 = {
limit: 96,
offset: 0,
total: 1,
results: [
{
fee_rate: '1234',
nonce: 0,
anchor_mode: 'any',
post_condition_mode: 'allow',
post_conditions: [],
receipt_time: 1594307645,
receipt_time_iso: '2020-07-09T15:14:05.000Z',
sender_address: 'SP25YGP221F01S9SSCGN114MKDAK9VRK8P3KXGEMB',
sponsored: false,
token_transfer: {
amount: '1234',
memo: '',
recipient_address: 'SP10EZK56MB87JYF5A704K7N18YAT6G6M09HY22GC',
},
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000005',
tx_status: 'pending',
tx_type: 'token_transfer',
},
],
};
expect(JSON.parse(searchResult3.text)).toEqual(expectedResp3);
const searchResult4 = await supertest(api.server).get(
`/extended/v1/tx/mempool?address=${sendAddr}`
);
expect(searchResult4.status).toBe(200);
expect(searchResult4.type).toBe('application/json');
const expectedResp4 = {
limit: 96,
offset: 0,
total: 2,
results: [
{
fee_rate: '1234',
nonce: 0,
anchor_mode: 'any',
post_condition_mode: 'allow',
post_conditions: [],
receipt_time: 1594307646,
receipt_time_iso: '2020-07-09T15:14:06.000Z',
sender_address: 'SP25YGP221F01S9SSCGN114MKDAK9VRK8P3KXGEMB',
sponsored: false,
token_transfer: {
amount: '1234',
memo: '',
recipient_address: 'testRecv1',
},
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000006',
tx_status: 'pending',
tx_type: 'token_transfer',
},
{
fee_rate: '1234',
nonce: 0,
anchor_mode: 'any',
post_condition_mode: 'allow',
post_conditions: [],
receipt_time: 1594307645,
receipt_time_iso: '2020-07-09T15:14:05.000Z',
sender_address: 'SP25YGP221F01S9SSCGN114MKDAK9VRK8P3KXGEMB',
sponsored: false,
token_transfer: {
amount: '1234',
memo: '',
recipient_address: 'SP10EZK56MB87JYF5A704K7N18YAT6G6M09HY22GC',
},
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000005',
tx_status: 'pending',
tx_type: 'token_transfer',
},
],
};
expect(JSON.parse(searchResult4.text)).toEqual(expectedResp4);
const searchResult5 = await supertest(api.server).get(
`/extended/v1/tx/mempool?address=${contractCallId}`
);
expect(searchResult5.status).toBe(200);
expect(searchResult5.type).toBe('application/json');
const expectedResp5 = {
limit: 96,
offset: 0,
total: 2,
results: [
{
fee_rate: '1234',
nonce: 0,
anchor_mode: 'any',
post_condition_mode: 'allow',
post_conditions: [],
receipt_time: 1594307650,
receipt_time_iso: '2020-07-09T15:14:10.000Z',
sender_address: 'testSend1',
sponsored: false,
token_transfer: {
amount: '1234',
memo: '',
recipient_address: 'SP32AEEF6WW5Y0NMJ1S8SBSZDAY8R5J32NBZFPKKZ.free-punks-v0',
},
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000010',
tx_status: 'pending',
tx_type: 'token_transfer',
},
{
fee_rate: '1234',
nonce: 0,
anchor_mode: 'any',
post_condition_mode: 'allow',
post_conditions: [],
receipt_time: 1594307648,
receipt_time_iso: '2020-07-09T15:14:08.000Z',
sender_address: 'testSend1',
sponsored: false,
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000008',
tx_status: 'pending',
tx_type: 'contract_call',
contract_call: {
contract_id: 'SP32AEEF6WW5Y0NMJ1S8SBSZDAY8R5J32NBZFPKKZ.free-punks-v0',
function_name: 'mint',
function_signature: '',
},
},
],
};
expect(JSON.parse(searchResult5.text)).toEqual(expectedResp5);
const searchResult6 = await supertest(api.server).get(
`/extended/v1/tx/mempool?address=${contractAddr}`
);
expect(searchResult6.status).toBe(200);
expect(searchResult6.type).toBe('application/json');
const expectedResp6 = {
limit: 96,
offset: 0,
total: 1,
results: [
{
fee_rate: '1234',
nonce: 0,
anchor_mode: 'any',
post_condition_mode: 'allow',
post_conditions: [],
receipt_time: 1594307649,
receipt_time_iso: '2020-07-09T15:14:09.000Z',
sender_address: 'testSend1',
sponsored: false,
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000009',
tx_status: 'pending',
tx_type: 'smart_contract',
smart_contract: {
contract_id: 'SP466FNC0P7JWTNM2R9T199QRZN1MYEDTAR0KP27',
source_code: '(define-public (say-hi) (ok "hello world"))',
},
},
],
};
expect(JSON.parse(searchResult6.text)).toEqual(expectedResp6);
const searchResult7 = await supertest(api.server).get(
`/extended/v1/tx/mempool?recipient_address=${contractCallId}`
);
expect(searchResult7.status).toBe(200);
expect(searchResult7.type).toBe('application/json');
const expectedResp7 = {
limit: 96,
offset: 0,
total: 1,
results: [
{
fee_rate: '1234',
nonce: 0,
anchor_mode: 'any',
post_condition_mode: 'allow',
post_conditions: [],
receipt_time: 1594307650,
receipt_time_iso: '2020-07-09T15:14:10.000Z',
sender_address: 'testSend1',
sponsored: false,
token_transfer: {
amount: '1234',
memo: '',
recipient_address: 'SP32AEEF6WW5Y0NMJ1S8SBSZDAY8R5J32NBZFPKKZ.free-punks-v0',
},
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000010',
tx_status: 'pending',
tx_type: 'token_transfer',
},
],
};
expect(JSON.parse(searchResult7.text)).toEqual(expectedResp7);
});
test('mempool - contract_call tx abi details are retrieved', async () => {
const block1 = new TestBlockBuilder()
.addTx()
.addTxSmartContract()
.addTxContractLogEvent()
.build();
await db.update(block1);
const mempoolTx1 = new TestMempoolTxBuilder({
type_id: DbTxTypeId.ContractCall,
tx_id: '0x1232000000000000000000000000000000000000000000000000000000000000',
}).build();
await db.updateMempoolTxs({ mempoolTxs: [mempoolTx1] });
const expectedContractDetails = {
contract_id: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
function_args: [
{
hex: '0x010000000000000000000000000001e240',
name: 'amount',
repr: 'u123456',
type: 'uint',
},
],
function_name: 'test-contract-fn',
function_signature: '(define-public (test-contract-fn (amount uint)))',
};
// Mempool txs
const mempoolResults = await supertest(api.server).get(`/extended/v1/tx/mempool`);
expect(mempoolResults.status).toBe(200);
expect(mempoolResults.type).toBe('application/json');
expect(JSON.parse(mempoolResults.text).results[0].contract_call).toEqual(
expectedContractDetails
);
// Search mempool tx metadata
const searchResults = await supertest(api.server).get(
`/extended/v1/search/${mempoolTx1.tx_id}?include_metadata=true`
);
expect(searchResults.status).toBe(200);
expect(searchResults.type).toBe('application/json');
expect(JSON.parse(searchResults.text).result.metadata.contract_call).toEqual(
expectedContractDetails
);
// Search principal metadata
const searchPrincipalResults = await supertest(api.server).get(
`/extended/v1/search/${expectedContractDetails.contract_id}?include_metadata=true`
);
expect(searchPrincipalResults.status).toBe(200);
expect(searchPrincipalResults.type).toBe('application/json');
expect(JSON.parse(searchPrincipalResults.text).result.metadata.contract_call).toEqual(
expectedContractDetails
);
// Dropped mempool tx
await db.dropMempoolTxs({
status: DbTxStatus.DroppedReplaceAcrossFork,
txIds: [mempoolTx1.tx_id],
});
const mempoolDropResults = await supertest(api.server).get(`/extended/v1/tx/mempool/dropped`);
expect(mempoolDropResults.status).toBe(200);
expect(mempoolDropResults.type).toBe('application/json');
expect(JSON.parse(mempoolDropResults.text).results[0].contract_call).toEqual(
expectedContractDetails
);
});
test('search term - hash', async () => {
const block: DbBlock = {
block_hash: '0x1234000000000000000000000000000000000000000000000000000000000000',
index_block_hash: '0xdeadbeef',
parent_index_block_hash: '0x00',
parent_block_hash: '0xff0011',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1235,
burn_block_time: 94869286,
burn_block_hash: '0x1234',
burn_block_height: 123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateBlock(client, block);
const tx: DbTx = {
tx_id: '0x4567000000000000000000000000000000000000000000000000000000000000',
tx_index: 4,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.alloc(0),
index_block_hash: block.index_block_hash,
block_hash: block.block_hash,
block_height: 68456,
burn_block_time: 2837565,
parent_burn_block_time: 1626122935,
type_id: DbTxTypeId.Coinbase,
coinbase_payload: Buffer.from('coinbase hi'),
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: '',
parent_block_hash: '',
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'sender-addr',
origin_hash_mode: 1,
event_count: 0,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateTx(client, tx);
const mempoolTx: DbMempoolTx = {
pruned: false,
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000000',
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.from('test-raw-tx'),
type_id: DbTxTypeId.Coinbase,
receipt_time: 123456,
coinbase_payload: Buffer.from('coinbase hi'),
status: 1,
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'sender-addr',
origin_hash_mode: 1,
};
await db.updateMempoolTxs({ mempoolTxs: [mempoolTx] });
const searchResult1 = await supertest(api.server).get(
`/extended/v1/search/0x1234000000000000000000000000000000000000000000000000000000000000`
);
expect(searchResult1.status).toBe(200);
expect(searchResult1.type).toBe('application/json');
const expectedResp1 = {
found: true,
result: {
entity_id: '0x1234000000000000000000000000000000000000000000000000000000000000',
entity_type: 'block_hash',
block_data: {
canonical: true,
hash: '0x1234000000000000000000000000000000000000000000000000000000000000',
parent_block_hash: '0xff0011',
burn_block_time: 94869286,
height: 1235,
},
},
};
expect(JSON.parse(searchResult1.text)).toEqual(expectedResp1);
// test without 0x-prefix
const searchResult2 = await supertest(api.server).get(
`/extended/v1/search/1234000000000000000000000000000000000000000000000000000000000000`
);
expect(searchResult2.status).toBe(200);
expect(searchResult2.type).toBe('application/json');
const expectedResp2 = {
found: true,
result: {
entity_id: '0x1234000000000000000000000000000000000000000000000000000000000000',
entity_type: 'block_hash',
block_data: {
canonical: true,
hash: '0x1234000000000000000000000000000000000000000000000000000000000000',
parent_block_hash: '0xff0011',
burn_block_time: 94869286,
height: 1235,
},
},
};
expect(JSON.parse(searchResult2.text)).toEqual(expectedResp2);
// test whitespace
const searchResult3 = await supertest(api.server).get(
`/extended/v1/search/ 1234000000000000000000000000000000000000000000000000000000000000`
);
expect(searchResult3.status).toBe(200);
expect(searchResult3.type).toBe('application/json');
const expectedResp3 = {
found: true,
result: {
entity_id: '0x1234000000000000000000000000000000000000000000000000000000000000',
entity_type: 'block_hash',
block_data: {
canonical: true,
hash: '0x1234000000000000000000000000000000000000000000000000000000000000',
parent_block_hash: '0xff0011',
burn_block_time: 94869286,
height: 1235,
},
},
};
expect(JSON.parse(searchResult3.text)).toEqual(expectedResp3);
// test tx search
const searchResult4 = await supertest(api.server).get(
`/extended/v1/search/0x4567000000000000000000000000000000000000000000000000000000000000`
);
expect(searchResult4.status).toBe(200);
expect(searchResult4.type).toBe('application/json');
const expectedResp4 = {
found: true,
result: {
entity_id: '0x4567000000000000000000000000000000000000000000000000000000000000',
entity_type: 'tx_id',
tx_data: {
canonical: true,
block_hash: '0x1234000000000000000000000000000000000000000000000000000000000000',
burn_block_time: 2837565,
block_height: 68456,
tx_type: 'coinbase',
},
},
};
expect(JSON.parse(searchResult4.text)).toEqual(expectedResp4);
// test mempool tx search
const searchResult5 = await supertest(api.server).get(
`/extended/v1/search/0x8912000000000000000000000000000000000000000000000000000000000000`
);
expect(searchResult5.status).toBe(200);
expect(searchResult5.type).toBe('application/json');
const expectedResp5 = {
found: true,
result: {
entity_id: '0x8912000000000000000000000000000000000000000000000000000000000000',
entity_type: 'mempool_tx_id',
tx_data: { tx_type: 'coinbase' },
},
};
expect(JSON.parse(searchResult5.text)).toEqual(expectedResp5);
// test hash not found
const searchResult6 = await supertest(api.server).get(
`/extended/v1/search/0x1111000000000000000000000000000000000000000000000000000000000000`
);
expect(searchResult6.status).toBe(404);
expect(searchResult6.type).toBe('application/json');
const expectedResp6 = {
found: false,
result: { entity_type: 'unknown_hash' },
error:
'No block or transaction found with hash "0x1111000000000000000000000000000000000000000000000000000000000000"',
};
expect(JSON.parse(searchResult6.text)).toEqual(expectedResp6);
// test invalid hash hex
const invalidHex = '0x1111w00000000000000000000000000000000000000000000000000000000000';
const searchResult7 = await supertest(api.server).get(`/extended/v1/search/${invalidHex}`);
expect(searchResult7.status).toBe(404);
expect(searchResult7.type).toBe('application/json');
const expectedResp7 = {
found: false,
result: { entity_type: 'invalid_term' },
error:
'The term "0x1111w00000000000000000000000000000000000000000000000000000000000" is not a valid block hash, transaction ID, contract principal, or account address principal',
};
expect(JSON.parse(searchResult7.text)).toEqual(expectedResp7);
});
test('search term - hash with metadata', async () => {
const block: DbBlock = {
block_hash: '0x1234000000000000000000000000000000000000000000000000000000000000',
index_block_hash: '0xdeadbeef',
parent_index_block_hash: '0x00',
parent_block_hash: '0xff0011',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 94869286,
burn_block_hash: '0x1234',
burn_block_height: 123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const tx: DbTx = {
tx_id: '0x4567000000000000000000000000000000000000000000000000000000000000',
tx_index: 4,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.alloc(0),
index_block_hash: block.index_block_hash,
block_hash: block.block_hash,
block_height: 1,
burn_block_time: 2837565,
parent_burn_block_time: 1626122935,
type_id: DbTxTypeId.Coinbase,
coinbase_payload: Buffer.from('coinbase hi'),
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: '',
parent_block_hash: '',
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'sender-addr',
origin_hash_mode: 1,
event_count: 0,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const mempoolTx: DbMempoolTx = {
pruned: false,
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000000',
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.from('test-raw-tx'),
type_id: DbTxTypeId.Coinbase,
receipt_time: 123456,
coinbase_payload: Buffer.from('coinbase hi'),
status: 1,
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'sender-addr',
origin_hash_mode: 1,
};
await db.updateMempoolTxs({ mempoolTxs: [mempoolTx] });
const dataStoreUpdate: DataStoreBlockUpdateData = {
block: block,
microblocks: [],
minerRewards: [],
txs: [
{
tx: tx,
stxEvents: [],
stxLockEvents: [],
ftEvents: [],
nftEvents: [],
contractLogEvents: [],
smartContracts: [],
names: [],
namespaces: [],
},
],
};
await db.update(dataStoreUpdate);
const blockMetadata = {
burn_block_hash: '0x1234',
burn_block_height: 123,
burn_block_time: 94869286,
burn_block_time_iso: '1973-01-03T00:34:46.000Z',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
hash: '0x1234000000000000000000000000000000000000000000000000000000000000',
height: 1,
microblocks_accepted: [],
microblocks_streamed: [],
miner_txid: '0x4321',
parent_block_hash: '0xff0011',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
txs: ['0x4567000000000000000000000000000000000000000000000000000000000000'],
};
const searchResult1 = await supertest(api.server).get(
`/extended/v1/search/0x1234000000000000000000000000000000000000000000000000000000000000?include_metadata=true`
);
expect(searchResult1.status).toBe(200);
expect(searchResult1.type).toBe('application/json');
const expectedResp1 = {
found: true,
result: {
entity_id: '0x1234000000000000000000000000000000000000000000000000000000000000',
entity_type: 'block_hash',
block_data: {
canonical: true,
hash: '0x1234000000000000000000000000000000000000000000000000000000000000',
parent_block_hash: '0xff0011',
burn_block_time: 94869286,
height: 1,
},
metadata: blockMetadata,
},
};
expect(JSON.parse(searchResult1.text)).toEqual(expectedResp1);
// test without 0x-prefix
const searchResult2 = await supertest(api.server).get(
`/extended/v1/search/1234000000000000000000000000000000000000000000000000000000000000?include_metadata=true`
);
expect(searchResult2.status).toBe(200);
expect(searchResult2.type).toBe('application/json');
const expectedResp2 = {
found: true,
result: {
entity_id: '0x1234000000000000000000000000000000000000000000000000000000000000',
entity_type: 'block_hash',
block_data: {
canonical: true,
hash: '0x1234000000000000000000000000000000000000000000000000000000000000',
parent_block_hash: '0xff0011',
burn_block_time: 94869286,
height: 1,
},
metadata: blockMetadata,
},
};
expect(JSON.parse(searchResult2.text)).toEqual(expectedResp2);
// test whitespace
const searchResult3 = await supertest(api.server).get(
`/extended/v1/search/ 1234000000000000000000000000000000000000000000000000000000000000?include_metadata=true`
);
expect(searchResult3.status).toBe(200);
expect(searchResult3.type).toBe('application/json');
const expectedResp3 = {
found: true,
result: {
entity_id: '0x1234000000000000000000000000000000000000000000000000000000000000',
entity_type: 'block_hash',
block_data: {
canonical: true,
hash: '0x1234000000000000000000000000000000000000000000000000000000000000',
parent_block_hash: '0xff0011',
burn_block_time: 94869286,
height: 1,
},
metadata: blockMetadata,
},
};
expect(JSON.parse(searchResult3.text)).toEqual(expectedResp3);
// test mempool tx search
const searchResult4 = await supertest(api.server).get(
`/extended/v1/search/0x8912000000000000000000000000000000000000000000000000000000000000?include_metadata=1`
);
expect(searchResult4.status).toBe(200);
expect(searchResult4.type).toBe('application/json');
const expectedResp4 = {
found: true,
result: {
entity_id: '0x8912000000000000000000000000000000000000000000000000000000000000',
entity_type: 'mempool_tx_id',
tx_data: { tx_type: 'coinbase' },
metadata: {
anchor_mode: 'any',
coinbase_payload: {
data: '0x636f696e62617365206869',
},
fee_rate: '1234',
nonce: 0,
post_condition_mode: 'allow',
post_conditions: [],
receipt_time: 123456,
receipt_time_iso: '1970-01-02T10:17:36.000Z',
sender_address: 'sender-addr',
sponsored: false,
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000000',
tx_status: 'success',
tx_type: 'coinbase',
},
},
};
expect(JSON.parse(searchResult4.text)).toEqual(expectedResp4);
// test hash not found
const searchResult5 = await supertest(api.server).get(
`/extended/v1/search/0x1111000000000000000000000000000000000000000000000000000000000000?include_metadata=on`
);
expect(searchResult5.status).toBe(404);
expect(searchResult5.type).toBe('application/json');
const expectedResp6 = {
found: false,
result: { entity_type: 'unknown_hash' },
error:
'No block or transaction found with hash "0x1111000000000000000000000000000000000000000000000000000000000000"',
};
expect(JSON.parse(searchResult5.text)).toEqual(expectedResp6);
// test invalid hash hex
const invalidHex = '0x1111w00000000000000000000000000000000000000000000000000000000000';
const searchResult6 = await supertest(api.server).get(
`/extended/v1/search/${invalidHex}?include_metadata`
);
expect(searchResult6.status).toBe(404);
expect(searchResult6.type).toBe('application/json');
const expectedResp7 = {
found: false,
result: { entity_type: 'invalid_term' },
error:
'The term "0x1111w00000000000000000000000000000000000000000000000000000000000" is not a valid block hash, transaction ID, contract principal, or account address principal',
};
expect(JSON.parse(searchResult6.text)).toEqual(expectedResp7);
// test tx search
const searchResult8 = await supertest(api.server).get(
`/extended/v1/search/0x4567000000000000000000000000000000000000000000000000000000000000?include_metadata`
);
expect(searchResult8.status).toBe(200);
expect(searchResult8.type).toBe('application/json');
const expectedResp8 = {
found: true,
result: {
entity_id: '0x4567000000000000000000000000000000000000000000000000000000000000',
entity_type: 'tx_id',
tx_data: {
canonical: true,
block_hash: '0x1234000000000000000000000000000000000000000000000000000000000000',
burn_block_time: 2837565,
block_height: 1,
tx_type: 'coinbase',
},
metadata: {
tx_id: '0x4567000000000000000000000000000000000000000000000000000000000000',
nonce: 0,
fee_rate: '1234',
sender_address: 'sender-addr',
sponsored: false,
post_condition_mode: 'allow',
post_conditions: [],
anchor_mode: 'any',
is_unanchored: false,
block_hash: '0x1234000000000000000000000000000000000000000000000000000000000000',
parent_block_hash: '',
block_height: 1,
burn_block_time: 2837565,
burn_block_time_iso: '1970-02-02T20:12:45.000Z',
parent_burn_block_time: 1626122935,
parent_burn_block_time_iso: '2021-07-12T20:48:55.000Z',
canonical: true,
tx_index: 4,
tx_status: 'success',
tx_result: {
hex: '0x0100000000000000000000000000000001',
repr: 'u1',
},
microblock_hash: '',
microblock_sequence: 2147483647,
microblock_canonical: true,
event_count: 0,
events: [],
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
tx_type: 'coinbase',
coinbase_payload: {
data: '0x636f696e62617365206869',
},
},
},
};
expect(JSON.parse(searchResult8.text)).toEqual(expectedResp8);
});
test('search term - principal', async () => {
const addr1 = 'ST3J8EVYHVKH6XXPD61EE8XEHW4Y2K83861225AB1';
const addr2 = 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4';
const addr3 = 'ST37VASHEJRMFRS91GWK1HZZKKEYQTEP85ARXCQPH';
const addr4 = 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C';
const addr5 = 'ST3YKTGBCY1BNKN6J18A3QKAX7CE36SZH3A5XN9ZQ';
const addr6 = 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR';
const addr7 = 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G';
const addr8 = 'ST3AMFNNS7KBQ28ECMJMN2G3AGJ37SSA2HSY82CMH';
const addr9 = 'STAR26VJ4BC24SMNKRY533MAM0K3JA5ZJDVBD45A';
const addr10 = 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6';
const addr11 = 'ST3R34339DRYJ7V6E4Y78P9ZQYRJ7D68SG2RYDEEX';
const addr12 = 'STG087YK10C83YJVPGSVZA7276A9REH656HCAKPT';
const addr13 = 'ST2WVE3HKMQ7YQ6QMRDM8QE6S9G9CG9JNXD0A4P8W';
const contractAddr1 = 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world';
const contractAddr2 = 'STSPS4JYDEYCPPCSHE3MM2NCEGR07KPBETNEZCBQ.contract-name';
const contractAddr3 = 'STSPS4JYDEYCPPCSHE3MM2NCEGR07KPBETNEZCBQ.test-contract';
const block: DbBlock = {
block_hash: '0x1234',
index_block_hash: '0x1234',
parent_index_block_hash: '0x2345',
parent_block_hash: '0x5678',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 100123123,
burn_block_time: 39486,
burn_block_hash: '0x1234',
burn_block_height: 100123123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateBlock(client, block);
const stxTx1: DbTx = {
tx_id: '0x1111000000000000000000000000000000000000000000000000000000000000',
tx_index: 0,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.alloc(0),
index_block_hash: '0x5432',
block_hash: '0x9876',
block_height: 68456,
burn_block_time: 2837565,
parent_burn_block_time: 1626122935,
type_id: DbTxTypeId.TokenTransfer,
token_transfer_amount: 1n,
token_transfer_memo: Buffer.from('hi'),
token_transfer_recipient_address: 'none',
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: '',
parent_block_hash: '',
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: addr1,
origin_hash_mode: 1,
event_count: 0,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateTx(client, stxTx1);
// test address as a tx sender
const searchResult1 = await supertest(api.server).get(`/extended/v1/search/${addr1}`);
expect(searchResult1.status).toBe(200);
expect(searchResult1.type).toBe('application/json');
const expectedResp1 = {
found: true,
result: {
entity_type: 'standard_address',
entity_id: addr1,
},
};
expect(JSON.parse(searchResult1.text)).toEqual(expectedResp1);
const stxTx2: DbTx = {
tx_id: '0x2222000000000000000000000000000000000000000000000000000000000000',
tx_index: 0,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.alloc(0),
index_block_hash: '0x5432',
block_hash: '0x9876',
block_height: 68456,
burn_block_time: 2837565,
parent_burn_block_time: 1626122935,
type_id: DbTxTypeId.TokenTransfer,
token_transfer_amount: 1n,
token_transfer_memo: Buffer.from('hi'),
token_transfer_recipient_address: addr2,
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: '',
parent_block_hash: '',
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'none',
origin_hash_mode: 1,
event_count: 0,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateTx(client, stxTx2);
// test address as a stx tx recipient
const searchResult2 = await supertest(api.server).get(`/extended/v1/search/${addr2}`);
expect(searchResult2.status).toBe(200);
expect(searchResult2.type).toBe('application/json');
const expectedResp2 = {
found: true,
result: {
entity_type: 'standard_address',
entity_id: addr2,
},
};
expect(JSON.parse(searchResult2.text)).toEqual(expectedResp2);
const stxEvent1: DbStxEvent = {
canonical: true,
event_type: DbEventTypeId.StxAsset,
asset_event_type_id: DbAssetEventTypeId.Transfer,
event_index: 0,
tx_id: '0x1111000000000000000000000000000000000000000000000000000000000000',
tx_index: 1,
block_height: 1,
amount: 1n,
recipient: addr3,
sender: 'none',
};
await db.updateStxEvent(client, stxTx1, stxEvent1);
// test address as a stx event recipient
const searchResult3 = await supertest(api.server).get(`/extended/v1/search/${addr3}`);
expect(searchResult3.status).toBe(200);
expect(searchResult3.type).toBe('application/json');
const expectedResp3 = {
found: true,
result: {
entity_type: 'standard_address',
entity_id: addr3,
},
};
expect(JSON.parse(searchResult3.text)).toEqual(expectedResp3);
const stxEvent2: DbStxEvent = {
canonical: true,
event_type: DbEventTypeId.StxAsset,
asset_event_type_id: DbAssetEventTypeId.Transfer,
event_index: 0,
tx_id: '0x1111000000000000000000000000000000000000000000000000000000000000',
tx_index: 1,
block_height: 1,
amount: 1n,
recipient: 'none',
sender: addr4,
};
await db.updateStxEvent(client, stxTx1, stxEvent2);
// test address as a stx event sender
const searchResult4 = await supertest(api.server).get(`/extended/v1/search/${addr4}`);
expect(searchResult4.status).toBe(200);
expect(searchResult4.type).toBe('application/json');
const expectedResp4 = {
found: true,
result: {
entity_type: 'standard_address',
entity_id: addr4,
},
};
expect(JSON.parse(searchResult4.text)).toEqual(expectedResp4);
const ftEvent1: DbFtEvent = {
canonical: true,
event_type: DbEventTypeId.FungibleTokenAsset,
asset_event_type_id: DbAssetEventTypeId.Transfer,
event_index: 0,
tx_id: '0x1111000000000000000000000000000000000000000000000000000000000000',
tx_index: 1,
block_height: 1,
asset_identifier: 'some-asset',
amount: 1n,
recipient: addr5,
sender: 'none',
};
await db.updateFtEvent(client, stxTx1, ftEvent1);
// test address as a ft event recipient
const searchResult5 = await supertest(api.server).get(`/extended/v1/search/${addr5}`);
expect(searchResult5.status).toBe(200);
expect(searchResult5.type).toBe('application/json');
const expectedResp5 = {
found: true,
result: {
entity_type: 'standard_address',
entity_id: addr5,
},
};
expect(JSON.parse(searchResult5.text)).toEqual(expectedResp5);
const ftEvent2: DbFtEvent = {
canonical: true,
event_type: DbEventTypeId.FungibleTokenAsset,
asset_event_type_id: DbAssetEventTypeId.Transfer,
event_index: 0,
tx_id: '0x1111000000000000000000000000000000000000000000000000000000000000',
tx_index: 1,
block_height: 1,
asset_identifier: 'some-asset',
amount: 1n,
recipient: 'none',
sender: addr6,
};
await db.updateFtEvent(client, stxTx1, ftEvent2);
// test address as a ft event sender
const searchResult6 = await supertest(api.server).get(`/extended/v1/search/${addr6}`);
expect(searchResult6.status).toBe(200);
expect(searchResult6.type).toBe('application/json');
const expectedResp6 = {
found: true,
result: {
entity_type: 'standard_address',
entity_id: addr6,
},
};
expect(JSON.parse(searchResult6.text)).toEqual(expectedResp6);
const nftEvent1: DbNftEvent = {
canonical: true,
event_type: DbEventTypeId.NonFungibleTokenAsset,
asset_event_type_id: DbAssetEventTypeId.Transfer,
event_index: 0,
tx_id: '0x1111000000000000000000000000000000000000000000000000000000000000',
tx_index: 1,
block_height: 1,
asset_identifier: 'some-asset',
value: serializeCV(intCV(0)),
recipient: addr7,
sender: 'none',
};
await db.updateNftEvent(client, stxTx1, nftEvent1);
// test address as a nft event recipient
const searchResult7 = await supertest(api.server).get(`/extended/v1/search/${addr7}`);
expect(searchResult7.status).toBe(200);
expect(searchResult7.type).toBe('application/json');
const expectedResp7 = {
found: true,
result: {
entity_type: 'standard_address',
entity_id: addr7,
},
};
expect(JSON.parse(searchResult7.text)).toEqual(expectedResp7);
const nftEvent2: DbNftEvent = {
canonical: true,
event_type: DbEventTypeId.NonFungibleTokenAsset,
asset_event_type_id: DbAssetEventTypeId.Transfer,
event_index: 0,
tx_id: '0x1111000000000000000000000000000000000000000000000000000000000000',
tx_index: 1,
block_height: 1,
asset_identifier: 'some-asset',
value: serializeCV(intCV(0)),
recipient: 'none',
sender: addr8,
};
await db.updateNftEvent(client, stxTx1, nftEvent2);
// test address as a nft event sender
const searchResult8 = await supertest(api.server).get(`/extended/v1/search/${addr8}`);
expect(searchResult8.status).toBe(200);
expect(searchResult8.type).toBe('application/json');
const expectedResp8 = {
found: true,
result: {
entity_type: 'standard_address',
entity_id: addr8,
},
};
expect(JSON.parse(searchResult8.text)).toEqual(expectedResp8);
const smartContract: DbTx = {
type_id: DbTxTypeId.SmartContract,
tx_id: '0x1111880000000000000000000000000000000000000000000000000000000000',
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.alloc(0),
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: '',
parent_block_hash: '',
smart_contract_contract_id: contractAddr1,
smart_contract_source_code: '(some-src)',
block_height: 1,
tx_index: 0,
index_block_hash: '0x543288',
block_hash: '0x9876',
burn_block_time: 2837565,
parent_burn_block_time: 1626122935,
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'none',
origin_hash_mode: 1,
event_count: 0,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateTx(client, smartContract);
// test contract address
const searchResult9 = await supertest(api.server).get(`/extended/v1/search/${contractAddr1}`);
expect(searchResult9.status).toBe(200);
expect(searchResult9.type).toBe('application/json');
const expectedResp9 = {
found: true,
result: {
entity_id: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
entity_type: 'contract_address',
tx_data: {
canonical: true,
block_hash: '0x9876',
burn_block_time: 2837565,
block_height: 1,
tx_type: 'smart_contract',
tx_id: '0x1111880000000000000000000000000000000000000000000000000000000000',
},
},
};
expect(JSON.parse(searchResult9.text)).toEqual(expectedResp9);
const smartContractMempoolTx: DbMempoolTx = {
pruned: false,
type_id: DbTxTypeId.SmartContract,
tx_id: '0x1111882200000000000000000000000000000000000000000000000000000000',
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.from('test-raw-tx'),
receipt_time: 123456,
smart_contract_contract_id: contractAddr2,
smart_contract_source_code: '(some-src)',
status: 1,
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'none',
origin_hash_mode: 1,
};
await db.updateMempoolTxs({ mempoolTxs: [smartContractMempoolTx] });
// test contract address associated with mempool tx
const searchResult10 = await supertest(api.server).get(`/extended/v1/search/${contractAddr2}`);
expect(searchResult10.status).toBe(200);
expect(searchResult10.type).toBe('application/json');
const expectedResp10 = {
found: true,
result: {
entity_id: 'STSPS4JYDEYCPPCSHE3MM2NCEGR07KPBETNEZCBQ.contract-name',
entity_type: 'contract_address',
tx_data: {
tx_type: 'smart_contract',
tx_id: '0x1111882200000000000000000000000000000000000000000000000000000000',
},
},
};
expect(JSON.parse(searchResult10.text)).toEqual(expectedResp10);
// test contract address not found
const searchResult11 = await supertest(api.server).get(`/extended/v1/search/${contractAddr3}`);
expect(searchResult11.status).toBe(404);
expect(searchResult11.type).toBe('application/json');
const expectedResp11 = {
found: false,
result: { entity_type: 'contract_address' },
error:
'No principal found with address "STSPS4JYDEYCPPCSHE3MM2NCEGR07KPBETNEZCBQ.test-contract"',
};
expect(JSON.parse(searchResult11.text)).toEqual(expectedResp11);
// test standard address not found
const searchResult12 = await supertest(api.server).get(`/extended/v1/search/${addr9}`);
expect(searchResult12.status).toBe(404);
expect(searchResult12.type).toBe('application/json');
const expectedResp12 = {
found: false,
result: { entity_type: 'standard_address' },
error: 'No principal found with address "STAR26VJ4BC24SMNKRY533MAM0K3JA5ZJDVBD45A"',
};
expect(JSON.parse(searchResult12.text)).toEqual(expectedResp12);
// test invalid term
const invalidTerm = 'bogus123';
const searchResult13 = await supertest(api.server).get(`/extended/v1/search/${invalidTerm}`);
expect(searchResult13.status).toBe(404);
expect(searchResult13.type).toBe('application/json');
const expectedResp13 = {
found: false,
result: { entity_type: 'invalid_term' },
error:
'The term "bogus123" is not a valid block hash, transaction ID, contract principal, or account address principal',
};
expect(JSON.parse(searchResult13.text)).toEqual(expectedResp13);
});
test('search term - principal with metadata', async () => {
const addr1 = 'ST3J8EVYHVKH6XXPD61EE8XEHW4Y2K83861225AB1';
const addr2 = 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4';
const addr3 = 'ST37VASHEJRMFRS91GWK1HZZKKEYQTEP85ARXCQPH';
const addr4 = 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C';
const addr5 = 'ST3YKTGBCY1BNKN6J18A3QKAX7CE36SZH3A5XN9ZQ';
const addr6 = 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR';
const addr7 = 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G';
const addr8 = 'ST3AMFNNS7KBQ28ECMJMN2G3AGJ37SSA2HSY82CMH';
const addr9 = 'STAR26VJ4BC24SMNKRY533MAM0K3JA5ZJDVBD45A';
const contractAddr1 = 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world';
const contractAddr2 = 'STSPS4JYDEYCPPCSHE3MM2NCEGR07KPBETNEZCBQ.contract-name';
const contractAddr3 = 'STSPS4JYDEYCPPCSHE3MM2NCEGR07KPBETNEZCBQ.test-contract';
const block: DbBlock = {
block_hash: '0x1234',
index_block_hash: '0x1234',
parent_index_block_hash: '0x2345',
parent_block_hash: '0x5678',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 39486,
burn_block_hash: '0x1234',
burn_block_height: 100123123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const stxTx1: DbTx = {
tx_id: '0x1111000000000000000000000000000000000000000000000000000000000000',
tx_index: 0,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.alloc(0),
index_block_hash: '0x5432',
block_hash: block.block_hash,
block_height: block.block_height,
burn_block_time: 2837565,
parent_burn_block_time: 1626122935,
type_id: DbTxTypeId.TokenTransfer,
token_transfer_amount: 1n,
token_transfer_memo: Buffer.from('hi'),
token_transfer_recipient_address: 'none',
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: '',
parent_block_hash: '',
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: addr1,
origin_hash_mode: 1,
event_count: 0,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const stxTx2: DbTx = {
tx_id: '0x2222000000000000000000000000000000000000000000000000000000000000',
tx_index: 0,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.alloc(0),
index_block_hash: '0x5432',
block_hash: block.block_hash,
block_height: block.block_height,
burn_block_time: 2837565,
parent_burn_block_time: 1626122935,
type_id: DbTxTypeId.TokenTransfer,
token_transfer_amount: 1n,
token_transfer_memo: Buffer.from('hi'),
token_transfer_recipient_address: addr2,
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: '',
parent_block_hash: '',
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'none',
origin_hash_mode: 1,
event_count: 0,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const stxEvent1: DbStxEvent = {
canonical: true,
event_type: DbEventTypeId.StxAsset,
asset_event_type_id: DbAssetEventTypeId.Transfer,
event_index: 0,
tx_id: '0x1111000000000000000000000000000000000000000000000000000000000000',
tx_index: 1,
block_height: block.block_height,
amount: 1n,
recipient: addr3,
sender: 'none',
};
const stxEvent2: DbStxEvent = {
canonical: true,
event_type: DbEventTypeId.StxAsset,
asset_event_type_id: DbAssetEventTypeId.Transfer,
event_index: 0,
tx_id: '0x1111000000000000000000000000000000000000000000000000000000000000',
tx_index: 1,
block_height: 1,
amount: 1n,
recipient: 'none',
sender: addr4,
};
const ftEvent1: DbFtEvent = {
canonical: true,
event_type: DbEventTypeId.FungibleTokenAsset,
asset_event_type_id: DbAssetEventTypeId.Transfer,
event_index: 0,
tx_id: '0x1111000000000000000000000000000000000000000000000000000000000000',
tx_index: 1,
block_height: block.block_height,
asset_identifier: 'some-asset',
amount: 1n,
recipient: addr5,
sender: 'none',
};
const ftEvent2: DbFtEvent = {
canonical: true,
event_type: DbEventTypeId.FungibleTokenAsset,
asset_event_type_id: DbAssetEventTypeId.Transfer,
event_index: 0,
tx_id: '0x1111000000000000000000000000000000000000000000000000000000000000',
tx_index: 1,
block_height: block.block_height,
asset_identifier: 'some-asset',
amount: 1n,
recipient: 'none',
sender: addr6,
};
const nftEvent1: DbNftEvent = {
canonical: true,
event_type: DbEventTypeId.NonFungibleTokenAsset,
asset_event_type_id: DbAssetEventTypeId.Transfer,
event_index: 0,
tx_id: '0x1111000000000000000000000000000000000000000000000000000000000000',
tx_index: 1,
block_height: block.block_height,
asset_identifier: 'some-asset',
value: serializeCV(intCV(0)),
recipient: addr7,
sender: 'none',
};
const nftEvent2: DbNftEvent = {
canonical: true,
event_type: DbEventTypeId.NonFungibleTokenAsset,
asset_event_type_id: DbAssetEventTypeId.Transfer,
event_index: 0,
tx_id: '0x1111000000000000000000000000000000000000000000000000000000000000',
tx_index: 1,
block_height: block.block_height,
asset_identifier: 'some-asset',
value: serializeCV(intCV(0)),
recipient: 'none',
sender: addr8,
};
const smartContractTx: DbTx = {
type_id: DbTxTypeId.SmartContract,
tx_id: '0x1111880000000000000000000000000000000000000000000000000000000000',
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.alloc(0),
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: '',
parent_block_hash: '',
smart_contract_contract_id: contractAddr1,
smart_contract_source_code: '(some-src)',
block_height: 1,
tx_index: 0,
index_block_hash: block.index_block_hash,
block_hash: block.block_hash,
burn_block_time: 2837565,
parent_burn_block_time: 1626122935,
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'none',
origin_hash_mode: 1,
event_count: 0,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const smartContract: DbSmartContract = {
tx_id: '0x421234',
canonical: true,
block_height: block.block_height,
contract_id: contractAddr1,
source_code: '(some-src)',
abi: '{"some-abi":1}',
};
const dataStoreUpdate: DataStoreBlockUpdateData = {
block: block,
microblocks: [],
minerRewards: [],
txs: [
{
tx: stxTx1,
stxEvents: [stxEvent1, stxEvent2],
stxLockEvents: [],
ftEvents: [ftEvent1, ftEvent2],
nftEvents: [nftEvent1, nftEvent2],
contractLogEvents: [],
smartContracts: [],
names: [],
namespaces: [],
},
{
tx: stxTx2,
stxEvents: [],
stxLockEvents: [],
ftEvents: [],
nftEvents: [],
contractLogEvents: [],
smartContracts: [],
names: [],
namespaces: [],
},
{
tx: smartContractTx,
stxEvents: [],
stxLockEvents: [],
ftEvents: [],
nftEvents: [],
contractLogEvents: [],
smartContracts: [smartContract],
names: [],
namespaces: [],
},
],
};
await db.update(dataStoreUpdate);
// test address as a tx sender
const searchResult1 = await supertest(api.server).get(
`/extended/v1/search/${addr1}?include_metadata`
);
expect(searchResult1.status).toBe(200);
expect(searchResult1.type).toBe('application/json');
const expectedResp1 = {
found: true,
result: {
entity_type: 'standard_address',
entity_id: addr1,
metadata: {
balance: '-1234',
burnchain_lock_height: 0,
burnchain_unlock_height: 0,
lock_height: 0,
lock_tx_id: '',
locked: '0',
total_fees_sent: '1234',
total_miner_rewards_received: '0',
total_received: '0',
total_sent: '0',
},
},
};
expect(JSON.parse(searchResult1.text)).toEqual(expectedResp1);
// test address as a stx tx recipient
const searchResult2 = await supertest(api.server).get(
`/extended/v1/search/${addr2}?include_metadata`
);
expect(searchResult2.status).toBe(200);
expect(searchResult2.type).toBe('application/json');
const expectedResp2 = {
found: true,
result: {
entity_type: 'standard_address',
entity_id: addr2,
metadata: {
balance: '0',
burnchain_lock_height: 0,
burnchain_unlock_height: 0,
lock_height: 0,
lock_tx_id: '',
locked: '0',
total_fees_sent: '0',
total_miner_rewards_received: '0',
total_received: '0',
total_sent: '0',
},
},
};
expect(JSON.parse(searchResult2.text)).toEqual(expectedResp2);
// test address as a stx event recipient
const searchResult3 = await supertest(api.server).get(
`/extended/v1/search/${addr3}?include_metadata`
);
expect(searchResult3.status).toBe(200);
expect(searchResult3.type).toBe('application/json');
const expectedResp3 = {
found: true,
result: {
entity_type: 'standard_address',
entity_id: addr3,
metadata: {
balance: '1',
burnchain_lock_height: 0,
burnchain_unlock_height: 0,
lock_height: 0,
lock_tx_id: '',
locked: '0',
total_fees_sent: '0',
total_miner_rewards_received: '0',
total_received: '1',
total_sent: '0',
},
},
};
expect(JSON.parse(searchResult3.text)).toEqual(expectedResp3);
// test address as a stx event sender
const searchResult4 = await supertest(api.server).get(
`/extended/v1/search/${addr4}?include_metadata=true`
);
expect(searchResult4.status).toBe(200);
expect(searchResult4.type).toBe('application/json');
const expectedResp4 = {
found: true,
result: {
entity_type: 'standard_address',
entity_id: addr4,
metadata: {
balance: '-1',
burnchain_lock_height: 0,
burnchain_unlock_height: 0,
lock_height: 0,
lock_tx_id: '',
locked: '0',
total_fees_sent: '0',
total_miner_rewards_received: '0',
total_received: '0',
total_sent: '1',
},
},
};
expect(JSON.parse(searchResult4.text)).toEqual(expectedResp4);
// test address as a ft event recipient
const searchResult5 = await supertest(api.server).get(
`/extended/v1/search/${addr5}?include_metadata`
);
expect(searchResult5.status).toBe(200);
expect(searchResult5.type).toBe('application/json');
const emptyStandardAddressMetadata = {
balance: '0',
burnchain_lock_height: 0,
burnchain_unlock_height: 0,
lock_height: 0,
lock_tx_id: '',
locked: '0',
total_fees_sent: '0',
total_miner_rewards_received: '0',
total_received: '0',
total_sent: '0',
};
const expectedResp5 = {
found: true,
result: {
entity_type: 'standard_address',
entity_id: addr5,
metadata: emptyStandardAddressMetadata,
},
};
expect(JSON.parse(searchResult5.text)).toEqual(expectedResp5);
// test address as a ft event sender
const searchResult6 = await supertest(api.server).get(
`/extended/v1/search/${addr6}?include_metadata`
);
expect(searchResult6.status).toBe(200);
expect(searchResult6.type).toBe('application/json');
const expectedResp6 = {
found: true,
result: {
entity_type: 'standard_address',
entity_id: addr6,
metadata: emptyStandardAddressMetadata,
},
};
expect(JSON.parse(searchResult6.text)).toEqual(expectedResp6);
// test address as a nft event recipient
const searchResult7 = await supertest(api.server).get(
`/extended/v1/search/${addr7}?include_metadata`
);
expect(searchResult7.status).toBe(200);
expect(searchResult7.type).toBe('application/json');
const expectedResp7 = {
found: true,
result: {
entity_type: 'standard_address',
entity_id: addr7,
metadata: emptyStandardAddressMetadata,
},
};
expect(JSON.parse(searchResult7.text)).toEqual(expectedResp7);
// test address as a nft event sender
const searchResult8 = await supertest(api.server).get(
`/extended/v1/search/${addr8}?include_metadata`
);
expect(searchResult8.status).toBe(200);
expect(searchResult8.type).toBe('application/json');
const expectedResp8 = {
found: true,
result: {
entity_type: 'standard_address',
entity_id: addr8,
metadata: emptyStandardAddressMetadata,
},
};
expect(JSON.parse(searchResult8.text)).toEqual(expectedResp8);
// test contract address
const searchResult9 = await supertest(api.server).get(
`/extended/v1/search/${contractAddr1}?include_metadata=true`
);
expect(searchResult9.status).toBe(200);
expect(searchResult9.type).toBe('application/json');
const expectedResp9 = {
found: true,
result: {
entity_id: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
entity_type: 'contract_address',
tx_data: {
canonical: true,
block_hash: '0x1234',
burn_block_time: 2837565,
block_height: 1,
tx_type: 'smart_contract',
tx_id: '0x1111880000000000000000000000000000000000000000000000000000000000',
},
metadata: {
anchor_mode: 'any',
block_hash: '0x1234',
block_height: 1,
burn_block_time: 2837565,
burn_block_time_iso: '1970-02-02T20:12:45.000Z',
canonical: true,
event_count: 0,
events: [],
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
fee_rate: '1234',
is_unanchored: false,
microblock_canonical: true,
microblock_hash: '',
microblock_sequence: 2147483647,
nonce: 0,
parent_block_hash: '',
parent_burn_block_time: 1626122935,
parent_burn_block_time_iso: '2021-07-12T20:48:55.000Z',
post_condition_mode: 'allow',
post_conditions: [],
sender_address: 'none',
smart_contract: {
contract_id: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
source_code: '(some-src)',
},
sponsored: false,
tx_id: '0x1111880000000000000000000000000000000000000000000000000000000000',
tx_index: 0,
tx_result: {
hex: '0x0100000000000000000000000000000001',
repr: 'u1',
},
tx_status: 'success',
tx_type: 'smart_contract',
},
},
};
expect(JSON.parse(searchResult9.text)).toEqual(expectedResp9);
const smartContractMempoolTx: DbMempoolTx = {
pruned: false,
type_id: DbTxTypeId.SmartContract,
tx_id: '0x1111882200000000000000000000000000000000000000000000000000000000',
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.from('test-raw-tx'),
receipt_time: 123456,
smart_contract_contract_id: contractAddr2,
smart_contract_source_code: '(some-src)',
status: 1,
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'none',
origin_hash_mode: 1,
};
await db.updateMempoolTxs({ mempoolTxs: [smartContractMempoolTx] });
// test contract address associated with mempool tx
const searchResult10 = await supertest(api.server).get(
`/extended/v1/search/${contractAddr2}?include_metadata`
);
expect(searchResult10.status).toBe(200);
expect(searchResult10.type).toBe('application/json');
const expectedResp10 = {
found: true,
result: {
entity_id: 'STSPS4JYDEYCPPCSHE3MM2NCEGR07KPBETNEZCBQ.contract-name',
entity_type: 'contract_address',
tx_data: {
tx_type: 'smart_contract',
tx_id: '0x1111882200000000000000000000000000000000000000000000000000000000',
},
metadata: {
anchor_mode: 'any',
fee_rate: '1234',
nonce: 0,
post_condition_mode: 'allow',
post_conditions: [],
receipt_time: 123456,
receipt_time_iso: '1970-01-02T10:17:36.000Z',
sender_address: 'none',
smart_contract: {
contract_id: 'STSPS4JYDEYCPPCSHE3MM2NCEGR07KPBETNEZCBQ.contract-name',
source_code: '(some-src)',
},
sponsored: false,
tx_id: '0x1111882200000000000000000000000000000000000000000000000000000000',
tx_status: 'success',
tx_type: 'smart_contract',
},
},
};
expect(JSON.parse(searchResult10.text)).toEqual(expectedResp10);
// test contract address not found
const searchResult11 = await supertest(api.server).get(
`/extended/v1/search/${contractAddr3}?include_metadata`
);
expect(searchResult11.status).toBe(404);
expect(searchResult11.type).toBe('application/json');
const expectedResp11 = {
found: false,
result: { entity_type: 'contract_address' },
error:
'No principal found with address "STSPS4JYDEYCPPCSHE3MM2NCEGR07KPBETNEZCBQ.test-contract"',
};
expect(JSON.parse(searchResult11.text)).toEqual(expectedResp11);
// test standard address not found
const searchResult12 = await supertest(api.server).get(
`/extended/v1/search/${addr9}?include_metadata`
);
expect(searchResult12.status).toBe(404);
expect(searchResult12.type).toBe('application/json');
const expectedResp12 = {
found: false,
result: { entity_type: 'standard_address' },
error: 'No principal found with address "STAR26VJ4BC24SMNKRY533MAM0K3JA5ZJDVBD45A"',
};
expect(JSON.parse(searchResult12.text)).toEqual(expectedResp12);
// test invalid term
const invalidTerm = 'bogus123';
const searchResult13 = await supertest(api.server).get(
`/extended/v1/search/${invalidTerm}?include_metadata`
);
expect(searchResult13.status).toBe(404);
expect(searchResult13.type).toBe('application/json');
const expectedResp13 = {
found: false,
result: { entity_type: 'invalid_term' },
error:
'The term "bogus123" is not a valid block hash, transaction ID, contract principal, or account address principal',
};
expect(JSON.parse(searchResult13.text)).toEqual(expectedResp13);
});
test('address transaction transfers', async () => {
const testAddr1 = 'ST3J8EVYHVKH6XXPD61EE8XEHW4Y2K83861225AB1';
const testAddr2 = 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4';
const testContractAddr = 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world';
const testAddr4 = 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C';
const testTxId = '0x12340006';
const block: DbBlock = {
block_hash: '0x1234',
index_block_hash: '0x1234',
parent_index_block_hash: '0x2345',
parent_block_hash: '0x5678',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 100123123,
burn_block_time: 39486,
burn_block_hash: '0x1234',
burn_block_height: 100123123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateBlock(client, block);
let indexIdIndex = 0;
const createStxTx = (
sender: string,
recipient: string,
amount: number,
canonical: boolean = true,
stxEventCount = 1,
ftEventCount = 1,
nftEventCount = 1
): [DbTx, DbStxEvent[], DbFtEvent[], DbNftEvent[]] => {
const tx: DbTx = {
tx_id: '0x1234' + (++indexIdIndex).toString().padStart(4, '0'),
tx_index: indexIdIndex,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.alloc(0),
index_block_hash: '0x5432',
block_hash: '0x9876',
block_height: 68456,
burn_block_time: 1594647994,
parent_burn_block_time: 1626122935,
type_id: DbTxTypeId.TokenTransfer,
token_transfer_amount: BigInt(amount),
token_transfer_memo: Buffer.from('hi'),
token_transfer_recipient_address: recipient,
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: '',
parent_block_hash: '',
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: sender,
origin_hash_mode: 1,
event_count: 0,
execution_cost_read_count: 1,
execution_cost_read_length: 2,
execution_cost_runtime: 3,
execution_cost_write_count: 4,
execution_cost_write_length: 5,
};
const stxEvents: DbStxEvent[] = [];
for (let i = 0; i < stxEventCount; i++) {
const stxEvent: DbStxEvent = {
canonical,
event_type: DbEventTypeId.StxAsset,
asset_event_type_id: DbAssetEventTypeId.Transfer,
event_index: i,
tx_id: tx.tx_id,
tx_index: tx.tx_index,
block_height: tx.block_height,
amount: BigInt(amount),
recipient,
sender,
};
stxEvents.push(stxEvent);
}
const ftEvents: DbFtEvent[] = [];
for (let i = 0; i < ftEventCount; i++) {
const ftEvent: DbFtEvent = {
canonical,
event_type: DbEventTypeId.FungibleTokenAsset,
asset_event_type_id: DbAssetEventTypeId.Transfer,
asset_identifier: 'usdc',
event_index: i,
tx_id: tx.tx_id,
tx_index: tx.tx_index,
block_height: tx.block_height,
amount: BigInt(amount),
recipient,
sender,
};
ftEvents.push(ftEvent);
}
const nftEvents: DbNftEvent[] = [];
for (let i = 0; i < nftEventCount; i++) {
const nftEvent: DbNftEvent = {
canonical,
event_type: DbEventTypeId.NonFungibleTokenAsset,
asset_event_type_id: DbAssetEventTypeId.Transfer,
asset_identifier: 'punk1',
event_index: i,
tx_id: tx.tx_id,
tx_index: tx.tx_index,
block_height: tx.block_height,
value: serializeCV(uintCV(amount)),
recipient,
sender,
};
nftEvents.push(nftEvent);
}
return [tx, stxEvents, ftEvents, nftEvents];
};
const txs = [
createStxTx(testAddr1, testAddr2, 100_000, true, 1, 1, 1),
createStxTx(testAddr2, testContractAddr, 100, true, 1, 2, 1),
createStxTx(testAddr2, testContractAddr, 250, true, 1, 0, 1),
createStxTx(testAddr2, testContractAddr, 40, false, 1, 1, 1),
createStxTx(testContractAddr, testAddr4, 15, true, 1, 1, 0),
createStxTx(testAddr2, testAddr4, 35, true, 3, 1, 2),
];
for (const [tx, stxEvents, ftEvents, nftEvents] of txs) {
await db.updateTx(client, tx);
for (const event of stxEvents) {
await db.updateStxEvent(client, tx, event);
}
for (const event of ftEvents) {
await db.updateFtEvent(client, tx, event);
}
for (const event of nftEvents) {
await db.updateNftEvent(client, tx, event);
}
}
const fetch1 = await supertest(api.server).get(
`/extended/v1/address/${testAddr2}/transactions_with_transfers?limit=3&offset=0`
);
expect(fetch1.status).toBe(200);
expect(fetch1.type).toBe('application/json');
const expected1 = {
limit: 3,
offset: 0,
total: 4,
results: [
{
tx: {
tx_id: '0x12340006',
tx_type: 'token_transfer',
nonce: 0,
anchor_mode: 'any',
fee_rate: '1234',
is_unanchored: false,
sender_address: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
sponsored: false,
post_condition_mode: 'allow',
post_conditions: [],
tx_status: 'success',
block_hash: '0x9876',
block_height: 68456,
burn_block_time: 1594647994,
burn_block_time_iso: '2020-07-13T13:46:34.000Z',
canonical: true,
microblock_canonical: true,
microblock_hash: '',
microblock_sequence: I32_MAX,
parent_block_hash: '',
parent_burn_block_time: 1626122935,
parent_burn_block_time_iso: '2021-07-12T20:48:55.000Z',
tx_index: 6,
tx_result: { hex: '0x0100000000000000000000000000000001', repr: 'u1' },
token_transfer: {
recipient_address: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
amount: '35',
memo: '0x6869',
},
events: [],
event_count: 0,
execution_cost_read_count: 1,
execution_cost_read_length: 2,
execution_cost_runtime: 3,
execution_cost_write_count: 4,
execution_cost_write_length: 5,
},
stx_sent: '1339',
stx_received: '0',
stx_transfers: [
{
amount: '35',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
},
{
amount: '35',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
},
{
amount: '35',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
},
],
ft_transfers: [
{
amount: '35',
asset_identifier: 'usdc',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
},
],
nft_transfers: [
{
asset_identifier: 'punk1',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
value: {
hex: '0x0100000000000000000000000000000023',
repr: 'u35',
},
},
{
asset_identifier: 'punk1',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
value: {
hex: '0x0100000000000000000000000000000023',
repr: 'u35',
},
},
],
},
{
tx: {
tx_id: '0x12340003',
tx_type: 'token_transfer',
nonce: 0,
anchor_mode: 'any',
fee_rate: '1234',
is_unanchored: false,
sender_address: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
sponsored: false,
post_condition_mode: 'allow',
post_conditions: [],
tx_status: 'success',
block_hash: '0x9876',
block_height: 68456,
burn_block_time: 1594647994,
burn_block_time_iso: '2020-07-13T13:46:34.000Z',
canonical: true,
microblock_canonical: true,
microblock_hash: '',
microblock_sequence: I32_MAX,
parent_block_hash: '',
parent_burn_block_time: 1626122935,
parent_burn_block_time_iso: '2021-07-12T20:48:55.000Z',
tx_index: 3,
tx_result: { hex: '0x0100000000000000000000000000000001', repr: 'u1' },
token_transfer: {
recipient_address: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
amount: '250',
memo: '0x6869',
},
events: [],
event_count: 0,
execution_cost_read_count: 1,
execution_cost_read_length: 2,
execution_cost_runtime: 3,
execution_cost_write_count: 4,
execution_cost_write_length: 5,
},
stx_sent: '1484',
stx_received: '0',
stx_transfers: [
{
amount: '250',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
},
],
ft_transfers: [],
nft_transfers: [
{
asset_identifier: 'punk1',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
value: {
hex: '0x01000000000000000000000000000000fa',
repr: 'u250',
},
},
],
},
{
tx: {
tx_id: '0x12340002',
tx_type: 'token_transfer',
nonce: 0,
anchor_mode: 'any',
fee_rate: '1234',
is_unanchored: false,
sender_address: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
sponsored: false,
post_condition_mode: 'allow',
post_conditions: [],
tx_status: 'success',
block_hash: '0x9876',
block_height: 68456,
burn_block_time: 1594647994,
burn_block_time_iso: '2020-07-13T13:46:34.000Z',
canonical: true,
microblock_canonical: true,
microblock_hash: '',
microblock_sequence: I32_MAX,
parent_block_hash: '',
parent_burn_block_time: 1626122935,
parent_burn_block_time_iso: '2021-07-12T20:48:55.000Z',
tx_index: 2,
tx_result: { hex: '0x0100000000000000000000000000000001', repr: 'u1' },
token_transfer: {
recipient_address: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
amount: '100',
memo: '0x6869',
},
events: [],
event_count: 0,
execution_cost_read_count: 1,
execution_cost_read_length: 2,
execution_cost_runtime: 3,
execution_cost_write_count: 4,
execution_cost_write_length: 5,
},
stx_sent: '1334',
stx_received: '0',
stx_transfers: [
{
amount: '100',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
},
],
ft_transfers: [
{
amount: '100',
asset_identifier: 'usdc',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
},
{
amount: '100',
asset_identifier: 'usdc',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
},
],
nft_transfers: [
{
asset_identifier: 'punk1',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
value: {
hex: '0x0100000000000000000000000000000064',
repr: 'u100',
},
},
],
},
],
};
expect(JSON.parse(fetch1.text)).toEqual(expected1);
// testing single txs information based on given tx_id
const fetchSingleTxInformation = await supertest(api.server).get(
`/extended/v1/address/${testAddr4}/${testTxId}/with_transfers`
);
expect(fetchSingleTxInformation.status).toBe(200);
expect(fetchSingleTxInformation.type).toBe('application/json');
const expectedSingleTxInformation = {
tx: {
tx_id: '0x12340006',
tx_type: 'token_transfer',
nonce: 0,
anchor_mode: 'any',
fee_rate: '1234',
is_unanchored: false,
sender_address: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
sponsored: false,
post_condition_mode: 'allow',
post_conditions: [],
tx_status: 'success',
block_hash: '0x9876',
block_height: 68456,
burn_block_time: 1594647994,
burn_block_time_iso: '2020-07-13T13:46:34.000Z',
canonical: true,
microblock_canonical: true,
microblock_hash: '',
microblock_sequence: I32_MAX,
parent_block_hash: '',
parent_burn_block_time: 1626122935,
parent_burn_block_time_iso: '2021-07-12T20:48:55.000Z',
tx_index: 6,
tx_result: { hex: '0x0100000000000000000000000000000001', repr: 'u1' },
token_transfer: {
recipient_address: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
amount: '35',
memo: '0x6869',
},
events: [],
event_count: 0,
execution_cost_read_count: 1,
execution_cost_read_length: 2,
execution_cost_runtime: 3,
execution_cost_write_count: 4,
execution_cost_write_length: 5,
},
stx_sent: '0',
stx_received: '105',
stx_transfers: [
{
amount: '35',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
},
{
amount: '35',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
},
{
amount: '35',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
},
],
};
expect(JSON.parse(fetchSingleTxInformation.text)).toEqual(expectedSingleTxInformation);
// testing for multiple tx_ids given a single stx addr
const fetch2 = await supertest(api.server).get(
`/extended/v1/address/${testAddr4}/transactions_with_transfers`
);
expect(fetch2.status).toBe(200);
expect(fetch2.type).toBe('application/json');
const expected2 = {
limit: 20,
offset: 0,
total: 2,
results: [
{
tx: {
tx_id: '0x12340006',
tx_type: 'token_transfer',
nonce: 0,
anchor_mode: 'any',
fee_rate: '1234',
is_unanchored: false,
sender_address: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
sponsored: false,
post_condition_mode: 'allow',
post_conditions: [],
tx_status: 'success',
block_hash: '0x9876',
block_height: 68456,
burn_block_time: 1594647994,
burn_block_time_iso: '2020-07-13T13:46:34.000Z',
canonical: true,
microblock_canonical: true,
microblock_hash: '',
microblock_sequence: I32_MAX,
parent_block_hash: '',
parent_burn_block_time: 1626122935,
parent_burn_block_time_iso: '2021-07-12T20:48:55.000Z',
tx_index: 6,
tx_result: { hex: '0x0100000000000000000000000000000001', repr: 'u1' },
token_transfer: {
recipient_address: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
amount: '35',
memo: '0x6869',
},
events: [],
event_count: 0,
execution_cost_read_count: 1,
execution_cost_read_length: 2,
execution_cost_runtime: 3,
execution_cost_write_count: 4,
execution_cost_write_length: 5,
},
stx_sent: '0',
stx_received: '105',
stx_transfers: [
{
amount: '35',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
},
{
amount: '35',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
},
{
amount: '35',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
},
],
ft_transfers: [
{
amount: '35',
asset_identifier: 'usdc',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
},
],
nft_transfers: [
{
asset_identifier: 'punk1',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
value: {
hex: '0x0100000000000000000000000000000023',
repr: 'u35',
},
},
{
asset_identifier: 'punk1',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
value: {
hex: '0x0100000000000000000000000000000023',
repr: 'u35',
},
},
],
},
{
tx: {
tx_id: '0x12340005',
tx_type: 'token_transfer',
nonce: 0,
anchor_mode: 'any',
fee_rate: '1234',
is_unanchored: false,
sender_address: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
sponsored: false,
post_condition_mode: 'allow',
post_conditions: [],
tx_status: 'success',
block_hash: '0x9876',
block_height: 68456,
burn_block_time: 1594647994,
burn_block_time_iso: '2020-07-13T13:46:34.000Z',
canonical: true,
microblock_canonical: true,
microblock_hash: '',
microblock_sequence: I32_MAX,
parent_block_hash: '',
parent_burn_block_time: 1626122935,
parent_burn_block_time_iso: '2021-07-12T20:48:55.000Z',
tx_index: 5,
tx_result: { hex: '0x0100000000000000000000000000000001', repr: 'u1' },
token_transfer: {
recipient_address: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
amount: '15',
memo: '0x6869',
},
events: [],
event_count: 0,
execution_cost_read_count: 1,
execution_cost_read_length: 2,
execution_cost_runtime: 3,
execution_cost_write_count: 4,
execution_cost_write_length: 5,
},
stx_sent: '0',
stx_received: '15',
stx_transfers: [
{
amount: '15',
sender: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
recipient: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
},
],
ft_transfers: [
{
amount: '15',
asset_identifier: 'usdc',
sender: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
recipient: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
},
],
nft_transfers: [],
},
],
};
expect(JSON.parse(fetch2.text)).toEqual(expected2);
});
test('address info', async () => {
const testAddr1 = 'ST3J8EVYHVKH6XXPD61EE8XEHW4Y2K83861225AB1';
const testAddr2 = 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4';
const testContractAddr = 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world';
const testAddr4 = 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C';
const testAddr5 = 'ST3V11C6X2EBFN72RMS3B1NYQ1BX98F61GVYRDRXW';
const testAddr6 = 'ST2F8G7616B2F8PYG216BX9AJCHP7YRK7ND7M0ZN3';
const block: DbBlock = {
block_hash: '0x1234',
index_block_hash: '0x1234',
parent_index_block_hash: '0x2345',
parent_block_hash: '0x5678',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 39486,
burn_block_hash: '0x1234',
burn_block_height: 100123123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
let indexIdIndex = 0;
const createStxTx = (
sender: string,
recipient: string,
amount: number,
canonical: boolean = true
): DbTx => {
const tx: DbTx = {
tx_id: '0x1234' + (++indexIdIndex).toString().padStart(4, '0'),
tx_index: indexIdIndex,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.alloc(0),
index_block_hash: block.index_block_hash,
block_hash: block.block_hash,
block_height: block.block_height,
burn_block_time: block.burn_block_time,
parent_burn_block_time: 1626122935,
type_id: DbTxTypeId.TokenTransfer,
token_transfer_amount: BigInt(amount),
token_transfer_memo: Buffer.from('hi'),
token_transfer_recipient_address: recipient,
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: '',
parent_block_hash: '',
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: sender,
origin_hash_mode: 1,
event_count: 0,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
return tx;
};
const txs = [
createStxTx(testAddr1, testAddr2, 100_000),
createStxTx(testAddr2, testContractAddr, 100),
createStxTx(testAddr2, testContractAddr, 250),
createStxTx(testAddr2, testContractAddr, 40, false),
createStxTx(testContractAddr, testAddr4, 15),
createStxTx(testAddr2, testAddr4, 35),
];
const tx: DbTx = {
tx_id: '0x1234',
tx_index: 4,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.alloc(0),
index_block_hash: block.index_block_hash,
block_hash: block.block_hash,
block_height: block.block_height,
burn_block_time: block.burn_block_time,
parent_burn_block_time: 1626122935,
type_id: DbTxTypeId.Coinbase,
coinbase_payload: Buffer.from('coinbase hi'),
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: '',
parent_block_hash: '',
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: testAddr1,
origin_hash_mode: 1,
event_count: 5,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const createStxEvent = (
sender: string,
recipient: string,
amount: number,
canonical: boolean = true
): DbStxEvent => {
const stxEvent: DbStxEvent = {
canonical,
event_type: DbEventTypeId.StxAsset,
asset_event_type_id: DbAssetEventTypeId.Transfer,
event_index: 0,
tx_id: tx.tx_id,
tx_index: tx.tx_index,
block_height: tx.block_height,
amount: BigInt(amount),
recipient,
sender,
};
return stxEvent;
};
const events = [
createStxEvent(testAddr1, testAddr2, 100_000),
createStxEvent(testAddr2, testContractAddr, 100),
createStxEvent(testAddr2, testContractAddr, 1250),
createStxEvent(testAddr2, testContractAddr, 40, false),
createStxEvent(testContractAddr, testAddr4, 15),
createStxEvent(testAddr2, testAddr4, 35),
];
const createFtEvent = (
sender: string,
recipient: string,
assetId: string,
amount: number,
canonical: boolean = true
): DbFtEvent => {
const ftEvent: DbFtEvent = {
canonical,
event_type: DbEventTypeId.FungibleTokenAsset,
asset_event_type_id: DbAssetEventTypeId.Transfer,
event_index: 0,
tx_id: tx.tx_id,
tx_index: tx.tx_index,
block_height: tx.block_height,
asset_identifier: assetId,
amount: BigInt(amount),
recipient,
sender,
};
return ftEvent;
};
const ftEvents = [
createFtEvent(testAddr1, testAddr2, 'bux', 100_000),
createFtEvent(testAddr2, testContractAddr, 'bux', 100),
createFtEvent(testAddr2, testContractAddr, 'bux', 250),
createFtEvent(testAddr2, testContractAddr, 'bux', 40, false),
createFtEvent(testContractAddr, testAddr4, 'bux', 15),
createFtEvent(testAddr2, testAddr4, 'bux', 35),
createFtEvent(testAddr1, testAddr2, 'gox', 200_000),
createFtEvent(testAddr2, testContractAddr, 'gox', 200),
createFtEvent(testAddr2, testContractAddr, 'gox', 350),
createFtEvent(testAddr2, testContractAddr, 'gox', 60, false),
createFtEvent(testContractAddr, testAddr4, 'gox', 25),
createFtEvent(testAddr2, testAddr4, 'gox', 75),
createFtEvent(testAddr1, testAddr2, 'cash', 500_000),
createFtEvent(testAddr2, testAddr1, 'tendies', 1_000_000),
];
const createNFtEvents = (
sender: string,
recipient: string,
assetId: string,
count: number,
canonical: boolean = true
): DbNftEvent[] => {
const events: DbNftEvent[] = [];
for (let i = 0; i < count; i++) {
const nftEvent: DbNftEvent = {
canonical,
event_type: DbEventTypeId.NonFungibleTokenAsset,
asset_event_type_id: DbAssetEventTypeId.Transfer,
event_index: 0,
tx_id: tx.tx_id,
tx_index: tx.tx_index,
block_height: tx.block_height,
asset_identifier: assetId,
value: serializeCV(intCV(0)),
recipient,
sender,
};
events.push(nftEvent);
}
return events;
};
const nftEvents = [
createNFtEvents(testAddr1, testAddr2, 'bux', 300),
createNFtEvents(testAddr2, testContractAddr, 'bux', 10),
createNFtEvents(testAddr2, testContractAddr, 'bux', 25),
createNFtEvents(testAddr2, testContractAddr, 'bux', 4, false),
createNFtEvents(testContractAddr, testAddr4, 'bux', 1),
createNFtEvents(testAddr2, testAddr4, 'bux', 3),
createNFtEvents(testAddr1, testAddr2, 'gox', 200),
createNFtEvents(testAddr2, testContractAddr, 'gox', 20),
createNFtEvents(testAddr2, testContractAddr, 'gox', 35),
createNFtEvents(testAddr2, testContractAddr, 'gox', 6, false),
createNFtEvents(testContractAddr, testAddr4, 'gox', 2),
createNFtEvents(testAddr2, testAddr4, 'gox', 7),
createNFtEvents(testAddr1, testAddr2, 'cash', 500),
createNFtEvents(testAddr2, testAddr1, 'tendies', 100),
];
const contractJsonAbi = {
maps: [],
functions: [
{
args: [
{ type: 'uint128', name: 'amount' },
{ type: 'string-ascii', name: 'desc' },
],
name: 'test-contract-fn',
access: 'public',
outputs: {
type: {
response: {
ok: 'uint128',
error: 'none',
},
},
},
},
],
variables: [],
fungible_tokens: [],
non_fungible_tokens: [],
};
const contractLogEvent1: DbSmartContractEvent = {
event_index: 4,
tx_id: '0x421234',
tx_index: 0,
block_height: block.block_height,
canonical: true,
event_type: DbEventTypeId.SmartContractLog,
contract_identifier: testContractAddr,
topic: 'some-topic',
value: serializeCV(bufferCVFromString('some val')),
};
const smartContract1: DbSmartContract = {
tx_id: '0x421234',
canonical: true,
block_height: block.block_height,
contract_id: testContractAddr,
source_code: '(some-contract-src)',
abi: JSON.stringify(contractJsonAbi),
};
const contractCall: DbTx = {
tx_id: '0x1232000000000000000000000000000000000000000000000000000000000000',
tx_index: 5,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.alloc(0),
index_block_hash: block.index_block_hash,
block_hash: block.block_hash,
block_height: block.block_height,
burn_block_time: block.burn_block_time,
parent_burn_block_time: 1626122935,
type_id: DbTxTypeId.ContractCall,
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: '',
parent_block_hash: '',
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 10n,
sponsored: false,
sponsor_address: testAddr1,
sender_address: testContractAddr,
origin_hash_mode: 1,
event_count: 5,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
contract_call_contract_id: testContractAddr,
contract_call_function_name: 'test-contract-fn',
contract_call_function_args: createClarityValueArray(uintCV(123456), stringAsciiCV('hello')),
abi: JSON.stringify(contractJsonAbi),
};
const dataStoreTxs = txs.map(dbTx => {
return {
tx: dbTx,
stxLockEvents: [],
stxEvents: [],
ftEvents: [],
nftEvents: [],
contractLogEvents: [],
smartContracts: [],
names: [],
namespaces: [],
} as DataStoreTxEventData;
});
dataStoreTxs.push({
tx: tx,
stxLockEvents: [],
stxEvents: events,
ftEvents: ftEvents,
nftEvents: nftEvents.flat(),
contractLogEvents: [contractLogEvent1],
smartContracts: [smartContract1],
names: [],
namespaces: [],
});
dataStoreTxs.push({
tx: contractCall,
stxLockEvents: [],
stxEvents: [
{
canonical: true,
event_type: DbEventTypeId.StxAsset,
asset_event_type_id: DbAssetEventTypeId.Transfer,
event_index: 0,
tx_id: contractCall.tx_id,
tx_index: contractCall.tx_index,
block_height: contractCall.block_height,
amount: 4321n,
sender: testAddr5,
recipient: testAddr6,
},
],
ftEvents: [],
nftEvents: [],
contractLogEvents: [],
smartContracts: [],
names: [],
namespaces: [],
});
await db.update({
block: block,
microblocks: [],
minerRewards: [],
txs: dataStoreTxs,
});
const tokenOfferingLocked: DbTokenOfferingLocked = {
address: testAddr2,
value: BigInt(4139394444),
block: 1,
};
await db.updateBatchTokenOfferingLocked(client, [tokenOfferingLocked]);
const fetchAddrBalance1 = await supertest(api.server).get(
`/extended/v1/address/${testAddr2}/balances`
);
expect(fetchAddrBalance1.status).toBe(200);
expect(fetchAddrBalance1.type).toBe('application/json');
const expectedResp1 = {
stx: {
balance: '94913',
total_sent: '1385',
total_received: '100000',
total_fees_sent: '3702',
total_miner_rewards_received: '0',
burnchain_lock_height: 0,
burnchain_unlock_height: 0,
lock_height: 0,
lock_tx_id: '',
locked: '0',
},
fungible_tokens: {
bux: { balance: '99615', total_sent: '385', total_received: '100000' },
cash: { balance: '500000', total_sent: '0', total_received: '500000' },
gox: { balance: '199375', total_sent: '625', total_received: '200000' },
tendies: { balance: '-1000000', total_sent: '1000000', total_received: '0' },
},
non_fungible_tokens: {
bux: { count: '262', total_sent: '38', total_received: '300' },
cash: { count: '500', total_sent: '0', total_received: '500' },
gox: { count: '138', total_sent: '62', total_received: '200' },
tendies: { count: '-100', total_sent: '100', total_received: '0' },
},
token_offering_locked: {
total_locked: '0',
total_unlocked: '4139394444',
unlock_schedule: [
{
amount: '4139394444',
block_height: 1,
},
],
},
};
expect(JSON.parse(fetchAddrBalance1.text)).toEqual(expectedResp1);
const fetchAddrBalance2 = await supertest(api.server).get(
`/extended/v1/address/${testContractAddr}/balances`
);
expect(fetchAddrBalance2.status).toBe(200);
expect(fetchAddrBalance2.type).toBe('application/json');
const expectedResp2 = {
stx: {
balance: '91',
total_sent: '15',
total_received: '1350',
total_fees_sent: '1244',
total_miner_rewards_received: '0',
burnchain_lock_height: 0,
burnchain_unlock_height: 0,
lock_height: 0,
lock_tx_id: '',
locked: '0',
},
fungible_tokens: {
bux: { balance: '335', total_sent: '15', total_received: '350' },
gox: { balance: '525', total_sent: '25', total_received: '550' },
},
non_fungible_tokens: {
bux: { count: '34', total_sent: '1', total_received: '35' },
gox: { count: '53', total_sent: '2', total_received: '55' },
},
};
expect(JSON.parse(fetchAddrBalance2.text)).toEqual(expectedResp2);
const tokenLocked: DbTokenOfferingLocked = {
address: testContractAddr,
value: BigInt(4139391122),
block: 1,
};
await db.updateBatchTokenOfferingLocked(client, [tokenLocked]);
const fetchAddrStxBalance1 = await supertest(api.server).get(
`/extended/v1/address/${testContractAddr}/stx`
);
expect(fetchAddrStxBalance1.status).toBe(200);
expect(fetchAddrStxBalance1.type).toBe('application/json');
const expectedStxResp1 = {
balance: '91',
total_sent: '15',
total_received: '1350',
total_fees_sent: '1244',
total_miner_rewards_received: '0',
burnchain_lock_height: 0,
burnchain_unlock_height: 0,
lock_height: 0,
lock_tx_id: '',
locked: '0',
token_offering_locked: {
total_locked: '0',
total_unlocked: '4139391122',
unlock_schedule: [
{
amount: '4139391122',
block_height: 1,
},
],
},
};
expect(JSON.parse(fetchAddrStxBalance1.text)).toEqual(expectedStxResp1);
const fetchAddrAssets1 = await supertest(api.server).get(
`/extended/v1/address/${testContractAddr}/assets?limit=8&offset=2`
);
expect(fetchAddrAssets1.status).toBe(200);
expect(fetchAddrAssets1.type).toBe('application/json');
const expectedResp3 = {
limit: 8,
offset: 2,
total: 102,
results: [
{
event_index: 0,
event_type: 'fungible_token_asset',
tx_id: '0x1234',
asset: {
asset_event_type: 'transfer',
asset_id: 'bux',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
amount: '100',
},
},
{
event_index: 0,
event_type: 'fungible_token_asset',
tx_id: '0x1234',
asset: {
asset_event_type: 'transfer',
asset_id: 'bux',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
amount: '250',
},
},
{
event_index: 0,
event_type: 'fungible_token_asset',
tx_id: '0x1234',
asset: {
asset_event_type: 'transfer',
asset_id: 'bux',
sender: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
recipient: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
amount: '15',
},
},
{
event_index: 0,
event_type: 'fungible_token_asset',
tx_id: '0x1234',
asset: {
asset_event_type: 'transfer',
asset_id: 'gox',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
amount: '200',
},
},
{
event_index: 0,
event_type: 'fungible_token_asset',
tx_id: '0x1234',
asset: {
asset_event_type: 'transfer',
asset_id: 'gox',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
amount: '350',
},
},
{
event_index: 0,
event_type: 'fungible_token_asset',
tx_id: '0x1234',
asset: {
asset_event_type: 'transfer',
asset_id: 'gox',
sender: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
recipient: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
amount: '25',
},
},
{
event_index: 0,
event_type: 'non_fungible_token_asset',
tx_id: '0x1234',
asset: {
asset_event_type: 'transfer',
asset_id: 'bux',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
value: { hex: '0x0000000000000000000000000000000000', repr: '0' },
},
},
{
event_index: 0,
event_type: 'stx_asset',
tx_id: '0x1234',
asset: {
asset_event_type: 'transfer',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
amount: '100',
},
},
],
};
expect(JSON.parse(fetchAddrAssets1.text)).toEqual(expectedResp3);
const fetchAddrTx1 = await supertest(api.server).get(
`/extended/v1/address/${testContractAddr}/transactions`
);
expect(fetchAddrTx1.status).toBe(200);
expect(fetchAddrTx1.type).toBe('application/json');
const expectedResp4 = {
limit: 20,
offset: 0,
total: 4,
results: [
{
tx_id: '0x12340005',
tx_status: 'success',
tx_result: {
hex: '0x0100000000000000000000000000000001', // u1
repr: 'u1',
},
tx_type: 'token_transfer',
fee_rate: '1234',
is_unanchored: false,
nonce: 0,
anchor_mode: 'any',
sender_address: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
sponsored: false,
post_condition_mode: 'allow',
post_conditions: [],
block_hash: '0x1234',
block_height: 1,
burn_block_time: 39486,
burn_block_time_iso: '1970-01-01T10:58:06.000Z',
canonical: true,
microblock_canonical: true,
microblock_hash: '',
microblock_sequence: I32_MAX,
parent_block_hash: '',
parent_burn_block_time: 1626122935,
parent_burn_block_time_iso: '2021-07-12T20:48:55.000Z',
tx_index: 5,
token_transfer: {
recipient_address: 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C',
amount: '15',
memo: '0x6869',
},
event_count: 0,
events: [],
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
},
{
tx_id: '0x1232000000000000000000000000000000000000000000000000000000000000',
tx_status: 'success',
tx_result: {
hex: '0x0100000000000000000000000000000001', // u1
repr: 'u1',
},
tx_type: 'contract_call',
fee_rate: '10',
is_unanchored: false,
nonce: 0,
anchor_mode: 'any',
sender_address: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
sponsor_address: 'ST3J8EVYHVKH6XXPD61EE8XEHW4Y2K83861225AB1',
sponsored: false,
post_condition_mode: 'allow',
post_conditions: [],
block_hash: '0x1234',
block_height: 1,
burn_block_time: 39486,
burn_block_time_iso: '1970-01-01T10:58:06.000Z',
canonical: true,
microblock_canonical: true,
microblock_hash: '',
microblock_sequence: I32_MAX,
parent_block_hash: '',
parent_burn_block_time: 1626122935,
parent_burn_block_time_iso: '2021-07-12T20:48:55.000Z',
tx_index: 5,
contract_call: {
contract_id: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
function_name: 'test-contract-fn',
function_signature:
'(define-public (test-contract-fn (amount uint) (desc string-ascii)))',
function_args: [
{
hex: '0x010000000000000000000000000001e240',
name: 'amount',
repr: 'u123456',
type: 'uint',
},
{
hex: '0x0d0000000568656c6c6f',
name: 'desc',
repr: '"hello"',
type: 'string-ascii',
},
],
},
event_count: 5,
events: [],
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
},
{
tx_id: '0x12340003',
tx_status: 'success',
tx_result: {
hex: '0x0100000000000000000000000000000001', // u1
repr: 'u1',
},
tx_type: 'token_transfer',
fee_rate: '1234',
is_unanchored: false,
nonce: 0,
anchor_mode: 'any',
sender_address: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
sponsored: false,
post_condition_mode: 'allow',
post_conditions: [],
block_hash: '0x1234',
block_height: 1,
burn_block_time: 39486,
burn_block_time_iso: '1970-01-01T10:58:06.000Z',
canonical: true,
microblock_canonical: true,
microblock_hash: '',
microblock_sequence: I32_MAX,
parent_block_hash: '',
parent_burn_block_time: 1626122935,
parent_burn_block_time_iso: '2021-07-12T20:48:55.000Z',
tx_index: 3,
token_transfer: {
recipient_address: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
amount: '250',
memo: '0x6869',
},
event_count: 0,
events: [],
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
},
{
tx_id: '0x12340002',
tx_status: 'success',
tx_result: {
hex: '0x0100000000000000000000000000000001', // u1
repr: 'u1',
},
tx_type: 'token_transfer',
fee_rate: '1234',
is_unanchored: false,
nonce: 0,
anchor_mode: 'any',
sender_address: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
sponsored: false,
post_condition_mode: 'allow',
post_conditions: [],
block_hash: '0x1234',
block_height: 1,
burn_block_time: 39486,
burn_block_time_iso: '1970-01-01T10:58:06.000Z',
canonical: true,
microblock_canonical: true,
microblock_hash: '',
microblock_sequence: I32_MAX,
parent_block_hash: '',
parent_burn_block_time: 1626122935,
parent_burn_block_time_iso: '2021-07-12T20:48:55.000Z',
tx_index: 2,
token_transfer: {
recipient_address: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
amount: '100',
memo: '0x6869',
},
event_count: 0,
events: [],
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
},
],
};
expect(JSON.parse(fetchAddrTx1.text)).toEqual(expectedResp4);
const fetchAddrTx2 = await supertest(api.server).get(
`/extended/v1/address/${testAddr5}/transactions`
);
expect(fetchAddrTx2.status).toBe(200);
expect(fetchAddrTx2.type).toBe('application/json');
const expectedResp5 = {
limit: 20,
offset: 0,
total: 1,
results: [
{
tx_id: '0x1232000000000000000000000000000000000000000000000000000000000000',
tx_status: 'success',
tx_result: {
hex: '0x0100000000000000000000000000000001', // u1
repr: 'u1',
},
tx_type: 'contract_call',
fee_rate: '10',
is_unanchored: false,
nonce: 0,
anchor_mode: 'any',
sender_address: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
sponsor_address: 'ST3J8EVYHVKH6XXPD61EE8XEHW4Y2K83861225AB1',
sponsored: false,
post_condition_mode: 'allow',
post_conditions: [],
block_hash: '0x1234',
block_height: 1,
burn_block_time: 39486,
burn_block_time_iso: '1970-01-01T10:58:06.000Z',
canonical: true,
microblock_canonical: true,
microblock_hash: '',
microblock_sequence: I32_MAX,
parent_block_hash: '',
parent_burn_block_time: 1626122935,
parent_burn_block_time_iso: '2021-07-12T20:48:55.000Z',
tx_index: 5,
contract_call: {
contract_id: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
function_name: 'test-contract-fn',
function_signature:
'(define-public (test-contract-fn (amount uint) (desc string-ascii)))',
function_args: [
{
hex: '0x010000000000000000000000000001e240',
name: 'amount',
repr: 'u123456',
type: 'uint',
},
{
hex: '0x0d0000000568656c6c6f',
name: 'desc',
repr: '"hello"',
type: 'string-ascii',
},
],
},
event_count: 5,
events: [],
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
},
],
};
expect(JSON.parse(fetchAddrTx2.text)).toEqual(expectedResp5);
const fetchAddrTx3 = await supertest(api.server).get(
`/extended/v1/address/${testAddr5}/transactions_with_transfers`
);
expect(fetchAddrTx3.status).toBe(200);
expect(fetchAddrTx3.type).toBe('application/json');
const expectedResp6 = {
limit: 20,
offset: 0,
total: 1,
results: [
{
ft_transfers: [],
nft_transfers: [],
stx_received: '0',
stx_sent: '4321',
stx_transfers: [
{
amount: '4321',
recipient: 'ST2F8G7616B2F8PYG216BX9AJCHP7YRK7ND7M0ZN3',
sender: 'ST3V11C6X2EBFN72RMS3B1NYQ1BX98F61GVYRDRXW',
},
],
tx: {
anchor_mode: 'any',
block_hash: '0x1234',
block_height: 1,
burn_block_time: 39486,
burn_block_time_iso: '1970-01-01T10:58:06.000Z',
canonical: true,
contract_call: {
contract_id: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
function_args: [
{
hex: '0x010000000000000000000000000001e240',
name: 'amount',
repr: 'u123456',
type: 'uint',
},
{
hex: '0x0d0000000568656c6c6f',
name: 'desc',
repr: '"hello"',
type: 'string-ascii',
},
],
function_name: 'test-contract-fn',
function_signature:
'(define-public (test-contract-fn (amount uint) (desc string-ascii)))',
},
event_count: 5,
events: [],
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
fee_rate: '10',
is_unanchored: false,
microblock_canonical: true,
microblock_hash: '',
microblock_sequence: 2147483647,
nonce: 0,
parent_block_hash: '',
parent_burn_block_time: 1626122935,
parent_burn_block_time_iso: '2021-07-12T20:48:55.000Z',
post_condition_mode: 'allow',
post_conditions: [],
sender_address: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
sponsor_address: 'ST3J8EVYHVKH6XXPD61EE8XEHW4Y2K83861225AB1',
sponsored: false,
tx_id: '0x1232000000000000000000000000000000000000000000000000000000000000',
tx_index: 5,
tx_result: {
hex: '0x0100000000000000000000000000000001',
repr: 'u1',
},
tx_status: 'success',
tx_type: 'contract_call',
},
},
],
};
expect(JSON.parse(fetchAddrTx3.text)).toEqual(expectedResp6);
const fetchAddrTx4 = await supertest(api.server).get(
`/extended/v1/address/${testAddr5}/0x1232000000000000000000000000000000000000000000000000000000000000/with_transfers`
);
expect(fetchAddrTx4.status).toBe(200);
expect(fetchAddrTx4.type).toBe('application/json');
const expectedResp7 = {
stx_received: '0',
stx_sent: '4321',
stx_transfers: [
{
amount: '4321',
recipient: 'ST2F8G7616B2F8PYG216BX9AJCHP7YRK7ND7M0ZN3',
sender: 'ST3V11C6X2EBFN72RMS3B1NYQ1BX98F61GVYRDRXW',
},
],
tx: {
anchor_mode: 'any',
block_hash: '0x1234',
block_height: 1,
burn_block_time: 39486,
burn_block_time_iso: '1970-01-01T10:58:06.000Z',
canonical: true,
contract_call: {
contract_id: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
function_args: [
{
hex: '0x010000000000000000000000000001e240',
name: 'amount',
repr: 'u123456',
type: 'uint',
},
{
hex: '0x0d0000000568656c6c6f',
name: 'desc',
repr: '"hello"',
type: 'string-ascii',
},
],
function_name: 'test-contract-fn',
function_signature:
'(define-public (test-contract-fn (amount uint) (desc string-ascii)))',
},
event_count: 5,
events: [],
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
fee_rate: '10',
is_unanchored: false,
microblock_canonical: true,
microblock_hash: '',
microblock_sequence: 2147483647,
nonce: 0,
parent_block_hash: '',
parent_burn_block_time: 1626122935,
parent_burn_block_time_iso: '2021-07-12T20:48:55.000Z',
post_condition_mode: 'allow',
post_conditions: [],
sender_address: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
sponsor_address: 'ST3J8EVYHVKH6XXPD61EE8XEHW4Y2K83861225AB1',
sponsored: false,
tx_id: '0x1232000000000000000000000000000000000000000000000000000000000000',
tx_index: 5,
tx_result: {
hex: '0x0100000000000000000000000000000001',
repr: 'u1',
},
tx_status: 'success',
tx_type: 'contract_call',
},
};
expect(JSON.parse(fetchAddrTx4.text)).toEqual(expectedResp7);
const contractCallExpectedResults = {
tx_id: '0x1232000000000000000000000000000000000000000000000000000000000000',
tx_status: 'success',
tx_result: {
hex: '0x0100000000000000000000000000000001', // u1
repr: 'u1',
},
tx_type: 'contract_call',
fee_rate: '10',
is_unanchored: false,
nonce: 0,
anchor_mode: 'any',
sender_address: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
sponsor_address: 'ST3J8EVYHVKH6XXPD61EE8XEHW4Y2K83861225AB1',
sponsored: false,
post_condition_mode: 'allow',
post_conditions: [],
block_hash: '0x1234',
block_height: 1,
burn_block_time: 39486,
burn_block_time_iso: '1970-01-01T10:58:06.000Z',
canonical: true,
microblock_canonical: true,
microblock_hash: '',
microblock_sequence: I32_MAX,
parent_block_hash: '',
parent_burn_block_time: 1626122935,
parent_burn_block_time_iso: '2021-07-12T20:48:55.000Z',
tx_index: 5,
contract_call: {
contract_id: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
function_name: 'test-contract-fn',
function_signature: '(define-public (test-contract-fn (amount uint) (desc string-ascii)))',
function_args: [
{
hex: '0x010000000000000000000000000001e240',
name: 'amount',
repr: 'u123456',
type: 'uint',
},
{
hex: '0x0d0000000568656c6c6f',
name: 'desc',
repr: '"hello"',
type: 'string-ascii',
},
],
},
event_count: 5,
events: [],
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const blockTxsRows = await api.datastore.getBlockTxsRows(block.block_hash);
expect(blockTxsRows.found).toBe(true);
const blockTxsRowsResult = blockTxsRows.result as DbTx[];
expect(blockTxsRowsResult.find(tx => tx.tx_id === contractCall.tx_id)).toEqual({
...contractCall,
...{ abi: contractJsonAbi },
});
const searchResult8 = await supertest(api.server).get(
`/extended/v1/search/0x1232000000000000000000000000000000000000000000000000000000000000?include_metadata`
);
expect(searchResult8.status).toBe(200);
expect(searchResult8.type).toBe('application/json');
expect(JSON.parse(searchResult8.text).result.metadata).toEqual(contractCallExpectedResults);
const blockTxResult = await db.getTxsFromBlock('0x1234', 20, 0);
expect(blockTxResult.results[6]).toEqual({ ...contractCall, ...{ abi: contractJsonAbi } });
});
test('list contract log events', async () => {
const block1: DbBlock = {
block_hash: '0x1234',
index_block_hash: '0xdeadbeef',
parent_index_block_hash: '0x00',
parent_block_hash: '0xff0011',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 1594647996,
burn_block_hash: '0x1234',
burn_block_height: 123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const tx1: DbTx = {
tx_id: '0x421234',
tx_index: 0,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.alloc(0),
index_block_hash: '0x1234',
block_hash: '0x5678',
block_height: block1.block_height,
burn_block_time: 1594647995,
parent_burn_block_time: 1626122935,
type_id: DbTxTypeId.Coinbase,
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: '',
parent_block_hash: '',
post_conditions: Buffer.from([]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'sender-addr',
origin_hash_mode: 1,
coinbase_payload: Buffer.from('hi'),
event_count: 0,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const tx2: DbTx = {
...tx1,
tx_id: '0x012345',
tx_index: 1,
};
const contractLogEvent1: DbSmartContractEvent = {
event_index: 4,
tx_id: '0x421234',
tx_index: 0,
block_height: block1.block_height,
canonical: true,
event_type: DbEventTypeId.SmartContractLog,
contract_identifier: 'some-contract-id',
topic: 'some-topic',
value: serializeCV(bufferCVFromString('some val')),
};
const smartContract1: DbSmartContract = {
tx_id: '0x421234',
canonical: true,
block_height: block1.block_height,
contract_id: 'some-contract-id',
source_code: '(some-contract-src)',
abi: '{"some-abi":1}',
};
await db.update({
block: block1,
microblocks: [],
minerRewards: [],
txs: [
{
tx: tx1,
stxLockEvents: [],
stxEvents: [],
ftEvents: [],
nftEvents: [],
contractLogEvents: [contractLogEvent1],
smartContracts: [smartContract1],
names: [],
namespaces: [],
},
{
tx: tx2,
stxLockEvents: [],
stxEvents: [],
ftEvents: [],
nftEvents: [],
contractLogEvents: [],
smartContracts: [],
names: [],
namespaces: [],
},
],
});
const fetchTx = await supertest(api.server).get(
'/extended/v1/contract/some-contract-id/events'
);
expect(fetchTx.status).toBe(200);
expect(fetchTx.type).toBe('application/json');
expect(JSON.parse(fetchTx.text)).toEqual({
limit: 20,
offset: 0,
results: [
{
event_index: 4,
event_type: 'smart_contract_log',
tx_id: '0x421234',
contract_log: {
contract_id: 'some-contract-id',
topic: 'some-topic',
value: { hex: '0x0200000008736f6d652076616c', repr: '0x736f6d652076616c' },
},
},
],
});
});
test('get contract by ID', async () => {
const block1: DbBlock = {
block_hash: '0x1234',
index_block_hash: '0xdeadbeef',
parent_index_block_hash: '0x00',
parent_block_hash: '0xff0011',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 1594647996,
burn_block_hash: '0x1234',
burn_block_height: 123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const txId1 = '0x421234';
const smartContract1: DbSmartContract = {
tx_id: txId1,
canonical: true,
block_height: block1.block_height,
contract_id: 'some-contract-id',
source_code: '(some-contract-src)',
abi: '{"some-abi":1}',
};
const tx1: DbTx = {
tx_id: txId1,
tx_index: 0,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.alloc(0),
index_block_hash: '0x1234',
block_hash: '0x5678',
block_height: block1.block_height,
burn_block_time: 1594647995,
parent_burn_block_time: 1626122935,
type_id: DbTxTypeId.SmartContract,
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: '',
parent_block_hash: '',
post_conditions: Buffer.from([]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'sender-addr',
origin_hash_mode: 1,
smart_contract_contract_id: smartContract1.contract_id,
smart_contract_source_code: smartContract1.source_code,
event_count: 0,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.update({
block: block1,
microblocks: [],
minerRewards: [],
txs: [
{
tx: tx1,
stxLockEvents: [],
stxEvents: [],
ftEvents: [],
nftEvents: [],
contractLogEvents: [],
smartContracts: [smartContract1],
names: [],
namespaces: [],
},
],
});
const fetchTx = await supertest(api.server).get('/extended/v1/contract/some-contract-id');
expect(fetchTx.status).toBe(200);
expect(fetchTx.type).toBe('application/json');
expect(JSON.parse(fetchTx.text)).toEqual({
tx_id: '0x421234',
canonical: true,
contract_id: 'some-contract-id',
block_height: 1,
source_code: '(some-contract-src)',
abi: '{"some-abi":1}',
});
});
test('list contract with given trait', async () => {
const block1: DbBlock = {
block_hash: '0x1235',
index_block_hash: '0xdeadbeeg',
parent_index_block_hash: '0x01',
parent_block_hash: '0xff0012',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 1594647996,
burn_block_hash: '0x1235',
burn_block_height: 123,
miner_txid: '0x4322',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const tx1: DbTx = {
tx_id: '0x421235',
tx_index: 0,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.alloc(0),
index_block_hash: '0x1235',
block_hash: '0x5679',
block_height: block1.block_height,
burn_block_time: 1594647995,
parent_burn_block_time: 1626122935,
type_id: DbTxTypeId.Coinbase,
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: '',
parent_block_hash: '',
post_conditions: Buffer.from([]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'sender-addr',
origin_hash_mode: 1,
coinbase_payload: Buffer.from('hi'),
event_count: 0,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const tx2: DbTx = {
...tx1,
tx_id: '0x012345',
tx_index: 1,
};
const contractLogEvent1: DbSmartContractEvent = {
event_index: 4,
tx_id: '0x421234',
tx_index: 0,
block_height: block1.block_height,
canonical: true,
event_type: DbEventTypeId.SmartContractLog,
contract_identifier: 'some-contract-id',
topic: 'some-topic',
value: serializeCV(bufferCVFromString('some val')),
};
const contractJsonAbi = {
maps: [],
functions: [
{
args: [
{
name: 'code',
type: 'uint128',
},
],
name: 'err-nft-mint',
access: 'private',
outputs: {
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
},
},
{
args: [
{
name: 'code',
type: 'uint128',
},
],
name: 'err-nft-transfer',
access: 'private',
outputs: {
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
},
},
{
args: [
{
name: 'code',
type: 'int128',
},
],
name: 'err-pox-stack-aggregation-commit',
access: 'private',
outputs: {
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
},
},
{
args: [
{
name: 'code',
type: 'uint128',
},
],
name: 'err-stx-transfer',
access: 'private',
outputs: {
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
},
},
{
args: [
{
name: 'stack-result',
type: {
response: {
ok: {
tuple: [
{
name: 'lock-amount',
type: 'uint128',
},
{
name: 'stacker',
type: 'principal',
},
{
name: 'unlock-burn-height',
type: 'uint128',
},
],
},
error: {
tuple: [
{
name: 'code',
type: 'uint128',
},
{
name: 'kind',
type: {
'string-ascii': {
length: 32,
},
},
},
],
},
},
},
},
{
name: 'total',
type: 'uint128',
},
],
name: 'get-total',
access: 'private',
outputs: {
type: 'uint128',
},
},
{
args: [
{
name: 'stacker',
type: 'principal',
},
{
name: 'amount-ustx',
type: 'uint128',
},
{
name: 'until-burn-ht',
type: {
optional: 'uint128',
},
},
],
name: 'mint-and-delegatedly-stack',
access: 'private',
outputs: {
type: {
response: {
ok: {
tuple: [
{
name: 'id',
type: 'uint128',
},
{
name: 'pox',
type: {
tuple: [
{
name: 'lock-amount',
type: 'uint128',
},
{
name: 'stacker',
type: 'principal',
},
{
name: 'unlock-burn-height',
type: 'uint128',
},
],
},
},
],
},
error: 'uint128',
},
},
},
},
{
args: [
{
name: 'nft-id',
type: 'uint128',
},
{
name: 'ctx',
type: {
tuple: [
{
name: 'pay-stacks-tip',
type: 'uint128',
},
{
name: 'result',
type: {
list: {
type: {
response: {
ok: 'bool',
error: 'uint128',
},
},
length: 750,
},
},
},
{
name: 'reward-ustx',
type: 'uint128',
},
{
name: 'stx-from',
type: 'principal',
},
{
name: 'total-ustx',
type: 'uint128',
},
],
},
},
],
name: 'payout-nft',
access: 'private',
outputs: {
type: {
tuple: [
{
name: 'pay-stacks-tip',
type: 'uint128',
},
{
name: 'result',
type: {
list: {
type: {
response: {
ok: 'bool',
error: 'uint128',
},
},
length: 750,
},
},
},
{
name: 'reward-ustx',
type: 'uint128',
},
{
name: 'stx-from',
type: 'principal',
},
{
name: 'total-ustx',
type: 'uint128',
},
],
},
},
},
{
args: [
{
name: 'amount-ustx',
type: 'uint128',
},
{
name: 'until-burn-ht',
type: {
optional: 'uint128',
},
},
],
name: 'pox-delegate-stx-and-stack',
access: 'private',
outputs: {
type: {
response: {
ok: {
tuple: [
{
name: 'lock-amount',
type: 'uint128',
},
{
name: 'stacker',
type: 'principal',
},
{
name: 'unlock-burn-height',
type: 'uint128',
},
],
},
error: 'uint128',
},
},
},
},
{
args: [
{
name: 'nft-id',
type: 'uint128',
},
{
name: 'total',
type: 'uint128',
},
],
name: 'sum-stacked-ustx',
access: 'private',
outputs: {
type: 'uint128',
},
},
{
args: [
{
name: 'id',
type: 'uint128',
},
{
name: 'stacked-ustx',
type: 'uint128',
},
],
name: 'update-meta',
access: 'private',
outputs: {
type: 'bool',
},
},
{
args: [
{
name: 'this-contract',
type: 'principal',
},
],
name: 'allow-contract-caller',
access: 'public',
outputs: {
type: {
response: {
ok: 'bool',
error: 'int128',
},
},
},
},
{
args: [
{
name: 'amount-ustx',
type: 'uint128',
},
{
name: 'stacker',
type: 'principal',
},
{
name: 'until-burn-ht',
type: {
optional: 'uint128',
},
},
{
name: 'pox-addr',
type: {
optional: {
tuple: [
{
name: 'hashbytes',
type: {
buffer: {
length: 20,
},
},
},
{
name: 'version',
type: {
buffer: {
length: 1,
},
},
},
],
},
},
},
],
name: 'delegate-stx',
access: 'public',
outputs: {
type: {
response: {
ok: {
tuple: [
{
name: 'id',
type: 'uint128',
},
{
name: 'pox',
type: {
tuple: [
{
name: 'lock-amount',
type: 'uint128',
},
{
name: 'stacker',
type: 'principal',
},
{
name: 'unlock-burn-height',
type: 'uint128',
},
],
},
},
],
},
error: 'uint128',
},
},
},
},
{
args: [
{
name: 'reward-ustx',
type: 'uint128',
},
{
name: 'nfts',
type: {
list: {
type: 'uint128',
length: 750,
},
},
},
{
name: 'pay-stacks-tip',
type: 'uint128',
},
],
name: 'payout',
access: 'public',
outputs: {
type: {
response: {
ok: {
tuple: [
{
name: 'pay-stacks-tip',
type: 'uint128',
},
{
name: 'result',
type: {
list: {
type: {
response: {
ok: 'bool',
error: 'uint128',
},
},
length: 750,
},
},
},
{
name: 'reward-ustx',
type: 'uint128',
},
{
name: 'stx-from',
type: 'principal',
},
{
name: 'total-ustx',
type: 'uint128',
},
],
},
error: 'uint128',
},
},
},
},
{
args: [
{
name: 'reward-cycle',
type: 'uint128',
},
],
name: 'stack-aggregation-commit',
access: 'public',
outputs: {
type: {
response: {
ok: 'bool',
error: 'uint128',
},
},
},
},
{
args: [
{
name: 'id',
type: 'uint128',
},
{
name: 'sender',
type: 'principal',
},
{
name: 'recipient',
type: 'principal',
},
],
name: 'transfer',
access: 'public',
outputs: {
type: {
response: {
ok: 'bool',
error: 'uint128',
},
},
},
},
{
args: [
{
name: 'code',
type: 'uint128',
},
],
name: 'get-errstr',
access: 'read_only',
outputs: {
type: {
'string-ascii': {
length: 32,
},
},
},
},
{
args: [],
name: 'get-last-token-id',
access: 'read_only',
outputs: {
type: {
response: {
ok: 'uint128',
error: 'none',
},
},
},
},
{
args: [
{
name: 'id',
type: 'uint128',
},
],
name: 'get-owner',
access: 'read_only',
outputs: {
type: {
response: {
ok: {
optional: 'principal',
},
error: 'none',
},
},
},
},
{
args: [
{
name: 'id',
type: 'uint128',
},
],
name: 'get-owner-raw?',
access: 'read_only',
outputs: {
type: {
optional: 'principal',
},
},
},
{
args: [
{
name: 'id',
type: 'uint128',
},
],
name: 'get-token-uri',
access: 'read_only',
outputs: {
type: {
response: {
ok: {
optional: {
'string-ascii': {
length: 92,
},
},
},
error: 'none',
},
},
},
},
{
args: [],
name: 'get-total-stacked',
access: 'read_only',
outputs: {
type: 'uint128',
},
},
{
args: [
{
name: 'nfts',
type: {
list: {
type: 'uint128',
length: 750,
},
},
},
],
name: 'get-total-stacked-ustx',
access: 'read_only',
outputs: {
type: 'uint128',
},
},
{
args: [
{
name: 'nfts',
type: {
list: {
type: 'uint128',
length: 750,
},
},
},
{
name: 'stacks-tip',
type: 'uint128',
},
],
name: 'get-total-stacked-ustx-at-block',
access: 'read_only',
outputs: {
type: {
response: {
ok: 'uint128',
error: 'uint128',
},
},
},
},
{
args: [],
name: 'last-token-id-raw',
access: 'read_only',
outputs: {
type: 'uint128',
},
},
{
args: [
{
name: 'nft-id',
type: 'uint128',
},
],
name: 'nft-details',
access: 'read_only',
outputs: {
type: {
response: {
ok: {
tuple: [
{
name: 'owner',
type: 'principal',
},
{
name: 'stacked-ustx',
type: 'uint128',
},
],
},
error: 'uint128',
},
},
},
},
{
args: [
{
name: 'nft-id',
type: 'uint128',
},
{
name: 'stacks-tip',
type: 'uint128',
},
],
name: 'nft-details-at-block',
access: 'read_only',
outputs: {
type: {
response: {
ok: {
tuple: [
{
name: 'owner',
type: 'principal',
},
{
name: 'stacked-ustx',
type: 'uint128',
},
],
},
error: 'uint128',
},
},
},
},
],
variables: [
{
name: 'accnt',
type: 'principal',
access: 'constant',
},
{
name: 'dplyr',
type: 'principal',
access: 'constant',
},
{
name: 'err-amount-not-positive',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
},
{
name: 'err-commit-too-early',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
},
{
name: 'err-delegate-below-minimum',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
},
{
name: 'err-delegate-invalid-stacker',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
},
{
name: 'err-delegate-too-late',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
},
{
name: 'err-invalid-asset-id',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
},
{
name: 'err-invalid-stacks-tip',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
},
{
name: 'err-map-function-failed',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
},
{
name: 'err-nft-exists',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
},
{
name: 'err-nft-not-found',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
},
{
name: 'err-nft-not-owned',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
},
{
name: 'err-no-asset-owner',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
},
{
name: 'err-not-allowed-sender',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
},
{
name: 'err-not-enough-funds',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
},
{
name: 'err-sender-equals-recipient',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
},
{
name: 'minimum-amount',
type: 'uint128',
access: 'constant',
},
{
name: 'px-addr',
type: {
tuple: [
{
name: 'hashbytes',
type: {
buffer: {
length: 20,
},
},
},
{
name: 'version',
type: {
buffer: {
length: 1,
},
},
},
],
},
access: 'constant',
},
{
name: 'time-limit',
type: 'uint128',
access: 'constant',
},
{
name: 'last-id',
type: 'uint128',
access: 'variable',
},
{
name: 'start',
type: {
optional: 'uint128',
},
access: 'variable',
},
{
name: 'total-stacked',
type: 'uint128',
access: 'variable',
},
],
fungible_tokens: [],
non_fungible_tokens: [
{
name: 'b-12',
type: 'uint128',
},
],
};
const traitJsonAbiRequest = {
maps: [],
functions: [
{
args: [
{
name: 'id',
type: 'uint128',
},
{
name: 'sender',
type: 'principal',
},
{
name: 'recipient',
type: 'principal',
},
],
name: 'transfer',
access: 'public',
outputs: {
type: {
response: {
ok: 'bool',
error: 'uint128',
},
},
},
},
{
args: [],
name: 'get-last-token-id',
access: 'read_only',
outputs: {
type: {
response: {
ok: 'uint128',
error: 'none',
},
},
},
},
{
args: [
{
name: 'id',
type: 'uint128',
},
],
name: 'get-owner',
access: 'read_only',
outputs: {
type: {
response: {
ok: {
optional: 'principal',
},
error: 'none',
},
},
},
},
{
args: [
{
name: 'id',
type: 'uint128',
},
],
name: 'get-token-uri',
access: 'read_only',
outputs: {
type: {
response: {
ok: {
optional: {
'string-ascii': {
length: 92,
},
},
},
error: 'none',
},
},
},
},
],
variables: [],
fungible_tokens: [],
non_fungible_tokens: [],
};
const smartContract1: DbSmartContract = {
tx_id: '0x421234',
canonical: true,
block_height: block1.block_height,
contract_id: 'some-contract-id',
source_code: '(some-contract-src)',
abi: JSON.stringify(contractJsonAbi),
};
await db.update({
block: block1,
microblocks: [],
minerRewards: [],
txs: [
{
tx: tx1,
stxLockEvents: [],
stxEvents: [],
ftEvents: [],
nftEvents: [],
contractLogEvents: [contractLogEvent1],
smartContracts: [smartContract1],
names: [],
namespaces: [],
},
{
tx: tx2,
stxLockEvents: [],
stxEvents: [],
ftEvents: [],
nftEvents: [],
contractLogEvents: [],
smartContracts: [],
names: [],
namespaces: [],
},
],
});
const query = await supertest(api.server).get(
`/extended/v1/contract/by_trait?trait_abi=${JSON.stringify(traitJsonAbiRequest)}`
);
expect(query.status).toBe(200);
expect(JSON.parse(query.body.results[0].abi)).toStrictEqual(contractJsonAbi);
const traitJsonAbiRequest1 = {
maps: [],
functions: [
{
args: [
{
name: 'id',
type: 'uint128',
},
{
name: 'sender',
type: 'principal',
},
{
name: 'recipient',
type: 'principal',
},
],
name: 'wrong name',
access: 'public',
outputs: {
type: {
response: {
ok: 'bool',
error: 'uint128',
},
},
},
},
{
args: [],
name: 'get-last-token-id',
access: 'read_only',
outputs: {
type: {
response: {
ok: 'uint128',
error: 'none',
},
},
},
},
{
args: [
{
name: 'id',
type: 'uint128',
},
],
name: 'get-owner',
access: 'read_only',
outputs: {
type: {
response: {
ok: {
optional: 'principal',
},
error: 'none',
},
},
},
},
{
args: [
{
name: 'id',
type: 'uint128',
},
],
name: 'get-token-uri',
access: 'read_only',
outputs: {
type: {
response: {
ok: {
optional: {
'string-ascii': {
length: 92,
},
},
},
error: 'none',
},
},
},
},
],
variables: [],
fungible_tokens: [],
non_fungible_tokens: [],
};
const query1 = await supertest(api.server).get(
`/extended/v1/contract/by_trait?trait_abi=${JSON.stringify(traitJsonAbiRequest1)}`
);
expect(query1.status).toBe(404);
});
test('list contract with given trait: Bad request', async () => {
const traitJsonAbiRequest = {
//missing functions
maps: [],
variables: [],
fungible_tokens: [],
non_fungible_tokens: [],
};
const query = await supertest(api.server).get(
`/extended/v1/contract/by_trait?trait_abi=${JSON.stringify(traitJsonAbiRequest)}`
);
expect(query.status).toBe(400);
const query1 = await supertest(api.server).get('/extended/v1/contract/by_trait');
expect(query1.status).toBe(400);
});
test('test large query param', async () => {
let randomData = 'A';
randomData = randomData.repeat(32 * 1024);
const query = await supertest(api.server).get(
`/extended/v1/contract/by_trait?trait_abi=${randomData}`
);
expect(query.status).toBe(431);
});
test('getTxList() returns object', async () => {
const expectedResp = {
limit: 96,
offset: 0,
results: [],
total: 0,
};
const fetchTx = await supertest(api.server).get('/extended/v1/tx/');
expect(fetchTx.status).toBe(200);
expect(fetchTx.type).toBe('application/json');
expect(JSON.parse(fetchTx.text)).toEqual(expectedResp);
});
test('block store and process', async () => {
const block: DbBlock = {
block_hash: '0x1234',
index_block_hash: '0xdeadbeef',
parent_index_block_hash: '0x5678',
parent_block_hash: '0xff0011',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1235,
burn_block_time: 1594647996,
burn_block_hash: '0x1234',
burn_block_height: 123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateBlock(client, block);
const tx: DbTx = {
tx_id: '0x1234',
anchor_mode: 3,
tx_index: 4,
nonce: 0,
raw_tx: Buffer.alloc(0),
index_block_hash: block.index_block_hash,
block_hash: block.block_hash,
block_height: 68456,
burn_block_time: 1594647995,
parent_burn_block_time: 1626122935,
type_id: DbTxTypeId.Coinbase,
coinbase_payload: Buffer.from('coinbase hi'),
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: '',
parent_block_hash: '',
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'sender-addr',
origin_hash_mode: 1,
event_count: 0,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateTx(client, tx);
const blockQuery = await getBlockFromDataStore({
blockIdentifer: { hash: block.block_hash },
db,
});
if (!blockQuery.found) {
throw new Error('block not found');
}
const expectedResp = {
burn_block_time: 1594647996,
burn_block_time_iso: '2020-07-13T13:46:36.000Z',
burn_block_hash: '0x1234',
burn_block_height: 123,
miner_txid: '0x4321',
canonical: true,
hash: '0x1234',
height: 1235,
parent_block_hash: '0xff0011',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
txs: ['0x1234'],
microblocks_accepted: [],
microblocks_streamed: [],
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
expect(blockQuery.result).toEqual(expectedResp);
const fetchBlockByHash = await supertest(api.server).get(
`/extended/v1/block/${block.block_hash}`
);
expect(fetchBlockByHash.status).toBe(200);
expect(fetchBlockByHash.type).toBe('application/json');
expect(JSON.parse(fetchBlockByHash.text)).toEqual(expectedResp);
const fetchBlockByHeight = await supertest(api.server).get(
`/extended/v1/block/by_height/${block.block_height}`
);
expect(fetchBlockByHeight.status).toBe(200);
expect(fetchBlockByHeight.type).toBe('application/json');
expect(JSON.parse(fetchBlockByHeight.text)).toEqual(expectedResp);
const fetchBlockByBurnBlockHeight = await supertest(api.server).get(
`/extended/v1/block/by_burn_block_height/${block.burn_block_height}`
);
expect(fetchBlockByBurnBlockHeight.status).toBe(200);
expect(fetchBlockByBurnBlockHeight.type).toBe('application/json');
expect(JSON.parse(fetchBlockByBurnBlockHeight.text)).toEqual(expectedResp);
const fetchBlockByInvalidBurnBlockHeight1 = await supertest(api.server).get(
`/extended/v1/block/by_burn_block_height/999`
);
expect(fetchBlockByInvalidBurnBlockHeight1.status).toBe(404);
expect(fetchBlockByInvalidBurnBlockHeight1.type).toBe('application/json');
const expectedResp1 = {
error: 'cannot find block by height 999',
};
expect(JSON.parse(fetchBlockByInvalidBurnBlockHeight1.text)).toEqual(expectedResp1);
const fetchBlockByInvalidBurnBlockHeight2 = await supertest(api.server).get(
`/extended/v1/block/by_burn_block_height/abc`
);
expect(fetchBlockByInvalidBurnBlockHeight2.status).toBe(400);
expect(fetchBlockByInvalidBurnBlockHeight2.type).toBe('application/json');
const expectedResp2 = {
error: 'burnchain height is not a valid integer: abc',
};
expect(JSON.parse(fetchBlockByInvalidBurnBlockHeight2.text)).toEqual(expectedResp2);
const fetchBlockByInvalidBurnBlockHeight3 = await supertest(api.server).get(
`/extended/v1/block/by_burn_block_height/0`
);
expect(fetchBlockByInvalidBurnBlockHeight3.status).toBe(400);
expect(fetchBlockByInvalidBurnBlockHeight3.type).toBe('application/json');
const expectedResp3 = {
error: 'burnchain height is not a positive integer: 0',
};
expect(JSON.parse(fetchBlockByInvalidBurnBlockHeight3.text)).toEqual(expectedResp3);
const fetchBlockByBurnBlockHash = await supertest(api.server).get(
`/extended/v1/block/by_burn_block_hash/${block.burn_block_hash}`
);
expect(fetchBlockByBurnBlockHash.status).toBe(200);
expect(fetchBlockByBurnBlockHash.type).toBe('application/json');
expect(JSON.parse(fetchBlockByBurnBlockHash.text)).toEqual(expectedResp);
const fetchBlockByInvalidBurnBlockHash = await supertest(api.server).get(
`/extended/v1/block/by_burn_block_hash/0x000000`
);
expect(fetchBlockByInvalidBurnBlockHash.status).toBe(404);
expect(fetchBlockByInvalidBurnBlockHash.type).toBe('application/json');
const expectedResp4 = {
error: 'cannot find block by burn block hash 0x000000',
};
expect(JSON.parse(fetchBlockByInvalidBurnBlockHash.text)).toEqual(expectedResp4);
});
test('tx - sponsored', async () => {
const dbBlock: DbBlock = {
block_hash: '0xff',
index_block_hash: '0x1234',
parent_index_block_hash: '0x5678',
parent_block_hash: '0x5678',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 1594647995,
burn_block_hash: '0x1234',
burn_block_height: 123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateBlock(client, dbBlock);
const txBuilder = await makeContractCall({
contractAddress: 'ST11NJTTKGVT6D1HY4NJRVQWMQM7TVAR091EJ8P2Y',
contractName: 'hello-world',
functionName: 'fn-name',
functionArgs: [{ type: ClarityType.Int, value: BigInt(556) }],
fee: new BN(200),
senderKey: 'b8d99fd45da58038d630d9855d3ca2466e8e0f89d3894c4724f0efc9ff4b51f001',
nonce: new BN(0),
sponsored: true,
anchorMode: AnchorMode.Any,
});
const sponsoredTx = await sponsorTransaction({
transaction: txBuilder,
sponsorPrivateKey: '381314da39a45f43f45ffd33b5d8767d1a38db0da71fea50ed9508e048765cf301',
fee: new BN(300),
sponsorNonce: new BN(2),
});
const serialized = sponsoredTx.serialize();
const tx = readTransaction(new BufferReader(serialized));
const dbTx = createDbTxFromCoreMsg({
core_tx: {
raw_tx: '0x' + serialized.toString('hex'),
status: 'success',
raw_result: '0x0100000000000000000000000000000001', // u1
txid: '0x' + txBuilder.txid(),
tx_index: 2,
contract_abi: null,
microblock_hash: null,
microblock_parent_hash: null,
microblock_sequence: null,
execution_cost: {
read_count: 0,
read_length: 0,
runtime: 0,
write_count: 0,
write_length: 0,
},
},
nonce: 0,
raw_tx: Buffer.alloc(0),
parsed_tx: tx,
sender_address: 'ST11NJTTKGVT6D1HY4NJRVQWMQM7TVAR091EJ8P2Y',
sponsor_address: 'SP2ZRX0K27GW0SP3GJCEMHD95TQGJMKB7GB36ZAR0',
index_block_hash: dbBlock.index_block_hash,
parent_index_block_hash: dbBlock.parent_index_block_hash,
parent_block_hash: dbBlock.parent_block_hash,
microblock_hash: '',
microblock_sequence: I32_MAX,
block_hash: dbBlock.block_hash,
block_height: dbBlock.block_height,
burn_block_time: dbBlock.burn_block_time,
parent_burn_block_hash: '0xaa',
parent_burn_block_time: 1626122935,
});
await db.updateTx(client, dbTx);
const contractAbi: ClarityAbi = {
functions: [
{
name: 'fn-name',
args: [{ name: 'arg1', type: 'int128' }],
access: 'public',
outputs: { type: 'bool' },
},
],
variables: [],
maps: [],
fungible_tokens: [],
non_fungible_tokens: [],
};
await db.updateSmartContract(client, dbTx, {
tx_id: dbTx.tx_id,
canonical: true,
contract_id: 'ST11NJTTKGVT6D1HY4NJRVQWMQM7TVAR091EJ8P2Y.hello-world',
block_height: dbBlock.block_height,
source_code: '()',
abi: JSON.stringify(contractAbi),
});
const txQuery = await getTxFromDataStore(db, { txId: dbTx.tx_id, includeUnanchored: false });
expect(txQuery.found).toBe(true);
if (!txQuery.found) {
throw Error('not found');
}
const expectedResp = {
block_hash: '0xff',
block_height: 1,
burn_block_time: 1594647995,
burn_block_time_iso: '2020-07-13T13:46:35.000Z',
canonical: true,
microblock_canonical: true,
microblock_hash: '',
microblock_sequence: I32_MAX,
parent_block_hash: '0x5678',
parent_burn_block_time: 1626122935,
parent_burn_block_time_iso: '2021-07-12T20:48:55.000Z',
tx_id: '0xc889d593d349834e100f63cf58975b6aa2787d6f3784a26f5654221e38f75b05',
tx_index: 2,
tx_status: 'success',
tx_result: {
hex: '0x0100000000000000000000000000000001', // u1
repr: 'u1',
},
tx_type: 'contract_call',
fee_rate: '200',
is_unanchored: false,
nonce: 0,
anchor_mode: 'any',
sender_address: 'ST11NJTTKGVT6D1HY4NJRVQWMQM7TVAR091EJ8P2Y',
sponsor_address: 'SP2ZRX0K27GW0SP3GJCEMHD95TQGJMKB7GB36ZAR0',
sponsored: true,
post_condition_mode: 'deny',
post_conditions: [],
contract_call: {
contract_id: 'ST11NJTTKGVT6D1HY4NJRVQWMQM7TVAR091EJ8P2Y.hello-world',
function_name: 'fn-name',
function_signature: '(define-public (fn-name (arg1 int)))',
function_args: [
{ hex: '0x000000000000000000000000000000022c', repr: '556', name: 'arg1', type: 'int' },
],
},
event_count: 0,
events: [],
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const fetchTx = await supertest(api.server).get(`/extended/v1/tx/${dbTx.tx_id}`);
expect(fetchTx.status).toBe(200);
expect(fetchTx.type).toBe('application/json');
expect(JSON.parse(fetchTx.text)).toEqual(expectedResp);
expect(txQuery.result).toEqual(expectedResp);
});
test('tx store and processing', async () => {
const dbBlock: DbBlock = {
block_hash: '0xff',
index_block_hash: '0x1234',
parent_index_block_hash: '0x5678',
parent_block_hash: '0x5678',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 1594647995,
burn_block_hash: '0x1234',
burn_block_height: 123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateBlock(client, dbBlock);
const pc1 = createNonFungiblePostCondition(
'ST1HB1T8WRNBYB0Y3T7WXZS38NKKPTBR3EG9EPJKR',
NonFungibleConditionCode.Owns,
'STRYYQQ9M8KAF4NS7WNZQYY59X93XEKR31JP64CP.hello::asset-name',
bufferCVFromString('asset-value')
);
const pc2 = createFungiblePostCondition(
'ST1HB1T8WRNBYB0Y3T7WXZS38NKKPTBR3EG9EPJKR',
FungibleConditionCode.GreaterEqual,
new BN(123456),
'STRYYQQ9M8KAF4NS7WNZQYY59X93XEKR31JP64CP.hello-ft::asset-name-ft'
);
const pc3 = createSTXPostCondition(
'ST1HB1T8WRNBYB0Y3T7WXZS38NKKPTBR3EG9EPJKR',
FungibleConditionCode.LessEqual,
new BN(36723458)
);
const txBuilder = await makeContractCall({
contractAddress: 'ST11NJTTKGVT6D1HY4NJRVQWMQM7TVAR091EJ8P2Y',
contractName: 'hello-world',
functionName: 'fn-name',
functionArgs: [{ type: ClarityType.Int, value: BigInt(556) }],
fee: new BN(200),
senderKey: 'b8d99fd45da58038d630d9855d3ca2466e8e0f89d3894c4724f0efc9ff4b51f001',
postConditions: [pc1, pc2, pc3],
nonce: new BN(0),
anchorMode: AnchorMode.Any,
});
const serialized = txBuilder.serialize();
const tx = readTransaction(new BufferReader(serialized));
const dbTx = createDbTxFromCoreMsg({
core_tx: {
raw_tx: '0x' + serialized.toString('hex'),
status: 'success',
raw_result: '0x0100000000000000000000000000000001', // u1
txid: '0x' + txBuilder.txid(),
tx_index: 2,
contract_abi: null,
microblock_hash: null,
microblock_parent_hash: null,
microblock_sequence: null,
execution_cost: {
read_count: 0,
read_length: 0,
runtime: 0,
write_count: 0,
write_length: 0,
},
},
nonce: 0,
raw_tx: Buffer.alloc(0),
parsed_tx: tx,
sender_address: 'ST11NJTTKGVT6D1HY4NJRVQWMQM7TVAR091EJ8P2Y',
sponsor_address: undefined,
index_block_hash: dbBlock.index_block_hash,
parent_index_block_hash: dbBlock.parent_index_block_hash,
parent_block_hash: dbBlock.parent_block_hash,
microblock_hash: '',
microblock_sequence: I32_MAX,
block_hash: dbBlock.block_hash,
block_height: dbBlock.block_height,
burn_block_time: 1594647995,
parent_burn_block_hash: '0xaa',
parent_burn_block_time: 1626122935,
});
await db.updateTx(client, dbTx);
const contractAbi: ClarityAbi = {
functions: [
{
name: 'fn-name',
args: [{ name: 'arg1', type: 'int128' }],
access: 'public',
outputs: { type: 'bool' },
},
],
variables: [],
maps: [],
fungible_tokens: [],
non_fungible_tokens: [],
};
await db.updateSmartContract(client, dbTx, {
tx_id: dbTx.tx_id,
canonical: true,
contract_id: 'ST11NJTTKGVT6D1HY4NJRVQWMQM7TVAR091EJ8P2Y.hello-world',
block_height: 123,
source_code: '()',
abi: JSON.stringify(contractAbi),
});
const txQuery = await getTxFromDataStore(db, { txId: dbTx.tx_id, includeUnanchored: false });
expect(txQuery.found).toBe(true);
if (!txQuery.found) {
throw Error('not found');
}
const expectedResp = {
block_hash: '0xff',
block_height: 1,
burn_block_time: 1594647995,
burn_block_time_iso: '2020-07-13T13:46:35.000Z',
canonical: true,
microblock_canonical: true,
microblock_hash: '',
microblock_sequence: I32_MAX,
parent_block_hash: '0x5678',
parent_burn_block_time: 1626122935,
parent_burn_block_time_iso: '2021-07-12T20:48:55.000Z',
tx_id: '0xc3e2fabaf7017fa2f6967db4f21be4540fdeae2d593af809c18a6adf369bfb03',
tx_index: 2,
tx_status: 'success',
tx_result: {
hex: '0x0100000000000000000000000000000001', // u1
repr: 'u1',
},
tx_type: 'contract_call',
fee_rate: '200',
is_unanchored: false,
nonce: 0,
anchor_mode: 'any',
sender_address: 'ST11NJTTKGVT6D1HY4NJRVQWMQM7TVAR091EJ8P2Y',
sponsored: false,
sponsor_address: undefined,
post_condition_mode: 'deny',
post_conditions: [
{
type: 'non_fungible',
condition_code: 'not_sent',
principal: {
type_id: 'principal_standard',
address: 'ST1HB1T8WRNBYB0Y3T7WXZS38NKKPTBR3EG9EPJKR',
},
asset: {
contract_name: 'hello',
asset_name: 'asset-name',
contract_address: 'STRYYQQ9M8KAF4NS7WNZQYY59X93XEKR31JP64CP',
},
asset_value: {
hex: '0x020000000b61737365742d76616c7565',
repr: '0x61737365742d76616c7565',
},
},
{
type: 'fungible',
condition_code: 'sent_greater_than_or_equal_to',
amount: '123456',
principal: {
type_id: 'principal_standard',
address: 'ST1HB1T8WRNBYB0Y3T7WXZS38NKKPTBR3EG9EPJKR',
},
asset: {
contract_name: 'hello-ft',
asset_name: 'asset-name-ft',
contract_address: 'STRYYQQ9M8KAF4NS7WNZQYY59X93XEKR31JP64CP',
},
},
{
type: 'stx',
condition_code: 'sent_less_than_or_equal_to',
amount: '36723458',
principal: {
type_id: 'principal_standard',
address: 'ST1HB1T8WRNBYB0Y3T7WXZS38NKKPTBR3EG9EPJKR',
},
},
],
contract_call: {
contract_id: 'ST11NJTTKGVT6D1HY4NJRVQWMQM7TVAR091EJ8P2Y.hello-world',
function_name: 'fn-name',
function_signature: '(define-public (fn-name (arg1 int)))',
function_args: [
{ hex: '0x000000000000000000000000000000022c', repr: '556', name: 'arg1', type: 'int' },
],
},
event_count: 0,
events: [],
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
expect(txQuery.result).toEqual(expectedResp);
const fetchTx = await supertest(api.server).get(`/extended/v1/tx/${dbTx.tx_id}`);
expect(fetchTx.status).toBe(200);
expect(fetchTx.type).toBe('application/json');
expect(JSON.parse(fetchTx.text)).toEqual(expectedResp);
const expectedListResp = {
limit: 96,
offset: 0,
total: 1,
results: [expectedResp],
};
const fetchTxList = await supertest(api.server).get(`/extended/v1/tx`);
expect(fetchTxList.status).toBe(200);
expect(fetchTxList.type).toBe('application/json');
expect(JSON.parse(fetchTxList.text)).toEqual(expectedListResp);
});
test('tx store and processing - abort_by_response', async () => {
const dbBlock: DbBlock = {
block_hash: '0xff',
index_block_hash: '0x1234',
parent_index_block_hash: '0x5678',
parent_block_hash: '0x5678',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 1594647995,
burn_block_hash: '0x1234',
burn_block_height: 123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateBlock(client, dbBlock);
const txBuilder = await makeContractDeploy({
contractName: 'hello-world',
codeBody: '()',
fee: new BN(200),
nonce: new BN(0),
senderKey: 'b8d99fd45da58038d630d9855d3ca2466e8e0f89d3894c4724f0efc9ff4b51f001',
postConditions: [],
anchorMode: AnchorMode.Any,
});
const serialized = txBuilder.serialize();
const tx = readTransaction(new BufferReader(serialized));
const dbTx = createDbTxFromCoreMsg({
core_tx: {
raw_tx: '0x' + serialized.toString('hex'),
raw_result: '0x0100000000000000000000000000000001', // u1
status: 'abort_by_response',
txid: '0x' + txBuilder.txid(),
tx_index: 2,
contract_abi: null,
microblock_hash: null,
microblock_parent_hash: null,
microblock_sequence: null,
execution_cost: {
read_count: 0,
read_length: 0,
runtime: 0,
write_count: 0,
write_length: 0,
},
},
nonce: 0,
raw_tx: Buffer.alloc(0),
parsed_tx: tx,
sender_address: 'SP2ZRX0K27GW0SP3GJCEMHD95TQGJMKB7GB36ZAR0',
sponsor_address: undefined,
index_block_hash: dbBlock.index_block_hash,
parent_index_block_hash: dbBlock.parent_index_block_hash,
parent_block_hash: dbBlock.parent_block_hash,
microblock_hash: '',
microblock_sequence: I32_MAX,
block_hash: dbBlock.parent_block_hash,
block_height: dbBlock.block_height,
burn_block_time: 1594647995,
parent_burn_block_hash: '0xaa',
parent_burn_block_time: 1626122935,
});
await db.updateTx(client, dbTx);
const txQuery = await getTxFromDataStore(db, { txId: dbTx.tx_id, includeUnanchored: false });
expect(txQuery.found).toBe(true);
if (!txQuery.found) {
throw Error('not found');
}
const expectedResp = {
block_hash: '0x5678',
block_height: 1,
burn_block_time: 1594647995,
burn_block_time_iso: '2020-07-13T13:46:35.000Z',
canonical: true,
microblock_canonical: true,
microblock_hash: '',
microblock_sequence: I32_MAX,
parent_block_hash: '0x5678',
parent_burn_block_time: 1626122935,
parent_burn_block_time_iso: '2021-07-12T20:48:55.000Z',
tx_id: '0x79abc7783de19569106087302b02379dd02cbb52d20c6c3a7c3d79cbedd559fa',
tx_index: 2,
tx_status: 'abort_by_response',
tx_result: {
hex: '0x0100000000000000000000000000000001', // u1
repr: 'u1',
},
tx_type: 'smart_contract',
fee_rate: '200',
is_unanchored: false,
nonce: 0,
anchor_mode: 'any',
sender_address: 'SP2ZRX0K27GW0SP3GJCEMHD95TQGJMKB7GB36ZAR0',
sponsored: false,
sponsor_address: undefined,
post_condition_mode: 'deny',
post_conditions: [],
smart_contract: {
contract_id: 'SP2ZRX0K27GW0SP3GJCEMHD95TQGJMKB7GB36ZAR0.hello-world',
source_code: '()',
},
event_count: 0,
events: [],
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
expect(txQuery.result).toEqual(expectedResp);
const fetchTx = await supertest(api.server).get(`/extended/v1/tx/${dbTx.tx_id}`);
expect(fetchTx.status).toBe(200);
expect(fetchTx.type).toBe('application/json');
expect(JSON.parse(fetchTx.text)).toEqual(expectedResp);
});
test('tx store and processing - abort_by_post_condition', async () => {
const dbBlock: DbBlock = {
block_hash: '0xff',
index_block_hash: '0x1234',
parent_index_block_hash: '0x5678',
parent_block_hash: '0x5678',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 1594647995,
burn_block_hash: '0x1234',
burn_block_height: 123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateBlock(client, dbBlock);
const txBuilder = await makeContractDeploy({
contractName: 'hello-world',
codeBody: '()',
fee: new BN(200),
senderKey: 'b8d99fd45da58038d630d9855d3ca2466e8e0f89d3894c4724f0efc9ff4b51f001',
postConditions: [],
nonce: new BN(0),
anchorMode: AnchorMode.Any,
});
const serialized = txBuilder.serialize();
const tx = readTransaction(new BufferReader(serialized));
const dbTx = createDbTxFromCoreMsg({
core_tx: {
raw_tx: '0x' + serialized.toString('hex'),
raw_result: '0x0100000000000000000000000000000001', // u1
status: 'abort_by_post_condition',
txid: '0x' + txBuilder.txid(),
tx_index: 2,
contract_abi: null,
microblock_hash: null,
microblock_parent_hash: null,
microblock_sequence: null,
execution_cost: {
read_count: 0,
read_length: 0,
runtime: 0,
write_count: 0,
write_length: 0,
},
},
nonce: 0,
raw_tx: Buffer.alloc(0),
parsed_tx: tx,
sender_address: 'SP2ZRX0K27GW0SP3GJCEMHD95TQGJMKB7GB36ZAR0',
sponsor_address: undefined,
index_block_hash: dbBlock.index_block_hash,
parent_index_block_hash: dbBlock.parent_index_block_hash,
parent_block_hash: dbBlock.parent_block_hash,
microblock_hash: '',
microblock_sequence: I32_MAX,
block_hash: dbBlock.block_hash,
block_height: dbBlock.block_height,
burn_block_time: dbBlock.burn_block_time,
parent_burn_block_hash: '0xaa',
parent_burn_block_time: 1626122935,
});
await db.updateTx(client, dbTx);
const txQuery = await getTxFromDataStore(db, { txId: dbTx.tx_id, includeUnanchored: false });
expect(txQuery.found).toBe(true);
if (!txQuery.found) {
throw Error('not found');
}
const expectedResp = {
block_hash: '0xff',
block_height: 1,
burn_block_time: 1594647995,
burn_block_time_iso: '2020-07-13T13:46:35.000Z',
microblock_canonical: true,
microblock_hash: '',
microblock_sequence: I32_MAX,
parent_block_hash: '0x5678',
parent_burn_block_time: 1626122935,
parent_burn_block_time_iso: '2021-07-12T20:48:55.000Z',
canonical: true,
tx_id: '0x79abc7783de19569106087302b02379dd02cbb52d20c6c3a7c3d79cbedd559fa',
tx_index: 2,
tx_status: 'abort_by_post_condition',
tx_result: {
hex: '0x0100000000000000000000000000000001', // u1
repr: 'u1',
},
tx_type: 'smart_contract',
fee_rate: '200',
is_unanchored: false,
nonce: 0,
anchor_mode: 'any',
sender_address: 'SP2ZRX0K27GW0SP3GJCEMHD95TQGJMKB7GB36ZAR0',
sponsored: false,
sponsor_address: undefined,
post_condition_mode: 'deny',
post_conditions: [],
smart_contract: {
contract_id: 'SP2ZRX0K27GW0SP3GJCEMHD95TQGJMKB7GB36ZAR0.hello-world',
source_code: '()',
},
event_count: 0,
events: [],
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
expect(txQuery.result).toEqual(expectedResp);
const fetchTx = await supertest(api.server).get(`/extended/v1/tx/${dbTx.tx_id}`);
expect(fetchTx.status).toBe(200);
expect(fetchTx.type).toBe('application/json');
expect(JSON.parse(fetchTx.text)).toEqual(expectedResp);
});
test('fetch raw tx', async () => {
const block: DbBlock = {
block_hash: '0x1234',
index_block_hash: '0xdeadbeef',
parent_index_block_hash: '0x00',
parent_block_hash: '0xff0011',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 94869286,
burn_block_hash: '0x1234',
burn_block_height: 123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const tx: DbTx = {
tx_id: '0x421234',
tx_index: 0,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.from('test-raw-tx'),
index_block_hash: block.index_block_hash,
block_hash: block.block_hash,
block_height: block.block_height,
burn_block_time: 2837565,
parent_burn_block_time: 1626122935,
type_id: DbTxTypeId.Coinbase,
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: block.parent_index_block_hash,
parent_block_hash: block.parent_block_hash,
post_conditions: Buffer.from([]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'sender-addr',
origin_hash_mode: 1,
coinbase_payload: Buffer.from('hi'),
event_count: 0,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.update({
block: block,
microblocks: [],
minerRewards: [],
txs: [
{
tx: tx,
stxLockEvents: [],
stxEvents: [],
ftEvents: [],
nftEvents: [],
contractLogEvents: [],
smartContracts: [],
names: [],
namespaces: [],
},
],
});
const mempoolTx: DbMempoolTx = {
tx_id: '0x521234',
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.from('test-raw-mempool-tx'),
type_id: DbTxTypeId.Coinbase,
status: 1,
post_conditions: Buffer.from([]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'sender-addr',
origin_hash_mode: 1,
coinbase_payload: Buffer.from('hi'),
pruned: false,
receipt_time: 1616063078,
};
await db.updateMempoolTxs({ mempoolTxs: [mempoolTx] });
const searchResult1 = await supertest(api.server).get(`/extended/v1/tx/${tx.tx_id}/raw`);
expect(searchResult1.status).toBe(200);
expect(searchResult1.type).toBe('application/json');
expect(searchResult1.body.raw_tx).toEqual(bufferToHexPrefixString(Buffer.from('test-raw-tx')));
const expectedResponse1 = {
raw_tx: bufferToHexPrefixString(Buffer.from('test-raw-tx')),
};
expect(JSON.parse(searchResult1.text)).toEqual(expectedResponse1);
const searchResult2 = await supertest(api.server).get(`/extended/v1/tx/${mempoolTx.tx_id}/raw`);
expect(searchResult2.status).toBe(200);
expect(searchResult2.type).toBe('application/json');
expect(searchResult2.body.raw_tx).toEqual(
bufferToHexPrefixString(Buffer.from('test-raw-mempool-tx'))
);
const expectedResponse2 = {
raw_tx: bufferToHexPrefixString(Buffer.from('test-raw-mempool-tx')),
};
expect(JSON.parse(searchResult2.text)).toEqual(expectedResponse2);
});
test('fetch raw tx: transaction not found', async () => {
const block: DbBlock = {
block_hash: '0x1234',
index_block_hash: '0xdeadbeef',
parent_index_block_hash: '0x00',
parent_block_hash: '0xff0011',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 94869286,
burn_block_hash: '0x1234',
burn_block_height: 123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateBlock(client, block);
const tx: DbTx = {
tx_id: '0x421234',
tx_index: 0,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.from('test-raw-tx'),
index_block_hash: '0x1234',
block_hash: block.block_hash,
block_height: block.block_height,
burn_block_time: block.burn_block_time,
parent_burn_block_time: 1626122935,
type_id: DbTxTypeId.Coinbase,
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: block.parent_index_block_hash,
parent_block_hash: block.parent_block_hash,
post_conditions: Buffer.from([]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'sender-addr',
origin_hash_mode: 1,
coinbase_payload: Buffer.from('hi'),
event_count: 0,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.update({
block: block,
microblocks: [],
minerRewards: [],
txs: [
{
tx: tx,
stxLockEvents: [],
stxEvents: [],
ftEvents: [],
nftEvents: [],
contractLogEvents: [],
smartContracts: [],
names: [],
namespaces: [],
},
],
});
const searchResult = await supertest(api.server).get(`/extended/v1/tx/0x1234/raw`);
expect(searchResult.status).toBe(404);
});
test('exclusive address endpoints params', async () => {
const addressEndpoints = [
'/stx',
'/balances',
'/transactions',
'/transactions_with_transfers',
'/assets',
'/stx_inbound',
'/nft_events',
];
//check for mutually exclusive unachored and and until_block
for (const path of addressEndpoints) {
const response = await supertest(api.server).get(
`/extended/v1/address/STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6${path}?until_block=5&unanchored=true`
);
expect(response.status).toBe(400);
}
const addressEndpoints1 = ['/transactions', '/transactions_with_transfers', '/stx_inbound'];
/// check for mutually exclusive until_block adn height params
for (const path of addressEndpoints1) {
const response1 = await supertest(api.server).get(
`/extended/v1/address/STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6${path}?until_block=5&height=0`
);
expect(response1.status).toBe(400);
}
});
test('Success: nft events for address', async () => {
const addr1 = 'ST3J8EVYHVKH6XXPD61EE8XEHW4Y2K83861225AB1';
const addr2 = 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4';
const dbBlock: DbBlock = {
block_hash: '0xff',
index_block_hash: '0x1234',
parent_index_block_hash: '0x5678',
parent_block_hash: '0x5678',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 1594647995,
burn_block_hash: '0x1234',
burn_block_height: 123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const stxTx: DbTx = {
tx_id: '0x1111000000000000000000000000000000000000000000000000000000000000',
tx_index: 0,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.alloc(0),
index_block_hash: dbBlock.index_block_hash,
block_hash: dbBlock.block_hash,
block_height: dbBlock.block_height,
burn_block_time: dbBlock.burn_block_time,
parent_burn_block_time: 1626122935,
type_id: DbTxTypeId.TokenTransfer,
token_transfer_amount: 1n,
token_transfer_memo: Buffer.from('hi'),
token_transfer_recipient_address: 'none',
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: dbBlock.parent_index_block_hash,
parent_block_hash: dbBlock.parent_block_hash,
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: addr1,
origin_hash_mode: 1,
event_count: 10,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const nftEvents: DbNftEvent[] = [];
for (let i = 0; i < 10; i++) {
nftEvents.push({
canonical: true,
event_type: DbEventTypeId.NonFungibleTokenAsset,
asset_event_type_id: DbAssetEventTypeId.Transfer,
event_index: 0,
tx_id: stxTx.tx_id,
tx_index: 1,
block_height: dbBlock.block_height,
asset_identifier: 'some-asset',
value: serializeCV(intCV(0)),
recipient: addr1,
sender: 'none',
});
}
await db.update({
block: dbBlock,
microblocks: [],
minerRewards: [],
txs: [
{
tx: stxTx,
stxLockEvents: [],
stxEvents: [],
ftEvents: [],
nftEvents: nftEvents,
contractLogEvents: [],
smartContracts: [],
names: [],
namespaces: [],
},
],
});
const limit = 2;
const offset = 0;
// test nft for given addresses
const result = await supertest(api.server).get(
`/extended/v1/address/${addr1}/nft_events?limit=${limit}&offset=${offset}`
);
expect(result.status).toBe(200);
expect(result.type).toBe('application/json');
expect(result.body.total).toEqual(10);
expect(result.body.nft_events.length).toEqual(2);
expect(result.body.nft_events[0].recipient).toBe(addr1);
expect(result.body.nft_events[0].tx_id).toBe(
'0x1111000000000000000000000000000000000000000000000000000000000000'
);
expect(result.body.nft_events[0].block_height).toBe(1);
expect(result.body.nft_events[0].value.repr).toBe('0');
const dbBlock2: DbBlock = {
block_hash: '0xffff',
index_block_hash: '0x123466',
parent_index_block_hash: '0x1234',
parent_block_hash: '0xff',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 2,
burn_block_time: 1594649995,
burn_block_hash: '0x123456',
burn_block_height: 124,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const stxTx1: DbTx = {
tx_id: '0x1111100000000000000000000000000000000000000000000000000000000001',
tx_index: 0,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.alloc(0),
index_block_hash: dbBlock2.index_block_hash,
block_hash: dbBlock2.block_hash,
block_height: dbBlock2.block_height,
burn_block_time: dbBlock2.burn_block_time,
parent_burn_block_time: 1626124935,
type_id: DbTxTypeId.TokenTransfer,
token_transfer_amount: 1n,
token_transfer_memo: Buffer.from('hi'),
token_transfer_recipient_address: 'none',
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: dbBlock2.parent_index_block_hash,
parent_block_hash: dbBlock2.parent_block_hash,
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: addr2,
origin_hash_mode: 1,
event_count: 1,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const nftEvent2: DbNftEvent = {
canonical: true,
event_type: DbEventTypeId.NonFungibleTokenAsset,
asset_event_type_id: DbAssetEventTypeId.Transfer,
event_index: 1,
tx_id: stxTx1.tx_id,
tx_index: 2,
block_height: dbBlock2.block_height,
asset_identifier: 'some-asset',
value: serializeCV(intCV(0)),
recipient: addr2,
sender: 'none',
};
await db.update({
block: dbBlock2,
microblocks: [],
minerRewards: [],
txs: [
{
tx: stxTx1,
stxLockEvents: [],
stxEvents: [],
ftEvents: [],
nftEvents: [nftEvent2],
contractLogEvents: [],
smartContracts: [],
names: [],
namespaces: [],
},
],
});
const result1 = await supertest(api.server).get(`/extended/v1/address/${addr2}/nft_events`);
expect(result1.status).toBe(200);
expect(result1.type).toBe('application/json');
expect(result1.body.total).toEqual(1);
expect(result1.body.nft_events.length).toEqual(1);
expect(result1.body.nft_events[0].recipient).toBe(addr2);
expect(result1.body.nft_events[0].tx_id).toBe(
'0x1111100000000000000000000000000000000000000000000000000000000001'
);
expect(result1.body.nft_events[0].block_height).toBe(2);
expect(result.body.nft_events[0].value.repr).toBe('0');
//check ownership for addr
const result2 = await supertest(api.server).get(`/extended/v1/address/${addr1}/nft_events`);
expect(result2.status).toBe(200);
expect(result2.type).toBe('application/json');
expect(result2.body.nft_events.length).toEqual(0);
expect(result2.body.total).toEqual(0);
});
test('nft invalid address', async () => {
const result = await supertest(api.server).get(
`/extended/v1/address/invalid-address/nft_events`
);
expect(result.status).toBe(400);
expect(result.type).toBe('application/json');
});
test('event count value', async () => {
const testAddr1 = 'ST3J8EVYHVKH6XXPD61EE8XEHW4Y2K83861225AB1';
const testAddr2 = 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4';
const block: DbBlock = {
block_hash: '0x1234',
index_block_hash: '0xdeadbeef',
parent_index_block_hash: '0x00',
parent_block_hash: '0xff0011',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 1594647996,
burn_block_hash: '0x1234',
burn_block_height: 123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateBlock(client, block);
const tx: DbTx = {
tx_id: '0x1234',
tx_index: 4,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.alloc(0),
index_block_hash: block.index_block_hash,
block_hash: block.block_hash,
block_height: block.block_height,
burn_block_time: 1594647995,
parent_burn_block_time: 1626122935,
type_id: DbTxTypeId.Coinbase,
coinbase_payload: Buffer.from('coinbase hi'),
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: block.parent_index_block_hash,
parent_block_hash: block.parent_block_hash,
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'sender-addr',
origin_hash_mode: 1,
event_count: 1,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateTx(client, tx);
const nftEvent: DbNftEvent = {
canonical: true,
event_type: DbEventTypeId.NonFungibleTokenAsset,
asset_event_type_id: DbAssetEventTypeId.Transfer,
event_index: 0,
tx_id: tx.tx_id,
tx_index: tx.tx_index,
block_height: tx.block_height,
asset_identifier: 'bux',
value: serializeCV(intCV(0)),
recipient: testAddr1,
sender: testAddr2,
};
await db.updateNftEvent(client, tx, nftEvent);
const expectedResponse = {
tx_id: '0x1234',
tx_type: 'coinbase',
nonce: 0,
anchor_mode: 'any',
fee_rate: '1234',
is_unanchored: false,
sender_address: 'sender-addr',
sponsored: false,
post_condition_mode: 'allow',
post_conditions: [],
microblock_canonical: true,
microblock_hash: '',
microblock_sequence: I32_MAX,
parent_block_hash: block.parent_block_hash,
parent_burn_block_time: 1626122935,
parent_burn_block_time_iso: '2021-07-12T20:48:55.000Z',
tx_status: 'success',
block_hash: '0x1234',
block_height: 1,
burn_block_time: 1594647995,
burn_block_time_iso: '2020-07-13T13:46:35.000Z',
canonical: true,
tx_index: 4,
tx_result: {
hex: '0x0100000000000000000000000000000001',
repr: 'u1',
},
coinbase_payload: {
data: '0x636f696e62617365206869',
},
event_count: 1,
events: [
{
event_index: 0,
event_type: 'non_fungible_token_asset',
tx_id: '0x1234',
asset: {
asset_event_type: 'transfer',
asset_id: 'bux',
sender: 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4',
recipient: 'ST3J8EVYHVKH6XXPD61EE8XEHW4Y2K83861225AB1',
value: { hex: '0x0000000000000000000000000000000000', repr: '0' },
},
},
],
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const fetchTx = await supertest(api.server).get(`/extended/v1/tx/${tx.tx_id}`);
expect(fetchTx.status).toBe(200);
expect(fetchTx.type).toBe('application/json');
expect(JSON.parse(fetchTx.text)).toEqual(expectedResponse);
});
test('get mempool transactions from address', async () => {
const dbBlock: DbBlock = {
block_hash: '0xff',
index_block_hash: '0x1234',
parent_index_block_hash: '0x5678',
parent_block_hash: '0x5678',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 1594647995,
burn_block_hash: '0x1234',
burn_block_height: 123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateBlock(client, dbBlock);
const senderAddress = 'SP25YGP221F01S9SSCGN114MKDAK9VRK8P3KXGEMB';
const mempoolTx: DbMempoolTx = {
tx_id: '0x521234',
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.from('test-raw-mempool-tx'),
type_id: DbTxTypeId.Coinbase,
status: 1,
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: senderAddress,
origin_hash_mode: 1,
coinbase_payload: Buffer.from('hi'),
pruned: false,
receipt_time: 1616063078,
};
await db.updateMempoolTxs({ mempoolTxs: [mempoolTx] });
const result = await supertest(api.server).get(
`/extended/v1/address/${mempoolTx.sender_address}/mempool`
);
expect(result.status).toBe(200);
expect(result.type).toBe('application/json');
});
test('get mempool transactions: address not valid', async () => {
const senderAddress = 'test-sender-address';
const mempoolTx: DbMempoolTx = {
tx_id: '0x521234',
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.from('test-raw-mempool-tx'),
type_id: DbTxTypeId.Coinbase,
status: 1,
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: senderAddress,
origin_hash_mode: 1,
coinbase_payload: Buffer.from('hi'),
pruned: false,
receipt_time: 1616063078,
};
await db.updateMempoolTxs({ mempoolTxs: [mempoolTx] });
const result = await supertest(api.server).get(`/extended/v1/address/${senderAddress}/mempool`);
expect(result.status).toBe(400);
expect(result.type).toBe('application/json');
});
test('get mempool transactions from address with offset and limit', async () => {
const dbBlock: DbBlock = {
block_hash: '0xff',
index_block_hash: '0x1234',
parent_index_block_hash: '0x5678',
parent_block_hash: '0x5678',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 1594647995,
burn_block_hash: '0x1234',
burn_block_height: 123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateBlock(client, dbBlock);
const senderAddress = 'SP25YGP221F01S9SSCGN114MKDAK9VRK8P3KXGEMB';
const mempoolTx: DbMempoolTx = {
tx_id: '0x521234',
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.from('test-raw-mempool-tx'),
type_id: DbTxTypeId.Coinbase,
status: 1,
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: senderAddress,
origin_hash_mode: 1,
coinbase_payload: Buffer.from('hi'),
pruned: false,
receipt_time: 1616063078,
};
await db.updateMempoolTxs({ mempoolTxs: [mempoolTx] });
const result = await supertest(api.server).get(
`/extended/v1/address/${mempoolTx.sender_address}/mempool?limit=20&offset=0`
);
const expectedResponse = {
limit: 20,
offset: 0,
total: 1,
results: [
{
tx_id: '0x521234',
tx_status: 'pending',
tx_type: 'coinbase',
receipt_time: 1616063078,
receipt_time_iso: '2021-03-18T10:24:38.000Z',
anchor_mode: 'any',
nonce: 0,
fee_rate: '1234',
sender_address: 'SP25YGP221F01S9SSCGN114MKDAK9VRK8P3KXGEMB',
sponsored: false,
post_condition_mode: 'allow',
post_conditions: [],
coinbase_payload: {
data: '0x6869',
},
},
],
};
expect(result.status).toBe(200);
expect(result.type).toBe('application/json');
expect(result.body.results.length).toBe(1);
expect(result.body.total).toBe(1);
expect(result.body.limit).toBe(20);
expect(result.body.offset).toBe(0);
expect(JSON.parse(result.text)).toEqual(expectedResponse);
});
test('fetch transactions from block', async () => {
const block: DbBlock = {
block_hash: '0x1234',
index_block_hash: '0xdeadbeef',
parent_index_block_hash: '0x00',
parent_block_hash: '0xff0011',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 94869286,
burn_block_hash: '0x1234',
burn_block_height: 123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateBlock(client, block);
const tx: DbTx = {
tx_id: '0x1234',
tx_index: 4,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.alloc(0),
index_block_hash: block.index_block_hash,
block_hash: block.block_hash,
block_height: block.block_height,
burn_block_time: block.burn_block_time,
parent_burn_block_time: 1626122935,
type_id: DbTxTypeId.Coinbase,
coinbase_payload: Buffer.from('coinbase hi'),
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: block.parent_microblock_hash,
parent_block_hash: block.parent_index_block_hash,
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'sender-addr',
origin_hash_mode: 1,
event_count: 0,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateTx(client, tx);
const result = await supertest(api.server).get(
`/extended/v1/tx/block/${block.block_hash}?limit=20&offset=0`
);
expect(result.status).toBe(200);
expect(result.type).toBe('application/json');
});
test('fetch transactions from block', async () => {
const block: DbBlock = {
block_hash: '0x1234',
index_block_hash: '0xdeadbeef',
parent_index_block_hash: '0x00',
parent_block_hash: '0xff0011',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 94869286,
burn_block_hash: '0x1234',
burn_block_height: 123,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateBlock(client, block);
const tx: DbTx = {
tx_id: '0x1234',
tx_index: 4,
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.alloc(0),
index_block_hash: block.index_block_hash,
block_hash: block.block_hash,
block_height: block.block_height,
burn_block_time: block.burn_block_time,
parent_burn_block_time: 1626122935,
type_id: DbTxTypeId.Coinbase,
coinbase_payload: Buffer.from('coinbase hi'),
status: 1,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: block.parent_index_block_hash,
parent_block_hash: block.parent_block_hash,
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'sender-addr',
origin_hash_mode: 1,
event_count: 0,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
await db.updateTx(client, tx);
const result1 = await supertest(api.server).get(`/extended/v1/tx/block/${block.block_hash}`);
expect(result1.status).toBe(200);
expect(result1.type).toBe('application/json');
expect(result1.body.limit).toBe(96);
expect(result1.body.offset).toBe(0);
expect(result1.body.total).toBe(1);
expect(result1.body.results.length).toBe(1);
const result2 = await supertest(api.server).get(
`/extended/v1/tx/block/${block.block_hash}?limit=20&offset=15`
);
expect(result2.body.limit).toBe(20);
expect(result2.body.offset).toBe(15);
expect(result2.body.total).toBe(1);
expect(result2.body.results.length).toBe(0);
const result3 = await supertest(api.server).get(
`/extended/v1/tx/block_height/${block.block_height}`
);
expect(result3.status).toBe(200);
expect(result3.type).toBe('application/json');
expect(result3.body.limit).toBe(96);
expect(result3.body.offset).toBe(0);
expect(result3.body.total).toBe(1);
expect(result3.body.results.length).toBe(1);
const result4 = await supertest(api.server).get(
`/extended/v1/tx/block_height/${block.block_height}?limit=20&offset=15`
);
expect(result4.body.limit).toBe(20);
expect(result4.body.offset).toBe(15);
expect(result4.body.total).toBe(1);
expect(result4.body.results.length).toBe(0);
});
test('Get fee rate', async () => {
const request: FeeRateRequest = {
transaction: '0x5e9f3933e358df6a73fec0d47ce3e1062c20812c129f5294e6f37a8d27c051d9',
};
const result = await supertest(api.server).post('/extended/v1/fee_rate').send(request);
expect(result.status).toBe(200);
expect(result.type).toBe('application/json');
expect(result.body.fee_rate).toBe(FEE_RATE);
});
test('Block execution cost', async () => {
const dbBlock: DbBlock = {
block_hash: '0x0123',
index_block_hash: '0x1234',
parent_index_block_hash: '0x00',
parent_block_hash: '0x5678',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 39486,
burn_block_hash: '0x1234',
burn_block_height: 123,
miner_txid: '0x4321',
canonical: false,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
};
const dbTx1: DbTx = {
...dbBlock,
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000000',
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.from('test-raw-tx'),
type_id: DbTxTypeId.Coinbase,
coinbase_payload: Buffer.from('coinbase hi'),
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: true,
sender_address: 'sender-addr',
sponsor_address: 'sponsor-addr',
origin_hash_mode: 1,
parent_burn_block_time: 1626122935,
tx_index: 4,
status: DbTxStatus.Success,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: '',
event_count: 0,
execution_cost_read_count: 1,
execution_cost_read_length: 2,
execution_cost_runtime: 2,
execution_cost_write_count: 1,
execution_cost_write_length: 1,
};
const dbTx2: DbTx = {
...dbBlock,
tx_id: '0x8912000000000000000000000000000000000000000000000000000000000001',
anchor_mode: 3,
nonce: 0,
raw_tx: Buffer.from('test-raw-tx'),
type_id: DbTxTypeId.Coinbase,
coinbase_payload: Buffer.from('coinbase hi'),
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 1234n,
sponsored: true,
sender_address: 'sender-addr',
sponsor_address: 'sponsor-addr',
origin_hash_mode: 1,
parent_burn_block_time: 1626122935,
tx_index: 4,
status: DbTxStatus.Success,
raw_result: '0x0100000000000000000000000000000001', // u1
canonical: true,
microblock_canonical: true,
microblock_sequence: I32_MAX,
microblock_hash: '',
parent_index_block_hash: '',
event_count: 0,
execution_cost_read_count: 2,
execution_cost_read_length: 2,
execution_cost_runtime: 2,
execution_cost_write_count: 2,
execution_cost_write_length: 2,
};
const dataStoreUpdate: DataStoreBlockUpdateData = {
block: dbBlock,
microblocks: [],
minerRewards: [],
txs: [
{
tx: dbTx1,
stxEvents: [],
stxLockEvents: [],
ftEvents: [],
nftEvents: [],
contractLogEvents: [],
smartContracts: [],
names: [],
namespaces: [],
},
{
tx: dbTx2,
stxEvents: [],
stxLockEvents: [],
ftEvents: [],
nftEvents: [],
contractLogEvents: [],
smartContracts: [],
names: [],
namespaces: [],
},
],
};
await db.update(dataStoreUpdate);
const blockQuery = await supertest(api.server).get(`/extended/v1/block/${dbBlock.block_hash}`);
expect(blockQuery.body.execution_cost_read_count).toBe(3);
expect(blockQuery.body.execution_cost_read_length).toBe(4);
expect(blockQuery.body.execution_cost_runtime).toBe(4);
expect(blockQuery.body.execution_cost_write_count).toBe(3);
expect(blockQuery.body.execution_cost_write_length).toBe(3);
});
test('empty abi', async () => {
const source_code = `;; pg-mdomains-v1\n;;\n;; Decentralized domain names manager for Paradigma\n;; To facilitate acquisition of Stacks decentralized domain names\n(use-trait sip-010-trait 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.sip-010-trait )\n(use-trait token-trait 'SP3YK7KWMYRCDMV5M4792T0T7DERQXHJJGGEPV1N8.paradigma-token-trait-v1.paradigma-token-trait)\n\n\n;; constants\n(define-constant ERR_INSUFFICIENT_FUNDS 101)\n(define-constant ERR_UNAUTHORIZED 109)\n(define-constant ERR_NAME_PREORDER_FUNDS_INSUFFICIENT 203) ;; transfer to sponsored \n(define-constant ERR_DOMAINNAME_MANAGER_NOT_FOUND 501)\n\n;; set constant for contract owner, used for updating token-uri\n(define-constant CONTRACT_OWNER tx-sender)\n\n;; initial value for domain wallet, set to this contract until initialized\n(define-data-var domainWallet principal 'SP3YK7KWMYRCDMV5M4792T0T7DERQXHJJGGEPV1N8)\n\n(define-data-var platformDomainWallet principal 'SPRK2JVQ988PYT19JSAJNR3K9YZAZGVY04XMC2Z7) ;; Wallet where to transfer share fee services\n\n;; Manage domain name service fees\n;; by accepted tokens\n(define-map DomainServiceFeeIndex\n {\n serviceId: uint\n }\n {\n tokenSymbol: (string-ascii 32),\n } \n)\n\n(define-read-only (get-domain-service-fee-index (id uint))\n (map-get? DomainServiceFeeIndex\n {\n serviceId: id\n }\n ) \n)\n\n(define-map DomainServiceFee\n {\n tokenSymbol: (string-ascii 32),\n }\n {\n fee: uint\n }\n)\n(define-read-only (get-domain-service-fee (tokenSymbol (string-ascii 32)))\n (unwrap-panic (get fee \n (map-get? DomainServiceFee\n {tokenSymbol: tokenSymbol}\n )\n )\n )\n)\n(define-data-var domainServiceFeeCount uint u0)\n(define-read-only (get-domain-service-fee-count)\n (var-get domainServiceFeeCount)\n)\n\n;; Set reference info for domain service fee\n;; protected function to update domain service fee variable\n(define-public (create-domain-service-fee \n (tokenSymbol (string-ascii 32))\n (fee uint) \n )\n (begin\n (if (is-authorized-domain) \n (if\n (is-none \n (map-get? DomainServiceFee\n {\n tokenSymbol: tokenSymbol\n }\n ) \n )\n (begin\n (var-set domainServiceFeeCount (+ (var-get domainServiceFeeCount) u1))\n (map-insert DomainServiceFeeIndex\n { \n serviceId: (var-get domainServiceFeeCount)\n }\n {\n tokenSymbol: tokenSymbol\n } \n )\n (map-insert DomainServiceFee \n {\n tokenSymbol: tokenSymbol\n } \n {\n fee: fee\n }\n ) \n (ok true)\n )\n (begin\n (ok \n (map-set DomainServiceFee \n {\n tokenSymbol: tokenSymbol\n } \n {\n fee: fee\n }\n )\n )\n )\n )\n (err ERR_UNAUTHORIZED)\n )\n )\n)\n\n;; check if contract caller is contract owner\n(define-private (is-authorized-owner)\n (is-eq contract-caller CONTRACT_OWNER)\n)\n\n;; Token flow management\n\n;; Stores participants DomainName service sell\n\n;; (define-data-var domainNameManagerCount -list (list 2000 uint) (list))\n\n(define-data-var domainNameManagerCount uint u0)\n\n(define-read-only (get-domain-name-manager-count)\n (var-get domainNameManagerCount)\n)\n(define-map DomainNameManagersIndex\n { domainNMId: uint }\n {\n nameSpace: (buff 48), ;; domain namespace defined in Blockchain Name Service (BNS) like .app\n domainName: (buff 48) ;; domain name under a namespace like xck in xck.app\n }\n)\n\n(define-read-only (get-domain-name-managers-index (id uint))\n (map-get? DomainNameManagersIndex\n {\n domainNMId: id\n }\n ) \n)\n\n(define-map DomainNameManagers\n {\n nameSpace: (buff 48), ;; domain namespace defined in Blockchain Name Service (BNS) like .app\n domainName: (buff 48) ;; domain name under a namespace like xck in xck.app\n }\n {\n domainNameWallet: principal, ;; DomainName manager account - branding and domainName token\n domainNameFeePerc: uint, ;; DomainName share percentage of fee (ie u10)\n domainNameFeeTokenMint: uint, ;; Tokens considered reciprocity to domainName token\n domainNameTokenSymbol: (string-utf8 5), ;; Token Symbol used to mint domainName token\n sponsoredWallet: principal, ;; Sponsored institution account\n sponsoredFeePerc: uint, ;; Sponsored share percentage of fee (ie u10)\n sponsoredDID: (string-utf8 256), ;; Sponsored Stacks ID\n sponsoredUri: (string-utf8 256), ;; Sponsored website Uri\n referencerFeeTokenMint: uint ;; Tokens for promoters references as reciprocity \n }\n)\n\n;; returns set domain wallet principal\n(define-read-only (get-domain-wallet)\n (var-get domainWallet)\n)\n\n;; checks if caller is Auth contract\n(define-private (is-authorized-auth) \n (is-eq contract-caller 'SP3YK7KWMYRCDMV5M4792T0T7DERQXHJJGGEPV1N8)\n) \n\n;; protected function to update domain wallet variable\n(define-public (set-domain-wallet (newDomainWallet principal))\n (begin\n (asserts! (is-authorized-auth) (err ERR_UNAUTHORIZED)) \n (ok (var-set domainWallet newDomainWallet))\n )\n)\n\n;; check if contract caller is domain wallet\n(define-private (is-authorized-domain)\n (is-eq contract-caller (var-get domainWallet))\n)\n\n;; Set reference info for domainName managers\n(define-public (create-domainname-manager \n (nameSpace (buff 48))\n (domainName (buff 48)) \n (domainNameWallet principal) \n (domainNameFeePerc uint) \n (domainNameFeeTokenMint uint) \n (tokenSymbol (string-utf8 5))\n (sponsoredWallet principal) \n (sponsoredFeePerc uint)\n (sponsoredDID (string-utf8 256))\n (sponsoredUri (string-utf8 256))\n (referencerFeeTokenMint uint)\n )\n (begin\n (if (is-authorized-domain) \n (if\n (is-none \n (map-get? DomainNameManagers \n {\n nameSpace: nameSpace,\n domainName: domainName\n }\n ) \n )\n (begin\n (var-set domainNameManagerCount (+ (var-get domainNameManagerCount) u1))\n (map-insert DomainNameManagersIndex\n { \n domainNMId: (var-get domainNameManagerCount)\n }\n {\n nameSpace: nameSpace,\n domainName: domainName\n } \n )\n (map-insert DomainNameManagers \n {\n nameSpace: nameSpace,\n domainName: domainName\n } \n {\n domainNameWallet: domainNameWallet,\n domainNameFeePerc: domainNameFeePerc,\n domainNameFeeTokenMint: domainNameFeeTokenMint,\n domainNameTokenSymbol: tokenSymbol,\n sponsoredWallet: sponsoredWallet,\n sponsoredFeePerc: sponsoredFeePerc,\n sponsoredDID: sponsoredDID,\n sponsoredUri: sponsoredUri,\n referencerFeeTokenMint: referencerFeeTokenMint\n }\n ) \n (ok true)\n )\n (begin\n (ok \n (map-set DomainNameManagers \n {\n nameSpace: nameSpace,\n domainName: domainName\n } \n {\n domainNameWallet: domainNameWallet,\n domainNameFeePerc: domainNameFeePerc,\n domainNameFeeTokenMint: domainNameFeeTokenMint,\n domainNameTokenSymbol: tokenSymbol,\n sponsoredWallet: sponsoredWallet,\n sponsoredFeePerc: sponsoredFeePerc,\n sponsoredDID: sponsoredDID,\n sponsoredUri: sponsoredUri,\n referencerFeeTokenMint: referencerFeeTokenMint\n }\n )\n )\n )\n )\n (err ERR_UNAUTHORIZED)\n )\n )\n)\n\n;; Gets the principal for domainName managers\n(define-read-only (get-ref-domainname-manager (nameSpace (buff 48)) (domainName (buff 48)))\n (ok (unwrap! (map-get? DomainNameManagers \n {\n nameSpace: nameSpace,\n domainName: domainName\n }\n )\n (err ERR_DOMAINNAME_MANAGER_NOT_FOUND)\n )\n )\n)\n\n\n;; Makes the name-preorder\n(define-public (bns-name-preorder (hashedSaltedFqn (buff 20)) (stxToBurn uint) (paymentSIP010Trait <sip-010-trait>) (reciprocityTokenTrait <token-trait>) (referencerWallet principal))\n (begin\n (asserts! (> (stx-get-balance tx-sender) stxToBurn) (err ERR_NAME_PREORDER_FUNDS_INSUFFICIENT))\n (let \n (\n (symbol (unwrap-panic (contract-call? paymentSIP010Trait get-symbol)))\n (fee (get-domain-service-fee symbol))\n (toBurn (- stxToBurn fee))\n (tr (order-to-register-domain tx-sender fee 0x616c6c 0x616c6c 0x737461636b73 paymentSIP010Trait reciprocityTokenTrait referencerWallet)) ;; Includes subdomain:all namespace:all name:stacks as domainnames\n )\n (ok (try! (contract-call? 'SP000000000000000000002Q6VF78.bns name-preorder hashedSaltedFqn toBurn)))\n ) \n )\n)\n\n;;
Gives the order to register a domain and subdomain associated to a domainName and transfers to the domain managers\n(define-public (order-to-register-domain (sender principal) (fee uint) (nameSpace (buff 48)) (domainName (buff 48)) (subDomain (buff 48)) \n (paymentSIP010Trait <sip-010-trait>) (reciprocityTokenTrait <token-trait>) (referencerWallet principal))\n (begin\n (asserts! (is-eq tx-sender sender) (err ERR_UNAUTHORIZED))\n (asserts! (> (unwrap-panic (contract-call? paymentSIP010Trait get-balance tx-sender)) fee) (err ERR_INSUFFICIENT_FUNDS))\n (let \n (\n (domainNameRef \n (unwrap-panic (map-get? DomainNameManagers \n {\n nameSpace: nameSpace,\n domainName: domainName\n }\n )\n )\n )\n (sponsoredFeePerc \n (get sponsoredFeePerc domainNameRef)\n )\n (sponsoredWallet \n (get sponsoredWallet domainNameRef)\n )\n (domainNameFeePerc \n (get domainNameFeePerc domainNameRef)\n ) \n (domainNameWallet \n (get domainNameWallet domainNameRef)\n )\n (domainNameFeeTokenMint \n (get domainNameFeeTokenMint domainNameRef)\n )\n (referencerFeeTokenMint\n (get referencerFeeTokenMint domainNameRef))\n (transferToSponsored (/ (* sponsoredFeePerc fee) u100) )\n (transferToDomainManager (/ (* domainNameFeePerc fee) u100))\n (transferToPlatform (/ (* (- u100 (+ domainNameFeePerc sponsoredFeePerc ) ) fee) u100))\n (platformDWallet (get-platform-domain-wallet))\n ) \n ;; transfer to sponsored \n (if (> transferToSponsored u0)\n (unwrap-panic (contract-call? paymentSIP010Trait transfer \n transferToSponsored \n sender \n sponsoredWallet\n none\n )\n )\n true\n )\n ;; transfer to domain name manager\n (if (> transferToDomainManager u0)\n (unwrap-panic (contract-call? paymentSIP010Trait transfer\n transferToDomainManager\n sender\n domainNameWallet\n none\n )\n )\n true\n )\n ;; transfer to platform manager\n (if (> transferToPlatform u0)\n (unwrap-panic (contract-call? paymentSIP010Trait transfer\n transferToPlatform\n sender \n platformDWallet\n none\n )\n )\n true\n )\n ;; mint token to sender as reciprocity\n (if (> domainNameFeeTokenMint u0)\n (unwrap-panic (as-contract (contract-call? reciprocityTokenTrait \n mint \n domainNameFeeTokenMint\n sender\n )\n )\n )\n true\n )\n ;; mint token for referencer (if there is) as reciprocity\n (if (> referencerFeeTokenMint u0)\n (unwrap-panic (as-contract (contract-call? reciprocityTokenTrait \n mint \n referencerFeeTokenMint\n referencerWallet\n )\n )\n )\n true\n )\n )\n (ok true)\n )\n)\n\n;; returns set domain wallet principal\n(define-read-only (get-platform-domain-wallet)\n (var-get platformDomainWallet)\n)\n;; protected function to update domain wallet variable\n(define-public (set-platform-domain-wallet (newPDomainWallet principal))\n (begin\n (asserts! (is-authorized-auth) (err ERR_UNAUTHORIZED)) \n (ok (var-set platformDomainWallet newPDomainWallet))\n )\n)`;
const abi = `{\"maps\":[{\"key\":{\"tuple\":[{\"name\":\"domainName\",\"type\":{\"buffer\":{\"length\":48}}},{\"name\":\"nameSpace\",\"type\":{\"buffer\":{\"length\":48}}}]},\"name\":\"DomainNameManagers\",\"value\":{\"tuple\":[{\"name\":\"domainNameFeePerc\",\"type\":\"uint128\"},{\"name\":\"domainNameFeeTokenMint\",\"type\":\"uint128\"},{\"name\":\"domainNameTokenSymbol\",\"type\":{\"string-utf8\":{\"length\":5}}},{\"name\":\"domainNameWallet\",\"type\":\"principal\"},{\"name\":\"referencerFeeTokenMint\",\"type\":\"uint128\"},{\"name\":\"sponsoredDID\",\"type\":{\"string-utf8\":{\"length\":256}}},{\"name\":\"sponsoredFeePerc\",\"type\":\"uint128\"},{\"name\":\"sponsoredUri\",\"type\":{\"string-utf8\":{\"length\":256}}},{\"name\":\"sponsoredWallet\",\"type\":\"principal\"}]}},{\"key\":{\"tuple\":[{\"name\":\"domainNMId\",\"type\":\"uint128\"}]},\"name\":\"DomainNameManagersIndex\",\"value\":{\"tuple\":[{\"name\":\"domainName\",\"type\":{\"buffer\":{\"length\":48}}},{\"name\":\"nameSpace\",\"type\":{\"buffer\":{\"length\":48}}}]}},{\"key\":{\"tuple\":[{\"name\":\"tokenSymbol\",\"type\":{\"string-ascii\":{\"length\":32}}}]},\"name\":\"DomainServiceFee\",\"value\":{\"tuple\":[{\"name\":\"fee\",\"type\":\"uint128\"}]}},{\"key\":{\"tuple\":[{\"name\":\"serviceId\",\"type\":\"uint128\"}]},\"name\":\"DomainServiceFeeIndex\",\"value\":{\"tuple\":[{\"name\":\"tokenSymbol\",\"type\":{\"string-ascii\":{\"length\":32}}}]}}],\"functions\":[{\"args\":[],\"name\":\"is-authorized-auth\",\"access\":\"private\",\"outputs\":{\"type\":\"bool\"}},{\"args\":[],\"name\":\"is-authorized-domain\",\"access\":\"private\",\"outputs\":{\"type\":\"bool\"}},{\"args\":[],\"name\":\"is-authorized-owner\",\"access\":\"private\",\"outputs\":{\"type\":\"bool\"}},{\"args\":[{\"name\":\"hashedSaltedFqn\",\"type\":{\"buffer\":{\"length\":20}}},{\"name\":\"stxToBurn\",\"type\":\"uint128\"},{\"name\":\"paymentSIP010Trait\",\"type\":\"trait_reference\"},{\"name\":\"reciprocityTokenTrait\",\"type\":\"trait_reference\"},{\"name\":\"referencerWallet\",\"type\":\"principal\"}],\"name\":\"bns-name-preorder\",\"access\":\"public\",\"outputs\":{\"type\":{\"response\":{\"ok\":\"uint128\",\"error\":\"int128\"}}}},{\"args\":[{\"name\":\"tokenSymbol\",\"type\":{\"string-ascii\":{\"length\":32}}},{\"name\":\"fee\",\"type\":\"uint128\"}],\"name\":\"create-domain-service-fee\",\"access\":\"public\",\"outputs\":{\"type\":{\"response\":{\"ok\":\"bool\",\"error\":\"int128\"}}}},{\"args\":[{\"name\":\"nameSpace\",\"type\":{\"buffer\":{\"length\":48}}},{\"name\":\"domainName\",\"type\":{\"buffer\":{\"length\":48}}},{\"name\":\"domainNameWallet\",\"type\":\"principal\"},{\"name\":\"domainNameFeePerc\",\"type\":\"uint128\"},{\"name\":\"domainNameFeeTokenMint\",\"type\":\"uint128\"},{\"name\":\"tokenSymbol\",\"type\":{\"string-utf8\":{\"length\":5}}},{\"name\":\"sponsoredWallet\",\"type\":\"principal\"},{\"name\":\"sponsoredFeePerc\",\"type\":\"uint128\"},{\"name\":\"sponsoredDID\",\"type\":{\"string-utf8\":{\"length\":256}}},{\"name\":\"sponsoredUri\",\"type\":{\"string-utf8\":{\"length\":256}}},{\"name\":\"referencerFeeTokenMint\",\"type\":\"uint128\"}],\"name\":\"create-domainname-manager\",\"access\":\"public\",\"outputs\":{\"type\":{\"response\":{\"ok\":\"bool\",\"error\":\"int128\"}}}},{\"args\":[{\"name\":\"sender\",\"type\":\"principal\"},{\"name\":\"fee\",\"type\":\"uint128\"},{\"name\":\"nameSpace\",\"type\":{\"buffer\":{\"length\":48}}},{\"name\":\"domainName\",\"type\":{\"buffer\":{\"length\":48}}},{\"name\":\"subDomain\",\"type\":{\"buffer\":{\"length\":48}}},{\"name\":\"paymentSIP010Trait\",\"type\":\"trait_reference\"},{\"name\":\"reciprocityTokenTrait\",\"type\":\"trait_reference\"},{\"name\":\"referencerWallet\",\"type\":\"principal\"}],\"name\":\"order-to-register-domain\",\"access\":\"public\",\"outputs\":{\"type\":{\"response\":{\"ok\":\"bool\",\"error\":\"int128\"}}}},{\"args\":[{\"name\":\"newDomainWallet\",\"type\":\"principal\"}],\"name\":\"set-domain-wallet\",\"access\":\"public\",\"outputs\":{\"type\":{\"response\":{\"ok\":\"bool\",\"error\":\"int128\"}}}},{\"args\":[{\"name\":\"newPDomainWallet\",\"type\":\"principal\"}],\"name\":\"set-platform-domain-wallet\",\"access\":\"public\",\"outputs\":{\"type\":{\"response\":{\"ok\":\"bool\",\"error\":\"int128\"}}}},{\"args\":[],\"name\":\"get-domain-name-manager-count\",\"access\":\"read_only\",\"outputs\":{\"type\":\"uint128\"}},{\"args\":[{\"name\":\"id\",\"type\":\"uint128\"}],\"name\":\"get-domain-name-managers-index\",\"access\":\"read_only\",\"outputs\":{\"type\":{\"optional\":{\"tuple\":[{\"name\":\"domainName\",\"type\":{\"buffer\":{\"length\":48}}},{\"name\":\"nameSpace\",\"type\":{\"buffer\":{\"length\":48}}}]}}}},{\"args\":[{\"name\":\"tokenSymbol\",\"type\":{\"string-ascii\":{\"length\":32}}}],\"name\":\"get-domain-service-fee\",\"access\":\"read_only\",\"outputs\":{\"type\":\"uint128\"}},{\"args\":[],\"name\":\"get-domain-service-fee-count\",\"access\":\"read_only\",\"outputs\":{\"type\":\"uint128\"}},{\"args\":[{\"name\":\"id\",\"type\":\"uint128\"}],\"name\":\"get-domain-service-fee-index\",\"access\":\"read_only\",\"outputs\":{\"type\":{\"optional\":{\"tuple\":[{\"name\":\"tokenSymbol\",\"type\":{\"string-ascii\":{\"length\":32}}}]}}}},{\"args\":[],\"name\":\"get-domain-wallet\",\"access\":\"read_only\",\"outputs\":{\"type\":\"principal\"}},{\"args\":[],\"name\":\"get-platform-domain-wallet\",\"access\":\"read_only\",\"outputs\":{\"type\":\"principal\"}},{\"args\":[{\"name\":\"nameSpace\",\"type\":{\"buffer\":{\"length\":48}}},{\"name\":\"domainName\",\"type\":{\"buffer\":{\"length\":48}}}],\"name\":\"get-ref-domainname-manager\",\"access\":\"read_only\",\"outputs\":{\"type\":{\"response\":{\"ok\":{\"tuple\":[{\"name\":\"domainNameFeePerc\",\"type\":\"uint128\"},{\"name\":\"domainNameFeeTokenMint\",\"type\":\"uint128\"},{\"name\":\"domainNameTokenSymbol\",\"type\":{\"string-utf8\":{\"length\":5}}},{\"name\":\"domainNameWallet\",\"type\":\"principal\"},{\"name\":\"referencerFeeTokenMint\",\"type\":\"uint128\"},{\"name\":\"sponsoredDID\",\"type\":{\"string-utf8\":{\"length\":256}}},{\"name\":\"sponsoredFeePerc\",\"type\":\"uint128\"},{\"name\":\"sponsoredUri\",\"type\":{\"string-utf8\":{\"length\":256}}},{\"name\":\"sponsoredWallet\",\"type\":\"principal\"}]},\"error\":\"int128\"}}}}],\"variables\":[{\"name\":\"CONTRACT_OWNER\",\"type\":\"principal\",\"access\":\"constant\"},{\"name\":\"ERR_DOMAINNAME_MANAGER_NOT_FOUND\",\"type\":\"int128\",\"access\":\"constant\"},{\"name\":\"ERR_INSUFFICIENT_FUNDS\",\"type\":\"int128\",\"access\":\"constant\"},{\"name\":\"ERR_NAME_PREORDER_FUNDS_INSUFFICIENT\",\"type\":\"int128\",\"access\":\"constant\"},{\"name\":\"ERR_UNAUTHORIZED\",\"type\":\"int128\",\"access\":\"constant\"},{\"name\":\"domainNameManagerCount\",\"type\":\"uint128\",\"access\":\"variable\"},{\"name\":\"domainServiceFeeCount\",\"type\":\"uint128\",\"access\":\"variable\"},{\"name\":\"domainWallet\",\"type\":\"principal\",\"access\":\"variable\"},{\"name\":\"platformDomainWallet\",\"type\":\"principal\",\"access\":\"variable\"}],\"fungible_tokens\":[],\"non_fungible_tokens\":[]}`;
const tx1: DbTx = {
type_id: DbTxTypeId.ContractCall,
tx_id: '0x8407751d1a8d11ee986aca32a6459d9cd798283a12e048ebafcd4cc7dadb29af',
anchor_mode: DbTxAnchorMode.Any,
nonce: 0,
raw_tx: Buffer.alloc(0),
canonical: true,
microblock_canonical: true,
microblock_sequence: 2147483647,
microblock_hash: '',
parent_index_block_hash: '',
parent_block_hash: '',
block_height: 1,
tx_index: 33,
index_block_hash: '0xd10ccecfd7ac9e5f8a10de0532fac028559b31a6ff494d82147f6297fb663139',
block_hash: '0xd10ccecfd7ac9e5f8a10de0532fac028559b31a6ff494d82147f6297fb663139',
burn_block_time: 1637003433,
parent_burn_block_time: 1637002470,
status: DbTxStatus.Success,
raw_result: '0x0100000000000000000000000000000001', // u1
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 139200n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'SPX3DV9X9CGA8P14B3CMP2X8DBW6ZDXEAXDNPTER',
origin_hash_mode: 1,
event_count: 6,
execution_cost_read_count: 55,
execution_cost_read_length: 88420,
execution_cost_runtime: 116256000,
execution_cost_write_count: 9,
execution_cost_write_length: 339,
contract_call_contract_id: 'SP3YK7KWMYRCDMV5M4792T0T7DERQXHJJGGEPV1N8.pg-mdomains-v1',
contract_call_function_name: 'bns-name-preorder',
};
const tx2: DbTx = {
type_id: DbTxTypeId.ContractCall,
tx_id: '0x1513739d6a3f86d4597f5296cc536f6890e2affff9aece285e37399be697b43f',
anchor_mode: DbTxAnchorMode.Any,
nonce: 0,
raw_tx: Buffer.alloc(0),
canonical: true,
microblock_canonical: true,
microblock_sequence: 2147483647,
microblock_hash: '',
parent_index_block_hash: '',
parent_block_hash: '',
block_height: 1,
tx_index: 33,
index_block_hash: '0xd10ccecfd7ac9e5f8a10de0532fac028559b31a6ff494d82147f6297fb663139',
block_hash: '0xd10ccecfd7ac9e5f8a10de0532fac028559b31a6ff494d82147f6297fb663139',
burn_block_time: 1637003433,
parent_burn_block_time: 1637002470,
status: DbTxStatus.Success,
raw_result: '0x0100000000000000000000000000000001', // u1
post_conditions: Buffer.from([0x01, 0xf5]),
fee_rate: 139200n,
sponsored: false,
sponsor_address: undefined,
sender_address: 'SPX3DV9X9CGA8P14B3CMP2X8DBW6ZDXEAXDNPTER',
origin_hash_mode: 1,
event_count: 6,
execution_cost_read_count: 55,
execution_cost_read_length: 88420,
execution_cost_runtime: 116256000,
execution_cost_write_count: 9,
execution_cost_write_length: 339,
contract_call_contract_id: 'SP000000000000000000002Q6VF78.bns',
contract_call_function_name: 'name-register',
};
const contractCall: DbSmartContract = {
tx_id: '0x668142abbcabb846e3f83183325325071a8b4882dcf5476a38148cb5b738fc83',
canonical: true,
contract_id: 'SP3YK7KWMYRCDMV5M4792T0T7DERQXHJJGGEPV1N8.pg-mdomains-v1',
block_height: 1,
source_code,
abi,
};
const contractCall2: DbSmartContract = {
tx_id: '0xd8a9a4528ae833e1894eee676af8d218f8facbf95e166472df2c1a64219b5dfb',
canonical: true,
contract_id: 'SP000000000000000000002Q6VF78.bns',
block_height: 1,
source_code,
abi: JSON.stringify(''),
};
const dbBlock: DbBlock = {
block_hash: '0xd10ccecfd7ac9e5f8a10de0532fac028559b31a6ff494d82147f6297fb663139',
index_block_hash: '0xd10ccecfd7ac9e5f8a10de0532fac028559b31a6ff494d82147f6297fb663139',
parent_index_block_hash: '',
parent_block_hash: '',
parent_microblock_hash: '',
parent_microblock_sequence: 0,
block_height: 1,
burn_block_time: 1637003433,
burn_block_hash: '0x0000000000000000000342c6f7e9313ffa6f0a92618edaf86351ca265aee1c7a',
burn_block_height: 1,
miner_txid: '0x4321',
canonical: true,
execution_cost_read_count: 1210,
execution_cost_read_length: 1919542,
execution_cost_runtime: 2480886000,
execution_cost_write_count: 138,
execution_cost_write_length: 91116,
};
const expected = {
tx_id: '0x8407751d1a8d11ee986aca32a6459d9cd798283a12e048ebafcd4cc7dadb29af',
nonce: 0,
fee_rate: '139200',
sender_address: 'SPX3DV9X9CGA8P14B3CMP2X8DBW6ZDXEAXDNPTER',
sponsored: false,
post_condition_mode: 'allow',
post_conditions: [],
anchor_mode: 'any',
is_unanchored: false,
block_hash: '0xd10ccecfd7ac9e5f8a10de0532fac028559b31a6ff494d82147f6297fb663139',
parent_block_hash: '',
block_height: 1,
burn_block_time: 1637003433,
burn_block_time_iso: '2021-11-15T19:10:33.000Z',
parent_burn_block_time: 1637002470,
parent_burn_block_time_iso: '2021-11-15T18:54:30.000Z',
canonical: true,
tx_index: 33,
tx_status: 'success',
tx_result: {
hex: '0x0100000000000000000000000000000001',
repr: 'u1',
},
microblock_hash: '',
microblock_sequence: 2147483647,
microblock_canonical: true,
event_count: 6,
events: [],
execution_cost_read_count: 55,
execution_cost_read_length: 88420,
execution_cost_runtime: 116256000,
execution_cost_write_count: 9,
execution_cost_write_length: 339,
tx_type: 'contract_call',
contract_call: {
contract_id: 'SP3YK7KWMYRCDMV5M4792T0T7DERQXHJJGGEPV1N8.pg-mdomains-v1',
function_name: 'bns-name-preorder',
function_signature:
'(define-public (bns-name-preorder (hashedSaltedFqn (buff 20)) (stxToBurn uint) (paymentSIP010Trait trait_reference) (reciprocityTokenTrait trait_reference) (referencerWallet principal)))',
},
};
const dataStoreUpdate: DataStoreBlockUpdateData = {
block: dbBlock,
microblocks: [],
minerRewards: [],
txs: [
{
tx: tx1,
stxEvents: [],
stxLockEvents: [],
ftEvents: [],
nftEvents: [],
contractLogEvents: [],
smartContracts: [{ ...contractCall }],
names: [],
namespaces: [],
},
{
tx: tx2,
stxEvents: [],
stxLockEvents: [],
ftEvents: [],
nftEvents: [],
contractLogEvents: [],
smartContracts: [{ ...contractCall2 }],
names: [],
namespaces: [],
},
],
};
await db.update(dataStoreUpdate);
const searchResult1 = await supertest(api.server).get(`/extended/v1/tx/${tx1.tx_id}`);
expect(JSON.parse(searchResult1.text)).toEqual(expected);
const expected2 = {
tx_id: '0x1513739d6a3f86d4597f5296cc536f6890e2affff9aece285e37399be697b43f',
nonce: 0,
fee_rate: '139200',
sender_address: 'SPX3DV9X9CGA8P14B3CMP2X8DBW6ZDXEAXDNPTER',
sponsored: false,
post_condition_mode: 'allow',
post_conditions: [],
anchor_mode: 'any',
is_unanchored: false,
block_hash: '0xd10ccecfd7ac9e5f8a10de0532fac028559b31a6ff494d82147f6297fb663139',
parent_block_hash: '',
block_height: 1,
burn_block_time: 1637003433,
burn_block_time_iso: '2021-11-15T19:10:33.000Z',
parent_burn_block_time: 1637002470,
parent_burn_block_time_iso: '2021-11-15T18:54:30.000Z',
canonical: true,
tx_index: 33,
tx_status: 'success',
tx_result: { hex: '0x0100000000000000000000000000000001', repr: 'u1' },
microblock_hash: '',
microblock_sequence: 2147483647,
microblock_canonical: true,
event_count: 6,
execution_cost_read_count: 55,
execution_cost_read_length: 88420,
execution_cost_runtime: 116256000,
execution_cost_write_count: 9,
execution_cost_write_length: 339,
tx_type: 'contract_call',
contract_call: {
contract_id: 'SP000000000000000000002Q6VF78.bns',
function_name: 'name-register',
function_signature: '',
},
events: [],
};
const searchResult2 = await supertest(api.server).get(`/extended/v1/tx/${tx2.tx_id}`);
expect(searchResult2.status).toBe(200);
expect(JSON.parse(searchResult2.text)).toEqual(expected2);
});
afterEach(async () => {
await api.terminate();
client.release();
await db?.close();
await runMigrations(undefined, 'down');
});
});