Skip to main content

Read Multiple Universal States

Overview

Multiple reads let you compose several universal reads into a single ordered flow across chains and Web2 endpoints.

This allows you to submit a single user-signed transaction to Push Chain that requests state from Ethereum, Solana, HTTPS endpoints, or all three, and returns the results in the order you prepared them.

Prerequisite: Familiarize yourself with Read Universal State before reading this page.

Mental Model

  1. Prepare each read with prepareRead
  2. Execute all reads together with executeReads

Prepare Read

pushChainClient.universal.prepareRead(subject, {options}): Promise<PreparedRead>

Prepares a read without executing it. Returns a PreparedRead object that you pass to executeReads. Nothing is broadcast and nothing is paid until you execute.

const prepared = await pushChainClient.universal.prepareRead('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', {
chain: PushChain.CONSTANTS.CHAIN.ETHEREUM_SEPOLIA,
});

console.log(prepared.fees.total); // what this read will cost, in PC
info

PreparedRead is an intermediate object that you pass to executeReads. Most apps do not need to manually inspect or modify its fields.

These Arguments are mandatory

ArgumentsTypeDescription
subjectstringWhat to read: an account address for a balance, a contract address for a contract call or storage slot, or a URL for a Web2 read. Solana subjects are base58.
options.chainCHAINWhere to read from. Any supported EVM or Solana chain, or CHAIN.WEB2 for an HTTPS endpoint. Decides which query option applies; see the branches below. Pass at most one query option; with none, read returns the subject's native balance.
PushChain.CONSTANTS.CHAIN
PushChain.CONSTANTS.CHAIN.PUSH_TESTNETPushChain.CONSTANTS.CHAIN.PUSH_TESTNET_DONUTPushChain.CONSTANTS.CHAIN.ETHEREUM_SEPOLIAPushChain.CONSTANTS.CHAIN.BNB_TESTNETPushChain.CONSTANTS.CHAIN.BASE_SEPOLIAPushChain.CONSTANTS.CHAIN.ARBITRUM_SEPOLIAPushChain.CONSTANTS.CHAIN.SOLANA_DEVNET
:: Pass one of the following, based on options.chain
If options.chain is a Web3 chain [collapsed]
  options.tokenstringToken balance of subject. EVM: the ERC-20 contract address, read via balanceOf. Solana: the SPL mint address; the SDK derives the associated token account.
  options.abi | options.idlAbi | IdlEVM: the contract ABI, used to encode the call and decode value. Solana: the program's Anchor IDL; the subject account is decoded with the layout whose discriminator matches its data. Reads never execute an instruction.
  options.functionNamestringEVM: the function to call (required with abi). Solana: the account layout to decode; optional, since it is inferred from the discriminator. Both snake_case and camelCase are accepted and matched against the IDL.
  options.argsany[]EVM: positional function arguments. Solana: only when subject is the program id, the PDA seeds of the functionName account in IDL order; pubkeys accept base58 or 0x-hex 32-byte strings, integers take BigInt.
  options.storageSlotbigint | HexEVM only. One storage word from the subject contract. Not needed for Solana, as Solana has no storage slots.
If options.chain is a Web2 / HTTPS endpoint [collapsed]
  options.web2objectRequired. Describes the HTTPS request and which JSON fields to extract. Fields below.
    options.web2.extractArray<{ path, valueType, decimals? }>Required. 1 to 16 JSONPath entries. Results come back as an array in this order.
    options.web2.extract[].pathstringJSONPath into the response body, for example $.data.price.
    options.web2.extract[].valueType'uint256' | 'int256' | 'bool' | 'string' | 'bytes'How the extracted value is encoded on-chain.
    options.web2.extract[].decimalsnumberOptional, numeric types only. The value is multiplied by 10 to this power and truncated before encoding, so 1.2345 with decimals: 2 becomes 123.
    options.web2.method'GET' | 'POST'Optional. Defaults to GET.
    options.web2.headersRecord<string, string>Optional. Request headers. Written to a public event log forever, so never include secrets.
    options.web2.bodystring | Uint8ArrayOptional, POST only. A body on GET is rejected.
    options.web2.timeoutMsnumberOptional. Validator fetch timeout, default 5000, clamped by validators.
::
options.callback{ target, gasLimit, abi, functionName, args? }Optional. Routes the request through your own receiver contract instead of the registry; fields in Callback Arguments.
options.waitForCompletionbooleanDefault true. When false, resolves as soon as the request is confirmed on Push Chain, before validators vote.

Call wait() on the response to finish.
options.progressHook(progress: ProgressEvent) => voidCallback for progress updates through the read lifecycle.

prepareRead accepts the same subject and query options as Read Universal State. It does not take waitForCompletion, progressHook or advanced; those belong to Execute Reads.

Advanced Arguments
ArgumentsTypeDefaultDescription
options.blockNumberbigintOracle height minus minConfirmationsEVM only. Pins the read to a specific destination block. The oracle-observed height can lag the real head.
options.minConfirmationsnumber1EVM only. Confirmations the destination block must have before validators read it.
options.expiryBlocksbigint300nRequest lifetime in Push Chain blocks. An unfulfilled request expires and refunds the unused callback budget.
options.maxFeebigintSDK estimatedCap on the upfront payment (protocol fee plus callback budget).
options.refundTostringYour Push accountWhere unused callback budget is sent. Must accept native Push transfers; a contract without a payable receive() forfeits the refund.
options.advanced.pollingIntervalMsnumber2000Milliseconds between status polls. Minimum 500.
options.advanced.timeoutnumberDerived from expiry, at most 180000Maximum milliseconds this caller waits. Timing out does not cancel the request; resume it with trackRead. An explicit value overrides the cap.
options.advanced.enforceGasCheckbooleanfalsefalse warns and proceeds when the pre-flight balance check finds a shortfall. true throws before broadcasting.
Returns `PreparedRead` <object>
PropertyTypeDescription
queryKeystringLogical identity of the query, independent of pinning and payment.
chainCHAINDestination the read was prepared for.
specReadSpecThe validated on-chain request struct.
specTuplearrayPositional form of spec for encodeFunctionData.
encodedSpecstringabi.encode(spec) for hand-assembled calldata.
valuebigintmsg.value to send: protocol fee plus callback budget.
feesobjectprotocolFee, callbackBudget and total (equal to value).
callbackGasLimitbigintGas reserved for the result callback.
callbackobjectThe request entrypoint retained for executeReads.
resultShapeobjectHow the result is decoded, retained through execution.
preflightobjectOracle height, Push height, gas price and contract addresses used to build the spec.
warningsstring[]Advisories such as a sensitive header or a non-UEA refund recipient.
Live Playground: Inspect PreparedRead
VIRTUAL NODE IDE
Copy playground link
Copy code

Execute Reads

pushChainClient.universal.executeReads(preparedReads: PreparedRead[], options?: { waitForCompletion?, progressHook?, advanced? }): Promise<BatchReadResponse>

Executes an ordered array of prepared reads as one flow. Where the wallet supports atomic batching this is submitted as a single transaction; you sign once, and the SDK submits every read and collects every result.

Each prepared read becomes one read in the batch, and results come back in the same order.

const CHAIN = PushChain.CONSTANTS.CHAIN;

const ethBalance = await pushChainClient.universal.prepareRead(holder, { chain: CHAIN.ETHEREUM_SEPOLIA });
const solBalance = await pushChainClient.universal.prepareRead(solanaHolder, { chain: CHAIN.SOLANA_DEVNET });

// Live progress for submission and every read in the batch.
const batch = await pushChainClient.universal.executeReads([ethBalance, solBalance], {
progressHook: (event) => {
console.log('[' + event.id + '] ' + event.level + ' - ' + event.title);
},
});

const [eth, sol] = await batch.wait(); // results in prepared order

These Arguments are mandatory

ArgumentsTypeDescription
preparedReadsPreparedRead[]Reads returned by prepareRead. Results come back in this order. Without atomic batching in the wallet, they are submitted as sequential transactions.
options.waitForCompletionbooleanDefault true. When false, returns as soon as the requests are submitted, before validators vote.

Call wait() on the response to finish.
options.progressHook(progress: ProgressEvent) => voidCallback for batch and per-read progress events; see ProgressHook Type and Response.

In the API response, each executed read is reported in reads, in the order you prepared them.

Advanced Arguments
ArgumentsTypeDefaultDescription
options.blockNumberbigintOracle height minus minConfirmationsEVM only. Pins the read to a specific destination block. The oracle-observed height can lag the real head.
options.minConfirmationsnumber1EVM only. Confirmations the destination block must have before validators read it.
options.expiryBlocksbigint300nRequest lifetime in Push Chain blocks. An unfulfilled request expires and refunds the unused callback budget.
options.maxFeebigintSDK estimatedCap on the upfront payment (protocol fee plus callback budget).
options.refundTostringYour Push accountWhere unused callback budget is sent. Must accept native Push transfers; a contract without a payable receive() forfeits the refund.
options.advanced.pollingIntervalMsnumber2000Milliseconds between status polls. Minimum 500.
options.advanced.timeoutnumberDerived from expiry, at most 180000Maximum milliseconds this caller waits. Timing out does not cancel the request; resume it with trackRead. An explicit value overrides the cap.
options.advanced.enforceGasCheckbooleanfalsefalse warns and proceeds when the pre-flight balance check finds a shortfall. true throws before broadcasting.
ProgressHook Type and Response

Batch events wrap the single-read events documented in Read Universal State; each read in the batch also emits its own READ-TX-1xx events.

IDTitleMessageLevelResponse
READ-TX-001Batch Read InitiatedPreparing <count> reads across <chains>INFO{ count, chains }
READ-TX-002-01Starting Read #<n>/<total>Read <n> of <total> targets <chain>INFO{ n, total, chain }
READ-TX-002-99-99Read #<n>/<total> CompleteRead <n> of <total> settled as <requestId>INFO{ n, total, requestId }
READ-TX-999-01All Reads FulfilledAll <count> reads fulfilledSUCCESS{ count }
READ-TX-999-02Batch Reads FailedBatch failed at read <n> of <total>: <error>ERROR{ failedAt, total, error }
READ-TX-999-03Batch Reads TimeoutBatch timed out at read <n> of <total>ERROR{ failedAt, total, error: 'read timeout' }
Returns `BatchReadResponse` <object>
PropertyTypeDescription
txHashstringPrimary Push Chain transaction hash.
transactionHashesstring[]All request transactions in submission order when execution was not atomic.
readsUniversalReadResponse[]One response per prepared read, in input order.
countnumberNumber of reads submitted.
atomicbooleanWhether submission was all-or-nothing. It says nothing about validator fulfillment, which is per read.
waitfunctionWaits until every read is terminal and returns the responses in input order. Only a timeout throws.
Live Playground: Execute Multiple Reads in One Flow
VIRTUAL NODE IDE
Copy playground link
Copy code

More Examples

Read Ethereum, Solana and Web2 in One Signature

Read a Chainlink price on Ethereum Sepolia, a USDC balance on Solana Devnet and two fields from a public API, all requested with a single user signature and returned in order.

Live Playground: Cross-Chain Reads in One Batch
VIRTUAL NODE IDE
Copy playground link
Copy code

Key Considerations

  • Single signature: one transaction submits every read when the wallet supports atomic batching. Otherwise the reads go out as sequential transactions, listed in transactionHashes.
  • Fulfillment is per read: atomic only describes the submission. Validators fulfill each read on its own, so check value on every response.
  • Partial batches: with sequential submission, an earlier read can exist even if a later transaction fails. Check the hashes on the error before resubmitting, or you pay twice. A prepared read that fails revalidation must be prepared again.
  • Fees: each read carries its own fee and the batch costs their sum. Nothing is charged at prepare time.
  • Tracking: the progress hook streams batch events plus each read's own events. A timed-out wait cancels nothing; resume with Track Universal Read.

Next Steps