mirror of
https://github.com/alexgo-io/stacks-blockchain-api.git
synced 2026-01-12 22:43:34 +08:00
This commit is contained in:
@@ -544,8 +544,8 @@ export function createAddressRouter(db: DataStore, chainId: ChainID): RouterWith
|
||||
return;
|
||||
}
|
||||
const results: AddressNonces = {
|
||||
last_executed_tx_nonce: nonceQuery.result.nonce,
|
||||
possible_next_nonce: nonceQuery.result.nonce + 1,
|
||||
last_executed_tx_nonce: nonceQuery.result.lastExecutedTxNonce as number,
|
||||
possible_next_nonce: nonceQuery.result.possibleNextNonce,
|
||||
// Note: OpenAPI type generator doesn't support `nullable: true` so force cast it here
|
||||
last_mempool_tx_nonce: (null as unknown) as number,
|
||||
detected_missing_nonces: [],
|
||||
|
||||
@@ -71,6 +71,7 @@ export function createRosettaAccountRouter(db: DataStore, chainId: ChainID): Rou
|
||||
stxAddress: accountIdentifier.address,
|
||||
blockIdentifier: { height: block.block_height },
|
||||
});
|
||||
const sequenceNumber = accountNonceQuery.found ? accountNonceQuery.result.possibleNextNonce : 0;
|
||||
|
||||
const extra_metadata: any = {};
|
||||
|
||||
@@ -120,7 +121,7 @@ export function createRosettaAccountRouter(db: DataStore, chainId: ChainID): Rou
|
||||
},
|
||||
],
|
||||
metadata: {
|
||||
sequence_number: accountNonceQuery.found ? accountNonceQuery.result.nonce ?? 0 : 0,
|
||||
sequence_number: sequenceNumber,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -780,7 +780,7 @@ export interface DataStore extends DataStoreEventEmitter {
|
||||
getAddressNonceAtBlock(args: {
|
||||
stxAddress: string;
|
||||
blockIdentifier: BlockIdentifier;
|
||||
}): Promise<FoundOrNot<{ nonce: number }>>;
|
||||
}): Promise<FoundOrNot<{ lastExecutedTxNonce: number | null; possibleNextNonce: number }>>;
|
||||
|
||||
getAddressNonces(args: {
|
||||
stxAddress: string;
|
||||
|
||||
@@ -573,7 +573,7 @@ export class MemoryDataStore
|
||||
getAddressNonceAtBlock(args: {
|
||||
stxAddress: string;
|
||||
blockIdentifier: BlockIdentifier;
|
||||
}): Promise<FoundOrNot<{ nonce: number }>> {
|
||||
}): Promise<FoundOrNot<{ lastExecutedTxNonce: number | null; possibleNextNonce: number }>> {
|
||||
throw new Error('not yet implemented');
|
||||
}
|
||||
|
||||
|
||||
@@ -1725,13 +1725,13 @@ export class PgDataStore
|
||||
async getAddressNonceAtBlock(args: {
|
||||
stxAddress: string;
|
||||
blockIdentifier: BlockIdentifier;
|
||||
}): Promise<FoundOrNot<{ nonce: number }>> {
|
||||
}): Promise<FoundOrNot<{ lastExecutedTxNonce: number | null; possibleNextNonce: number }>> {
|
||||
return await this.queryTx(async client => {
|
||||
const dbBlock = await this.getBlockInternal(client, args.blockIdentifier);
|
||||
if (!dbBlock.found) {
|
||||
return { found: false };
|
||||
}
|
||||
const executedTxNonce = await client.query<{ nonce: number | null }>(
|
||||
const nonceQuery = await client.query<{ nonce: number | null }>(
|
||||
`
|
||||
SELECT MAX(nonce) nonce
|
||||
FROM txs
|
||||
@@ -1741,8 +1741,15 @@ export class PgDataStore
|
||||
`,
|
||||
[args.stxAddress, dbBlock.result.block_height]
|
||||
);
|
||||
const nonce = executedTxNonce.rows[0]?.nonce ?? 0;
|
||||
return { found: true, result: { nonce } };
|
||||
let lastExecutedTxNonce: number | null = null;
|
||||
let possibleNextNonce = 0;
|
||||
if (nonceQuery.rows.length > 0 && typeof nonceQuery.rows[0].nonce === 'number') {
|
||||
lastExecutedTxNonce = nonceQuery.rows[0].nonce;
|
||||
possibleNextNonce = lastExecutedTxNonce + 1;
|
||||
} else {
|
||||
possibleNextNonce = 0;
|
||||
}
|
||||
return { found: true, result: { lastExecutedTxNonce, possibleNextNonce } };
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -628,6 +628,8 @@ describe('Rosetta API', () => {
|
||||
const testAddr1 = 'STNN931GWC0XMRBWXYJQXTEKT4YFB1Z7YTCV3RZN';
|
||||
const testAddr1Key = '532d5ff9f0d4980225a031f65a2dff75b351d675b086766917d43372cedf762901';
|
||||
const testAddr2 = 'ST2WFY0H48AS2VYPA7N69V2VJ8VKS8FSPQSPFE1Z8';
|
||||
const testAddr3 = 'ST5F760KN84TZK3VTZCTVFYCVXQBEVKNV9M7H2CW';
|
||||
|
||||
let expectedTxId: string = '';
|
||||
const broadcastTx = new Promise<DbTx>(resolve => {
|
||||
const listener: (txId: string) => void = async txId => {
|
||||
@@ -726,7 +728,7 @@ describe('Rosetta API', () => {
|
||||
},
|
||||
}],
|
||||
metadata: {
|
||||
sequence_number: 1,
|
||||
sequence_number: 2,
|
||||
},
|
||||
};
|
||||
expect(JSON.parse(nonceResult1.text)).toEqual(expectedResponse1);
|
||||
@@ -759,10 +761,44 @@ describe('Rosetta API', () => {
|
||||
},
|
||||
}],
|
||||
metadata: {
|
||||
sequence_number: 0,
|
||||
sequence_number: 1,
|
||||
},
|
||||
};
|
||||
expect(JSON.parse(nonceResult2.text)).toEqual(expectedResponse2);
|
||||
|
||||
// Test account without any existing txs, should have "next nonce" value of 0
|
||||
const request3: RosettaAccountBalanceRequest = {
|
||||
network_identifier: {
|
||||
blockchain: 'stacks',
|
||||
network: 'testnet',
|
||||
},
|
||||
block_identifier: {
|
||||
index: tx2!.block_height,
|
||||
},
|
||||
account_identifier: {
|
||||
address: testAddr3,
|
||||
},
|
||||
};
|
||||
const nonceResult3 = await supertest(api.server).post(`/rosetta/v1/account/balance/`).send(request3);
|
||||
expect(nonceResult3.status).toBe(200);
|
||||
expect(nonceResult3.type).toBe('application/json');
|
||||
const expectedResponse3: RosettaAccountBalanceResponse = {
|
||||
block_identifier: {
|
||||
hash: tx2!.block_hash,
|
||||
index: tx2!.block_height,
|
||||
},
|
||||
balances: [{
|
||||
value: '0',
|
||||
currency: {
|
||||
symbol: 'STX',
|
||||
decimals: 6,
|
||||
},
|
||||
}],
|
||||
metadata: {
|
||||
sequence_number: 0,
|
||||
},
|
||||
};
|
||||
expect(JSON.parse(nonceResult3.text)).toEqual(expectedResponse3);
|
||||
});
|
||||
|
||||
test('account/balance - fees calculated properly', async () => {
|
||||
|
||||
@@ -4016,6 +4016,7 @@ describe('api tests', () => {
|
||||
|
||||
test('address nonce', async () => {
|
||||
const testAddr1 = 'ST3DWSXBPYDB484QXFTR81K4AWG4ZB5XZNFF3H70C';
|
||||
const testAddr2 = 'ST5F760KN84TZK3VTZCTVFYCVXQBEVKNV9M7H2CW';
|
||||
|
||||
const block1 = new TestBlockBuilder({
|
||||
block_height: 1,
|
||||
@@ -4117,6 +4118,34 @@ describe('api tests', () => {
|
||||
expect(nonceResults4.type).toBe('application/json');
|
||||
expect(nonceResults4.body).toEqual(expectedNonceResults4);
|
||||
|
||||
// Get nonce for account with no transactions
|
||||
const expectedNonceResultsNoTxs1 = {
|
||||
detected_missing_nonces: [],
|
||||
last_executed_tx_nonce: null,
|
||||
last_mempool_tx_nonce: null,
|
||||
possible_next_nonce: 0,
|
||||
};
|
||||
const nonceResultsNoTxs1 = await supertest(api.server).get(
|
||||
`/extended/v1/address/${testAddr2}/nonces`
|
||||
);
|
||||
expect(nonceResultsNoTxs1.status).toBe(200);
|
||||
expect(nonceResultsNoTxs1.type).toBe('application/json');
|
||||
expect(nonceResultsNoTxs1.body).toEqual(expectedNonceResultsNoTxs1);
|
||||
|
||||
// Get nonce for account with no transactions
|
||||
const expectedNonceResultsNoTxs2 = {
|
||||
detected_missing_nonces: [],
|
||||
last_executed_tx_nonce: null,
|
||||
last_mempool_tx_nonce: null,
|
||||
possible_next_nonce: 0,
|
||||
};
|
||||
const nonceResultsNoTxs2 = await supertest(api.server).get(
|
||||
`/extended/v1/address/${testAddr2}/nonces?block_height=${block2.block.block_height}`
|
||||
);
|
||||
expect(nonceResultsNoTxs2.status).toBe(200);
|
||||
expect(nonceResultsNoTxs2.type).toBe('application/json');
|
||||
expect(nonceResultsNoTxs2.body).toEqual(expectedNonceResultsNoTxs2);
|
||||
|
||||
// Bad requests
|
||||
const nonceResults5 = await supertest(api.server).get(
|
||||
`/extended/v1/address/${testAddr1}/nonces?block_hash=xcvbnmn`
|
||||
|
||||
Reference in New Issue
Block a user