Last active
September 9, 2020 08:58
-
-
Save radfish/60dc2464ab469389c70e657720cd684d to your computer and use it in GitHub Desktop.
Dissambled + reverse engineered code for Ethereum contract for TimeMiner coin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This is some notes after inspecting the code of the TimeMiner | |
contract on Ethereum, as reconstructed from disassembly and | |
from a fragment of code posted officially. The original contract | |
code was not open sourced by the authors as of this writing. | |
Coin: https://www.timeminer.site/ | |
Officially posted screenshot with partial code of the contract: | |
https://i.imgur.com/iFdbLaH.png | |
Contract disassebly: | |
https://etherscan.io/bytecode-decompiler?a=0xA54C67bd320Da4F9725a6f585b7635a0c09B122e | |
1. The contract maintains token count for each holding address | |
in '.balance' field. This field for a given address is updated | |
upon transacting from or to that address, but the update changes | |
the amount only every hour (accrual interval of "time" interest). | |
2. Withdrawal of tokens is protected by an '.allowance' field, | |
which sets the limit of how many tokens can be moved from the | |
owner address to a given receiver address. So, nobody except | |
the owner can authorize a transaction to move tokens to another | |
address. Seems solid. | |
3. Admin wallet can control the "time" interest amount at will | |
by changing info.supplydivision or info.supplymultiply values, | |
which seems possible to do at will through | |
'setPrizeFromNewAddress' method. | |
An attack by the admin would be to issue three transactions | |
back-to-back, near a hour boundary (when totalSupply is updated): | |
1. set supplymultiply to some very large value (like 1000000), | |
2. move tokens between some random address to another address | |
(this will update totalSupply to a huge value, and accrue | |
huge interest to the two addresses between which this move | |
takes place. | |
3. set supplymultiply back to 1 (so that others don't get | |
the inflated interest, nor notice anything). | |
Thus, the admin can create arbitrary amounts of tokens at will. | |
4. There is a mode in the contract that moves tokens without | |
interest acrual: see 'stableCoinSystem'. This mode can be | |
activated by the admin wallet at will. Seems mostly harmless, | |
at most it is a switch that disables accrual and can reenable | |
it again, without any loss to how much "interest" accumulated | |
during the time that accrual was disabled. The switch is basically | |
delaying settlement of "interest". | |
5. In the dissassembled code, paranthesis appear wrong: | |
(10^6 * block.timestamp - info.coinCreationTime / 3600 ... | |
should be | |
(10^6 * (block.timestamp - info.coinCreationTime) / 3600 ... | |
I am going to assume this is a bug in the disassembler, not the | |
contract, since there are also issues with order of argumennts | |
passed to 'log' statements, however I can't be sure; and since | |
the effect of such an error would make the contract behavior | |
completely wild, which is not the case, as far as I know. | |
The code fragment in the official screenshot happens to not | |
include the code that performs this calculation, so can't be sure | |
what code exactly is in the contract. | |
6. There is a whitelist feature that allows marking some token | |
holder addresses as 'whitelisted' however it is not used | |
anywhere in the code. Not clear what this is about. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# Panoramix v4 Oct 2019 | |
# Decompiled source of 0xA54C67bd320Da4F9725a6f585b7635a0c09B122e | |
# | |
# Let's make the world open source | |
# | |
const name = '' | |
const decimals = 6 | |
const symbol = '' | |
const TOKEN_PRECISION = 10^6 | |
def storage: | |
info.totalSupply is uint256 at storage 0 | |
balanceOf is mapping of struct at storage 1 | |
info.admin is addr at storage 2 | |
info.supplydivision is uint256 at storage 3 # info.supplydivision | |
info.supplymultiply is uint256 at storage 4 # info.supplymultiply | |
info.stableCoinSystem is uint8 at storage 5 # info.stableCoinSystem | |
info.coinWorkingTime is uint256 at storage 6 | |
info.coinCreationTime is uint256 at storage 7 | |
preSaleInfo.isPreSaleActive is uint8 at storage 8 offset 160 | |
preSaleInfo.admin is addr at storage 8 | |
preSaleInfo.preSaleDivide is uint256 at storage 9 | |
def isWhitelisted(address _address): # not payable | |
require calldata.size - 4 >= 32 | |
return bool(balanceOf[addr(_address)].whitelisted) | |
def balanceOf(address _owner): # not payable | |
require calldata.size - 4 >= 32 | |
return balanceOf[addr(_owner)].balance | |
def balanceOfTokenCirculation(addr _user): # not payable | |
require calldata.size - 4 >= 32 | |
return balanceOf[addr(_user)].appliedTokenCirculation | |
def allowance(address _owner, address _spender): # not payable | |
require calldata.size - 4 >= 64 | |
return balanceOf[addr(_owner)][2][addr(_spender)].allowance | |
# | |
# Regular functions | |
# | |
def _fallback() payable: # default function | |
revert | |
def infoStableSystem(): # not payable | |
return bool(info.stableCoinSystem), info.supplydivision, info.supplymultiply | |
def changePreSalePriceIfToHigh(uint256 _preSaleDivide): # not payable | |
require calldata.size - 4 >= 32 | |
require caller == info.admin | |
preSaleInfo.preSaleDivide = _preSaleDivide | |
def totalSupply(): # not payable | |
require info.supplydivision | |
return ((10^6 * block.timestamp - info.coinCreationTime / 3600 / info.supplydivision * info.supplymultiply) + initial_supply) | |
def setStableCoinSystem(bool _stableCoinSystem): # not payable | |
require calldata.size - 4 >= 32 | |
require caller == info.admin | |
info.stableCoinSystem = uint8(_stableCoinSystem) | |
def setPrizeFromNewAddress(uint256 _supplydivisiong, uint256 _supplymultiply): # not payable | |
require calldata.size - 4 >= 64 | |
require caller == info.admin | |
info.supplydivision = _supplydivision | |
info.supplymultiply = _supplymultiply | |
def whitelist(addr _user, bool _status): # not payable | |
require calldata.size - 4 >= 64 | |
require caller == info.admin | |
balanceOf[addr(_user)].whitelisted = uint8(_status) | |
log 0x5a25e09a: _status, _user | |
def approve(address _spender, uint256 _value): # not payable | |
require calldata.size - 4 >= 64 | |
balanceOf[caller][2][addr(_spender)].allowance = _value | |
log Approval( | |
address owner=_value, | |
address spender=caller, | |
uint256 value=_spender) | |
return 1 | |
def tokensToClaim(addr _param1): # not payable | |
require calldata.size - 4 >= 32 | |
require info.supplydivision | |
require balanceOf[addr(_param1)].appliedTokenCirculation | |
return ((initial_supply * 10^12 * | |
balanceOf[addr(_param1)].balance / balanceOf[addr(_param1)].appliedTokenCirculation)+ | |
(10^6 * block.timestamp - info.coinCreationTime / 3600 / | |
info.supplydivision * info.supplymultiply | |
* 10^12 * | |
* balanceOf[addr(_param1)].balance / balanceOf[addr(_param1)].appliedTokenCirculation) / 10^12) | |
def allInfoFor(addr _user): # not payable | |
require calldata.size - 4 >= 32 | |
require info.supplydivision | |
require info.supplydivision | |
require balanceOf[addr(_user)].appliedTokenCirculation | |
return (totalSupply(), | |
balanceofTokenCirculation(_user), | |
balanceOf(_user), | |
tokensToClaim(_user)) | |
def transfer(address _to, uint256 _tokens): # not payable | |
require calldata.size - 4 >= 64 | |
_transfer(caller, _to, _tokens) | |
return 1 | |
def preSaleFinished(): # not payable | |
require caller == info.admin | |
uint8(preSaleInfo.isPreSaleActive) = 0 | |
_transfer(addr(this.address), info.admin, balanceOf[this.address].balance) | |
def preSale(uint256 _tokens) payable: | |
require calldata.size - 4 >= 32 | |
require uint8(preSaleInfo.isPreSaleActive) | |
require preSaleInfo.preSaleDivide | |
require call.value > 5 * 10^18 * _tokens / preSaleInfo.preSaleDivide | |
_transfer(addr(this.address), caller, _tokens * TOKEN_PRECISION) | |
call addr(preSaleInfo.admin) with: | |
value call.value wei | |
gas 2300 * is_zero(value) wei | |
if not ext_call.success: | |
revert with ext_call.return_data[0 len return_data.size] | |
def transferFrom(address _from, address _to, uint256 _tokens): # not payable | |
require calldata.size - 4 >= 96 | |
require _tokens <= balanceOf[addr(_from)][2][caller].allowance | |
balanceOf[addr(_from)][2][caller].allowance -= _tokens | |
_transfer(_from, _to, _tokens) | |
return 1 | |
def _transfer(address _from, address _to, uint256 _tokens): | |
require balanceOf[addr(_from)].balance >= _tokens | |
require balanceOf[addr(_from)].balance >= 1 | |
if not info.stableCoinSystem: | |
balanceOf[addr(_from)].balance -= _tokens | |
balanceOf[_to].balance += _tokens | |
log Transfer( | |
address from=_tokens, | |
address to=_from, | |
uint256 value=_to) | |
else: | |
if not balanceOf[addr(_to)].balance: | |
balanceOf[addr(_to)].appliedTokenCirculation = info.totalSupply | |
if info.coinWorkingTime + 3600 < block.timestamp: | |
info.coinWorkingTime = block.timestamp | |
require info.supplydivision | |
info.totalSupply = (10^6 * block.timestamp - info.coinCreationTime / 3600 / info.supplydivision * info.supplymultiply) + initial_supply | |
require balanceOf[addr(_from)].appliedTokenCirculation | |
balanceOf[addr(_from)].balance = 10^12 * balanceOf[addr(_from)].balance / balanceOf[addr(_from)].appliedTokenCirculation * info.totalSupply / 10^12 | |
balanceOf[addr(_from)].appliedTokenCirculation = info.totalSupply | |
require balanceOf[_to].appliedTokenCirculation | |
balanceOf[addr(_to)].balance = 10^12 * balanceOf[_to].balance / balanceOf[_to].appliedTokenCirculation * info.totalSupply / 10^12 | |
balanceOf[addr(_to)].appliedTokenCirculation = info.totalSupply | |
require balanceOf[addr(_from)].appliedTokenCirculation | |
balanceOf[addr(_from)].balance -= 10^12 * _tokens / balanceOf[addr(_from)].appliedTokenCirculation * info.totalSupply / 10^12 | |
balanceOf[addr(_to)].balance += 10^12 * _tokens / balanceOf[addr(_from)].appliedTokenCirculation * info.totalSupply / 10^12 | |
log Transfer( | |
address from=(10^12 * _tokens / balanceOf[addr(_from)].appliedTokenCirculation * info.totalSupply / 10^12), | |
address to=_from, | |
uint256 value=_to) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment