我们看过这个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白皮书说的既有线下签名的安全,又有线上签名的灵活了。
Hello! Your post has been resteemed and upvoted by @ilovecoding because we love coding! Keep up good work! Consider upvoting this comment to support the @ilovecoding and increase your future rewards! ^_^ Steem On!
Reply !stop to disable the comment. Thanks!