BoringVault Protocol Integration
In order for a ManagerWithMerkleVerification to manage a BoringVault, it needs a DecoderAndSanitizer for every protocol the BoringVault will interact with. The job of the DecoderAndSanitizer is to implement the function selector for every function the BoringVault will need to call. When a function is called, the DecoderAndSanitizer decodes the arguments and possibly sanitize them to return bytes containing all the addresses found in the msg.data in an abi.encodePacked format. The ManagerWithMerkleVerification, then takes this bytes and uses it to verify a Merkle proof.
Below is a tutorial for how to create the DecoderAndSanitizer for a Uniswap V3 Integration. The fully implemented DecoderAndSanitizer can be found here.
Step 1: Determine what functions need to be callable by the BoringVault
For a Uniswap V3 Integration, the boring vault should be able to
Swap
Create new positions
Add liquidity to existing positions
Remove liquidity from positions
Collect fees from positions
Thus the UniswapV3DecoderAndSanitizer must implement the following functions.
BoringVault Action
Function to implement
Swap with Uniswap V3
exactInput(DecoderCustomTypes.ExactInputParams calldata params)
Create an Uniswap V3 liquidity position
mint(DecoderCustomTypes.MintParams calldata params)
Add to an Uniswap V3 liquidity position
increaseLiquidity(DecoderCustomTypes.IncreaseLiquidityParams calldata params)
Take from an Uniswap V3 liquidity position
decreaseLiquidity(DecoderCustomTypes.DecreaseLiquidityParams calldata params)
Collect from an Uniswap V3 liquidity position
collect(DecoderCustomTypes.CollectParams calldata params)
Step 2: Implementing the functions
All DecoderAndSanitizer function implementations will follow these specifications.
The implemented function selector must match the underlying protocols function selector.
The implemented function must follow the form.
All DecoderAndSanitizer function implementations may follow these specifications.
Can revert if a sanitation check fails.
Can return an empty
bytesif there are no address arguments.Can further decode non address arguments, if there is an address in them.
Example:
bytesarguments can have abi encoded addresses in thembytes32arguments could be used to derive an address, as is the case with balancer pool ids
The function body itself should extract all addresses from the input arguments and sanitize any arguments that need it.
💡 Sanitizing an argument means to revert if a specific argument input should not be allowed. For instance, the MorphoBlueDecoderAndSanitizer will revert if the bytes calldata data argument has a non zero length, as BoringVaults do not implement the required MorphoBlue callback functions.
Implementing exactInput results in the following code.
💡 The params.path argument contains an abi encode packed sequence of (TOKEN_0_ADDRESS, FEE_0, TOKEN_1_ADRESS, FEE_1,…… TOKEN_N_ADDRESS) the implementation must iterate through this data, and extract every token address in it.
Implementing mint results in the following code.
Implementing increaseLiquidity results in the following code.
💡 The params.tokenId argument must be sanitized to check that the manager is not trying to add liquidity to a token id not owned by the BoringVault.
💡 We add uniswapV3NonFungiblePositionManager as an immutable constructor value for this DecoderAndSanitizer, so we can run ownerOf checks.
Implementing decreaseLiquidity results in the following code.
💡 The params.tokenId argument is sanitized to check that the manager is not trying to remove liquidity from a token id not owned by the BoringVault. This not a strict security requirement, rather we do it just to close scope.
Implementing collect results in the following code.
💡 The params.tokenId argument is sanitized to check that the manager is not trying to collect from a token id not owned by the BoringVault. This not a strict security requirement, rather we do it just to close scope.
Step 3: Integrating UniswapV3DecoderAndSanitizer into a BoringVault specific DecoderAndSanitizer
💡 Combining the several DecodersAndSanitizers into a single DecodersAndSanitizer is not required. Rather, it is a gas optimization so manage calls make less calls to cold addresses. It is perfectly acceptable to have multiple different DecodersAndSanitizers contracts.
It is possible for other protocol DecoderAndSanitizers to implement the exact same function selectors, which causes compiler errors. This should be addresses in the BoringVault specific DecoderAndSanitizer.
Example The BalancerV2, ERC4626, and Curve DecoderAndSanitizers all implement the deposit(uint256,address) function. Since they all use the exact same function body, it can be safely overridden.
Last updated