EVM Block Funnel
Block funnel is the most standard funnel type in Paima. It simply downloads the blocks from the RPC provider for the chain you are deploying to.
Configuration
Hardhat1:
type: evm-main
chainUri: 'http://localhost:8545'
chainId: 31337
chainCurrencyName: 'Test Hardhat Tokens'
chainCurrencySymbol: 'TEST'
chainCurrencyDecimals: 18
blockTime: 2
paimaL2ContractAddress: '0x5FbDB2315678afecb367f032d93F642f64180aa3'
Conceptually
Notably, block funnel will do the following:
- Get the latest block number using
eth_blockNumber
so we know how far we are from the tip, and cache it tolatestAvailableBlockNumber
- Fetch a group of
DEFAULT_FUNNEL_GROUP_SIZE
blocks (or less if we're already at the tip) - Fetch any dynamic primitive that needs to be registered
- Fetch all the block numbers needed in parallel using
eth_getBlockByNumber
- Fetch all the
PaimaGameInteraction
Solidity events for the block range usingeth_getLogs
- Fetch all the Primitives for the block range using
eth_getLogs
Here is a visual representation of the flow:
Fetching more than just logs
Note that all the primitives for this funnel are based on eth_getLogs
. That is to say, there is no way to access data outside of these events provided by the funnel itself. If you need this functionality, there are a way few ways that it can be achieved:
- Use
eth_call
in your state machine once your state transition function gets triggered by an event. You can use this to read from contract storage. Note: when doing this, be sure to specify the block height for the call to make sure it matches the same block height of your STF call, otherwise this will not be deterministic. Be careful if this event comes from a different chain than your main chain (see this issue for more)- Benefit: Conceptually simple
- Drawback:
eth_call
only support getting the state of a contract after all transactions in that block have been processed. This means that if multiple transactions interact with your dApp in the same block, the data may not longer match the state when the event happened.
- Use
debug_traceTransaction
in your state machine once your state transition function gets triggered by an event. When run inprestateTracer
mode, it can tell you the storage modified by a specific transaction. You can useeth_getStorageAt
to get the initial storage state of the contract, then repeatedly applydebug_traceTransaction
all the way until you et to the desired transaction hash.- Benefit: Gets you the exact state when for the transaction where the event was emitted
- Drawback: It's extremely hard to reason about the correctness of this approach (How do you simulate the transaction up to the exact point when the event was emitted if you need that? What if the event gets emitted twice in the same transaction? What if the event or contract occurs multiple times in the transaction (see: internal transactions)). It is also heavy, as it requires not only multiple RPC calls, but also because some of these RPC calls return large data payloads.
- (if you can modify the contract) simply modify your event to include any information you need
- Benefit: Conceptually simple
- Drawback: It may not be possible if your contract is already live. Additionally, logging more data increases the gas cost.
- (if you can't modify the contract) sse a separate tool like Shadow to simulate modifying the logs emitted by the contract
- Benefit: Allows you to modify the events to get the data you need, even if your contract is already live
- Drawback: Introduces a dependency on an external company