Skip to content

Commit

Permalink
Merge branch 'main' into brian/increaseFee
Browse files Browse the repository at this point in the history
  • Loading branch information
Geometer1729 committed Jan 10, 2025
2 parents 78c7448 + 2f4d437 commit e282a1e
Show file tree
Hide file tree
Showing 7 changed files with 420 additions and 217 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Added
- `setFee` and `setFeePerWU` for `Transaction` and `PendingTransaction`

### Changed
- Sort order for actions now includes the transaction sequence number and the exact account id sequence https://github.com/o1-labs/o1js/pull/1917

## [2.2.0](https://github.com/o1-labs/o1js/compare/e1bac02...b857516) - 2024-12-10

### Added
Expand Down
77 changes: 72 additions & 5 deletions src/lib/mina/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -641,9 +641,11 @@ function sendZkapp(
* @returns A promise that resolves to an array of objects containing event data, block information and transaction information for the account.
* @throws If the GraphQL request fails or the response is invalid.
* @example
* ```ts
* const accountInfo = { publicKey: 'B62qiwmXrWn7Cok5VhhB3KvCwyZ7NHHstFGbiU5n7m8s2RqqNW1p1wF' };
* const events = await fetchEvents(accountInfo);
* console.log(events);
* ```
*/
async function fetchEvents(
accountInfo: { publicKey: string; tokenId?: string },
Expand Down Expand Up @@ -691,10 +693,32 @@ async function fetchEvents(
});
}

/**
* Fetches account actions for a specified public key and token ID by performing a GraphQL query.
*
* @param accountInfo - An {@link ActionsQueryInputs} containing the public key, and optional query parameters for the actions query
* @param graphqlEndpoint - The GraphQL endpoint to fetch from. Defaults to the configured Mina endpoint.
*
* @returns A promise that resolves to an object containing the final actions hash for the account, and a list of actions
* @throws Will throw an error if the GraphQL endpoint is invalid or if the fetch request fails.
*
* @example
* ```ts
* const accountInfo = { publicKey: 'B62qiwmXrWn7Cok5VhhB3KvCwyZ7NHHstFGbiU5n7m8s2RqqNW1p1wF' };
* const actionsList = await fetchAccount(accountInfo);
* console.log(actionsList);
* ```
*/
async function fetchActions(
accountInfo: ActionsQueryInputs,
graphqlEndpoint = networkConfig.archiveEndpoint
) {
): Promise<
| {
actions: string[][];
hash: string;
}[]
| { error: FetchError }
> {
if (!graphqlEndpoint)
throw Error(
'fetchActions: Specified GraphQL endpoint is undefined. When using actions, you must set the archive node endpoint in Mina.Network(). Please ensure your Mina.Network() configuration includes an archive node endpoint.'
Expand All @@ -710,7 +734,26 @@ async function fetchActions(
graphqlEndpoint,
networkConfig.archiveFallbackEndpoints
);
if (error) throw Error(error.statusText);
// As of 2025-01-07, minascan is running a version of the node which supports `sequenceNumber` and `zkappAccountUpdateIds` fields
// We could consider removing this fallback since no other nodes are widely used
if (error) {
const originalError = error;
[response, error] = await makeGraphqlRequest<ActionQueryResponse>(
getActionsQuery(
publicKey,
actionStates,
tokenId,
/* _filterOptions= */ undefined,
/* _excludeTransactionInfo= */ true
),
graphqlEndpoint,
networkConfig.archiveFallbackEndpoints
);
if (error)
throw Error(
`ORIGINAL ERROR: ${originalError.statusText} \n\nRETRY ERROR: ${error.statusText}`
);
}
let fetchedActions = response?.data.actions;
if (fetchedActions === undefined) {
return {
Expand Down Expand Up @@ -757,9 +800,33 @@ export function createActionsList(
`No action data was found for the account ${publicKey} with the latest action state ${actionState}`
);

actionData = actionData.sort((a1, a2) => {
return Number(a1.accountUpdateId) < Number(a2.accountUpdateId) ? -1 : 1;
});
// DEPRECATED: In case the archive node is running an out-of-date version, best guess is to sort by the account update id
// As of 2025-01-07, minascan is running a version of the node which supports `sequenceNumber` and `zkappAccountUpdateIds` fields
// We could consider removing this fallback since no other nodes are widely used
if (!actionData[0].transactionInfo) {
actionData = actionData.sort((a1, a2) => {
return Number(a1.accountUpdateId) - Number(a2.accountUpdateId);
});
} else {
// sort actions within one block by transaction sequence number and account update sequence
actionData = actionData.sort((a1, a2) => {
const a1TxSequence = a1.transactionInfo!.sequenceNumber;
const a2TxSequence = a2.transactionInfo!.sequenceNumber;
if (a1TxSequence === a2TxSequence) {
const a1AuSequence =
a1.transactionInfo!.zkappAccountUpdateIds.indexOf(
Number(a1.accountUpdateId)
);
const a2AuSequence =
a2.transactionInfo!.zkappAccountUpdateIds.indexOf(
Number(a2.accountUpdateId)
);
return a1AuSequence - a2AuSequence;
} else {
return a1TxSequence - a2TxSequence;
}
});
}

// split actions by account update
let actionsByAccountUpdate: string[][][] = [];
Expand Down
141 changes: 48 additions & 93 deletions src/lib/mina/fetch.unit-test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { PrivateKey, TokenId } from 'o1js';
import { createActionsList } from './fetch.js';
import { mockFetchActionsResponse } from './fixtures/fetch-actions-response.js';
import { mockFetchActionsResponse as fetchResponseWithTxInfo } from './fixtures/fetch-actions-response-with-transaction-info.js';
import { mockFetchActionsResponse as fetchResponseNoTxInfo } from './fixtures/fetch-actions-response-without-transaction-info.js';
import { test, describe } from 'node:test';
import { removeJsonQuotes } from './graphql.js';
import { expect } from 'expect';
Expand Down Expand Up @@ -123,8 +124,8 @@ expect(actual).toEqual(expected);

console.log('regex tests complete 🎉');

describe('Fetch', async (t) => {
describe('#createActionsList with default params', async (t) => {
describe('Fetch', () => {
describe('#createActionsList with default params', () => {
const defaultPublicKey = PrivateKey.random().toPublicKey().toBase58();
const defaultActionStates = {
fromActionState: undefined,
Expand All @@ -136,96 +137,50 @@ describe('Fetch', async (t) => {
tokenId: TokenId.default.toString(),
};

const actionsList = createActionsList(
defaultAccountInfo,
mockFetchActionsResponse.data.actions
);

await test('orders the actions correctly', async () => {
expect(actionsList).toEqual([
{
actions: [
[
'20374659537065244088703638031937922870146667362923279084491778322749365537089',
'1',
],
],
hash: '10619825168606131449407092474314250900469658818945385329390497057469974757422',
},
{
actions: [
[
'20503089751358270987184701275168489753952341816059774976784079526478451099801',
'1',
],
],
hash: '25525130517416993227046681664758665799110129890808721833148757111140891481208',
},
{
actions: [
[
'3374074164183544078218789545772953663729921088152354292852793744356608231707',
'0',
],
],
hash: '290963518424616502946790040851348455652296009700336010663574777600482385855',
},
{
actions: [
[
'12630758077588166643924428865613845067150916064939816120404808842510620524633',
'1',
],
],
hash: '20673199655841577810393943638910551364027795297920791498278816237738641857371',
},
{
actions: [
[
'5643224648393140391519847064914429159616501351124129591669928700148350171602',
'0',
],
],
hash: '5284016523143033193387918577616839424871122381326995145988133445906503263869',
},
{
actions: [
[
'15789351988619560045401465240113496854401074115453702466673859303925517061263',
'0',
],
],
hash: '16944163018367910067334012882171366051616125936127175065464614786387687317044',
},
{
actions: [
[
'27263309408256888453299195755797013857604561285332380691270111409680109142128',
'1',
],
],
hash: '23662159967366296714544063539035629952291787828104373633198732070740691309118',
},
{
actions: [
[
'3378367318331499715304980508337843233019278703665446829424824679144818589558',
'1',
],
],
hash: '1589729766029695153975344283092689798747741638003354620355672853210932754595',
},
{
actions: [
[
'17137397755795687855356639427474789131368991089558570411893673365904353943290',
'1',
],
],
hash: '10964420428484427410756859799314206378989718180435238943573393516522086219419',
},
]);
describe('with a payload that is missing transaction info', () => {
const actionsList = createActionsList(
defaultAccountInfo,
fetchResponseNoTxInfo.data.actions
);

test('orders the actions correctly', () => {
const correctActionsHashes = [
'10619825168606131449407092474314250900469658818945385329390497057469974757422',
'25525130517416993227046681664758665799110129890808721833148757111140891481208',
'290963518424616502946790040851348455652296009700336010663574777600482385855',
'20673199655841577810393943638910551364027795297920791498278816237738641857371',
'5284016523143033193387918577616839424871122381326995145988133445906503263869',
'16944163018367910067334012882171366051616125936127175065464614786387687317044',
'23662159967366296714544063539035629952291787828104373633198732070740691309118',
'1589729766029695153975344283092689798747741638003354620355672853210932754595',
'10964420428484427410756859799314206378989718180435238943573393516522086219419',
];
expect(actionsList.map(({ hash }) => hash)).toEqual(
correctActionsHashes
);
});
});

describe('with a payload that includes transaction info', () => {
const actionsList = createActionsList(
defaultAccountInfo,
fetchResponseWithTxInfo.data.actions
);

test('orders the actions correctly', () => {
const correctActionsHashes = [
'23562173419146814432140831830018386191372262558717813981702672868292521523493',
'17091049856171838105194364005412166905307014398334933913160405653259432088216',
'17232885850087529233459756382038742870248640044940153006158312935267918515979',
'12636308717155378495657553296284990333618148856424346334743675423201692801125',
'17082487567758469425757467457967473265642001333824907522427890208991758759731',
'14226491442770650712364681911870921131508915865197379983185088742764625929348',
'13552033292375176242184292341671233419412691991179711376625259275814019808194',
];
expect(actionsList.map(({ hash }) => hash)).toEqual(
correctActionsHashes
);
});
});
});
});
``;
Loading

0 comments on commit e282a1e

Please sign in to comment.