Skip to main content

Diamond 101 (2)

· 3 min read

本文為 Diamond 101 系列的第二篇文章,將解釋這個 EIP 具體的實作細節。

Contract Architecture

首先來看一下使用 EIP-2535 的合約架構圖。EIP-2535 發明了很多術語,圖中 Diamond 就是 Proxy Contract,Facet 就是 Logic Contract。一有合約調用,則會先在 Lookup Table 查詢有沒有 selector 的資料,然後調用 delegatecall 過去,資料一樣寫在 Proxy 裡面。

Lookup Table

Lookup Table 的資料結構可以以 mapping 或是 array 等來設計,作者以 bytes4[]mapping 一起使用,主要是為了達成 Enumerable 的特性。另外為了避免 storage collision,也將 Lookup Table 放到特定的 storage 裡面。

Lookup Table 的資料結構和 storage slot

bytes32 constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage");

struct FacetAddressAndSelectorPosition {
address facetAddress;
uint16 selectorPosition;
}

struct DiamondStorage {
// function selector => facet address and selector position in selectors array
mapping(bytes4 => FacetAddressAndSelectorPosition) facetAddressAndSelectorPosition;
bytes4[] selectors;
mapping(bytes4 => bool) supportedInterfaces;
// owner of the contract
address contractOwner;
}

Loupe

實作了 Lookup Table 查詢功能的 Facet 稱為 Loupe,主要功能為 Lookup Table 的查詢。為何 Lookup Table 的資料結構那麼複雜,看看這堆查詢函式能什麼資料就知道了。

interface IDiamondLoupe {
struct Facet {
address facetAddress;
bytes4[] functionSelectors;
}

function facets() external view returns (Facet[] memory facets_);

function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetFunctionSelectors_);

function facetAddresses() external view returns (address[] memory facetAddresses_);

function facetAddress(bytes4 _functionSelector) external view returns (address facetAddress_);
}

diamondCut

diamondCut 則是一個函式,專門管理(新增、刪除、修正)Lookup Table。

interface IDiamondCut {
// Add = 0, Replace = 1, Remove = 2
enum FacetCutAction {Add, Replace, Remove}

struct FacetCut {
address facetAddress;
FacetCutAction action;
bytes4[] functionSelectors;
}

function diamondCut(
FacetCut[] calldata _diamondCut,
address _init,
bytes calldata _calldata
) external;

event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);
}

Glossary

列出 EIP2535 發明的術語

  • diamond: Proxy Contract
  • facet: Logic Contract
  • loupe: 一個實作 Diamond 查詢介面的 Facet
  • diamondCut: 一個特定的函式名稱,專門用來管理(新增、刪除、修正)Lookup Table
  • Upgradeable Diamond: 有註冊 diamondCut 的 Diamond Contract,可以註冊新的函式到 Lookup Table 裡做升級
  • Finished Diamond: 一個 Upgradeable Diamond,歷經升級迭代後功能都完全了,最後將 diamondCut 移除 Lookup Table
  • Single Cut Diamond: 只在 constructor 註冊 function 進註冊表,部署完成後就不能做任何升級