SOLIDITY关键字EMIT

引入emit背景:

ERC20 token标准介绍了一种Transfer事件以及一个transfer()方法。
它们的调用语法不完全相同:
transfer(address to, uint value);
Transfer(address from, address to, uint256 _value);

但是这种相似足够引起混淆。
对未来的Solidity程序员来说这是一个很严重的问题,必须避免意外地将外部调用函数映射到一个
名字相似的事件上,而这导致了去年的DAO攻击。有人建议在事件名前面加上Log前缀来标识以避免将函数和事件混淆,但是最后还是决定引进一个新的关键字emit。

所以:
event Transfer(address from, address to, uint256 _value);
// …
Transfer(from, to, value);
就变为了:

event Transfer(address from, address to, uint256 _value);
// …
emit Transfer(from, to, value);

这就能够让函数调用和事件日志之间具备了语义上的不同。

Transfer事件:
当token被转移的时候必须触发该事件,包括零值转移。
一个创建新token的合约在给_from地址赋0x0值时必须触发一个Transfer事件event Transfer(address indexed _from, address indexed _to, uint256 _value)

transfer方法:
转移_value个token到地址_to,必须激活Transfer事件,若_from账户余额token不足,则该函数应该抛出异常。注意零值转移必须和普通转移一样必须激活Transfer事件

function transfer(address _to, uint256 _value) returns (bool success)

最近3月8日的版本v0.4.21,引进emit关键字来触发事件,这有助于分清功能和事件,这也是之前遭遇DAO攻击导致以太坊硬分叉并催生经典以太坊ETC的原因之一。

根据solidity版本的注释可知:
一般:支持并推荐使用emit EventName()来明确地调用事件。为了让事件较常规函数调用更突出,应该是用emit EventName()而不是EventName()
来调用事件。
下面这个实例用于触发一个事件:
pragma solidity ^0.4.21;
contract ClientReceipt {
event Deposit(
address indexed _from,
bytes32 indexed _id,
uint _value
); //声明一个事件
function deposit(bytes32 _id) public payable {
// Events are emitted using `emit`, followed by
// the name of the event and the arguments
// (if any) in parentheses. Any such invocation
// (even deeply nested) can be detected from
// the JavaScript API by filtering for `Deposit`.
//事件被emit触发,括号内有事件名参数和其他参数,通过JS API过滤Deposit可检测到任何类似的调用
//(即使是深度嵌套也能够检测到)
emit Deposit(msg.sender, _id, msg.value);
}
}

这里必须确保编译器版本为0.4.12及以上,若较低的版本编译器会抛出错误。

### Solidity 编程最佳实践与教程 #### 1. 数据类型的合理使用 Solidity 提供了多种数据类型,从基本的布尔值、整数到更复杂的映射和数组。为了提高代码的安全性和效率,在定义变量时应尽可能指定具体的数据类型并设置合理的大小范围[^1]。 例如,当需要存储一个小于 256 的无符号整数值时,可以选择 `uint8` 而不是通用的 `uint` 类型: ```solidity // 不推荐的方式 uint public myNumber; // 推荐的方式 uint8 public myNumber; ``` 这种做法不仅节省 gas 成本,还能减少潜在溢出风险。 #### 2. 函数权限管理 在编写智能合约时,确保只有授权方才能执行某些敏感操作是非常重要的。可以通过修饰符(modifiers)来实现这一目标。比如下面的例子展示了如何创建一个仅允许合同所有者调用的方法: ```solidity contract Owned { address owner; constructor() { owner = msg.sender; // 将部署者的地址设为owner } modifier onlyOwner { require(msg.sender == owner, "Caller is not the owner"); _; } } ``` 任何继承自该类别的其他合约都可以利用此功能保护特定方法不被未认证用户访问。 #### 3. 错误处理机制 良好的错误处理对于防止程序崩溃至关重要。Solidity 支持通过 revert 或 throw 关键字中断交易,并返回异常消息给客户端应用程序。此外还可以抛出自定义事件以便追踪问题所在位置。 ```solidity function withdraw(uint amount) external payable{ if (balanceOf[msg.sender] >= amount){ balanceOf[msg.sender]-=amount; msg.sender.transfer(amount); }else{ emit InsufficientFunds(); revert('Insufficient funds'); } } ``` 上述例子中如果账户余额不足,则会触发 `revert()` 并终止当前事务的同时发送一条日志通知前端界面显示相应提示信息。 #### 4. 使用库简化开发流程 除了内置的功能外,Solidity 还允许我们引入外部库文件进一步扩展其能力。这些预编写的模块可以帮助程序员快速搭建常用组件而无需重复造轮子。例如 OpenZeppelin 是一个非常流行的开源框架集合了许多经过审计验证过的标准接口可供直接引用: ```bash npm install @openzeppelin/contracts ``` 之后就可以轻松集成 ERC20 token 创建等功能模块至自己的项目当中去了. #### 5. 测试驱动开发(TDD) 最后但同样重要的一点就是坚持采用测试先行的原则来进行整个软件生命周期内的迭代改进工作。借助 Ganache-cli 模拟私有链环境配合 Mocha 和 Chai 断言库一起完成单元测验覆盖率达到90%以上再考虑上线正式版本发布出去接受公众检验[^3]: ```javascript const assert = require('assert'); const ganache = require('ganache-cli'); const Web3 = require('web3'); let accounts; let inboxContractInstance; beforeEach(async () => { web3 = new Web3(ganache.provider()); accounts = await web3.eth.getAccounts(); const InboxFactory = artifacts.require('Inbox'); inboxContractInstance = await InboxFactory.new("Hello World",{from:accounts[0]}); }); describe('Inbox Contract', function(){ it('deploys a contract', ()=>{ assert.ok(inboxContractInstance.address); }); }); ``` ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值