Transaction Hash:
Block:
15964447 at Nov-13-2022 11:39:11 PM +UTC
Transaction Fee:
0.000811830229728428 ETH
$1.58
Gas Used:
54,454 Gas / 14.908550882 Gwei
Emitted Events:
| 398 |
MLAReward.Payout( to=[Sender] 0x24f94bca5b7c685248636d0004c93f2025f817fd, amount=84000000000000000 )
|
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
| 0x24F94BCA...025F817FD |
0.094647483178489932 Eth
Nonce: 27
|
0.177835652948761504 Eth
Nonce: 28
| 0.083188169770271572 | ||
| 0x398fbeAD...8C10fFB8D | 45.531 Eth | 45.447 Eth | 0.084 | ||
|
0x95222290...5CC4BAfe5
Miner
| (beaverbuild) | 95.007219022826409896 Eth | 95.007292174872929764 Eth | 0.000073152046519868 |
Execution Trace
MLAReward.payout( _amount=84000000000000000, _timestamp=1668382735, _signature=0x7CDCD2F55561415DE8859AE6F458785E4819EE02FB62EB2C094C0FA4579149127F696A4261EF91AD377C90B7F4306461B4DEC41119060E8B152F6C44B3CC31D11C )
-
Null: 0x000...001.34730c3b( ) - ETH 0.084
0x24f94bca5b7c685248636d0004c93f2025f817fd.CALL( )
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
// @author: olive
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// //
// 888b d888 888 d8888 8888888b. 888 //
// 8888b d8888 888 d88888 888 Y88b 888 //
// 88888b.d88888 888 d88P888 888 888 888 //
// 888Y88888P888 888 d88P 888 888 d88P .d88b. 888 888 888 8888b. 888d888 .d88888 //
// 888 Y888P 888 888 d88P 888 8888888P" d8P Y8b 888 888 888 "88b 888P" d88" 888 //
// 888 Y8P 888 888 d88P 888 888 T88b 88888888 888 888 888 .d888888 888 888 888 //
// 888 " 888 888 d8888888888 888 T88b Y8b. Y88b 888 d88P 888 888 888 Y88b 888 //
// 888 888 88888888 d88P 888 888 T88b "Y8888 "Y8888888P" "Y888888 888 "Y88888 //
// //
// //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
contract MLAReward is Ownable, ReentrancyGuard {
address private signerAddress;
mapping(address => bool) internal admins;
mapping(address => uint256) lastCheckPoint;
uint256 public delayBetweenPayout = 20 * 24 * 60 * 60;
mapping(address => uint256) payoutLimit;
uint256 public defaultLimit = 5 ether;
uint256 timeLimit = 90;
address public constant topAdminAddress = 0xe4Fb4CC7d6A568231B139553636bfa2A6dBcDb46;
event Deposited(uint256 amount);
event Payout(address to, uint256 amount);
constructor(address _signer) {
admins[msg.sender] = true;
signerAddress = _signer;
}
modifier onlyAdmin() {
require(admins[_msgSender()], 'MLAReward: Caller is not the admin');
_;
}
function addAdminRole(address _address) external onlyOwner {
admins[_address] = true;
}
function revokeAdminRole(address _address) external onlyOwner {
admins[_address] = false;
}
function deposit() public payable onlyAdmin {
require(msg.value > 0, "MLAReward: Not a valid amount");
emit Deposited(msg.value);
}
function withdrawSome(uint256 _amount) public onlyAdmin {
uint256 balance = address(this).balance;
require(balance > 0 && _amount <= balance);
_widthdraw(topAdminAddress, _amount);
}
function withdrawAll() public onlyAdmin {
uint256 balance = address(this).balance;
require(balance > 0);
_widthdraw(topAdminAddress, address(this).balance);
}
function _widthdraw(address _address, uint256 _amount) private {
(bool success, ) = _address.call{value: _amount}("");
require(success, "MLAReward: Transfer failed.");
}
function getLimit(address _claimer) public view returns (uint256) {
uint256 limit = payoutLimit[_claimer];
if (limit == 0) { limit = defaultLimit; }
return limit;
}
function setLimit(address[] memory _claimer, uint256[] memory _limit) public onlyOwner {
for(uint i = 0; i < _claimer.length; i ++){
payoutLimit[_claimer[i]] = _limit[i];
}
}
function payout(uint256 _amount, uint256 _timestamp, bytes memory _signature) external nonReentrant {
uint256 balance = address(this).balance;
require(_amount <= balance, "MLAReward: Not enough balance");
address wallet = _msgSender();
address signerOwner = signatureWallet(wallet, _amount, _timestamp, _signature);
require(signerOwner == signerAddress, "MLAReward: Invalid data provided");
require(_timestamp >= block.timestamp - timeLimit, "MLAReward: Out of time");
require(_timestamp >= lastCheckPoint[wallet] + delayBetweenPayout, "MLAReward: Invalid timestamp");
require(_amount < getLimit(wallet), "MLAReward: Amount exceeds limit");
lastCheckPoint[wallet] = block.timestamp;
_widthdraw(wallet, _amount);
emit Payout(wallet, _amount);
}
function signatureWallet(address _wallet, uint256 _amount, uint256 _timestamp, bytes memory _signature) public pure returns (address){
return ECDSA.recover(keccak256(abi.encode(_wallet, _amount, _timestamp)), _signature);
}
function setCheckPoint(address _claimer, uint256 _point) public onlyOwner {
require(_claimer != address(0), "MLAReward: Unknown address");
lastCheckPoint[_claimer] = _point;
}
function getCheckPoint(address _claimer) external view returns (uint256) {
return lastCheckPoint[_claimer];
}
function updateSignerAddress(address _signer) public onlyOwner {
signerAddress = _signer;
}
function updateTimeLimit(uint256 _timeLimit) public onlyOwner {
timeLimit = _timeLimit;
}
function updateDefaultLimit(uint256 _limit) public onlyOwner {
defaultLimit = _limit;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.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 {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
} else if (error == RecoverError.InvalidSignatureV) {
revert("ECDSA: invalid signature 'v' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. 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.
*
* 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.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
// Check the signature length
// - case 65: r,s,v signature (standard)
// - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else if (signature.length == 64) {
bytes32 r;
bytes32 vs;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
assembly {
r := mload(add(signature, 0x20))
vs := mload(add(signature, 0x40))
}
return tryRecover(hash, r, vs);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @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.
*
* 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) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address, RecoverError) {
bytes32 s;
uint8 v;
assembly {
s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
v := add(shr(255, vs), 27)
}
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError) {
// 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 (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): 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), RecoverError.InvalidSignatureS);
}
if (v != 27 && v != 28) {
return (address(0), RecoverError.InvalidSignatureV);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* 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:\
32", hash));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.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 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.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @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.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_setOwner(_msgSender());
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the 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 virtual onlyOwner {
_setOwner(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 virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_setOwner(newOwner);
}
function _setOwner(address newOwner) private {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}