msg.sender 和 address(this) - Solidity

文章通过代码示例介绍了Solidity中msg.sender的用法,并展示了如何利用msg.sender实现权限控制。文章还提到tx.origin的安全问题,并演示了如何获取智能合约的地址。

还记得我们之前那个不好的 ERC20 代币的例子吗?

再看一下


contract ERC20Token {

    mapping(address => uint256) public balances;

    function setSomeonesBalance(
        address owner,
        uint256 amount
    )
        public {
            balances[owner] = amount;
    }

    function transferTokensBetweenAddresses(
        address sender,
        address receiver,
        uint256 amount
    )
        public {
            balances[sender] -= amount;   // 扣除发送方的余额
            balances[receiver] += amount; // 增加接收方的余额
    }
}

问题在于我们不知道是谁在调用这个函数。

幸运的是,Solidity 有一个机制可以识别谁在调用智能合约:msg.sender。msg.sender 返回调用智能合约函数的地址。

在 Remix 中试试以下代码:


contract ExampleContract {
    function whoami()
        public
        view
        returns (address) {
            address sender = msg.sender;
            return sender;
    }
}

它将返回你在 Remix 中使用的测试地址。

现在通过在“ACCOUNT”下拉菜单中选择来更改测试地址。然后再次尝试该函数。返回的地址将会不同。

https://static.wixstatic.com/media/61a666_6911916215e746e4be2a25bdbcf2e9ac~mv2.png/v1/fill/w_939,h_610,al_c,q_95,enc_auto/msgsender.png

通过将 msg.sender 与 if 语句结合,你可以为某些地址赋予特殊权限。

假设我们希望 Remix 中的默认地址成为特殊地址。


contract ERC20Token {
    address public banker = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;

    mapping(address => uint256) public balances;

    function setSomeonesBalance(
        address owner,
        uint256 amount
    )
        public {
            if (msg.sender == banker) {
                balances[owner] = amount;
            }
            // 什么都不做
    }

    function transferTokensBetweenAddresses(
        address sender,
        address receiver,
        uint256 amount
    )
        public {
            if (msg.sender == banker) {
                balances[sender] -= amount;   // 扣除发送方的余额
                balances[receiver] += amount; // 增加接收方的余额
            }
            // 什么都不做
    }
}

上面的代码让人们可以查看他们的余额(因为 balances 是一个公共变量),但只有银行家可以更改它。

通过稍微巧妙一些的设计,我们实际上可以允许人们在不通过银行家的情况下将他们的余额转给其他人。考虑以下示例。


contract ERC20 {
    address public banker = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;

    mapping(address => uint256) public balances;

    function setSomeonesBalance(
        address owner,
        uint256 amount
    )
        public {
            if (msg.sender == banker) {
                balances[owner] = amount;
            }
            // 什么都不做
    }

    function transfer(
        address receiver,
        uint256 amount
    )
        public {
            balances[msg.sender] -= amount;
            balances[receiver] += amount;
    }
}

transfer 函数可以被任何人调用。然而,它只能从 msg.sender 的余额中扣除。作为一个练习,我鼓励你思考为什么使用 transfer 函数不可能窃取他人的余额。

一个自然的问题是,如果有人试图发送超过他们余额的金额会发生什么?如果你使用的是 Solidity 0.8.0 或更高版本,什么都不会发生。交易会被回滚,因为你不能让一个无符号数字变成负数。

tx.origin

还有另一种获取发送者的机制,tx.origin。尽管它的行为与 msg.sender 相似,但你不应该使用它。为了避免一次性轰炸你过多信息,我们现在不解释 tx.origin 的安全问题。但重要的是,除非在非常特定的情况下,否则不要使用 tx.origin。

address(this)

智能合约可以通过以下代码知道自己的地址


contract ExampleContract {

    function whoami()
        public
        view
        returns (address) {
            return address(this);
    }
}

在 Remix 中试试,看地址是否匹配

https://static.wixstatic.com/media/61a666_055fd70f34e24119a3e16f51820845f3~mv2.png/v1/fill/w_939,h_275,al_c,q_95,enc_auto/addressmsg.png

练习问题

WhoCalledMe

了解更多

请查看 区块链培训营,以了解更多关于智能合约开发和代币标准的内容。

  • 原文链接: rareskills.io/learn-soli...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
RareSkills
RareSkills
https://www.rareskills.io/