区块链学习(8)-修饰词payable及有用的全局变量
  n52Qc2vwKhjY 2023年11月02日 75 0

一、在Solidity中,payable是一个修饰符,用于指定一个函数可以接收token(Ether)转账。当一个合约的函数被标记为payable时,用户可以在调用该函数时向合约发送token。如果没有将函数标记为payable,则调用函数时发送token会导致错误。

如下案例:

pragma solidity ^0.8.0;
contract PayableExample {
    // 事件,用于记录接收到的token
    event Received(address sender, uint256 amount);
    // 接收token的payable函数
    function receive() external payable {
        // 当用户调用此函数时,可以向合约发送token
        // 函数内部可以通过msg.value获取发送的token数量(单位:wei)
        uint256 receivedAmount = msg.value;

        // 触发事件,记录接收到的token
        emit Received(msg.sender, receivedAmount);
    }
}

在这个demo中创建了一个名为PayableExample的智能合约,该合约包含一个名为receivepayable函数。用户可以调用这个函数并向合约发送token。函数内部通过msg.value获取发送的token数量,并使用emit关键字触发Received事件,记录接收到的token。

注意,在Solidity 0.4.0之前的版本中,合约可以直接接收token,而无需将函数标记为payable。但在0.4.0及之后的版本中,必须使用payable修饰符来明确指定哪些函数可以接收token。这样做是为了提高安全性,防止意外地向合约发送token。

注意:

在Solidity中,function() public payable { }表示一个可接收以太token的默认函数,这是在旧版本(0.6.0之前)的Solidity中定义的。从Solidity 0.6.0开始,默认函数被分为两类:fallback函数和receive函数。以下是默认函数在不同版本的Solidity中的定义:

  1. 在Solidity 0.6.0及之后的版本中:
  • fallback函数:当合约收到一个没有匹配到任何其他函数的调用时,会触发fallback函数。这通常是因为发送的数据无法匹配合约中任何已定义的函数,或者调用为空(没有数据)。定义fallback函数的语法如下:
fallback() external payable { ... }
  • receive函数:当合约接收到一个普通转账(即发送的交易没有数据)时,会触发receive函数。定义receive函数的语法如下:
receive() external payable { ... }
  1. 在Solidity 0.6.0之前的版本中:
  • 默认函数:当合约收到一个没有匹配到任何其他函数的调用时,会触发默认函数。这包括普通转账和发送无法匹配到已定义函数的数据。定义默认函数的语法如下:
function() public payable { ... }

默认函数的作用:

  • 可接收以太token:默认函数可以接收以太token,因此合约可以接收并存储发送的资金。请注意,在没有定义payable的默认函数(或fallbackreceive函数)的情况下,合约将拒绝接收以太token。
  • 动态调用:默认函数还可用于在合约之间进行动态调用。当与其他合约交互时,如果目标合约的ABI未知或可能发生变化,可以使用默认函数进行动态调用。这通常通过calldelegatecallstaticcall等底层函数实现。

请注意,fallback函数和receive函数的引入是为了提高安全性和可读性。在设计新的合约时,建议使用Solidity 0.6.0及更高版本,并使用fallbackreceive函数替代旧版本的默认函数。

二、关于msg.value

msg.value是一个特殊的全局变量,表示在当前函数调用中发送的token数量(单位:wei)。msg.value仅在调用payable函数时才有意义,因为只有payable函数才能接收token。以下是msg.value的用法案例:

pragma solidity ^0.8.0;
contract Deposit {
    mapping(address => uint256) public balances;
    event DepositMade(address indexed depositor, uint256 amount);

    function deposit() external payable {
        require(msg.value > 0, "Deposit amount must be greater than zero.");
        balances[msg.sender] += msg.value;
        emit DepositMade(msg.sender, msg.value);
    }
 }

在这个示例中,我们创建了一个名为Deposit的智能合约,该合约包含一个deposit函数。用户可以调用这个payable函数并向合约发送token。函数内部通过msg.value获取发送的token数量,并将其添加到用户的余额中。同时,函数触发了DepositMade事件来记录存款操作。

remix中value就是当前0x5B...地址对应的要发送出的数量,如下:

区块链学习(8)-修饰词payable及有用的全局变量_访问控制

注意事项:

  1. msg.value的单位是wei(1 Ether = 10^18 wei),所以在处理大额数值时,需要注意数字溢出的问题。Solidity 0.8.0及更高版本会自动进行溢出检查,但在更早的版本中,建议使用SafeMath库以避免溢出问题。
  2. 当接收token时,应确保只有payable函数才能访问msg.value。这有助于提高合约安全性,防止意外地向合约发送token。
  3. 在处理token时,请确保合约逻辑正确处理了msg.value,避免损失或者被黑客利用的可能性。在转移token时,使用transfersendcall{value: ...}()方法,并检查可能的异常。
  4. 请注意gas消耗。处理msg.value的函数可能会因gas消耗过高而失败。在设计智能合约时,考虑到gas消耗和限制可能带来的问题。

总之,msg.value表示当前函数调用中发送的token数量。在使用msg.value时,请注意其单位(wei)以及相关的安全问题。确保只有payable函数才能访问msg.value,并在设计智能合约时关注gas消耗。

三、关于msg.sender

在Solidity中,msg.sender是一个特殊的全局变量,表示当前函数调用的发起者(也就是发送交易的Ethereum地址)。msg.sender在许多场景中都非常有用,例如验证权限、记录交易发起者或分配token等。以下是msg.sender的用法示例:

pragma solidity ^0.8.0;
contract SimpleToken {
    mapping(address => uint256) public balances;
    address public owner;
    constructor() {
        owner = msg.sender;
        balances[msg.sender] = 1000;
    }

    function transfer(address to, uint256 amount) external {
        require(balances[msg.sender] >= amount, "Insufficient balance.");
        balances[msg.sender] -= amount;
        balances[to] += amount;
    }
}

这是一个简单的合约。合约的构造函数将msg.sender设置为合约的拥有者,并分配初始token。transfer函数允许合约的拥有者将token转移到其他地址。函数内部通过msg.sender检查调用者的余额是否充足,然后进行转账操作。

注意事项:

  1. msg.sender可以被潜在的操控。在设计合约时,请考虑恶意用户可能伪造msg.sender以试图利用合约漏洞的情况。始终使用适当的访问控制和验证以确保安全。
  2. 请注意重入漏洞。当合约与其他合约交互时,恶意合约可能在调用期间再次调用原合约,从而导致意外的行为。要避免重入,可以使用锁(例如reentrancyGuard)或先进行状态变更,然后再调用外部合约。
  3. 谨慎使用msg.sender作为唯一验证方式。在某些情况下,使用msg.sender可能不足以保证安全性。例如,当涉及到权限管理时,可以考虑使用角色管理库(如OpenZeppelin的AccessControl)以提供更强大的访问控制功能。

总之,msg.sender表示当前函数调用的发起者。在使用msg.sender时,请注意安全问题,特别是潜在的操控和重入。使用适当的访问控制和验证机制以确保合约安全。


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
n52Qc2vwKhjY