---
# System prepended metadata

title: 合約更新

---

區塊鏈全端工程師第7週課程: 智能合約更新
===
Outline
---
* Fallback及delegatecall說明
* 基本的Proxy運行機制
* ERC721的Proxy
* Openzeppelin Proxy合約說明
* ERC1967
* Transparent Proxy
* UUPS Proxy

Fallback及delegatecall
---

* Fallback:
    * 說明：
        * 是一個特殊的函數，它沒有名字、不接受任何參數、也不回傳任何值。合約接收直接發送的以太幣（沒有調用任何函數）或調用的函數不存在時被觸發。
    * 使用場景：
        * 接收以太幣
        * 代理合約
    * 範例：
```solidity=
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract FallbackExample {
    event Log(uint gas);


    fallback() external payable {
        emit Log(gasleft());
    }

    function getBalance() public view returns (uint) {
        return address(this).balance;
    }
}

```
* delegatecall: 
    * 說明：
        * 要用另外一個外部合約時，用當前合約的狀態與該外部合約互動。
    * 使用場景：
        * 代理合約
    * 範例：
        * 直接見後方Proxy範例。
基本的Proxy運行機制
---
* 概念圖：
![未命名绘图-第 12 页.drawio](https://hackmd.io/_uploads/rJ5Y7MJ26.png)
* 範例：
Proxy.sol
```solidity=
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

contract Proxy{

    uint256 public num;
    address public logic;

    constructor (address _logic){
        logic = _logic;
    }

    function setLogic (address _new_logic) external {
        logic = _new_logic;
    }
    
    fallback() external payable{
        (bool success, bytes memory data) = logic.delegatecall(msg.data);
        require(success, "delegatecall error! ");
    }
    
}
```

Logic.sol
```solidity=
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

contract Logic{
    uint256 public num;

    function setNum(uint256 _num) external {
        num = _num;
    }

    function getNum() external view returns(uint256) {
        return num;
    }
}
```
Setup.sol
```solidity=
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "./Proxy.sol";
import "./Logic.sol";

contract Setup{
    Proxy public proxy;
    Logic public logic;

    constructor (){
        logic = new Logic();
        proxy = new Proxy(address(logic));
    }

}
```
ERC721的Proxy
---
Proxy.sol
```solidity=
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

contract Proxy{

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    mapping(uint256 tokenId => address) private _owners;

    mapping(address owner => uint256) private _balances;

    mapping(uint256 tokenId => address) private _tokenApprovals;

    mapping(address owner => mapping(address operator => bool)) private _operatorApprovals;
    address public logic;

    constructor (address _logic){
        logic = _logic;
    }

    function setLogic (address _new_logic) external {
        logic = _new_logic;
    }
    
    fallback() external payable{
        (bool success, bytes memory data) = logic.delegatecall(msg.data);
        require(success, "delegatecall error! ");
    }
    
}
```

ERC721Logic.sol
```solidity=
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.20;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract ERC721Logic is ERC721{

    constructor (string memory _name, string memory _symbol) ERC721 (_name, _symbol){
    }

    function mint (uint256 tokenId) external {
        _mint(msg.sender, tokenId);
    }
    
}
```

SetupERC721Proxy.sol
```solidity=
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "./Proxy.sol";
import "./ERC721Logic.sol";

contract SetupERC721Proxy{
    Proxy public proxy;
    ERC721Logic public logic;

    constructor (){
        logic = new ERC721Logic("PAUL","P");
        proxy = new Proxy(address(logic));
    }

}
```

* 上述有個問題， delegatecall無法取得回傳值！

Openzeppelin Proxy合約說明
---
* Proxy.sol
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/Proxy.sol

* 範例：

NewProxy.sol
```solidity=
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "@openzeppelin/contracts/proxy/Proxy.sol";
import "@openzeppelin/contracts/access/Ownable.sol";


contract NewProxy is Proxy, Ownable{

    uint256 public num;
    address public logic;

    constructor (address _logic) Ownable (msg.sender){
        logic = _logic;
    }

    function setImplementation(address _new_address) external onlyOwner {
        logic = _new_address;
    }

    function _implementation() internal view override returns (address){
        return logic;
    }


}
```

Logic.sol
```solidity=
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

contract Logic{
    uint256 public num;

    function setNum(uint256 _num) external {
        num = _num+10;
    }

    function getNum() external view returns(uint256) {
        return num;
    }
}
```

SetupNewProxy.sol
```solidity=
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "./NewProxy.sol";
import "./Logic.sol";

contract Setup{
    NewProxy public proxy;
    Logic public logic;

    constructor (){
        logic = new Logic();
        proxy = new NewProxy(address(logic));
    }

}
```
* Slot 衝突發生！！！
- owner地址被更改了！

ERC1967
---
此標準出來就是來解決Slot衝突問題的！
https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/proxy/ERC1967

bytes32 to uint256 工具:
https://neptunemutual.com/web3-tools/bytes32-to-number-converter/

ERC1967ProxyDemo.sol
```solidity=
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.20;

import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

contract ERC1967ProxyDemo is ERC1967Proxy{

    constructor (address _logic, bytes memory _data) ERC1967Proxy(_logic, _data){
    }
}
```

Logic.sol
```solidity=
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

contract Logic{
    uint256 public num;

    function setNum(uint256 _num) external {
        num = _num+10;
    }

    function getNum() external view returns(uint256) {
        return num;
    }
}
```

Transparent Proxy
---
* Openzeppelin合約：
https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/proxy/transparent

* Proxy合約管理者為```ProxyAdmin```合約，當ProxyAdmin合約的管理者，透過ProxyAdmin合約間接呼叫到Proxy合約時，會去檢查是否為```更新```操作，如我是，則更新。
* 如果呼叫者為一般用戶，則直接丟給邏輯合約。

* 範例：

TransparentProxyDemo.sol
```solidity=
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.20;

import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";

contract TransparentProxyDemo is TransparentUpgradeableProxy{

    constructor (address _logic, address _owner, bytes memory _data) TransparentUpgradeableProxy(_logic, _owner, _data){
    }

    function admin() external returns(address){
        return _proxyAdmin();
    }


}
```

ERC721Logic.sol
```solidity=
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract ERC721Logic is ERC721{
    uint256 public num;
    uint256 count = 0;

    constructor (string memory _name, string memory _symbol ) ERC721 (_name, _symbol){
        
    }

    function setNum(uint256 _num) external {
        num = _num+10;
    }

    function getNum() external view returns(uint256) {
        return num;
    }

    function burn(uint256 _tokenId) external {
        _burn (_tokenId);
    }

    function mint (uint256 amount) external {
        for (uint256 i = 0 ; i < amount; i++){
            _mint(msg.sender, count);
            count ++ ;
        }
    }
}
```

SetuoTransparentProxy.sol
```solidity=
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "./TransparentProxyDemo.sol";
import "./ERC721Logic.sol";

contract SetupTransparentProxy{
    TransparentProxyDemo public proxy;
    ERC721Logic public logic;

    constructor (bytes memory _data){
        logic = new ERC721Logic("Test", "T");
        proxy = new TransparentProxyDemo(address(logic), msg.sender, _data);
    }
}
```

* Admin從ProxyAdmin更新合約！
* 一般用戶直接互動，都會走到邏輯合約。


UUPS Proxy
---
* UUPS Proxy全名叫做：Universal Upgradeable Proxy Standard。
* 和Transparent Proxy的運作差異在於，合約更新的操作Transparent Proxy運作機制在Proxy 這層，而UUPS Proxy的機制在Logic這層。

* Openzeppelin的UUPS：
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/utils/UUPSUpgradeable.sol

* 範例：
NewERC1967Proxy.sol
```solidity=
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.20;

import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

contract NewERC1967Proxy is ERC1967Proxy{

    constructor (address _logic, bytes memory _data) ERC1967Proxy(_logic, _data){
        
    }
}
```

UUPSLogic.sol
```solidity=
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract UUPSLogic is  Initializable, UUPSUpgradeable, ERC721Upgradeable, OwnableUpgradeable{


    
    function initialize(string memory _name, string memory _symbol) external initializer{
        __UUPSUpgradeable_init();
        __ERC721_init(_name, _symbol);
        __Ownable_init(tx.origin); 
    }

    function mint (uint256 _tokenId) external {
        _mint(msg.sender, _tokenId);
        
    }
    
    function _authorizeUpgrade(address newImplementation) internal override{

    }
}
```

SetupUUPS.sol
```solidity=
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "./NewERC1967Proxy.sol";
import "./UUPSLogic.sol";

contract SetupTransparentProxy{
    NewERC1967Proxy public proxy;
    UUPSLogic public logic;

    constructor (){
        logic = new UUPSLogic();
        proxy = new NewERC1967Proxy(address(logic), abi.encodeWithSelector(logic.initialize.selector, "Paul","P"));
    }
}
```


