|
| 1 | +## Motivation |
| 2 | + |
| 3 | +Many projects may rely on state which already exists on the chain. |
| 4 | +This might be difficult to replicate on a local testing environment |
| 5 | +(this might include deploying multiple contracts, performing many transactions etc.). |
| 6 | +In order to make this easier, some frameworks from ethereum (like foundry) introduce "state forking" (see [foundry docs](https://book.getfoundry.sh/forge/fork-testing)). |
| 7 | + |
| 8 | +## State forking in foundry |
| 9 | + |
| 10 | +Original implementation allows for 2 ways of forking the state. |
| 11 | + |
| 12 | +Both of the approaches require the user to specify the source (RPC endpoint) and optionally - a block number we want to fork state from. |
| 13 | + |
| 14 | +### 1. Forking the state once for all tests |
| 15 | +This is a simpler way of forking, which requires only to pass some arguments to the forge CLI, |
| 16 | +and state is forked for the whole suite. |
| 17 | + |
| 18 | +### 2. Switching forks dynamically in tests, using cheatcodes |
| 19 | +Effectively, the state in tests will be switched after creating and selecting the fork. |
| 20 | +This allows a per-case configuration which is more flexible and allows for writing more specific tests. |
| 21 | +This also enables testing more advanced scenarios like testing RPC adapters, cross-chain swaps etc. |
| 22 | + |
| 23 | +## Considered solutions |
| 24 | + |
| 25 | +For the state to be considered as "forked", we have to swap the storage object |
| 26 | +to a one which reads/writes on data from the fork. This can be done in two ways: |
| 27 | + |
| 28 | +- Pulling the state from the fork eagerly (use a fork DB dump as storage, clone and use it for reading & writing) |
| 29 | +- Pulling the state from the fork lazily (read from fork on read request, cache locally, write state diffs to local cache) |
| 30 | + |
| 31 | +The latter is preferred as the first is too heavy, inconsistent between node implementations, and generally |
| 32 | +unfeasible for fast testing. |
| 33 | + |
| 34 | +## Architecture |
| 35 | + |
| 36 | +All things considered it's worth to implement the data fetching mechanism properly from the get-go, |
| 37 | +since it's going to be usable in both forking approaches. |
| 38 | + |
| 39 | +### State-forking architecture |
| 40 | + |
| 41 | + |
| 42 | + |
| 43 | +### Reading data from the fork |
| 44 | + |
| 45 | + |
| 46 | + |
| 47 | + |
| 48 | +## Next steps |
| 49 | + |
| 50 | +In order to make things simple and easy to review, we should approach implementing this iteratively, |
| 51 | +while considering future expansion. |
| 52 | + |
| 53 | +### MVP |
| 54 | +1. Implement fetching data (contracts, state, classes) utilizing a single worker utilizing `ForkDatabase` architecture |
| 55 | +2. Implement executing transaction on top of the forked state (should be able to modify the local state only) |
| 56 | +3. Implement user interface, enabling the forking in the tests |
| 57 | + |
| 58 | +### Further down the line |
| 59 | +4. Implement storing worker cache on disk, loading and clearing it |
| 60 | +5. Implement multifork, fetching utilizing multiple workers + controlling cheatcodes |
0 commit comments