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
bytes
if there are no address arguments.Can further decode non address arguments, if there is an address in them.
Example:
bytes
arguments can have abi encoded addresses in thembytes32
arguments 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