Feature Tip: Add private address tag to any address under My Name Tag !
Latest 25 from a total of 28,592 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Transfer | 24486025 | 3 days ago | IN | 0 ETH | 0.00003828 | ||||
| Transfer | 24486014 | 3 days ago | IN | 0 ETH | 0.00001782 | ||||
| Transfer | 24483627 | 3 days ago | IN | 0 ETH | 0.00000458 | ||||
| Transfer | 24451776 | 8 days ago | IN | 0 ETH | 0.00006881 | ||||
| Transfer | 24441098 | 9 days ago | IN | 0 ETH | 0.00006224 | ||||
| Transfer | 24441088 | 9 days ago | IN | 0 ETH | 0.00003239 | ||||
| Transfer | 24431490 | 11 days ago | IN | 0 ETH | 0.00002107 | ||||
| Transfer | 24431487 | 11 days ago | IN | 0 ETH | 0.00000188 | ||||
| Approve | 24431038 | 11 days ago | IN | 0 ETH | 0.00006936 | ||||
| Transfer | 24428564 | 11 days ago | IN | 0 ETH | 0.00002521 | ||||
| Transfer | 24428561 | 11 days ago | IN | 0 ETH | 0.00000478 | ||||
| Approve | 24413676 | 13 days ago | IN | 0 ETH | 0.00000942 | ||||
| Transfer | 24411072 | 13 days ago | IN | 0 ETH | 0.00002249 | ||||
| Transfer | 24411067 | 13 days ago | IN | 0 ETH | 0.00000292 | ||||
| Transfer | 24406478 | 14 days ago | IN | 0 ETH | 0.00002564 | ||||
| Transfer | 24406468 | 14 days ago | IN | 0 ETH | 0.00000587 | ||||
| Approve | 24403882 | 14 days ago | IN | 0 ETH | 0.00007629 | ||||
| Transfer | 24392769 | 16 days ago | IN | 0 ETH | 0.00009705 | ||||
| Transfer | 24392766 | 16 days ago | IN | 0 ETH | 0.00011233 | ||||
| Approve | 24382817 | 17 days ago | IN | 0 ETH | 0.00007613 | ||||
| Transfer | 24377167 | 18 days ago | IN | 0 ETH | 0.00003845 | ||||
| Transfer | 24377166 | 18 days ago | IN | 0 ETH | 0.00001494 | ||||
| Transfer | 24353902 | 21 days ago | IN | 0 ETH | 0.00002047 | ||||
| Transfer | 24353898 | 21 days ago | IN | 0 ETH | 0.00000633 | ||||
| Transfer | 24353193 | 22 days ago | IN | 0 ETH | 0.00002381 |
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| Transfer | 19330910 | 724 days ago | 0.00144939 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
ERC20Proxy
Compiler Version
v0.5.10+commit.5a6ea5b1
Contract Source Code (Solidity Multiple files format)
pragma solidity ^0.5.10;
/** @title A contract for generating unique identifiers
*
* @notice A contract that provides an identifier generation scheme,
* guaranteeing uniqueness across all contracts that inherit from it,
* as well as the unpredictability of future identifiers.
*
* @dev This contract is intended to be inherited by any contract that
* implements the callback software pattern for cooperative custodianship.
*
*/
contract LockRequestable {
// MEMBERS
/// @notice the count of all invocations of `generateLockId`.
uint256 public lockRequestCount;
// CONSTRUCTOR
constructor() public {
lockRequestCount = 0;
}
// FUNCTIONS
/** @notice Returns a fresh unique identifier.
*
* @dev the generation scheme uses three components.
* First, the blockhash of the previous block.
* Second, the deployed address.
* Third, the next value of the counter.
* This ensures that identifiers are unique across all contracts
* following this scheme, and that future identifiers are
* unpredictable.
*
* @return a 32-byte unique identifier.
*/
function generateLockId() internal returns (bytes32 lockId) {
return keccak256(abi.encodePacked(blockhash(block.number - 1), address(this), ++lockRequestCount));
}
}
contract ERC20Interface {
// METHODS
// NOTE:
// public getter functions are not currently recognised as an
// implementation of the matching abstract function by the compiler.
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#name
// function name() public view returns (string);
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#symbol
// function symbol() public view returns (string);
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#totalsupply
// function decimals() public view returns (uint8);
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#totalsupply
function totalSupply() public view returns (uint256);
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#balanceof
function balanceOf(address _owner) public view returns (uint256 balance);
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#transfer
function transfer(address _to, uint256 _value) public returns (bool success);
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#transferfrom
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#approve
function approve(address _spender, uint256 _value) public returns (bool success);
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#allowance
function allowance(address _owner, address _spender) public view returns (uint256 remaining);
// EVENTS
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#transfer-1
event Transfer(address indexed _from, address indexed _to, uint256 _value);
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#approval
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}
/** @title A dual control contract.
*
* @notice A general-purpose contract that implements dual control over
* co-operating contracts through a callback mechanism.
*
* @dev This contract implements dual control through a 2-of-N
* threshold multi-signature scheme. The contract recognizes a set of N signers,
* and will unlock requests with signatures from any distinct pair of them.
* This contract signals the unlocking through a co-operative callback
* scheme.
* This contract also provides time lock and revocation features.
* Requests made by a 'primary' account have a default time lock applied.
* All other requests must pay a 1 ETH stake and have an extended time lock
* applied.
* A request that is completed will prevent all previous pending requests
* that share the same callback from being completed: this is the
* revocation feature.
*
*/
contract Custodian {
// TYPES
/** @dev The `Request` struct stores a pending unlocking.
* `callbackAddress` and `callbackSelector` are the data required to
* make a callback. The custodian completes the process by
* calling `callbackAddress.call(callbackSelector, lockId)`, which
* signals to the contract co-operating with the Custodian that
* the 2-of-N signatures have been provided and verified.
*/
struct Request {
bytes32 lockId;
bytes4 callbackSelector; // bytes4 and address can be packed into 1 word
address callbackAddress;
uint256 idx;
uint256 timestamp;
bool extended;
}
// EVENTS
/// @dev Emitted by successful `requestUnlock` calls.
event Requested(
bytes32 _lockId,
address _callbackAddress,
bytes4 _callbackSelector,
uint256 _nonce,
address _whitelistedAddress,
bytes32 _requestMsgHash,
uint256 _timeLockExpiry
);
/// @dev Emitted by `completeUnlock` calls on requests in the time-locked state.
event TimeLocked(
uint256 _timeLockExpiry,
bytes32 _requestMsgHash
);
/// @dev Emitted by successful `completeUnlock` calls.
event Completed(
bytes32 _lockId,
bytes32 _requestMsgHash,
address _signer1,
address _signer2
);
/// @dev Emitted by `completeUnlock` calls where the callback failed.
event Failed(
bytes32 _lockId,
bytes32 _requestMsgHash,
address _signer1,
address _signer2
);
/// @dev Emitted by successful `extendRequestTimeLock` calls.
event TimeLockExtended(
uint256 _timeLockExpiry,
bytes32 _requestMsgHash
);
// MEMBERS
/** @dev The count of all requests.
* This value is used as a nonce, incorporated into the request hash.
*/
uint256 public requestCount;
/// @dev The set of signers: signatures from two signers unlock a pending request.
mapping (address => bool) public signerSet;
/// @dev The map of request hashes to pending requests.
mapping (bytes32 => Request) public requestMap;
/// @dev The map of callback addresses to callback selectors to request indexes.
mapping (address => mapping (bytes4 => uint256)) public lastCompletedIdxs;
/** @dev The default period (in seconds) to time-lock requests.
* All requests will be subject to this default time lock, and the duration
* is fixed at contract creation.
*/
uint256 public defaultTimeLock;
/** @dev The extended period (in seconds) to time-lock requests.
* Requests not from the primary account are subject to this time lock.
* The primary account may also elect to extend the time lock on requests
* that originally received the default.
*/
uint256 public extendedTimeLock;
/// @dev The primary account is the privileged account for making requests.
address public primary;
// CONSTRUCTOR
constructor(
address[] memory _signers,
uint256 _defaultTimeLock,
uint256 _extendedTimeLock,
address _primary
)
public
{
// check for at least two `_signers`
require(_signers.length >= 2, "at least two `_signers`");
// validate time lock params
require(_defaultTimeLock <= _extendedTimeLock, "valid timelock params");
defaultTimeLock = _defaultTimeLock;
extendedTimeLock = _extendedTimeLock;
primary = _primary;
// explicitly initialize `requestCount` to zero
requestCount = 0;
// turn the array into a set
for (uint i = 0; i < _signers.length; i++) {
// no zero addresses or duplicates
require(_signers[i] != address(0) && !signerSet[_signers[i]], "no zero addresses or duplicates");
signerSet[_signers[i]] = true;
}
}
// MODIFIERS
modifier onlyPrimary {
require(msg.sender == primary, "only primary");
_;
}
modifier onlySigner {
require(signerSet[msg.sender], "only signer");
_;
}
// METHODS
/** @notice Requests an unlocking with a lock identifier and a callback.
*
* @dev If called by an account other than the primary a 1 ETH stake
* must be paid. When the request is unlocked stake will be transferred to the message sender.
* This is an anti-spam measure. As well as the callback
* and the lock identifier parameters a 'whitelisted address' is required
* for compatibility with existing signature schemes.
*
* @param _lockId The identifier of a pending request in a co-operating contract.
* @param _callbackAddress The address of a co-operating contract.
* @param _callbackSelector The function selector of a function within
* the co-operating contract at address `_callbackAddress`.
* @param _whitelistedAddress An address whitelisted in existing
* offline control protocols.
*
* @return requestMsgHash The hash of a request message to be signed.
*/
function requestUnlock(
bytes32 _lockId,
address _callbackAddress,
bytes4 _callbackSelector,
address _whitelistedAddress
)
public
payable
returns (bytes32 requestMsgHash)
{
require(msg.sender == primary || msg.value >= 1 ether, "sender is primary or stake is paid");
// disallow using a zero value for the callback address
require(_callbackAddress != address(0), "no zero value for callback address");
uint256 requestIdx = ++requestCount;
// compute a nonce value
// - the blockhash prevents prediction of future nonces
// - the address of this contract prevents conflicts with co-operating contracts using this scheme
// - the counter prevents conflicts arising from multiple txs within the same block
uint256 nonce = uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), address(this), requestIdx)));
requestMsgHash = keccak256(
abi.encodePacked(
nonce,
_whitelistedAddress,
uint256(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
)
);
requestMap[requestMsgHash] = Request({
lockId: _lockId,
callbackSelector: _callbackSelector,
callbackAddress: _callbackAddress,
idx: requestIdx,
timestamp: block.timestamp,
extended: false
});
// compute the expiry time
uint256 timeLockExpiry = block.timestamp;
if (msg.sender == primary) {
timeLockExpiry += defaultTimeLock;
} else {
timeLockExpiry += extendedTimeLock;
// any sender that is not the creator will get the extended time lock
requestMap[requestMsgHash].extended = true;
}
emit Requested(_lockId, _callbackAddress, _callbackSelector, nonce, _whitelistedAddress, requestMsgHash, timeLockExpiry);
}
/** @notice Completes a pending unlocking with two signatures.
*
* @dev Given a request message hash as two signatures of it from
* two distinct signers in the signer set, this function completes the
* unlocking of the pending request by executing the callback.
*
* @param _requestMsgHash The request message hash of a pending request.
* @param _recoveryByte1 The public key recovery byte (27 or 28)
* @param _ecdsaR1 The R component of an ECDSA signature (R, S) pair
* @param _ecdsaS1 The S component of an ECDSA signature (R, S) pair
* @param _recoveryByte2 The public key recovery byte (27 or 28)
* @param _ecdsaR2 The R component of an ECDSA signature (R, S) pair
* @param _ecdsaS2 The S component of an ECDSA signature (R, S) pair
*
* @return success True if the callback successfully executed.
*/
function completeUnlock(
bytes32 _requestMsgHash,
uint8 _recoveryByte1, bytes32 _ecdsaR1, bytes32 _ecdsaS1,
uint8 _recoveryByte2, bytes32 _ecdsaR2, bytes32 _ecdsaS2
)
public
onlySigner
returns (bool success)
{
Request storage request = requestMap[_requestMsgHash];
// copy storage to locals before `delete`
bytes32 lockId = request.lockId;
address callbackAddress = request.callbackAddress;
bytes4 callbackSelector = request.callbackSelector;
// failing case of the lookup if the callback address is zero
require(callbackAddress != address(0), "no zero value for callback address");
// reject confirms of earlier withdrawals buried under later confirmed withdrawals
require(request.idx > lastCompletedIdxs[callbackAddress][callbackSelector],
"reject confirms of earlier withdrawals buried under later confirmed withdrawals");
address signer1 = ecrecover(
keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _requestMsgHash)),
_recoveryByte1,
_ecdsaR1,
_ecdsaS1
);
require(signerSet[signer1], "signer is set");
address signer2 = ecrecover(
keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _requestMsgHash)),
_recoveryByte2,
_ecdsaR2,
_ecdsaS2
);
require(signerSet[signer2], "signer is set");
require(signer1 != signer2, "signers are different");
if (request.extended && ((block.timestamp - request.timestamp) < extendedTimeLock)) {
emit TimeLocked(request.timestamp + extendedTimeLock, _requestMsgHash);
return false;
} else if ((block.timestamp - request.timestamp) < defaultTimeLock) {
emit TimeLocked(request.timestamp + defaultTimeLock, _requestMsgHash);
return false;
} else {
if (address(this).balance > 0) {
// reward sender with anti-spam payments
msg.sender.transfer(address(this).balance);
}
// raise the waterline for the last completed unlocking
lastCompletedIdxs[callbackAddress][callbackSelector] = request.idx;
// and delete the request
delete requestMap[_requestMsgHash];
// invoke callback
(success,) = callbackAddress.call(abi.encodeWithSelector(callbackSelector, lockId));
if (success) {
emit Completed(lockId, _requestMsgHash, signer1, signer2);
} else {
emit Failed(lockId, _requestMsgHash, signer1, signer2);
}
}
}
/** @notice Reclaim the storage of a pending request that is uncompletable.
*
* @dev If a pending request shares the callback (address and selector) of
* a later request has been completed, then the request can no longer
* be completed. This function will reclaim the contract storage of the
* pending request.
*
* @param _requestMsgHash The request message hash of a pending request.
*/
function deleteUncompletableRequest(bytes32 _requestMsgHash) public {
Request storage request = requestMap[_requestMsgHash];
uint256 idx = request.idx;
require(0 < idx && idx < lastCompletedIdxs[request.callbackAddress][request.callbackSelector],
"there must be a completed latter request with same callback");
delete requestMap[_requestMsgHash];
}
/** @notice Extend the time lock of a pending request.
*
* @dev Requests made by the primary account receive the default time lock.
* This function allows the primary account to apply the extended time lock
* to one its own requests.
*
* @param _requestMsgHash The request message hash of a pending request.
*/
function extendRequestTimeLock(bytes32 _requestMsgHash) public onlyPrimary {
Request storage request = requestMap[_requestMsgHash];
// reject ‘null’ results from the map lookup
// this can only be the case if an unknown `_requestMsgHash` is received
require(request.callbackAddress != address(0), "reject ‘null’ results from the map lookup");
// `extendRequestTimeLock` must be idempotent
require(request.extended != true, "`extendRequestTimeLock` must be idempotent");
// set the `extended` flag; note that this is never unset
request.extended = true;
emit TimeLockExtended(request.timestamp + extendedTimeLock, _requestMsgHash);
}
}
/** @title A contract to inherit upgradeable custodianship.
*
* @notice A contract that provides re-usable code for upgradeable
* custodianship. That custodian may be an account or another contract.
*
* @dev This contract is intended to be inherited by any contract
* requiring a custodian to control some aspect of its functionality.
* This contract provides the mechanism for that custodianship to be
* passed from one custodian to the next.
*
*/
contract CustodianUpgradeable is LockRequestable {
// TYPES
/// @dev The struct type for pending custodian changes.
struct CustodianChangeRequest {
address proposedNew;
}
// MEMBERS
/// @dev The address of the account or contract that acts as the custodian.
address public custodian;
/// @dev The map of lock ids to pending custodian changes.
mapping (bytes32 => CustodianChangeRequest) public custodianChangeReqs;
// CONSTRUCTOR
constructor(
address _custodian
)
LockRequestable()
public
{
custodian = _custodian;
}
// MODIFIERS
modifier onlyCustodian {
require(msg.sender == custodian, "only custodian");
_;
}
// PUBLIC FUNCTIONS
// (UPGRADE)
/** @notice Requests a change of the custodian associated with this contract.
*
* @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 _proposedCustodian The address of the new custodian.
* @return lockId A unique identifier for this request.
*/
function requestCustodianChange(address _proposedCustodian) public returns (bytes32 lockId) {
require(_proposedCustodian != address(0), "no null value for `_proposedCustodian`");
lockId = generateLockId();
custodianChangeReqs[lockId] = CustodianChangeRequest({
proposedNew: _proposedCustodian
});
emit CustodianChangeRequested(lockId, msg.sender, _proposedCustodian);
}
/** @notice Confirms a pending change of the custodian associated with this contract.
*
* @dev When called by the current custodian with a lock id associated with a
* pending custodian change, the `address custodian` member will be updated with the
* requested address.
*
* @param _lockId The identifier of a pending change request.
*/
function confirmCustodianChange(bytes32 _lockId) public onlyCustodian {
custodian = getCustodianChangeReq(_lockId);
delete custodianChangeReqs[_lockId];
emit CustodianChangeConfirmed(_lockId, custodian);
}
// PRIVATE FUNCTIONS
function getCustodianChangeReq(bytes32 _lockId) private view returns (address _proposedNew) {
CustodianChangeRequest storage changeRequest = custodianChangeReqs[_lockId];
// reject ‘null’ results from the map lookup
// this can only be the case if an unknown `_lockId` is received
require(changeRequest.proposedNew != address(0), "reject ‘null’ results from the map lookup");
return changeRequest.proposedNew;
}
//EVENTS
/// @dev Emitted by successful `requestCustodianChange` calls.
event CustodianChangeRequested(
bytes32 _lockId,
address _msgSender,
address _proposedCustodian
);
/// @dev Emitted by successful `confirmCustodianChange` calls.
event CustodianChangeConfirmed(bytes32 _lockId, address _newCustodian);
}
/** @title A contract to inherit upgradeable token implementations.
*
* @notice A contract that provides re-usable code for upgradeable
* token implementations. It itself inherits from `CustodianUpgradable`
* as the upgrade process is controlled by the custodian.
*
* @dev This contract is intended to be inherited by any contract
* requiring a reference to the active token implementation, either
* to delegate calls to it, or authorize calls from it. This contract
* provides the mechanism for that implementation to be replaced,
* which constitutes an implementation upgrade.
*
*/
contract ERC20ImplUpgradeable is CustodianUpgradeable {
// TYPES
/// @dev The struct type for pending implementation changes.
struct ImplChangeRequest {
address proposedNew;
}
// MEMBERS
// @dev The reference to the active token implementation.
ERC20Impl public erc20Impl;
/// @dev The map of lock ids to pending implementation changes.
mapping (bytes32 => ImplChangeRequest) public implChangeReqs;
// CONSTRUCTOR
constructor(address _custodian) CustodianUpgradeable(_custodian) public {
erc20Impl = ERC20Impl(0x0);
}
// MODIFIERS
modifier onlyImpl {
require(msg.sender == address(erc20Impl), "only ERC20Impl");
_;
}
// PUBLIC FUNCTIONS
// (UPGRADE)
/** @notice Requests a change of the active implementation associated
* with this contract.
*
* @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 _proposedImpl The address of the new active implementation.
* @return lockId A unique identifier for this request.
*/
function requestImplChange(address _proposedImpl) public returns (bytes32 lockId) {
require(_proposedImpl != address(0), "no null value for `_proposedImpl`");
lockId = generateLockId();
implChangeReqs[lockId] = ImplChangeRequest({
proposedNew: _proposedImpl
});
emit ImplChangeRequested(lockId, msg.sender, _proposedImpl);
}
/** @notice Confirms a pending change of the active implementation
* associated with this contract.
*
* @dev When called by the custodian with a lock id associated with a
* pending change, the `ERC20Impl erc20Impl` member will be updated
* with the requested address.
*
* @param _lockId The identifier of a pending change request.
*/
function confirmImplChange(bytes32 _lockId) public onlyCustodian {
erc20Impl = getImplChangeReq(_lockId);
delete implChangeReqs[_lockId];
emit ImplChangeConfirmed(_lockId, address(erc20Impl));
}
// PRIVATE FUNCTIONS
function getImplChangeReq(bytes32 _lockId) private view returns (ERC20Impl _proposedNew) {
ImplChangeRequest storage changeRequest = implChangeReqs[_lockId];
// reject ‘null’ results from the map lookup
// this can only be the case if an unknown `_lockId` is received
require(changeRequest.proposedNew != address(0), "reject ‘null’ results from the map lookup");
return ERC20Impl(changeRequest.proposedNew);
}
//EVENTS
/// @dev Emitted by successful `requestImplChange` calls.
event ImplChangeRequested(
bytes32 _lockId,
address _msgSender,
address _proposedImpl
);
/// @dev Emitted by successful `confirmImplChange` calls.
event ImplChangeConfirmed(bytes32 _lockId, address _newImpl);
}
/** @title Public interface to ERC20 compliant token.
*
* @notice This contract is a permanent entry point to an ERC20 compliant
* system of contracts.
*
* @dev This contract contains no business logic and instead
* delegates to an instance of ERC20Impl. This contract also has no storage
* that constitutes the operational state of the token. This contract is
* upgradeable in the sense that the `custodian` can update the
* `erc20Impl` address, thus redirecting the delegation of business logic.
* The `custodian` is also authorized to pass custodianship.
*
*/
contract ERC20Proxy is ERC20Interface, ERC20ImplUpgradeable {
// MEMBERS
/// @notice Returns the name of the token.
string public name;
/// @notice Returns the symbol of the token.
string public symbol;
/// @notice Returns the number of decimals the token uses.
uint8 public decimals;
// CONSTRUCTOR
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals,
address _custodian
)
ERC20ImplUpgradeable(_custodian)
public
{
name = _name;
symbol = _symbol;
decimals = _decimals;
}
// PUBLIC FUNCTIONS
// (ERC20Interface)
/** @notice Returns the total token supply.
*
* @return the total token supply.
*/
function totalSupply() public view returns (uint256) {
return erc20Impl.totalSupply();
}
/** @notice Returns the account balance of another account with an address
* `_owner`.
*
* @return balance the balance of account with address `_owner`.
*/
function balanceOf(address _owner) public view returns (uint256 balance) {
return erc20Impl.balanceOf(_owner);
}
/** @dev Internal use only.
*/
function emitTransfer(address _from, address _to, uint256 _value) public onlyImpl {
emit Transfer(_from, _to, _value);
}
/** @notice Transfers `_value` amount of tokens to address `_to`.
*
* @dev Will fire the `Transfer` event. Will revert if the `_from`
* account balance does not have enough tokens to spend.
*
* @return success true if transfer completes.
*/
function transfer(address _to, uint256 _value) public returns (bool success) {
return erc20Impl.transferWithSender(msg.sender, _to, _value);
}
/** @notice Transfers `_value` amount of tokens from address `_from`
* to address `_to`.
*
* @dev Will fire the `Transfer` event. Will revert unless the `_from`
* account has deliberately authorized the sender of the message
* via some mechanism.
*
* @return success true if transfer completes.
*/
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
return erc20Impl.transferFromWithSender(msg.sender, _from, _to, _value);
}
/** @dev Internal use only.
*/
function emitApproval(address _owner, address _spender, uint256 _value) public onlyImpl {
emit Approval(_owner, _spender, _value);
}
/** @notice Allows `_spender` to withdraw from your account multiple times,
* up to the `_value` amount. If this function is called again it
* overwrites the current allowance with _value.
*
* @dev Will fire the `Approval` event.
*
* @return success true if approval completes.
*/
function approve(address _spender, uint256 _value) public returns (bool success) {
return erc20Impl.approveWithSender(msg.sender, _spender, _value);
}
/** @notice Increases the amount `_spender` is allowed to withdraw from
* your account.
* This function is implemented to avoid the race condition in standard
* ERC20 contracts surrounding the `approve` method.
*
* @dev Will fire the `Approval` event. This function should be used instead of
* `approve`.
*
* @return success true if approval completes.
*/
function increaseApproval(address _spender, uint256 _addedValue) public returns (bool success) {
return erc20Impl.increaseApprovalWithSender(msg.sender, _spender, _addedValue);
}
/** @notice Decreases the amount `_spender` is allowed to withdraw from
* your account. This function is implemented to avoid the race
* condition in standard ERC20 contracts surrounding the `approve` method.
*
* @dev Will fire the `Approval` event. This function should be used
* instead of `approve`.
*
* @return success true if approval completes.
*/
function decreaseApproval(address _spender, uint256 _subtractedValue) public returns (bool success) {
return erc20Impl.decreaseApprovalWithSender(msg.sender, _spender, _subtractedValue);
}
/** @notice Returns how much `_spender` is currently allowed to spend from
* `_owner`'s balance.
*
* @return remaining the remaining allowance.
*/
function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
return erc20Impl.allowance(_owner, _spender);
}
}
/** @title ERC20 compliant token balance store.
*
* @notice This contract serves as the store of balances, allowances, and
* supply for the ERC20 compliant token. No business logic exists here.
*
* @dev This contract contains no business logic and instead
* is the final destination for any change in balances, allowances, or token
* supply. This contract is upgradeable in the sense that its custodian can
* update the `erc20Impl` address, thus redirecting the source of logic that
* determines how the balances will be updated.
*
*/
contract ERC20Store is ERC20ImplUpgradeable {
// MEMBERS
/// @dev The total token supply.
uint256 public totalSupply;
/// @dev The mapping of balances.
mapping (address => uint256) public balances;
/// @dev The mapping of allowances.
mapping (address => mapping (address => uint256)) public allowed;
// CONSTRUCTOR
constructor(address _custodian) ERC20ImplUpgradeable(_custodian) public {
totalSupply = 0;
}
// PUBLIC FUNCTIONS
// (ERC20 Ledger)
/** @notice The function to set the total supply of tokens.
*
* @dev Intended for use by token implementation functions
* that update the total supply. The only authorized caller
* is the active implementation.
*
* @param _newTotalSupply the value to set as the new total supply
*/
function setTotalSupply(
uint256 _newTotalSupply
)
public
onlyImpl
{
totalSupply = _newTotalSupply;
}
/** @notice Sets how much `_owner` allows `_spender` to transfer on behalf
* of `_owner`.
*
* @dev Intended for use by token implementation functions
* that update spending allowances. The only authorized caller
* is the active implementation.
*
* @param _owner The account that will allow an on-behalf-of spend.
* @param _spender The account that will spend on behalf of the owner.
* @param _value The limit of what can be spent.
*/
function setAllowance(
address _owner,
address _spender,
uint256 _value
)
public
onlyImpl
{
allowed[_owner][_spender] = _value;
}
/** @notice Sets the balance of `_owner` to `_newBalance`.
*
* @dev Intended for use by token implementation functions
* that update balances. The only authorized caller
* is the active implementation.
*
* @param _owner The account that will hold a new balance.
* @param _newBalance The balance to set.
*/
function setBalance(
address _owner,
uint256 _newBalance
)
public
onlyImpl
{
balances[_owner] = _newBalance;
}
/** @notice Adds `_balanceIncrease` to `_owner`'s balance.
*
* @dev Intended for use by token implementation functions
* that update balances. The only authorized caller
* is the active implementation.
* WARNING: the caller is responsible for preventing overflow.
*
* @param _owner The account that will hold a new balance.
* @param _balanceIncrease The balance to add.
*/
function addBalance(
address _owner,
uint256 _balanceIncrease
)
public
onlyImpl
{
balances[_owner] = balances[_owner] + _balanceIncrease;
}
}
/** @title ERC20 compliant token intermediary contract holding core logic.
*
* @notice This contract serves as an intermediary between the exposed ERC20
* interface in ERC20Proxy and the store of balances in ERC20Store. This
* contract contains core logic that the proxy can delegate to
* and that the store is called by.
*
* @dev This contract contains the core logic to implement the
* ERC20 specification as well as several extensions.
* 1. Changes to the token supply.
* 2. Batched transfers.
* 3. Relative changes to spending approvals.
* 4. Delegated transfer control ('sweeping').
*
*/
contract ERC20Impl is CustodianUpgradeable {
// TYPES
/// @dev The struct type for pending increases to the token supply (print).
struct PendingPrint {
address receiver;
uint256 value;
}
// MEMBERS
/// @dev The reference to the proxy.
ERC20Proxy public erc20Proxy;
/// @dev The reference to the store.
ERC20Store public erc20Store;
/// @dev The sole authorized caller of delegated transfer control ('sweeping').
address public sweeper;
/** @dev The static message to be signed by an external account that
* signifies their permission to forward their balance to any arbitrary
* address. This is used to consolidate the control of all accounts
* backed by a shared keychain into the control of a single key.
* Initialized as the concatenation of the address of this contract
* and the word "sweep". This concatenation is done to prevent a replay
* attack in a subsequent contract, where the sweeping message could
* potentially be replayed to re-enable sweeping ability.
*/
bytes32 public sweepMsg;
/** @dev The mapping that stores whether the address in question has
* enabled sweeping its contents to another account or not.
* If an address maps to "true", it has already enabled sweeping,
* and thus does not need to re-sign the `sweepMsg` to enact the sweep.
*/
mapping (address => bool) public sweptSet;
/// @dev The map of lock ids to pending token increases.
mapping (bytes32 => PendingPrint) public pendingPrintMap;
/// @dev The map of blocked addresses.
mapping (address => bool) public blocked;
// CONSTRUCTOR
constructor(
address _erc20Proxy,
address _erc20Store,
address _custodian,
address _sweeper
)
CustodianUpgradeable(_custodian)
public
{
require(_sweeper != address(0), "no null value for `_sweeper`");
erc20Proxy = ERC20Proxy(_erc20Proxy);
erc20Store = ERC20Store(_erc20Store);
sweeper = _sweeper;
sweepMsg = keccak256(abi.encodePacked(address(this), "sweep"));
}
// MODIFIERS
modifier onlyProxy {
require(msg.sender == address(erc20Proxy), "only ERC20Proxy");
_;
}
modifier onlySweeper {
require(msg.sender == sweeper, "only sweeper");
_;
}
/** @notice Core logic of the ERC20 `approve` function.
*
* @dev This function can only be called by the referenced proxy,
* which has an `approve` function.
* Every argument passed to that function as well as the original
* `msg.sender` gets passed to this function.
* NOTE: approvals for the zero address (unspendable) are disallowed.
*
* @param _sender The address initiating the approval in a proxy.
*/
function approveWithSender(
address _sender,
address _spender,
uint256 _value
)
public
onlyProxy
returns (bool success)
{
require(_spender != address(0), "no null value for `_spender`");
require(blocked[_sender] != true, "_sender must not be blocked");
require(blocked[_spender] != true, "_spender must not be blocked");
erc20Store.setAllowance(_sender, _spender, _value);
erc20Proxy.emitApproval(_sender, _spender, _value);
return true;
}
/** @notice Core logic of the `increaseApproval` function.
*
* @dev This function can only be called by the referenced proxy,
* which has an `increaseApproval` function.
* Every argument passed to that function as well as the original
* `msg.sender` gets passed to this function.
* NOTE: approvals for the zero address (unspendable) are disallowed.
*
* @param _sender The address initiating the approval.
*/
function increaseApprovalWithSender(
address _sender,
address _spender,
uint256 _addedValue
)
public
onlyProxy
returns (bool success)
{
require(_spender != address(0),"no null value for_spender");
require(blocked[_sender] != true, "_sender must not be blocked");
require(blocked[_spender] != true, "_spender must not be blocked");
uint256 currentAllowance = erc20Store.allowed(_sender, _spender);
uint256 newAllowance = currentAllowance + _addedValue;
require(newAllowance >= currentAllowance, "new allowance must not be smaller than previous");
erc20Store.setAllowance(_sender, _spender, newAllowance);
erc20Proxy.emitApproval(_sender, _spender, newAllowance);
return true;
}
/** @notice Core logic of the `decreaseApproval` function.
*
* @dev This function can only be called by the referenced proxy,
* which has a `decreaseApproval` function.
* Every argument passed to that function as well as the original
* `msg.sender` gets passed to this function.
* NOTE: approvals for the zero address (unspendable) are disallowed.
*
* @param _sender The address initiating the approval.
*/
function decreaseApprovalWithSender(
address _sender,
address _spender,
uint256 _subtractedValue
)
public
onlyProxy
returns (bool success)
{
require(_spender != address(0), "no unspendable approvals"); // disallow unspendable approvals
require(blocked[_sender] != true, "_sender must not be blocked");
require(blocked[_spender] != true, "_spender must not be blocked");
uint256 currentAllowance = erc20Store.allowed(_sender, _spender);
uint256 newAllowance = currentAllowance - _subtractedValue;
require(newAllowance <= currentAllowance, "new allowance must not be smaller than previous");
erc20Store.setAllowance(_sender, _spender, newAllowance);
erc20Proxy.emitApproval(_sender, _spender, newAllowance);
return true;
}
/** @notice Requests an increase in the token supply, with the newly created
* tokens to be added to the balance of the specified account.
*
* @dev Returns a unique lock id associated with the request.
* Anyone can call this function, but confirming the request is authorized
* by the custodian.
* NOTE: printing to the zero address is disallowed.
*
* @param _receiver The receiving address of the print, if confirmed.
* @param _value The number of tokens to add to the total supply and the
* balance of the receiving address, if confirmed.
*
* @return lockId A unique identifier for this request.
*/
function requestPrint(address _receiver, uint256 _value) public returns (bytes32 lockId) {
require(_receiver != address(0), "no null value for `_receiver`");
require(blocked[msg.sender] != true, "account blocked");
require(blocked[_receiver] != true, "_receiver must not be blocked");
lockId = generateLockId();
pendingPrintMap[lockId] = PendingPrint({
receiver: _receiver,
value: _value
});
emit PrintingLocked(lockId, _receiver, _value);
}
/** @notice Confirms a pending increase in the token supply.
*
* @dev When called by the custodian with a lock id associated with a
* pending increase, the amount requested to be printed in the print request
* is printed to the receiving address specified in that same request.
* NOTE: this function will not execute any print that would overflow the
* total supply, but it will not revert either.
*
* @param _lockId The identifier of a pending print request.
*/
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), "unknown `_lockId`");
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);
}
}
/** @notice Burns the specified value from the sender's balance.
*
* @dev Sender's balanced is subtracted by the amount they wish to burn.
*
* @param _value The amount to burn.
*
* @return success true if the burn succeeded.
*/
function burn(uint256 _value) public returns (bool success) {
require(blocked[msg.sender] != true, "account blocked");
uint256 balanceOfSender = erc20Store.balances(msg.sender);
require(_value <= balanceOfSender, "disallow burning more, than amount of the balance");
erc20Store.setBalance(msg.sender, balanceOfSender - _value);
erc20Store.setTotalSupply(erc20Store.totalSupply() - _value);
erc20Proxy.emitTransfer(msg.sender, address(0), _value);
return true;
}
/** @notice Burns the specified value from the balance in question.
*
* @dev Suspected balance is subtracted by the amount which will be burnt.
*
* @dev If the suspected balance has less than the amount requested, it will be set to 0.
*
* @param _from The address of suspected balance.
*
* @param _value The amount to burn.
*
* @return success true if the burn succeeded.
*/
function burn(address _from, uint256 _value) public onlyCustodian returns (bool success) {
uint256 balance = erc20Store.balances(_from);
if(_value <= balance){
erc20Store.setBalance(_from, balance - _value);
erc20Store.setTotalSupply(erc20Store.totalSupply() - _value);
erc20Proxy.emitTransfer(_from, address(0), _value);
emit Wiped(_from, _value, _value, balance - _value);
}
else {
erc20Store.setBalance(_from,0);
erc20Store.setTotalSupply(erc20Store.totalSupply() - balance);
erc20Proxy.emitTransfer(_from, address(0), balance);
emit Wiped(_from, _value, balance, 0);
}
return true;
}
/** @notice A function for a sender to issue multiple transfers to multiple
* different addresses at once. This function is implemented for gas
* considerations when someone wishes to transfer, as one transaction is
* cheaper than issuing several distinct individual `transfer` transactions.
*
* @dev By specifying a set of destination addresses and values, the
* sender can issue one transaction to transfer multiple amounts to
* distinct addresses, rather than issuing each as a separate
* transaction. The `_tos` and `_values` arrays must be equal length, and
* an index in one array corresponds to the same index in the other array
* (e.g. `_tos[0]` will receive `_values[0]`, `_tos[1]` will receive
* `_values[1]`, and so on.)
* NOTE: transfers to the zero address are disallowed.
*
* @param _tos The destination addresses to receive the transfers.
* @param _values The values for each destination address.
* @return success If transfers succeeded.
*/
function batchTransfer(address[] memory _tos, uint256[] memory _values) public returns (bool success) {
require(_tos.length == _values.length, "_tos and _values must be the same length");
require(blocked[msg.sender] != true, "account blocked");
uint256 numTransfers = _tos.length;
uint256 senderBalance = erc20Store.balances(msg.sender);
for (uint256 i = 0; i < numTransfers; i++) {
address to = _tos[i];
require(to != address(0), "no null values for _tos");
require(blocked[to] != true, "_tos must not be blocked");
uint256 v = _values[i];
require(senderBalance >= v, "insufficient funds");
if (msg.sender != to) {
senderBalance -= v;
erc20Store.addBalance(to, v);
}
erc20Proxy.emitTransfer(msg.sender, to, v);
}
erc20Store.setBalance(msg.sender, senderBalance);
return true;
}
/** @notice Enables the delegation of transfer control for many
* accounts to the sweeper account, transferring any balances
* as well to the given destination.
*
* @dev An account delegates transfer control by signing the
* value of `sweepMsg`. The sweeper account is the only authorized
* caller of this function, so it must relay signatures on behalf
* of accounts that delegate transfer control to it. Enabling
* delegation is idempotent and permanent. If the account has a
* balance at the time of enabling delegation, its balance is
* also transferred to the given destination account `_to`.
* NOTE: transfers to the zero address are disallowed.
*
* @param _vs The array of recovery byte components of the ECDSA signatures.
* @param _rs The array of 'R' components of the ECDSA signatures.
* @param _ss The array of 'S' components of the ECDSA signatures.
* @param _to The destination for swept balances.
*/
function enableSweep(uint8[] memory _vs, bytes32[] memory _rs, bytes32[] memory _ss, address _to) public onlySweeper {
require(_to != address(0), "no null value for `_to`");
require(blocked[_to] != true, "_to must not be blocked");
require((_vs.length == _rs.length) && (_vs.length == _ss.length), "_vs[], _rs[], _ss lengths are different");
uint256 numSignatures = _vs.length;
uint256 sweptBalance = 0;
for (uint256 i = 0; i < numSignatures; ++i) {
address from = ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32",sweepMsg)), _vs[i], _rs[i], _ss[i]);
require(blocked[from] != true, "_froms must not be blocked");
// ecrecover returns 0 on malformed input
if (from != address(0)) {
sweptSet[from] = true;
uint256 fromBalance = erc20Store.balances(from);
if (fromBalance > 0) {
sweptBalance += fromBalance;
erc20Store.setBalance(from, 0);
erc20Proxy.emitTransfer(from, _to, fromBalance);
}
}
}
if (sweptBalance > 0) {
erc20Store.addBalance(_to, sweptBalance);
}
}
/** @notice For accounts that have delegated, transfer control
* to the sweeper, this function transfers their balances to the given
* destination.
*
* @dev The sweeper account is the only authorized caller of
* this function. This function accepts an array of addresses to have their
* balances transferred for gas efficiency purposes.
* NOTE: any address for an account that has not been previously enabled
* will be ignored.
* NOTE: transfers to the zero address are disallowed.
*
* @param _froms The addresses to have their balances swept.
* @param _to The destination address of all these transfers.
*/
function replaySweep(address[] memory _froms, address _to) public onlySweeper {
require(_to != address(0), "no null value for `_to`");
require(blocked[_to] != true, "_to must not be blocked");
uint256 lenFroms = _froms.length;
uint256 sweptBalance = 0;
for (uint256 i = 0; i < lenFroms; ++i) {
address from = _froms[i];
require(blocked[from] != true, "_froms must not be blocked");
if (sweptSet[from]) {
uint256 fromBalance = erc20Store.balances(from);
if (fromBalance > 0) {
sweptBalance += fromBalance;
erc20Store.setBalance(from, 0);
erc20Proxy.emitTransfer(from, _to, fromBalance);
}
}
}
if (sweptBalance > 0) {
erc20Store.addBalance(_to, sweptBalance);
}
}
/** @notice Core logic of the ERC20 `transferFrom` function.
*
* @dev This function can only be called by the referenced proxy,
* which has a `transferFrom` function.
* Every argument passed to that function as well as the original
* `msg.sender` gets passed to this function.
* NOTE: transfers to the zero address are disallowed.
*
* @param _sender The address initiating the transfer in a proxy.
*/
function transferFromWithSender(
address _sender,
address _from,
address _to,
uint256 _value
)
public
onlyProxy
returns (bool success)
{
require(_to != address(0), "no null values for `_to`");
require(blocked[_sender] != true, "_sender must not be blocked");
require(blocked[_from] != true, "_from must not be blocked");
require(blocked[_to] != true, "_to must not be blocked");
uint256 balanceOfFrom = erc20Store.balances(_from);
require(_value <= balanceOfFrom, "insufficient funds on `_from` balance");
uint256 senderAllowance = erc20Store.allowed(_from, _sender);
require(_value <= senderAllowance, "insufficient allowance amount");
erc20Store.setBalance(_from, balanceOfFrom - _value);
erc20Store.addBalance(_to, _value);
erc20Store.setAllowance(_from, _sender, senderAllowance - _value);
erc20Proxy.emitTransfer(_from, _to, _value);
return true;
}
/** @notice Core logic of the ERC20 `transfer` function.
*
* @dev This function can only be called by the referenced proxy,
* which has a `transfer` function.
* Every argument passed to that function as well as the original
* `msg.sender` gets passed to this function.
* NOTE: transfers to the zero address are disallowed.
*
* @param _sender The address initiating the transfer in a proxy.
*/
function transferWithSender(
address _sender,
address _to,
uint256 _value
)
public
onlyProxy
returns (bool success)
{
require(_to != address(0), "no null value for `_to`");
require(blocked[_sender] != true, "_sender must not be blocked");
require(blocked[_to] != true, "_to must not be blocked");
uint256 balanceOfSender = erc20Store.balances(_sender);
require(_value <= balanceOfSender, "insufficient funds");
erc20Store.setBalance(_sender, balanceOfSender - _value);
erc20Store.addBalance(_to, _value);
erc20Proxy.emitTransfer(_sender, _to, _value);
return true;
}
/** @notice Transfers the specified value from the balance in question.
*
* @dev Suspected balance is subtracted by the amount which will be transferred.
*
* @dev If the suspected balance has less than the amount requested, it will be set to 0.
*
* @param _from The address of suspected balance.
*
* @param _value The amount to transfer.
*
* @return success true if the transfer succeeded.
*/
function forceTransfer(
address _from,
address _to,
uint256 _value
)
public
onlyCustodian
returns (bool success)
{
require(_to != address(0), "no null value for `_to`");
uint256 balanceOfSender = erc20Store.balances(_from);
if(_value <= balanceOfSender) {
erc20Store.setBalance(_from, balanceOfSender - _value);
erc20Store.addBalance(_to, _value);
erc20Proxy.emitTransfer(_from, _to, _value);
} else {
erc20Store.setBalance(_from, 0);
erc20Store.addBalance(_to, balanceOfSender);
erc20Proxy.emitTransfer(_from, _to, balanceOfSender);
}
return true;
}
// METHODS (ERC20 sub interface impl.)
/// @notice Core logic of the ERC20 `totalSupply` function.
function totalSupply() public view returns (uint256) {
return erc20Store.totalSupply();
}
/// @notice Core logic of the ERC20 `balanceOf` function.
function balanceOf(address _owner) public view returns (uint256 balance) {
return erc20Store.balances(_owner);
}
/// @notice Core logic of the ERC20 `allowance` function.
function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
return erc20Store.allowed(_owner, _spender);
}
/// @dev internal use only.
function blockWallet(address wallet) public onlyCustodian returns (bool success) {
blocked[wallet] = true;
return true;
}
/// @dev internal use only.
function unblockWallet(address wallet) public onlyCustodian returns (bool success) {
blocked[wallet] = false;
return true;
}
// EVENTS
/// @dev Emitted by successful `requestPrint` calls.
event PrintingLocked(bytes32 _lockId, address _receiver, uint256 _value);
/// @dev Emitted by successful `confirmPrint` calls.
event PrintingConfirmed(bytes32 _lockId, address _receiver, uint256 _value);
/** @dev Emitted by successful `confirmWipe` calls.
*
* @param _value Amount requested to be burned.
*
* @param _burned Amount which was burned.
*
* @param _balance Amount left on account after burn.
*
* @param _from Account which balance was burned.
*/
event Wiped(address _from, uint256 _value, uint256 _burned, uint _balance);
}
/** @title A contact to govern hybrid control over increases to the token supply and managing accounts.
*
* @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 'controller') to increase the token supply up to a ceiling,
* and this supply ceiling can only be raised by the custodian.
*
*/
contract Controller is LockRequestable {
// TYPES
/// @dev The struct type for pending ceiling raises.
struct PendingCeilingRaise {
uint256 raiseBy;
}
/// @dev The struct type for pending wipes.
struct wipeAddress {
uint256 value;
address from;
}
/// @dev The struct type for pending force transfer requests.
struct forceTransferRequest {
uint256 value;
address from;
address to;
}
// 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.
Custodian public custodian;
/** @dev The sole authorized caller of limited printing.
* This account is also authorized to lower the supply ceiling and
* wiping suspected accounts or force transferring funds from them.
*/
address public controller;
/** @dev The maximum that the token supply can be increased to
* through the use of the limited printing feature.
* The difference between the current total supply and the supply
* ceiling is what is available to the 'controller' 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;
/// @dev The map of lock ids to pending wipes.
mapping (bytes32 => wipeAddress[]) public pendingWipeMap;
/// @dev The map of lock ids to pending force transfer requests.
mapping (bytes32 => forceTransferRequest) public pendingForceTransferRequestMap;
// CONSTRUCTOR
constructor(
address _erc20Impl,
address _custodian,
address _controller,
uint256 _initialCeiling
)
public
{
erc20Impl = ERC20Impl(_erc20Impl);
custodian = Custodian(_custodian);
controller = _controller;
totalSupplyCeiling = _initialCeiling;
}
// MODIFIERS
modifier onlyCustodian {
require(msg.sender == address(custodian), "only custodian");
_;
}
modifier onlyController {
require(msg.sender == controller, "only controller");
_;
}
modifier onlySigner {
require(custodian.signerSet(msg.sender) == true, "only signer");
_;
}
/** @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 onlyController {
uint256 totalSupply = erc20Impl.totalSupply();
uint256 newTotalSupply = totalSupply + _value;
require(newTotalSupply >= totalSupply, "new total supply overflow");
require(newTotalSupply <= totalSupplyCeiling, "total supply ceiling overflow");
erc20Impl.confirmPrint(erc20Impl.requestPrint(_receiver, _value));
}
/** @notice Requests wipe of suspected accounts.
*
* @dev Returns a unique lock id associated with the request.
* Only controller can call this function, and only the custodian
* can confirm the request.
*
* @param _froms The array of suspected accounts.
*
* @param _values array of amounts by which suspected accounts will be wiped.
*
* @return lockId A unique identifier for this request.
*/
function requestWipe(address[] memory _froms, uint256[] memory _values) public onlyController returns (bytes32 lockId) {
require(_froms.length == _values.length, "_froms[] and _values[] must be same length");
lockId = generateLockId();
uint256 amount = _froms.length;
for(uint256 i = 0; i < amount; i++) {
address from = _froms[i];
uint256 value = _values[i];
pendingWipeMap[lockId].push(wipeAddress(value, from));
}
emit WipeRequested(lockId);
return lockId;
}
/** @notice Confirms a pending wipe of suspected accounts.
*
* @dev When called by the custodian with a lock id associated with a
* pending wipe, the amount requested is burned from the suspected accounts.
*
* @param _lockId The identifier of a pending wipe request.
*/
function confirmWipe(bytes32 _lockId) public onlyCustodian {
uint256 amount = pendingWipeMap[_lockId].length;
for(uint256 i = 0; i < amount; i++) {
wipeAddress memory addr = pendingWipeMap[_lockId][i];
address from = addr.from;
uint256 value = addr.value;
erc20Impl.burn(from, value);
}
delete pendingWipeMap[_lockId];
emit WipeCompleted(_lockId);
}
/** @notice Requests force transfer from the suspected account.
*
* @dev Returns a unique lock id associated with the request.
* Only controller can call this function, and only the custodian
* can confirm the request.
*
* @param _from address of suspected account.
*
* @param _to address of reciever.
*
* @param _value amount which will be transferred.
*
* @return lockId A unique identifier for this request.
*/
function requestForceTransfer(address _from, address _to, uint256 _value) public onlyController returns (bytes32 lockId) {
lockId = generateLockId();
require (_value != 0, "no zero value transfers");
pendingForceTransferRequestMap[lockId] = forceTransferRequest(_value, _from, _to);
emit ForceTransferRequested(lockId, _from, _to, _value);
return lockId;
}
/** @notice Confirms a pending force transfer request.
*
* @dev When called by the custodian with a lock id associated with a
* pending transfer request, the amount requested is transferred from the suspected account.
*
* @param _lockId The identifier of a pending transfer request.
*/
function confirmForceTransfer(bytes32 _lockId) public onlyCustodian {
address from = pendingForceTransferRequestMap[_lockId].from;
address to = pendingForceTransferRequestMap[_lockId].to;
uint256 value = pendingForceTransferRequestMap[_lockId].value;
delete pendingForceTransferRequestMap[_lockId];
erc20Impl.forceTransfer(from, to, value);
emit ForceTransferCompleted(_lockId, from, to, 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, "no zero ceiling raise");
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, "no gibberish _lockId");
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 controller.
*
* @dev The controller 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 onlyController {
uint256 newCeiling = totalSupplyCeiling - _lowerBy;
// overflow check
require(newCeiling <= totalSupplyCeiling, "totalSupplyCeiling overflow");
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);
}
/** @notice Blocks all transactions with a wallet.
*
* @dev Only signers from custodian are authorized to call this function
*
* @param wallet account which will be blocked.
*/
function blockWallet(address wallet) public onlySigner {
erc20Impl.blockWallet(wallet);
emit Blocked(wallet);
}
/** @notice Unblocks all transactions with a wallet.
*
* @dev Only signers from custodian are authorized to call this function
*
* @param wallet account which will be unblocked.
*/
function unblockWallet(address wallet) public onlySigner {
erc20Impl.unblockWallet(wallet);
emit Unblocked(wallet);
}
// 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);
/// @dev Emitted by successful `blockWallet` calls.
event Blocked(address _wallet);
/// @dev Emitted by successful `unblockWallet` calls.
event Unblocked(address _wallet);
/// @dev Emitted by successful `requestForceTransfer` calls.
event ForceTransferRequested(bytes32 _lockId, address _from, address _to, uint256 _value);
/// @dev Emitted by successful `confirmForceTransfer` calls.
event ForceTransferCompleted(bytes32 _lockId, address _from, address _to, uint256 _value);
/// @dev Emitted by successful `requestWipe` calls.
event WipeRequested(bytes32 _lockId);
/// @dev Emitted by successful `confirmWipe` calls.
event WipeCompleted(bytes32 _lockId);
}pragma solidity ^0.5.10;
/**
* @title contract for generating HEX pointer
* for functions.
*
* @dev this contract is a tool meant to be used
* on local JavaScript VM.
*
*/
contract callbackSelector {
/**
* @notice function which returns function HEX pointer (callbackSelector)
*
* @param _function function name with parameter types. Case and whitespace sensitive.
*
* @dev example: `function get(string memory _function)`
* _function: `get(string)`
* result: `0x693ec85e`
*/
function get(string memory _function) public pure returns (bytes4) {
return bytes4(keccak256(abi.encodePacked(_function)));
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_proposedCustodian","type":"address"}],"name":"requestCustodianChange","outputs":[{"name":"lockId","type":"bytes32"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"emitTransfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"custodian","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_lockId","type":"bytes32"}],"name":"confirmCustodianChange","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"erc20Impl","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_proposedImpl","type":"address"}],"name":"requestImplChange","outputs":[{"name":"lockId","type":"bytes32"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"emitApproval","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_subtractedValue","type":"uint256"}],"name":"decreaseApproval","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_lockId","type":"bytes32"}],"name":"confirmImplChange","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"implChangeReqs","outputs":[{"name":"proposedNew","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lockRequestCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"custodianChangeReqs","outputs":[{"name":"proposedNew","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_addedValue","type":"uint256"}],"name":"increaseApproval","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_name","type":"string"},{"name":"_symbol","type":"string"},{"name":"_decimals","type":"uint8"},{"name":"_custodian","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_lockId","type":"bytes32"},{"indexed":false,"name":"_msgSender","type":"address"},{"indexed":false,"name":"_proposedImpl","type":"address"}],"name":"ImplChangeRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_lockId","type":"bytes32"},{"indexed":false,"name":"_newImpl","type":"address"}],"name":"ImplChangeConfirmed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_lockId","type":"bytes32"},{"indexed":false,"name":"_msgSender","type":"address"},{"indexed":false,"name":"_proposedCustodian","type":"address"}],"name":"CustodianChangeRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_lockId","type":"bytes32"},{"indexed":false,"name":"_newCustodian","type":"address"}],"name":"CustodianChangeConfirmed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Approval","type":"event"}]Contract Creation Code
60806040523480156200001157600080fd5b50604051620011e7380380620011e7833981810160405260808110156200003757600080fd5b8101908080516401000000008111156200005057600080fd5b820160208101848111156200006457600080fd5b81516401000000008111828201871017156200007f57600080fd5b505092919060200180516401000000008111156200009c57600080fd5b82016020810184811115620000b057600080fd5b8151640100000000811182820187101715620000cb57600080fd5b505060208083015160409093015160008055600180546001600160a01b0383166001600160a01b03199182161790915560038054909116905586519295509293506200011d9160059187019062000152565b5082516200013390600690602086019062000152565b50506007805460ff191660ff9290921691909117905550620001f79050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200019557805160ff1916838001178555620001c5565b82800160010185558215620001c5579182015b82811115620001c5578251825591602001919060010190620001a8565b50620001d3929150620001d7565b5090565b620001f491905b80821115620001d35760008155600101620001de565b90565b610fe080620002076000396000f3fe608060405234801561001057600080fd5b50600436106101425760003560e01c80635687f2b8116100b8578063a9059cbb1161007c578063a9059cbb146103ec578063b508069b14610418578063cb81fecf14610435578063cf6e44881461043d578063d73dd6231461045a578063dd62ed3e1461048657610142565b80635687f2b81461033f578063661884631461037557806370a08231146103a15780638181b029146103c757806395d89b41146103e457610142565b806323de66511161010a57806323de66511461027a578063313ce567146102b2578063375b74c3146102d05780633a8343ee146102f45780633c389cc41461031157806348f9e2461461031957610142565b806306fdde0314610147578063095ea7b3146101c457806315b210821461020457806318160ddd1461023c57806323b872dd14610244575b600080fd5b61014f6104b4565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610189578181015183820152602001610171565b50505050905090810190601f1680156101b65780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101f0600480360360408110156101da57600080fd5b506001600160a01b038135169060200135610542565b604080519115158252519081900360200190f35b61022a6004803603602081101561021a57600080fd5b50356001600160a01b03166105d4565b60408051918252519081900360200190f35b61022a6106aa565b6101f06004803603606081101561025a57600080fd5b506001600160a01b03813581169160208101359091169060400135610720565b6102b06004803603606081101561029057600080fd5b506001600160a01b038135811691602081013590911690604001356107bb565b005b6102ba61085b565b6040805160ff9092168252519081900360200190f35b6102d8610864565b604080516001600160a01b039092168252519081900360200190f35b6102b06004803603602081101561030a57600080fd5b5035610873565b6102d8610940565b61022a6004803603602081101561032f57600080fd5b50356001600160a01b031661094f565b6102b06004803603606081101561035557600080fd5b506001600160a01b03813581169160208101359091169060400135610a25565b6101f06004803603604081101561038b57600080fd5b506001600160a01b038135169060200135610ac5565b61022a600480360360208110156103b757600080fd5b50356001600160a01b0316610b24565b6102b0600480360360208110156103dd57600080fd5b5035610ba7565b61014f610c74565b6101f06004803603604081101561040257600080fd5b506001600160a01b038135169060200135610ccf565b6102d86004803603602081101561042e57600080fd5b5035610d2e565b61022a610d49565b6102d86004803603602081101561045357600080fd5b5035610d4f565b6101f06004803603604081101561047057600080fd5b506001600160a01b038135169060200135610d6a565b61022a6004803603604081101561049c57600080fd5b506001600160a01b0381358116916020013516610dc9565b6005805460408051602060026001851615610100026000190190941693909304601f8101849004840282018401909252818152929183018282801561053a5780601f1061050f5761010080835404028352916020019161053a565b820191906000526020600020905b81548152906001019060200180831161051d57829003601f168201915b505050505081565b6003546040805163448327e960e11b81523360048201526001600160a01b03858116602483015260448201859052915160009392909216916389064fd29160648082019260209290919082900301818787803b1580156105a157600080fd5b505af11580156105b5573d6000803e3d6000fd5b505050506040513d60208110156105cb57600080fd5b50519392505050565b60006001600160a01b03821661061b5760405162461bcd60e51b8152600401808060200182810382526026815260200180610f386026913960400191505060405180910390fd5b610623610e36565b60408051602080820183526001600160a01b038087168084526000868152600284528590209351845492166001600160a01b031990921691909117909255825184815233918101919091528083019190915290519192507fd76fc900a7e1a6fcf11d54b7ba943918df6c53a3128140658c389b3da1e997ba919081900360600190a1919050565b600354604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd916004808301926020929190829003018186803b1580156106ef57600080fd5b505afa158015610703573d6000803e3d6000fd5b505050506040513d602081101561071957600080fd5b5051905090565b60035460408051635d5e22cd60e01b81523360048201526001600160a01b03868116602483015285811660448301526064820185905291516000939290921691635d5e22cd9160848082019260209290919082900301818787803b15801561078757600080fd5b505af115801561079b573d6000803e3d6000fd5b505050506040513d60208110156107b157600080fd5b5051949350505050565b6003546001600160a01b0316331461080b576040805162461bcd60e51b815260206004820152600e60248201526d1bdb9b1e48115490cc8c125b5c1b60921b604482015290519081900360640190fd5b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a3505050565b60075460ff1681565b6001546001600160a01b031681565b6001546001600160a01b031633146108c3576040805162461bcd60e51b815260206004820152600e60248201526d37b7363c9031bab9ba37b234b0b760911b604482015290519081900360640190fd5b6108cc81610e7f565b600180546001600160a01b039283166001600160a01b031991821617825560008481526002602090815260409182902080549093169092559154825185815293169083015280517f9a99272c0f6b7a30ef9e76e684a7cd408bfd4f11a72f36a8e276253c920e442d9281900390910190a150565b6003546001600160a01b031681565b60006001600160a01b0382166109965760405162461bcd60e51b8152600401808060200182810382526021815260200180610f5e6021913960400191505060405180910390fd5b61099e610e36565b60408051602080820183526001600160a01b038087168084526000868152600484528590209351845492166001600160a01b031990921691909117909255825184815233918101919091528083019190915290519192507f5df12834436b8dc248df3f7f1796a3e39f851d610be49cdcd92514fa821b9f97919081900360600190a1919050565b6003546001600160a01b03163314610a75576040805162461bcd60e51b815260206004820152600e60248201526d1bdb9b1e48115490cc8c125b5c1b60921b604482015290519081900360640190fd5b816001600160a01b0316836001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040518082815260200191505060405180910390a3505050565b600354604080516361e1077d60e01b81523360048201526001600160a01b03858116602483015260448201859052915160009392909216916361e1077d9160648082019260209290919082900301818787803b1580156105a157600080fd5b600354604080516370a0823160e01b81526001600160a01b038481166004830152915160009392909216916370a0823191602480820192602092909190829003018186803b158015610b7557600080fd5b505afa158015610b89573d6000803e3d6000fd5b505050506040513d6020811015610b9f57600080fd5b505192915050565b6001546001600160a01b03163314610bf7576040805162461bcd60e51b815260206004820152600e60248201526d37b7363c9031bab9ba37b234b0b760911b604482015290519081900360640190fd5b610c0081610ee3565b600380546001600160a01b039283166001600160a01b031991821617825560008481526004602090815260409182902080549093169092559154825185815293169083015280517f9d55b0349a0a4c5b511f72228170bb91d45c9ac78dba8ab5b4175d3ed42f06b39281900390910190a150565b6006805460408051602060026001851615610100026000190190941693909304601f8101849004840282018401909252818152929183018282801561053a5780601f1061050f5761010080835404028352916020019161053a565b60035460408051636ff0786560e11b81523360048201526001600160a01b038581166024830152604482018590529151600093929092169163dfe0f0ca9160648082019260209290919082900301818787803b1580156105a157600080fd5b6004602052600090815260409020546001600160a01b031681565b60005481565b6002602052600090815260409020546001600160a01b031681565b60035460408051632e0179b560e01b81523360048201526001600160a01b0385811660248301526044820185905291516000939290921691632e0179b59160648082019260209290919082900301818787803b1580156105a157600080fd5b60035460408051636eb1769f60e11b81526001600160a01b03858116600483015284811660248301529151600093929092169163dd62ed3e91604480820192602092909190829003018186803b158015610e2257600080fd5b505afa1580156105b5573d6000803e3d6000fd5b6000805460010190819055604080516000194301406020808301919091523060601b82840152605480830194909452825180830390940184526074909101909152815191012090565b600081815260026020526040812080546001600160a01b0316610ed35760405162461bcd60e51b815260040180806020018281038252602d815260200180610f7f602d913960400191505060405180910390fd5b546001600160a01b031692915050565b600081815260046020526040812080546001600160a01b0316610ed35760405162461bcd60e51b815260040180806020018281038252602d815260200180610f7f602d913960400191505060405180910390fdfe6e6f206e756c6c2076616c756520666f7220605f70726f706f736564437573746f6469616e606e6f206e756c6c2076616c756520666f7220605f70726f706f736564496d706c6072656a65637420e280986e756c6ce2809920726573756c74732066726f6d20746865206d6170206c6f6f6b7570a265627a7a72305820baabbacc8ceca23a4dae3f8ac5b64485590af765813d6aa70a3b3d801f959a4564736f6c634300050a0032000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000d95096d10dc3f7d9a6e583381a56ee69913e87a000000000000000000000000000000000000000000000000000000000000000652657661696e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035245560000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101425760003560e01c80635687f2b8116100b8578063a9059cbb1161007c578063a9059cbb146103ec578063b508069b14610418578063cb81fecf14610435578063cf6e44881461043d578063d73dd6231461045a578063dd62ed3e1461048657610142565b80635687f2b81461033f578063661884631461037557806370a08231146103a15780638181b029146103c757806395d89b41146103e457610142565b806323de66511161010a57806323de66511461027a578063313ce567146102b2578063375b74c3146102d05780633a8343ee146102f45780633c389cc41461031157806348f9e2461461031957610142565b806306fdde0314610147578063095ea7b3146101c457806315b210821461020457806318160ddd1461023c57806323b872dd14610244575b600080fd5b61014f6104b4565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610189578181015183820152602001610171565b50505050905090810190601f1680156101b65780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101f0600480360360408110156101da57600080fd5b506001600160a01b038135169060200135610542565b604080519115158252519081900360200190f35b61022a6004803603602081101561021a57600080fd5b50356001600160a01b03166105d4565b60408051918252519081900360200190f35b61022a6106aa565b6101f06004803603606081101561025a57600080fd5b506001600160a01b03813581169160208101359091169060400135610720565b6102b06004803603606081101561029057600080fd5b506001600160a01b038135811691602081013590911690604001356107bb565b005b6102ba61085b565b6040805160ff9092168252519081900360200190f35b6102d8610864565b604080516001600160a01b039092168252519081900360200190f35b6102b06004803603602081101561030a57600080fd5b5035610873565b6102d8610940565b61022a6004803603602081101561032f57600080fd5b50356001600160a01b031661094f565b6102b06004803603606081101561035557600080fd5b506001600160a01b03813581169160208101359091169060400135610a25565b6101f06004803603604081101561038b57600080fd5b506001600160a01b038135169060200135610ac5565b61022a600480360360208110156103b757600080fd5b50356001600160a01b0316610b24565b6102b0600480360360208110156103dd57600080fd5b5035610ba7565b61014f610c74565b6101f06004803603604081101561040257600080fd5b506001600160a01b038135169060200135610ccf565b6102d86004803603602081101561042e57600080fd5b5035610d2e565b61022a610d49565b6102d86004803603602081101561045357600080fd5b5035610d4f565b6101f06004803603604081101561047057600080fd5b506001600160a01b038135169060200135610d6a565b61022a6004803603604081101561049c57600080fd5b506001600160a01b0381358116916020013516610dc9565b6005805460408051602060026001851615610100026000190190941693909304601f8101849004840282018401909252818152929183018282801561053a5780601f1061050f5761010080835404028352916020019161053a565b820191906000526020600020905b81548152906001019060200180831161051d57829003601f168201915b505050505081565b6003546040805163448327e960e11b81523360048201526001600160a01b03858116602483015260448201859052915160009392909216916389064fd29160648082019260209290919082900301818787803b1580156105a157600080fd5b505af11580156105b5573d6000803e3d6000fd5b505050506040513d60208110156105cb57600080fd5b50519392505050565b60006001600160a01b03821661061b5760405162461bcd60e51b8152600401808060200182810382526026815260200180610f386026913960400191505060405180910390fd5b610623610e36565b60408051602080820183526001600160a01b038087168084526000868152600284528590209351845492166001600160a01b031990921691909117909255825184815233918101919091528083019190915290519192507fd76fc900a7e1a6fcf11d54b7ba943918df6c53a3128140658c389b3da1e997ba919081900360600190a1919050565b600354604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd916004808301926020929190829003018186803b1580156106ef57600080fd5b505afa158015610703573d6000803e3d6000fd5b505050506040513d602081101561071957600080fd5b5051905090565b60035460408051635d5e22cd60e01b81523360048201526001600160a01b03868116602483015285811660448301526064820185905291516000939290921691635d5e22cd9160848082019260209290919082900301818787803b15801561078757600080fd5b505af115801561079b573d6000803e3d6000fd5b505050506040513d60208110156107b157600080fd5b5051949350505050565b6003546001600160a01b0316331461080b576040805162461bcd60e51b815260206004820152600e60248201526d1bdb9b1e48115490cc8c125b5c1b60921b604482015290519081900360640190fd5b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a3505050565b60075460ff1681565b6001546001600160a01b031681565b6001546001600160a01b031633146108c3576040805162461bcd60e51b815260206004820152600e60248201526d37b7363c9031bab9ba37b234b0b760911b604482015290519081900360640190fd5b6108cc81610e7f565b600180546001600160a01b039283166001600160a01b031991821617825560008481526002602090815260409182902080549093169092559154825185815293169083015280517f9a99272c0f6b7a30ef9e76e684a7cd408bfd4f11a72f36a8e276253c920e442d9281900390910190a150565b6003546001600160a01b031681565b60006001600160a01b0382166109965760405162461bcd60e51b8152600401808060200182810382526021815260200180610f5e6021913960400191505060405180910390fd5b61099e610e36565b60408051602080820183526001600160a01b038087168084526000868152600484528590209351845492166001600160a01b031990921691909117909255825184815233918101919091528083019190915290519192507f5df12834436b8dc248df3f7f1796a3e39f851d610be49cdcd92514fa821b9f97919081900360600190a1919050565b6003546001600160a01b03163314610a75576040805162461bcd60e51b815260206004820152600e60248201526d1bdb9b1e48115490cc8c125b5c1b60921b604482015290519081900360640190fd5b816001600160a01b0316836001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040518082815260200191505060405180910390a3505050565b600354604080516361e1077d60e01b81523360048201526001600160a01b03858116602483015260448201859052915160009392909216916361e1077d9160648082019260209290919082900301818787803b1580156105a157600080fd5b600354604080516370a0823160e01b81526001600160a01b038481166004830152915160009392909216916370a0823191602480820192602092909190829003018186803b158015610b7557600080fd5b505afa158015610b89573d6000803e3d6000fd5b505050506040513d6020811015610b9f57600080fd5b505192915050565b6001546001600160a01b03163314610bf7576040805162461bcd60e51b815260206004820152600e60248201526d37b7363c9031bab9ba37b234b0b760911b604482015290519081900360640190fd5b610c0081610ee3565b600380546001600160a01b039283166001600160a01b031991821617825560008481526004602090815260409182902080549093169092559154825185815293169083015280517f9d55b0349a0a4c5b511f72228170bb91d45c9ac78dba8ab5b4175d3ed42f06b39281900390910190a150565b6006805460408051602060026001851615610100026000190190941693909304601f8101849004840282018401909252818152929183018282801561053a5780601f1061050f5761010080835404028352916020019161053a565b60035460408051636ff0786560e11b81523360048201526001600160a01b038581166024830152604482018590529151600093929092169163dfe0f0ca9160648082019260209290919082900301818787803b1580156105a157600080fd5b6004602052600090815260409020546001600160a01b031681565b60005481565b6002602052600090815260409020546001600160a01b031681565b60035460408051632e0179b560e01b81523360048201526001600160a01b0385811660248301526044820185905291516000939290921691632e0179b59160648082019260209290919082900301818787803b1580156105a157600080fd5b60035460408051636eb1769f60e11b81526001600160a01b03858116600483015284811660248301529151600093929092169163dd62ed3e91604480820192602092909190829003018186803b158015610e2257600080fd5b505afa1580156105b5573d6000803e3d6000fd5b6000805460010190819055604080516000194301406020808301919091523060601b82840152605480830194909452825180830390940184526074909101909152815191012090565b600081815260026020526040812080546001600160a01b0316610ed35760405162461bcd60e51b815260040180806020018281038252602d815260200180610f7f602d913960400191505060405180910390fd5b546001600160a01b031692915050565b600081815260046020526040812080546001600160a01b0316610ed35760405162461bcd60e51b815260040180806020018281038252602d815260200180610f7f602d913960400191505060405180910390fdfe6e6f206e756c6c2076616c756520666f7220605f70726f706f736564437573746f6469616e606e6f206e756c6c2076616c756520666f7220605f70726f706f736564496d706c6072656a65637420e280986e756c6ce2809920726573756c74732066726f6d20746865206d6170206c6f6f6b7570a265627a7a72305820baabbacc8ceca23a4dae3f8ac5b64485590af765813d6aa70a3b3d801f959a4564736f6c634300050a0032
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000d95096d10dc3f7d9a6e583381a56ee69913e87a000000000000000000000000000000000000000000000000000000000000000652657661696e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035245560000000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _name (string): Revain
Arg [1] : _symbol (string): REV
Arg [2] : _decimals (uint8): 6
Arg [3] : _custodian (address): 0x0D95096d10DC3f7d9a6e583381A56EE69913e87a
-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [1] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [3] : 0000000000000000000000000d95096d10dc3f7d9a6e583381a56ee69913e87a
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [5] : 52657661696e0000000000000000000000000000000000000000000000000000
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [7] : 5245560000000000000000000000000000000000000000000000000000000000
Deployed Bytecode Sourcemap
24708:4623:1:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;24708:4623:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;24838:18;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:100:-1;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;24838:18:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27600:162;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;27600:162:1;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;18563:428;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;18563:428:1;-1:-1:-1;;;;;18563:428:1;;:::i;:::-;;;;;;;;;;;;;;;;25488:100;;;:::i;26888:184::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;26888:184:1;;;;;;;;;;;;;;;;;:::i;25952:132::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;25952:132:1;;;;;;;;;;;;;;;;;:::i;:::-;;25004:21;;;:::i;:::-;;;;;;;;;;;;;;;;;;;17659:24;;;:::i;:::-;;;;-1:-1:-1;;;;;17659:24:1;;;;;;;;;;;;;;19383:235;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;19383:235:1;;:::i;21372:26::-;;;:::i;22292:383::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;22292:383:1;-1:-1:-1;;;;;22292:383:1;;:::i;27119:144::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;27119:144:1;;;;;;;;;;;;;;;;;:::i;28791:200::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;28791:200:1;;;;;;;;:::i;25781:124::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;25781:124:1;-1:-1:-1;;;;;25781:124:1;;:::i;23071:224::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;23071:224:1;;:::i;24913:20::-;;;:::i;26374:154::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;26374:154:1;;;;;;;;:::i;21474:60::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;21474:60:1;;:::i;562:31::-;;;:::i;17754:70::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;17754:70:1;;:::i;28185:190::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;28185:190:1;;;;;;;;:::i;29175:154::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;29175:154:1;;;;;;;;;;:::i;24838:18::-;;;;;;;;;;;;;;;-1:-1:-1;;24838:18:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;27600:162::-;27698:9;;:57;;;-1:-1:-1;;;27698:57:1;;27726:10;27698:57;;;;-1:-1:-1;;;;;27698:57:1;;;;;;;;;;;;;;;27667:12;;27698:9;;;;;:27;;:57;;;;;;;;;;;;;;;27667:12;27698:9;:57;;;5:2:-1;;;;30:1;27;20:12;5:2;27698:57:1;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;27698:57:1;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;27698:57:1;;27600:162;-1:-1:-1;;;27600:162:1:o;18563:428::-;18639:14;-1:-1:-1;;;;;18673:32:1;;18665:83;;;;-1:-1:-1;;;18665:83:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;18768:16;:14;:16::i;:::-;18825:79;;;;;;;;;-1:-1:-1;;;;;18825:79:1;;;;;;-1:-1:-1;18795:27:1;;;:19;:27;;;;;:109;;;;;;-1:-1:-1;;;;;;18795:109:1;;;;;;;;;;18920:64;;;;;18953:10;18920:64;;;;;;;;;;;;;;;;18759:25;;-1:-1:-1;18920:64:1;;;;;;;;;;18563:428;;;:::o;25488:100::-;25558:9;;:23;;;-1:-1:-1;;;25558:23:1;;;;25532:7;;-1:-1:-1;;;;;25558:9:1;;:21;;:23;;;;;;;;;;;;;;:9;:23;;;5:2:-1;;;;30:1;27;20:12;5:2;25558:23:1;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;25558:23:1;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;25558:23:1;;-1:-1:-1;25488:100:1;:::o;26888:184::-;27001:9;;:64;;;-1:-1:-1;;;27001:64:1;;27034:10;27001:64;;;;-1:-1:-1;;;;;27001:64:1;;;;;;;;;;;;;;;;;;;;;;26970:12;;27001:9;;;;;:32;;:64;;;;;;;;;;;;;;;26970:12;27001:9;:64;;;5:2:-1;;;;30:1;27;20:12;5:2;27001:64:1;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;27001:64:1;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;27001:64:1;;26888:184;-1:-1:-1;;;;26888:184:1:o;25952:132::-;21756:9;;-1:-1:-1;;;;;21756:9:1;21734:10;:32;21726:59;;;;;-1:-1:-1;;;21726:59:1;;;;;;;;;;;;-1:-1:-1;;;21726:59:1;;;;;;;;;;;;;;;26065:3;-1:-1:-1;;;;;26049:28:1;26058:5;-1:-1:-1;;;;;26049:28:1;;26070:6;26049:28;;;;;;;;;;;;;;;;;;25952:132;;;:::o;25004:21::-;;;;;;:::o;17659:24::-;;;-1:-1:-1;;;;;17659:24:1;;:::o;19383:235::-;18054:9;;-1:-1:-1;;;;;18054:9:1;18040:10;:23;18032:50;;;;;-1:-1:-1;;;18032:50:1;;;;;;;;;;;;-1:-1:-1;;;18032:50:1;;;;;;;;;;;;;;;19475:30;19497:7;19475:21;:30::i;:::-;19463:9;:42;;-1:-1:-1;;;;;19463:42:1;;;-1:-1:-1;;;;;;19463:42:1;;;;;;:9;19523:28;;;:19;:28;;;;;;;;;19516:35;;;;;;;;19601:9;;19567:44;;;;;19601:9;;19567:44;;;;;;;;;;;;;;;;19383:235;:::o;21372:26::-;;;-1:-1:-1;;;;;21372:26:1;;:::o;22292:383::-;22358:14;-1:-1:-1;;;;;22392:27:1;;22384:73;;;;-1:-1:-1;;;22384:73:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;22477:16;:14;:16::i;:::-;22529:69;;;;;;;;;-1:-1:-1;;;;;22529:69:1;;;;;;-1:-1:-1;22504:22:1;;;:14;:22;;;;;:94;;;;;;-1:-1:-1;;;;;;22504:94:1;;;;;;;;;;22614:54;;;;;22642:10;22614:54;;;;;;;;;;;;;;;;22468:25;;-1:-1:-1;22614:54:1;;;;;;;;;;22292:383;;;:::o;27119:144::-;21756:9;;-1:-1:-1;;;;;21756:9:1;21734:10;:32;21726:59;;;;;-1:-1:-1;;;21726:59:1;;;;;;;;;;;;-1:-1:-1;;;21726:59:1;;;;;;;;;;;;;;;27239:8;-1:-1:-1;;;;;27222:34:1;27231:6;-1:-1:-1;;;;;27222:34:1;;27249:6;27222:34;;;;;;;;;;;;;;;;;;27119:144;;;:::o;28791:200::-;28908:9;;:76;;;-1:-1:-1;;;28908:76:1;;28945:10;28908:76;;;;-1:-1:-1;;;;;28908:76:1;;;;;;;;;;;;;;;28877:12;;28908:9;;;;;:36;;:76;;;;;;;;;;;;;;;28877:12;28908:9;:76;;;5:2:-1;;;;30:1;27;20:12;25781:124:1;25871:9;;:27;;;-1:-1:-1;;;25871:27:1;;-1:-1:-1;;;;;25871:27:1;;;;;;;;;25837:15;;25871:9;;;;;:19;;:27;;;;;;;;;;;;;;;:9;:27;;;5:2:-1;;;;30:1;27;20:12;5:2;25871:27:1;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;25871:27:1;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;25871:27:1;;25781:124;-1:-1:-1;;25781:124:1:o;23071:224::-;18054:9;;-1:-1:-1;;;;;18054:9:1;18040:10;:23;18032:50;;;;;-1:-1:-1;;;18032:50:1;;;;;;;;;;;;-1:-1:-1;;;18032:50:1;;;;;;;;;;;;;;;23158:25;23175:7;23158:16;:25::i;:::-;23146:9;:37;;-1:-1:-1;;;;;23146:37:1;;;-1:-1:-1;;;;;;23146:37:1;;;;;;:9;23201:23;;;:14;:23;;;;;;;;;23194:30;;;;;;;;23277:9;;23240:48;;;;;23277:9;;23240:48;;;;;;;;;;;;;;;;23071:224;:::o;24913:20::-;;;;;;;;;;;;;;;-1:-1:-1;;24913:20:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;26374:154;26468:9;;:53;;;-1:-1:-1;;;26468:53:1;;26497:10;26468:53;;;;-1:-1:-1;;;;;26468:53:1;;;;;;;;;;;;;;;26437:12;;26468:9;;;;;:28;;:53;;;;;;;;;;;;;;;26437:12;26468:9;:53;;;5:2:-1;;;;30:1;27;20:12;21474:60:1;;;;;;;;;;;;-1:-1:-1;;;;;21474:60:1;;:::o;562:31::-;;;;:::o;17754:70::-;;;;;;;;;;;;-1:-1:-1;;;;;17754:70:1;;:::o;28185:190::-;28297:9;;:71;;;-1:-1:-1;;;28297:71:1;;28334:10;28297:71;;;;-1:-1:-1;;;;;28297:71:1;;;;;;;;;;;;;;;28266:12;;28297:9;;;;;:36;;:71;;;;;;;;;;;;;;;28266:12;28297:9;:71;;;5:2:-1;;;;30:1;27;20:12;29175:154:1;29285:9;;:37;;;-1:-1:-1;;;29285:37:1;;-1:-1:-1;;;;;29285:37:1;;;;;;;;;;;;;;;;29249:17;;29285:9;;;;;:19;;:37;;;;;;;;;;;;;;;:9;:37;;;5:2:-1;;;;30:1;27;20:12;5:2;29285:37:1;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;1170:175:1;1214:14;1318:18;;1299:1;1318:18;;;;;1257:80;;;-1:-1:-1;;1284:12:1;:16;1274:27;1257:80;;;;;;;;1311:4;1257:80;;;;;;;;;;;;;;;;26:21:-1;;;22:32;;;6:49;;1257:80:1;;;;;;;1247:91;;;;;1170:175;:::o;19649:465::-;19719:20;19798:28;;;:19;:28;;;;;19975:25;;-1:-1:-1;;;;;19975:25:1;19967:97;;;;-1:-1:-1;;;19967:97:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;20082:25;-1:-1:-1;;;;;20082:25:1;;19649:465;-1:-1:-1;;19649:465:1:o;23326:463::-;23391:22;23467:23;;;:14;:23;;;;;23639:25;;-1:-1:-1;;;;;23639:25:1;23631:97;;;;-1:-1:-1;;;23631:97:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Swarm Source
bzzr://baabbacc8ceca23a4dae3f8ac5b64485590af765813d6aa70a3b3d801f959a45
Loading...
Loading
Loading...
Loading
OVERVIEW
Revain is a blockchain-based review platform for the crypto community. Revain's ultimate goal is to provide high-quality reviews on all global products and services using emerging technologies like blockchain and AI.Net Worth in USD
$1,030.41
Net Worth in ETH
0.521811
Token Allocations
BNB
72.94%
USDT
12.13%
TUSD
9.21%
Others
5.72%
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| BSC | 72.94% | $621.69 | 1.2089 | $751.59 | |
| BSC | 1.36% | $0.999651 | 13.9762 | $13.97 | |
| BSC | 0.57% | $1,975.23 | 0.00296001 | $5.85 | |
| BSC | 0.54% | $0.51827 | 10.7293 | $5.56 | |
| BSC | 0.34% | $0.00 | 1,780,062 | $0.00 | |
| BSC | 0.25% | $1.31 | 2 | $2.62 | |
| BSC | 0.03% | <$0.000001 | 5,921,990.8848 | $0.3031 | |
| ETH | 12.13% | $0.999651 | 125 | $124.96 | |
| ETH | 9.21% | $0.999198 | 95 | $94.92 | |
| ETH | 2.63% | $0.000004 | 6,772,092.8172 | $27.09 |
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.