How Diamond Upgrades Work
Published: 2021-05-14 04:20:19

To understand diamond upgrades, let's first understand how function calls work in contracts created by Solidity.

## How Function Calls Work

In order for a transaction to execute a function on a smart contract there needs to be a way for a transaction to specify to the contract what function to execute. This is done by a transaction supplying a four-byte value that identifies for a contract what function to execute.

The four-byte value is called a function selector. It consists of the first four bytes of a hash of a string of a function signature.

A function signature is a string that consists of a function name and the types of its arguments. Let's look at an example. Here's a function signature: `"transfer(address,uint256)"`

Here we get the first four bytes of a hash of that function signature: 

```
bytes functionSelector = bytes4(keccak256("transfer(address,uint256)"));
```

The function selector is` 0xa9059cbb`. So if the first four bytes of transaction data is that value and the transaction is sent to a contract, then it tells that contract to execute the `"transfer(address,uint256)"` function.

## How Diamond Upgrades Work

Diamonds store function selectors and contract addresses of contracts that have the functions that the function selectors identify. The contracts that diamonds use are called facets.

A diamond has a mapping that maps function selectors to facet addresses.

Adding functions to a diamond means adding function selectors to a diamond and adding the facet addresses of the facets that have the functions that the function selectors identify. 

Replacing functions means associating existing function selectors in a diamond with different facet addresses.

Removing functions means removing function selectors and their facet addresses from the diamond.

The `diamondCut` function is used to upgrade diamonds. It has a `_diamondCut` argument that consists of an array of function selectors and facet addresses and an action which specifies whether to add, replace or remove them. Here's an example of a value for the `_diamondCut` argument:

```
[
  {
    facetAddress: '0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82',
    action: 0,
    functionSelectors: [
      '0x4cd5d1f7',
      '0x24a6665e',
      '0x4113e5ca',
      '0x49aa1f27',
      '0x24d86f00',
      '0x2e0bcb43',
      '0xbec10cde',
      '0xa3ea00f1'
    ]
  },
  {
    facetAddress: '0x549549085493058324890438543949485a499A1b',                  
    action: 1,
    functionSelectors: [
      '0x45943954',
      '0xa4349495',
      '0x9684834e'
    ]
  }  
]
```

In the example above the action value of 0 means adding new functions.  The action value of 1 means replacing the functions by replacing the facet address for them.

In Javascript scripts the FacetCutAction object can used to more explicitly show the action. Here is its definition:

```
const FacetCutAction = { Add: 0, Replace: 1, Remove: 2 }
```

Here is an example of a Javascript script that creates the `_diamondCut` argument:

```
const FacetCutAction = { Add: 0, Replace: 1, Remove: 2 }

  const newFuncs = [
    getSelector('function setSleeves(tuple(uint256 sleeveId, uint256 wearableId)[] calldata _sleeves) external'),
    getSelector('function updateSvg(string calldata _svg, tuple(bytes32 svgType, uint256[] ids, uint256[] sizes)[] calldata _typesAndIdsAndSizes) external')
  ]
  const existingFuncs = getSelectors(facet).filter(selector => !newFuncs.includes(selector))

  const _diamondCut = [
    {
      facetAddress: facet.address,
      action: FacetCutAction.Add,
      functionSelectors: newFuncs
    },
    {
      facetAddress: facet.address,
      action: FacetCutAction.Replace,
      functionSelectors: existingFuncs
    }
  ]
  console.log(_diamondCut)
```

The [AavegotchiDiamond has many upgrade scripts](https://github.com/aavegotchi/aavegotchi-contracts/tree/master/scripts/upgrades). Check them out for more code examples.

You may want to initialize state after an upgrade. That is what the second and third arguments of the `diamondCut` function are for. The `_init` argument holds the contract address of a function to call to initialize the state of the diamond. The `_calldata` argument holds a function call to send to the contract at `_init`. The function call is executed with delegatecall so that the diamond's state is affected and can be initialized.

Executing an initialization function by using the `_init` and `_calldata` arguments when making an upgrade means that the upgrade and state initialization are done in the same transaction. This is good because it prevents the diamond from getting into a bad or inconsistent state, which could possibly happen if an upgrade and state initialization are done in separate transactions.