solidity encode encodePacked encodeWithSignature delegatecall

bit bytes 0x uint

bit 二进制位
byte 最小的存储单位 1 byte = 8bit (bytes1-32) bytes 不定长,引用类型
uint8 = 8 bit 最大值就是255 = 2^8-1 (uint(8-256))
0x 十六进制 一位 = 4bit 15 =2^4-1
一个数字或字母占一个字节,一个汉字占用3个字节

编码ASCII和BaseXX
  1. ASCII编码 7个bit位代表一个字符 0-127 每个数字对应一个字符也就是128个字符
    [0, 31] 以及 127 (del) 这 33 个属于不可打印的控制字符才有了base64 MIME多媒体传递数据
  2. Base32编码 A-Z(26) 2-7(6) 空白用= padding 5个bit代表一个字符 0-31 每个数字代表要给字符也就是32个字符
  3. Base64编码 A-Za-z(52) 0-9(10) + / 6个bit代表一个字符0-63 每个数字代表要给字符也就是64个字符
    算法就是3个字节(3*8bit)转成4个base64字符(可打印的字符),不是3的倍数,用=号补充
    ASCII对应的对应的字符 转成bit 然后转成base64显示出来
    3*8bit=6bit(base64字符)*4
  4. Base58 o0 1i +/ 去掉
1. encode 标准的对参数abi编码

abi.encode(address(0x630959E5aE57D1165c29B5aDC2F77C2bB8B730a0),127)
000000000000000000000000630959e5ae57d1165c29b5adc2f77c2bb8b730a0
000000000000000000000000000000000000000000000000000000000000007f
生成的abi会自动填充到32个byte 就是64个十六进制位

2. encodePacked

abi.encodePacked(address(0x630959E5aE57D1165c29B5aDC2F77C2bB8B730a0),uint8(127));
0x630959e5ae57d1165c29b5adc2f77c2bb8b730a0
7f
不会填充,紧编码数据

3. decode对编码后的数据解码

abi.encode(“a”,“b”)
abi.decode(data, (string, string))

4. encodeWithSelector 对方法keccak256和参数encode并拼接到一起返回

abi.encodeWithSelector(bytes4(keccak256(“set(uint256)”)),number)

5. encodeWithSignature 相当于上面的简写

abi.encodeWithSignature(“set(uint256)”,number)

6. Storage Proxy中的属性要和impl的一致
7. delegatecall 调用编码后的方法 修改proxy中的值 读取的时候是proxy中的值
8. call 调用编码后的方法 修改impl中的值 staticcall-只能读取值,不能写值
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
contract BytesStudy{
  
    uint public setX; 
    bytes1 a2 = '1';
    bytes5 hh = 0xffffffffff;
    bytes2 b = '11';
    bytes3 c = 0xffffff;
    bytes32 d = '01234567890123456789012345678900';
 
    function test() public view returns (bytes4, bytes1, bytes1, bytes1, bytes1,bytes1){
      bytes4 a = 0x001122FF;
      return (a, a[0], a[1], a[2], a[3],a2);
    }

    function concat(string memory _a, string memory _b) public pure returns (string memory) {
        return string(abi.encodePacked(_a, _b));
    }

  //生成的abi需要填充到32个byte 就是64个十六进制位
  function testEncode() public pure returns (bytes memory){
    return abi.encode("a","b");
  }

   //
    function decode(bytes memory data) public pure returns (string memory _str1, string memory _str2) {
        (_str1, _str2) = abi.decode(data, (string, string));
    }


  //000000000000000000000000630959e5ae57d1165c29b5adc2f77c2bb8b730a0
  //000000000000000000000000000000000000000000000000000000000000007f
   function testEncode2() public pure returns (bytes memory){
    return abi.encode(address(0x630959E5aE57D1165c29B5aDC2F77C2bB8B730a0),127);
  }

      //0xb63e800d000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000500e5eb0cf12736f237604bb90ee996a24def620000000000000000000000000aeb79c89bd801348da200c73533908562a1d726e00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000
   //去掉前面的方法id(0x+4个bytes(8个16进制位))就是参数值000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000500e5eb0cf12736f237604bb90ee996a24def620000000000000000000000000aeb79c89bd801348da200c73533908562a1d726e00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000
   function test4(bytes memory d) public pure returns (address[] memory,uint256,address,bytes memory,address,address,uint256,address){
     return abi.decode(d, (address[],uint256,address,bytes,address,address,uint256,address));
  }


  //不会填充到64位 uint8=8bit = 1bytes = 2十六进制位
  //0x630959e5ae57d1165c29b5adc2f77c2bb8b730a0
  //7f
  function testEncodePacked2() public pure returns (bytes memory){
      return abi.encodePacked(address(0x630959E5aE57D1165c29B5aDC2F77C2bB8B730a0),uint8(127));
  }

  function testEncodePacked() public pure returns (bytes memory){
     return abi.encodePacked("a","b");
  }
  //方法名和参数的abi编码1
  //传入10返回 
  //0x60fe47b1
  //000000000000000000000000000000000000000000000000000000000000000a
   function testEncodeWithSelector(uint number) public pure returns (bytes memory){
     return abi.encodeWithSelector(bytes4(keccak256("set(uint256)")),number);
  }

  function set(uint x) public {
     setX = x;
  }

    //方法名和参数的abi编码2
  function testEncodeWithSignature(uint number) public pure returns (bytes memory){
    return abi.encodeWithSignature("set(uint256)",number);
  }
 

}

contract MyProxy {
  uint public setX; 

  //可以修改MyProxy-setX值
  //0x60fe47b1
  //000000000000000000000000000000000000000000000000000000000000000a
  function doFunction(address _address,bytes calldata _calldata ) public   returns (bool){
      (bool success,) =   _address.delegatecall(_calldata);
      require(success);
      return true;
  }

  //修改的是BytesStudy-setX值
  function doFunction2(address _address,bytes calldata _calldata ) public   returns (bool){
      (bool success,) =   _address.call(_calldata);
      require(success);
      return true;
  }

}

contract TestPayable {
    uint x;
    uint y;
    fallback() external payable { x = 1; y = msg.value; }
    receive() external payable { x = 2; y = msg.value; }
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;

contract TestPayable {
    event Log(string methodName,uint _x,uint _y);
    uint x;
    uint y;
    //调用没有对应的方法时候的回调
    fallback() external payable { 
      x = 1; 
      y = msg.value; 
      emit Log("fallback",x,y);
      
    }
    //接受到eth的回调
    receive() external payable {
       x = 2; 
       y = msg.value; 
       emit Log("receive",x,y);
    }
    function clearXY() public returns (uint) {
       x = 0;
       y = 0;
      emit Log("clearXY",x,y);
       return 0;
    }

    function setXY(uint _x,uint _y) public {
       x = _x;
       y = _y;
       emit Log("setXY",x,y);
    }

    function getValue() public pure returns (uint) {
       return 0;
    }
    function getValue2() public pure returns (uint,uint) {
       return (0,1);
    }

}

contract TestSender {
  event Log(string name,address _addre);

  //msg.sender调用这个合约的地址(包括普通地址或者合约地址)
   function testSender() public {
      emit Log("TestSender",msg.sender);
   } 
}

contract TestProxy {
    event Log(string name,address _addre);
    event LogBool(string name,bool isOK, bytes  dt);

 // 测试msg.sender
   function callTestSender(TestSender ts) public {
     emit Log("TestProxy",msg.sender);
     ts.testSender();
   }

  // 写的方法有1个返回值
  function callTestPayable(address _address) public  returns (bool) {
      (bool success,bytes memory returnData) =  _address.delegatecall(abi.encodeWithSignature("clearXY()"));
      emit LogBool("callTestPayable-clearXY",success,returnData);
      require(success);
      return true;
   }

  // 读的方法有1个返回值
   function callTestPayable2(address _address) public  returns (bool) {
      (bool success,bytes memory returnData) =  _address.delegatecall(abi.encodeWithSignature("getValue()"));
      emit LogBool("callTestPayable2-getValue",success,returnData);
      require(success);
      return true;
   }

  //没有对应的调用方法
   function callTestPayableNo(address _address) public  returns (bool) {
      (bool success,) =  _address.delegatecall(abi.encodeWithSignature("clearXYZ()"));
       emit LogBool("callTestPayableNo-clearXYZ",success,'aaaa');
      require(success);
      return true;
   }

    // 读的方法有1个返回值
    //0000000000000000000000000000000000000000000000000000000000000000
    //0000000000000000000000000000000000000000000000000000000000000001
    function callTestPayable3(address _address) public  returns (bool) {
      (bool success,bytes memory returnData) =  _address.delegatecall(abi.encodeWithSignature("getValue2()"));
       emit LogBool("callTestPayable3-getValue2",success,returnData);
      require(success);
      return true;
   }
  
}

9. 实例代码App
MyApp
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;

//属性存储插槽slot中,每个solt顺序合约A和aProxy一致,否则会报错
//定义不同的Storage来存储属性值 让合约A,aProxy继承来统一插槽

//用户信息
contract MyAppStorage {

 struct User{
     uint age;
     uint sex;
     bool isMember;
 }
  mapping (address => User) public users;
  bool public isOpen;
  string public appName;
}


contract OwnableStorage {
  address public owner;
   constructor()  {
    owner = msg.sender;
  }

  event OwnerUpdate(address _prevOwner, address _newOwner);

  modifier onlyOwner {
    require(msg.sender == owner,"only owner");
    _;
  }

  function transferOwnership(address _newOwner) public onlyOwner {
    require(_newOwner != owner, "Cannot transfer to yourself");
    owner = _newOwner;
  }

}

contract ProxyStorage  {

  /**
   * Current contract to which we are proxing
   */
  address public currentContract;

}

contract Storage is MyAppStorage,OwnableStorage,ProxyStorage {
}

interface IMyApp{
 event RegisterUser(
    address indexed _caller,
    address indexed _deployer
  );
  event eOpen(
    bool indexed rIsOpen
  );
  event logName(string name);

   function updateOpen(bool _isOpen) external;
}

contract MyApp is Storage,IMyApp{


  function initialize() external {
    appName = "AppTest";
  }

  modifier onlyOpen {
    require(isOpen,"only open");
    _;
  }
  // 测试阶段用public,正式环境用external
  function registerUser(address beneficiary) public onlyOpen{
    require(beneficiary != address(0), "invalid address");
    require(users[beneficiary].isMember == false, "address is already register");
    User storage user =  users[beneficiary];
    user.isMember = true;
    emit RegisterUser(msg.sender, beneficiary);
  } 

   function updateOpen(bool _isOpen) override public onlyOwner{
         isOpen = _isOpen;
         emit eOpen(isOpen);
    }


}

proxy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;



//用户信息
contract MyAppStorage {

 struct User{
     uint age;
     uint sex;
     bool isMember;
 }
  mapping (address => User) public users;
  bool public isOpen;
  string public appName;
}


contract OwnableStorage {
  address public owner;
   constructor()  {
    owner = msg.sender;
  }

  event OwnerUpdate(address _prevOwner, address _newOwner);

  modifier onlyOwner {
    require(msg.sender == owner,"only owner");
    _;
  }

  function transferOwnership(address _newOwner) public onlyOwner {
    require(_newOwner != owner, "Cannot transfer to yourself");
    owner = _newOwner;
  }

}

contract ProxyStorage  {

  /**
   * Current contract to which we are proxing
   */
  address public currentContract;

}

contract Storage is MyAppStorage,OwnableStorage,ProxyStorage {
}



contract DelegateProxy {
  /**
   * @dev Performs a delegatecall and returns whatever the delegatecall returned (entire context execution will return!)
   * @param _dst Destination address to perform the delegatecall
   * @param _calldata Calldata for the delegatecall
   */
  function delegatedFwd(address _dst, bytes memory _calldata) internal {
    require(isContract(_dst));
    assembly {
      let result := delegatecall(gas(), _dst, add(_calldata, 0x20), mload(_calldata), 0, 0)
      let size := returndatasize()

      let ptr := mload(0x40)
      returndatacopy(ptr, 0, size)

      // revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas.
      // if the call returned error data, forward it
      switch result case 0 { revert(ptr, size) }
      default { return(ptr, size) }
    }
  }

  function isContract(address _target) view internal returns (bool) {
    uint256 size;
    assembly { size := extcodesize(_target) }
    return size > 0;
  }
}


contract Proxy is Storage, DelegateProxy {
  event UpgradeE(address indexed newContract);
  event Fallback(bytes msgData);

  function Upgrade(address _proxy) public onlyOwner {
     currentContract = _proxy;
  }

  fallback() external payable {
      emit Fallback(msg.data);
      require(isContract(currentContract),"error currentContract");
      delegatedFwd(currentContract,msg.data);
   }
   receive() external payable {  }

}


contract MyAppProxy is  Proxy {}


<think>我们正在讨论Solidity中`delegatecall`和`call`在处理`msg.data`时的区别。根据引用内容,我们可以总结如下: 1. **上下文区别**: - `call`:当合约A使用`call`调用合约B时,执行的上下文是合约B(即被调用合约)。这意味着: - 代码在合约B的上下文中执行(使用合约B的存储和状态) - `msg.sender`和`msg.value`保持不变,但`msg.data`是调用合约B时传入的数据 - `delegatecall`:当合约A使用`delegatecall`调用合约B时,执行的上下文是合约A(即调用合约)。这意味着: - 代码在合约A的上下文中执行(使用合约A的存储和状态) - `msg.sender`、`msg.value`和`msg.data`都保持不变(与原始调用合约A时相同) 2. **`msg.data`的处理**: - 在`call`中,`msg.data`是调用合约B时传入的完整数据(包括函数选择器和参数)。合约B会使用这个数据来执行相应的函数。 - 在`delegatecall`中,`msg.data`保持不变,即原始调用合约A时的数据。这非常重要,因为合约B(被委托调用的合约)执行的函数选择器和参数实际上来自于最初调用合约A的数据。因此,合约B必须具有与合约A兼容的函数签名,否则可能导致执行错误。 3. **实际应用中的区别**: - 使用`call`时,我们通常是在调用另一个合约的函数,并期望该函数改变被调用合约的状态。 - 使用`delegatecall`时,我们是在借用另一个合约的代码来改变当前合约的状态。这常用于代理模式,其中代理合约(使用`delegatecall`)将调用委托给逻辑合约,但状态改变发生在代理合约中。 4. **代码示例**: - `call`示例: ```solidity (bool success, bytes memory data) = someAddress.call{value: msg.value}(msg.data); ``` 这里,`someAddress`是被调用的合约,`msg.data`是当前调用中传入的数据(函数选择器和参数)。这个调用会改变`someAddress`合约的状态。 - `delegatecall`示例: ```solidity (bool success, bytes memory data) = someAddress.delegatecall(msg.data); ``` 这里,`someAddress`是逻辑合约。`msg.data`是最初调用当前合约(代理合约)时传入的数据。执行逻辑合约的代码,但状态改变发生在当前合约(代理合约)中。 5. **注意事项**: - 使用`delegatecall`时,必须确保两个合约的存储布局兼容,否则可能导致状态变量损坏[^4]。 - 在代理模式中,通过`delegatecall`转发`msg.data`时,需要避免存储冲突(通常使用非结构化存储或钻石模式解决)[^3]。 总结:`call`和`delegatecall`在处理`msg.data`时的关键区别在于上下文。`call`会使用新的调用数据(可以重新构造,但通常直接传递原始数据)来执行被调用合约,而`delegatecall`则保持原始的`msg.data`(包括函数选择器和参数)不变,并在调用者合约的上下文中执行被调用合约的代码。 §§相关问题§§ 1. 在代理合约中,如何确保`delegatecall`转发时存储布局兼容? 2. 使用`call`和`delegatecall`时,`msg.sender`分别是什么? 3. 如何手动构造`msg.data`以进行低级调用?</think>### Solidity 中 `delegatecall` 与 `call` 处理 `msg.data` 的区别 #### 1. **核心区别:执行上下文** | 特性 | `call` | `delegatecall` | |--------------|---------------------------------------|---------------------------------------| | **执行环境** | 在被调用合约的上下文中执行 | **在调用者合约的上下文中执行** | | **存储修改** | 修改被调用合约的存储 | 修改调用者合约的存储 | | **`msg.data`** | 保持原始调用数据不变 | **保持原始调用数据不变** | | **`msg.sender`** | 保持原始调用者地址不变 | 保持原始调用者地址不变 | > 关键结论:**两者都完整传递原始 `msg.data`,但执行环境不同**。`delegatecall` 会保留调用者合约的上下文(存储、余额等),而 `call` 切换到被调用合约的上下文[^1][^3]。 --- #### 2. **`msg.data` 传递机制对比 ##### (1) `call` 的工作流程 ```solidity // 合约A调用合约B (bool success, ) = address(B).call{value: msg.value}(msg.data); ``` - ✅ `msg.data` 完整传递给合约B - ❌ 合约B修改的是**自己的存储** - ❌ 合约B的 `this` 指向自身 ##### (2) `delegatecall` 的工作流程 ```solidity // 合约A调用合约B (bool success, ) = address(B).delegatecall(msg.data); ``` - ✅ `msg.data` 完整传递给合约B - ✅ 合约B的代码在**合约A的存储**上执行 - ✅ 合约B的 `this` 指向合约A(调用者) - ✅ 所有状态变量修改发生在合约A中[^3] --- #### 3. 实际应用场景差异 | 场景 | `call` 适用性 | `delegatecall` 适用性 | |---------------------|---------------------------|------------------------------| | **普通合约交互** | ✅ 直接调用外部合约功能 | ❌ 不适用 | | **代理合约** | ❌ 无法实现状态继承 | ✅ **核心机制**(如可升级合约)| | **跨合约状态共享** | ❌ 状态隔离 | ✅ 共享调用者状态 | | **ETH 转账** | ✅ 支持 `{value: X}` | ❌ 无法传递 ETH(但可转发值)| > 代理合约典型实现: ```solidity // 代理合约的fallback函数 fallback() external payable { address impl = implementation; // 逻辑合约地址 (bool success, ) = impl.delegatecall(msg.data); // 关键调用 require(success, "Delegatecall failed"); } ``` 此时 `msg.data` 包含用户请求的函数选择器和参数,在逻辑合约中执行但修改代理合约的存储[^3][^4]。 --- #### 4. 安全注意事项 1. **存储布局兼容性** `delegatecall` 要求调用者和被调用合约的**状态变量声明顺序和类型完全一致**,否则会导致存储冲突[^4]。 2. **重入攻击风险** 两者都需防范重入攻击,建议使用检查-效果-交互模式: ```solidity bool private locked; function safeCall() external { require(!locked, "Reentrancy"); locked = true; (bool success, ) = someAddress.call(msg.data); locked = false; } ``` 3. **Gas 限制** `delegatecall` 会继承调用者的 gas,需注意: ```solidity // 明确指定gas限制 (bool success, ) = impl.delegatecall{gas: 5000}(msg.data); ``` --- #### 5. 为什么两者都保持 `msg.data` 不变? Solidity 设计原则:**低级调用不修改原始调用数据**。无论 `call` 还是 `delegatecall`: - 保持 `msg.sender` 为原始调用者 - 保持 `msg.value` 为原始发送的 ETH - 保持 `msg.data` 为原始调用数据 这确保了调用链的透明性和可追溯性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值