Transaction Hash:
Block:
18485739 at Nov-02-2023 05:00:35 PM +UTC
Transaction Fee:
0.000762113061498888 ETH
$1.90
Gas Used:
23,694 Gas / 32.164812252 Gwei
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x3871A64B...9193C97A2 |
0.630136090736656817 Eth
Nonce: 224
|
0.629373977675157929 Eth
Nonce: 225
| 0.000762113061498888 | ||
0x95222290...5CC4BAfe5
Miner
| (beaverbuild) | 8.079235010098581102 Eth | 8.079237379498581102 Eth | 0.0000023694 |
Execution Trace
HVMTLForge.purchase( quantity=24 )
purchase[HVMTLForge (ln:189)]
ForgePurchasesNotEnabled[HVMTLForge (ln:190)]
transferFrom[HVMTLForge (ln:191)]
_msgSender[HVMTLForge (ln:192)]
_msgSender[HVMTLForge (ln:196)]
ForgePurchase[HVMTLForge (ln:197)]
_msgSender[HVMTLForge (ln:197)]
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) 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() { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { 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 { _transferOwnership(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"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @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); /** * @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 `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, 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 `from` to `to` 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 from, address to, uint256 amount ) external returns (bool); } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) 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.17; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "./lib/Operator.sol"; error ForgePurchasesNotEnabled(); contract HVMTLForge is Operator { bool public isForgePurchasesEnabled = false; address public immutable apeCoinContract; mapping(address => uint256) totalPurchased; event ForgePurchase( address indexed playerAddress, uint256 indexed amountApePurchased ); constructor(address _apeCoinContract, address _operator) Operator(_operator) { apeCoinContract = _apeCoinContract; } /** * @notice purchase gears with apecoin * @param quantity the amount of apecoin to purchase */ function purchase(uint256 quantity) external { if (!isForgePurchasesEnabled) revert ForgePurchasesNotEnabled(); IERC20(apeCoinContract).transferFrom( _msgSender(), address(this), quantity * 1 ether ); totalPurchased[_msgSender()] += quantity; emit ForgePurchase(_msgSender(), quantity); } /** * @notice get the total purchases for a player address * @param playerAddress the address of the player * @return uint256 total number of purchases */ function getPurchasesByPlayer(address playerAddress) external view returns (uint256) { return totalPurchased[playerAddress]; } // Operator functions /** * @notice set the state of forge purchases * @param isEnabled the state of forge purchases */ function setIsForgePurchasesEnabled(bool isEnabled) external onlyOperator { isForgePurchasesEnabled = isEnabled; } /** * @notice withdraw APE erc-20 tokens from the contract */ function withdrawAPE() external onlyOperator { uint256 balance = IERC20(apeCoinContract).balanceOf(address(this)); if (balance > 0) { IERC20(apeCoinContract).transfer(operator, balance); } } /** * @notice withdraw any erc-20 tokens from the contract * @param coinContract the erc-20 contract address */ function withdraw(address coinContract) external onlyOperator { uint256 balance = IERC20(coinContract).balanceOf(address(this)); if (balance > 0) { IERC20(coinContract).transfer(operator, balance); } } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.17; import "@openzeppelin/contracts/access/Ownable.sol"; error OnlyOperatorError(); error OperatorZeroAddressCheck(); contract Operator is Ownable { address public operator; event OperatorChanged(address operator); modifier onlyOperator() { if (operator != _msgSender()) revert OnlyOperatorError(); _; } constructor(address _operator) { if (_operator == address(0)) revert OperatorZeroAddressCheck(); operator = _operator; } /** * @notice change operator */ function setOperator(address _operator) external onlyOwner { if (_operator == address(0)) revert OperatorZeroAddressCheck(); operator = _operator; emit OperatorChanged(_operator); } }