这些修饰符可以同时作用于一个函数定义上:
function test() external view onlyOwner anotherModifier {
}
payable是一个新的函数修饰符.
payable 方法是让 Solidity 和以太坊变得如此酷的一部分 —— 它们是一种可以接收以太的特殊函数。
先放一下。当你在调用一个普通网站服务器上的API函数的时候,你无法用你的函数传送美元——你也不能传送比特币。
但是在以太坊中, 因为钱 (以太), 数据 (事务负载), 以及合约代码本身都存在于以太坊。你可以在同时调用函数 并付钱给另外一个合约。
这就允许出现很多有趣的逻辑, 比如向一个合约要求支付一定的钱来运行一个函数。
eg.
contract OnlineStore {
function buySomething() external payable {
// 检查以确定0.001以太发送出去来运行函数:
require(msg.value == 0.001 ether);
// 如果为真,一些用来向函数调用者发送数字内容的逻辑
transferThing(msg.sender);
}
}
在这里,msg.value 是一种可以查看向合约发送了多少以太的方法,另外 ether是一个內建单元。
这里发生的事是,一些人会从 web3.js 调用这个函数 (从DApp的前端), 像这样 :
// 假设 `OnlineStore` 在以太坊上指向你的合约:
OnlineStore.buySomething().send(from: web3.eth.defaultAccount, value: web3.utils.toWei(0.001))
注意这个 value字段, JavaScript 调用来指定发送多少(0.001)以太。如果把事务想象成一个信封,你发送到函数的参数就是信的内容。 添加一个 value 很像在信封里面放钱 —— 信件内容和钱同时发送给了接收者。
注意: 如果一个函数没标记为payable, 而你尝试利用上面的方法发送以太,函数将拒绝你的事务。
知道了如何向合约发送以太,那么在发送之后会发生什么呢?
在你发送以太之后,它将被存储进以合约的以太坊账户中, 并冻结在哪里 —— 除非你添加一个函数来从合约中把以太提现。
你可以写一个函数来从合约中提现以太,类似这样:
contract GetPaid is Ownable {
function withdraw() external onlyOwner {
owner.transfer(this.balance);
}
}
注意我们使用 Ownable 合约中的 owner 和 onlyOwner,假定它已经被引入了。
你可以通过 transfer 函数向一个地址发送以太, 然后 this.balance 将返回当前合约存储了多少以太。 所以如果100个用户每人向我们支付1以太, this.balance 将是100以太。
你可以通过 transfer 向任何以太坊地址付钱。 比如,你可以有一个函数在 msg.sender 超额付款的时候给他们退钱:
uint itemFee = 0.001 ether;
msg.sender.transfer(msg.value - itemFee);
或者在一个有卖家和卖家的合约中, 你可以把卖家的地址存储起来, 当有人买了它的东西的时候,把买家支付的钱发送给它 seller.transfer(msg.value)。
ERC20标准:
contract ERC20 {
function totalSupply() constant returns (uint totalSupply); //获取总的发行量
function balanceOf(address _owner) constant returns (uint balance); //查询账户余额
function transfer(address _to, uint _value)returns(bool success); // 发送Token到某个地址(转账)
function transferFrom(address _from, address _to, uint _value) returns (bool success); //从地址from 发送token到to地址
function approve(address _spender, uint _value)returns(bool success);//允许_spender从你的账户转出token
function allowance(address _owner, address _spender) constant returns (uint remaining);//查询允许spender转移的Token数量
event Transfer(address indexed _from, address indexed _to, uint _value);//transfer方法调用时的通知事件
event Approval(address indexed _owner, address indexed _spender, uint _value); //approve方法调用时的通知事件
}
ERC721标准
contract ERC721 {
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);//当成功转移token时,一定要触发Transfer事件
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);//当调用approval函数成功时,一定要触发Approval事件
function balanceOf(address _owner) public view returns (uint256 _balance);//返回传入address地址拥有多少代币
function ownerOf(uint256 _tokenId) public view returns (address _owner);//传入一个代币Id作为参数,返回该代币拥有者的地址address
function transfer(address _to, uint256 _tokenId) public;//代币的拥有者调用这个方法,传入他想转移到的address和他想转移的代币Id
function approve(address _to, uint256 _tokenId) public;//代币的拥有者调用该方法,参数同上,该合约会存储谁被允许提取代币,通常存储到一个mapping (uint256 => address)里.然后,当有人调用takeOwnership时,合约会检查msg.sender 是否得到拥有者的批准来提取代币,如果是,则将代币转移给他。
function takeOwnership(uint256 _tokenId) public;//transfer 和 takeOwnership 都将包含相同的转移逻辑,只是以相反的顺序。 (一种情况是代币的发送者调用函数;另一种情况是代币的接收者调用它)
}
在实现一个代币合约的时候,我们首先要做的是将接口复制到它自己的 Solidity 文件并导入它,import ./erc721.sol。 接着,让我们的合约继承它,然后我们用一个函数定义来重写每个方法。
在Solidity中,合约可以继承多个合约.
contract ZombieOwnership is ZombieAttack, ERC721 {
function balanceOf(address _owner) public view returns (uint256 _balance) {
}
function ownerOf(uint256 _tokenId) public view returns (address _owner) {
}
function transfer(address _to, uint256 _tokenId) public {
}
function approve(address _to, uint256 _tokenId) public {
}
function takeOwnership(uint256 _tokenId) public {
}
}
使用 approve 或者 takeOwnership 的时候,转移有2个步骤:
你,作为所有者,用新主人的 address 和你希望他获取的 _tokenId 来调用 approve
新主人用 _tokenId 来调用 takeOwnership,合约会检查确保他获得了批准,然后把代币转移给他。
因为这发生在2个函数的调用中,所以在函数调用之间,我们需要一个数据结构来存储什么人被批准获取什么。
用户不会不小心把他们的僵尸转移给0 地址(这被称作 “烧币”, 基本上就是把代币转移到一个谁也没有私钥的地址,让这个代币永远也无法恢复)。
https://openzeppelin.org/ 一个智能合约审查服务商
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}