映射 mapping: 可以理解为一个2个字段的表。
【function 类型】internal/external/public/private:
private
意味着它只能被合约内部调用; internal
就像 private
但是也能被继承的合约调用; external
只能从合约外部调用;最后 public
可以在任何地方调用,不管是内部还是外部。
Ownerable 合约:
*/
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() public {
owner = msg.sender;
}
Ownable
合约基本都会这么干:
-
合约创建,构造函数先行,将其
owner
设置为msg.sender
(其部署者) -
为它加上一个修饰符
onlyOwner
,它会限制陌生人的访问,将访问某些函数的权限锁定在owner
上。 -
允许将合约所有权转让给他人。
modifier 函数修饰符:
onlyOwner
函数修饰符是这么用的:
contract MyContract is Ownable { event LaughManiacally(string laughter); //注意! `onlyOwner`上场 : function likeABoss() external onlyOwner { LaughManiacally("Muahahahaha"); } }
注意 likeABoss
函数上的 onlyOwner
修饰符。 当你调用 likeABoss
时,首先执行 onlyOwner
中的代码, 执行到 onlyOwner
中的 _;
语句时,程序再返回并执行 likeABoss
中的代码。
可见,尽管函数修饰符也可以应用到各种场合,但最常见的还是放在函数执行之前添加快速的 require
检查。
变量建立原则:尽量使用最小的整数子类型以节约空间。 并且把同样类型的变量放一起(即在 struct 中将把变量按照类型依次放置),这样 Solidity 可以将存储空间最小化。
internal 和 internal view 的区别:
view
函数不会真正改变区块链上的任何数据 - 它们只是读取。因此用 view
标记一个函数,意味着告诉 web3.js
,运行这个函数只需要查询你的本地以太坊节点,而不需要在区块链上创建一个事务(事务需要运行在每个节点上,因此花费 gas)。
注意:如果一个 view
函数在另一个函数的内部被调用,而调用函数与 view
函数的不属于同一个合约,也会产生调用成本。这是因为如果主调函数在以太坊创建了一个事务,它仍然需要逐个节点去验证。所以标记为 view
的函数只有在外部调用时才是免费的。
我们也有状态修饰符, 告诉我们函数如何和区块链交互: view
告诉我们运行这个函数不会更改和保存任何数据; pure
告诉我们这个函数不但不会往区块链写数据,它甚至不从区块链读取数据。这两种在被从合约外部调用的时候都不花费任何gas(但是它们在被内部其他函数调用的时候将会耗费gas)。
传入指针参数时候使用 XXX storge XXX 传入的是结构体指针。
ERC721 规范有两种不同的方法来转移代币:transfer:_from调用,修改币的所有者mapping,调用Transfer event。approve:_from调用,建立即将拥有币的所有者的mapping。tankownership:_To调用,查询Approve mapping 的关系并调用transfer。
library
关键字 — 库和 合约
很相似,但是又有一些不同。 就我们的目的而言,库允许我们使用 using
关键字,它可以自动把库的所有方法添加给一个数据类型。
assert
和 require
相似,若结果为否它就会抛出错误。 assert
和 require
区别在于,require
若失败则会返还给用户剩下的 gas, assert
则不会。所以大部分情况下,你写代码的时候会比较喜欢 require
,assert
只在代码可能出现严重错误的时候使用,比如 uint
溢出。
Solidity 社区所使用的一个标准是使用一种被称作 natspec 的格式,看起来像这样:
/// @title 一个简单的基础运算合约
/// @author H4XF13LD MORRIS ?????
/// @notice 现在,这个合约只添加一个乘法
contract Math {
/// @notice 两个数相乘
/// @param x 第一个 uint
/// @param y 第二个 uint
/// @return z (x * y) 的结果
/// @dev 现在这个方法不检查溢出
function multiply(uint x, uint y) returns (uint z) {
// 这只是个普通的注释,不会被 natspec 解释
z = x * y;
}
}
@title
(标题) 和 @author
(作者)很直接了.
@notice
(须知)向 用户 解释这个方法或者合约是做什么的。 @dev
(开发者) 是向开发者解释更多的细节。
@param
(参数)和 @return
(返回) 用来描述这个方法需要传入什么参数以及返回什么值。
注意你并不需要每次都用上所有的标签,它们都是可选的。不过最少,写下一个 @dev
注释来解释每个方法是做什么的。