关于稳定代币的说明

什么是稳定币

稳定币就是一种相对稳定的加密货币。这里的“稳定”主要是指价格稳定,即,在一段时间内的价格不会有大幅波动。

  1. 第一代以USDT为代表稳定币,其特征是无监管、无透明、属于自说自话的一种。

  2. 第二代以TrueUSD为代表的稳定币,比USDT进了一步,它的在透明度上要比USDT好很多,因为TUSD宣称使用托管帐户作为基金管理中使用最广泛的合法工具,为持有人提供定期审计和强有力的法律保护,即多银行负责托管帐户、第三方出具帐户余额认证、团队绝不和存入的USDT直接打交道等等,所以不会出现裁判员和运动员于一身的情况。

  3. 第三代以GUSD和PAX为代表的稳定币,则更进一步,直接以美国国家信用为背书。2018年9月10日,纽约金融服务部(NYDFS)同时批准了两种基于以太坊-ERC20发行的稳定币,分别是 Gemini 公司发行的稳定币Gemini Dollar(GUSD),与Paxos公司发行的稳定币Paxos。Standard (PAX),每个稳定币都有 1 美元支撑。此外,还有两个非常突出的特点:一个是获得政府部门纽约金融服务部正式批准,成为第一个合规合法、接受监督的稳定币(也就意味着受到法律保护),信用背书大幅提升;另一个是基于以太坊的ERC20来发行的,这意味着财务相关数据完全公开透明、不可窜改,而且完全去中心化。那么理论上说,每一笔GUSD的增发都会有相应的资金入账。和完全中心化的稳定币对比,对于投资者来说,无疑更加具有可信度。

但我们也应该看到,这次给GUSD颁发执照的,是NYDFS(纽约州金融服务部门),即一个地方金融部门。美国州级金融部门的监管并不意味着在全美有效,NYDFS(纽约州金融服务部门)上面还有SEC和美联储,这两个部门的态度才是决定稳定币未来的关键。而且GUSD目前只有25万美金上限的保额(GUSD发行的美元是存放在美国的在岸银行账户上的,这个账户适用FDIC的存款保险)。所以此次GUSD作为试点的目的很浓,就像我们国家一样,新东西慢慢来,先搞个试点,从中总结经验、完善相关法律法规,待成熟后逐步全面推广。

关于ERC20代币

如果问以太坊的智能合约应用最广泛的地方在哪里, 肯定就是发币了。 虽然可能V神也没想到世界还没被改变, ICO(圈钱)的方式倒是又多出了一个。

既然是数字token, 代码实现起来就可以有各种各样的方案。 实质无外乎就是记录下每个用户拥有的数字token数量, 具有转账,查询余额等功能就行了。 上面我们说到只要调用对应相关的函数去执行即可完成相关的功能。 可是这个时候假如某个ICO方A 写了代币转账功能, 它的转账函数叫做tx(uint256). 那么如果想调用它的转账就要调用tx这个函数, 我们知道只要调用函数不相同, input的内容就不会一样。 如果所有的ICO方写的这些函数名称均不一样。 调用者就要查看每一个ICO方的合约代码。 于是这个时候ERC20就来了, 它定义了一些发币(圈钱)的规范。也即是如果你想发行ICO, 最好按着我的规范来, 这样大家用起来就跟方便了。ERC20简而言之就是定义了下面几个接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
contract ERC20 {

uint256 public totalSupply;

event Transfer(address indexed from, address indexed to, uint256 value); // 转账触发
event Approval(address indexed owner, address indexed spender, uint256 value); // 容许提取触发

function balanceOf(address who) public view returns (uint256); // 查询余额函数
function transfer(address to, uint256 value) public returns (bool); // 进行转账函数

function approve(address spender, uint256 value) public returns (bool); // 容许某个地址提款
function transferFrom(address from, address to, uint256 value) public returns (bool); // 从一方向另一方转账的余额

}

有了这个规范, 各个ICO方在发行token时都实现上面的接口, 这样无论任何的ERC20代币, 均可以用一套方法实现所有代币转账,查询余额等功能。

分析一下PAX稳定币的智能合约代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
pragma solidity ^0.4.24;
pragma experimental "v0.5.0";

// 导入外部包 此包中主要是一些
// 安全的数学运算
// 因为在一些场景 出现了数据溢出没有考虑的问题导致了
// 一些ico币直接归零
import "./zeppelin/SafeMath.sol";

contract PAXImplementation {

using SafeMath for uint256;
bool private initialized = false;

// 定义了ERC20规定的代币名称 符号 精度
mapping(address => uint256) internal balances;
uint256 internal totalSupply_;
string public constant name = "PAX"; // solium-disable-line uppercase
string public constant symbol = "PAX"; // solium-disable-line uppercase
uint8 public constant decimals = 18; // solium-disable-line uppercase

// ERC20 DATA
mapping (address => mapping (address => uint256)) internal allowed;

// OWNER DATA
address public owner;

// PAUSABILITY DATA
bool public paused = false;

// LAW ENFORCEMENT DATA
address public lawEnforcementRole;
mapping(address => bool) internal frozen;

// SUPPLY CONTROL DATA
address public supplyController;

// 定义触发时间 当转账或者授权别人转账时 调用此事件 当调用时 其实质会在以太坊节点区块上写入日志。
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);

// OWNABLE EVENTS
event OwnershipTransferred(
address indexed oldOwner,
address indexed newOwner
);

// PAUSABLE EVENTS
event Pause();
event Unpause();

// LAW ENFORCEMENT EVENTS
event AddressFrozen(address indexed addr);
event AddressUnfrozen(address indexed addr);
event FrozenAddressWiped(address indexed addr);
event LawEnforcementRoleSet (
address indexed oldLawEnforcementRole,
address indexed newLawEnforcementRole
);

// SUPPLY CONTROL EVENTS
event SupplyIncreased(address indexed to, uint256 value);
event SupplyDecreased(address indexed from, uint256 value);
event SupplyControllerSet(
address indexed oldSupplyController,
address indexed newSupplyController
);

/**
* FUNCTIONALITY
*/

// INITIALIZATION FUNCTIONALITY

/**
合约部署时的初始化过程
设置合约拥有者为部署合约的账户
设置总供应量为0
并保证此函数只会被调用一次
*/
function initialize() public {
require(!initialized, "already initialized");
owner = msg.sender;
lawEnforcementRole = address(0);
totalSupply_ = 0;
supplyController = msg.sender;
initialized = true;
}

/**
合约的构造函数 调用上面的初始化函数 并且设置暂停交易
*/
constructor() public {
initialize();
pause();
}

// ERC20 BASIC FUNCTIONALITY

/**
ERC20接口 返回总的供应量
*/
function totalSupply() public view returns (uint256) {
return totalSupply_;
}

/*
转账函数 实现将调用者的token转给其他人
msg.sender 即为合约的调用者
并且此函数要求必须是非暂停状态 即whenNotPaused返回真

这个函数有需要验证条件
1.交易没有被暂停
2.接收方地址不能是0
3.接收方和发起方均不可以是冻结地址
4.转账的token余额要足够。
*/
function transfer(address _to, uint256 _value) public whenNotPaused returns (bool) {
require(_to != address(0), "cannot transfer to address zero");
require(!frozen[_to] && !frozen[msg.sender], "address frozen");
require(_value <= balances[msg.sender], "insufficient funds");

balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
emit Transfer(msg.sender, _to, _value);
return true;
}

/**
ERC20接口 返回某个账户的token余额
*/
function balanceOf(address _addr) public view returns (uint256) {
return balances[_addr];
}

// ERC20 FUNCTIONALITY

/*
ERC20接口 实现了 _from地址下容许调用方可以转出金额到其他_to
此函数要求必须是非暂停状态
*/
function transferFrom(
address _from,
address _to,
uint256 _value
)
public
whenNotPaused
returns (bool)
{
require(_to != address(0), "cannot transfer to address zero");
require(!frozen[_to] && !frozen[_from] && !frozen[msg.sender], "address frozen");
require(_value <= balances[_from], "insufficient funds");
require(_value <= allowed[_from][msg.sender], "insufficient allowance");

balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(_value);
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
emit Transfer(_from, _to, _value);
return true;
}

/**
ERC20接口 实现调用方容许_spender 可以从我的账户转出的金额 这个函数和上面的函数是相对应的。
只有一个账户容许了其他账户能从我的账户转出的金额 上述的函数才能转账成功。
*/
function approve(address _spender, uint256 _value) public whenNotPaused returns (bool) {
require(!frozen[_spender] && !frozen[msg.sender], "address frozen");
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}

/**
ERC20接口 返回_owner账户容许_spender账户从自己名下转移出去的资产数量
*/
function allowance(
address _owner,
address _spender
)
public
view
returns (uint256)
{
return allowed[_owner][_spender];
}

// OWNER FUNCTIONALITY

/**
这个函数被称为修饰函数 上面的whenNotPaused 也是一个修饰函数 实质是一种断言。 只有
断言通过 才会执行函数内部的内容
*/
modifier onlyOwner() {
require(msg.sender == owner, "onlyOwner");
_;
}

/*
将只能合约的拥有者转给别人
*/
function transferOwnership(address _newOwner) public onlyOwner {
require(_newOwner != address(0), "cannot transfer ownership to address zero");
emit OwnershipTransferred(owner, _newOwner);
owner = _newOwner;
}

// PAUSABILITY FUNCTIONALITY

/**
* 修饰函数 要求处于非暂停交易状态
*/
modifier whenNotPaused() {
require(!paused, "whenNotPaused");
_;
}

/**
* 只有合约的拥有者才可以设置暂停交易
*/
function pause() public onlyOwner {
require(!paused, "already paused");
paused = true;
emit Pause();
}

/**
* 只有合约的拥有者才能取消暂停交易
*/
function unpause() public onlyOwner {
require(paused, "already unpaused");
paused = false;
emit Unpause();
}

// LAW ENFORCEMENT FUNCTIONALITY

/**
设置一个法定的的强制角色 这个角色可以冻结或者解冻别人账户的token
设置一个这样的角色要求首先调用方要么是合约的拥有者 要么自己已经是法定的强制者
* @param _newLawEnforcementRole The new address allowed to freeze/unfreeze addresses and seize their tokens.
*/
function setLawEnforcementRole(address _newLawEnforcementRole) public {
require(msg.sender == lawEnforcementRole || msg.sender == owner, "only lawEnforcementRole or Owner");
emit LawEnforcementRoleSet(lawEnforcementRole, _newLawEnforcementRole);
lawEnforcementRole = _newLawEnforcementRole;
}

// 断言函数 要求调用方必须是强制者角色
modifier onlyLawEnforcementRole() {
require(msg.sender == lawEnforcementRole, "onlyLawEnforcementRole");
_;
}

/**
冻结某个账户的token 使用了断言 onlyLawEnforcementRole 也是只有调用方角色是
法定强制者才有权限冻结别人的token
*/
function freeze(address _addr) public onlyLawEnforcementRole {
require(!frozen[_addr], "address already frozen");
frozen[_addr] = true;
emit AddressFrozen(_addr);
}

/**
解冻某个账户的token 使用了断言 onlyLawEnforcementRole 也是只有调用方角色是
法定强制者才有权限解冻别人的token
*/
function unfreeze(address _addr) public onlyLawEnforcementRole {
require(frozen[_addr], "address already unfrozen");
frozen[_addr] = false;
emit AddressUnfrozen(_addr);
}

/**
摧毁冻结账户的token 也就是说如果这个地址是一个冻结地址调用这个函数会把这个地址的token销毁同时总供应数量也会被减少
当然这个函数也不是谁都可以调用的 只有法定的强制者才有权限
*/
function wipeFrozenAddress(address _addr) public onlyLawEnforcementRole {
require(frozen[_addr], "address is not frozen");
uint256 _balance = balances[_addr];
balances[_addr] = 0;
totalSupply_ = totalSupply_.sub(_balance);
emit FrozenAddressWiped(_addr);
emit SupplyDecreased(_addr, _balance);
emit Transfer(_addr, address(0), _balance);
}

/**
用于检查某个地址是否被冻结了
*/
function isFrozen(address _addr) public view returns (bool) {
return frozen[_addr];
}

// SUPPLY CONTROL FUNCTIONALITY

/**
设置token供应量的控制着 在合约初始化时 token供应量是合约发起则 调用这个函数可以更改
这个函数只有调用方已经是token供应量控制着或者整个合约的拥有者才能调用成功
也就是在整个合约中 合约的拥有者实质是可以控制一切权限的。 它能更改法定强制者 更改总token
供应量的控制者。
*/
function setSupplyController(address _newSupplyController) public {
require(msg.sender == supplyController || msg.sender == owner, "only SupplyController or Owner");
require(_newSupplyController != address(0), "cannot set supply controller to address zero");
emit SupplyControllerSet(supplyController, _newSupplyController);
supplyController = _newSupplyController;
}

modifier onlySupplyController() {
require(msg.sender == supplyController, "onlySupplyController");
_;
}

/**
增加总的token供应量 并把新增供应量加到supplyController这个账户的名下。
*/
function increaseSupply(uint256 _value) public onlySupplyController returns (bool success) {
totalSupply_ = totalSupply_.add(_value);
balances[supplyController] = balances[supplyController].add(_value);
emit SupplyIncreased(supplyController, _value);
emit Transfer(address(0), supplyController, _value);
return true;
}

/**
减少总的token供应量 待减少供应量从supplyController这个账户的名下减掉 。
这个函数要求supplyController
*/
function decreaseSupply(uint256 _value) public onlySupplyController returns (bool success) {
require(_value <= balances[supplyController], "not enough supply");
balances[supplyController] = balances[supplyController].sub(_value);
totalSupply_ = totalSupply_.sub(_value);
emit SupplyDecreased(supplyController, _value);
emit Transfer(supplyController, address(0), _value);
return true;
}
}

PAX稳定币功能概述

PAX除了具有这个ERC20的功能外,还具有一些其他功能:

  1. 可以暂停整个代币转账
  2. 可以增加或者减少整个代币的数量
  3. 可以任意冻结或者解冻某个账户的代币
  4. 可以销毁某个冻结账户的代币
  5. 可以转移合约控制权。可以转移总供应量控制权。

总的来说PAX币做的限制特别多, 它的合约拥有机会可以做任何事情。 就算token转移给你了, 依然能分分钟钟消失。