Contract Address Details
contract

0xc790b402a6F7858408349504EE5948Df690A16E1

Contract Name
Distributions_v0
Creator
0xee385a–28a04f at 0x8da0c7–349b00
Balance
0 xDAI ( )
Tokens
Fetching tokens...
Transactions
Fetching transactions...
Transfers
Fetching transfers...
Gas Used
Fetching gas used...
Last Balance Update
19444581
Contract name:
Distributions_v0




Optimization enabled
true
Compiler version
v0.5.17+commit.d19bba13




Optimization runs
200
EVM Version
default




Verified at
2020-07-15 11:28:21.967862Z

Contract source code

// File: @openzeppelin/upgrades/contracts/Initializable.sol
pragma solidity >=0.4.24 <0.6.0;
/**
* @title Initializable
*
* @dev Helper contract to support initializer functions. To use it, replace
* the constructor with a function that has the `initializer` modifier.
* WARNING: Unlike constructors, initializer functions must be manually
* invoked. This applies both to deploying an Initializable contract, as well
* as extending an Initializable contract via inheritance.
* WARNING: When used with inheritance, manual care must be taken to not invoke
* a parent initializer twice, or ensure that all initializers are idempotent,
* because this is not dealt with automatically as with constructors.
*/
contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
*/
bool private initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private initializing;
/**
* @dev Modifier to use in the initializer function of a contract.
*/
modifier initializer() {
require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
bool isTopLevelCall = !initializing;
if (isTopLevelCall) {
initializing = true;
initialized = true;
}
_;
if (isTopLevelCall) {
initializing = false;
}
}
/// @dev Returns true if and only if the function is running in the constructor
function isConstructor() private view returns (bool) {
// extcodesize checks the size of the code stored in an address, and
// address returns the current address. Since the code is still not
// deployed when running a constructor, any checks on its code size will
// yield zero, making it an effective way to detect if a contract is
// under construction or not.
uint256 cs;
assembly { cs := extcodesize(address) }
return cs == 0;
}
// Reserved storage space to allow for layout changes in the future.
uint256[50] private ______gap;
}
// File: @openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol
pragma solidity ^0.5.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*
* _Available since v2.4.0._
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
// File: @openzeppelin/contracts-ethereum-package/contracts/utils/Address.sol
pragma solidity ^0.5.5;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* This test is non-exhaustive, and there may be false-negatives: during the
* execution of a contract's constructor, its address will be reported as
* not containing a contract.
*
* IMPORTANT: It is unsafe to assume that an address for which this
* function returns false is an externally-owned account (EOA) and not a
* contract.
*/
function isContract(address account) internal view returns (bool) {
// This method relies in extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
// for accounts without code, i.e. `keccak256('')`
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
// solhint-disable-next-line no-inline-assembly
assembly { codehash := extcodehash(account) }
return (codehash != 0x0 && codehash != accountHash);
}
/**
* @dev Converts an `address` into `address payable`. Note that this is
* simply a type cast: the actual underlying value is not changed.
*
* _Available since v2.4.0._
*/
function toPayable(address account) internal pure returns (address payable) {
return address(uint160(account));
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*
* _Available since v2.4.0._
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-call-value
(bool success, ) = recipient.call.value(amount)("");
require(success, "Address: unable to send value, recipient may have reverted");
}
}
// File: @openzeppelin/contracts-ethereum-package/contracts/cryptography/ECDSA.sol
pragma solidity ^0.5.0;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* NOTE: This call _does not revert_ if the signature is invalid, or
* if the signer is otherwise unable to be retrieved. In those scenarios,
* the zero address is returned.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
// Check the signature length
if (signature.length != 65) {
return (address(0));
}
// Divide the signature in r, s and v variables
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
// solhint-disable-next-line no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return address(0);
}
if (v != 27 && v != 28) {
return address(0);
}
// If the signature is valid (and not malleable), return the signer address
return ecrecover(hash, v, r, s);
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* replicates the behavior of the
* https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
* JSON-RPC method.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
}
// File: @openzeppelin/contracts-ethereum-package/contracts/GSN/Context.sol
pragma solidity ^0.5.0;
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
contract Context is Initializable {
// Empty internal constructor, to prevent people from mistakenly deploying
// an instance of this contract, which should be used via inheritance.
constructor () internal { }
// solhint-disable-previous-line no-empty-blocks
function _msgSender() internal view returns (address payable) {
return msg.sender;
}
function _msgData() internal view returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
// File: @openzeppelin/contracts-ethereum-package/contracts/ownership/Ownable.sol
pragma solidity ^0.5.0;
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be aplied to your functions to restrict their use to
* the owner.
*/
contract Ownable is Initializable, Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function initialize(address sender) public initializer {
_owner = sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Returns true if the caller is the current owner.
*/
function isOwner() public view returns (bool) {
return _msgSender() == _owner;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* > Note: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
uint256[50] private ______gap;
}
// File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/IERC20.sol
pragma solidity ^0.5.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see {ERC20Detailed}.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// File: @openzeppelin/contracts-ethereum-package/contracts/GSN/IRelayRecipient.sol
pragma solidity ^0.5.0;
/**
* @dev Base interface for a contract that will be called via the GSN from {IRelayHub}.
*
* TIP: You don't need to write an implementation yourself! Inherit from {GSNRecipient} instead.
*/
contract IRelayRecipient {
/**
* @dev Returns the address of the {IRelayHub} instance this recipient interacts with.
*/
function getHubAddr() public view returns (address);
/**
* @dev Called by {IRelayHub} to validate if this recipient accepts being charged for a relayed call. Note that the
* recipient will be charged regardless of the execution result of the relayed call (i.e. if it reverts or not).
*
* The relay request was originated by `from` and will be served by `relay`. `encodedFunction` is the relayed call
* calldata, so its first four bytes are the function selector. The relayed call will be forwarded `gasLimit` gas,
* and the transaction executed with a gas price of at least `gasPrice`. `relay`'s fee is `transactionFee`, and the
* recipient will be charged at most `maxPossibleCharge` (in wei). `nonce` is the sender's (`from`) nonce for
* replay attack protection in {IRelayHub}, and `approvalData` is a optional parameter that can be used to hold a signature
* over all or some of the previous values.
*
* Returns a tuple, where the first value is used to indicate approval (0) or rejection (custom non-zero error code,
* values 1 to 10 are reserved) and the second one is data to be passed to the other {IRelayRecipient} functions.
*
* {acceptRelayedCall} is called with 50k gas: if it runs out during execution, the request will be considered
* rejected. A regular revert will also trigger a rejection.
*/
function acceptRelayedCall(
address relay,
address from,
bytes calldata encodedFunction,
uint256 transactionFee,
uint256 gasPrice,
uint256 gasLimit,
uint256 nonce,
bytes calldata approvalData,
uint256 maxPossibleCharge
)
external
view
returns (uint256, bytes memory);
/**
* @dev Called by {IRelayHub} on approved relay call requests, before the relayed call is executed. This allows to e.g.
* pre-charge the sender of the transaction.
*
* `context` is the second value returned in the tuple by {acceptRelayedCall}.
*
* Returns a value to be passed to {postRelayedCall}.
*
* {preRelayedCall} is called with 100k gas: if it runs out during exection or otherwise reverts, the relayed call
* will not be executed, but the recipient will still be charged for the transaction's cost.
*/
function preRelayedCall(bytes calldata context) external returns (bytes32);
/**
* @dev Called by {IRelayHub} on approved relay call requests, after the relayed call is executed. This allows to e.g.
* charge the user for the relayed call costs, return any overcharges from {preRelayedCall}, or perform
* contract-specific bookkeeping.
*
* `context` is the second value returned in the tuple by {acceptRelayedCall}. `success` is the execution status of
* the relayed call. `actualCharge` is an estimate of how much the recipient will be charged for the transaction,
* not including any gas used by {postRelayedCall} itself. `preRetVal` is {preRelayedCall}'s return value.
*
*
* {postRelayedCall} is called with 100k gas: if it runs out during execution or otherwise reverts, the relayed call
* and the call to {preRelayedCall} will be reverted retroactively, but the recipient will still be charged for the
* transaction's cost.
*/
function postRelayedCall(bytes calldata context, bool success, uint256 actualCharge, bytes32 preRetVal) external;
}
// File: @openzeppelin/contracts-ethereum-package/contracts/GSN/IRelayHub.sol
pragma solidity ^0.5.0;
/**
* @dev Interface for `RelayHub`, the core contract of the GSN. Users should not need to interact with this contract
* directly.
*
* See the https://github.com/OpenZeppelin/openzeppelin-gsn-helpers[OpenZeppelin GSN helpers] for more information on
* how to deploy an instance of `RelayHub` on your local test network.
*/
contract IRelayHub {
// Relay management
/**
* @dev Adds stake to a relay and sets its `unstakeDelay`. If the relay does not exist, it is created, and the caller
* of this function becomes its owner. If the relay already exists, only the owner can call this function. A relay
* cannot be its own owner.
*
* All Ether in this function call will be added to the relay's stake.
* Its unstake delay will be assigned to `unstakeDelay`, but the new value must be greater or equal to the current one.
*
* Emits a {Staked} event.
*/
function stake(address relayaddr, uint256 unstakeDelay) external payable;
/**
* @dev Emitted when a relay's stake or unstakeDelay are increased
*/
event Staked(address indexed relay, uint256 stake, uint256 unstakeDelay);
/**
* @dev Registers the caller as a relay.
* The relay must be staked for, and not be a contract (i.e. this function must be called directly from an EOA).
*
* This function can be called multiple times, emitting new {RelayAdded} events. Note that the received
* `transactionFee` is not enforced by {relayCall}.
*
* Emits a {RelayAdded} event.
*/
function registerRelay(uint256 transactionFee, string memory url) public;
/**
* @dev Emitted when a relay is registered or re-registerd. Looking at these events (and filtering out
* {RelayRemoved} events) lets a client discover the list of available relays.
*/
event RelayAdded(address indexed relay, address indexed owner, uint256 transactionFee, uint256 stake, uint256 unstakeDelay, string url);
/**
* @dev Removes (deregisters) a relay. Unregistered (but staked for) relays can also be removed.
*
* Can only be called by the owner of the relay. After the relay's `unstakeDelay` has elapsed, {unstake} will be
* callable.
*
* Emits a {RelayRemoved} event.
*/
function removeRelayByOwner(address relay) public;
/**
* @dev Emitted when a relay is removed (deregistered). `unstakeTime` is the time when unstake will be callable.
*/
event RelayRemoved(address indexed relay, uint256 unstakeTime);
/** Deletes the relay from the system, and gives back its stake to the owner.
*
* Can only be called by the relay owner, after `unstakeDelay` has elapsed since {removeRelayByOwner} was called.
*
* Emits an {Unstaked} event.
*/
function unstake(address relay) public;
/**
* @dev Emitted when a relay is unstaked for, including the returned stake.
*/
event Unstaked(address indexed relay, uint256 stake);
// States a relay can be in
enum RelayState {
Unknown, // The relay is unknown to the system: it has never been staked for
Staked, // The relay has been staked for, but it is not yet active
Registered, // The relay has registered itself, and is active (can relay calls)
Removed // The relay has been removed by its owner and can no longer relay calls. It must wait for its unstakeDelay to elapse before it can unstake
}
/**
* @dev Returns a relay's status. Note that relays can be deleted when unstaked or penalized, causing this function
* to return an empty entry.
*/
function getRelay(address relay) external view returns (uint256 totalStake, uint256 unstakeDelay, uint256 unstakeTime, address payable owner, RelayState state);
// Balance management
/**
* @dev Deposits Ether for a contract, so that it can receive (and pay for) relayed transactions.
*
* Unused balance can only be withdrawn by the contract itself, by calling {withdraw}.
*
* Emits a {Deposited} event.
*/
function depositFor(address target) public payable;
/**
* @dev Emitted when {depositFor} is called, including the amount and account that was funded.
*/
event Deposited(address indexed recipient, address indexed from, uint256 amount);
/**
* @dev Returns an account's deposits. These can be either a contracts's funds, or a relay owner's revenue.
*/
function balanceOf(address target) external view returns (uint256);
/**
* Withdraws from an account's balance, sending it back to it. Relay owners call this to retrieve their revenue, and
* contracts can use it to reduce their funding.
*
* Emits a {Withdrawn} event.
*/
function withdraw(uint256 amount, address payable dest) public;
/**
* @dev Emitted when an account withdraws funds from `RelayHub`.
*/
event Withdrawn(address indexed account, address indexed dest, uint256 amount);
// Relaying
/**
* @dev Checks if the `RelayHub` will accept a relayed operation.
* Multiple things must be true for this to happen:
* - all arguments must be signed for by the sender (`from`)
* - the sender's nonce must be the current one
* - the recipient must accept this transaction (via {acceptRelayedCall})
*
* Returns a `PreconditionCheck` value (`OK` when the transaction can be relayed), or a recipient-specific error
* code if it returns one in {acceptRelayedCall}.
*/
function canRelay(
address relay,
address from,
address to,
bytes memory encodedFunction,
uint256 transactionFee,
uint256 gasPrice,
uint256 gasLimit,
uint256 nonce,
bytes memory signature,
bytes memory approvalData
) public view returns (uint256 status, bytes memory recipientContext);
// Preconditions for relaying, checked by canRelay and returned as the corresponding numeric values.
enum PreconditionCheck {
OK, // All checks passed, the call can be relayed
WrongSignature, // The transaction to relay is not signed by requested sender
WrongNonce, // The provided nonce has already been used by the sender
AcceptRelayedCallReverted, // The recipient rejected this call via acceptRelayedCall
InvalidRecipientStatusCode // The recipient returned an invalid (reserved) status code
}
/**
* @dev Relays a transaction.
*
* For this to succeed, multiple conditions must be met:
* - {canRelay} must `return PreconditionCheck.OK`
* - the sender must be a registered relay
* - the transaction's gas price must be larger or equal to the one that was requested by the sender
* - the transaction must have enough gas to not run out of gas if all internal transactions (calls to the
* recipient) use all gas available to them
* - the recipient must have enough balance to pay the relay for the worst-case scenario (i.e. when all gas is
* spent)
*
* If all conditions are met, the call will be relayed and the recipient charged. {preRelayedCall}, the encoded
* function and {postRelayedCall} will be called in that order.
*
* Parameters:
* - `from`: the client originating the request
* - `to`: the target {IRelayRecipient} contract
* - `encodedFunction`: the function call to relay, including data
* - `transactionFee`: fee (%) the relay takes over actual gas cost
* - `gasPrice`: gas price the client is willing to pay
* - `gasLimit`: gas to forward when calling the encoded function
* - `nonce`: client's nonce
* - `signature`: client's signature over all previous params, plus the relay and RelayHub addresses
* - `approvalData`: dapp-specific data forwared to {acceptRelayedCall}. This value is *not* verified by the
* `RelayHub`, but it still can be used for e.g. a signature.
*
* Emits a {TransactionRelayed} event.
*/
function relayCall(
address from,
address to,
bytes memory encodedFunction,
uint256 transactionFee,
uint256 gasPrice,
uint256 gasLimit,
uint256 nonce,
bytes memory signature,
bytes memory approvalData
) public;
/**
* @dev Emitted when an attempt to relay a call failed.
*
* This can happen due to incorrect {relayCall} arguments, or the recipient not accepting the relayed call. The
* actual relayed call was not executed, and the recipient not charged.
*
* The `reason` parameter contains an error code: values 1-10 correspond to `PreconditionCheck` entries, and values
* over 10 are custom recipient error codes returned from {acceptRelayedCall}.
*/
event CanRelayFailed(address indexed relay, address indexed from, address indexed to, bytes4 selector, uint256 reason);
/**
* @dev Emitted when a transaction is relayed.
* Useful when monitoring a relay's operation and relayed calls to a contract
*
* Note that the actual encoded function might be reverted: this is indicated in the `status` parameter.
*
* `charge` is the Ether value deducted from the recipient's balance, paid to the relay's owner.
*/
event TransactionRelayed(address indexed relay, address indexed from, address indexed to, bytes4 selector, RelayCallStatus status, uint256 charge);
// Reason error codes for the TransactionRelayed event
enum RelayCallStatus {
OK, // The transaction was successfully relayed and execution successful - never included in the event
RelayedCallFailed, // The transaction was relayed, but the relayed call failed
PreRelayedFailed, // The transaction was not relayed due to preRelatedCall reverting
PostRelayedFailed, // The transaction was relayed and reverted due to postRelatedCall reverting
RecipientBalanceChanged // The transaction was relayed and reverted due to the recipient's balance changing
}
/**
* @dev Returns how much gas should be forwarded to a call to {relayCall}, in order to relay a transaction that will
* spend up to `relayedCallStipend` gas.
*/
function requiredGas(uint256 relayedCallStipend) public view returns (uint256);
/**
* @dev Returns the maximum recipient charge, given the amount of gas forwarded, gas price and relay fee.
*/
function maxPossibleCharge(uint256 relayedCallStipend, uint256 gasPrice, uint256 transactionFee) public view returns (uint256);
// Relay penalization.
// Any account can penalize relays, removing them from the system immediately, and rewarding the
// reporter with half of the relay's stake. The other half is burned so that, even if the relay penalizes itself, it
// still loses half of its stake.
/**
* @dev Penalize a relay that signed two transactions using the same nonce (making only the first one valid) and
* different data (gas price, gas limit, etc. may be different).
*
* The (unsigned) transaction data and signature for both transactions must be provided.
*/
function penalizeRepeatedNonce(bytes memory unsignedTx1, bytes memory signature1, bytes memory unsignedTx2, bytes memory signature2) public;
/**
* @dev Penalize a relay that sent a transaction that didn't target `RelayHub`'s {registerRelay} or {relayCall}.
*/
function penalizeIllegalTransaction(bytes memory unsignedTx, bytes memory signature) public;
/**
* @dev Emitted when a relay is penalized.
*/
event Penalized(address indexed relay, address sender, uint256 amount);
/**
* @dev Returns an account's nonce in `RelayHub`.
*/
function getNonce(address from) external view returns (uint256);
}
// File: @openzeppelin/contracts-ethereum-package/contracts/GSN/GSNRecipient.sol
pragma solidity ^0.5.0;
/**
* @dev Base GSN recipient contract: includes the {IRelayRecipient} interface
* and enables GSN support on all contracts in the inheritance tree.
*
* TIP: This contract is abstract. The functions {acceptRelayedCall},
* {_preRelayedCall}, and {_postRelayedCall} are not implemented and must be
* provided by derived contracts. See the
* xref:ROOT:gsn-strategies.adoc#gsn-strategies[GSN strategies] for more
* information on how to use the pre-built {GSNRecipientSignature} and
* {GSNRecipientERC20Fee}, or how to write your own.
*/
contract GSNRecipient is Initializable, IRelayRecipient, Context {
function initialize() public initializer {
if (_relayHub == address(0)) {
setDefaultRelayHub();
}
}
function setDefaultRelayHub() public {
_upgradeRelayHub(0xD216153c06E857cD7f72665E0aF1d7D82172F494);
}
// Default RelayHub address, deployed on mainnet and all testnets at the same address
address private _relayHub;
uint256 constant private RELAYED_CALL_ACCEPTED = 0;
uint256 constant private RELAYED_CALL_REJECTED = 11;
// How much gas is forwarded to postRelayedCall
uint256 constant internal POST_RELAYED_CALL_MAX_GAS = 100000;
/**
* @dev Emitted when a contract changes its {IRelayHub} contract to a new one.
*/
event RelayHubChanged(address indexed oldRelayHub, address indexed newRelayHub);
/**
* @dev Returns the address of the {IRelayHub} contract for this recipient.
*/
function getHubAddr() public view returns (address) {
return _relayHub;
}
/**
* @dev Switches to a new {IRelayHub} instance. This method is added for future-proofing: there's no reason to not
* use the default instance.
*
* IMPORTANT: After upgrading, the {GSNRecipient} will no longer be able to receive relayed calls from the old
* {IRelayHub} instance. Additionally, all funds should be previously withdrawn via {_withdrawDeposits}.
*/
function _upgradeRelayHub(address newRelayHub) internal {
address currentRelayHub = _relayHub;
require(newRelayHub != address(0), "GSNRecipient: new RelayHub is the zero address");
require(newRelayHub != currentRelayHub, "GSNRecipient: new RelayHub is the current one");
emit RelayHubChanged(currentRelayHub, newRelayHub);
_relayHub = newRelayHub;
}
/**
* @dev Returns the version string of the {IRelayHub} for which this recipient implementation was built. If
* {_upgradeRelayHub} is used, the new {IRelayHub} instance should be compatible with this version.
*/
// This function is view for future-proofing, it may require reading from
// storage in the future.
function relayHubVersion() public view returns (string memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return "1.0.0";
}
/**
* @dev Withdraws the recipient's deposits in `RelayHub`.
*
* Derived contracts should expose this in an external interface with proper access control.
*/
function _withdrawDeposits(uint256 amount, address payable payee) internal {
IRelayHub(_relayHub).withdraw(amount, payee);
}
// Overrides for Context's functions: when called from RelayHub, sender and
// data require some pre-processing: the actual sender is stored at the end
// of the call data, which in turns means it needs to be removed from it
// when handling said data.
/**
* @dev Replacement for msg.sender. Returns the actual sender of a transaction: msg.sender for regular transactions,
* and the end-user for GSN relayed calls (where msg.sender is actually `RelayHub`).
*
* IMPORTANT: Contracts derived from {GSNRecipient} should never use `msg.sender`, and use {_msgSender} instead.
*/
function _msgSender() internal view returns (address payable) {
if (msg.sender != _relayHub) {
return msg.sender;
} else {
return _getRelayedCallSender();
}
}
/**
* @dev Replacement for msg.data. Returns the actual calldata of a transaction: msg.data for regular transactions,
* and a reduced version for GSN relayed calls (where msg.data contains additional information).
*
* IMPORTANT: Contracts derived from {GSNRecipient} should never use `msg.data`, and use {_msgData} instead.
*/
function _msgData() internal view returns (bytes memory) {
if (msg.sender != _relayHub) {
return msg.data;
} else {
return _getRelayedCallData();
}
}
// Base implementations for pre and post relayedCall: only RelayHub can invoke them, and data is forwarded to the
// internal hook.
/**
* @dev See `IRelayRecipient.preRelayedCall`.
*
* This function should not be overriden directly, use `_preRelayedCall` instead.
*
* * Requirements:
*
* - the caller must be the `RelayHub` contract.
*/
function preRelayedCall(bytes calldata context) external returns (bytes32) {
require(msg.sender == getHubAddr(), "GSNRecipient: caller is not RelayHub");
return _preRelayedCall(context);
}
/**
* @dev See `IRelayRecipient.preRelayedCall`.
*
* Called by `GSNRecipient.preRelayedCall`, which asserts the caller is the `RelayHub` contract. Derived contracts
* must implement this function with any relayed-call preprocessing they may wish to do.
*
*/
function _preRelayedCall(bytes memory context) internal returns (bytes32);
/**
* @dev See `IRelayRecipient.postRelayedCall`.
*
* This function should not be overriden directly, use `_postRelayedCall` instead.
*
* * Requirements:
*
* - the caller must be the `RelayHub` contract.
*/
function postRelayedCall(bytes calldata context, bool success, uint256 actualCharge, bytes32 preRetVal) external {
require(msg.sender == getHubAddr(), "GSNRecipient: caller is not RelayHub");
_postRelayedCall(context, success, actualCharge, preRetVal);
}
/**
* @dev See `IRelayRecipient.postRelayedCall`.
*
* Called by `GSNRecipient.postRelayedCall`, which asserts the caller is the `RelayHub` contract. Derived contracts
* must implement this function with any relayed-call postprocessing they may wish to do.
*
*/
function _postRelayedCall(bytes memory context, bool success, uint256 actualCharge, bytes32 preRetVal) internal;
/**
* @dev Return this in acceptRelayedCall to proceed with the execution of a relayed call. Note that this contract
* will be charged a fee by RelayHub
*/
function _approveRelayedCall() internal pure returns (uint256, bytes memory) {
return _approveRelayedCall("");
}
/**
* @dev See `GSNRecipient._approveRelayedCall`.
*
* This overload forwards `context` to _preRelayedCall and _postRelayedCall.
*/
function _approveRelayedCall(bytes memory context) internal pure returns (uint256, bytes memory) {
return (RELAYED_CALL_ACCEPTED, context);
}
/**
* @dev Return this in acceptRelayedCall to impede execution of a relayed call. No fees will be charged.
*/
function _rejectRelayedCall(uint256 errorCode) internal pure returns (uint256, bytes memory) {
return (RELAYED_CALL_REJECTED + errorCode, "");
}
/*
* @dev Calculates how much RelayHub will charge a recipient for using `gas` at a `gasPrice`, given a relayer's
* `serviceFee`.
*/
function _computeCharge(uint256 gas, uint256 gasPrice, uint256 serviceFee) internal pure returns (uint256) {
// The fee is expressed as a percentage. E.g. a value of 40 stands for a 40% fee, so the recipient will be
// charged for 1.4 times the spent amount.
return (gas * gasPrice * (100 + serviceFee)) / 100;
}
function _getRelayedCallSender() private pure returns (address payable result) {
// We need to read 20 bytes (an address) located at array index msg.data.length - 20. In memory, the array
// is prefixed with a 32-byte length value, so we first add 32 to get the memory read index. However, doing
// so would leave the address in the upper 20 bytes of the 32-byte word, which is inconvenient and would
// require bit shifting. We therefore subtract 12 from the read index so the address lands on the lower 20
// bytes. This can always be done due to the 32-byte prefix.
// The final memory read index is msg.data.length - 20 + 32 - 12 = msg.data.length. Using inline assembly is the
// easiest/most-efficient way to perform this operation.
// These fields are not accessible from assembly
bytes memory array = msg.data;
uint256 index = msg.data.length;
// solhint-disable-next-line no-inline-assembly
assembly {
// Load the 32 bytes word from memory with the address on the lower 20 bytes, and mask those.
result := and(mload(add(array, index)), 0xffffffffffffffffffffffffffffffffffffffff)
}
return result;
}
function _getRelayedCallData() private pure returns (bytes memory) {
// RelayHub appends the sender address at the end of the calldata, so in order to retrieve the actual msg.data,
// we must strip the last 20 bytes (length of an address type) from it.
uint256 actualDataLength = msg.data.length - 20;
bytes memory actualData = new bytes(actualDataLength);
for (uint256 i = 0; i < actualDataLength; ++i) {
actualData[i] = msg.data[i];
}
return actualData;
}
}
// File: contracts/UpdatableGSNRecipientSignature.sol
/*
Copyright (c) 2020 Reddit Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided
that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse
or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSEARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
pragma solidity ^0.5.0;
contract UpdatableGSNRecipientSignature is Initializable, GSNRecipient {
using ECDSA for bytes32;
event SignerUpdated(address signer);
address private _trustedSigner;
enum GSNRecipientSignatureErrorCodes {
INVALID_SIGNER
}
/**
* @dev Sets the trusted signer that is going to be producing signatures to approve relayed calls.
*/
function initialize(address trustedSigner) public initializer {
updateSigner(trustedSigner);
GSNRecipient.initialize();
}
/**
* @dev Ensures that only transactions with a trusted signature can be relayed through the GSN.
*/
function acceptRelayedCall(
address relay,
address from,
bytes calldata encodedFunction,
uint256 transactionFee,
uint256 gasPrice,
uint256 gasLimit,
uint256 nonce,
bytes calldata approvalData,
uint256
)
external
view
returns (uint256, bytes memory)
{
bytes memory blob = abi.encode(
relay,
from,
encodedFunction,
transactionFee,
gasPrice,
gasLimit,
nonce, // Prevents replays on RelayHub
getHubAddr(), // Prevents replays in multiple RelayHubs
address(this) // Prevents replays in multiple recipients
);
if (keccak256(blob).toEthSignedMessageHash().recover(approvalData) == _trustedSigner) {
return _approveRelayedCall();
} else {
return _rejectRelayedCall(uint256(GSNRecipientSignatureErrorCodes.INVALID_SIGNER));
}
}
function _preRelayedCall(bytes memory) internal returns (bytes32) {
// solhint-disable-previous-line no-empty-blocks
}
function _postRelayedCall(bytes memory, bool, uint256, bytes32) internal {
// solhint-disable-previous-line no-empty-blocks
}
function updateSigner(address trustedSigner) internal {
require(trustedSigner != address(0), "GSNRecipientSignature: trusted signer is the zero address");
_trustedSigner = trustedSigner;
emit SignerUpdated(trustedSigner);
}
}
// File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20.sol
pragma solidity ^0.5.0;
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20Mintable}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Initializable, Context, IERC20 {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) public returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20};
*
* Requirements:
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for `sender`'s tokens of at least
* `amount`.
*/
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
return true;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements
*
* - `to` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal {
require(account != address(0), "ERC20: mint to the zero address");
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal {
require(account != address(0), "ERC20: burn from the zero address");
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
*
* This is internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Destroys `amount` tokens from `account`.`amount` is then deducted
* from the caller's allowance.
*
* See {_burn} and {_approve}.
*/
function _burnFrom(address account, uint256 amount) internal {
_burn(account, amount);
_approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
}
uint256[50] private ______gap;
}
// File: contracts/SubredditPoints_v0.sol
/*
Copyright (c) 2020 Reddit Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided
that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse
or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSEARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
pragma solidity >=0.5.0 < 0.6.0;
contract ISubredditPoints {
function mint(
address operator,
address account,
uint256 amount,
bytes calldata userData,
bytes calldata operatorData
) external; // solium-disable-line indentation
function burn(
uint256 amount,
bytes calldata data
) external; // solium-disable-line indentation
function operatorBurn(
address account,
uint256 amount,
bytes calldata data,
bytes calldata operatorData
) external; // solium-disable-line indentation
function subreddit() external view returns (string memory);
}
// ERC20 and borrows only operators notion from ERC777, accounts can revoke default operator
contract SubredditPoints_v0 is Initializable, ISubredditPoints, Ownable, UpdatableGSNRecipientSignature, ERC20 {
using SafeMath for uint256;
using Address for address;
event Sent(
address indexed operator,
address indexed from,
address indexed to,
uint256 amount,
bytes data,
bytes operatorData
);
event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);
event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData);
event AuthorizedOperator(address indexed operator, address indexed tokenHolder);
event RevokedOperator(address indexed operator, address indexed tokenHolder);
event DefaultOperatorAdded(address indexed operator);
event DefaultOperatorRemoved(address indexed operator);
// ------------------------------------------------------------------------------------
// VARIABLES BLOCK, MAKE SURE ONLY ADD TO THE END
string private _subreddit;
string private _name;
string private _symbol;
// operators notion from ERC777, accounts can revoke default operator
mapping(address => bool) private _defaultOperators;
// Maps operators and revoked default operators to state (enabled/disabled)
mapping(address => mapping(address => bool)) private _operators;
mapping(address => mapping(address => bool)) private _revokedDefaultOperators;
// operators notion from ERC777, accounts can revoke default operator
// This isn't ever read from - it's only used to respond to the defaultOperators query.
address[] private _defaultOperatorsArray;
address _distributionContract;
// END OF VARS
// ------------------------------------------------------------------------------------
function initialize(
address owner_,
address gsnApprover_,
address distributionContract_,
string calldata subreddit_,
string calldata name_,
string calldata symbol_,
address[] calldata defaultOperators_
) external initializer {
require(bytes(subreddit_).length != 0, "SubredditPoints: subreddit can't be empty");
require(bytes(name_).length != 0, "SubredditPoints: name can't be empty");
require(bytes(symbol_).length != 0, "SubredditPoints: symbol can't be empty");
require(owner_ != address(0), "SubredditPoints: owner should not be 0");
Ownable.initialize(owner_);
UpdatableGSNRecipientSignature.initialize(gsnApprover_);
updateDistributionContract(distributionContract_);
_subreddit = subreddit_;
_name = name_;
_symbol = symbol_;
_defaultOperatorsArray = defaultOperators_;
for (uint256 i = 0; i < defaultOperators_.length; i++) {
_defaultOperators[defaultOperators_[i]] = true;
emit DefaultOperatorAdded(defaultOperators_[i]);
}
}
function mint(
address operator,
address account,
uint256 amount,
bytes calldata userData,
bytes calldata operatorData
) external {
require(_msgSender() == _distributionContract, "SubredditPoints: only distribution contract can mint points");
ERC20._mint(account, amount);
emit Minted(operator, account, amount, userData, operatorData);
}
function burn(
uint256 amount,
bytes calldata userData
) external {
address account = _msgSender();
_burn(account, account, amount, userData, "");
}
function isOperatorFor(
address operator,
address tokenHolder
) public view returns (bool) {
return operator == tokenHolder ||
(_defaultOperators[operator] && !_revokedDefaultOperators[tokenHolder][operator]) ||
_operators[tokenHolder][operator];
}
function authorizeOperator(address operator) external {
require(_msgSender() != operator, "SubredditPoints: authorizing self as operator");
require(address(0) != operator, "SubredditPoints: operator can't have 0 address");
if (_defaultOperators[operator]) {
delete _revokedDefaultOperators[_msgSender()][operator];
} else {
_operators[_msgSender()][operator] = true;
}
emit AuthorizedOperator(operator, _msgSender());
}
function revokeOperator(address operator) external {
require(operator != _msgSender(), "SubredditPoints: revoking self as operator");
require(address(0) != operator, "SubredditPoints: operator can't have 0 address");
if (_defaultOperators[operator]) {
_revokedDefaultOperators[_msgSender()][operator] = true;
} else {
delete _operators[_msgSender()][operator];
}
emit RevokedOperator(operator, _msgSender());
}
function operatorSend(
address sender,
address recipient,
uint256 amount,
bytes calldata userData,
bytes calldata operatorData
) external {
address operator = _msgSender();
require(isOperatorFor(operator, sender), "SubredditPoints: caller is not an operator for holder");
_transfer(sender, recipient, amount);
emit Sent(operator, sender, recipient, amount, userData, operatorData);
}
function operatorBurn(
address account,
uint256 amount,
bytes calldata data,
bytes calldata operatorData
) external {
address operator = _msgSender();
require(isOperatorFor(operator, account), "SubredditPoints: caller is not an operator for holder");
_burn(operator, account, amount, data, operatorData);
}
function defaultOperators() external view returns (address[] memory) {
return _defaultOperatorsArray;
}
function addDefaultOperator(address operator) external onlyOwner {
require(operator != address(0), "SubredditPoints: operator address shouldn't be 0");
require(!_defaultOperators[operator], "SubredditPoints: operator already exists");
_defaultOperatorsArray.push(operator);
_defaultOperators[operator] = true;
emit DefaultOperatorAdded(operator);
}
function removeDefaultOperator(address operator) external onlyOwner {
require(operator != address(0), "SubredditPoints: operator address shouldn't be 0");
require(_defaultOperators[operator], "SubredditPoints: operator doesn't exists");
for (uint256 i = 0; i < _defaultOperatorsArray.length; i++) {
if (_defaultOperatorsArray[i] == operator) {
if (i != (_defaultOperatorsArray.length - 1)) { // if it's not last element, replace it from the tail
_defaultOperatorsArray[i] = _defaultOperatorsArray[_defaultOperatorsArray.length-1];
}
_defaultOperatorsArray.length = _defaultOperatorsArray.length - 1;
break;
}
}
delete _defaultOperators[operator];
emit DefaultOperatorRemoved(operator);
}
function _burn(
address operator,
address account,
uint256 amount,
bytes memory userData,
bytes memory operatorData
) internal {
ERC20._burn(account, amount);
emit Burned(operator, account, amount, userData, operatorData);
}
function subreddit() external view returns (string memory) {
return _subreddit;
}
function name() external view returns (string memory) {
return _name;
}
function symbol() external view returns (string memory) {
return _symbol;
}
function updateGSNApprover(address gsnApprover) external onlyOwner {
updateSigner(gsnApprover);
}
function updateDistributionContract(address distributionContract_) public onlyOwner {
require(distributionContract_ != address(0), "SubredditPoints: distributionContract should not be 0");
_distributionContract = distributionContract_;
}
function distributionContract() external view returns (address) {
return _distributionContract;
}
}
// File: contracts/Distributions_v0.sol
/*
Copyright (c) 2020 Reddit Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided
that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse
or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSEARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
pragma solidity >=0.5.0 < 0.6.0;
contract Distributions_v0 is Initializable, Ownable, UpdatableGSNRecipientSignature {
struct SharedOwner {
address account;
uint256 percent; // e.g. 30% percent = 30 * percentPrecision/100
}
struct DistributionRound {
uint256 availablePoints;
uint256 sharedOwnersAvailablePoints;
uint256 totalKarma;
}
event SharedOwnerUpdated(
address indexed _from,
address indexed _to,
uint256 _percent
);
event AdvanceRound(uint256 round, uint256 totalPoints, uint256 sharedOwnersPoints);
event ClaimPoints(uint256 round, address indexed user, uint256 karma, uint256 points);
event KarmaSourceUpdated(address _karmaSource, address _prevKarmaSource);
event SupplyDecayPercentUpdated(uint256 supplyDecayPercent);
event RoundsBeforeExpirationUpdated(uint256 roundsBeforeExpiration);
using SafeMath for uint256;
using Address for address;
// ------------------------------------------------------------------------------------
// VARIABLES BLOCK, MAKE SURE ONLY ADD TO THE END
address public subredditPoints;
address public karmaSource;
string public subreddit;
string private _subredditLowerCase;
uint256 public lastRound;
uint256 public startBlockNumber;
// maps round number to round data
mapping(uint256 => DistributionRound) private _distributionRounds;
// maps account to next claimable round
mapping(address => uint256) private _claimableRounds;
// when sharing percentage, the least possible share is 1/percentPrecision
uint256 public constant PERCENT_PRECISION = 1000000;
uint256 public constant MAX_ROUND_SUPPLY = 10**11 * 10**18; // max is 100 bln, to prevent overflows
// 1 shared owner ~ 35k gas + 250k gas for other ops in advanceToRound
// so we limit to (8M - 250k)/35k = 221 maximum shared owners
uint256 public constant MAX_SHARED_OWNERS = 200;
uint256 public constant MAX_SKIP_ROUNDS = 10;
uint256 public initialSupply;
uint256 public roundsBeforeExpiration;
uint256 public nextSupply;
uint256 public supplyDecayPercent;
// those owners if exists will get proportion of minted points according to value in percentage
SharedOwner[] public sharedOwners;
uint256 private _prevRoundSupply; // supply in a prev round
uint256 private _prevClaimed; // total claimed in a prev round
// Previous karmaSource signer. Used when rotating karmaSource key to enable
// previous signer to still be valid for a while.
address public prevKarmaSource;
// END OF VARS
// ------------------------------------------------------------------------------------
function initialize(
address owner_,
address subredditPoints_, // ISubredditPoints + IERC20 token contract address
address karmaSource_, // Karma source provider address
address gsnApprover_, // GSN approver address
uint256 initialSupply_,
uint256 nextSupply_,
uint256 initialKarma_,
uint256 roundsBeforeExpiration_, // how many rounds are passed before claiming is possible
uint256 supplyDecayPercent_, // defines percentage of next rounds' supply from the current
address[] calldata sharedOwners_,
uint256[] calldata sharedOwnersPercs_ // index of percentages must correspond to _sharedOwners array
) external initializer {
require(initialSupply_ > 0 && initialSupply_ <= MAX_ROUND_SUPPLY, "Distributions: initial supply should be > 0 and <= MAX_ROUND_SUPPLY");
require(initialKarma_ > 0, "Distributions: initial karma should be more than 0");
require(nextSupply_ > 0 && nextSupply_ <= MAX_ROUND_SUPPLY, "Distributions: nextSupply should be > 0 and <= MAX_ROUND_SUPPLY");
require(karmaSource_ != address(0), "Distributions: karma source should not be 0");
require(gsnApprover_ != address(0), "Distributions: GSN approver should not be 0");
require(owner_ != address(0), "Distributions: owner should not be 0");
require(sharedOwners_.length == sharedOwnersPercs_.length, "Shared owners: Addresses array must be same length as percentages");
Ownable.initialize(owner_);
UpdatableGSNRecipientSignature.initialize(gsnApprover_);
updateSupplyDecayPercent(supplyDecayPercent_);
updateRoundsBeforeExpiration(roundsBeforeExpiration_);
subredditPoints = subredditPoints_;
karmaSource = karmaSource_;
prevKarmaSource = karmaSource_;
subreddit = ISubredditPoints(subredditPoints_).subreddit();
_subredditLowerCase = _toLower(subreddit);
startBlockNumber = block.number;
initialSupply = initialSupply_;
nextSupply = nextSupply_;
for (uint i = 0; i < sharedOwners_.length; i++) {
_updateSharedOwner(sharedOwners_[i], sharedOwnersPercs_[i]);
}
uint256 sharedOwnersPoints = calcSharedOwnersAvailablePoints(initialSupply);
_distributionRounds[0] = DistributionRound({
availablePoints: initialSupply,
sharedOwnersAvailablePoints: sharedOwnersPoints,
totalKarma: initialKarma_
});
emit AdvanceRound(0, initialSupply, sharedOwnersPoints);
}
function claim(uint256 round, address account, uint256 karma, bytes calldata signature) external {
require(karma > 0, "Distributions: karma should be > 0");
require(_claimableRounds[account] <= round, "Distributions: this rounds points are already claimed");
require(round <= lastRound, "Distributions: too early to claim this round");
uint256 mc = minClaimableRound();
require(round >= mc, "Distributions: too late to claim this round");
address signedBy = verifySignature(account, round, karma, signature);
require(signedBy == karmaSource || (prevKarmaSource != address(0) && signedBy == prevKarmaSource), "Distributions: claim is not signed by the karma source");
DistributionRound memory dr = _distributionRounds[round];
require(dr.availablePoints > 0, "Distributions: no points to claim in this round");
require(dr.totalKarma > 0, "Distributions: this round has no karma");
uint256 userPoints = dr.availablePoints
.sub(dr.sharedOwnersAvailablePoints)
.mul(karma)
.div(dr.totalKarma);
require(userPoints > 0, "Distributions: user karma is too low to claim points");
_prevClaimed = _prevClaimed.add(userPoints);
_claimableRounds[account] = round.add(1);
emit ClaimPoints(round, account, karma, userPoints);
ISubredditPoints(subredditPoints).mint(address(this), account, userPoints, "", "");
}
// corresponding _distributionRounds mappings are added with
// + every next distribution supply is `previous - decay` and stored in nextSupply
// + distributed 50% of burned points in a previous round
// rounds are removed if they are not claimable anymore
function advanceToRound(uint256 round, uint256 totalKarma) external {
require((round > lastRound) && (round < lastRound + MAX_SKIP_ROUNDS), "Distributions: round should be > lastRound and < lastRound + MAX_SKIP_ROUNDS");
require(totalKarma > 0, "Distributions: totalKarma should be > 0");
require(_msgSender() == karmaSource, "Distributions: only karma source can advance rounds");
uint256 mc = minClaimableRound();
if (mc >= (round - lastRound)) {
for (uint256 i = mc - (round - lastRound); i < mc; i++) {
delete(_distributionRounds[i]);
}
}
uint256 ts = IERC20(subredditPoints).totalSupply();
uint256 prevClaimedCopy = _prevClaimed;
// normally this loop should complete in 1 cycle
// we move backwards from last to previous rounds
for (uint256 i = round; i >= (lastRound + 1) && i >= mc; i--) {
uint256 roundPoints = nextSupply;
// reintroduce 50 % of previously burned tokens
uint256 ps = _prevRoundSupply.add(_prevClaimed);
if (ps > ts) {
roundPoints = roundPoints.add(ps.sub(ts).div(2));
}
// if there is more than 1 cycle, all burned will be reintroduced into the last round
// the loop is not stopped due to it may switch to halving for a previous rounds
_prevRoundSupply = ts;
_prevClaimed = 0;
if (nextSupply > 0 && supplyDecayPercent > 0) {
nextSupply = nextSupply.sub(nextSupply.mul(supplyDecayPercent).div(PERCENT_PRECISION));
}
uint256 sharedOwnersPoints = 0;
if (roundPoints > 0) {
sharedOwnersPoints = calcSharedOwnersAvailablePoints(roundPoints);
_distributionRounds[i] = DistributionRound({
availablePoints: roundPoints,
sharedOwnersAvailablePoints: sharedOwnersPoints,
totalKarma: 0
});
}
emit AdvanceRound(i, roundPoints, sharedOwnersPoints);
}
lastRound = round;
_prevRoundSupply = ts;
_prevClaimed = 0;
DistributionRound storage dc = _distributionRounds[round];
dc.totalKarma = totalKarma;
// distribute shared cut, but no more than it was claimed by users
// this protects from exceeding total amount by increasing percentage between rounds
if (dc.sharedOwnersAvailablePoints > 0 && prevClaimedCopy > 0) {
uint256 totalSharedPercent;
for (uint256 i = 0; i < sharedOwners.length; i++) {
totalSharedPercent = totalSharedPercent.add(sharedOwners[i].percent);
}
uint256 claimedPlusShared = prevClaimedCopy
.mul(PERCENT_PRECISION)
.div(PERCENT_PRECISION.sub(totalSharedPercent));
uint256 sharedLeft = claimedPlusShared.sub(prevClaimedCopy);
for (uint256 i = 0; i < sharedOwners.length && sharedLeft > 0; i++) {
uint256 ownerPoints = claimedPlusShared.mul(sharedOwners[i].percent).div(PERCENT_PRECISION);
if (ownerPoints > 0 && ownerPoints <= sharedLeft) {
ISubredditPoints(subredditPoints).mint(address(this), sharedOwners[i].account, ownerPoints, "", "");
sharedLeft = sharedLeft.sub(ownerPoints);
}
}
}
}
function totalSharedOwners() external view returns (uint256) {
return sharedOwners.length;
}
function updateSupplyDecayPercent(uint256 _supplyDecayPercent) public onlyOwner {
require(_supplyDecayPercent < PERCENT_PRECISION, "Distributions: supplyDecayPercent should be < PERCENT_PRECISION");
supplyDecayPercent = _supplyDecayPercent;
emit SupplyDecayPercentUpdated(_supplyDecayPercent);
}
function updateRoundsBeforeExpiration(uint256 _roundsBeforeExpiration) public onlyOwner {
roundsBeforeExpiration = _roundsBeforeExpiration;
emit RoundsBeforeExpirationUpdated(_roundsBeforeExpiration);
}
function minClaimableRound() public view returns (uint256) {
if (lastRound >= roundsBeforeExpiration) {
return lastRound.sub(roundsBeforeExpiration);
}
return 0;
}
function verifySignature(address account, uint256 round, uint256 karma, bytes memory signature)
private view returns (address) {
bytes32 hash = keccak256(abi.encode(_subredditLowerCase, uint256(round), account, karma));
bytes32 prefixedHash = ECDSA.toEthSignedMessageHash(hash);
return ECDSA.recover(prefixedHash, signature);
}
function calcSharedOwnersAvailablePoints(uint256 points) private view returns (uint256) {
uint256 r;
for (uint256 i = 0; i < sharedOwners.length; i++) {
r = r.add(calcSharedPoints(points, sharedOwners[i]));
}
return r;
}
function calcSharedPoints(uint256 points, SharedOwner memory sharedOwner) private pure returns (uint256) {
return points
.mul(sharedOwner.percent)
.div(PERCENT_PRECISION);
}
function updateKarmaSource(address _karmaSource) external onlyOwner {
require(_karmaSource != address(0), "Distributions: karma source should not be 0");
prevKarmaSource = karmaSource;
karmaSource = _karmaSource;
emit KarmaSourceUpdated(_karmaSource, prevKarmaSource);
}
function updateGSNApprover(address gsnApprover) external onlyOwner {
updateSigner(gsnApprover);
}
// shared owners get their points 1 round later within advancement
// increasing total shared percentage can lead to some of the owners not receiving their cut within a next round
function updateSharedOwner(address account, uint256 percent) external onlyOwner {
_updateSharedOwner(account, percent);
}
function _updateSharedOwner(address account, uint256 percent) internal {
require(percent < PERCENT_PRECISION, "Distributions: shared owners percent should be < percentPrecision");
require(percent > 0 && sharedOwners.length < MAX_SHARED_OWNERS, "Distributions: shared owners limit reached, see MAX_SHARED_OWNERS");
bool updated = false;
for (uint256 i = 0; i < sharedOwners.length; i++) {
SharedOwner memory so = sharedOwners[i];
if (so.account == account) {
if (percent == 0) {
if (i != (sharedOwners.length - 1)) { // if it's not last element, replace it from the tail
sharedOwners[i] = sharedOwners[sharedOwners.length-1];
}
// remove tail
sharedOwners.length = sharedOwners.length - 1;
} else {
sharedOwners[i].percent = percent;
}
updated = true;
}
}
if (!updated) {
if (percent == 0) {
return;
}
sharedOwners.push(SharedOwner(account, percent));
}
checkSharedPercentage();
// allow to update sharedOwnersAvailablePoints for a rounds which aren't claimed yet
DistributionRound storage dr = _distributionRounds[lastRound];
if (_prevClaimed == 0 && dr.availablePoints > 0) {
dr.sharedOwnersAvailablePoints = calcSharedOwnersAvailablePoints(dr.availablePoints);
}
emit SharedOwnerUpdated(_msgSender(), account, percent);
}
function checkSharedPercentage() private view {
uint256 total;
for (uint256 i = 0; i < sharedOwners.length; i++) {
total = sharedOwners[i].percent.add(total);
}
require(total < PERCENT_PRECISION, "Distributions: can't share all 100% of points");
}
function percentPrecision() external pure returns (uint256) {
return PERCENT_PRECISION;
}
function _toLower(string memory str) internal pure returns (string memory) {
bytes memory bStr = bytes(str);
bytes memory bLower = new bytes(bStr.length);
for (uint i = 0; i < bStr.length; i++) {
if ((int8(bStr[i]) >= 65) && (int8(bStr[i]) <= 90)) {
bLower[i] = bytes1(int8(bStr[i]) + 32);
} else {
bLower[i] = bStr[i];
}
}
return string(bLower);
}
}

Contract ABI

[{"type":"event","name":"AdvanceRound","inputs":[{"type":"uint256","name":"round","internalType":"uint256","indexed":false},{"type":"uint256","name":"totalPoints","internalType":"uint256","indexed":false},{"type":"uint256","name":"sharedOwnersPoints","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ClaimPoints","inputs":[{"type":"uint256","name":"round","internalType":"uint256","indexed":false},{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"uint256","name":"karma","internalType":"uint256","indexed":false},{"type":"uint256","name":"points","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"KarmaSourceUpdated","inputs":[{"type":"address","name":"_karmaSource","internalType":"address","indexed":false},{"type":"address","name":"_prevKarmaSource","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"RelayHubChanged","inputs":[{"type":"address","name":"oldRelayHub","internalType":"address","indexed":true},{"type":"address","name":"newRelayHub","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"RoundsBeforeExpirationUpdated","inputs":[{"type":"uint256","name":"roundsBeforeExpiration","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SharedOwnerUpdated","inputs":[{"type":"address","name":"_from","internalType":"address","indexed":true},{"type":"address","name":"_to","internalType":"address","indexed":true},{"type":"uint256","name":"_percent","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SignerUpdated","inputs":[{"type":"address","name":"signer","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"SupplyDecayPercentUpdated","inputs":[{"type":"uint256","name":"supplyDecayPercent","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_ROUND_SUPPLY","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_SHARED_OWNERS","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_SKIP_ROUNDS","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"PERCENT_PRECISION","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"bytes","name":"","internalType":"bytes"}],"name":"acceptRelayedCall","inputs":[{"type":"address","name":"relay","internalType":"address"},{"type":"address","name":"from","internalType":"address"},{"type":"bytes","name":"encodedFunction","internalType":"bytes"},{"type":"uint256","name":"transactionFee","internalType":"uint256"},{"type":"uint256","name":"gasPrice","internalType":"uint256"},{"type":"uint256","name":"gasLimit","internalType":"uint256"},{"type":"uint256","name":"nonce","internalType":"uint256"},{"type":"bytes","name":"approvalData","internalType":"bytes"},{"type":"uint256","name":"","internalType":"uint256"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"advanceToRound","inputs":[{"type":"uint256","name":"round","internalType":"uint256"},{"type":"uint256","name":"totalKarma","internalType":"uint256"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"claim","inputs":[{"type":"uint256","name":"round","internalType":"uint256"},{"type":"address","name":"account","internalType":"address"},{"type":"uint256","name":"karma","internalType":"uint256"},{"type":"bytes","name":"signature","internalType":"bytes"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":"","internalType":"address"}],"name":"getHubAddr","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"initialSupply","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"initialize","inputs":[],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"initialize","inputs":[{"type":"address","name":"trustedSigner","internalType":"address"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"initialize","inputs":[{"type":"address","name":"owner_","internalType":"address"},{"type":"address","name":"subredditPoints_","internalType":"address"},{"type":"address","name":"karmaSource_","internalType":"address"},{"type":"address","name":"gsnApprover_","internalType":"address"},{"type":"uint256","name":"initialSupply_","internalType":"uint256"},{"type":"uint256","name":"nextSupply_","internalType":"uint256"},{"type":"uint256","name":"initialKarma_","internalType":"uint256"},{"type":"uint256","name":"roundsBeforeExpiration_","internalType":"uint256"},{"type":"uint256","name":"supplyDecayPercent_","internalType":"uint256"},{"type":"address[]","name":"sharedOwners_","internalType":"address[]"},{"type":"uint256[]","name":"sharedOwnersPercs_","internalType":"uint256[]"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isOwner","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":"","internalType":"address"}],"name":"karmaSource","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"lastRound","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"minClaimableRound","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"nextSupply","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[],"constant":true},{"type":"function","stateMutability":"pure","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"percentPrecision","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"postRelayedCall","inputs":[{"type":"bytes","name":"context","internalType":"bytes"},{"type":"bool","name":"success","internalType":"bool"},{"type":"uint256","name":"actualCharge","internalType":"uint256"},{"type":"bytes32","name":"preRetVal","internalType":"bytes32"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"preRelayedCall","inputs":[{"type":"bytes","name":"context","internalType":"bytes"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":"","internalType":"address"}],"name":"prevKarmaSource","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"string","name":"","internalType":"string"}],"name":"relayHubVersion","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"renounceOwnership","inputs":[],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"roundsBeforeExpiration","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"setDefaultRelayHub","inputs":[],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":"account","internalType":"address"},{"type":"uint256","name":"percent","internalType":"uint256"}],"name":"sharedOwners","inputs":[{"type":"uint256","name":"","internalType":"uint256"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"startBlockNumber","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"string","name":"","internalType":"string"}],"name":"subreddit","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":"","internalType":"address"}],"name":"subredditPoints","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"supplyDecayPercent","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalSharedOwners","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"updateGSNApprover","inputs":[{"type":"address","name":"gsnApprover","internalType":"address"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"updateKarmaSource","inputs":[{"type":"address","name":"_karmaSource","internalType":"address"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"updateRoundsBeforeExpiration","inputs":[{"type":"uint256","name":"_roundsBeforeExpiration","internalType":"uint256"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"updateSharedOwner","inputs":[{"type":"address","name":"account","internalType":"address"},{"type":"uint256","name":"percent","internalType":"uint256"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"updateSupplyDecayPercent","inputs":[{"type":"uint256","name":"_supplyDecayPercent","internalType":"uint256"}],"constant":false}]
            

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106102315760003560e01c80638129fc1c11610130578063b994a7c5116100b8578063e06e0e221161007c578063e06e0e221461083e578063e361349d146108ba578063f2fde38b146108c2578063f9714c0c146108e8578063ffe8e372146108f057610231565b8063b994a7c5146106c1578063bdc330cb146106c9578063c32be428146106d1578063c4d66de814610711578063d8906f3d1461073757610231565b80638da5cb5b116100ff5780638da5cb5b1461058e5780638f32d59b1461059657806399016142146105b2578063ad61ccd51461063c578063b361b00f146106b957610231565b80638129fc1c1461040957806382bc07e61461041157806383947ea014610419578063893040611461058657610231565b8063498a4c2d116101be578063715018a611610182578063715018a61461034857806374e861d6146103505780637e6b26cf146103585780637f3619281461037557806380274db71461039b57610231565b8063498a4c2d146102f057806351ad5a97146102f857806356df8adb146103005780635a5f219414610323578063636049a71461032b57610231565b80632ad9b698116102055780632ad9b698146102a45780632fd4af05146102ac578063303238a2146102d857806330e3a035146102e0578063378dc3dc146102e857610231565b806215d5b1146102365780631220e2ff1461025a578063197ad63a146102645780631c00f07f1461027e575b600080fd5b61023e6108f8565b604080516001600160a01b039092168252519081900360200190f35b610262610907565b005b61026c610926565b60408051918252519081900360200190f35b6102626004803603602081101561029457600080fd5b50356001600160a01b0316610956565b61026c6109a9565b610262600480360360408110156102c257600080fd5b506001600160a01b0381351690602001356109af565b61026c610a04565b61026c610a09565b61026c610a0e565b61026c610a14565b61026c610a1a565b6102626004803603604081101561031657600080fd5b5080359060200135610a20565b61023e610f54565b6102626004803603602081101561034157600080fd5b5035610f63565b610262610fe5565b61023e611076565b6102626004803603602081101561036e57600080fd5b5035611085565b6102626004803603602081101561038b57600080fd5b50356001600160a01b0316611148565b61026c600480360360208110156103b157600080fd5b810190602081018135600160201b8111156103cb57600080fd5b8201836020820111156103dd57600080fd5b803590602001918460018302840111600160201b831117156103fe57600080fd5b509092509050611240565b6102626112e1565b61026c61139a565b610507600480360361012081101561043057600080fd5b6001600160a01b038235811692602081013590911691810190606081016040820135600160201b81111561046357600080fd5b82018360208201111561047557600080fd5b803590602001918460018302840111600160201b8311171561049657600080fd5b9193909282359260208101359260408201359260608301359260a081019060800135600160201b8111156104c957600080fd5b8201836020820111156104db57600080fd5b803590602001918460018302840111600160201b831117156104fc57600080fd5b9193509150356113a0565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561054a578181015183820152602001610532565b50505050905090810190601f1680156105775780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b61023e611532565b61023e611541565b61059e611550565b604080519115158252519081900360200190f35b610262600480360360808110156105c857600080fd5b8135916001600160a01b036020820135169160408201359190810190608081016060820135600160201b8111156105fe57600080fd5b82018360208201111561061057600080fd5b803590602001918460018302840111600160201b8311171561063157600080fd5b509092509050611576565b6106446119ac565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561067e578181015183820152602001610666565b50505050905090810190601f1680156106ab5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61026c6119cb565b61026c6119dc565b6106446119e3565b6106ee600480360360208110156106e757600080fd5b5035611a71565b604080516001600160a01b03909316835260208301919091528051918290030190f35b6102626004803603602081101561072757600080fd5b50356001600160a01b0316611aa6565b610262600480360361016081101561074e57600080fd5b6001600160a01b038235811692602081013582169260408201358316926060830135169160808101359160a08201359160c08101359160e082013591610100810135918101906101408101610120820135600160201b8111156107b057600080fd5b8201836020820111156107c257600080fd5b803590602001918460208302840111600160201b831117156107e357600080fd5b919390929091602081019035600160201b81111561080057600080fd5b82018360208201111561081257600080fd5b803590602001918460208302840111600160201b8311171561083357600080fd5b509092509050611b59565b6102626004803603608081101561085457600080fd5b810190602081018135600160201b81111561086e57600080fd5b82018360208201111561088057600080fd5b803590602001918460018302840111600160201b831117156108a157600080fd5b9193509150803515159060208101359060400135612184565b61026c612226565b610262600480360360208110156108d857600080fd5b50356001600160a01b031661222c565b61026c61227c565b61026c612282565b6069546001600160a01b031681565b61092473d216153c06e857cd7f72665e0af1d7d82172f494612289565b565b6000607154606c541061094f57607154606c546109489163ffffffff61238916565b9050610953565b5060005b90565b61095e611550565b61099d576040805162461bcd60e51b81526020600482018190526024820152600080516020613466833981519152604482015290519081900360640190fd5b6109a6816123cb565b50565b60725481565b6109b7611550565b6109f6576040805162461bcd60e51b81526020600482018190526024820152600080516020613466833981519152604482015290519081900360640190fd5b610a008282612464565b5050565b60c881565b600a81565b60705481565b606d5481565b60735481565b606c5482118015610a355750600a606c540182105b610a705760405162461bcd60e51b815260040180806020018281038252604c8152602001806133ac604c913960600191505060405180910390fd5b60008111610aaf5760405162461bcd60e51b815260040180806020018281038252602781526020018061343f6027913960400191505060405180910390fd5b6069546001600160a01b0316610ac3612758565b6001600160a01b031614610b085760405162461bcd60e51b81526004018080602001828103825260338152602001806136246033913960400191505060405180910390fd5b6000610b12610926565b9050606c5483038110610b5957606c54830381035b81811015610b57576000818152606e60205260408120818155600180820183905560029091019190915501610b27565b505b606854604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd916004808301926020929190829003018186803b158015610b9e57600080fd5b505afa158015610bb2573d6000803e3d6000fd5b505050506040513d6020811015610bc857600080fd5b5051607654909150845b606c546001018110158015610be75750838110155b15610d3857607254607654607554600091610c08919063ffffffff61277c16565b905084811115610c4657610c43610c366002610c2a848963ffffffff61238916565b9063ffffffff6127d616565b839063ffffffff61277c16565b91505b6075859055600060765560725415801590610c6357506000607354115b15610c9c57610c98610c89620f4240610c2a60735460725461281890919063ffffffff16565b6072549063ffffffff61238916565b6072555b60008215610cec57610cad83612871565b60408051606081018252858152602080820184815260008385018181528a8252606e909352939093209151825591516001820155905160029091015590505b604080518581526020810185905280820183905290517f7abd75c194a519c7fa820e973913b881d125f00b25a62aa1b242f1a3be7313a09181900360600190a150505060001901610bd2565b50606c859055607582905560006076819055858152606e6020526040902060028101859055600181015415801590610d705750600082115b15610f4c576000805b607454811015610dbe57610db460748281548110610d9357fe5b9060005260206000209060020201600101548361277c90919063ffffffff16565b9150600101610d79565b506000610dea610dd7620f42408463ffffffff61238916565b610c2a86620f424063ffffffff61281816565b90506000610dfe828663ffffffff61238916565b905060005b60745481108015610e145750600082115b15610f47576000610e53620f4240610c2a60748581548110610e3257fe5b9060005260206000209060020201600101548761281890919063ffffffff16565b9050600081118015610e655750828111155b15610f3e57606854607480546001600160a01b039092169163ab89013b91309186908110610e8f57fe5b60009182526020822060029190910201546040805160e086811b6001600160e01b03191682526001600160a01b0395861660048301529490921660248301526044820187905260a0606483015260a48201839052608482019390935260e4810182905291516101248084019382900301818387803b158015610f1057600080fd5b505af1158015610f24573d6000803e3d6000fd5b50505050610f3b818461238990919063ffffffff16565b92505b50600101610e03565b505050505b505050505050565b6077546001600160a01b031681565b610f6b611550565b610faa576040805162461bcd60e51b81526020600482018190526024820152600080516020613466833981519152604482015290519081900360640190fd5b60718190556040805182815290517f9b9d9ee399388905981700b7c8de8ca8853dbf397b77d509d7ed7a3d4dff52a69181900360200190a150565b610fed611550565b61102c576040805162461bcd60e51b81526020600482018190526024820152600080516020613466833981519152604482015290519081900360640190fd5b6033546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3603380546001600160a01b0319169055565b6066546001600160a01b031690565b61108d611550565b6110cc576040805162461bcd60e51b81526020600482018190526024820152600080516020613466833981519152604482015290519081900360640190fd5b620f4240811061110d5760405162461bcd60e51b815260040180806020018281038252603f8152602001806131db603f913960400191505060405180910390fd5b60738190556040805182815290517f3fc8c6c6a50072e9b4dcce50dc8a4f21877704651dff1c72f102afde8e08016b9181900360200190a150565b611150611550565b61118f576040805162461bcd60e51b81526020600482018190526024820152600080516020613466833981519152604482015290519081900360640190fd5b6001600160a01b0381166111d45760405162461bcd60e51b815260040180806020018281038252602b8152602001806136b1602b913960400191505060405180910390fd5b60698054607780546001600160a01b038084166001600160a01b031992831617928390559216848316908117909355604080519384529116602083015280517fea601ecdff79475b71fb4d1545a7ebca8349b2af1ca1ff06dae26d8598067e129281900390910190a150565b600061124a611076565b6001600160a01b0316336001600160a01b0316146112995760405162461bcd60e51b81526004018080602001828103825260248152602001806135cb6024913960400191505060405180910390fd5b6112d883838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061094f92505050565b90505b92915050565b600054610100900460ff16806112fa57506112fa6128dd565b80611308575060005460ff16155b6113435760405162461bcd60e51b815260040180806020018281038252602e815260200180613486602e913960400191505060405180910390fd5b600054610100900460ff1615801561136e576000805460ff1961ff0019909116610100171660011790555b6066546001600160a01b031661138657611386610907565b80156109a6576000805461ff001916905550565b606c5481565b60006060808d8d8d8d8d8d8d8d6113b5611076565b30604051602001808b6001600160a01b03166001600160a01b031681526020018a6001600160a01b03166001600160a01b0316815260200180602001888152602001878152602001868152602001858152602001846001600160a01b03166001600160a01b03168152602001836001600160a01b03166001600160a01b0316815260200182810382528a8a82818152602001925080828437600081840152601f19601f8201169050808301925050509b5050505050505050505050506040516020818303038152906040529050606760009054906101000a90046001600160a01b03166001600160a01b03166114f787878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050855160208701206114eb925090506128e3565b9063ffffffff61293416565b6001600160a01b031614156115185761150e612a22565b9250925050611522565b61150e6000612a46565b9b509b9950505050505050505050565b6068546001600160a01b031681565b6033546001600160a01b031690565b6033546000906001600160a01b0316611567612758565b6001600160a01b031614905090565b600083116115b55760405162461bcd60e51b81526004018080602001828103825260228152602001806132d26022913960400191505060405180910390fd5b6001600160a01b0384166000908152606f602052604090205485101561160c5760405162461bcd60e51b81526004018080602001828103825260358152602001806135ef6035913960400191505060405180910390fd5b606c5485111561164d5760405162461bcd60e51b815260040180806020018281038252602c81526020018061311f602c913960400191505060405180910390fd5b6000611657610926565b9050808610156116985760405162461bcd60e51b815260040180806020018281038252602b81526020018061314b602b913960400191505060405180910390fd5b60006116dc86888787878080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612a5e92505050565b6069549091506001600160a01b038083169116148061171d57506077546001600160a01b03161580159061171d57506077546001600160a01b038281169116145b6117585760405162461bcd60e51b81526004018080602001828103825260368152602001806136576036913960400191505060405180910390fd5b611760612ff2565b506000878152606e602090815260409182902082516060810184528154808252600183015493820193909352600290910154928101929092526117d45760405162461bcd60e51b815260040180806020018281038252602f81526020018061355b602f913960400191505060405180910390fd5b60008160400151116118175760405162461bcd60e51b81526004018080602001828103825260268152602001806133f86026913960400191505060405180910390fd5b600061184c8260400151610c2a896118408660200151876000015161238990919063ffffffff16565b9063ffffffff61281816565b90506000811161188d5760405162461bcd60e51b815260040180806020018281038252603481526020018061325d6034913960400191505060405180910390fd5b6076546118a0908263ffffffff61277c16565b6076556118b489600163ffffffff61277c16565b6001600160a01b0389166000818152606f60209081526040918290209390935580518c81529283018a90528281018490525190917f05c0dd48c9c829cad200bd1db15fa75fb03cae88add3bd16cb8899cc39412f74919081900360600190a26068546040805163ab89013b60e01b81523060048201526001600160a01b038b811660248301526044820185905260a06064830152600060a4830181905260e0608484015260e48301819052925193169263ab89013b926101248084019391929182900301818387803b15801561198957600080fd5b505af115801561199d573d6000803e3d6000fd5b50505050505050505050505050565b6040805180820190915260058152640312e302e360dc1b602082015290565b6c01431e0fae6d7217caa000000081565b620f424090565b606a805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015611a695780601f10611a3e57610100808354040283529160200191611a69565b820191906000526020600020905b815481529060010190602001808311611a4c57829003601f168201915b505050505081565b60748181548110611a7e57fe5b6000918252602090912060029091020180546001909101546001600160a01b03909116915082565b600054610100900460ff1680611abf5750611abf6128dd565b80611acd575060005460ff16155b611b085760405162461bcd60e51b815260040180806020018281038252602e815260200180613486602e913960400191505060405180910390fd5b600054610100900460ff16158015611b33576000805460ff1961ff0019909116610100171660011790555b611b3c826123cb565b611b446112e1565b8015610a00576000805461ff00191690555050565b600054610100900460ff1680611b725750611b726128dd565b80611b80575060005460ff16155b611bbb5760405162461bcd60e51b815260040180806020018281038252602e815260200180613486602e913960400191505060405180910390fd5b600054610100900460ff16158015611be6576000805460ff1961ff0019909116610100171660011790555b60008a118015611c0357506c01431e0fae6d7217caa00000008a11155b611c3e5760405162461bcd60e51b815260040180806020018281038252604381526020018061321a6043913960600191505060405180910390fd5b60008811611c7d5760405162461bcd60e51b815260040180806020018281038252603281526020018061337a6032913960400191505060405180910390fd5b600089118015611c9a57506c01431e0fae6d7217caa00000008911155b611cd55760405162461bcd60e51b815260040180806020018281038252603f815260200180613176603f913960400191505060405180910390fd5b6001600160a01b038c16611d1a5760405162461bcd60e51b815260040180806020018281038252602b8152602001806136b1602b913960400191505060405180910390fd5b6001600160a01b038b16611d5f5760405162461bcd60e51b815260040180806020018281038252602b81526020018061334f602b913960400191505060405180910390fd5b6001600160a01b038e16611da45760405162461bcd60e51b815260040180806020018281038252602481526020018061368d6024913960400191505060405180910390fd5b838214611de25760405162461bcd60e51b81526004018080602001828103825260418152602001806134e16041913960600191505060405180910390fd5b611deb8e612b48565b611df48b611aa6565b611dfd86611085565b611e0687610f63565b8c606860006101000a8154816001600160a01b0302191690836001600160a01b031602179055508b606960006101000a8154816001600160a01b0302191690836001600160a01b031602179055508b607760006101000a8154816001600160a01b0302191690836001600160a01b031602179055508c6001600160a01b031663bdc330cb6040518163ffffffff1660e01b815260040160006040518083038186803b158015611eb457600080fd5b505afa158015611ec8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015611ef157600080fd5b8101908080516040519392919084600160201b821115611f1057600080fd5b908301906020820185811115611f2557600080fd5b8251600160201b811182820188101715611f3e57600080fd5b82525081516020918201929091019080838360005b83811015611f6b578181015183820152602001611f53565b50505050905090810190601f168015611f985780820380516001836020036101000a031916815260200191505b5060405250508151611fb192606a925060200190613013565b50606a8054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152612049939092909183018282801561203f5780601f106120145761010080835404028352916020019161203f565b820191906000526020600020905b81548152906001019060200180831161202257829003601f168201915b5050505050612c39565b805161205d91606b91602090910190613013565b5043606d5560708a9055607289905560005b848110156120b6576120ae86868381811061208657fe5b905060200201356001600160a01b03168585848181106120a257fe5b90506020020135612464565b60010161206f565b5060006120c4607054612871565b9050604051806060016040528060705481526020018281526020018a815250606e60008081526020019081526020016000206000820151816000015560208201518160010155604082015181600201559050507f7abd75c194a519c7fa820e973913b881d125f00b25a62aa1b242f1a3be7313a060006070548360405180848152602001838152602001828152602001935050505060405180910390a1508015612174576000805461ff00191690555b5050505050505050505050505050565b61218c611076565b6001600160a01b0316336001600160a01b0316146121db5760405162461bcd60e51b81526004018080602001828103825260248152602001806135cb6024913960400191505060405180910390fd5b61221f85858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250879250869150859050612d52565b5050505050565b60745490565b612234611550565b612273576040805162461bcd60e51b81526020600482018190526024820152600080516020613466833981519152604482015290519081900360640190fd5b6109a681612d58565b60715481565b620f424081565b6066546001600160a01b039081169082166122d55760405162461bcd60e51b815260040180806020018281038252602e815260200180613321602e913960400191505060405180910390fd5b806001600160a01b0316826001600160a01b031614156123265760405162461bcd60e51b815260040180806020018281038252602d8152602001806134b4602d913960400191505060405180910390fd5b816001600160a01b0316816001600160a01b03167fb9f84b8e65164b14439ae3620df0a4d8786d896996c0282b683f9d8c08f046e860405160405180910390a350606680546001600160a01b0319166001600160a01b0392909216919091179055565b60006112d883836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612df9565b6001600160a01b0381166124105760405162461bcd60e51b81526004018080602001828103825260398152602001806135226039913960400191505060405180910390fd5b606780546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f5553331329228fbd4123164423717a4a7539f6dfa1c3279a923b98fd681a6c739181900360200190a150565b620f424081106124a55760405162461bcd60e51b81526004018080602001828103825260418152602001806132916041913960600191505060405180910390fd5b6000811180156124b7575060745460c8115b6124f25760405162461bcd60e51b815260040180806020018281038252604181526020018061358a6041913960600191505060405180910390fd5b6000805b60745481101561261c57612508613091565b6074828154811061251557fe5b60009182526020918290206040805180820190915260029092020180546001600160a01b0390811680845260019092015493830193909352909250908616141561261357836125ea576074546000190182146125d15760748054600019810190811061257d57fe5b90600052602060002090600202016074838154811061259857fe5b60009182526020909120825460029092020180546001600160a01b0319166001600160a01b039092169190911781556001918201549101555b6074805460001901906125e490826130a8565b5061260e565b83607483815481106125f857fe5b9060005260206000209060020201600101819055505b600192505b506001016124f6565b50806126c1578161262d5750610a00565b604080518082019091526001600160a01b038481168252602082018481526074805460018101825560009190915292517f19a0b39aa25ac793b5f6e9a0534364cc0b3fd1ea9b651e79c7f50a59d48ef813600290940293840180546001600160a01b0319169190931617909155517f19a0b39aa25ac793b5f6e9a0534364cc0b3fd1ea9b651e79c7f50a59d48ef814909101555b6126c9612e90565b606c546000908152606e602052604090206076541580156126ea5750805415155b156127005780546126fa90612871565b60018201555b836001600160a01b0316612712612758565b6001600160a01b03167f67efa3716a83d716fc277019d454659a46cd1a11e7f4518b63ce585d5e6713e5856040518082815260200191505060405180910390a350505050565b6066546000906001600160a01b03163314612774575033610953565b610948612f1b565b6000828201838110156112d8576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b60006112d883836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250612f68565b600082612827575060006112db565b8282028284828161283457fe5b04146112d85760405162461bcd60e51b815260040180806020018281038252602181526020018061341e6021913960400191505060405180910390fd5b600080805b6074548110156128d6576128cc610c36856074848154811061289457fe5b60009182526020918290206040805180820190915260029092020180546001600160a01b031682526001015491810191909152612fcd565b9150600101612876565b5092915050565b303b1590565b604080517f19457468657265756d205369676e6564204d6573736167653a0a333200000000602080830191909152603c8083019490945282518083039094018452605c909101909152815191012090565b60008151604114612947575060006112db565b60208201516040830151606084015160001a7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a082111561298d57600093505050506112db565b8060ff16601b141580156129a557508060ff16601c14155b156129b657600093505050506112db565b6040805160008152602080820180845289905260ff8416828401526060820186905260808201859052915160019260a0808401939192601f1981019281900390910190855afa158015612a0d573d6000803e3d6000fd5b5050604051601f190151979650505050505050565b60006060612a3e60405180602001604052806000815250612fed565b915091509091565b604080516020810190915260008152600b9190910191565b604080519081018490526001600160a01b0385166060820152608080820184905260208201908152606b80546002600019610100600184161502019091160460a08401819052600093849388928a92899291829160c0019087908015612b055780601f10612ada57610100808354040283529160200191612b05565b820191906000526020600020905b815481529060010190602001808311612ae857829003601f168201915b5050955050505050506040516020818303038152906040528051906020012090506000612b31826128e3565b9050612b3d8185612934565b979650505050505050565b600054610100900460ff1680612b615750612b616128dd565b80612b6f575060005460ff16155b612baa5760405162461bcd60e51b815260040180806020018281038252602e815260200180613486602e913960400191505060405180910390fd5b600054610100900460ff16158015612bd5576000805460ff1961ff0019909116610100171660011790555b603380546001600160a01b0319166001600160a01b0384811691909117918290556040519116906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a38015610a00576000805461ff00191690555050565b606080829050606081516040519080825280601f01601f191660200182016040528015612c6d576020820181803883390190505b50905060005b8251811015612d4a576041838281518110612c8a57fe5b016020015160f81c60000b12801590612cba5750605a838281518110612cac57fe5b016020015160f81c60000b13155b15612d0757828181518110612ccb57fe5b602001015160f81c60f81b60f81c60200160f81b828281518110612ceb57fe5b60200101906001600160f81b031916908160001a905350612d42565b828181518110612d1357fe5b602001015160f81c60f81b828281518110612d2a57fe5b60200101906001600160f81b031916908160001a9053505b600101612c73565b509392505050565b50505050565b6001600160a01b038116612d9d5760405162461bcd60e51b81526004018080602001828103825260268152602001806131b56026913960400191505060405180910390fd5b6033546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3603380546001600160a01b0319166001600160a01b0392909216919091179055565b60008184841115612e885760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015612e4d578181015183820152602001612e35565b50505050905090810190601f168015612e7a5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6000805b607454811015612ed957612ecf8260748381548110612eaf57fe5b90600052602060002090600202016001015461277c90919063ffffffff16565b9150600101612e94565b50620f424081106109a65760405162461bcd60e51b815260040180806020018281038252602d8152602001806132f4602d913960400191505060405180910390fd5b600060606000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050503601516001600160a01b031692915050565b60008183612fb75760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315612e4d578181015183820152602001612e35565b506000838581612fc357fe5b0495945050505050565b60006112d8620f4240610c2a84602001518661281890919063ffffffff16565b600091565b60405180606001604052806000815260200160008152602001600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061305457805160ff1916838001178555613081565b82800160010185558215613081579182015b82811115613081578251825591602001919060010190613066565b5061308d9291506130d9565b5090565b604080518082019091526000808252602082015290565b8154818355818111156130d4576002028160020283600052602060002091820191016130d491906130f3565b505050565b61095391905b8082111561308d57600081556001016130df565b61095391905b8082111561308d5780546001600160a01b0319168155600060018201556002016130f956fe446973747269627574696f6e733a20746f6f206561726c7920746f20636c61696d207468697320726f756e64446973747269627574696f6e733a20746f6f206c61746520746f20636c61696d207468697320726f756e64446973747269627574696f6e733a206e657874537570706c792073686f756c64206265203e203020616e64203c3d204d41585f524f554e445f535550504c594f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373446973747269627574696f6e733a20737570706c79446563617950657263656e742073686f756c64206265203c2050455243454e545f505245434953494f4e446973747269627574696f6e733a20696e697469616c20737570706c792073686f756c64206265203e203020616e64203c3d204d41585f524f554e445f535550504c59446973747269627574696f6e733a2075736572206b61726d6120697320746f6f206c6f7720746f20636c61696d20706f696e7473446973747269627574696f6e733a20736861726564206f776e6572732070657263656e742073686f756c64206265203c2070657263656e74507265636973696f6e446973747269627574696f6e733a206b61726d612073686f756c64206265203e2030446973747269627574696f6e733a2063616e277420736861726520616c6c2031303025206f6620706f696e747347534e526563697069656e743a206e65772052656c617948756220697320746865207a65726f2061646472657373446973747269627574696f6e733a2047534e20617070726f7665722073686f756c64206e6f742062652030446973747269627574696f6e733a20696e697469616c206b61726d612073686f756c64206265206d6f7265207468616e2030446973747269627574696f6e733a20726f756e642073686f756c64206265203e206c617374526f756e6420616e64203c206c617374526f756e64202b204d41585f534b49505f524f554e4453446973747269627574696f6e733a207468697320726f756e6420686173206e6f206b61726d61536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77446973747269627574696f6e733a20746f74616c4b61726d612073686f756c64206265203e20304f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572436f6e747261637420696e7374616e63652068617320616c7265616479206265656e20696e697469616c697a656447534e526563697069656e743a206e65772052656c6179487562206973207468652063757272656e74206f6e65536861726564206f776e6572733a20416464726573736573206172726179206d7573742062652073616d65206c656e6774682061732070657263656e746167657347534e526563697069656e745369676e61747572653a2074727573746564207369676e657220697320746865207a65726f2061646472657373446973747269627574696f6e733a206e6f20706f696e747320746f20636c61696d20696e207468697320726f756e64446973747269627574696f6e733a20736861726564206f776e657273206c696d697420726561636865642c20736565204d41585f5348415245445f4f574e45525347534e526563697069656e743a2063616c6c6572206973206e6f742052656c6179487562446973747269627574696f6e733a207468697320726f756e647320706f696e74732061726520616c726561647920636c61696d6564446973747269627574696f6e733a206f6e6c79206b61726d6120736f757263652063616e20616476616e636520726f756e6473446973747269627574696f6e733a20636c61696d206973206e6f74207369676e656420627920746865206b61726d6120736f75726365446973747269627574696f6e733a206f776e65722073686f756c64206e6f742062652030446973747269627574696f6e733a206b61726d6120736f757263652073686f756c64206e6f742062652030a265627a7a723158202c0152dd687f17e8bbf4fa1ec51cfe9c0046d56716f80c8e8d2b318054f665c764736f6c63430005110032