@@@ Mapping 映射
这是solidity语言中提供的一种强大的数据结构,作用就像传统面向对象编程语言C++、Java中的map一样。
关键字:mapping
// For a financial app, storing a uint that holds the user's account balance:
mapping (address => uint) public accountBalance;
// Or could be used to store / lookup usernames based on userId
mapping (uint => string) userIdToName;
@@@ 关键字 address
是一种账户地址,以太坊中有两类账户:用户账户与合约账户,在solidity中均是用address来声明定义。
其值是一种16进制的字符串,类似下面这样:
0x0cE446255506E92DF41614C46F1d6df9Cc969183
@@@ 全局变量 msg.sender
全局变量是在solidity中的任何function均可直接访问的变量。msg.sender的值是函数调用者的账户地址,这里的调用者既可以是具体链上的某个用户,也可以是其他智能合约。
使用例子
mapping (address => uint) favoriteNumber;
function setMyNumber(uint _myNumber) public {
// Update our `favoriteNumber` mapping to store `_myNumber` under `msg.sender`
favoriteNumber[msg.sender] = _myNumber;
// ^ The syntax for storing data in a mapping is just like with arrays
}
function whatIsMyNumber() public view returns (uint) {
// Retrieve the value stored in the sender's address
// Will be `0` if the sender hasn't called `setMyNumber` yet
return favoriteNumber[msg.sender];
}
@@@关键字require
类似高级语言中的断言,用来判断条件是否满足,不满足则将停止继续运行并且向外抛出异常。
用法:
require(keccak256(_name) == keccak256("Vitalik"));
如果变量_name的hash值跟"Vitalik"的hash值不相等,则会抛出异常。
@@@合约继承(inheritance)
关键字: is
概念类似其他高级语言,如C++,Java, 子合约会继续父合约的public方法。
例子:
contract Doge {
function catchphrase() public returns (string) {
return "So Wow CryptoDoge";
}
}
contract BabyDoge is Doge {
function anotherCatchphrase() public returns (string) {
return "Such Moon BabyDoge";
}
}
@@@ 关键字import
用于引用其他solidity代码文件,类似于C++中的#include的作用。
例子:
import "./someothercontract.sol";
contract newContract is SomeOtherContract {
}
@@@关键字 storage VS memory
storage : 用此关键字修饰的变量,意味着会持久化存储在区块链上,类似中电脑中的硬盘存储;
memory: 用此关键字修饰的变量,意味着只是存储在内存中,在不同的外部函数调用 当前合约间会被擦除。
大部分情况,不用我们显示的使用这两种关键字,solidity会有默认的处理方式:比如合约内的变量(函数外的变量),默认就是storage,而函数内的变量,则默认是memory的,当离开函数时,这些变量就会消失。而有一些特殊情况,则是需要显示使用这两个变量的。
例子:
contract SandwichFactory {
struct Sandwich {
string name;
string status;
}
Sandwich[] sandwiches;
function eatSandwich(uint _index) public {
// Sandwich mySandwich = sandwiches[_index];
// ^ Seems pretty straightforward, but solidity will give you a warning
// telling you that you should explicitly declare `storage` or `memory` here.
// So instead, you should declare with the `storage` keyword, like:
Sandwich storage mySandwich = sandwiches[_index];
// ...in which case `mySandwich` is a pointer to `sandwiches[_index]`
// in storage, and...
mySandwich.status = "Eaten!";
// ...this will permanently change `sandwiches[_index]` on the blockchain.
// If you just want a copy, you can use `memory`:
Sandwich memory anotherSandwich = sandwiches[_index + 1];
// ...in which case `anotherSandwich` will simply be a copy of the
// data in memory, and...
anotherSandwich.status = "Eaten!";
// ...will just modify the temporary variable and have no effect
// on `sandwiches[_index + 1]`. But you can do this:
sandwiches[_index + 1] = anotherSandwich;
// ...if you want to copy the changes back into blockchain storage.
}
}
@@@函数可见性修饰符
除第一课中提到的public 与private 外,solidity中还有两种函数可见性修饰符:internal 和 external。
internal与private很类似,不同点在于被internal修饰的函数可以被子合约调用,而后者不可以;
external与public类似,不同点在于被external修饰的函数只能在合约外被调用,合约内的方法不能调用此方法。
contract Sandwich {
uint private sandwichesEaten = 0;
function eat() internal {
sandwichesEaten++;
}
}
contract BLT is Sandwich {
uint private baconSandwichesEaten = 0;
function eatWithBacon() public returns (string) {
baconSandwichesEaten++;
// We can call this here because it's internal
eat();
}
}
@@@定义接口合约
contract NumberInterface {
function getNum(address _myAddress) public view returns (uint);
}
接口使用例子:
contract MyContract {
address NumberInterfaceAddress = 0xab38...
// ^ The address of the FavoriteNumber contract on Ethereum
NumberInterface numberContract = NumberInterface(NumberInterfaceAddress);
// Now `numberContract` is pointing to the other contract
function someFunction() public {
// Now we can call `getNum` from that contract:
uint num = numberContract.getNum(msg.sender);
// ...and do something with `num` here
}
}
@@@ returns 函数里一次返回多个值
function getKitty(uint256 _id) external view returns (
bool isGestating,
bool isReady,
uint256 cooldownIndex,
uint256 nextActionAt,
uint256 siringWithId,
uint256 birthTime,
uint256 matronId,
uint256 sireId,
uint256 generation,
uint256 genes
) {
Kitty storage kit = kitties[_id];
// if this variable is 0 then it's not gestating
isGestating = (kit.siringWithId != 0);
isReady = (kit.cooldownEndBlock <= block.number);
cooldownIndex = uint256(kit.cooldownIndex);
nextActionAt = uint256(kit.cooldownEndBlock);
siringWithId = uint256(kit.siringWithId);
birthTime = uint256(kit.birthTime);
matronId = uint256(kit.matronId);
sireId = uint256(kit.sireId);
generation = uint256(kit.generation);
genes = kit.genes;
}
function multipleReturns() internal returns(uint a, uint b, uint c) {
return (1, 2, 3);
}
function processMultipleReturns() external {
uint a;
uint b;
uint c;
// This is how you do multiple assignment:
(a, b, c) = multipleReturns();
}
// Or if we only cared about one of the values:
function getLastReturnValue() external {
uint c;
// We can just leave the other fields blank:
(,,c) = multipleReturns();
}
@@@ 第二课例子代码
pragma solidity ^0.4.19;
import "./zombiefactory.sol";
contract KittyInterface {
function getKitty(uint256 _id) external view returns (
bool isGestating,
bool isReady,
uint256 cooldownIndex,
uint256 nextActionAt,
uint256 siringWithId,
uint256 birthTime,
uint256 matronId,
uint256 sireId,
uint256 generation,
uint256 genes
);
}
contract ZombieFeeding is ZombieFactory {
address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
KittyInterface kittyContract = KittyInterface(ckAddress);
// Modify function definition here:
function feedAndMultiply(uint _zombieId, uint _targetDna,string _species) public {
require(msg.sender == zombieToOwner[_zombieId]);
Zombie storage myZombie = zombies[_zombieId];
_targetDna = _targetDna % dnaModulus;
uint newDna = (myZombie.dna + _targetDna) / 2;
// Add an if statement here
if (keccak256(_species) == keccak256("kitty")) {
newDna = newDna - newDna % 100 + 99;
}
_createZombie("NoName", newDna);
}
function feedOnKitty(uint _zombieId, uint _kittyId) public {
uint kittyDna;
(,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
// And modify function call here:
feedAndMultiply(_zombieId, kittyDna);
}
}
pragma solidity ^0.4.19;
contract ZombieFactory {
event NewZombie(uint zombieId, string name, uint dna);
uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
struct Zombie {
string name;
uint dna;
}
Zombie[] public zombies;
mapping (uint => address) public zombieToOwner;
mapping (address => uint) ownerZombieCount;
function _createZombie(string _name, uint _dna) internal {
uint id = zombies.push(Zombie(_name, _dna)) - 1;
zombieToOwner[id] = msg.sender;
ownerZombieCount[msg.sender]++;
NewZombie(id, _name, _dna);
}
function _generateRandomDna(string _str) private view returns (uint) {
uint rand = uint(keccak256(_str));
return rand % dnaModulus;
}
function createRandomZombie(string _name) public {
require(ownerZombieCount[msg.sender] == 0);
uint randDna = _generateRandomDna(_name);
randDna = randDna - randDna % 100;
_createZombie(_name, randDna);
}
}