地址工具
用于檢測某個地址是否為合約的工具
pragma solidity ^0.4.24
library AddressUtils{
function isContract(address addr) internal view returns(bool){
uint256 size;
assembly{size:=extcodesize(addr)}
//assembly 指明后面程序為內聯匯編。extcodesizs取得參數addr對應賬戶關聯地址的EVM字節(jié)碼長度。
return size>0;//
}
}
這個合約是一個librray,只有一個函數isContract灯蝴,且被聲明為internal view.internal 限制這個函數只能由import這個合約內部使用;view 聲明這個函數不會改變狀態(tài)
限制子合約的余額
限制子合約以太幣余額的基礎合約
pragam solidity ^0.4.24;
contract LimitBlance{
uint256 public limit;
constructor(uint256 _limit) public{
limit=_limit;
}
modifier limitedPayable{
require(address(this).blance<=limit);
_;
}
}
缺點:這種利用modifier來限制的方式,無法限制其它合約通過selfdestruct操作中指定合約地址而引發(fā)的轉入操作鞠绰;也沒法限制沒有使用limitedPayable來聲明的合約函數進行轉入操作。
安全算術(SafeMath.sol)
對uint256類型進行算術四則運算的庫飒焦,也是最常用庫蜈膨,防止溢出。
pragma solidity ^0.4.24;
library SafeMath{
function mul(uint256 _a, uint256 _b) internal pure returns(uint256 c){
if (_a == 0){//a==0比a!=0便宜牺荠。原因:=只執(zhí)行了一個EVM判斷
return 0;
}
c = _a * _b;
assert(c / _a == _b);//_a*_b結果可能超過最大值翁巍,判斷有沒有溢出。return c;
}
function div(uint256 _a, uint256 _b) internal pure returns(uint256 ){
return _a / _b;//結果不為整休雌,小數會被舍棄灶壶。}
function sub(uint256 _a, uint256 _b) internal pure returns(uint256 c ){
assert(_a >= _b);//防止下溢(2^256+_a-_b)
return _a - _b;
}
function add(uint256 _a, uint256 _b) internal pure returns(uint256 c ){
c= _a + _b;
assert(c >= _b);//防止上溢((a+b)mod 2^256)
return c;
}
}
自省(ERC165)
這是一個向外界提供一個特定函數的基礎合約挑辆,這個函數可以用來查詢合約支持的接口(函數)例朱。
pragma solidity ^0.4.24;
interface ERC165{
function supportsInterface(bytes64 _interfaceId)
external //外部函數
view //不會修改狀態(tài)
returns(bool);
}//gas 消耗控制在30000以內
_interfaceId函數選擇器,是合約某個函數的標識(合約函數調用數據的前4個字節(jié))
接口查找基礎合約
pragma solidity ^0.4.24;
contract SupportsInterfaceWithLiikUp is ERC165{
bytes4 public constant InterfaceId_ERC165 = 0x01ffc9a7;
//0x01ffc9a7 ===bytes4(keccak256('SupportsInterface(bytes4)'))
mapping(bytes4 => bool) internal supportsInterfaces;
constructor()
public
{
_registerInterface(InterfaceId_ERC165);
}
function _registerInterface(bytes4 _interfaceId)
internal
{
require(interfaceId != 0xffffffff);
supportsInterface[_interfaceId] = true;
}
}
這是一個實現了ERC165的基礎合約鱼蝉。
用一個mapping類型的狀態(tài)變量持久化地保存了一個由函數接口(函數選擇器)到布爾值的映射洒嗤。它提供了一個internal函數來注冊合約自身的接口函數,并在合約構造函數中直接注冊了ERC165接口函數supportsInterfaces魁亦。注意_registerInterface斷言基于ERC165的gas消耗渔隶。
歸屬權(Owner.sol)
這是一個用來給合約提供所屬權特性的基礎合約,這是一個非常重要的,大概也是最基礎的合約间唉。
pragma solidity ^0.4.24;
contract Owner{
address public owner;
event OwnershipRenounced(address indexed previousOwner);
event OwnershipTransfered(
address indexed previousOwner,
address indexed newOwner
);
constract() public{
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function renounceOwner() public onlyOwner {
emit OwnershipRenounced(owner);
owner = address(0);//沒人再能調用聲明為onlyOwner的函數绞灼。 }
function transferOwnership(address _newOwner) public onlyOwner{
_transferOwnership(_newOwner);
}
function _transferOwnership(address _newOwner) internal{
require(_newOwner != address(0));
emit OwnershipTransfered(owner, _newOwner);
owner = _newOwner;
}
}
這是一個非常通用的歸屬權基礎合約。由一個狀態(tài)變量來保存它的所有者地址呈野,并在構造函數中將合約創(chuàng)建人設置為合約所有者低矮。定義了兩個事件:OwnershipRenounced 用來通知外部世界所有者放棄對合約的所有權;OwnershipTransfered 用來通知外部世界合約歸屬權發(fā)生轉移被冒。
用戶角色(Roles.sol)
pragma solidity ^0.4.24;
library Roles{
struct Role{
mapping (address => bool) bearer;
}
function add(Role storage role, address addr)
internal
{//添加角色
role.bearer[addr] = true;
}
function remove(Role storage role,address addr)
internal
{//移除角色
role.bearer[addr] = false;
}
function check(Role storage role,address addr)
view
internal
{//驗證角色
require(has(role,addr))
}
function has(Role storage role,address addr)
view
internal
returns(bool)
{//驗證角色
return role.bearer[addr];
}
}
定義結構體Role,其中保留一組地址到布爾值的映射军掂,也就是保留“某個地址是否是當前Role”的信息。
基于角色的訪問控制(RBAC.sol)
pragma solidity ^0.4.24;
import"./Role.sol";
contract RBAC{
using Role for Roles.Role;
mapping (string => Roles.Role) private roles;
event RoleAdded(address indexed operator, string role);
event RoleRemoved(address indexed operator, string role);
function chekRole(address _operator, string _role)
view
public
{
roles[_role].check(_operator);
}
function hasRole(address _operator, string _role)
view
internal
returns(bool)
{
return roles[_role].has(_operator);
}
function addRole(address _operator, string _role)
internal
{
roles[_role].add(_operator);
emit RoleAdded(_operator, _role);
}
function removeRole(address _operator, string _role)
internal
{
roles[_role].remove(_operator);
emit RoleRemoved(_operator, _role);
}
modifier onlyRole
{//特定角色可用的函數修改器
checkRole(msg.sender, _role);
_;
}
}
非常重要的基礎合約昨悼』茸叮可以用來基于用戶角色進行相應的訪問控制。合約中定義了一個string到Roles.Role的private映射率触,也就是角色名稱到與角色相關聯的所有地址信息映射的對應關系终议。
超級用戶(Superuser.sol)
pragma solidity ^0.4.24;
import "./Ownable.sol";
import "./rbac.RBAC.sol";
contract Superuser is Ownable, RBAC {
string public constant ROLE_SUPERUSER = "superuser" ;
constructor() public{
addRole(msg.sender,ROLE_SUPERUSER);
}
modifier onlySuperuser(){
checkRole(msg.sender,ROLE_SUPERUSER);
_;
}
modifier onlyOwnerOrSuperuser(){
require(msg.sender == owner || isSuperuser(msg.sender));
_;
}
function isSuperuser(address _addr)
public
view
returns(bool)
{
return hasRole(_addr, ROLE_SUPERUSER);
}
function transferSuperuser(address _newSuperuser) public onlySuperuser{
require(_newSuperuser != address(0));
removeRole(msg.sender, ROLE_SUPERUSER);
addRole(msg.sender,ROLE_SUPERUSER);
}
//轉移合約地址
function transferOwnership(address _newOwner) public onlySuperuser{
_transferOwnership(_newOwner);
}
超級用戶可用直接修改合約歸屬權恤筛,即使他不是合約的Owner.
聯系方式(Contactable.sol)
簡單地給Owner合約添加字符串附加信息的基礎合約
pragma solidity ^0.4.24;
import "./Ownable.sol";
contract contactable is Owner{
string public contactInformation;
function setcontactInformation(string info) onlyOwner public{
contactInformation = info;
}
}
歸屬權轉移請求(Claimable.sol)
Ownable的擴展队塘,允許在做歸屬權轉移時,由新的的合約擁有者“聲明接受歸屬權”
pragma solidity ^0.4.24;
import "./Ownable.sol";
contract Claimable is Owner{
address public pendingOwner;
modifier onlypendingOwner(){
require(msg.sender == pendingOwner);
_;
}
function transferOwnership(address newOwner) onlyOwner public{
pendingOwner = newOner;
}
function claimOwnership() onlypengdingOwner public{
emit Ownershiptransferred(owner, pendingOwner);
owner = pendingOwner;
pendingOwner = address(0);
}
}
有時限的歸屬權轉移請求(DelayedClaimable.sol)
當前合約是對Claimable.sol的擴展缝呕,由當前合約所有者指定了一個接受歸屬權轉移的時間期限两曼,新的owner只有在時間期限內調用claimOwnership函數才能獲得合約的歸屬權陆馁。
這里的時間條件是區(qū)塊號(block.number)的范圍。原因:區(qū)塊鏈系統基于分布式對等網絡合愈,各個節(jié)點(客戶端)本地時間未必與UTC時間一致,所有使用區(qū)塊號這個全網共識的時間標志作為判定條件击狮。
pragma solidity ^0.4.24;
import "./Claimable.sol";
contract DelayedClaimable is Claimable{
uint256 public end;
uint256 public start;
function setLimits(uint256 _start, uint256 _end) onlyOwner public{
require(_start <= _end);
end = _end;
start = _start;
}
function claimOwnership() onlyPendingOwner public{
requier((block.number <= end) && (block.number >= start));
emit OwnershipTransferred(owner, pendingOwner);
owner = pendingOwner;
pendingOwner = address(0);
end = 0;
}
}
歸屬權繼承(Heritable.sol)
pragma solidity ^0.4.24;
import "./Ownable.sol";
contract Heritable is Ownable{
address private heir_;
uint256 private heartbeatTimeout_;
uint256 private timeOfDeath_;
event HeirChange(address indexed owner, addresss indexed newHeir);
event OwnerHeartbeated(address indexed owner);
event OwnerProclaimedDead(
address indexed owener,
address indexed heir,
uint256 timeOfDeath
);
event HeirOwnershipClaimed(
address indexed previousOwner,
addresss indexed newOwner
);
modifier onlyHeir(){
require(msg.sender == heir_);
_;
}
constructor(uint256 _heartbeatTimeout) public{
setHeartbeatTimeout(_heartbeatTimeout);
}
function setHeir(address newHeir) public onlyOwner{
require(newOwner != owner);
heartbeat();
emit HeirChanged(owner, newHeir);
heir_ = newHeir;
}
}
合約不歸屬合約(HasNocontracts.sol)
當某個繼承Ownable合約的合約佛析,其所有者地址被設置為一個合約地址的時候,可以使用HasNoConstracts合約定義的reclaimConstract方法將其所有者地址轉移到當前合約的所有者彪蓬。
pragma solidity ^0.4.24;
import "./Ownable.sol";
contact HaoNoContracts is Ownable{
function reclaimConstract(address constractAddr) external onlyOwner{
Ownable constractInst = Ownable(contractAddr);
constractInst.transferOwnership(owner);
}
}
合約不持有以太幣(HasNoEther.sol)
對Ownable合約的拓展寸莫,來確保合約中不持有以太幣。
pragma solidity ^0.4.24;
import "./Ownale.sol";
contract HasNoEther is Ownable{
constructer() public payable{
require(msg.sender == 0);
}
function() external{
}
//將此合約余額轉給合約的擁有者
function() reclaimEther() external onlyOwner{
owner.transfer(address(this).balance);
}
}
合約可找回token(CanClaimToken.sol)
將合約所持有的ERC20 token取回到合約所有者的地址档冬。
pragma solidity ^0.4.24;
import "./Ownable.sol";
import "../token/ERC20/ERC20Basic.sol";
import "../token/ERC20/SafeERC20.sol";
contract CanClaimToken is Ownable{
using SafeERC20 for ERC20Basic;
function reclaimToken(ERC20Basic token) external onlyOwner {
uint256 balance = token.balance(this);//此合約余額
token.safeTransfer(owner,balance);//合約擁有者轉走余額
}
}
合約不持有token(HasNoToken.sol)
pragma solidity ^0.4.24;
import "./CanClaimToken.sol";
contract HasNoTokens is CanReclaimToken{
function tokenFallback(address _from, uint256 _value, bytes _data) external {
_from;
_value;
_data;
revert();//revert函數會導致EVM異常終止回退所有先前改變的狀態(tài)
}
}
tokenFallback函數就是ERC233標準中要求接受者合約實現的一個函數膘茎,實現這個函數的合約會被認定是可以持有ERC233token的合約。
合約什么都不持有(NoOwner.sol)
pragma solidity ^0.4.24;
import "./HasNoEther.sol";
import "./HasNoToken.sol";
import "./HasNocontracts.sol";
contract NoOwner is HasNoEther, HasNoToken, HasNocontracts{
}
簽名保鏢(SigmatureBouncer.sol)
pragma solidity ^0.4.24;
import "../Ownership/Ownable.sol";
import "../Ownership/rbac/RBAC.sol";
import "../ERCRecovery.sol";
contract SigmatureBouncer is Owner, RBAC {
using ERCRecovery for bytes32;
string public constant bytes32;
uint constant METHOD_ID_SIZE = 4;
//(簽名數據長度)32字節(jié) + (65 字節(jié)簽名的整字長度)96字節(jié)
uint constant SIGNATURE_SIZE = 128;
//需要提供保鏢的有效簽名
modifier onlyValidSignture(bytes _sig)
{
require(isValidSignture(msg.sender, _sig));
_;
}
//需要保鏢提供對某個特定函數的有效簽名
modifier onlyValidSigntureAndMethod(bytes _sig)
{
require(isValidSigntureAndMethod(msg.sender, _sig));
_;
}
//需要保鏢提供對某個特定參數表的函數的有效簽名
modifier onlyValidSigntureAndData(bytes _sig)
{
require(isValidSigntureAndData(msg.sender, _sig));
_;
}
//允許合約所有者添加保鏢
function addBouncer(address _bouncer)
onlyOwner
public
{
require(_bouncer != address(0));
addRole(_bouncer, ROLE_BOUNCER);
}
//允許合約所有者移除保鏢
function removeBouncer(address _bouncer)
onlyOwner
public
{
require(_bouncer != address(0));
removeRole(_bouncer, ROLE_BOUNCER);
}
//判斷保鏢簽名是否是"this+sender"的簽名
function isValidSignature(address _address, bytes _sig)
internal
view
returns(bool)
{
return isValidDataHash(
keccak256(abi.encodePacked(address(this), _address)),
_sig
);
}
//判斷保鏢簽名是否是"this+sender+methodId"的簽名
function isValidSignatureAndMethod(address _address, bytes _sig)
internal
view
returns(bool)
{
bytes memory data = new bytes (METHOD_ID_SIZE);
for (uint i = 0; i < data.length; i++){
data[i] = msg.data[];
}
return isValidDataHash(
keccak256(abi.encodePacked(address(this), _address data)),
_sig
);
}
//判斷保鏢簽名是否是"this+sender+methodId+params(s)"的簽名
function isValidSignatureAndData(address _address, bytes _sig)
internal
view
returns(bool)
{
reqire(msg.data.length > SIGNATURE_SIZE);
bytes memory data = new bytes (msg.data.length - SIGNATURE_SIZE);
for (uint i = 0; i < data.length; i++){
data[i] = msg.data[];
}
return isValidDataHash(
keccak256(abi.encodePacked(address(this), _address data)),
_sig
);
}
//一個internal函數酷誓,將hash值轉換成客戶端簽發(fā)的消息數據披坏,而后恢復成簽名公鑰來驗證簽名是否來自一個具有保鏢角色的地址。 function isValidDataHash(byte32 hash, bytes _sig)
internal
view
returns(bool)
{
address signer = hash
.toEthSignedMessageHash()
.recover(_sig);
return hasRole(signer, ROLE_BOUNCER);
}
}
這個合約繼承了Owner和RBAC盐数,它有一個所有者棒拂,且有一個保存了角色和相應地址的映射
白名單(Whitelist.sol)
pragma solidity ^0.4.24;
import "../Ownership/Ownable.sol";
import "../Ownership/rbac/RBAC.sol";
contract Whitelist is Ownable, RBAC{
string public constant ROLE_WHITELISTED = "whitelist";
modifier onlyIfWhitelisted(address _operator){
checkRole(_operator, ROLE_WHITRLISTED);
_;
}
//向白名單中添加一個地址
function addAddressToWhitelist(address _operator)
onlyOwner
public
{
addRole(_operator, ROLE_WHITELISTED);
}
//檢查白名單中是否存在這個地址
function whitelist(address _operator)
onlyOwner
public
returns(bool)
{
return hasRole(_operator, ROLE_WHITELISTED);
}
//添加一組地址
function addAddressesToWhitelist(address[] _operator)
onlyOwner
public
{
for (uint256 i = 0; i < _operator.length; i++){
addAddressToWhitelist(_operator[i]);
}
}
//從白名單中移除一個地址
function removeAddressFromWhitelist(address _operator)
onlyOwner
public
{
removeRole(_operator, ROLE_WHITELISTED);
}
//從白名單中移除一組地址
function removeAddressesFromWhitelist(address[] _operator)
onlyOwner
public
{
for (uint256 i = 0; i < _operator.length; i++){
removeAddressFromWhitelist(_operator[i]);
}
}
}
可自毀(Destructible.sol)
pragma solidity ^0.4.24;
import "../Ownership/Ownable.sol";
contract Destructible is Ownable{
constructor() public payable{}
//銷毀合約將余額發(fā)個合約所有者
function destroy() onlyOwner public{
selfdestruct(owner);
}
//銷毀合約將余額發(fā)個指定地址
function destoryAndSend(address _recipient) onlyOwner public{
selfdestruct(_recipient);
}
}
可暫停運作(Pausable.sol)
pragma solidity ^0.4.24;
import "../Ownership/Ownable.sol";
contract Pausable is Ownable{
event Pause();
event Unpause();
bool public paused = false;
//在未暫停狀態(tài)下使用
modifier whenNotPaused(){
require(!paused);
_;
}
//在暫停狀態(tài)下使用
modifier whenPaused(){
require(paused);
_;
}
//合約所有者暫停合約
function pause() onlyOwner wenNotPaused public {
paused = true;
emit pause();
}
//合約所有者啟動合約
function Unpause() onlyOwner wenPaused public {
paused = false;
emit Unpause();
}
}
token可自毀(TokenDestuctible.sol)
pragma solidity ^0.4.24;
import "../Ownership/Ownable.sol";
import "../token/ERC20/ERC20Gasic.sol";
contract TokenDestuctible is Ownable{
constructor() public payable{}
function destroy(address[] tokens) onlyOwner public{
for (uint256 i = 0; i < tokens.length; i++){
ERC20Gasic token = ERC20Gasic(tokens[i]);
uint256 balance = token.balanceOf(this);
token.transfer(owner,balance);
}
selfdestruct(owner);//自毀
}
}
托管(Escrow.sol)
pragma solidity ^0.4.24;
import "../Ownership/Ownable.sol";
import "../math/SafeMath";
contract Escrow is Ownable{
using SafeMath for uint256;
event Deposited(address indexed payee, uint256 weiAmount);
event withdrawn(address indexed payee, uint256 weiAmount);
mapping(address => uint256) private deposits;
function depositsOf(address _payee) public view returns (uint256){
renturn deposits[_payee];
}
//充值
function deposit(address _payee) public onlyOwner payable{
uint256 amount = msg.value;
deposits[_payee] = deposits[_payee].add(amount);
emit Deposited(_payee, amount);
}
//取回
function withdraw(address _payee) public onlyOwner {
uint256 payment = deposits[_payee];
assert(address(this).balance >= payment);
deposits[_payee] = 0;
_payee.transfer(payment);
emit Witndrawn(_payee, payment)
}
}
條件托(ConditionalEscrow.sol)
super is 調用父類的回撤方法
pragma solidity ^0.4.24;
import "./Escrow.sol";
contract ConditionalEscrow is Escrow {
function withdrawalAllowed(address _payee) public view returns(bool);
function withdraw(address _payee) public{
reqire(withdrawalAllowed(_payee));
super.withdraw(_payee);
}
}
退還托管(RefundEscrow.sol)
pragma solidity ^0.4.24;
import "../Ownership/Ownable.sol";
import "./ConditionalEscrow.sol";
contract RefundEscrow is Ownable, ConditionalEscrow{
enum State {Active, Refund, Closec}
event Closed();
event RefundsEnable();
State public state;
address public beneficiary;
//_beneficiary受益人地址
constructor(address _beneficiary) public {
require(_beneficiary != address(0));
beneficiary = _beneficiary;
state = State.Active;//合約狀態(tài)
}
//為可能退還的處理保存資金
function deposit(address _refundee) public paypable{
require(state == State.Active);
super.deposit(_refundee);
}
//允許受益人取回資金并拒絕其再次充值
function colse() public onlyOwner{
require(state == State.Active);
state = State.Closed;
emit Closed;
}
//允許款項退還拒絕其再次充值
function enableRefunds() public onlyOwner{
require(state == State.Active);
state = State.Refunding;
emit RefundsEnabled();
}
//將合約余額轉給受益人
function beneficiaryWithdraw() public{
require(state == State.Closed);
beneficiary.transfer(address(this).balance);
}
//返回(是否正在進行退款處理)
function withdrawalAllowed(address _payee) public view returns (bool){
return state == State.Refunding;
}
}