diff --git a/run-ci-live-tests.sh b/run-ci-live-tests.sh index 3d6a53de9..d49f1dbc6 100755 --- a/run-ci-live-tests.sh +++ b/run-ci-live-tests.sh @@ -15,6 +15,8 @@ echo "" ./run src/examples/zkapps/hello-world/run-live.ts --bundle | add_prefix "HELLO_WORLD" & HELLO_WORLD_PROC=$! +./run src/examples/zkapps/reducer/run-live.ts --bundle | add_prefix "REDUCER" & +REDUCER_FLOW_PROC=$! ./run src/examples/zkapps/dex/run-live.ts --bundle | add_prefix "DEX" & DEX_PROC=$! ./run src/examples/fetch-live.ts --bundle | add_prefix "FETCH" & @@ -52,6 +54,13 @@ if [ $? -ne 0 ]; then echo "" FAILURE=1 fi +wait $REDUCER_FLOW_PROC +if [ $? -ne 0 ]; then + echo "" + echo "REDUCER_FLOW test failed." + echo "" + FAILURE=1 +fi # Exit with failure if any process failed if [ $FAILURE -ne 0 ]; then diff --git a/run-ci-tests.sh b/run-ci-tests.sh index c5153b514..263ceee16 100755 --- a/run-ci-tests.sh +++ b/run-ci-tests.sh @@ -14,8 +14,7 @@ case $TEST_TYPE in "Reducer integration tests") echo "Running reducer integration tests" - ./run src/examples/zkapps/reducer/actions-as-merkle-list.ts --bundle - ./run src/examples/zkapps/reducer/actions-as-merkle-list-iterator.ts --bundle + ./run src/examples/zkapps/reducer/run.ts --bundle ;; "Voting integration tests") diff --git a/src/examples/utils/network-configuration.ts b/src/examples/utils/network-configuration.ts new file mode 100644 index 000000000..1fb26628a --- /dev/null +++ b/src/examples/utils/network-configuration.ts @@ -0,0 +1,9 @@ +export { + DEFAULT_LIGHTNET_CONFIG +} + +const DEFAULT_LIGHTNET_CONFIG = { + mina: 'http://localhost:8080/graphql', + archive: 'http://localhost:8282', + lightnetAccountManager: 'http://localhost:8181', +} \ No newline at end of file diff --git a/src/examples/utils/random-accounts.ts b/src/examples/utils/random-accounts.ts new file mode 100644 index 000000000..c1ca28265 --- /dev/null +++ b/src/examples/utils/random-accounts.ts @@ -0,0 +1,23 @@ +import { PrivateKey, PublicKey } from 'o1js'; + +export { + randomAccounts +} + +/** + * Predefined accounts keys, labeled by the input strings. Useful for testing/debugging with consistent keys. + */ +function randomAccounts( + ...names: [K, ...K[]] +): { keys: Record; addresses: Record } { + let base58Keys = Array(names.length) + .fill('') + .map(() => PrivateKey.random().toBase58()); + let keys = Object.fromEntries( + names.map((name, idx) => [name, PrivateKey.fromBase58(base58Keys[idx])]) + ) as Record; + let addresses = Object.fromEntries( + names.map((name) => [name, keys[name].toPublicKey()]) + ) as Record; + return { keys, addresses }; +} diff --git a/src/examples/zkapps/reducer/actions-as-merkle-list-iterator.ts b/src/examples/zkapps/reducer/actions-as-merkle-list-iterator.ts index 0f7b7c4a2..15b9c6e67 100644 --- a/src/examples/zkapps/reducer/actions-as-merkle-list-iterator.ts +++ b/src/examples/zkapps/reducer/actions-as-merkle-list-iterator.ts @@ -4,11 +4,11 @@ * * This is mainly intended as an example for using `Iterator` and `MerkleList`, but it might also be useful as * a blueprint for processing actions in a custom and more explicit way. - * - * Warning: The reducer API in o1js is currently not safe to use in production applications. The `reduce()` - * method breaks if more than the hard-coded number (default: 32) of actions are pending. Work is actively + * + * Warning: The reducer API in o1js is currently not safe to use in production applications. The `reduce()` + * method breaks if more than the hard-coded number (default: 32) of actions are pending. Work is actively * in progress to mitigate this limitation. - */ + */ import { Field, Mina, @@ -20,6 +20,8 @@ import { assert, } from 'o1js'; +export { ActionsContract, testLocal }; + // constants for our static-sized provable code const MAX_UPDATES_WITH_ACTIONS = 100; const MAX_ACTIONS_PER_UPDATE = 2; @@ -81,44 +83,46 @@ class ActionsContract extends SmartContract { // TESTS -// set up a local blockchain +async function testLocal() { + // set up a local blockchain -let Local = await Mina.LocalBlockchain({ proofsEnabled: false }); -Mina.setActiveInstance(Local); + let Local = await Mina.LocalBlockchain({ proofsEnabled: true }); + Mina.setActiveInstance(Local); -let [sender, contractAddress] = Local.testAccounts; + let [sender, contractAddress] = Local.testAccounts.slice(4); -let contract = new ActionsContract(contractAddress); + let contract = new ActionsContract(contractAddress); -// deploy the contract + // deploy the contract -await ActionsContract.compile(); -console.log( - `rows for ${MAX_UPDATES_WITH_ACTIONS} updates with actions`, - (await ActionsContract.analyzeMethods()).accumulate.rows -); -let deployTx = await Mina.transaction(sender, async () => contract.deploy()); -await deployTx.sign([sender.key, contractAddress.key]).send(); + await ActionsContract.compile(); + console.log( + `rows for ${MAX_UPDATES_WITH_ACTIONS} updates with actions`, + (await ActionsContract.analyzeMethods()).accumulate.rows + ); + let deployTx = await Mina.transaction(sender, async () => contract.deploy()); + await deployTx.sign([sender.key, contractAddress.key]).send(); -// push some actions + // push some actions -let dispatchTx = await Mina.transaction(sender, async () => { - await contract.increment(Field(1)); - await contract.increment(Field(3)); - await contract.increment(Field(5)); - await contract.increment(Field(9)); - await contract.twoIncrements(Field(18), Field(19)); -}); -await dispatchTx.prove(); -await dispatchTx.sign([sender.key]).send(); + let dispatchTx = await Mina.transaction(sender, async () => { + await contract.increment(Field(1)); + await contract.increment(Field(3)); + await contract.increment(Field(5)); + await contract.increment(Field(9)); + await contract.twoIncrements(Field(18), Field(19)); + }); + await dispatchTx.prove(); + await dispatchTx.sign([sender.key]).send(); -assert(contract.reducer.getActions().data.get().length === 5); + assert(contract.reducer.getActions().data.get().length === 5); -// accumulate actions + // accumulate actions -Local.setProofsEnabled(true); -let accTx = await Mina.transaction(sender, () => contract.accumulate()); -await accTx.prove(); -await accTx.sign([sender.key]).send(); + Local.setProofsEnabled(true); + let accTx = await Mina.transaction(sender, () => contract.accumulate()); + await accTx.prove(); + await accTx.sign([sender.key]).send(); -assert(contract.counter.get().toBigInt() === 55n); + assert(contract.counter.get().toBigInt() === 55n); +} diff --git a/src/examples/zkapps/reducer/actions-as-merkle-list.ts b/src/examples/zkapps/reducer/actions-as-merkle-list.ts index 1eb813dbb..4d66e92b4 100644 --- a/src/examples/zkapps/reducer/actions-as-merkle-list.ts +++ b/src/examples/zkapps/reducer/actions-as-merkle-list.ts @@ -4,11 +4,11 @@ * * This is mainly intended as an example for using `MerkleList`, but it might also be useful as * a blueprint for processing actions in a custom and more explicit way. - * - * Warning: The reducer API in o1js is currently not safe to use in production applications. The `reduce()` - * method breaks if more than the hard-coded number (default: 32) of actions are pending. Work is actively + * + * Warning: The reducer API in o1js is currently not safe to use in production applications. The `reduce()` + * method breaks if more than the hard-coded number (default: 32) of actions are pending. Work is actively * in progress to mitigate this limitation. - */ + */ import { Bool, Mina, @@ -19,6 +19,8 @@ import { assert, } from 'o1js'; +export { MerkleListReducing, testLocal }; + // in this example, an action is just a public key type Action = PublicKey; const Action = PublicKey; @@ -64,7 +66,6 @@ class MerkleListReducing extends SmartContract { hasAddress = hasAddress.or(action.equals(address)); } } - assert(actions.isEmpty()); // we processed all actions assert(hasAddress); // we found the address } @@ -74,43 +75,45 @@ class MerkleListReducing extends SmartContract { // set up a local blockchain -let Local = await Mina.LocalBlockchain({ proofsEnabled: false }); -Mina.setActiveInstance(Local); +async function testLocal() { + let Local = await Mina.LocalBlockchain({ proofsEnabled: true }); + Mina.setActiveInstance(Local); -let [sender, contractAccount, otherAddress, anotherAddress] = - Local.testAccounts; + let [sender, contractAccount, otherAddress, anotherAddress] = + Local.testAccounts; -let contract = new MerkleListReducing(contractAccount); + let contract = new MerkleListReducing(contractAccount); -// deploy the contract + // deploy the contract -await MerkleListReducing.compile(); -console.log( - `rows for ${MAX_UPDATES_WITH_ACTIONS} updates with actions`, - (await MerkleListReducing.analyzeMethods()).assertContainsAddress.rows -); -let deployTx = await Mina.transaction(sender, async () => contract.deploy()); -await deployTx.sign([sender.key, contractAccount.key]).send(); + await MerkleListReducing.compile(); + console.log( + `rows for ${MAX_UPDATES_WITH_ACTIONS} updates with actions`, + (await MerkleListReducing.analyzeMethods()).assertContainsAddress.rows + ); + let deployTx = await Mina.transaction(sender, async () => contract.deploy()); + await deployTx.sign([sender.key, contractAccount.key]).send(); -// push some actions + // push some actions -let dispatchTx = await Mina.transaction(sender, async () => { - await contract.postAddress(otherAddress); - await contract.postAddress(contractAccount); - await contract.postTwoAddresses(anotherAddress, sender); - await contract.postAddress(anotherAddress); - await contract.postTwoAddresses(contractAccount, otherAddress); -}); -await dispatchTx.prove(); -await dispatchTx.sign([sender.key]).send(); + let dispatchTx = await Mina.transaction(sender, async () => { + await contract.postAddress(otherAddress); + await contract.postAddress(contractAccount); + await contract.postTwoAddresses(anotherAddress, sender); + await contract.postAddress(anotherAddress); + await contract.postTwoAddresses(contractAccount, otherAddress); + }); + await dispatchTx.prove(); + await dispatchTx.sign([sender.key]).send(); -assert(contract.reducer.getActions().data.get().length === 5); + assert(contract.reducer.getActions().data.get().length === 5); -// check if the actions contain the `sender` address + // check if the actions contain the `sender` address -Local.setProofsEnabled(true); -let containsTx = await Mina.transaction(sender, () => - contract.assertContainsAddress(sender) -); -await containsTx.prove(); -await containsTx.sign([sender.key]).send(); + Local.setProofsEnabled(true); + let containsTx = await Mina.transaction(sender, () => + contract.assertContainsAddress(sender) + ); + await containsTx.prove(); + await containsTx.sign([sender.key]).send(); +} diff --git a/src/examples/zkapps/reducer/run-live.ts b/src/examples/zkapps/reducer/run-live.ts new file mode 100644 index 000000000..dc291dba0 --- /dev/null +++ b/src/examples/zkapps/reducer/run-live.ts @@ -0,0 +1,139 @@ +import { + AccountUpdate, + Lightnet, + Mina, + PrivateKey, +} from 'o1js'; +import { DEFAULT_LIGHTNET_CONFIG } from '../../utils/network-configuration.js'; +import { randomAccounts } from '../../utils/random-accounts.js'; +import { tic, toc } from '../../utils/tic-toc.node.js'; +import { MerkleListReducing } from './actions-as-merkle-list.js'; + +tic('Run reducer examples against real network.'); +console.log(); +const network = Mina.Network(DEFAULT_LIGHTNET_CONFIG); +Mina.setActiveInstance(network); + +let { keys, addresses } = randomAccounts('contract', 'user1', 'user2'); + +let pendingTx: Mina.PendingTransaction; + +// compile contracts & wait for fee payer to be funded +const senderKey = (await Lightnet.acquireKeyPair()).privateKey +const sender = senderKey.toPublicKey(); + +const sender2Key = (await Lightnet.acquireKeyPair()).privateKey +const sender2 = sender2Key.toPublicKey(); + +tic('Compiling Merkle List Reducer Smart Contract'); +await MerkleListReducing.compile(); +toc(); +const merkleListReducerContract = new MerkleListReducing(addresses.contract); + +let senderSpec = { sender, fee: 1000000000 }; +let sender2Spec = { sender: sender2, fee: 1000000000 }; + +tic('deploy contracts'); +let deployTx = await Mina.transaction(senderSpec, async () => { + AccountUpdate.createSigned(sender).balance.subInPlace( + Mina.getNetworkConstants().accountCreationFee + ); + await merkleListReducerContract.deploy(); +}); +pendingTx = await deployTx.sign([senderKey, keys.contract]).send(); + +await pendingTx.wait(); +toc(); +// push some actions + +tic('dispatch transactions'); +let dispatchTx = await Mina.transaction(senderSpec, async () => { + await merkleListReducerContract.postAddress(addresses.user1); + await merkleListReducerContract.postAddress(addresses.contract); + await merkleListReducerContract.postTwoAddresses(addresses.user2, sender); + await merkleListReducerContract.postAddress(addresses.user2); + await merkleListReducerContract.postTwoAddresses( + addresses.contract, + addresses.user1 + ); +}); +await dispatchTx.prove(); +pendingTx = await dispatchTx.sign([senderKey]).send(); +await pendingTx.wait(); +toc(); + +tic('proving inclusion'); +// check if the actions contain the `sender` address +let containsTx = await Mina.transaction(senderSpec, async () => { + await merkleListReducerContract.assertContainsAddress(sender); + await merkleListReducerContract.assertContainsAddress(addresses.user1); + await merkleListReducerContract.assertContainsAddress(addresses.user2); + await merkleListReducerContract.assertContainsAddress(addresses.contract); +}); +await containsTx.prove(); +pendingTx = await containsTx.sign([senderKey]).send(); +await pendingTx.wait(); +toc(); + +tic('dispatch transactions (multi-transaction)'); + +tic('building'); +const txs = []; +let dispatchTx1 = await Mina.transaction(senderSpec, async () => { + await merkleListReducerContract.postAddress(addresses.user1); +}); +await dispatchTx1.prove(); +txs.push({ tx: dispatchTx1, privateKey: senderKey }); + +let dispatchTx2 = await Mina.transaction(sender2Spec, async () => { + await merkleListReducerContract.postTwoAddresses( + addresses.user2, + addresses.contract + ); +}); +await dispatchTx2.prove(); +txs.push({ tx: dispatchTx2, privateKey: sender2Key }); +toc(); + +tic('sending'); +const txPromises = []; +for (let i = 0; i < txs.length; i++) { + txPromises.push(txs[i].tx.sign([txs[i].privateKey]).send()); +} +await txPromises[0].wait(); +await txPromises[1].wait(); +toc(); + +tic('waiting'); +await new Promise((_r) => setTimeout(_r, 60_000)); +toc(); + +toc(); + +tic('proving inclusion (multi-transaction)'); +// check if the actions contain the `sender` address +let containsTx2 = await Mina.transaction(senderSpec, async () => { + await merkleListReducerContract.assertContainsAddress(addresses.user1); + await merkleListReducerContract.assertContainsAddress(addresses.user2); + await merkleListReducerContract.assertContainsAddress(addresses.contract); +}); +await containsTx2.prove(); +pendingTx = await containsTx2.sign([senderKey]).send(); +await pendingTx.wait(); +toc(); + +// ---- +toc(); + +console.log('Success!'); + +// Tear down +const keyPairReleaseMessage = await Lightnet.releaseKeyPair({ + publicKey: sender.toBase58(), +}); +if (keyPairReleaseMessage) console.info(keyPairReleaseMessage); + +const keyPairReleaseMessage2 = await Lightnet.releaseKeyPair({ + publicKey: sender2.toBase58(), +}); +if (keyPairReleaseMessage2) console.info(keyPairReleaseMessage2); diff --git a/src/examples/zkapps/reducer/run.ts b/src/examples/zkapps/reducer/run.ts new file mode 100644 index 000000000..d029c6f9a --- /dev/null +++ b/src/examples/zkapps/reducer/run.ts @@ -0,0 +1,5 @@ +import { testLocal as testActionsContract } from './actions-as-merkle-list-iterator.js'; +import { testLocal as testMerkleListContract } from './actions-as-merkle-list.js'; + +await testMerkleListContract(); +await testActionsContract(); diff --git a/src/lib/mina/fetch.ts b/src/lib/mina/fetch.ts index c9058e36c..8165bad6f 100644 --- a/src/lib/mina/fetch.ts +++ b/src/lib/mina/fetch.ts @@ -17,6 +17,7 @@ import { type LastBlockQueryResponse, type GenesisConstantsResponse, type LastBlockQueryFailureCheckResponse, + type FetchedAction, type FetchedBlock, type TransactionStatus, type TransactionStatusQueryResponse, @@ -64,6 +65,7 @@ export { sendZkapp, fetchEvents, fetchActions, + makeGraphqlRequest, Lightnet, type GenesisConstants, type ActionStatesStringified, @@ -77,6 +79,12 @@ type NetworkConfig = { lightnetAccountManagerEndpoint: string; }; +type ActionsQueryInputs = { + publicKey: string; + actionStates: ActionStatesStringified; + tokenId?: string; +}; + let networkConfig = { minaEndpoint: '', minaFallbackEndpoints: [] as string[], @@ -684,11 +692,7 @@ async function fetchEvents( } async function fetchActions( - accountInfo: { - publicKey: string; - actionStates: ActionStatesStringified; - tokenId?: string; - }, + accountInfo: ActionsQueryInputs, graphqlEndpoint = networkConfig.archiveEndpoint ) { if (!graphqlEndpoint) @@ -700,6 +704,7 @@ async function fetchActions( actionStates, tokenId = TokenId.toBase58(TokenId.default), } = accountInfo; + let [response, error] = await makeGraphqlRequest( getActionsQuery(publicKey, actionStates, tokenId), graphqlEndpoint, @@ -716,6 +721,22 @@ async function fetchActions( }; } + const actionsList = createActionsList(accountInfo, fetchedActions); + addCachedActions({ publicKey, tokenId }, actionsList, graphqlEndpoint); + + return actionsList; +} + +/** + * Given a graphQL response from #getActionsQuery, process the actions into a canonical actions list + */ +export function createActionsList( + accountInfo: ActionsQueryInputs, + fetchedActions: FetchedAction[] +) { + const _fetchedActions = fetchedActions; + const { publicKey, actionStates } = accountInfo; + let actionsList: { actions: string[][]; hash: string }[] = []; // correct for archive node sending one block too many if ( @@ -736,6 +757,10 @@ async function fetchActions( `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; + }); + // split actions by account update let actionsByAccountUpdate: string[][][] = []; let currentAccountUpdateId = 'none'; @@ -769,7 +794,6 @@ async function fetchActions( ); } }); - addCachedActions({ publicKey, tokenId }, actionsList, graphqlEndpoint); return actionsList; } diff --git a/src/lib/mina/fetch.unit-test.ts b/src/lib/mina/fetch.unit-test.ts index e61a7155a..3f0d662ce 100644 --- a/src/lib/mina/fetch.unit-test.ts +++ b/src/lib/mina/fetch.unit-test.ts @@ -1,3 +1,7 @@ +import { PrivateKey, TokenId } from 'o1js'; +import { createActionsList } from './fetch.js'; +import { mockFetchActionsResponse } from './fixtures/fetch-actions-response.js'; +import { test, describe } from 'node:test'; import { removeJsonQuotes } from './graphql.js'; import { expect } from 'expect'; @@ -118,3 +122,110 @@ actual = removeJsonQuotes(input); expect(actual).toEqual(expected); console.log('regex tests complete 🎉'); + +describe('Fetch', async (t) => { + describe('#createActionsList with default params', async (t) => { + const defaultPublicKey = PrivateKey.random().toPublicKey().toBase58(); + const defaultActionStates = { + fromActionState: undefined, + endActionState: undefined, + }; + const defaultAccountInfo = { + publicKey: defaultPublicKey, + actionStates: defaultActionStates, + 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', + }, + ]); + }); + }); +}); +``; diff --git a/src/lib/mina/fixtures/fetch-actions-response.ts b/src/lib/mina/fixtures/fetch-actions-response.ts new file mode 100644 index 000000000..3e280bbff --- /dev/null +++ b/src/lib/mina/fixtures/fetch-actions-response.ts @@ -0,0 +1,118 @@ +export { + mockFetchActionsResponse +} + +const mockFetchActionsResponse = +{ + "data": { + "actions": [ + { + "blockInfo": { + "distanceFromMaxBlockHeight": 11 + }, + "actionState": { + "actionStateOne": "25525130517416993227046681664758665799110129890808721833148757111140891481208", + "actionStateTwo": "25079927036070901246064867767436987657692091363973573142121686150614948079097" + }, + "actionData": [ + { + "accountUpdateId": "5", + "data": [ + "20503089751358270987184701275168489753952341816059774976784079526478451099801", + "1" + ] + }, + { + "accountUpdateId": "3", + "data": [ + "20374659537065244088703638031937922870146667362923279084491778322749365537089", + "1" + ] + } + ] + }, + { + "blockInfo": { + "distanceFromMaxBlockHeight": 5 + }, + "actionState": { + "actionStateOne": "290963518424616502946790040851348455652296009700336010663574777600482385855", + "actionStateTwo": "25525130517416993227046681664758665799110129890808721833148757111140891481208" + }, + "actionData": [ + { + "accountUpdateId": "7", + "data": [ + "3374074164183544078218789545772953663729921088152354292852793744356608231707", + "0" + ] + } + ] + }, + { + "blockInfo": { + "distanceFromMaxBlockHeight": 3 + }, + "actionState": { + "actionStateOne": "20673199655841577810393943638910551364027795297920791498278816237738641857371", + "actionStateTwo": "290963518424616502946790040851348455652296009700336010663574777600482385855" + }, + "actionData": [ + { + "accountUpdateId": "9", + "data": [ + "12630758077588166643924428865613845067150916064939816120404808842510620524633", + "1" + ] + } + ] + }, + { + "blockInfo": { + "distanceFromMaxBlockHeight": 2 + }, + "actionState": { + "actionStateOne": "10964420428484427410756859799314206378989718180435238943573393516522086219419", + "actionStateTwo": "20673199655841577810393943638910551364027795297920791498278816237738641857371" + }, + "actionData": [ + { + "accountUpdateId": "19", + "data": [ + "17137397755795687855356639427474789131368991089558570411893673365904353943290", + "1" + ] + }, + { + "accountUpdateId": "17", + "data": [ + "3378367318331499715304980508337843233019278703665446829424824679144818589558", + "1" + ] + }, + { + "accountUpdateId": "15", + "data": [ + "27263309408256888453299195755797013857604561285332380691270111409680109142128", + "1" + ] + }, + { + "accountUpdateId": "13", + "data": [ + "15789351988619560045401465240113496854401074115453702466673859303925517061263", + "0" + ] + }, + { + "accountUpdateId": "11", + "data": [ + "5643224648393140391519847064914429159616501351124129591669928700148350171602", + "0" + ] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/src/lib/mina/graphql.ts b/src/lib/mina/graphql.ts index 5d20a2c71..04e0340a6 100644 --- a/src/lib/mina/graphql.ts +++ b/src/lib/mina/graphql.ts @@ -9,6 +9,7 @@ export { type GenesisConstantsResponse, type FailureReasonResponse, type LastBlockQueryFailureCheckResponse, + type FetchedAction, type FetchedBlock, type TransactionStatus, type TransactionStatusQueryResponse, @@ -250,22 +251,24 @@ type EventQueryResponse = { }[]; }; -type ActionQueryResponse = { - actions: { - blockInfo: { - distanceFromMaxBlockHeight: number; - }; - actionState: { - actionStateOne: string; - actionStateTwo: string; - }; - actionData: { - accountUpdateId: string; - data: string[]; - }[]; +type FetchedAction = { + blockInfo: { + distanceFromMaxBlockHeight: number; + }; + actionState: { + actionStateOne: string; + actionStateTwo: string; + }; + actionData: { + accountUpdateId: string; + data: string[]; }[]; }; +type ActionQueryResponse = { + actions: FetchedAction[]; +}; + type EventActionFilterOptions = { to?: UInt32; from?: UInt32; diff --git a/src/lib/mina/mina.ts b/src/lib/mina/mina.ts index a30c290bc..2dc41d542 100644 --- a/src/lib/mina/mina.ts +++ b/src/lib/mina/mina.ts @@ -448,7 +448,7 @@ function Network( if (actions !== undefined) return actions; } throw Error( - `getActions: Could not find actions for the public key ${publicKey}` + `getActions: Could not find actions for the public key ${publicKey.toBase58()}` ); }, proofsEnabled: true,