GUSD发行Token
我们看过这个token发行图,在Impl和Custodian中插入了PrintLimiter,这次来仔细看下PrintLimiter的实现。
合约代码
这部分代码不多,直接全放上来吧!
/** @title A contact to govern hybrid control over increases to the token supply.
*
* @notice A contract that acts as a custodian of the active token
* implementation, and an intermediary between it and the ‘true’ custodian.
* It preserves the functionality of direct custodianship as well as granting
* limited control of token supply increases to an additional key.
*
* @dev This contract is a layer of indirection between an instance of
* ERC20Impl and a custodian. The functionality of the custodianship over
* the token implementation is preserved (printing and custodian changes),
* but this contract adds the ability for an additional key
* (the 'limited printer') to increase the token supply up to a ceiling,
* and this supply ceiling can only be raised by the custodian.
*
* @author Gemini Trust Company, LLC
*/
contract PrintLimiter is LockRequestable {
// TYPES
/// @dev The struct type for pending ceiling raises.
struct PendingCeilingRaise {
uint256 raiseBy;
}
// MEMBERS
/// @dev The reference to the active token implementation.
ERC20Impl public erc20Impl;
/// @dev The address of the account or contract that acts as the custodian.
address public custodian;
/** @dev The sole authorized caller of limited printing.
* This account is also authorized to lower the supply ceiling.
*/
address public limitedPrinter;
/** @dev The maximum that the token supply can be increased to
* through use of the limited printing feature.
* The difference between the current total supply and the supply
* ceiling is what is available to the 'limited printer' account.
* The value of the ceiling can only be increased by the custodian.
*/
uint256 public totalSupplyCeiling;
/// @dev The map of lock ids to pending ceiling raises.
mapping (bytes32 => PendingCeilingRaise) public pendingRaiseMap;
// CONSTRUCTOR
function PrintLimiter(
address _erc20Impl,
address _custodian,
address _limitedPrinter,
uint256 _initialCeiling
)
public
{
erc20Impl = ERC20Impl(_erc20Impl);
custodian = _custodian;
limitedPrinter = _limitedPrinter;
totalSupplyCeiling = _initialCeiling;
}
// MODIFIERS
modifier onlyCustodian {
require(msg.sender == custodian);
_;
}
modifier onlyLimitedPrinter {
require(msg.sender == limitedPrinter);
_;
}
/** @notice Increases the token supply, with the newly created tokens
* being added to the balance of the specified account.
*
* @dev The function checks that the value to print does not
* exceed the supply ceiling when added to the current total supply.
* NOTE: printing to the zero address is disallowed.
*
* @param _receiver The receiving address of the print.
* @param _value The number of tokens to add to the total supply and the
* balance of the receiving address.
*/
function limitedPrint(address _receiver, uint256 _value) public onlyLimitedPrinter {
uint256 totalSupply = erc20Impl.totalSupply();
uint256 newTotalSupply = totalSupply + _value;
require(newTotalSupply >= totalSupply);
require(newTotalSupply <= totalSupplyCeiling);
erc20Impl.confirmPrint(erc20Impl.requestPrint(_receiver, _value));
}
/** @notice Requests an increase to the supply ceiling.
*
* @dev Returns a unique lock id associated with the request.
* Anyone can call this function, but confirming the request is authorized
* by the custodian.
*
* @param _raiseBy The amount by which to raise the ceiling.
*
* @return lockId A unique identifier for this request.
*/
function requestCeilingRaise(uint256 _raiseBy) public returns (bytes32 lockId) {
require(_raiseBy != 0);
lockId = generateLockId();
pendingRaiseMap[lockId] = PendingCeilingRaise({
raiseBy: _raiseBy
});
emit CeilingRaiseLocked(lockId, _raiseBy);
}
/** @notice Confirms a pending increase in the token supply.
*
* @dev When called by the custodian with a lock id associated with a
* pending ceiling increase, the amount requested is added to the
* current supply ceiling.
* NOTE: this function will not execute any raise that would overflow the
* supply ceiling, but it will not revert either.
*
* @param _lockId The identifier of a pending ceiling raise request.
*/
function confirmCeilingRaise(bytes32 _lockId) public onlyCustodian {
PendingCeilingRaise storage pendingRaise = pendingRaiseMap[_lockId];
// copy locals of references to struct members
uint256 raiseBy = pendingRaise.raiseBy;
// accounts for a gibberish _lockId
require(raiseBy != 0);
delete pendingRaiseMap[_lockId];
uint256 newCeiling = totalSupplyCeiling + raiseBy;
// overflow check
if (newCeiling >= totalSupplyCeiling) {
totalSupplyCeiling = newCeiling;
emit CeilingRaiseConfirmed(_lockId, raiseBy, newCeiling);
}
}
/** @notice Lowers the supply ceiling, further constraining the bound of
* what can be printed by the limited printer.
*
* @dev The limited printer is the sole authorized caller of this function,
* so it is the only account that can elect to lower its limit to increase
* the token supply.
*
* @param _lowerBy The amount by which to lower the supply ceiling.
*/
function lowerCeiling(uint256 _lowerBy) public onlyLimitedPrinter {
uint256 newCeiling = totalSupplyCeiling - _lowerBy;
// overflow check
require(newCeiling <= totalSupplyCeiling);
totalSupplyCeiling = newCeiling;
emit CeilingLowered(_lowerBy, newCeiling);
}
/** @notice Pass-through control of print confirmation, allowing this
* contract's custodian to act as the custodian of the associated
* active token implementation.
*
* @dev This contract is the direct custodian of the active token
* implementation, but this function allows this contract's custodian
* to act as though it were the direct custodian of the active
* token implementation. Therefore the custodian retains control of
* unlimited printing.
*
* @param _lockId The identifier of a pending print request in
* the associated active token implementation.
*/
function confirmPrintProxy(bytes32 _lockId) public onlyCustodian {
erc20Impl.confirmPrint(_lockId);
}
/** @notice Pass-through control of custodian change confirmation,
* allowing this contract's custodian to act as the custodian of
* the associated active token implementation.
*
* @dev This contract is the direct custodian of the active token
* implementation, but this function allows this contract's custodian
* to act as though it were the direct custodian of the active
* token implementation. Therefore the custodian retains control of
* custodian changes.
*
* @param _lockId The identifier of a pending custodian change request
* in the associated active token implementation.
*/
function confirmCustodianChangeProxy(bytes32 _lockId) public onlyCustodian {
erc20Impl.confirmCustodianChange(_lockId);
}
// EVENTS
/// @dev Emitted by successful `requestCeilingRaise` calls.
event CeilingRaiseLocked(bytes32 _lockId, uint256 _raiseBy);
/// @dev Emitted by successful `confirmCeilingRaise` calls.
event CeilingRaiseConfirmed(bytes32 _lockId, uint256 _raiseBy, uint256 _newCeiling);
/// @dev Emitted by successful `lowerCeiling` calls.
event CeilingLowered(uint256 _lowerBy, uint256 _newCeiling);
}
构造函数中传入了_erc20Impl(ERC20Impl合约地址)、_custodian(Custodian地址)、_limitedPrinter(受限发行人地址)和_initialCeiling(受限发行人初始最大发行量)。
通过Custodian可以改变受限发行人的最大发行量,以下三个函数处理:
function requestCeilingRaise(uint256 _raiseBy) public returns (bytes32 lockId)
function confirmCeilingRaise(bytes32 _lockId) public onlyCustodian
function lowerCeiling(uint256 _lowerBy) public onlyLimitedPrinter
受限发行人通过limitedPrint()函数就可以发行token,但是受限发行人不能无限发行token,是受totalSupplyCeiling这个变量控制的,而totalSupplyCeiling是由Custodian设置的。
合约中有ERC20Impl的变量erc20Impl,确认发行时会调用erc20Impl的confirmPrint函数。
同时在ERC20Impl中有两个函数,需要Custodian才能调用,如下:
function confirmPrint(bytes32 _lockId) public onlyCustodian
function confirmCustodianChange(bytes32 _lockId) public onlyCustodian
而ERC20Impl的Custodian是PrintLimiter合约地址,所以PrintLimiter实现了两个同样的函数,只是做下代理转发,这样就可以从PrintLimiter的Custodian调用了,如下:
function confirmPrintProxy(bytes32 _lockId) public onlyCustodian
function confirmCustodianChangeProxy(bytes32 _lockId) public onlyCustodian
ERC20Impl
看看ERC20Impl中的confirmPrint函数:
function confirmPrint(bytes32 _lockId) public onlyCustodian {
PendingPrint storage print = pendingPrintMap[_lockId];
// reject ‘null’ results from the map lookup
// this can only be the case if an unknown `_lockId` is received
address receiver = print.receiver;
require (receiver != address(0));
uint256 value = print.value;
delete pendingPrintMap[_lockId];
uint256 supply = erc20Store.totalSupply();
uint256 newSupply = supply + value;
if (newSupply >= supply) {
erc20Store.setTotalSupply(newSupply);
erc20Store.addBalance(receiver, value);
emit PrintingConfirmed(_lockId, receiver, value);
erc20Proxy.emitTransfer(address(0), receiver, value);
}
}
这个函数有onlyCustodian修饰,这也就是ERC20Impl的Custodian与ERC20Proxy和ERC20Store的Custodian不是一个的原因,因为ERC20Impl的Custodian是PrintLimiter合约地址。
结论
GUSD不像其它ERC20代币在合约建立时就设定了发行总量,而是存入美元才发行GUSD,通过PrintLimiter增加了代币发行的灵活性,可以在一定授权额度内自由发行,这也就是GUSD白皮书说的既有线下签名的安全,又有线上签名的灵活了。
Leave GUSD发行Token to:
Read more #gusd posts
Best Posts From 老鱼
We have not curated any of chaimyu's posts yet. But you can encourage our curation team to review posts by visiting them regularly and by referring other readers. Because we give priority to frequently read content.