Contract Upgrades

Contracts Registry

Systems composed of multiple contracts may rely on a central contracts registry. Whenever contract A needs to interact with B, it first queries the registry to obtain the address of B. By having a mutable registry, an admin can just replace B with an alternative implementation B’, changing its behaviour.

Delegate Calls

In a regular CALL from a contract A to a contract B, contract A sends a data payload to B. Contract B executes its code in response to this payload, potentially reading or writing from its own storage, and returns a response to A. While B executes its code, it can access information on the call itself, such as the msg.sender, which is set to A.

However, on a DELEGATECALL, while the code executed is that of contract B, execution happens in the context of contract A. This means that any reads or writes to storage affect the storage of A, not B. Also, msg.sender is set to the address who had called A in the first place. All in all, this opcode allows a contract to execute code from another contract as if it were calling an internal function. This is what powers Solidity external libraries under the hood.

Transparent Proxy

In the Transparent Proxy pattern, the upgrade logic resides in the Proxy contract - meaning upgrade is handled by Proxy. A function like upgradeTo(address newImpl) must be called to upgrade to a new implementation contract. However, since this logic resides at Proxy, it is expensive to deploy these kind of proxies.

Transparent proxies also require that admin mechanisms to determine whether to delegate the call to implementation or execute a Proxy contract's function.

How Proxies Work In Taker

The fundamental idea here is having a Proxy contract. A bit of contextual terminology here before moving on:

Proxy contract - A contract that acts as a proxy, delegating all calls to the contract it is the proxy for.

TakerAddressesProvider contract is the proxy contract.

Implementation contract - The contract that you want to upgrade or patch. This is the contract that Proxy contract will be acting as a proxy for.

LendingPool/LendingPoolConfigurator/TakerOracleGetter is the implementation contract.

The Proxy contract stores the address the implementation contract or logic layer, as a state variable. Unlike normal contracts, the user doesn't actually send calls directly to the logic layer - which is your original contract. Instead, all calls go through the proxy and this proxy delegates the calls to this logic layer - the implementation contract at the address that proxy has stored, returning any data it received from logic layer to the caller or reverting for errors.

Contracts Registry

Systems composed of multiple contracts may rely on a central contracts registry. Whenever contract A needs to interact with B, it first queries the registry to obtain the address of B. By having a mutable registry, an admin can just replace B with an alternative implementation B’, changing its behaviour.

Last updated