Solidity學習記錄
第一章 創建生產僵尸的工廠
第二章 設定僵尸的攻擊功能
第三章 撰寫DAPP所需的基礎理論
第四章 完善僵尸功能
第五章 ERC721 標準和加密資產
文章目錄
- Solidity學習記錄
- 前言
- 一、本章主要目的
- 二、學習程序
- 1.本節課程知識點
- 2.最終代碼
- 總結
前言
這應該是Solidity學習記錄的最后一章,這五章內容足夠應對學生上課的需要,不需要發布的程式用這些知識編程足以,如果需要發布軟體,還需要有前端知識HTML, JavaScript以及 JQuery 等來寫網站,有需要的可以自學,這里不做詳細敘述了,一、本章主要目的
作為Solidity知識的最后一章,這章主要介紹了代幣, ERC721 標準, 以及加密資產,預防溢位等增強程式安全性的操作,
二、學習程序
1.本節課程知識點
1、一個 代幣 在以太坊基本上就是一個遵循一些共同規則的智能合約——即它實作了所有其他代幣合約共享的一組標準函式,在智能合約內部,通常有一個映射,mapping(address => uint256) balances,用于追蹤每個地址還有多少余額,所以基本上一個代幣只是一個追蹤誰擁有多少該代幣的合約,和一些可以讓那些用戶將他們的代幣轉移到其他地址的函式,
2、代幣標準這里介紹兩種:ERC20 代幣和ERC721 代幣,主要區別:ERC20 代幣可以分割,可以發給對方0.025以太(類似于數字支付,可以支付小數);ERC721 代幣是不能互換的,因為每個代幣都被認為是唯一且不可分割的,只能以整個單位交易它們,并且每個單位都有唯一的 ID(類似于紙幣,不能撕碎了交易——撕毀人民幣違法——只能整張使用),
3、由于所有 ERC20 代幣共享具有相同名稱的同一組函式,它們都可以以相同的方式進行互動,這意味著如果你構建的應用程式能夠與一個 ERC20 代幣進行互動,那么它就也能夠與任何 ERC20 代幣進行互動,
(當交易所添加一個新的 ERC20 代幣時,實際上它只需要添加與之對話的另一個智能合約, 用戶可以讓那個合約將代幣發送到交易所的錢包地址,然后交易所可以讓合約在用戶要求取款時將代幣發送回給他們,)
4、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目前是一個草稿,還沒有正式商定的實作,在本教程中,我們使用的是 OpenZeppelin 庫中的當前版本,但在未來正式發布之前它可能會有更改, 所以把這 一個可能的實作當作考慮,但不要把它作為 ERC721 代幣的官方標準,)
5、在Solidity中使用多重繼承的時候,你只需要用逗號 , 來隔開幾個你想要繼承的合約,例如:
contract ZombieOwnership is ZombieAttack, ERC721{},
6、我們正在用 ERC721 代幣標準,意味著其他合約將期望我們的合約以這些確切的名稱來定義函式,這就是這些標準實用的原因——如果另一個合約知道我們的合約符合 ERC721 標準,它可以直接與我們互動,而無需了解任何關于我們內部如何實作的細節,
7、ERC721 規范有兩種不同的方法來轉移代幣:
function transfer(address _to, uint256 _tokenId) public;
function approve(address _to, uint256 _tokenId) public;
function takeOwnership(uint256 _tokenId) public;
第一種方法是代幣的擁有者呼叫transfer 方法,傳入他想轉移到的 address 和他想轉移的代幣的 _tokenId,
第二種方法是代幣擁有者首先呼叫 approve,然后傳入與以上相同的引數,接著,該合約會存盤誰被允許提取代幣,通常存盤到一個 mapping (uint256 => address) 里,然后,當有人呼叫 takeOwnership 時,合約會檢查 msg.sender 是否得到擁有者的批準來提取代幣,如果是,則將代幣轉移給他,
(一種情況是代幣的發送者呼叫函式;另一種情況是代幣的接收者呼叫它,)
8、合約安全增強: 預防溢位和下溢:
為了防止這些情況,OpenZeppelin 建立了一個叫做 SafeMath 的 庫(library),默認情況下可以防止這些問題,一個庫是 Solidity 中一種特殊的合約,其中一個有用的功能是給原始資料型別增加一些方法,SafeMath 庫有四個方法 — add, sub, mul, 以及 div,現在我們可以這樣來讓 uint256 呼叫這些方法:
using SafeMath for uint256;
uint256 a = 5;
uint256 b = a.add(3); // 5 + 3 = 8
uint256 c = a.mul(2); // 5 * 2 = 10
9、SafeMath 的部分代碼:
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;
}
}
首先我們有了 library 關鍵字 — 庫和 合約很相似,但是又有一些不同, 就我們的目的而言,庫允許我們使用 using 關鍵字,它可以自動把庫的所有方法添加給一個資料型別:
using SafeMath for uint;
// 這下我們可以為任何 uint 呼叫這些方法了
uint test = 2;
test = test.mul(3); // test 等于 6 了
test = test.add(5); // test 等于 11 了
(注意 mul 和 add 其實都需要兩個引數, 在我們宣告了 using SafeMath for uint 后,我們用來呼叫這些方法的 uint 就自動被作為第一個引數傳遞進去了(在此例中就是 test),)
10、為了防止溢位和下溢,我們可以在我們的代碼里找 +, -, *, 或 /,然后替換為 add, sub, mul, div.
將
myUint++;
改為
myUint = myUint.add(1);
2.最終代碼
代碼如下:
erc721.sol
//this is erc721.sol
pragma solidity ^0.4.19;
contract ERC721 {
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
function balanceOf(address _owner) external view returns (uint256);
function ownerOf(uint256 _tokenId) external view returns (address);
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
function approve(address _approved, uint256 _tokenId) external payable;
}
safemath.sol
//this is safemath.sol
pragma solidity ^0.4.19;
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
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;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
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;
}
/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
/**
* @title SafeMath32
* @dev SafeMath library implemented for uint32
*/
library SafeMath32 {
function mul(uint32 a, uint32 b) internal pure returns (uint32) {
if (a == 0) {
return 0;
}
uint32 c = a * b;
assert(c / a == b);
return c;
}
function div(uint32 a, uint32 b) internal pure returns (uint32) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint32 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function sub(uint32 a, uint32 b) internal pure returns (uint32) {
assert(b <= a);
return a - b;
}
function add(uint32 a, uint32 b) internal pure returns (uint32) {
uint32 c = a + b;
assert(c >= a);
return c;
}
}
/**
* @title SafeMath16
* @dev SafeMath library implemented for uint16
*/
library SafeMath16 {
function mul(uint16 a, uint16 b) internal pure returns (uint16) {
if (a == 0) {
return 0;
}
uint16 c = a * b;
assert(c / a == b);
return c;
}
function div(uint16 a, uint16 b) internal pure returns (uint16) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint16 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function sub(uint16 a, uint16 b) internal pure returns (uint16) {
assert(b <= a);
return a - b;
}
function add(uint16 a, uint16 b) internal pure returns (uint16) {
uint16 c = a + b;
assert(c >= a);
return c;
}
}
ownable.sol
//this is ownable.sol
pragma solidity ^0.4.19;
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address private _owner;
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor() internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @return the address of the owner.
*/
function owner() public view returns(address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner());
_;
}
/**
* @return true if `msg.sender` is the owner of the contract.
*/
function isOwner() public view returns(bool) {
return msg.sender == _owner;
}
/**
* @dev Allows the current owner to relinquish control of the contract.
* @notice Renouncing to ownership will leave the contract without an owner.
* It will not be possible to call the functions with the `onlyOwner`
* modifier anymore.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0));
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
zombiefactory.sol
//this is zombiefactory.sol
pragma solidity ^0.4.19;
import "./ownable.sol";
import "./safemath.sol";
contract ZombieFactory is Ownable {
using SafeMath for uint256;
using SafeMath32 for uint32;
using SafeMath16 for uint16;
event NewZombie(uint zombieId, string name, uint dna);
uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
uint cooldownTime = 1 days;
struct Zombie {
string name;
uint dna;
uint32 level;
uint32 readyTime;
uint16 winCount;
uint16 lossCount;
}
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, uint32(now + cooldownTime), 0, 0)) - 1;
zombieToOwner[id] = msg.sender;
ownerZombieCount[msg.sender] = ownerZombieCount[msg.sender].add(1);
emit NewZombie(id, _name, _dna);
}
function _generateRandomDna(string _str) private view returns (uint) {
uint rand = uint(keccak256(abi.encodePacked(_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);
}
}
zombiefeeding.sol
//this is zombiefeeding.sol
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 {
KittyInterface kittyContract;
modifier onlyOwnerOf(uint _zombieId) {
require(msg.sender == zombieToOwner[_zombieId]);
_;
}
function setKittyContractAddress(address _address) external onlyOwner {
kittyContract = KittyInterface(_address);
}
function _triggerCooldown(Zombie storage _zombie) internal {
_zombie.readyTime = uint32(now + cooldownTime);
}
function _isReady(Zombie storage _zombie) internal view returns (bool) {
return (_zombie.readyTime <= now);
}
function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) internal onlyOwnerOf(_zombieId) {
Zombie storage myZombie = zombies[_zombieId];
require(_isReady(myZombie));
_targetDna = _targetDna % dnaModulus;
uint newDna = (myZombie.dna + _targetDna) / 2;
if (keccak256(abi.encodePacked(_species)) == keccak256(abi.encodePacked("kitty"))) {
newDna = newDna - newDna % 100 + 99;
}
_createZombie("NoName", newDna);
_triggerCooldown(myZombie);
}
function feedOnKitty(uint _zombieId, uint _kittyId) public {
uint kittyDna;
(,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
feedAndMultiply(_zombieId, kittyDna, "kitty");
}
}
zombiehelper.sol
//this is zombiehelper.sol
pragma solidity ^0.4.19;
import "./zombiefeeding.sol";
contract ZombieHelper is ZombieFeeding {
uint levelUpFee = 0.001 ether;
modifier aboveLevel(uint _level, uint _zombieId) {
require(zombies[_zombieId].level >= _level);
_;
}
function withdraw() external onlyOwner {
address _owner = owner();
_owner.transfer(address(this).balance);
}
function setLevelUpFee(uint _fee) external onlyOwner {
levelUpFee = _fee;
}
function levelUp(uint _zombieId) external payable {
require(msg.value == levelUpFee);
zombies[_zombieId].level = zombies[_zombieId].level.add(1);
}
function changeName(uint _zombieId, string _newName) external aboveLevel(2, _zombieId) onlyOwnerOf(_zombieId) {
zombies[_zombieId].name = _newName;
}
function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) onlyOwnerOf(_zombieId) {
zombies[_zombieId].dna = _newDna;
}
function getZombiesByOwner(address _owner) external view returns(uint[]) {
uint[] memory result = new uint[](ownerZombieCount[_owner]);
uint counter = 0;
for (uint i = 0; i < zombies.length; i++) {
if (zombieToOwner[i] == _owner) {
result[counter] = i;
counter++;
}
}
return result;
}
}
zombieattack.sol
//this is zombieattack.sol
pragma solidity ^0.4.19;
import "./zombiehelper.sol";
contract ZombieAttack is ZombieHelper {
uint randNonce = 0;
uint attackVictoryProbability = 70;
function randMod(uint _modulus) internal returns(uint) {
randNonce = randNonce.add(1);
return uint(keccak256(abi.encodePacked(now, msg.sender, randNonce))) % _modulus;
}
function attack(uint _zombieId, uint _targetId) external onlyOwnerOf(_zombieId) {
Zombie storage myZombie = zombies[_zombieId];
Zombie storage enemyZombie = zombies[_targetId];
uint rand = randMod(100);
if (rand <= attackVictoryProbability) {
myZombie.winCount = myZombie.winCount.add(1);
myZombie.level = myZombie.level.add(1);
enemyZombie.lossCount = enemyZombie.lossCount.add(1);
feedAndMultiply(_zombieId, enemyZombie.dna, "zombie");
} else {
myZombie.lossCount = myZombie.lossCount.add(1);
enemyZombie.winCount = enemyZombie.winCount.add(1);
_triggerCooldown(myZombie);
}
}
}
zombieownership.sol
//this is zombieownership.sol
pragma solidity ^0.4.19;
import "./zombieattack.sol";
import "./erc721.sol";
import "./safemath.sol";
contract ZombieOwnership is ZombieAttack, ERC721 {
using SafeMath for uint256;
mapping (uint => address) zombieApprovals;
function balanceOf(address _owner) external view returns (uint256) {
return ownerZombieCount[_owner];
}
function ownerOf(uint256 _tokenId) external view returns (address) {
return zombieToOwner[_tokenId];
}
function _transfer(address _from, address _to, uint256 _tokenId) private {
ownerZombieCount[_to] = ownerZombieCount[_to].add(1);
ownerZombieCount[msg.sender] = ownerZombieCount[msg.sender].sub(1);
zombieToOwner[_tokenId] = _to;
emit Transfer(_from, _to, _tokenId);
}
function transferFrom(address _from, address _to, uint256 _tokenId) external payable {
require (zombieToOwner[_tokenId] == msg.sender || zombieApprovals[_tokenId] == msg.sender);
_transfer(_from, _to, _tokenId);
}
function approve(address _approved, uint256 _tokenId) external payable onlyOwnerOf(_tokenId) {
zombieApprovals[_tokenId] = _approved;
emit Approval(msg.sender, _approved, _tokenId);
}
}
總結
本章知識點只有兩個,一個是代幣標準,一個是代碼安全的預防溢位,這里就不做過多總結了,在文章最后,希望我的這個學習記錄可以幫到需要的人,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/308920.html
標籤:區塊鏈
上一篇:《巴菲特的投資組合》讀書筆記一
