使用delegatecall擴充合約功能

一车要、背景

你是否經歷過EIP-170(合約過大)的支配芬膝?你是否經歷過合約代碼臃腫,各種功能魚龍混雜睬棚,讓人看的頭昏眼花抠忘?如果是蔫耽,說明你需要了解一下delegatecall了。

在智能合約的開發(fā)中留夜,使用 delegatecall 是一種強大的技術手段匙铡,它允許一個合約在執(zhí)行時調用另一個合約的代碼。這種機制為開發(fā)者提供了在不修改合約存儲狀態(tài)的情況下碍粥,動態(tài)調用其他合約的能力鳖眼,從而實現(xiàn)合約的靈活性和可升級性。本文將深入介紹 delegatecall 的概念嚼摩,以及如何利用它來擴充合約功能钦讳。

二矿瘦、什么是 delegatecall

delegatecall 是以太坊虛擬機提供的一種調用機制愿卒,它允許一個合約在執(zhí)行時將執(zhí)行流程切換到另一個合約缚去,并在該合約的上下文中執(zhí)行代碼。與 call 不同琼开,delegatecall 在執(zhí)行過程中共享存儲空間易结,這意味著被調用的合約可以訪問調用者合約的狀態(tài)變量。這一特性使得在不破壞原有合約存儲結構的前提下柜候,可以實現(xiàn)功能的擴展和升級搞动。

三、使用場景

1. 升級合約邏輯

通過 delegatecall渣刷,我們可以將新版本的合約邏輯部署為一個獨立的合約鹦肿,并通過調用舊合約的 delegatecall 函數(shù)來執(zhí)行新邏輯。這樣就實現(xiàn)了合約的升級辅柴,而不需要遷移存儲數(shù)據箩溃。

solidityCopy code
contract UpgradableContract {
    address public currentLogic;

    function upgradeTo(address newLogic) external {
        require(msg.sender == owner);
        currentLogic.delegatecall(abi.encodeWithSignature("upgrade()"));
    }
}

2. 功能模塊化

使用 delegatecall 可以將合約的功能拆分為多個獨立的模塊,每個模塊部署為一個獨立的合約碌识。主合約通過 delegatecall 調用這些模塊碾篡,從而實現(xiàn)功能的動態(tài)組合和替換。

solidityCopy code
contract MainContract {
    address public module;

    function setModule(address newModule) external {
        require(msg.sender == owner);
        module.delegatecall(abi.encodeWithSignature("setModule()"));
    }
}

四筏餐、案例實戰(zhàn)

在Layer2开泽、跨鏈等項目方案中,需要將用戶的ERC20 Token/NFT轉給項目的主合約進行鎖定魁瞪,然后主合約收到后穆律,再其項目中生成對應的憑證做后續(xù)交易;當需要解鎖時导俘,從主合約再轉給用戶峦耘,因此主合約在鎖定期間是實際token或nft的持有者,后續(xù)轉出時旅薄,也必須有主合約親自發(fā)起辅髓。但,如果主合約寫不下取款的代碼了怎么辦少梁?如果不在主合約里寫可以嗎洛口?當然可以。

1. 主合約

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;
pragma abicoder v2;

contract DelegateCallDemo {
        struct DelegateCall {
            address sender;
            address delegatecall_contract_address;
            uint256 amount;
            address token;
        }
        event DelegateCallFailure(
            address indexed sender,
            address indexed delegatecall_contract_address,
            uint256 amount,
            address token
        );
        function _delegateCall(DelegateCall calldata dc) internal {
            address _target = dc.delegatecall_contract_address;
            (bool success, ) = _target.delegatecall(
                abi.encodeWithSignature(
                    "delegatecallReceive(address,uint256,address)",
                    dc.sender,
                                dc.amount,
                    dc.token
                )
            );
            if (!success) {
                emit DelegateCallFailure(
                    dc.sender,
                    dc.delegatecall_contract_address,
                    dc.amount,
                    dc.token
                );
            }
        }
}

2. DelegateCall合約

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

import "github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.2/contracts/token/ERC20/IERC20.sol";

contract DelegatecallReceiveDemo{
        function delegatecallReceive(
            address _sender,
            uint256 _amount,
            address _token
        ) external payable override returns (bool, bytes memory) {
            IERC20 token = IERC20(_token);
        
            token.transfer(_sender, _amount);
        }
}

這樣凯沪,通過一個新部署的DelegateCall合約就可以完全代替主合約進行任意操作了第焰。

值得說明的是,DelegateCall合約是沒辦法有自己的存儲空間的妨马,完全使用主合約的存儲空間挺举,因此如果要在DelegateCall中使用主合約的存儲變量杀赢,請完全聲明順序一模一樣的變量,或者繼承主合約的存儲合約湘纵,或者直接讀取存儲地址(就像你自己是主合約一樣)等脂崔,取決于你的主合約用何種方式存儲。

五瞻佛、注意事項

在使用 delegatecall 時脱篙,需要注意以下事項:

  • 存儲隔離: 被調用的合約可能訪問調用者合約的狀態(tài)變量,因此要確保存儲變量的隔離性伤柄,防止被調用合約意外修改調用者合約的狀態(tài)绊困。
  • 數(shù)據傳遞: 使用 delegatecall 時,要確保正確傳遞參數(shù)适刀〕永剩可以使用 abi.encodeWithSignature 將函數(shù)調用的簽名和參數(shù)打包。
  • 異常處理:delegatecall 中笔喉,被調用的合約拋出的異常會傳遞到調用者合約取视。因此,需要適當處理異常常挚,防止影響調用者合約的正常執(zhí)行作谭。

六、總結

delegatecall 是一項強大的技術奄毡,為智能合約提供了靈活性和可升級性折欠。通過合理利用 delegatecall,開發(fā)者可以在不破壞原有合約結構的基礎上吼过,實現(xiàn)功能的動態(tài)擴展和升級锐秦。在使用時需要小心處理存儲隔離、數(shù)據傳遞和異常處理等問題盗忱,確保合約的安全性和穩(wěn)定性酱床。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市趟佃,隨后出現(xiàn)的幾起案子扇谣,更是在濱河造成了極大的恐慌,老刑警劉巖闲昭,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件罐寨,死亡現(xiàn)場離奇詭異,居然都是意外死亡汤纸,警方通過查閱死者的電腦和手機衩茸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門芹血,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贮泞,“玉大人楞慈,你說我怎么就攤上這事】胁粒” “怎么了囊蓝?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長令蛉。 經常有香客問我聚霜,道長,這世上最難降的妖魔是什么珠叔? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任蝎宇,我火速辦了婚禮,結果婚禮上祷安,老公的妹妹穿的比我還像新娘姥芥。我一直安慰自己,他們只是感情好汇鞭,可當我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布凉唐。 她就那樣靜靜地躺著,像睡著了一般霍骄。 火紅的嫁衣襯著肌膚如雪台囱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天读整,我揣著相機與錄音簿训,去河邊找鬼。 笑死绘沉,一個胖子當著我的面吹牛煎楣,可吹牛的內容都是我干的。 我是一名探鬼主播车伞,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼择懂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了另玖?” 一聲冷哼從身側響起困曙,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谦去,沒想到半個月后慷丽,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡鳄哭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年要糊,在試婚紗的時候發(fā)現(xiàn)自己被綠了幕侠。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姑原。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出占拍,到底是詐尸還是另有隱情孕似,我是刑警寧澤近刘,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布熙尉,位于F島的核電站,受9級特大地震影響毅戈,放射性物質發(fā)生泄漏苹丸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一苇经、第九天 我趴在偏房一處隱蔽的房頂上張望赘理。 院中可真熱鬧,春花似錦扇单、人聲如沸感憾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽阻桅。三九已至,卻和暖如春兼都,著一層夾襖步出監(jiān)牢的瞬間嫂沉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工扮碧, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留趟章,地道東北人。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓慎王,卻偏偏與公主長得像蚓土,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子赖淤,可洞房花燭夜當晚...
    茶點故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內容