Skip to content

Commit

Permalink
Merge pull request #141 from balancer/update-hook
Browse files Browse the repository at this point in the history
Update hook example
  • Loading branch information
mkflow27 authored Sep 30, 2024
2 parents 7ddb079 + f0a1c65 commit 5272722
Showing 1 changed file with 33 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,39 @@ A hooks contract should inherit the [BaseHooks.sol](https://github.com/balancer/
* **Base implementation**: A complete implementation of the [IHooks.sol](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/IHooks.sol) interface, with each implemented function returning false.
* **Configuration**: A virtual function `getHookFlags` that must be implemented by your hooks contract, defining which hooks your contract supports.

Below, we present a naive implementation of a swap-fee discount hook contract giving any veBAL holder a reduced swap fee:
Below, we present a naive implementation of a swap-fee discount hook contract giving any veBAL holder a reduced swap fee. Hooks should also inherit from `VaultGuard`, which stores a reference to the Vault and provides the `onlyVault` modifier. This is required for `onRegister` and hook overrides, to ensure they cannot be called except by the Vault.

```solidity
contract VeBALFeeDiscountHook is BaseHooks {
// only pools from the allowedFactory are able to register and use this hook
contract VeBALFeeDiscountHook is BaseHooks, VaultGuard {
// Only pools from a specific factory are able to register and use this hook.
address private immutable _allowedFactory;
// only calls from a trusted routers are allowed to call this hook, because the hook relies on the getSender
// implementation to work properly
// Only trusted routers are allowed to call this hook, because the hook relies on the `getSender` implementation
// implementation to work properly.
address private immutable _trustedRouter;
// The gauge token received from staking the 80/20 BAL/WETH pool token.
IERC20 private immutable _veBAL;
constructor(IVault vault, address allowedFactory, address veBAL, address trustedRouter) BaseHooks(vault) {
/**
* @notice A new `VeBALFeeDiscountHookExample` contract has been registered successfully.
* @dev If the registration fails the call will revert, so there will be no event.
* @param hooksContract This contract
* @param factory The factory (must be the allowed factory, or the call will revert)
* @param pool The pool on which the hook was registered
*/
event VeBALFeeDiscountHookExampleRegistered(
address indexed hooksContract,
address indexed factory,
address indexed pool
);
constructor(IVault vault, address allowedFactory, address veBAL, address trustedRouter) VaultGuard(vault) {
_allowedFactory = allowedFactory;
_trustedRouter = trustedRouter;
_veBAL = IERC20(veBAL);
}
/// @inheritdoc IHooks
function getHookFlags() external pure override returns (IHooks.HookFlags memory hookFlags) {
function getHookFlags() public pure override returns (IHooks.HookFlags memory hookFlags) {
hookFlags.shouldCallComputeDynamicSwapFee = true;
}
Expand All @@ -47,10 +61,14 @@ contract VeBALFeeDiscountHook is BaseHooks {
address pool,
TokenConfig[] memory,
LiquidityManagement calldata
) external view override returns (bool) {
) public override onlyVault returns (bool) {
// This hook implements a restrictive approach, where we check if the factory is an allowed factory and if
// the pool was created by the allowed factory. Since we only use onComputeDynamicSwapFeePercentage, this might
// be an overkill in real applications because the pool math doesn't play a role in the discount calculation.
// the pool was created by the allowed factory. Since we only use onComputeDynamicSwapFeePercentage, this
// might be an overkill in real applications because the pool math doesn't play a role in the discount
// calculation.
emit VeBALFeeDiscountHookExampleRegistered(address(this), factory, pool);
return factory == _allowedFactory && IBasePoolFactory(factory).isPoolFromFactory(pool);
}
Expand All @@ -59,7 +77,7 @@ contract VeBALFeeDiscountHook is BaseHooks {
PoolSwapParams calldata params,
address pool,
uint256 staticSwapFeePercentage
) external view override returns (bool, uint256) {
) public view override onlyVault returns (bool, uint256) {
// If the router is not trusted, does not apply the veBAL discount because getSender() may be manipulated by a
// malicious router.
if (params.router != _trustedRouter) {
Expand Down Expand Up @@ -98,13 +116,16 @@ function onRegister(
TokenConfig[] memory,
LiquidityManagement calldata
) external view override returns (bool) {
emit VeBALFeeDiscountHookExampleRegistered(address(this), factory, pool);
return factory == _allowedFactory && IBasePoolFactory(factory).isPoolFromFactory(pool);
}
```

The `onRegister` function enables developers to implement custom validation logic to ensure the registration is valid. When a new pool is registered, a hook address can be provided to "link" the pool and the hook. At this stage, the `onRegister` function is invoked by the Vault, and it must return true for the registration to be successful. If the validation fails, the function should return false, preventing the registration from being completed.

In this example we validate that the `factory` param forwarded from the Vault matches the `allowedFactory` set during the hook deployment, and that the pool was deployed by that factory.
In this example we validate that the `factory` param forwarded from the Vault matches the `allowedFactory` set during the hook deployment, and that the pool was deployed by that factory. If successful, it emits an event for tracking by offchain processes.

### Implementing the Swap Fee Logic

Expand Down

0 comments on commit 5272722

Please sign in to comment.