ERC20
This contract is an ERC20 token.
Name
Illuvium
Symbol
ILV
Decimals
18
Total Supply
7,000,000 ILV
About link
Illuvium (ILV) is a cryptocurrency and operates on the Ethereum platform. Illuvium has a current supply of 7,000,000 with 651,440.25822608 in circulation. The last known price of Illuvium is 94.70447146 USD and is down -17.11 over the last 24 hours. It is currently trading on 13 active market(s) with $7,163,868.25 traded over the last 24 hours. More information can be found at https://illuvium.io/.
Stats
Public Functions
24
Event Types
9
Code Size
61,514 bytes
Events (9) keyboard_arrow_up
Constants (20) keyboard_arrow_up
ROLE_ACCESS_MANAGER Constant
uint256 help
0x8000000000000000000000000000000000000000000000000000000000000000
State Variables (7) keyboard_arrow_up
Functions
features keyboard_arrow_up
updateFeatures keyboard_arrow_up
updateRole keyboard_arrow_up
Requirements help
null
Source Code
function updateRole(address operator, uint256 role) public {
// caller must have a permission to update user roles
require(
isSenderInRole(ROLE_ACCESS_MANAGER),
"insufficient privileges (ROLE_ACCESS_MANAGER required)"
);
// evaluate the role and reassign it
userRoles[operator] = evaluateBy(msg.sender, userRoles[operator], role);
// fire an event
emit RoleUpdated(msg.sender, operator, role, userRoles[operator]);
}
evaluateBy keyboard_arrow_up
Source Code
function evaluateBy(
address operator,
uint256 target,
uint256 desired
) public view returns (uint256) {
// read operator's permissions
uint256 p = userRoles[operator];
// taking into account operator's permissions,
// 1) enable the permissions desired on the `target`
target |= p & desired;
// 2) disable the permissions desired on the `target`
target &= FULL_PRIVILEGES_MASK ^ (p & (FULL_PRIVILEGES_MASK ^ desired));
// return calculated result
return target;
}
isFeatureEnabled keyboard_arrow_up
isSenderInRole keyboard_arrow_up
Source Code
function isSenderInRole(uint256 required) public view returns (bool) {
// delegate call to `isOperatorInRole`, passing transaction sender
return isOperatorInRole(msg.sender, required);
}
isOperatorInRole keyboard_arrow_up
Source Code
function isOperatorInRole(address operator, uint256 required)
public
view
returns (bool)
{
// delegate call to `__hasRole`, passing operator's permissions (role)
return __hasRole(userRoles[operator], required);
}
balanceOf keyboard_arrow_up
transfer keyboard_arrow_up
Source Code
function transfer(address _to, uint256 _value) public returns (bool success) {
// just delegate call to `transferFrom`,
// `FEATURE_TRANSFERS` is verified inside it
return transferFrom(msg.sender, _to, _value);
}
transferFrom keyboard_arrow_up
Source Code
function transferFrom(
address _from,
address _to,
uint256 _value
) public returns (bool success) {
// depending on `FEATURE_UNSAFE_TRANSFERS` we execute either safe (default)
// or unsafe transfer
// if `FEATURE_UNSAFE_TRANSFERS` is enabled
// or receiver has `ROLE_ERC20_RECEIVER` permission
// or sender has `ROLE_ERC20_SENDER` permission
if (
isFeatureEnabled(FEATURE_UNSAFE_TRANSFERS) ||
isOperatorInRole(_to, ROLE_ERC20_RECEIVER) ||
isSenderInRole(ROLE_ERC20_SENDER)
) {
// we execute unsafe transfer - delegate call to `unsafeTransferFrom`,
// `FEATURE_TRANSFERS` is verified inside it
unsafeTransferFrom(_from, _to, _value);
}
// otherwise - if `FEATURE_UNSAFE_TRANSFERS` is disabled
// and receiver doesn't have `ROLE_ERC20_RECEIVER` permission
else {
// we execute safe transfer - delegate call to `safeTransferFrom`, passing empty `_data`,
// `FEATURE_TRANSFERS` is verified inside it
safeTransferFrom(_from, _to, _value, "");
}
// both `unsafeTransferFrom` and `safeTransferFrom` throw on any error, so
// if we're here - it means operation successful,
// just return true
return true;
}
safeTransferFrom keyboard_arrow_up
Requirements help
One or more of the following:
-null - OR
_from
must not be equal to
the sender's address
- ORnull - OR
_from
must be equal to
the sender's address
Source Code
function safeTransferFrom(
address _from,
address _to,
uint256 _value,
bytes memory _data
) public {
// first delegate call to `unsafeTransferFrom`
// to perform the unsafe token(s) transfer
unsafeTransferFrom(_from, _to, _value);
// after the successful transfer - check if receiver supports
// ERC20Receiver and execute a callback handler `onERC20Received`,
// reverting whole transaction on any error:
// check if receiver `_to` supports ERC20Receiver interface
if (AddressUtils.isContract(_to)) {
// if `_to` is a contract - execute onERC20Received
bytes4 response = ERC20Receiver(_to).onERC20Received(
msg.sender,
_from,
_value,
_data
);
// expected response is ERC20_RECEIVED
require(response == ERC20_RECEIVED, "invalid onERC20Received response");
}
}
unsafeTransferFrom keyboard_arrow_up
Requirements help
One or more of the following:
-null - OR
_from
must not be equal to
the sender's address
- ORnull - OR
_from
must be equal to
the sender's address
Source Code
function unsafeTransferFrom(
address _from,
address _to,
uint256 _value
) public {
// if `_from` is equal to sender, require transfers feature to be enabled
// otherwise require transfers on behalf feature to be enabled
require(
(_from == msg.sender && isFeatureEnabled(FEATURE_TRANSFERS)) ||
(_from != msg.sender && isFeatureEnabled(FEATURE_TRANSFERS_ON_BEHALF)),
_from == msg.sender
? "transfers are disabled"
: "transfers on behalf are disabled"
);
// non-zero source address check - Zeppelin
// obviously, zero source address is a client mistake
// it's not part of ERC20 standard but it's reasonable to fail fast
// since for zero value transfer transaction succeeds otherwise
require(_from != address(0), "ERC20: transfer from the zero address"); // Zeppelin msg
// non-zero recipient address check
require(_to != address(0), "ERC20: transfer to the zero address"); // Zeppelin msg
// sender and recipient cannot be the same
require(_from != _to, "sender and recipient are the same (_from = _to)");
// sending tokens to the token smart contract itself is a client mistake
require(
_to != address(this),
"invalid recipient (transfer to the token smart contract itself)"
);
// according to ERC-20 Token Standard, https://eips.ethereum.org/EIPS/eip-20
// "Transfers of 0 values MUST be treated as normal transfers and fire the Transfer event."
if (_value == 0) {
// emit an ERC20 transfer event
emit Transfer(_from, _to, _value);
// don't forget to return - we're done
return;
}
// no need to make arithmetic overflow check on the _value - by design of mint()
// in case of transfer on behalf
if (_from != msg.sender) {
// read allowance value - the amount of tokens allowed to transfer - into the stack
uint256 _allowance = transferAllowances[_from][msg.sender];
// verify sender has an allowance to transfer amount of tokens requested
require(_allowance >= _value, "ERC20: transfer amount exceeds allowance"); // Zeppelin msg
// update allowance value on the stack
_allowance -= _value;
// update the allowance value in storage
transferAllowances[_from][msg.sender] = _allowance;
// emit an improved atomic approve event
emit Approved(_from, msg.sender, _allowance + _value, _allowance);
// emit an ERC20 approval event to reflect the decrease
emit Approval(_from, msg.sender, _allowance);
}
// verify sender has enough tokens to transfer on behalf
require(
tokenBalances[_from] >= _value,
"ERC20: transfer amount exceeds balance"
); // Zeppelin msg
// perform the transfer:
// decrease token owner (sender) balance
tokenBalances[_from] -= _value;
// increase `_to` address (receiver) balance
tokenBalances[_to] += _value;
// move voting power associated with the tokens transferred
__moveVotingPower(votingDelegates[_from], votingDelegates[_to], _value);
// emit an improved transfer event
emit Transferred(msg.sender, _from, _to, _value);
// emit an ERC20 transfer event
emit Transfer(_from, _to, _value);
}
approve keyboard_arrow_up
Source Code
function approve(address _spender, uint256 _value)
public
returns (bool success)
{
// non-zero spender address check - Zeppelin
// obviously, zero spender address is a client mistake
// it's not part of ERC20 standard but it's reasonable to fail fast
require(_spender != address(0), "ERC20: approve to the zero address"); // Zeppelin msg
// read old approval value to emmit an improved event (ISBN:978-1-7281-3027-9)
uint256 _oldValue = transferAllowances[msg.sender][_spender];
// perform an operation: write value requested into the storage
transferAllowances[msg.sender][_spender] = _value;
// emit an improved atomic approve event (ISBN:978-1-7281-3027-9)
emit Approved(msg.sender, _spender, _oldValue, _value);
// emit an ERC20 approval event
emit Approval(msg.sender, _spender, _value);
// operation successful, return true
return true;
}
allowance keyboard_arrow_up
Source Code
function allowance(address _owner, address _spender)
public
view
returns (uint256 remaining)
{
// read the value from storage and return
return transferAllowances[_owner][_spender];
}
increaseAllowance keyboard_arrow_up
Requirements help
Source Code
function increaseAllowance(address _spender, uint256 _value)
public
virtual
returns (bool)
{
// read current allowance value
uint256 currentVal = transferAllowances[msg.sender][_spender];
// non-zero _value and arithmetic overflow check on the allowance
require(
currentVal + _value > currentVal,
"zero value approval increase or arithmetic overflow"
);
// delegate call to `approve` with the new value
return approve(_spender, currentVal + _value);
}
decreaseAllowance keyboard_arrow_up
Requirements help
Source Code
function decreaseAllowance(address _spender, uint256 _value)
public
virtual
returns (bool)
{
// read current allowance value
uint256 currentVal = transferAllowances[msg.sender][_spender];
// non-zero _value check on the allowance
require(_value > 0, "zero value approval decrease");
// verify allowance decrease doesn't underflow
require(currentVal >= _value, "ERC20: decreased allowance below zero");
// delegate call to `approve` with the new value
return approve(_spender, currentVal - _value);
}
mint keyboard_arrow_up
Requirements help
null
Source Code
function mint(address _to, uint256 _value) public {
// check if caller has sufficient permissions to mint tokens
require(
isSenderInRole(ROLE_TOKEN_CREATOR),
"insufficient privileges (ROLE_TOKEN_CREATOR required)"
);
// non-zero recipient address check
require(_to != address(0), "ERC20: mint to the zero address"); // Zeppelin msg
// non-zero _value and arithmetic overflow check on the total supply
// this check automatically secures arithmetic overflow on the individual balance
require(
totalSupply + _value > totalSupply,
"zero value mint or arithmetic overflow"
);
// uint192 overflow check (required by voting delegation)
require(
totalSupply + _value <= type(uint192).max,
"total supply overflow (uint192)"
);
// perform mint:
// increase total amount of tokens value
totalSupply += _value;
// increase `_to` address balance
tokenBalances[_to] += _value;
// create voting power associated with the tokens minted
__moveVotingPower(address(0), votingDelegates[_to], _value);
// fire a minted event
emit Minted(msg.sender, _to, _value);
// emit an improved transfer event
emit Transferred(msg.sender, address(0), _to, _value);
// fire ERC20 compliant transfer event
emit Transfer(address(0), _to, _value);
}
burn keyboard_arrow_up
Requirements help
Source Code
function burn(address _from, uint256 _value) public {
// check if caller has sufficient permissions to burn tokens
// and if not - check for possibility to burn own tokens or to burn on behalf
if (!isSenderInRole(ROLE_TOKEN_DESTROYER)) {
// if `_from` is equal to sender, require own burns feature to be enabled
// otherwise require burns on behalf feature to be enabled
require(
(_from == msg.sender && isFeatureEnabled(FEATURE_OWN_BURNS)) ||
(_from != msg.sender && isFeatureEnabled(FEATURE_BURNS_ON_BEHALF)),
_from == msg.sender
? "burns are disabled"
: "burns on behalf are disabled"
);
// in case of burn on behalf
if (_from != msg.sender) {
// read allowance value - the amount of tokens allowed to be burnt - into the stack
uint256 _allowance = transferAllowances[_from][msg.sender];
// verify sender has an allowance to burn amount of tokens requested
require(_allowance >= _value, "ERC20: burn amount exceeds allowance"); // Zeppelin msg
// update allowance value on the stack
_allowance -= _value;
// update the allowance value in storage
transferAllowances[_from][msg.sender] = _allowance;
// emit an improved atomic approve event
emit Approved(msg.sender, _from, _allowance + _value, _allowance);
// emit an ERC20 approval event to reflect the decrease
emit Approval(_from, msg.sender, _allowance);
}
}
// at this point we know that either sender is ROLE_TOKEN_DESTROYER or
// we burn own tokens or on behalf (in latest case we already checked and updated allowances)
// we have left to execute balance checks and burning logic itself
// non-zero burn value check
require(_value != 0, "zero value burn");
// non-zero source address check - Zeppelin
require(_from != address(0), "ERC20: burn from the zero address"); // Zeppelin msg
// verify `_from` address has enough tokens to destroy
// (basically this is a arithmetic overflow check)
require(tokenBalances[_from] >= _value, "ERC20: burn amount exceeds balance"); // Zeppelin msg
// perform burn:
// decrease `_from` address balance
tokenBalances[_from] -= _value;
// decrease total amount of tokens value
totalSupply -= _value;
// destroy voting power associated with the tokens burnt
__moveVotingPower(votingDelegates[_from], address(0), _value);
// fire a burnt event
emit Burnt(msg.sender, _from, _value);
// emit an improved transfer event
emit Transferred(msg.sender, _from, address(0), _value);
// fire ERC20 compliant transfer event
emit Transfer(_from, address(0), _value);
}
getVotingPower keyboard_arrow_up
Source Code
function getVotingPower(address _of) public view returns (uint256) {
// get a link to an array of voting power history records for an address specified
VotingPowerRecord[] storage history = votingPowerHistory[_of];
// lookup the history and return latest element
return history.length == 0 ? 0 : history[history.length - 1].votingPower;
}
getVotingPowerAt keyboard_arrow_up
Requirements help
Source Code
function getVotingPowerAt(address _of, uint256 _blockNum)
public
view
returns (uint256)
{
// make sure block number is not in the past (not the finalized block)
require(_blockNum < block.number, "not yet determined"); // Compound msg
// get a link to an array of voting power history records for an address specified
VotingPowerRecord[] storage history = votingPowerHistory[_of];
// if voting power history for the account provided is empty
if (history.length == 0) {
// than voting power is zero - return the result
return 0;
}
// check latest voting power history record block number:
// if history was not updated after the block of interest
if (history[history.length - 1].blockNumber <= _blockNum) {
// we're done - return last voting power record
return getVotingPower(_of);
}
// check first voting power history record block number:
// if history was never updated before the block of interest
if (history[0].blockNumber > _blockNum) {
// we're done - voting power at the block num of interest was zero
return 0;
}
// `votingPowerHistory[_of]` is an array ordered by `blockNumber`, ascending;
// apply binary search on `votingPowerHistory[_of]` to find such an entry number `i`, that
// `votingPowerHistory[_of][i].blockNumber <= _blockNum`, but in the same time
// `votingPowerHistory[_of][i + 1].blockNumber > _blockNum`
// return the result - voting power found at index `i`
return history[__binaryLookup(_of, _blockNum)].votingPower;
}
getVotingPowerHistory keyboard_arrow_up
getVotingPowerHistoryLength keyboard_arrow_up
delegate keyboard_arrow_up
Requirements help
null
Source Code
function delegate(address _to) public {
// verify delegations are enabled
require(isFeatureEnabled(FEATURE_DELEGATIONS), "delegations are disabled");
// delegate call to `__delegate`
__delegate(msg.sender, _to);
}
delegateWithSig keyboard_arrow_up
Parameters help
Requirements help
Source Code
function delegateWithSig(
address _to,
uint256 _nonce,
uint256 _exp,
uint8 v,
bytes32 r,
bytes32 s
) public {
// verify delegations on behalf are enabled
require(
isFeatureEnabled(FEATURE_DELEGATIONS_ON_BEHALF),
"delegations on behalf are disabled"
);
// build the EIP-712 contract domain separator
bytes32 domainSeparator = keccak256(
abi.encode(
DOMAIN_TYPEHASH,
keccak256(bytes(name)),
block.chainid,
address(this)
)
);
// build the EIP-712 hashStruct of the delegation message
bytes32 hashStruct = keccak256(
abi.encode(DELEGATION_TYPEHASH, _to, _nonce, _exp)
);
// calculate the EIP-712 digest "\x19\x01" ‖ domainSeparator ‖ hashStruct(message)
bytes32 digest = keccak256(
abi.encodePacked("\x19\x01", domainSeparator, hashStruct)
);
// recover the address who signed the message with v, r, s
address signer = ecrecover(digest, v, r, s);
// perform message integrity and security validations
require(signer != address(0), "invalid signature"); // Compound msg
require(_nonce == nonces[signer], "invalid nonce"); // Compound msg
require(block.timestamp < _exp, "signature expired"); // Compound msg
// update the nonce for that particular signer to avoid replay attack
nonces[signer]++;
// delegate call to `__delegate` - execute the logic required
__delegate(signer, _to);
}
Internal Functions
Internal functions are parts of the contract that can't be used directly, but instead are used by the public functions listed above.
internal IlluviumERC20.__delegate keyboard_arrow_up
Source Code
function __delegate(address _from, address _to) private {
// read current delegate to be replaced by a new one
address _fromDelegate = votingDelegates[_from];
// read current voting power (it is equal to token balance)
uint256 _value = tokenBalances[_from];
// reassign voting delegate to `_to`
votingDelegates[_from] = _to;
// update voting power for `_fromDelegate` and `_to`
__moveVotingPower(_fromDelegate, _to, _value);
// emit an event
emit DelegateChanged(_from, _fromDelegate, _to);
}
internal IlluviumERC20.__moveVotingPower keyboard_arrow_up
Source Code
function __moveVotingPower(
address _from,
address _to,
uint256 _value
) private {
// if there is no move (`_from == _to`) or there is nothing to move (`_value == 0`)
if (_from == _to || _value == 0) {
// return silently with no action
return;
}
// if source address is not zero - decrease its voting power
if (_from != address(0)) {
// read current source address voting power
uint256 _fromVal = getVotingPower(_from);
// calculate decreased voting power
// underflow is not possible by design:
// voting power is limited by token balance which is checked by the callee
uint256 _toVal = _fromVal - _value;
// update source voting power from `_fromVal` to `_toVal`
__updateVotingPower(_from, _fromVal, _toVal);
}
// if destination address is not zero - increase its voting power
if (_to != address(0)) {
// read current destination address voting power
uint256 _fromVal = getVotingPower(_to);
// calculate increased voting power
// overflow is not possible by design:
// max token supply limits the cumulative voting power
uint256 _toVal = _fromVal + _value;
// update destination voting power from `_fromVal` to `_toVal`
__updateVotingPower(_to, _fromVal, _toVal);
}
}
internal IlluviumERC20.__updateVotingPower keyboard_arrow_up
Source Code
function __updateVotingPower(
address _of,
uint256 _fromVal,
uint256 _toVal
) private {
// get a link to an array of voting power history records for an address specified
VotingPowerRecord[] storage history = votingPowerHistory[_of];
// if there is an existing voting power value stored for current block
if (
history.length != 0 &&
history[history.length - 1].blockNumber == block.number
) {
// update voting power which is already stored in the current block
history[history.length - 1].votingPower = uint192(_toVal);
}
// otherwise - if there is no value stored for current block
else {
// add new element into array representing the value for current block
history.push(VotingPowerRecord(uint64(block.number), uint192(_toVal)));
}
// emit an event
emit VotingPowerChanged(_of, _fromVal, _toVal);
}
internal IlluviumERC20.__binaryLookup keyboard_arrow_up
Source Code
function __binaryLookup(address _to, uint256 n) private view returns (uint256) {
// get a link to an array of voting power history records for an address specified
VotingPowerRecord[] storage history = votingPowerHistory[_to];
// left bound of the search interval, originally start of the array
uint256 i = 0;
// right bound of the search interval, originally end of the array
uint256 j = history.length - 1;
// the iteration process narrows down the bounds by
// splitting the interval in a half oce per each iteration
while (j > i) {
// get an index in the middle of the interval [i, j]
uint256 k = j - (j - i) / 2;
// read an element to compare it with the value of interest
VotingPowerRecord memory cp = history[k];
// if we've got a strict equal - we're lucky and done
if (cp.blockNumber == n) {
// just return the result - index `k`
return k;
}
// if the value of interest is bigger - move left bound to the middle
else if (cp.blockNumber < n) {
// move left bound `i` to the middle position `k`
i = k;
}
// otherwise, when the value of interest is smaller - move right bound to the middle
else {
// move right bound `j` to the middle position `k - 1`:
// element at position `k` is bigger and cannot be the result
j = k - 1;
}
}
// reaching that point means no exact match found
// since we're interested in the element which is not bigger than the
// element of interest, we return the lower bound `i`
return i;
}
internal AccessControl.__hasRole keyboard_arrow_up
Source Code
function __hasRole(uint256 actual, uint256 required)
internal
pure
returns (bool)
{
// check the bitmask for the role required and return the result
return actual & required == required;
}