flashbots / mev-geth

Go implementation of MEV-Auction for Ethereum
GNU Lesser General Public License v3.0
784 stars 197 forks source link

New RPC endpoint: eth_balanceDeltaBundle #101

Open epheph opened 2 years ago

epheph commented 2 years ago

Rationale

We should find a better name for this, but the idea is that a bundle often changes token balances (ETH, ERC20, NFT's, etc) and while it's possible to "sandwich" your bundle with balanceOf calls for specific tokens you expect to transfer within a bundle, for a specific account, we could automatically figure out which tokens were moved. We should create an RPC endpoint that returns a report of all token balances that were modified as a result of this bundle running in simulation.

When conducting whitehat rescues, especially for a brand new rescue type, this is one of the most sensitive issues: your custom code worked enough to rescue assets, but has a bug that locks assets or transfers to the wrong place. Having bundle execution distilled to a set of balance changes would help prevent accidental fund loss by providing positive confirmation of token flow.

If this could be combined with Flashbots protect RPC endpoint, we could easily prevent certain behaviors from propagating through our RPC (like TokenOops).

Implementation

I think this would be a separate endpoint from callBundle, as that should be as fast as possible, and this implementation would require at LEAST two simulation passes and many extraneous storage lookups.

Would be fine to either use signed or unsigned transactions

libevm commented 2 years ago

It would be great to make this as generic as possible and not limit it to certain standards i.e. ERC20 / ERC721.

My API proposal would be something like:

method: 'eth_balanceDeltaBundle'

params: [{
    balanceDelta: [
        // This should return a uint256, for a future PR, we could add a "formatter" akin to the debug_trace*
        // JavaScript runtime environment
        { address: '0x0...', data: '0x0....' } 
    ],
    txs: [ ], // signed or unsigned txs, same as callBundle
    stateBlockNumber: 1,
    blockNumber: 1
}]

For now we can just assume all balances is uint256, and the response should be roughly:

result: {
    balanceDelta: [
        { delta: 100 }
    ],
    txs: [ ... ] // normal callBundle tx return
}

Let me know your thoughts

thegostep commented 2 years ago

For now we can just assume all balances is uint256

I like the simplicity of this api. By convention we can deem a null data field to return the ETH balance.

Care must be taken for the result to be an array of signed 256 bit integers.

thegostep commented 2 years ago

An even more generalized api would be a pre-flight and post-flight multicall. Though this is probably overkill.

Something like this:

method: 'eth_balanceDeltaBundle'

params: [{
    preCheck: [
        { address: '0x0...', data: '0x0....' } 
    ],
    postCheck: [
        { address: '0x0...', data: '0x0....' } 
    ],
    txs: [ ], // signed or unsigned txs, same as callBundle
    stateBlockNumber: 1,
    blockNumber: 1
}]

result: {
    preCheck: [
        { result: '0x0....' }
    ],
    postCheck: [
        { result: '0x0....' }
    ],
    txs: [ ... ] // normal callBundle tx return
}