# \[BIP-140\] Introduce TimelockAuthorizer

**URL:** <https://forum.balancer.fi/t/bip-140-introduce-timelockauthorizer/4159>\
**Category:** Governance\
**Created:** [December 7, 2022, 3:55pm UTC](https://forum.balancer.fi/t/bip-140-introduce-timelockauthorizer/4159 "2022-12-07T15:55:21Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![nventuro](https://yyz1.discourse-cdn.com/flex027/user_avatar/forum.balancer.fi/nventuro/32/49_2.png) [@nventuro](https://forum.balancer.fi/u/nventuro)\
**Post date:** [December 7, 2022, 3:55pm UTC](https://forum.balancer.fi/t/bip-140-introduce-timelockauthorizer/4159/1 "2022-12-07T15:55:21Z")

</div>

# **Introduction**

Balancer’s access control system is based on permissions, which can be granted and revoked. These are controlled by the `Authorizer` contract, which was deployed along with the `Vault` in April 2021.

This proposal aims to replace it with the `TimelockAuthorizer`, which provides more fine-grained control over permissions, as well as new security features. It additionally addresses a bug in the `AuthorizerAdaptor` contract that was disclosed via the Immunefi bug bounty program.

For simplicity, this migration will initially only take place on the main Ethereum network and none of the L2s or sidechains.

# **Specification**

The `TimelockAuthorizer` contract (developed by Balancer Labs) is a drop-in replacement of the current `Authorizer` contract, enabling new use cases and providing security improvements.

I encourage everyone to read more about each of these improvements, as they will **greatly impact the operation of the Balancer DAO** if this proposal where to pass (particularly the ‘Delays’ section).

## **Granular Permissions**

As of today, permissions are global: an account with a permission can use it on any contract in the entire network. For singleton contracts (like the `Vault` or the `ProtocolFeeWithdrawer`) this is not an issue since there is only one of them. However, for contracts with multiple instances (like liquidity pools) this means that an account with e.g. permission to set swap fees can use that permission on _any_ pool of the same type (i.e. that is created in the same factory).

`TimelockAuthorizer` allows granting permissions over a single contract, meaning it will e.g. be possible to allow an account to manage a single pool’s swap fees while not being able to do the same on any other pool. This has always been supported on the original `IAuthorizer` interface - the current `Authorizer` simply did not implement this capability (for simplicity).

It will still be possible to grant global permissions by passing the special sentinel address known as `EVERYWHERE` (equal to `0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF`).

## **Granters and Revokers**

The current `Authorizer` features a single all-powerful role (`0x00..00`), currently held by the Balancer DAO Governance Multisig (`0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f`). Accounts with this role can grant and revoke any permission - but they are the only one that can do so.

`TimelockAuthorizer` instead distinguishes between having a permission and being a _granter_ or a _revoker_ for a permission. This will increase the capacity of the the DAO Governance Multisig to delegate subtasks to other accounts. For example, instead of simply granting permission to trigger the Liquidity Mining BAL bridging process to the Balancer Labs Operations Multisig, it could make said multisig a _granter_, increasing flexibility (e.g. letting Balancer Labs develop contracts that trigger this process automatically and grant them permission to operate without having to involve the Governance Multisig).

The `TimelockAuthorizer` also has a _root_ account, initially set to the Balancer DAO Governance Multisig, which always has permission to grant and revoke all permissions, and to create and destroy granters and revokers. These permissions cannot be revoked by anyone (except by transferring _root_ to a different account), meaning it always retains full control over the system and can safely evict any rogue granters or revokers.

## **Delays**

Arguably the most important feature of the `TimelockAuthorizer` is the capacity to assign _minimum delays_ to certain actions. If an action has a delay, then accounts with that permission cannot immediately execute said action: they can only _schedule_ an execution at some time in the future (greater than the action’s delay).

Critically, these scheduled executions can be cancelled, which lets the community detect and stop malicious or erroneous actions after they are committed on-chain but **before they are actually executed** , greatly decreasing the risk of user error, phishing attacks, etc. This also provides users who are not happy with a future action (e.g. increased fees) with the opportunity to exit the system before those measures take place.

It is also possible to assign **delays to the granting and revoking** of permissions. By e.g. delaying granting and revoking the permission to mint BAL (currently only held by the automated Liquidity Mining system), we end up with a much stronger committment to said system, and greatly reduce the risk of e.g. a malicious actor somehow acquiring minting capabilities. Similar measures can be applied to other powerful permissions, such as relayer privilege, adding gauge factories to the `GaugeAdder`, etc.

In general, if a multisig wields a permission (e.g. adding gauges) then its _execution_ should have a delay (to review the multisig’s action). Instead, if a contract wields a permission (e.g. the `GaugeAdder`) then its _granting_ should be delayed (to review the code of the contract that will use it).

For more information on how delays relate to security, see [my talk at the DeFi Security Summit](https://www.youtube.com/watch?v=LRTTplIdxfI).

## **Authorizer Adaptor Entrypoint**

A team of independent security researchers submitted a vulnerability disclosure via the [Immunefi bug bounty program](https://immunefi.com/bounty/balancer/) that relates to the operation of the `AuthorizerAdaptor`. Said issue was promptly mitigated and resolved, but operation of some components of the system (particularly adding reward tokens and killing gauges) has been degraded as a result. The `TimelockAuthorizer` includes a fix for this in the form of the `AuthorizerAdaptorEntrypoint`, which provides a secure way to restore regular operation.

# **Migration Procedure**

To ease the transition to this new system, Balancer Labs has also developed the `TimelockAuthorizerMigrator` contract, which prepares the `TimelockAuthorizer` by granting some of the permissions that exist in the current `Authorizer`, as well as setting up some initial delays.

In order to complete the migration, the migrator needs permission to call `setAuthorizer` in the `Vault`, which can only be granted by the Balancer DAO Governance Multisig. Additionally, the Multisig must also claim the _root_ role in the `TimelockAuthorizer`, completing the _root_ transfer from the migrator to the Multisig. This **cannot be done before January 5th, 2023** , since the root transfer has a minimum delay of 30 days.

## Execution Details

From the Balancer DAO Governance Multisig (`0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f`):

- Call `grantRole` on the `Authorizer` (`0xA331D84eC860Bf466b4CdCcFb4aC09a1B43F3aE6`) with:

- `role`: `0x1cbb503dcc0f4acaedf71a098211ff8b15a220fc26a6974a8d9deaab040fa6e0`

- `account`: `0xf8ee6f1F9B54F9b2C192D703ea2d22112cBC062b`

- Call `claimRoot` on the `TimelockAuthorizer` (`0x9E3cD0606Db55ac68845bB60121847823712ae05`)

The `role` argument corresponds to [the permission to call `setAuthorizer` on the `Vault`](https://github.com/balancer-labs/balancer-v2-monorepo/blob/master/pkg/deployments/action-ids/mainnet/action-ids.json#L30), and `account` is the [`TimelockAuthorizerMigrator`](https://github.com/balancer-labs/balancer-v2-monorepo/blob/master/pkg/deployments/tasks/20221202-timelock-authorizer/output/mainnet.json#L2).

The `TimelockAuthorizer` address can also be found [in its deployment](https://github.com/balancer-labs/balancer-v2-monorepo/blob/master/pkg/deployments/tasks/20221202-timelock-authorizer/output/mainnet.json#L4).

**Recall that `claimRoot` cannot be called until January 5th, 2023.**

Once these two steps are done, any account in the network can call `finalizeMigration` on the `TimelockAuthorizerMigrator` (`0xf8ee6f1F9B54F9b2C192D703ea2d22112cBC062b`) to complete the process.

# **Initial Configuration**

**No new permissions will be granted**. The Migrator contract ensures that all permissions granted during the migration exist in the current `Authorizer`. Some of the current permissions have been left out however, since they no longer apply (e.g. permission to pause the `Vault`, which can no longer be paused as of July 2021).

Any permissions granted after the deployment of the `TimelockAuthorizer` (Nov 30th) **are not automatically migrated** , and will have to be re-granted after the migration.

## **Initial Delays**

The following actions have initial delays associated to them. As a rule of thumb, I propose that we assign delays to actions with high impact and that do not require urgent execution.

### **Execution**

- `Vault.setAuthorizer`, 30 days. All delays must be shorter or equal to this one, as they could be otherwise bypassed by simply changing the Authorizer and removing delays entirely.
- `SmartWalletChecker.allowlistAddress`, 7 days. Once veBAL is locked this cannot be undone in any way, so extra care should be taken when adding contracts to the allowlist.
- `VotingEscrowDelegationProxy.setDelegation`, 14 days. veBAL boost upgrades are infrequent and should be handled with care, since they can greatly affect the veBAL operation.
- `GaugeAdder.addGaugeFactory`, 14 days. Adding new gauge factories is a critical action as it grants them permission to mint BAL. See [my talk at ETHLatam (in Spanish)](https://www.youtube.com/watch?v=1Y5IDfJQGVk) for more details.

### **Granting**

- `BalancerTokenAdmin.mint`, 30 days. This prevents other accounts from being able to mint BAL.
- `ProtocolFeesCollector.withdrawCollectedFees`, 30 days. This prevents bypassing the `ProtocolFeesWithdrawer`.
- `GaugeController.addGauge`, 14 days. This prevents bypassing the `GaugeAadder`.
- `BALTokenHolder.withdrawFunds`, 7 days. This manages the BAL minted for the veBAL gauge.
- Vault relayer permissions, 7 days. Relayers are quite powerful, so creating them should be carefully reviewed.

The creation of the full list of permissions and their delays can be inspected in [this script](https://github.com/balancer-labs/balancer-v2-monorepo/blob/master/pkg/deployments/tasks/20221202-timelock-authorizer/input/mainnet.ts).

# **Risk**

The `TimelockAuthorizer` is a relatively complex contract, which will be used to manage all permissions on the Balancer ecosystem, including the Liquidity Mining program and management of Protocol Fees. As such, care should be taken to review it.

The Balancer Labs team has been working on this contract for around 8 months, and we are confident in its correctness. It has been [audited by ABDK](https://github.com/balancer-labs/balancer-v2-monorepo/blob/master/audits/abdk/2022-04-15.pdf), and we’re currently working with Certora to conduct a second review.

---

<div class="post-metadata">

**Author:** ![John\_TV\_Locke](https://yyz1.discourse-cdn.com/flex027/user_avatar/forum.balancer.fi/john_tv_locke/32/1414_2.png) [@John\_TV\_Locke](https://forum.balancer.fi/u/John_TV_Locke)\
**Post date:** [December 19, 2022, 9:55pm UTC](https://forum.balancer.fi/t/bip-140-introduce-timelockauthorizer/4159/2 "2022-12-19T21:55:53Z")

</div>

sir, was this implemented?

thank you

---

<div class="post-metadata">

**Author:** ![solarcurve](https://yyz1.discourse-cdn.com/flex027/user_avatar/forum.balancer.fi/solarcurve/32/1487_2.png) [@solarcurve](https://forum.balancer.fi/u/solarcurve)\
**Post date:** [December 19, 2022, 9:59pm UTC](https://forum.balancer.fi/t/bip-140-introduce-timelockauthorizer/4159/3 "2022-12-19T21:59:09Z")

</div>

we’ll vote on it this week. veBAL voters don’t take holidays

---

<div class="post-metadata">

**Author:** ![Xeonus](https://yyz1.discourse-cdn.com/flex027/user_avatar/forum.balancer.fi/xeonus/32/3621_2.png) [@Xeonus](https://forum.balancer.fi/u/Xeonus)\
**Post date:** [December 22, 2022, 6:59am UTC](https://forum.balancer.fi/t/bip-140-introduce-timelockauthorizer/4159/4 "2022-12-22T06:59:52Z")

</div>

Vote is pending: [**https://snapshot.org/#/balancer.eth/proposal/0x46b48f89247d28fc26a3851269ca30424b7cfb1dede75b99b77e6a27b998dd19**](https://snapshot.org/#/balancer.eth/proposal/0x46b48f89247d28fc26a3851269ca30424b7cfb1dede75b99b77e6a27b998dd19)

---

<div class="post-metadata">

**Author:** ![fwabbin](https://avatars.discourse-cdn.com/v4/letter/f/91b2a8/32.png) [@fwabbin](https://forum.balancer.fi/u/fwabbin)\
**Post date:** [January 23, 2024, 7:25am UTC](https://forum.balancer.fi/t/bip-140-introduce-timelockauthorizer/4159/5 "2024-01-23T07:25:27Z")

</div>

@nventuro Might be a bit off-topic but didn’t know where else to ask. I noticed that fine-grained authorization is granted for approved relayers by the DAO, and that normal users then add those relayers as approved delegators if they want to use them.

One question I have - is there a reason for not giving further access control at the user level? E.g. user A granting permission for relayer B to call a specific function (e.g. addLiquidity), but only that function.

This would basically allow users to delegate certain functionality to contracts or SAFEs or even relayers, but with further permissions restrictions, similar to how when you approve an app to do a certain set of functionalities (e.g. grant Google access to read your contact list).

My overarching question here is why not extend the flexibility further so users themselves can restrict the permissions of their relayers?

---

<div class="post-metadata">

**Author:** ![nventuro](https://yyz1.discourse-cdn.com/flex027/user_avatar/forum.balancer.fi/nventuro/32/49_2.png) [@nventuro](https://forum.balancer.fi/u/nventuro)\
**Post date:** [January 23, 2024, 5:47pm UTC](https://forum.balancer.fi/t/bip-140-introduce-timelockauthorizer/4159/6 "2024-01-23T17:47:57Z")

</div>

I don’t think we ever considered such a model, mostly because it would’ve made the code and UX more complicated for little benefit. Each user would need to select which actions they want to enable for their relayer, which would’ve meant tracking and checking each of those individually in the contract (on top of the global permissions!).

Rather the thinking was to design relayers in such a way that it wouldn’t be a security concern to approve the relayer, since it’d have the same security implications as regular Vault functions. In your example, there’s no notion of ‘approving’ the Safe to only call the Vault’s `swap` function but not `joinPool`. Similarly, once you approve relayers then all of their functionality is equally safe.

(the reason why you actually need to approve relayers is to prevent rogue actors or compromised keys from registering malicious relayers which would then be able to steal funds - because each user individually opts in such an actor would need to _also_ trick users into performing this action).

---

<div class="post-metadata">

**Author:** ![fwabbin](https://avatars.discourse-cdn.com/v4/letter/f/91b2a8/32.png) [@fwabbin](https://forum.balancer.fi/u/fwabbin)\
**Post date:** [January 23, 2024, 7:23pm UTC](https://forum.balancer.fi/t/bip-140-introduce-timelockauthorizer/4159/7 "2024-01-23T19:23:43Z")

</div>

Thanks for shedding the insight @nventuro !

As I understand, the current design works where authorizers have granular permissions on what relayers can call what functions:

// actionId → account → where → isGranted  
mapping(bytes32 =\> mapping(address =\> mapping(address =\> bool))) private \_isPermissionGranted;

And then users can approve relayers through the following:

// user → relayer → isGranted  
mapping(address =\> mapping(address =\> bool)) private \_approvedRelayers;

What if I wanted to approve to, say, another user, a SAFE, or even a multisig, to perform a swap but ONLY a swap, on my behalf? Wouldn’t extending the second mapping to something like:

// user → actionId → relayer → isGranted  
mapping(address =\> mapping(actionId =\> mapping(address =\> bool))) private \_approvedRelayers;

Help enable this behavior? And by default users could simply approve the whole relayer / EOA / SAFE / user by just doing \_approvedRelayers[user][\*][relayer][true];

In which case, the first mapping focuses on core, approved relayers (for service to service communication), and the latter focuses on user approvals of relayers or any other external entity that may or may not have been granted access for internal protocol authorization.

Is the idea that the benefits of more granular authorization does not outweigh the UX friction this adds?

And if so, how can users currently delegate to other entities (EOAs / multisigs / relayers) to peform certain actions without protocol approval, if these are entities they trust?

Thanks!
