国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

基于openzeppelin編寫(xiě)solidity可升級(jí)的智能合約

這篇具有很好參考價(jià)值的文章主要介紹了基于openzeppelin編寫(xiě)solidity可升級(jí)的智能合約。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

概述

????????現(xiàn)代軟件的設(shè)計(jì)原則是“敏捷開(kāi)發(fā),迅速迭代”,功能升級(jí)或bug修復(fù)是所有軟件系統(tǒng)都要面對(duì)的問(wèn)題。甚至可以說(shuō)軟件質(zhì)量在很大程度上依賴于升級(jí)和修補(bǔ)源代碼的能力。當(dāng)然Dapp(去中心化應(yīng)用)也不例外,尤其Dapp一切都是透明的,這使得任何級(jí)別的bug都會(huì)被成倍的放大,因此可升級(jí)的智能合約成為所有Dapp的必然選擇。

????????本文主要以openzeppelin為基礎(chǔ)來(lái)闡述構(gòu)建可升級(jí)智能合約的一般流程和注意事項(xiàng)。

原理

openzeppelin通過(guò)在用戶與智能合約中間加入一個(gè)代理來(lái)實(shí)現(xiàn)合約的透明升級(jí),用戶直接與代理交互,代理將用戶的請(qǐng)求轉(zhuǎn)發(fā)到實(shí)際合約,同時(shí)將合約的執(zhí)行結(jié)果響應(yīng)給用戶。

示意圖

基于openzeppelin編寫(xiě)solidity可升級(jí)的智能合約,區(qū)塊鏈,智能合約,區(qū)塊鏈,去中心化,javascript,開(kāi)發(fā)語(yǔ)言

如上圖所示,升級(jí)時(shí)只需要讓Proxy指向Implementation合約即可。

上圖有如下三種類型合約:

  • Proxy
  1. ?用戶直接也該合約交互;
  2. 所有的狀態(tài)變量都在該合約中維護(hù);
  3. 該合約符合EIP1967標(biāo)準(zhǔn);
  4. 該合約將用戶請(qǐng)求透明的轉(zhuǎn)發(fā)到Implementation合約,同時(shí)將Implementation合約的返回響應(yīng)給用戶;
  • Implementation

????????該合約被稱為邏輯合約,Dapp的所有邏輯都在該合約中完成,Proxy以delegatecall的形式調(diào)用該合約中的方法。

  • ProxyAdmin

????????在介紹該合約前我們先考慮一個(gè)問(wèn)題——我們?nèi)绾握{(diào)用Proxy本身的方法?比如Proxy與Implementation都有一個(gè)方法upgradeTo(address),那么當(dāng)用戶調(diào)用該方法時(shí),Proxy是該調(diào)用其自身方法還是以delegatecall的形式調(diào)用Implementation?

????????OpenZeppelin是通過(guò)”透明代理“(transparent proxy )的模式來(lái)解決這個(gè)問(wèn)題的。該模式通過(guò)發(fā)起調(diào)用的地址來(lái)決定如何調(diào)用方法。

  • 發(fā)起調(diào)用的地址為Proxy的管理地址(部署Proxy的地址)時(shí),Proxy將執(zhí)行自己的方法。
  • 發(fā)起調(diào)用的地址為其它地址時(shí),Proxy將以delegatecall的形式向Implementation發(fā)起調(diào)用。

假設(shè)Proxy有owner()和upgradeTo()方法,Implementation有owner()和transfer()方法,則不同用戶發(fā)起調(diào)用時(shí)具體調(diào)用方法如下:

msg.sender owner() upgradeto() transfer()

Owner

returns proxy.owner()

returns proxy.upgradeTo()

fails

Other

returns Implementation.owner()

fails

returns Implementation.transfer()

??????? 通過(guò)上面的討論我們可以看出,部署Proxy合約的賬戶無(wú)法調(diào)用Implementation合約中的方法,為此OpenZeppelin用ProxyAdmin來(lái)管理部署Proxy,此時(shí)Proxy的部署者為ProxyAdmin,這樣用戶就不用擔(dān)心本地賬戶無(wú)法調(diào)用Implementation的情況,當(dāng)然OpenZeppelin也提供了專門的接口用于更改Proxy的管理者。

執(zhí)行流程

  1. 用戶向Proxy發(fā)起調(diào)用。
  2. Proxy捕獲用戶請(qǐng)求,同時(shí)以delegatecall的形式向Implementation發(fā)起調(diào)用。此處理解delegatecall與call的區(qū)別尤其重要。
  3. Implementation收到請(qǐng)求后執(zhí)行相關(guān)邏輯,并將結(jié)果返回給Proxy。值得注意的是由于上步中以delegatecall形式調(diào)用,因此該合約中邏輯是在Proxy的上下文中執(zhí)行。
  4. Proxy將收到的返回?cái)?shù)據(jù)響應(yīng)給用戶。

示例

????????下面在hardhat本地節(jié)點(diǎn),以自動(dòng)售貨機(jī)的合約升級(jí)為例來(lái)說(shuō)明合約升級(jí)流程。

啟動(dòng)本地節(jié)點(diǎn)

npx hardhat node

合約版本1

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

// import "hardhat/console.sol";

contract VendingMachineV1 is Initializable {
    // these state variables and their values
    // will be preserved forever, regardless of upgrading
    uint public numSodas;
    address public owner;

    function initialize(uint _numSodas) public initializer {
        numSodas = _numSodas;
        owner = msg.sender;
    }

    function purchaseSoda() public payable {
        require(msg.value >= 1000 wei, "You must pay 1000 wei for a soda!");
        numSodas--;
    }

    function withdrawProfits() public onlyOwner {
        require(
            address(this).balance > 0,
            "Profits must be greater than 0 in order to withdraw!"
        );
        (bool sent, ) = owner.call{value: address(this).balance}("");
        require(sent, "Failed to send ether");
    }

    function setNewOwner(address _newOwner) public onlyOwner {
        owner = _newOwner;
    }

    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner can call this function.");
        _;
    }
}

部署腳本

const { ethers, upgrades } = require('hardhat');

async function main() {
  const VendingMachineV1 = await ethers.getContractFactory('VendingMachineV1');
  const proxy = await upgrades.deployProxy(VendingMachineV1, [100]);
  await proxy.waitForDeployment();

  const proxyAddr = await proxy.getAddress();
  const implementationAddress = await upgrades.erc1967.getImplementationAddress(
    proxyAddr
  );

  console.log('Proxy contract address: ' + proxyAddr);

  console.log('Implementation contract address: ' + implementationAddress);
}

main();

?合約部署

執(zhí)行部署腳本

npx hardhat run scripts/deployProxy.js --network localhost

部署腳本執(zhí)行后會(huì)打印出代理地址和VendingMachineV1合約的地址,如下:

基于openzeppelin編寫(xiě)solidity可升級(jí)的智能合約,區(qū)塊鏈,智能合約,區(qū)塊鏈,去中心化,javascript,開(kāi)發(fā)語(yǔ)言

合約版本2

該版本較版本1添加了supplySoda方法用于補(bǔ)充庫(kù)存,為了縮短篇幅這處省略版本1中相同的代碼部分。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

contract VendingMachineV2 is Initializable {

    //....
    //....舊版本相同的代碼

    function supplySoda(uint _num) public {
        require(_num > 0);
        numSodas += _num;
    }

}

合約升級(jí)

???????? 為了更清晰的說(shuō)明升級(jí)過(guò)程,此處我們?cè)趆ardhat終端進(jìn)行升級(jí)。

  • 開(kāi)啟hardhat終端
npx hardhat console --network localhost
  • 在終端中我們先觀察版本1合約的當(dāng)前狀態(tài),如下圖:

基于openzeppelin編寫(xiě)solidity可升級(jí)的智能合約,區(qū)塊鏈,智能合約,區(qū)塊鏈,去中心化,javascript,開(kāi)發(fā)語(yǔ)言

從圖中我們可以看到,Proxy指向的Implementation的地址與前面部署版本1時(shí)輸出的地址一致。

注:此處箭頭1是Proxy的地址,箭頭2是Implementation的地址

  • 部署版本2

基于openzeppelin編寫(xiě)solidity可升級(jí)的智能合約,區(qū)塊鏈,智能合約,區(qū)塊鏈,去中心化,javascript,開(kāi)發(fā)語(yǔ)言

由上圖可知,更新后Proxy已指向了新版本的地址(見(jiàn)紅框)。執(zhí)行新版本的方法補(bǔ)充庫(kù)存后結(jié)果如下:

基于openzeppelin編寫(xiě)solidity可升級(jí)的智能合約,區(qū)塊鏈,智能合約,區(qū)塊鏈,去中心化,javascript,開(kāi)發(fā)語(yǔ)言

要點(diǎn)

構(gòu)造函數(shù)

????????通過(guò)OpenZeppelin編寫(xiě)可升級(jí)合約時(shí),在合約中不能有構(gòu)造函數(shù),一般將初始化的操作放在一個(gè)initialize的普通函數(shù)中(當(dāng)然可以是任意的函數(shù)名,此時(shí)只需要調(diào)用部署API時(shí)指定該函數(shù)即可),升級(jí)過(guò)程中OpenZeppelin組件會(huì)主動(dòng)執(zhí)行該函數(shù)。

??????? 構(gòu)造函數(shù)與普通函數(shù)最大的區(qū)別是,構(gòu)造函數(shù)只在部署時(shí)執(zhí)行一次,而普通函數(shù)可以多次執(zhí)行,因此需要向initialize函數(shù)加上initializer修飾符,該modifer只允許該函數(shù)執(zhí)行一次。

??????? 在執(zhí)行上面升級(jí)版本2時(shí),我們發(fā)現(xiàn)更新版本時(shí)雖然我們新版本傳入了構(gòu)造函數(shù)的參數(shù)但新版本的initialize并未執(zhí)行。

upgrades.upgradeProxy('0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512', V2, {opts:{constructorArgs:[300]}});

添加變量

????????在編寫(xiě)合約的新版本時(shí),無(wú)論是由于新特性還是由于bug修復(fù),都有一個(gè)額外的限制需要遵守:您不能更改合約狀態(tài)變量聲明的順序,也不能更改它們的類型。具體原因看這里。

??????? 一般在寫(xiě)可升級(jí)合約時(shí)我們需要預(yù)留一些空間,以允許該合約的未來(lái)版本在不影響子合約的存儲(chǔ)布局的情況下使用這些槽。通用的做法是在基礎(chǔ)合約中預(yù)先定義固定大小的uint256數(shù)組(由于EVM以槽為單位執(zhí)行操作,而槽大小為32字節(jié)),該數(shù)組一般定義為_(kāi)_gap或以__gap_為前綴,以便OpenZeppelin升級(jí)可以識(shí)別該數(shù)組為預(yù)留空間,當(dāng)版本升級(jí)需要新加變量時(shí)可以釋放該數(shù)組的空間,如下:

//升級(jí)前合約
contract Base {
    uint256 base1;
    uint256[50] __gap;
}

contract Child is Base {
    uint256 child;
}

//升級(jí)后合約
contract Base {
    uint256 base1;
    uint256 base2;
    uint256[49] __gap;
}

或者
contract Base {
    uint256 base1;
    uint128 base2a;
    uint128 base2b;
    uint256[49] __gap;
}

Proxy透明轉(zhuǎn)發(fā)的原理

??????? 其實(shí)質(zhì)是在proxy的fallback函數(shù)中添加如下邏輯。

// This code is for "illustration" purposes. To implement this functionality in production it
// is recommended to use the `Proxy` contract from the `@openzeppelin/contracts` library.
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.2/contracts/proxy/Proxy.sol

assembly {
  // (1) copy incoming call data
  calldatacopy(0, 0, calldatasize())

  // (2) forward call to logic contract
  let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)

  // (3) retrieve return data
  returndatacopy(0, 0, returndatasize())

  // (4) forward return data back to caller
  switch result
  case 0 {
      revert(0, returndatasize())
  }
  default {
      return(0, returndatasize())
  }
}

Proxy與implementation狀態(tài)變量沖突

??????? 通過(guò)Unstructured Storage Proxies來(lái)解決

參考文檔:

Proxy Upgrade Pattern - OpenZeppelin Docs

Writing Upgradeable Contracts - OpenZeppelin Docs文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-802558.html

到了這里,關(guān)于基于openzeppelin編寫(xiě)solidity可升級(jí)的智能合約的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【區(qū)塊鏈技術(shù)開(kāi)發(fā)】OpenZeppelin智能合約庫(kù):提高智能合約的安全性和可靠性,加速去中心化應(yīng)用DApp的開(kāi)發(fā)與部署。

    專欄:【區(qū)塊鏈技術(shù)開(kāi)發(fā)】 前期文章: 【區(qū)塊鏈技術(shù)開(kāi)發(fā)】使用Infura連接以太坊節(jié)點(diǎn)和OpenZeppelin庫(kù)來(lái)構(gòu)建安全、可靠的智能合約 【區(qū)塊鏈技術(shù)開(kāi)發(fā)】 Solidity使用Truffle Box工具實(shí)現(xiàn)預(yù)構(gòu)建模板、自動(dòng)化部署、創(chuàng)建智能合約示例代碼 【區(qū)塊鏈技術(shù)開(kāi)發(fā)】 Solidity使用truffle工具創(chuàng)建

    2023年04月24日
    瀏覽(92)
  • 2.DApp-編寫(xiě)和運(yùn)行solidity智能合約

    2.DApp-編寫(xiě)和運(yùn)行solidity智能合約

    題記 ? ? ? ? 演示如何編寫(xiě)solidity智能合約,以及在remix中運(yùn)行solidity代碼。 準(zhǔn)備Remix環(huán)境 ? ? ? ? 在瀏覽器中搜索remix,找到remix官網(wǎng),并打開(kāi) ? ? ? ? 由于是國(guó)內(nèi)網(wǎng)絡(luò),所以訪問(wèn)國(guó)外網(wǎng)站較慢,可以耐心等待加載完成,或者科學(xué)上網(wǎng)。? ? ? ? ? 加載完成是這樣:? ? 編寫(xiě)

    2024年02月03日
    瀏覽(23)
  • 區(qū)塊鏈2——Solidity智能合約開(kāi)發(fā)

    區(qū)塊鏈 索引目錄 智能合約是一種以代碼形式編寫(xiě)的自動(dòng)執(zhí)行合約,它們運(yùn)行在區(qū)塊鏈上。這些合約定義了在特定條件下發(fā)生的事件以及相應(yīng)的行為。 1.1 智能合約結(jié)構(gòu) 版本聲明(Version Declaration): 智能合約通常以聲明版本開(kāi)始,指定合約應(yīng)該使用的Solidity編譯器版本。例如

    2024年02月05日
    瀏覽(29)
  • 區(qū)塊鏈智能合約編程語(yǔ)言 Solidity

    上文介紹了區(qū)塊鏈生態(tài)發(fā)展,我們知道以太坊的到來(lái)可以使開(kāi)發(fā)人員基于區(qū)塊鏈開(kāi)發(fā)DApp,本文介紹 Solidity 編程語(yǔ)言的使用,然后基于 Solidity 編寫(xiě)一個(gè)簡(jiǎn)單的智能合約。 Solidity 是以太坊開(kāi)發(fā)人員使用的編程語(yǔ)言,用來(lái)編寫(xiě)智能合約,運(yùn)行在以太坊虛擬機(jī)(EVM)上。 有開(kāi)發(fā)經(jīng)

    2024年02月12日
    瀏覽(29)
  • 【區(qū)塊鏈實(shí)戰(zhàn)】Solidity 智能合約如何給賬戶充值

    【區(qū)塊鏈實(shí)戰(zhàn)】Solidity 智能合約如何給賬戶充值

    目錄 一、實(shí)戰(zhàn)場(chǎng)景 二、知識(shí)點(diǎn) 智能合約 智能合約函數(shù) 智能合約充值 payable 智能合約部署地址 智能合約的運(yùn)行 合約 this 對(duì)象 三、菜鳥(niǎo)實(shí)戰(zhàn) 四、運(yùn)行結(jié)果 Solidity 智能合約如何給賬戶充值 1、充值金額 2、充值并查看結(jié)果

    2024年02月09日
    瀏覽(23)
  • 【區(qū)塊鏈-智能合約工程師】第二篇:Solidity入門

    【區(qū)塊鏈-智能合約工程師】第二篇:Solidity入門

    參考文章:一文速覽2022十大智能合約開(kāi)發(fā)工具 資料地址:WTF學(xué)院 HelloWorld remix:在線智能合約開(kāi)發(fā)IDE(Integrated Development Environment,集成開(kāi)發(fā)環(huán)境),可以在瀏覽器中快速部署測(cè)試智能合約。 合約HelloWorld: 事項(xiàng) 說(shuō)明 代碼所用的軟件許可(license) 不寫(xiě)許可的話編譯時(shí)會(huì)警告

    2024年02月09日
    瀏覽(27)
  • 區(qū)塊鏈智能合約solidity的中的一些關(guān)鍵字

    區(qū)塊鏈智能合約solidity的中的一些關(guān)鍵字

    目? 錄 pragma mapping msg對(duì)象 block對(duì)象 contract constructor struct 數(shù)據(jù)地址 地址類型 address payable revert 以下場(chǎng)景使用 revert() : require 以下場(chǎng)景使用 require() : assert 以下場(chǎng)景使用 assert(): 訪問(wèn)權(quán)限 internal public private external function returns return view pure constant event emit modifier pragma ??

    2024年01月16日
    瀏覽(27)
  • 【區(qū)塊鏈-智能合約工程師】第三篇:Solidity進(jìn)階(一)

    學(xué)習(xí)資料地址:WTF學(xué)院 庫(kù)合約一般都是一些好用的函數(shù)合集(庫(kù)函數(shù)),為了提升solidity代碼的復(fù)用性和減少gas而存在。他和普通合約主要有以下幾點(diǎn)不同: 不能存在狀態(tài)變量 不能夠繼承或被繼承 不能接收以太幣 不可以被銷毀 String庫(kù) String庫(kù)合約是將uint256(大正整數(shù))類型

    2024年02月06日
    瀏覽(21)
  • 區(qū)塊鏈web3智能合約Solidity學(xué)習(xí)資源整理

    Solidity 是一門面向合約的、為實(shí)現(xiàn)智能合約而創(chuàng)建的高級(jí)編程語(yǔ)言。這門語(yǔ)言受到了 C++,Python 和 Javascript 語(yǔ)言的影響,設(shè)計(jì)的目的是能在以太坊虛擬機(jī)(EVM)上運(yùn)行。 Solidity中文官方文檔: https://solidity-cn.readthedocs.io/zh/develop/ https://learnblockchain.cn/docs/solidity/index.html 在線rem

    2024年03月19日
    瀏覽(30)
  • 部署OpenZeppelin可升級(jí)合約

    部署OpenZeppelin可升級(jí)合約

    使用OpenZeppelin升級(jí)插件部署的合約具備可升級(jí)的特性:可以升級(jí)以修改其代碼,同時(shí)保留其地址,狀態(tài)和余額。 可以迭代地向項(xiàng)目中添加新功能,或修復(fù)在線上版本中可能發(fā)現(xiàn)的任何錯(cuò)誤。 創(chuàng)建一個(gè)新的npm項(xiàng)目 安裝并初始化Truffle 安裝Truffle升級(jí)插件 注意,可升級(jí)合約使用

    2023年04月08日
    瀏覽(19)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包