---
# System prepended metadata

title: 'Smart Contract [EPIC/948]'

---

# Smart Contract [EPIC/948]

https://app.clubhouse.io/manifoldfinance/story/948/smart-contract-optimizations-handling-error-cases-for-signing-bundle


app_url https://app.clubhouse.io/manifoldfinance/story/948
description Currently we use the following smart contracts to extract meaningful information from the Blockchain. The ones we are using are:

* `ManifoldCheckAndSend` | Signer: In charge of paying the miner the bribe if the expected balances for the affected accounts in a swap changes accordingly to what we expect.
* `BalanceFetcher` | Signer: In charge of fetching touched balances for swaps.
* `SushiswapLiquidity` | Web3Source : In charge of fetching liquidity information from SushiSwap.
* `AccountBalances` | Web3Source: In charge of fetching balances for our platform accounts.

After digging deeper in how to optimize better Solidity I came to the conclusion we can further optimize these smart contracts to consume less gas and to return more information per batch.

Below there's a list of documentation I've been reading in order to understand better these implications:

* [[Solidity] Compress input in smart contracts](https://medium.com/joyso/solidity-compress-input-in-smart-contract-d9a0eee30fe0)
* [[Solidity] Optimize Smart Contract Gas Usage](https://medium.com/joyso/solidity-save-gas-in-smart-contract-3d9f20626ea4)

Utils:

* [Solidity Optimize Name](https://emn178.github.io/solidity-optimize-name/)

https://app.clubhouse.io/manifoldfinance/story/948/smart-contract-optimizations-handling-error-cases-for-signing-bundle#activity-953


The basic gas for the transaction is `21,000`. The input data is `68 gas per byte`, and `4 gas` if the byte is `0x00`.

For example, gas is `68 * 4 = 272` if data is `0x0dbe671f`; It’s `68 * 1 + 4 * 3= 80` if `0x0000001f`.

Since all the parameters are `32 bytes`, the gas consumption is minimal when the parameter is zero. It will be `32 * 4 = 128`. The maximum value is as follows:

`n`: The number of bytes of the parameter
`n * 68 + ( 32 - n ) * 4`

For example, the maximum gas consumption of `bytes32` is `2,176`. Address is `20 bytes` so that it is `1,408`.

app_url https://app.clubhouse.io/manifoldfinance/story/948/smart-contract-optimizations-handling-error-cases-for-signing-bundle#activity-954

*How `eth_estimateGas` works?*

Whenever MetaMask is querying info for adjusting a given call how much gas is going to cost, it's using under the hood a call to `eth_estimateGas` RPC method.

This method is the same as `eth_call` but instead of returning the output of the execution returns the gas consumption.

We can see the implementation in Besu:

* [EthEstimateGas](https://github.com/VladLupashevskyi/besu/blob/master/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGas.java)
* [TransactionSimulator](https://github.com/hyperledger/besu/blob/master/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java)


#### How can we further optimize `eth_call`?

`eth_call` allows us to perform arbitrary calls to a given smart contract without modifying the state world.

In the case of Besu, it's using again the famous `TransactionSimulator`. We can see the following for [Gas Limit](https://github.com/hyperledger/besu/blob/1b45e225994a91de97551508dd20e69c438fde3a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java#L179):

```java
final Account sender = publicWorldState.get(senderAddress);
final long nonce = sender != null ? sender.getNonce() : 0L;
final Wei gasPrice = callParams.getGasPrice() != null ? callParams.getGasPrice() : Wei.ZERO;
final long gasLimit =
        callParams.getGasLimit() >= 0
            ? callParams.getGasLimit()
            : blockHeaderToProcess.getGasLimit();
```

As we can see, `gasLimit` param is a `Long` which in Java means we can pass the maximum allowed by the type `9,223,372,036,854,775,807`.

So, theoretically, we have room of margin to retrieve plenty of data.

In the case of Nethermind, we can see the following for the class [TransactionProcessor.cs](https://github.com/NethermindEth/nethermind/blob/8512fff799c222f18e669036eee9afdf95698905/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs#L150):

```cs
UInt256 gasPrice = transaction.CalculateEffectiveGasPrice(spec.IsEip1559Enabled, block.BaseFeePerGas);

long gasLimit = transaction.GasLimit;
byte[] machineCode = transaction.IsContractCreation ? transaction.Data : null;
byte[] data = transaction.IsMessageCall ? transaction.Data : Array.Empty<byte>();

Address? caller = transaction.SenderAddress;
if (_logger.IsTrace) _logger.Trace($"Executing tx {transaction.Hash}");
```

So, it's also a Long which is the same amount as in Java.

The only aspect I detected scrolling down the code is the following:

```cs
if (notSystemTransaction)
{
  if (gasLimit < intrinsicGas)
  {
    TraceLogInvalidTx(transaction, $"GAS_LIMIT_BELOW_INTRINSIC_GAS {gasLimit} < {intrinsicGas}");
    QuickFail(transaction, block, txTracer, eip658NotEnabled, "gas limit below intrinsic gas");
    return;
  }

  if (!restore && gasLimit > block.GasLimit - block.GasUsed)
  {
    TraceLogInvalidTx(transaction,
    $"BLOCK_GAS_LIMIT_EXCEEDED {gasLimit} > {block.GasLimit} - {block.GasUsed}");
     QuickFail(transaction, block, txTracer, eip658NotEnabled, "block gas limit exceeded");
    return;
  }
}
```

So in the worst case scenario we are limited by the block gas limit.
app_url https://app.clubhouse.io/manifoldfinance/story/948/smart-contract-optimizations-handling-error-cases-for-signing-bundle#activity-958

For balances here is an implementation from MyCrypto: https://github.com/MyCryptoHQ/eth-scan/blob/master/contracts/BalanceScanner.sol

### callData compression via `brotli`
here is a post describing using brotli as a compression tool for call data: https://github.com/rugpullindex/calldata-compression
app_url https://app.clubhouse.io/manifoldfinance/story/948/smart-contract-optimizations-handling-error-cases-for-signing-bundle#activity-961



### ManifoldCheckAndSend Contracts

For `ManifoldCheckAndSend`, currently we are checking for an a given swap the following:

* Swap path `[TokenA, TokenB]`
  * Check resulting balance of TokenA
  * Check resulting balance of TokenB
* Swap path of `[TokenA, TokenB, TokenC, TokenD]`
  * Check resulting balance of TokenA
  * Check resulting balance of TokenB

I think we can save one check per account by only checking the balance for the `amountOut`:

* Swap path `[TokenA, TokenB]`
  * Check resulting balance of TokenB
* Swap path of `[TokenA, TokenB, TokenC, TokenD]`
  * Check resulting balance of TokenB

Unless I'm missing something, we can save `n` checks where `n` is the amount of addresses to check.

The other consideration to have is the following:

![Clipboard 2021-02-08 at 1.15.32 PM.png](https://media.clubhouse.io/api/attachments/files/clubhouse-assets/60635444-7a0e-4e1f-bf60-2c056e623b51/6107d3d4-a230-4bcf-adb7-6cd0050204dc/Clipboard%202021-02-08%20at%201.15.32%20PM.png)

Our method won't work in the following circumstances:

* We are not the first in the bundle position (if we don't create a whole block with a big bundle, the miner will add more bundles).
* Before our turn, the previous bundles touches the same paths as us modifying the reserves and thus, invalidating our checks.

In the above screenshot we can see that that block is composed by three sub-bundles. So, using the above guidelines we can imagine the rest.

Currently I don't know an alternative way of paying the bribe to the miner without this downside.


per `megaBundles` see https://github.com/flashbots/mev-geth/pull/61 and https://hackmd.io/@flashbots/auction-v4-proposal
app_url https://app.clubhouse.io/manifoldfinance/story/948/smart-contract-optimizations-handling-error-cases-for-signing-bundle#activity-993


### Brian comments 

An astute observation and an interesting dilemma. 

I think we need to change tact. Instead of capturing absolute end balances for any touched account during simulation, instead we restrict the balance checking to just our platform trading account which is ultimately the only one we care about anyway. 

We can know at the beginning of any bundle what our current balances are. We also know from the way that trades are specified the min amount in or out that should occur against our token balances. 

From this we can estimate the net effect on our balances and perform a less precise balance check before sending the miner reward.

This will require changing the signer to perform a streaming join with the balances topic. I don't think this requires kafka streams level implementation. A simple enough in memory buffer should be enough, since the signer is only interested in latest state anyway. 

It also requires a bit more processing the signer to more closely examine the bundle and determine the balance changes. 

That being said, is this functionality that is best pushed back into the arb engine? It already has the token balances. It could publish in the balance the expected min balances we should have after executing the bundle and we just encode it.

This would have the added benefit of requiring the arb engine to state it's `end state` which we can verify with simulation and if we then get bad bundle merged we are protected from rewarding the miner. 

[@aldoborrero](clubhouse://members/6066d178-c6c5-44fa-b404-db6d12427f19) [@aldoborrero](clubhouse://members/6066d178-c6c5-44fa-b404-db6d12427f19) [@sam](clubhouse://members/60635444-88a5-458c-bd57-4fd75d309f19) thoughts?
app_url https://app.clubhouse.io/manifoldfinance/story/948/smart-contract-optimizations-handling-error-cases-for-signing-bundle#activity-999

### aldoborrero comments
I think it's not a bad idea.

If we only care about platform accounts then it makes sense to do it at arb level. As you mentioned we can know expected balances or at least that the min amount has happened. 

On the other side we save lot of gas as the bundle may contain a big number of swaps and we remove the possibility of collisions by touching same paths.


Optimize and sort method call by importance (using Solidity Optimize Name tool)

Optimize and compress method call input data (see comment below for a better description)

Pre-compute all calls to `abi.encodeWithSignature` to use`abi.encodeWithSelector` instead

description For `BalanceCheckerMultiCall` and `SushiswapLiquidityMultiCall` deploy optimized versions of those contracts to mainnet and refactor `AccountBalancesMultiCall` and `SushiswapLiquidityMultiCall` to use pre-deployed versions.

For `AccountBalancesMultiCall` and `SushiswapLiquidityMultiCall` tweak call parameters to have higher gas limit.
