IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 区块链 -> Solidity之DelegateCall委托调用详解 -> 正文阅读

[区块链]Solidity之DelegateCall委托调用详解

前言

在编写以太坊智能合约代码时,有些情况下(功能的解耦,分区明确)我们需要与其他合约进行交互。如果只是为了代码复用,可把公共代码单独抽离出来,部署到一个library中(类似util工具包),后面就可以直接引入使用了。但是library中不允许定义任何storage类型的变量,这就意味着library不能修改合约的状态。如果需要修改合约状态变量,我们就需要另外部署一个新合约,具体怎么操作呢?

贴个官方文档说明:

There exists a special variant of a message call, named delegatecall which is identical to a message call apart from the fact that the code at the target address is executed in the context of the calling contract and msg.sender and msg.value do not change their values.

?通俗地理解就是如何调用另一个合约函数。

三种调用函数

在 Solidity 中,call 函数簇可以实现跨合约的函数调用功能,其中包括 call、delegatecall 和 callcode 三种方式。

  • 调用模式

<address>.call(...) returns (bool)

<address>.callcode(...) returns (bool)

<address>.delegatecall(...) returns (bool)

这些函数提供了灵活的方式与合约进行交互,并且可以接受任何长度、任何类型的参数,其传入的参数会被填充至 32 字节最后拼接为一个字符串序列,由 EVM 解析执行。在函数调用的过程中,Solidity 中的内置变量?msg?会随着调用的发起而改变,msg?保存了调用方的信息包括:调用发起的地址,交易金额,被调用函数字符序列等。

异同点

  • call: 调用后内置变量?msg?的值会修改为调用者,执行环境为被调用者的运行环境
  • delegatecall: 调用后内置变量?msg?的值不会修改为调用者,但执行环境为调用者的运行环境(相当于复制被调用者的代码到调用者合约)
  • callcode: 调用后内置变量?msg?的值会修改为调用者,但执行环境为调用者的运行环境

警告:"callcode"已被弃用,取而代之的是" delegatcall "。

delegatecall 与 call 的区别

1、如果我们知道目标合约ABI,可以直接使用函数签名调用。

假设我们已经部署了一个名为“Storage”的简单合约,它允许用户保存一个val状态变量值。

pragma solidity ^0.5.8;
contract Storage {
    uint public val;
constructor(uint v) public {
        val = v;
    }
function setValue(uint v) public {
        val = v;
    }
}

我们要部署另一个名为“Machine”的合约,它是“Storage”合约的调用者。“Machine”引用“Storage”合约并更改val值,如下。

pragma solidity ^0.5.8;
import "./Storage.sol";
contract Machine {
    Storage public s;
constructor(Storage addr) public {
        s = addr;
        calculateResult = 0;
    }
    
    function saveValue(uint x) public returns (bool) {
        s.setValue(x);
        return true;
    }
function getValue() public view returns (uint) {
        return s.val();
    }
}

在这种情况下,我们知道“Storage”的 ABI 及其地址,这样我们就可以用地址初始化现有的“Storage”合约,ABI 告诉我们如何调用“Storage”合约的函数。 我们可以看到“Machine”合约调用“Storage”setValue()函数。

2、如果我们不知道目标合约 ABI,请使用 call 或 delegatecall调用。

在解释call()和delegatcall()之前,了解EVM如何保存合约变量以理解后续内容将有很有帮助。

EVM如何保存字段变量到存储

在以太坊中,有两种保存合约字段变量的空间。一个是“memory”,另一个是“storage”。' foo is saved to storage' 意味着' foo'的值被永久记录到区块链上了。
那么,一个合约中如此多的变量怎么做到不重叠彼此的地址空间呢?

EVM为字段变量分配槽号(slot number),有点抽象哈。

contract Sample1 {
    uint256 first;  // slot 0
    uint256 second; // slot 1
}

?因为first在“Sample1”中首先声明,所以它被分配了0号槽位。每个不同的变量由它的槽号来区分。

在EVM中,它在智能合约存储中有2^256个插槽,每个插槽可以保存32字节大小的数据。?

?在我们讨论如何调用智能合约函数时,有一个词Context。实际上,语境在软件中是一个比较普遍的概念,它的意义会随着语境的变化而变化。当我们讨论程序的执行时,我们可以说上下文是所有环境,比如执行点的变量或状态。例如,在程序A的执行点上,执行该程序的用户名是zeroFruit,那么用户名zeroFruit可以是程序A的上下文。在以太坊智能合约中,有很多Context,其中一个代表性的事情是“who execute this contract”。你可能会被大量的solidity代码中看到msg.sender和msg.value。发送方地址因执行该合约函数的人而异。

顾名思义,DelegateCall是调用方合约如何调用目标合约函数的调用机制,但当目标合约执行其逻辑时,上下文不是在执行调用方合约的用户上,而是在调用方合约上。

?那么当合约委托调用到目标合约时,存储的状态将如何改变?
上下文是在调用方合约上的,所有的状态变化逻辑反映在调用方的存储上。
例如,我们有代理合约和业务合约。代理合约将调用委托给业务合约功能。如果用户调用代理合约,代理合约将把调用委托给业务合约,并执行对应功能。而所有的状态变化都将反映在代理合约存储中,而不是业务合约中。

delegatecall合约测试

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

// NOTE: Deploy this contract first
contract OLD {
    // NOTE: storage layout must be the same as contract A
    uint public num;
    address public sender;
    uint public value;

    function setVars(uint _num) public payable {
        num = _num;
        sender = msg.sender;
        value = msg.value;
    }
}

contract New1 {
    uint public num;
    address public sender;
    uint public value;

    function setVars(address _contract, uint _num) public payable {
        // A's storage is set, B is not modified.
        (bool success, bytes memory data) = _contract.delegatecall(
            abi.encodeWithSignature("setVars(uint256)", _num)
        );
    }
}

contract New2 {
    uint public num;
    address public sender;
    uint public value;

    function setVars(address _contract, uint _num) public payable {
        // A's storage is set, B is not modified.
        (bool success, bytes memory data) = _contract.call(
            abi.encodeWithSignature("setVars(uint256)", _num)
        );
    }
}

contract New3 {
    uint public num1;
    uint public value1;
    uint public sender1;

    function setVars(address _contract, uint _num) public payable {
        // A's storage is set, B is not modified.
        (bool success, bytes memory data) = _contract.delegatecall(
            abi.encodeWithSignature("setVars(uint256)", _num)
        );
    }
}

?关于如何操作此合约的步骤,我会单独录制一小段视频来介绍,有兴趣或动手能力强的小伙伴可以对照着注释调试哈。其实呢delegatecall函数设计的初衷是使用给定地址的代码,其他信息则使用当前合约(如存储、余额等),某种程度上也是为了代码的复用吧。

欢迎━(*`?′*)ノ亻!博友们发现问题和提出宝贵建议,谢谢!

参考资料:https://medium.com/coinmonks/delegatecall-calling-another-contract-function-in-solidity-b579f804178c

  区块链 最新文章
盘点具备盈利潜力的几大加密板块,以及潜在
阅读笔记|让区块空间成为商品,打造Web3云
区块链1.0-比特币的数据结构
Team Finance被黑分析|黑客自建Token“瞒天
区块链≠绿色?波卡或成 Web3“生态环保”标
期货从入门到高深之手动交易系列D1课
以太坊基础---区块验证
进入以太坊合并的五个数字
经典同态加密算法Paillier解读 - 原理、实现
IPFS/Filecoin学习知识科普(四)
上一篇文章      下一篇文章      查看所有文章
加:2021-11-27 09:56:48  更:2021-11-27 09:57:32 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/28 19:27:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计