solidity基础:
基础语法(一)
1)函数定义:
function 函数名(参数类型 参数1, 参数类型 参数2) [访问权限] [读取数据类型函数] returns (返回数据类型){}
访问权限:public 、private (一般private权限的话,参数命名前面加一个_ ,例如:_name)
读取数据类型函数:view函数(只读且不可修改);pure函数(不访问应用里的数据)
2)定义结构体并实例化:
struct Zombie{
string name;
uint age;
}
//建立struct动态数组zombies
Zombie[] zombies;
//实例化
temp = Zombie("jack", 10);
//添加数据进数组
zombies.push(temp);
//一行代码实现添加数据
zombies.push(Zombie("jack", 10));
//结构体作为参数传入:
function _doStuff(Zombie storage _zombie) internal {
}
3)强制类型转换:
uint8 a = 5;
uint b =3;
//强制转换;高位不可直接转低位,需要强制转换
uint8 c = a * uint8(b);
4)事件监听
//创建事件
event work(uint x, uint y, uint result);
function add(uint _x, uint _y) public {
uint result = _x + _y;
//触发事件,做出反应
work(_x, _y, result);
return result;
}
基础语法(二)
1)映射(Mapping)和地址(Address):
mapping(keyType => valueType) [访问权限] 映射名;
例如:
mapping(uint => address) public zombieToOwner ;
mapping(address => uint) ownerZombieCount;
2)全局变量调用函数:
Msg.sender :当前调用者(或智能合约)的address
例如:
mapping (uint => address) public zombieToOwner;
mapping (address => uint) ownerZombieCount;
uint id = zombies.push(Zombie(_name, _dna)) - 1;//id为例子
//更新zombieToOwner值
zombieToOwner[id] = msg.sender;
//为这个 msg.sender 名下的 ownerZombieCount 加 1
ownerZombieCount[msg.sender]++;
3)Require:函数在执行过程中,当不满足某些条件时抛出错误,并停止执行
// 两字符串的 keccak256 哈希值来进行判断)
require(keccak256(_name) == keccak256("Vitalik"));
4)继承:
例如:ZombieFeeding 继承自 ZombieFactory 合约
contract ZombieFeeding is ZombieFactory {
}
5)引入(import):
例如:将 zombiefactory.sol 导入到我们的新文件 zombiefeeding.sol 中
import "./zombiefactory.sol ";
6)storage和memory:
Storage 变量是指永久存储在区块链中的变量。 Memory 变量则是临时的,当外部函数对某合约调用完成时,内存型变量即被移除。
Solidity默认处理:状态变量(在函数之外声明的变量)默认为“存储”形式,并永久写入区块链;而在函数内部声明的变量是“内存”型的,它们函数调用结束后消失。
例如:
//创建一个storage变量 (Zombie为struct类型)
Zombie storage storage;
7)internal和external修饰符:
internal:如果某个合约继承自其父合约,这个合约即可以访问父合约中定义的“内部”函数。
external:函数只能在合约之外调用,它们不能被合约内的其他函数调用。
8)合约接口:
例如:创建KittyInterface 接口
contract KittyInterface {
function getKitty(uint256 _id) external view return(bool isGestating,);
}
使用接口:创建一个名为 kittyContract 的 KittyInterface,并用 ckAddress 为它初始化
KittyContract kittyContract = KittyContract(ckAddress);
9)处理返回多个值:
例如:multipleReturns()函数可以返回3个值
(a, b, c) = multipleReturns();
若只返回c:
( , , c) = multipleReturns();
基础语法(三)
1)智能协议永固性:
代码不可更改和更新
2)onlyOwner函数修饰符;
为了确保函数值能被合约拥有者调用,添加onlyOwner函数修饰符确保
(调用权限限制)
例如:
function likeABoss() external onlyOwner {
}
3)gas:
gas:驱动以太坊DApps的能源(可以通过结构封装节约gas;将相同类型变量放在一起或者尽可能使用最小的整数子类型)
4)时间单位:
变量now可以返回当前的时间戳
5)公有函数和安全性:
检查声明为public和external的函数,若没有onlyOwner,可以在能够使用internal避免安全问题。
6)利用view节约gas:
在所能只读的函数上标记上表示“只读”的“external view 声明,就能为你的玩家减少在 DApp 中 gas 用量。
基础语法(四)
1)可支付函数修饰符:payable
总结:
private 意味着它只能被合约内部调用; internal 就像 private 但是也能被继承的合约调用; external 只能从合约外部调用;最后 public 可以在任何地方调用,不管是内部还是外部。
view 告诉我们运行这个函数不会更改和保存任何数据; pure 告诉我们这个函数不但不会往区块链写数据,它甚至不从区块链读取数据。这两种在被从合约外部调用的时候都不花费任何gas(但是它们在被内部其他函数调用的时候将会耗费gas)。
onlyOwner 和 aboveLevel。 对于这些修饰符我们可以自定义其对函数的约束逻辑。
payable 方法是一种可以接收以太的特殊函数。
例如:
contract OnlineStore {
function buySomething() external payable {
// 检查以确定0.001以太发送出去来运行函数:
require(msg.value == 0.001 ether);
// 如果为真,一些用来向函数调用者发送数字内容的逻辑
transferThing(msg.sender);
}
}
msg.value
是一种可以查看向合约发送了多少以太的方法。
2)提现:
contract GetPaid is Ownable {
function withdraw() external onlyOwner {
owner.transfer(this.balance);
}
}
transfer 函数向一个地址发送以太, 然后 this.balance 将返回当前合约存储了多少以太。
3)随机数(不安全的方式):
uint random = uint(keccak256(now, msg.sender, randNonce));
基础语法(五)
1)ERC721标准和多重继承:
contract ERC721 {
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
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;
}
例如:
// 对 ERC721 和ZombieAttack 的继承
contract ZombieOwnership is ZombieAttack , ZRC721{
}
balanceOf:返回代币数
ownerOf:返回代币所有者
transfer:代币交易
approve:批准交易
takeOwnership:简单地检查以确保 msg.sender 已经被批准来提取这个代币
2)预防溢出
溢出:假设我们有一个 uint8, 只能存储8 bit数据。这意味着我们能存储的最大数字就是二进制 11111111 (或者说十进制的 2^8 - 1 = 255)。
下溢:果你从一个等于 0 的 uint8 减去 1, 它将变成 255 (因为 uint 是无符号的,其不能等于负数)。
安全数学库:SafeMath
使用库:using SafeMath for uint256
SafeMath 库有四个方法:add, sub, mul, 以及 div。
调用:
uint256 =a;
a.add(1);
防止 uint16 和 uint32 溢出或下溢。我们可以将其命名为 SafeMath16 和 SafeMath32。