Solidity-delegateCall插槽冲突分析与解决

delegatecall插槽冲突

1.了解delegatecall的插槽冲突原理 首先严格来说应该不是插槽得冲突问题,而是本身delegate得运行机制问题,因为所有得delegatecall得运行环境都是在当前得被委托得合约里面

2.本身delegatecall他机制运行环境就是这样,所有修改得状态都会根据委托目标地址修改得状态变量内存位置,修改到被委托得合约里面。此委托调用无法修改目标地址得任何状态

3.如何解决

  • 一种解决方案是存储代理合约的数据时避开通常的Solidity存储布局机制,使用 EVM的sstore和sload指令来读取或写入数据到伪随机插槽中
  • 例如,使用由keccak256(my.proxy.version)返回的值作为插槽号。这样我们就可以避免槽位的冲突。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/utils/StorageSlotUpgradeable.sol";
/**
 * @title InitializedProxy
 * @author Anna Carroll
 */
contract InitializedProxy {
    // address of logic contract
    // slot bytes32(uint256(keccak256('EIP1967.PROXY.CONFTI.IMPLEMENTATION')) - 1)
    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x5f62ce3c9aebd463c7a36ab1b244d2bb94f07a2c13889b3b687940ebc467b9b3;

    // ======== Constructor =========
    constructor(
        address logic,
        bytes memory initializationCalldata
    ) { 
        require(logic != address(0),"Proxy :: Logical contracts cannot be zero addresses");
        StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = logic;
        // Delegatecall into the logic contract, supplying initialization calldata
        (bool _ok, bytes memory returnData) =
            logic.delegatecall(initializationCalldata);
        // Revert if delegatecall to implementation reverts
        require(_ok, string(returnData));
    }
     

    // ======== Fallback =========

    fallback() external payable {
        address _impl = StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
        assembly {
            let ptr := mload(0x40)
            calldatacopy(ptr, 0, calldatasize())
            let result := delegatecall(gas(), _impl, ptr, calldatasize(), 0, 0)
            let size := returndatasize()
            returndatacopy(ptr, 0, size)

            switch result
                case 0 {
                    revert(ptr, size)
                }
                default {
                    return(ptr, size)
                }
        }
    }

    // ======== Receive =========

    receive() external payable {} // solhint-disable-line no-empty-blocks
}
  • 另一种方法是使用相同的存储布局和高级别的数据争议解决,就像这个github仓库中的实现。 使用结构体\

问题模拟

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

/** 
 * @title Ballot
 * @dev Implements voting process along with vote delegation
 */
  
contract logic  {
   string public name;
   uint256 public age;
 
   function setName(string memory _name) public {
         name =_name;
   } 
   
}

contract prx{
   string public amount;
   uint256 public higth;
   uint256 public test;
   function _delegatecall(address _traget)public {
       (bool result ,bytes memory  _data)  = 
            _traget.delegatecall(abi.encodeWithSignature("setName(string)","zk"));
   }

}

最后调用setName方法,修改name 会修改到prx得amount上面,因为name得内存位置是0,amount也是0位置。 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在Solidity中,可以通过使用智能合约来实现ERC20代币的锁仓与释放。以下是一个简单的锁仓合约示例: ``` pragma solidity ^0.8.0; import "./IERC20.sol"; import "./SafeMath.sol"; contract TokenVesting { using SafeMath for uint256; address public beneficiary; uint256 public cliff; uint256 public start; uint256 public duration; uint256 public released; IERC20 public token; constructor( address _beneficiary, uint256 _cliff, uint256 _duration, address _token ) public { require(_beneficiary != address(0)); require(_cliff <= _duration); beneficiary = _beneficiary; cliff = _cliff; duration = _duration; start = block.timestamp; token = IERC20(_token); } function release() public { uint256 unreleased = releasableAmount(); require(unreleased > 0); released = released.add(unreleased); token.transfer(beneficiary, unreleased); } function releasableAmount() public view returns (uint256) { return vestedAmount().sub(released); } function vestedAmount() public view returns (uint256) { uint256 currentBalance = token.balanceOf(address(this)); uint256 totalBalance = currentBalance.add(released); if (block.timestamp < start.add(cliff)) { return 0; } else if (block.timestamp >= start.add(duration)) { return totalBalance; } else { return totalBalance.mul(block.timestamp.sub(start)).div(duration); } } } ``` 在这个合约中,当创建合约时,需要传入受益人地址、锁仓期、释放期、代币地址等信息。锁仓期结束后,受益人可以通过调用 `release()` 函数来释放锁仓代币。如果释放函数被调用,但是当前时间还没有到达释放期,则会抛出异常。 为了保证代币不能被提前释放,合约还实现了 cliff 的概念,即在锁仓期结束之前,代币不能被释放。当 cliff 结束之后,代币将按照线性方式释放,直到释放期结束。 需要注意的是,以上示例只是一个简单的锁仓合约示例,实际生产环境中需要更加严格地考虑各种情况和安全性问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zeke链上学堂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值