Transaction Hash:
Block:
12051386 at Mar-16-2021 06:51:44 PM +UTC
Transaction Fee:
0.007316334 ETH
$17.71
Gas Used:
41,103 Gas / 178 Gwei
Emitted Events:
172 |
TweetMarket.Offer( tweetID=1364560733472579591, bidder=[Sender] 0xbd1c30fc75c38c40e2d839c69385c475ea5cee13, outbid=0x0379c1fddc7ac17de2c0fbf1db86063c1c5c3ad9, amount=390676960000000000, timestamp=1615920704 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x0379c1fd...c1c5c3Ad9 | 1.142286912 Eth | 1.482733972 Eth | 0.34044706 | ||
0x5A0b54D5...D3E029c4c
Miner
| (Spark Pool) | 159.765955541894946695 Eth | 159.773271875894946695 Eth | 0.007316334 | |
0xBD1C30Fc...5EA5CEe13 |
0.446799344561717 Eth
Nonce: 10
|
0.048806050561717 Eth
Nonce: 11
| 0.397993294 | ||
0xE14ab3Ee...D2dF22585 | 2,568.46918217 Eth | 2,568.51941207 Eth | 0.0502299 |
Execution Trace
ETH 0.39067696
TweetMarket.offer( tweetID=1364560733472579591 )
- ETH 0.34044706
0x0379c1fddc7ac17de2c0fbf1db86063c1c5c3ad9.CALL( )
offer[TweetMarket (ln:82)]
round[TweetMarket (ln:84)]
loadBid[TweetMarket (ln:86)]
round[TweetMarket (ln:89)]
_payOrStore[TweetMarket (ln:94)]
saveBid[TweetMarket (ln:101)]
Offer[TweetMarket (ln:103)]
// SPDX-License-Identifier: MIT pragma solidity ^0.6.6; contract TweetMarket { struct Bid { uint256 amount; address bidder; uint256 lockup; } uint256 private constant UINT_160_MAX = (2 ** 160) - 1; uint256 private constant UINT_64_MAX = (2 ** 64) - 1; uint256 private constant UINT_32_MAX = (2 ** 32) - 1; uint256 private constant SATOSHI = 10000000000; address private constant NULL_ADDRESS = address(0); uint256 private BID_MINIMUM = SATOSHI; uint256 private BID_MIN_INCREASE_PCT = 5; uint256 private BID_LOCKUP_PERIOD = 24 hours; uint256 private CLOSE_FEE_PCT = 5; mapping(uint256 => uint256) private highBids; mapping(address => uint256) private balances; address private admin; address[] private delegateHistory; mapping(address => bool) private delegates; bool private halted; event Offer ( uint256 indexed tweetID, address indexed bidder, address indexed outbid, uint256 amount, uint256 timestamp ); event Cancel ( uint256 indexed tweetID, address indexed bidder, uint256 amount, uint256 timestamp ); event Close ( uint256 indexed tweetID, address indexed seller, address indexed buyer, uint256 amount, uint256 timestamp ); event Withdraw ( address indexed account, uint256 amount, uint256 timestamp ); modifier haltable { require(halted == false, "Halted"); _; } modifier onlyAdmin { require(msg.sender == admin, "Only Admin"); _; } modifier onlyDelegates { require(delegates[msg.sender] || msg.sender == admin, "Only Delegates"); _; } constructor() public { admin = msg.sender; } /** * USER APIs */ function offer(uint256 tweetID) public payable haltable { address bidder = msg.sender; uint256 amount = round(msg.value); Bid memory bid = loadBid(tweetID); uint256 oldAmount = bid.amount; uint256 minAmount = oldAmount == 0 ? BID_MINIMUM : round(oldAmount + (oldAmount * BID_MIN_INCREASE_PCT / 100)); require(amount >= minAmount, "Offer too low"); address oldBidder = bid.bidder; if (oldBidder != NULL_ADDRESS) { _payOrStore(oldBidder, oldAmount); } bid.amount = amount; bid.bidder = bidder; bid.lockup = now + BID_LOCKUP_PERIOD; saveBid(tweetID, bid); emit Offer( tweetID, bidder, oldBidder, amount, now ); } function cancel(uint256 tweetID) public haltable { address bidder = msg.sender; Bid memory bid = loadBid(tweetID); require(bid.bidder == bidder, "Current high offer uses a different address"); require(bid.lockup <= now, "Minimum offer period not met"); uint256 bidAmount = bid.amount; bid.amount = 0; bid.bidder = NULL_ADDRESS; bid.lockup = 0; saveBid(tweetID, bid); _payOrFail(bidder, bidAmount); emit Cancel( tweetID, bidder, bidAmount, now ); } function withdraw() public haltable { address account = msg.sender; uint256 balance = balances[account] = 0; balances[account] = 0; _payOrFail(account, balance); emit Withdraw(account, balance, now); } /** * HELPERS */ function _payOrStore(address recipient, uint256 amount) internal returns (bool) { (bool success, ) = recipient.call{value: amount, gas: 0}(""); if (!success) { balances[recipient] += amount; } return success; } function _payOrFail(address recipient, uint256 amount) internal { (bool success, ) = recipient.call{value: amount}(""); require(success, "Transfer failed"); } function _close(uint256 tweetID, address seller, uint256 minPrice, uint256 percentFee) internal returns (uint256) { Bid memory bid = loadBid(tweetID); address buyer = bid.bidder; uint256 price = bid.amount; require(price >= minPrice, "Below expected price"); uint256 fee = price * percentFee / 100; uint256 net = price - fee; bid.amount = 0; bid.bidder = NULL_ADDRESS; bid.lockup = 0; saveBid(tweetID, bid); _payOrStore(seller, net); emit Close( tweetID, seller, buyer, price, now ); return fee; } function loadBid(uint256 tweetID) internal view returns (Bid memory bid) { uint256 data = highBids[tweetID]; bid.amount = (data & UINT_64_MAX) * SATOSHI; data = data >> 64; bid.bidder = address(data & UINT_160_MAX); data = data >> 160; bid.lockup = data & UINT_32_MAX; return bid; } function saveBid(uint256 tweetID, Bid memory bid) internal { uint256 data = 0; data |= bid.lockup; data = data << 160; data |= uint256(bid.bidder); data = data << 64; data |= bid.amount / SATOSHI; highBids[tweetID] = data; } function round(uint256 amount) internal pure returns (uint256) { return amount - (amount % SATOSHI); } /** * DELEGATE APIs */ function close(uint256 tweetID, address seller, uint256 price) public onlyDelegates haltable { uint256 fees = _close(tweetID, seller, price, CLOSE_FEE_PCT); if (fees > 0) { _payOrFail(admin, fees); } } function closeMany(uint256[] memory tweetIDs, address[] memory sellers, uint256[] memory prices) public onlyDelegates haltable { uint256 fees = 0; uint256 percentFee = CLOSE_FEE_PCT; uint256 numClosing = tweetIDs.length; require(numClosing == sellers.length && numClosing == prices.length, "Bad params"); for (uint i = 0; i < numClosing; i++) { fees += _close(tweetIDs[i], sellers[i], prices[i], percentFee); } if (fees > 0) { _payOrFail(admin, fees); } } /** * ADMIN APIs */ function setDelegate(address delegate, bool privileged) public onlyAdmin { delegates[delegate] = privileged; if (privileged) { delegateHistory.push(delegate); } } function changeAdmin(address newAdmin) public onlyAdmin { admin = newAdmin; } function setBidMinimum(uint256 minimum) public onlyAdmin { require(minimum >= SATOSHI, "Invalid minimum"); BID_MINIMUM = minimum; } function setBidMinimumIncrease(uint256 percent) public onlyAdmin { BID_MIN_INCREASE_PCT = percent; } function setBidLockupTime(uint256 duration) public onlyAdmin { require(duration <= 30 days, "Invalid duration"); BID_LOCKUP_PERIOD = duration; } function setCloseFee(uint256 percent) public onlyAdmin { require(percent <= 100, "Invalid percent"); CLOSE_FEE_PCT = percent; } function halt() public onlyAdmin { halted = true; _payOrFail(msg.sender, address(this).balance); } /** * READ APIs */ function getBid(uint256 tweetID) public view returns (uint256, address, uint256) { Bid memory bid = loadBid(tweetID); return ( bid.lockup, bid.bidder, bid.amount ); } function getBidMinimum() public view returns (uint256) { return BID_MINIMUM; } function getBidLockupTime() public view returns (uint256) { return BID_LOCKUP_PERIOD; } function getDelegateHistory() public view returns (address[] memory) { return delegateHistory; } function getCloseFeePercent() public view returns (uint256) { return CLOSE_FEE_PCT; } function isDelegate(address delegate) public view returns (bool) { return delegates[delegate]; } function getBalance(address owner) public view returns (uint256) { return balances[owner]; } function getAdmin() public view returns (address) { return admin; } }