Solidity智能合约的库驱动开发
原文: https://blog.aragon.one/library-driven-development-in-solidity-2bebcaf88736
本文关于如何通过使用库来开发更多模块化,可重复使用和优雅的智能合约系统的综合评估.
solidity是一个受限的语言
- 字节码在每个节点上运行, 需要验证执行的有效性;
- 因此, 尽管EVM是图灵完备, 但计算比较昂贵,因为过多的计算会降低以太坊网络速度;
- 没有标准的库被开发出来. 比如数组和字符串操作;
- 不能从外界获取数据,除非通过一个交易(oracle);
- 合约部署后无法升级;
什么是库
在solidity中,库是一种不同类型的合约,没有存储,不拥有以太币. 代码可以被其他合约调用,而不需要重新部署.
这样可以节省大量的gas(因此不会用重复的代码污染区块链),因为同样的代码不需要一遍又多的部署,而不同的合同只能依赖于相同的已经部署的库。
事实上,多个合同依赖于确切的代码段,可以为更安全的环境创造条件。
库不允许可支付(payable)的函数, 没有fallback函数,
库的定义: library C {}
库的调用通过DELEGATECALL
, 即不切换上下文;
调用合约A的a()函数, 返回的是合约地址,而不是库的地址; 包括msg的属性:
library C {
function a() returns (address) {
return address(this);
}
}
contract A {
function a() constant returns (address) {
return C.a();
}
}
库的连接
在合约A部署的时候,会保留一个库地址:0073__C_____________________________________630dbe671f
, 0dbe671f表示a()函数.
链接的时候,会替换合约A中该地址为真实的库C的地址.
库升级
因为在执行链接时 地址已经写死, 所以无法升级, 但是通过另一种方式可以达到升级的目的,见这里
使用结构体和方法
库可以修改链接合约的状态, 通过传递一个"stroage"的引用. 有个语法糖using
.
声明库内函数的调用的第一个参数, 在真正调用时, 可以省略该参数.
library CounterLib {
struct Counter { uint i; }
function incremented(Counter storage self) returns (uint) {
return ++self.i;
}
}
contract CounterContract {
using CounterLib for CounterLib.Counter;
CounterLib.Counter counter;
function increment() returns (uint) {
return counter.incremented();
}
}
事件和库
库没有事件和log, 在库中的事件触发后, 在外部需要监听合约的事件,才能捕获.
library EventEmitterLib {
function emit(string s) {
Emit(s);
}
event Emit(string s);
}
contract EventEmitterContract {
using EventEmitterLib for string;
function emit(string s) {
s.emit();
}
event Emit(string s);
}
实现一个ERC20的lib
library SafeMathLib {
function times(uint a, uint b) returns (uint) {
uint c = a * b;
assert(a == 0 || c / a == b);
return c;
}
function minus(uint a, uint b) returns (uint) {
assert(b <= a);
return a - b;
}
function plus(uint a, uint b) returns (uint) {
uint c = a + b;
assert(c>=a && c>=b);
return c;
}
function assert(bool assertion) private {
if (!assertion) throw;
}
}
import "../SafeMathLib.sol";
library ERC20Lib {
using SafeMathLib for uint;
struct TokenStorage {
mapping (address => uint) balances;
mapping (address => mapping (address => uint)) allowed;
uint totalSupply;
}
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
function init(TokenStorage storage self, uint _initial_supply) {
self.totalSupply = _initial_supply;
self.balances[msg.sender] = _initial_supply;
}
function transfer(TokenStorage storage self, address _to, uint _value) returns (bool success) {
self.balances[msg.sender] = self.balances[msg.sender].minus(_value);
self.balances[_to] = self.balances[_to].plus(_value);
Transfer(msg.sender, _to, _value);
return true;
}
function transferFrom(TokenStorage storage self, address _from, address _to, uint _value) returns (bool success) {
var _allowance = self.allowed[_from][msg.sender];
self.balances[_to] = self.balances[_to].plus(_value);
self.balances[_from] = self.balances[_from].minus(_value);
self.allowed[_from][msg.sender] = _allowance.minus(_value);
Transfer(_from, _to, _value);
return true;
}
function balanceOf(TokenStorage storage self, address _owner) constant returns (uint balance) {
return self.balances[_owner];
}
function approve(TokenStorage storage self, address _spender, uint _value) returns (bool success) {
self.allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
function allowance(TokenStorage storage self, address _owner, address _spender) constant returns (uint remaining) {
return self.allowed[_owner][_spender];
}
}
import './ERC20Lib.sol';
contract StandardToken {
using ERC20Lib for ERC20Lib.TokenStorage;
ERC20Lib.TokenStorage token;
string public name = "SimpleToken";
string public symbol = "SIM";
uint public decimals = 18;
uint public INITIAL_SUPPLY = 10000;
function StandardToken() {
token.init(INITIAL_SUPPLY);
}
function totalSupply() constant returns (uint) {
return token.totalSupply;
}
function balanceOf(address who) constant returns (uint) {
return token.balanceOf(who);
}
function allowance(address owner, address spender) constant returns (uint) {
return token.allowance(owner, spender);
}
function transfer(address to, uint value) returns (bool ok) {
return token.transfer(to, value);
}
function transferFrom(address from, address to, uint value) returns (bool ok) {
return token.transferFrom(from, to, value);
}
function approve(address spender, uint value) returns (bool ok) {
return token.approve(spender, value);
}
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}
这样,ERC20Lib
和 SafeMathLib
只要部署一次, 其他合约都可以链接并使用.
Hi! I am a robot. I just upvoted you! I found similar content that readers might be interested in:
https://pastebin.com/BhqRb7zN