Transaction Hash:
Block:
17802061 at Jul-29-2023 11:46:11 PM +UTC
Transaction Fee:
0.00165910945742944 ETH
$3.77
Gas Used:
82,730 Gas / 20.054508128 Gwei
Emitted Events:
161 |
EthXYToken.Transfer( _from=0x0000000000000000000000000000000000000000, _to=[Sender] 0x3a43f88bb667dad028bae4b25cf84d6c6468b87e, _value=100000000000000000000 )
|
162 |
Grid.PlotPurchase( buyer=[Sender] 0x3a43f88bb667dad028bae4b25cf84d6c6468b87e, coord=9212, price=80000000000000000, purchase_count=4 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x055D0F4a...83c955e93 | 494.649394876772481394 Eth | 494.669394876772481394 Eth | 0.02 | ||
0x388C818C...7ccB19297
Miner
| (Lido: Execution Layer Rewards Vault) | 121.567657184999493059 Eth | 121.567671249099493059 Eth | 0.0000140641 | |
0x3a43F88b...c6468b87e |
4.87606722166787198 Eth
Nonce: 205
|
4.79440811221044254 Eth
Nonce: 206
| 0.08165910945742944 | ||
0x91E6bb62...6701135FD | 1.8925830591308932 Eth | 1.9525830591308932 Eth | 0.06 | ||
0xB1e69773...5102F282D | |||||
0xCe96cc49...F188fd035 |
Execution Trace
ETH 0.08
Grid.buy_plot( coord=9212 )
- ETH 0.06
0x91e6bb626d2cb59f0f2fb1e03a8bfe76701135fd.CALL( )
- ETH 0.02
TokenRedeemer.CALL( )
-
EthXYToken.mint( _to=0x3a43F88bb667dAd028Bae4b25Cf84D6c6468b87e, _value=100000000000000000000 )
File 1 of 3: Grid
File 2 of 3: EthXYToken
File 3 of 3: TokenRedeemer
# @version ^0.3.9 # @title Grid from vyper.interfaces import ERC20Detailed struct Plot: owner: address purchase_count: uint256 grid: public(HashMap[uint256, Plot]) BASE_PRICE: constant(uint256) = 10 ** 16 plots_owned: public(HashMap[address, uint256]) treasury: public(address) token: public(immutable(address)) mint_per_plot: immutable(uint256) interface MintableToken: def mint(to: address, amount: uint256): nonpayable event PlotPurchase: buyer: indexed(address) coord: indexed(uint256) price: uint256 purchase_count: uint256 @external def __init__(treasury: address, _token: address): self.treasury = treasury token = _token mint_per_plot = 100 * 10 ** convert(ERC20Detailed(token).decimals(), uint256) @payable @external def buy_plot(coord: uint256): assert coord < 10000, "Invalid coord" # plot price increases by 2x each time it is purchased plot_price: uint256 = BASE_PRICE * (2 ** self.grid[coord].purchase_count) current_owner: address = self.grid[coord].owner assert msg.value >= plot_price, "Not enough ETH sent to buy plot" if msg.value > plot_price: send(msg.sender, msg.value - plot_price) # protocol takes 25% of the purchase price # 75% goes to the previous owner # if there is no previous owner, 100% goes to the protocol # the protocol is the owner of all plots at the start if current_owner != ZERO_ADDRESS: send(current_owner, plot_price * 3 / 4) if current_owner != msg.sender: self.plots_owned[current_owner] -= 1 self.plots_owned[msg.sender] += 1 self.grid[coord].owner = msg.sender else: self.grid[coord].owner = msg.sender self.plots_owned[msg.sender] += 1 send(self.treasury, self.balance) self.grid[coord].purchase_count += 1 # mint tokens for the buyer MintableToken(token).mint(msg.sender, mint_per_plot) log PlotPurchase(msg.sender, coord, plot_price, self.grid[coord].purchase_count) @view @external def get_all_owners(start_index: uint256 = 0, end_index: uint256 = 10000) -> DynArray[address, 10000]: owners: DynArray[address, 10000] = [] for coord in range(start_index, start_index + 10000): if coord >= end_index: break owners.append(self.grid[coord].owner) return owners @view @external def get_all_purchase_counts(start_index: uint256 = 0, end_index: uint256 = 10000) -> DynArray[uint256, 10000]: purchase_counts: DynArray[uint256, 10000] = [] for coord in range(start_index, start_index + 10000): if coord >= end_index: break purchase_counts.append(self.grid[coord].purchase_count) return purchase_counts @view @external def price(coord: uint256) -> uint256: return BASE_PRICE * (2 ** self.grid[coord].purchase_count)
File 2 of 3: EthXYToken
# @version ^0.3.9 # @title EthXYToken from vyper.interfaces import ERC20 implements: ERC20 event Transfer: _from: indexed(address) _to: indexed(address) _value: uint256 event Approval: _owner: indexed(address) _spender: indexed(address) _value: uint256 name: public(immutable(String[10])) symbol: public(immutable(String[3])) decimals: public(constant(uint256)) = 18 totalSupply: public(uint256) balanceOf: public(HashMap[address, uint256]) allowance: public(HashMap[address, HashMap[address, uint256]]) minter: public(address) burner: public(address) @external def __init__(): name = "EthXYToken" symbol = "EXY" self.minter = msg.sender self.burner = msg.sender @external def set_minter(minter: address): assert msg.sender == self.minter self.minter = minter @external def set_burner(burner: address): assert msg.sender == self.burner self.burner = burner @external def approve(spender: address, amount: uint256) -> bool: self.allowance[msg.sender][spender] = amount log Approval(msg.sender, spender, amount) return True @external def increaseAllowance(spender: address, addedValue: uint256) -> bool: self.allowance[msg.sender][spender] += addedValue log Approval(msg.sender, spender, self.allowance[msg.sender][spender]) return True @external def decreaseAllowance(spender: address, subtractedValue: uint256) -> bool: self.allowance[msg.sender][spender] -= subtractedValue log Approval(msg.sender, spender, self.allowance[msg.sender][spender]) return True @external def transfer(_to: address, _value: uint256) -> bool: self.balanceOf[msg.sender] -= _value self.balanceOf[_to] += _value log Transfer(msg.sender, _to, _value) return True @external def transferFrom(_from: address, _to: address, _value: uint256) -> bool: self.allowance[_from][msg.sender] -= _value self.balanceOf[_from] -= _value self.balanceOf[_to] += _value log Transfer(_from, _to, _value) return True @external def mint(_to: address, _value: uint256): assert msg.sender == self.minter self.balanceOf[_to] += _value self.totalSupply += _value log Transfer(ZERO_ADDRESS, _to, _value) @external def burn(_value: uint256): assert msg.sender == self.burner self.balanceOf[msg.sender] -= _value self.totalSupply -= _value log Transfer(msg.sender, ZERO_ADDRESS, _value) ################################################################ # EIP-2612 # ################################################################ nonces: public(HashMap[address, uint256]) _DOMAIN_TYPEHASH: constant(bytes32) = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)") _PERMIT_TYPE_HASH: constant(bytes32) = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)") @external def permit(owner: address, spender: address, amount: uint256, deadline: uint256, v: uint8, r: bytes32, s: bytes32): assert deadline >= block.timestamp nonce: uint256 = self.nonces[owner] self.nonces[owner] = nonce + 1 domain_separator: bytes32 = keccak256( _abi_encode(_DOMAIN_TYPEHASH, name, "1.0", chain.id, self) ) struct_hash: bytes32 = keccak256(_abi_encode(_PERMIT_TYPE_HASH, owner, spender, amount, nonce, deadline)) hash: bytes32 = keccak256( concat( b"\x19\x01", domain_separator, struct_hash ) ) assert owner == ecrecover(hash, v, r, s) self.nonces[owner] += 1 self.allowance[owner][spender] = amount log Approval(owner, spender, amount) @internal def _mint(_to: address, _value: uint256): self.balanceOf[_to] += _value self.totalSupply += _value log Transfer(ZERO_ADDRESS, _to, _value) @internal def _burn(_from: address, _value: uint256): assert self.balanceOf[_from] >= _value self.balanceOf[_from] -= _value self.totalSupply -= _value log Transfer(_from, ZERO_ADDRESS, _value)
File 3 of 3: TokenRedeemer
# @version ^0.3.9 # @title TokenRedeemer """ Token redemption contract where users can redeem their tokens for ETH. They get (accumulated ETH) / (total supply) * (token balance) """ from vyper.interfaces import ERC20 token: public(ERC20) interface MintableBurnableToken: def mint(amount: uint256): nonpayable def burn(amount: uint256): nonpayable event Redeemed: redeemer: indexed(address) amount_earned: uint256 amount_burned: uint256 @external def __init__(_token: ERC20): self.token = _token event Attempt: user: indexed(address) amount: uint256 allowance: uint256 balance: uint256 @external def redeem(amount: uint256 = 0): """ Redeem tokens for ETH. """ totalSupply: uint256 = self.token.totalSupply() amount_to_burn: uint256 = amount if amount_to_burn == 0: amount_to_burn = self.token.balanceOf(msg.sender) amount_earned: uint256 = (self.balance * amount_to_burn) / totalSupply assert self.token.allowance(msg.sender, self) >= amount_to_burn, "Not enough allowance" assert self.token.balanceOf(msg.sender) >= amount_to_burn, "Not enough balance" log Attempt(msg.sender, amount_to_burn, self.token.allowance(msg.sender, self), self.token.balanceOf(msg.sender)) self.token.transferFrom(msg.sender, self, amount_to_burn) send(msg.sender, amount_earned) MintableBurnableToken(self.token.address).burn(amount_to_burn) log Redeemed(msg.sender, amount_earned, amount_to_burn) @view @external def claimable_amount(user: address) -> uint256: """ Returns the amount of ETH that can be claimed. """ totalSupply: uint256 = self.token.totalSupply() return (self.balance * self.token.balanceOf(user)) / totalSupply @payable @external def __default__(): """ Fallback function to receive ETH. """ pass