地址工具
用于检测某个地址是否为合约的工具
代码语言:javascript复制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字节码长度。
return size>0;//
}
}
这个合约是一个librray,只有一个函数isContract,且被声明为internal view.internal 限制这个函数只能由import这个合约内部使用;view 声明这个函数不会改变状态
限制子合约的余额
限制子合约以太币余额的基础合约
代码语言:javascript复制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操作中指定合约地址而引发的转入操作;也没法限制没有使用limitedPayable来声明的合约函数进行转入操作。
安全算术(SafeMath.sol)
对uint256类型进行算术四则运算的库,也是最常用库,防止溢出。
代码语言:javascript复制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便宜。原因:=只执行了一个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)
这是一个向外界提供一个特定函数的基础合约,这个函数可以用来查询合约支持的接口(函数)。
代码语言:javascript复制pragma solidity ^0.4.24;
interface ERC165{
function supportsInterface(bytes64 _interfaceId)
external //外部函数
view //不会修改状态
returns(bool);
}//gas 消耗控制在30000以内
_interfaceId函数选择器,是合约某个函数的标识(合约函数调用数据的前4个字节)
接口查找基础合约
代码语言:javascript复制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类型的状态变量持久化地保存了一个由函数接口(函数选择器)到布尔值的映射。它提供了一个internal函数来注册合约自身的接口函数,并在合约构造函数中直接注册了ERC165接口函数supportsInterfaces。注意_registerInterface断言基于ERC165的gas消耗。
归属权(Owner.sol)
这是一个用来给合约提供所属权特性的基础合约,这是一个非常重要的,大概也是最基础的合约。
代码语言:javascript复制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;
}
}
这是一个非常通用的归属权基础合约。由一个状态变量来保存它的所有者地址,并在构造函数中将合约创建人设置为合约所有者。定义了两个事件:OwnershipRenounced 用来通知外部世界所有者放弃对合约的所有权;OwnershipTransfered 用来通知外部世界合约归属权发生转移。
用户角色(Roles.sol)
代码语言:javascript复制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)
代码语言:javascript复制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)
代码语言:javascript复制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合约添加字符串附加信息的基础合约
代码语言:javascript复制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的扩展,允许在做归属权转移时,由新的的合约拥有者“声明接受归属权”
代码语言:javascript复制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函数才能获得合约的归属权。
这里的时间条件是区块号(block.number)的范围。原因:区块链系统基于分布式对等网络,各个节点(客户端)本地时间未必与UTC时间一致,所有使用区块号这个全网共识的时间标志作为判定条件。
代码语言:javascript复制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)
代码语言:javascript复制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方法将其所有者地址转移到当前合约的所有者。
代码语言:javascript复制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合约的拓展,来确保合约中不持有以太币。
代码语言:javascript复制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取回到合约所有者的地址。
代码语言:javascript复制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异常终止回退所有先前改变的状态
}
}
tokenFallback函数就是ERC233标准中要求接受者合约实现的一个函数,实现这个函数的合约会被认定是可以持有ERC233token的合约。
合约什么都不持有(NoOwner.sol)
代码语言:javascript复制pragma solidity ^0.4.24;
import "./HasNoEther.sol";
import "./HasNoToken.sol";
import "./HasNocontracts.sol";
contract NoOwner is HasNoEther, HasNoToken, HasNocontracts{
}
签名保镖(SigmatureBouncer.sol)
代码语言:javascript复制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字节 (65 字节签名的整字长度)96字节
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值转换成客户端签发的消息数据,而后恢复成签名公钥来验证签名是否来自一个具有保镖角色的地址。 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)
代码语言:javascript复制pragma solidity ^0.4.24;
import "../Ownership/Ownable.sol";
contract Destructible is Ownable{
constructor() public payable{}
//销毁合约将余额发个合约所有者
function destroy() onlyOwner public{
selfdestruct(owner);
}
//销毁合约将余额发个指定地址
function destoryAndSend(address _recipient) onlyOwner public{
selfdestruct(_recipient);
}
}
可暂停运作(Pausable.sol)
代码语言:javascript复制pragma solidity ^0.4.24;
import "../Ownership/Ownable.sol";
contract Pausable is Ownable{
event Pause();
event Unpause();
bool public paused = false;
//在未暂停状态下使用
modifier whenNotPaused(){
require(!paused);
_;
}
//在暂停状态下使用
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)
代码语言:javascript复制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)
代码语言:javascript复制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 调用父类的回撤方法
代码语言:javascript复制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)
代码语言:javascript复制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;//合约状态
}
//为可能退还的处理保存资金
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;
}
}