diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..d8d9f879 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +secret.py +__pycache__ +*/__pycache__ +cache_code +cache_pabi +cache_pan +cache +cache_pan +.DS_Store +supplement.db +supp2.db +cache_* \ No newline at end of file diff --git a/README.md b/README.md index e69de29b..cabe85d4 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,76 @@ +## Installation: + +``` +git pull .... +pip3 install -r requirements.txt +``` + +## Running: + +You *need* **python3.8** to run Panoramix. Yes, there was no way around it. + +``` +python3.8 panoramix.py address [func_name] [--verbose|--silent] +``` + +e.g. + +``` +python3.8 panoramix.py 0x06012c8cf97bead5deae237070f9587f8e7a266d +``` + +Output goes to two places: +- console +- cache_pan/ directory - .pan, .json, .asm files + +### Optional parameters: + +func_name -- name of the function to decompile (note: storage names won't be discovered in this mode) +--verbose -- prints out the assembly and stack as well as regular functions, a good way to try it out is +by running 'python panoramix.py kitties pause --verbose' - it's a simple function + +There are more parameters as well. You can find what they do in panoramix.py. + +### Address shortcuts +Some contract addresses, which are good for testing, have shortcuts, e.g. you can run +'python panoramix.py kitties' instead of 'python3 panoramix.py 0x06012c8cf97bead5deae237070f9587f8e7a266d'. + +See panoramix.py for the list of shortcuts, feel free to add your own. + +## Directories & Files + +### Code: +- core - modules for doing abstract/symbolic operations +- pano - the proper decompiler +- utils - various helper modules +- tilde - the library for handling pattern matching in python3.8 + +### Data: +- cache_code - cached bytecodes +- cache_pan - cached decompilation outputs +- cache_pabi - cached auto-generated p-abi files +- supplement.db - sqlite3 database of function definitions +- supp2.db - a lightweight variant o the above + +Cache directories are split into subdirectories, so the filesystem doesn't break down with large amounts +of cached contracts (important when running bulk_decompile on all 2.2M contracts on the chain) + +All of the above generated after the first run. + +## Utilities +bulk_decompile.py - batch-decompiles contracts, with multi-processing support +bulk_compare.py - decompiles a set of test contracts, fetches the current decompiled from Eveem, and prepares two files, so you can diff them and see what changes were made + +## Why **python3.8** and **Tilde** +Panoramix uses a ton of pattern matching operations, and python doesn't support those as a language. + +There are some pattern-matching libraries for older python versions, but none of them seemed good enough. +Because of that, I built Tilde, which is a language extension adding a new operator. + +Tilde replaces '~' pattern matching operator with a series of ':=' operators underneath. +Because of that, python3.8 is a must. + +Believe me, I spent a lot of time looking for some other way to make pattern matching readable. +Nothing was close to this good. + +But if you manage to figure out a way to do it without Tilde (and maintain readability), I'll gladly accept a PR :) \ No newline at end of file diff --git a/benchmark/0x06012c8cf97BEaD5deAe237070F9587f8E7A266d.pan b/benchmark/0x06012c8cf97BEaD5deAe237070F9587f8E7A266d.pan new file mode 100644 index 00000000..74697f77 --- /dev/null +++ b/benchmark/0x06012c8cf97BEaD5deAe237070F9587f8E7A266d.pan @@ -0,0 +1,673 @@ +# +# Panoramix 4 Oct 2019 +# Decompiled source of 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d +# +# Let's make the world open source +#  + +const name = 'CryptoKitties' +const symbol = 'CK' +const GEN0_STARTING_PRICE = 10^16 +const GEN0_AUCTION_DURATION = (24 * 3600) +const GEN0_CREATION_LIMIT = 45000 +const PROMO_CREATION_LIMIT = 5000 + +def storage: + ceoAddress is addr at storage 0 + cfoAddress is addr at storage 1 + cooAddress is addr at storage 2 + paused is uint8 at storage 2 offset 160 + cooldowns is array of uint32 at storage 3 + secondsPerBlock is uint256 at storage 5 + kitty is array of struct at storage 6 + kittyIndexToOwner is mapping of addr at storage 7 + balanceOf is mapping of uint256 at storage 8 + kittyIndexToApproved is mapping of addr at storage 9 + sireAllowedTo is mapping of addr at storage 10 + saleAuctionAddress is addr at storage 11 + siringAuctionAddress is addr at storage 12 + erc721MetadataAddress is addr at storage 13 + autoBirthFee is uint256 at storage 14 + pregnantKitties is uint256 at storage 15 + geneScienceAddress is addr at storage 16 + promoCreatedCount is uint256 at storage 17 + gen0CreatedCount is uint256 at storage 18 + newContractAddress is addr at storage 19 + +def cfoAddress(): # not payable + return cfoAddress + +def promoCreatedCount(): # not payable + return promoCreatedCount + +def ceoAddress(): # not payable + return ceoAddress + +def pregnantKitties(): # not payable + return pregnantKitties + +def isPregnant(uint256 _horseId): # not payable + require _horseId > 0 + require _horseId < kitty.length + return bool(kitty[_horseId].field_448) + +def siringAuction(): # not payable + return siringAuctionAddress + +def sireAllowedToAddress(uint256 _param1): # not payable + return sireAllowedTo[_param1] + +def kittyIndexToApproved(uint256 _param1): # not payable + return kittyIndexToApproved[_param1] + +def paused(): # not payable + return bool(paused) + +def ownerOf(uint256 _tokenId): # not payable + require kittyIndexToOwner[_tokenId] + return kittyIndexToOwner[_tokenId] + +def newContractAddress(): # not payable + return newContractAddress + +def balanceOf(address _tokenOwner): # not payable + return balanceOf[addr(_tokenOwner)] + +def secondsPerBlock(): # not payable + return secondsPerBlock + +def cooldowns(uint256 _param1): # not payable + require _param1 < 14 + return cooldowns[uint8(_param1)] + +def kittyIndexToOwner(uint256 _param1): # not payable + return kittyIndexToOwner[_param1] + +def cooAddress(): # not payable + return cooAddress + +def autoBirthFee(): # not payable + return autoBirthFee + +def erc721Metadata(): # not payable + return erc721MetadataAddress + +def saleAuction(): # not payable + return saleAuctionAddress + +def getKitty(uint256 _tokenId): # not payable + require _tokenId < kitty.length + return kitty[_tokenId].field_448 != 0, + kitty[_tokenId].field_320 <= block.number, + kitty[_tokenId].field_256, + kitty[_tokenId].field_256, + kitty[_tokenId].field_448, + kitty[_tokenId].field_256, + kitty[_tokenId].field_256, + kitty[_tokenId].field_256, + kitty[_tokenId].field_256, + kitty[_tokenId].field_0 + +def gen0CreatedCount(): # not payable + return gen0CreatedCount + +def geneScience(): # not payable + return geneScienceAddress + +# +# Regular functions +# + +def totalSupply(): # not payable + return (kitty.length - 1) + +def setAutoBirthFee(uint256 _val): # not payable + require cooAddress == caller + autoBirthFee = _val + +def _fallback() payable: # default function + if saleAuctionAddress != caller: + require siringAuctionAddress == caller + +def setCEO(address _newCEO): # not payable + require ceoAddress == caller + require _newCEO + ceoAddress = _newCEO + +def setCOO(address _newCoo): # not payable + require ceoAddress == caller + require _newCoo + cooAddress = _newCoo + +def setCFO(address _newCfo): # not payable + require ceoAddress == caller + require _newCfo + cfoAddress = _newCfo + +def setMetadataAddress(address _contractAddress): # not payable + require ceoAddress == caller + erc721MetadataAddress = _contractAddress + +def pause(): # not payable + if cooAddress != caller: + if ceoAddress != caller: + require cfoAddress == caller + require not paused + paused = 1 + +def setNewAddress(address _v2Address): # not payable + require ceoAddress == caller + require paused + newContractAddress = _v2Address + log ContractUpgrade(address newContract=_v2Address) + +def approveSiring(address _addr, uint256 _sireId): # not payable + require not paused + require kittyIndexToOwner[_sireId] == caller + sireAllowedTo[_sireId] = _addr + +def setSecondsPerBlock(uint256 _secs): # not payable + if cooAddress != caller: + if ceoAddress != caller: + require cfoAddress == caller + require _secs < cooldowns.length + secondsPerBlock = _secs + +def unpause(): # not payable + require ceoAddress == caller + require paused + require saleAuctionAddress + require siringAuctionAddress + require geneScienceAddress + require not newContractAddress + require ceoAddress == caller + require paused + paused = 0 + +def setGeneScienceAddress(address _address): # not payable + require ceoAddress == caller + require ext_code.size(_address) + call _address.isGeneScience() with: + gas gas_remaining - 710 wei + require ext_call.success + require ext_call.return_data[0] + geneScienceAddress = _address + +def setSaleAuctionAddress(address _address): # not payable + require ceoAddress == caller + require ext_code.size(_address) + call _address.isSaleClockAuction() with: + gas gas_remaining - 710 wei + require ext_call.success + require ext_call.return_data[0] + saleAuctionAddress = _address + +def setSiringAuctionAddress(address _address): # not payable + require ceoAddress == caller + require ext_code.size(_address) + call _address.isSiringClockAuction() with: + gas gas_remaining - 710 wei + require ext_call.success + require ext_call.return_data[0] + siringAuctionAddress = _address + +def withdrawBalance(): # not payable + require cfoAddress == caller + if eth.balance(this.address) > autoBirthFee + (pregnantKitties * autoBirthFee): + call cfoAddress with: + value eth.balance(this.address) - autoBirthFee - (pregnantKitties * autoBirthFee) wei + gas 2300 * is_zero(value) wei + +def approve(address _spender, uint256 _tokens): # not payable + require not paused + require kittyIndexToOwner[_tokens] == caller + kittyIndexToApproved[_tokens] = _spender + log Approval( +  address owner=caller, +  address spender=addr(_spender), +  uint256 value=_tokens) + +def isReadyToBreed(uint256 _tokenId): # not payable + require _tokenId > 0 + require _tokenId < kitty.length + if kitty[_tokenId].field_448: + return not bool(kitty[_tokenId].field_256) + return kitty[_tokenId].field_256 <= uint64(block.number) + +def withdrawAuctionBalances(): # not payable + if cooAddress != caller: + if ceoAddress != caller: + require cfoAddress == caller + require ext_code.size(saleAuctionAddress) + call saleAuctionAddress.withdrawBalance() with: + gas gas_remaining - 710 wei + require ext_call.success + require ext_code.size(siringAuctionAddress) + call siringAuctionAddress.withdrawBalance() with: + gas gas_remaining - 710 wei + require ext_call.success + +def supportsInterface(bytes4 _interfaceId): # not payable + if Mask(32, 224, _interfaceId) == Mask(32, 224, sha3('supportsInterface(bytes4)')): + return True + return (Mask(32, 224, _interfaceId) == Mask(32, 224, sha3('name()') xor sha3('symbol()') xor sha3('totalSupply()') xor sha3('balanceOf(address)') xor sha3('ownerOf(uint256)') xor sha3('approve(address,uint256)') xor sha3('transfer(address,uint256)') xor sha3('transferFrom(address,address,uin', 't256)') xor sha3('tokensOfOwner(address)') xor sha3('tokenMetadata(uint256,string)'))) + +def transfer(address _to, uint256 _tokens): # not payable + require not paused + require _to + require _to != this.address + require saleAuctionAddress != _to + require siringAuctionAddress != _to + require kittyIndexToOwner[_tokens] == caller + balanceOf[addr(_to)]++ + kittyIndexToOwner[_tokens] = _to + if caller: + balanceOf[caller]-- + sireAllowedTo[_tokens] = 0 + kittyIndexToApproved[_tokens] = 0 + log Transfer( +  address from=caller, +  address to=addr(_to), +  uint256 value=_tokens) + +def transferFrom(address _from, address _to, uint256 _tokens): # not payable + require not paused + require _to + require _to != this.address + require kittyIndexToApproved[_tokens] == caller + require kittyIndexToOwner[_tokens] == _from + balanceOf[addr(_to)]++ + kittyIndexToOwner[_tokens] = _to + if _from: + balanceOf[addr(_from)]-- + sireAllowedTo[_tokens] = 0 + kittyIndexToApproved[_tokens] = 0 + log Transfer( +  address from=addr(_from), +  address to=addr(_to), +  uint256 value=_tokens) + +def createSaleAuction(uint256 _artworkId, uint256 _startingPrice, uint256 _endingPrice, uint256 _duration): # not payable + require not paused + require kittyIndexToOwner[_artworkId] == caller + require _artworkId > 0 + require _artworkId < kitty.length + require not kitty[_artworkId].field_448 + kittyIndexToApproved[_artworkId] = saleAuctionAddress + require ext_code.size(saleAuctionAddress) + call saleAuctionAddress.createAuction(uint256 tokenId, uint256 startingPrice, uint256 endingPrice, uint256 duration, address seller) with: + gas gas_remaining - 710 wei + args 0, uint32(_artworkId), _startingPrice, _endingPrice, _duration, caller + require ext_call.success + +def createSiringAuction(uint256 _tokenId, uint256 _startingPrice, uint256 _endingPrice, uint256 _duration): # not payable + require not paused + require kittyIndexToOwner[_tokenId] == caller + require _tokenId > 0 + require _tokenId < kitty.length + require not kitty[_tokenId].field_448 + require kitty[_tokenId].field_320 <= uint64(block.number) + kittyIndexToApproved[_tokenId] = siringAuctionAddress + require ext_code.size(siringAuctionAddress) + call siringAuctionAddress.createAuction(uint256 tokenId, uint256 startingPrice, uint256 endingPrice, uint256 duration, address seller) with: + gas gas_remaining - 710 wei + args _tokenId, _startingPrice, _endingPrice, _duration, caller + require ext_call.success + +def tokenMetadata(uint256 _tokenId, string _preferredTransport): # not payable + require erc721MetadataAddress + mem[356 len _preferredTransport.length] = _preferredTransport[all] + require ext_code.size(erc721MetadataAddress) + call erc721MetadataAddress.0xcb4799f2 with: + gas gas_remaining - 710 wei + args _tokenId, Array(len=_preferredTransport.length, data=_preferredTransport[all]) + mem[256 len 160] = ext_call.return_data[0 len 160] + require ext_call.success + mem[416] = 0 + mem[448] = 0 + _msize = max(124, _preferredTransport.length) + mem[_msize + 356] = ext_call.return_data[128] + mem[(_msize + 356) + 32 len floor32(ext_call.return_data[128])] = mem[256 len floor32(ext_call.return_data[128])] + mem[(_msize + 356) + floor32(ext_call.return_data[128]) + -(ext_call.return_data[128] % 32) + 64 len ext_call.return_data[128] % 32] = mem[floor32(ext_call.return_data[128]) + -(ext_call.return_data[128] % 32) + 288 len ext_call.return_data[128] % 32] + return Array(len=ext_call.return_data[128], data=mem[(_msize + 356) + 32 len ext_call.return_data[128]]) + +def tokensOfOwner(address _owner): # not payable + if not balanceOf[addr(_owner)]: + return '' + idx = 1 + s = 0 + while idx <= kitty.length - 1: + mem[0] = idx + mem[32] = 7 + if kittyIndexToOwner[idx] != _owner: + idx = idx + 1 + s = s + continue  + require s < balanceOf[addr(_owner)] + mem[(32 * s) + 192] = idx + idx = idx + 1 + s = s + 1 + continue  + mem[(32 * balanceOf[addr(_owner)]) + 256 len floor32(balanceOf[addr(_owner)])] = mem[192 len floor32(balanceOf[addr(_owner)])] + return Array(len=balanceOf[addr(_owner)], data=mem[192 len floor32(balanceOf[addr(_owner)])], mem[(32 * balanceOf[addr(_owner)]) + floor32(balanceOf[addr(_owner)]) + 256 len (32 * balanceOf[addr(_owner)]) - floor32(balanceOf[addr(_owner)])]), + +def canBreedWith(uint256 _matronId, uint256 _sireId): # not payable + require _matronId > 0 + require _sireId > 0 + require _matronId < kitty.length + require _sireId < kitty.length + if _matronId == _sireId: + return 0 + if _sireId == kitty[_matronId].field_384: + return 0 + if _sireId == kitty[_matronId].field_416: + return 0 + if _matronId == kitty[_sireId].field_384: + return 0 + if _matronId == kitty[_sireId].field_416: + return 0 + if kitty[_sireId].field_384: + if kitty[_matronId].field_384: + if kitty[_sireId].field_384 == kitty[_matronId].field_384: + return 0 + if kitty[_matronId].field_416 == kitty[_sireId].field_384: + return 0 + if kitty[_matronId].field_384 == kitty[_sireId].field_416: + return 0 + if kitty[_sireId].field_416 == kitty[_matronId].field_416: + return 0 + if kittyIndexToOwner[_matronId] == kittyIndexToOwner[_sireId]: + return True + return (kittyIndexToOwner[_matronId] == sireAllowedTo[_sireId]) + +def createPromoKitty(uint256 _genes, address _owner): # not payable + require cooAddress == caller + require promoCreatedCount < 5000 + promoCreatedCount++ + kitty.length++ + if not kitty.length <= kitty.length + 1: + idx = 2 * kitty.length + 1 + while 2 * kitty.length > idx: + kitty[idx].field_0 = 0 + kitty[idx].field_256 = 0 + idx = idx + 2 + continue  + kitty[kitty.length].field_0 = _genes + kitty[kitty.length].field_256 = uint64(block.timestamp) + kitty[kitty.length].field_320 = 0 + kitty[kitty.length].field_512 = 0 + kitty[kitty.length].field_384 = 0 + kitty[kitty.length].field_512 = 0 + kitty[kitty.length].field_416 = 0 + kitty[kitty.length].field_512 = 0 + kitty[kitty.length].field_448 = 0 + kitty[kitty.length].field_512 = 0 + kitty[kitty.length].field_480 = 0 + kitty[kitty.length].field_512 = 0 + kitty[kitty.length].field_256 = kitty[kitty.length].field_256 + require kitty.length == uint32(kitty.length) + if _owner: + log Birth( +  address owner=addr(_owner), +  uint256 clownId=kitty.length, +  uint256 matronId=0, +  uint256 sireId=0, +  uint256 genes=_genes) + balanceOf[addr(_owner)]++ + kittyIndexToOwner[stor6.length] = _owner + log Transfer( +  address from=0, +  address to=addr(_owner), +  uint256 value=kitty.length) + else: + log Birth( +  address owner=cooAddress, +  uint256 clownId=kitty.length, +  uint256 matronId=0, +  uint256 sireId=0, +  uint256 genes=_genes) + balanceOf[stor2]++ + kittyIndexToOwner[stor6.length] = cooAddress + log Transfer( +  address from=0, +  address to=cooAddress, +  uint256 value=kitty.length) + +def createGen0Auction(uint256 _pandaId): # not payable + require cooAddress == caller + require gen0CreatedCount < 45000 + kitty.length++ + if not kitty.length <= kitty.length + 1: + idx = 2 * kitty.length + 1 + while 2 * kitty.length > idx: + kitty[idx].field_0 = 0 + kitty[idx].field_256 = 0 + idx = idx + 2 + continue  + kitty[kitty.length].field_0 = _pandaId + kitty[kitty.length].field_256 = uint64(block.timestamp) + kitty[kitty.length].field_320 = 0 + kitty[kitty.length].field_512 = 0 + kitty[kitty.length].field_384 = 0 + kitty[kitty.length].field_512 = 0 + kitty[kitty.length].field_416 = 0 + kitty[kitty.length].field_512 = 0 + kitty[kitty.length].field_448 = 0 + kitty[kitty.length].field_512 = 0 + kitty[kitty.length].field_480 = 0 + kitty[kitty.length].field_512 = 0 + kitty[kitty.length].field_256 = kitty[kitty.length].field_256 + require kitty.length == uint32(kitty.length) + log Birth( +  address owner=addr(this.address), +  uint256 clownId=kitty.length, +  uint256 matronId=0, +  uint256 sireId=0, +  uint256 genes=_pandaId) + balanceOf[addr(this.address)]++ + kittyIndexToOwner[stor6.length] = this.address + log Transfer( +  address from=0, +  address to=addr(this.address), +  uint256 value=kitty.length) + kittyIndexToApproved[stor6.length] = saleAuctionAddress + require ext_code.size(saleAuctionAddress) + call saleAuctionAddress.averageGen0SalePrice() with: + gas gas_remaining - 710 wei + require ext_call.success + require ext_call.return_data[0] == ext_call.return_data[16 len 16] + require ext_code.size(saleAuctionAddress) + if ext_call.return_data[0] + (ext_call.return_data[0] / 2) >= 10^16: + call saleAuctionAddress.createAuction(uint256 tokenId, uint256 startingPrice, uint256 endingPrice, uint256 duration, address seller) with: + gas gas_remaining - 710 wei + args kitty.length, ext_call.return_data[0] + (ext_call.return_data[0] / 2), 0, 24 * 3600, this.address + else: + call saleAuctionAddress.createAuction(uint256 tokenId, uint256 startingPrice, uint256 endingPrice, uint256 duration, address seller) with: + gas gas_remaining - 710 wei + args kitty.length, 10^16, 0, 24 * 3600, this.address + require ext_call.success + gen0CreatedCount++ + +def breedWithAuto(uint256 _matronId, uint256 _sireId) payable: + require not paused + require call.value >= autoBirthFee + require kittyIndexToOwner[_matronId] == caller + if kittyIndexToOwner[_matronId] != kittyIndexToOwner[_sireId]: + require kittyIndexToOwner[_matronId] == sireAllowedTo[_sireId] + require _matronId < kitty.length + require not kitty[_matronId].field_448 + require kitty[_matronId].field_320 <= uint64(block.number) + require _sireId < kitty.length + require not kitty[_sireId].field_448 + require kitty[_sireId].field_320 <= uint64(block.number) + require _matronId != _sireId + require _sireId != kitty[_matronId].field_384 + require _sireId != kitty[_matronId].field_416 + require _matronId != kitty[_sireId].field_384 + require _matronId != kitty[_sireId].field_416 + if kitty[_sireId].field_384: + if kitty[_matronId].field_384: + require kitty[_sireId].field_384 != kitty[_matronId].field_384 + require kitty[_matronId].field_416 != kitty[_sireId].field_384 + require kitty[_matronId].field_384 != kitty[_sireId].field_416 + require kitty[_sireId].field_416 != kitty[_matronId].field_416 + require _sireId < kitty.length + require _matronId < kitty.length + kitty[_matronId].field_448 = uint32(_sireId) + require kitty[_sireId].field_480 < 14 + require secondsPerBlock + kitty[_sireId].field_320 = uint64((cooldowns[stor6[_sireId].field_480] / secondsPerBlock) + block.number) + if kitty[_sireId].field_480 < 13: + kitty[_sireId].field_480 = uint16(kitty[_sireId].field_480 + 1) + require kitty[_matronId].field_480 < 14 + require secondsPerBlock + kitty[_matronId].field_320 = uint64((cooldowns[stor6[_matronId].field_480] / secondsPerBlock) + block.number) + if kitty[_matronId].field_480 < 13: + kitty[_matronId].field_480 = uint16(kitty[_matronId].field_480 + 1) + sireAllowedTo[_matronId] = 0 + sireAllowedTo[_sireId] = 0 + pregnantKitties++ + log Pregnant( +  address owner=kittyIndexToOwner[_matronId], +  uint256 mareId=_matronId, +  uint256 stallionId=_sireId, +  uint256 unproductiveEndBlock=kitty[_matronId].field_320) + +def bidOnSiringAuction(uint256 _sireId, uint256 _matronId) payable: + require not paused + require kittyIndexToOwner[_matronId] == caller + require _matronId > 0 + require _matronId < kitty.length + require not kitty[_matronId].field_448 + require kitty[_matronId].field_320 <= uint64(block.number) + require _matronId < kitty.length + require _sireId < kitty.length + require _matronId != _sireId + require _sireId != kitty[_matronId].field_384 + require _sireId != kitty[_matronId].field_416 + require _matronId != kitty[_sireId].field_384 + require _matronId != kitty[_sireId].field_416 + if kitty[_sireId].field_384: + if kitty[_matronId].field_384: + require kitty[_sireId].field_384 != kitty[_matronId].field_384 + require kitty[_matronId].field_416 != kitty[_sireId].field_384 + require kitty[_matronId].field_384 != kitty[_sireId].field_416 + require kitty[_sireId].field_416 != kitty[_matronId].field_416 + require ext_code.size(siringAuctionAddress) + call siringAuctionAddress.getCurrentPrice(uint256 tokenId) with: + gas gas_remaining - 710 wei + args _sireId + require ext_call.success + require call.value >= ext_call.return_data[0] + autoBirthFee + require ext_code.size(siringAuctionAddress) + call siringAuctionAddress.bid(uint256 tokenId) with: + value call.value - autoBirthFee wei + gas gas_remaining - 9710 wei + args _sireId + require ext_call.success + require uint32(_sireId) < kitty.length + require uint32(_matronId) < kitty.length + kitty[2 * uint32(_matronId)].field_448 = uint32(_sireId) + require kitty[2 * uint32(_sireId)].field_480 < 14 + require secondsPerBlock + kitty[2 * uint32(_sireId)].field_320 = uint64((cooldowns[stor6[2 * uint32(_sireId)].field_480] / secondsPerBlock) + block.number) + if kitty[2 * uint32(_sireId)].field_480 < 13: + kitty[2 * uint32(_sireId)].field_480 = uint16(kitty[2 * uint32(_sireId)].field_480 + 1) + require kitty[2 * uint32(_matronId)].field_480 < 14 + require secondsPerBlock + kitty[2 * uint32(_matronId)].field_320 = uint64((cooldowns[stor6[2 * uint32(_matronId)].field_480] / secondsPerBlock) + block.number) + if kitty[2 * uint32(_matronId)].field_480 < 13: + kitty[2 * uint32(_matronId)].field_480 = uint16(kitty[2 * uint32(_matronId)].field_480 + 1) + sireAllowedTo[_matronId << 224] = 0 + sireAllowedTo[uint32(_sireId)] = 0 + pregnantKitties++ + log Pregnant( +  address owner=kittyIndexToOwner[_matronId << 224], +  uint256 mareId=_matronId << 224, +  uint256 stallionId=_sireId << 224, +  uint256 unproductiveEndBlock=kitty[2 * uint32(_matronId)].field_320) + +def giveBirth(uint256 _matronId): # not payable + require not paused + require _matronId < kitty.length + require kitty[_matronId].field_256 + require kitty[_matronId].field_448 + require kitty[_matronId].field_320 <= uint64(block.number) + require kitty[_matronId].field_448 < kitty.length + require ext_code.size(geneScienceAddress) + call geneScienceAddress.mixGenes(uint256 genes1, uint256 genes2, uint256 targetBlock) with: + gas gas_remaining - 710 wei + args kitty[_matronId].field_0, kitty[kitty[_matronId].field_448].field_0, uint64(kitty[_matronId].field_320 - 1) + require ext_call.success + require _matronId == uint32(_matronId) + require kitty[_matronId].field_448 == kitty[_matronId].field_448 + if kitty[kitty[_matronId].field_448].field_496 <= kitty[_matronId].field_496: + require uint16(kitty[_matronId].field_496 + 1) == uint16(kitty[_matronId].field_496 + 1) + kitty.length++ + if not kitty.length <= kitty.length + 1: + idx = 2 * kitty.length + 1 + while 2 * kitty.length > idx: + kitty[idx].field_0 = 0 + kitty[idx].field_256 = 0 + idx = idx + 2 + continue  + kitty[kitty.length].field_0 = ext_call.return_data[0] + kitty[kitty.length].field_256 = uint64(block.timestamp) + kitty[kitty.length].field_320 = 0 + kitty[kitty.length].field_512 = 0 + kitty[kitty.length].field_384 = uint32(_matronId) + kitty[kitty.length].field_416 = kitty[_matronId].field_448 + kitty[kitty.length].field_448 = 0 + kitty[kitty.length].field_512 = 0 + if uint16(kitty[_matronId].field_496 + 1) / 2 <= 13: + kitty[kitty.length].field_480 = uint16(kitty[_matronId].field_496 + 1) / 2 + kitty[kitty.length].field_495 = 0 + else: + kitty[kitty.length].field_480 = 13 + kitty[kitty.length].field_488 = 0 + kitty[kitty.length].field_496 = uint16(kitty[_matronId].field_496 + 1) + else: + require uint16(kitty[kitty[_matronId].field_448].field_496 + 1) == uint16(kitty[kitty[_matronId].field_448].field_496 + 1) + kitty.length++ + if not kitty.length <= kitty.length + 1: + idx = 2 * kitty.length + 1 + while 2 * kitty.length > idx: + kitty[idx].field_0 = 0 + kitty[idx].field_256 = 0 + idx = idx + 2 + continue  + kitty[kitty.length].field_0 = ext_call.return_data[0] + kitty[kitty.length].field_256 = uint64(block.timestamp) + kitty[kitty.length].field_320 = 0 + kitty[kitty.length].field_512 = 0 + kitty[kitty.length].field_384 = uint32(_matronId) + kitty[kitty.length].field_416 = kitty[_matronId].field_448 + kitty[kitty.length].field_448 = 0 + kitty[kitty.length].field_512 = 0 + if uint16(kitty[kitty[_matronId].field_448].field_496 + 1) / 2 <= 13: + kitty[kitty.length].field_480 = uint16(kitty[kitty[_matronId].field_448].field_496 + 1) / 2 + kitty[kitty.length].field_495 = 0 + else: + kitty[kitty.length].field_480 = 13 + kitty[kitty.length].field_488 = 0 + kitty[kitty.length].field_496 = uint16(kitty[kitty[_matronId].field_448].field_496 + 1) + require kitty.length == uint32(kitty.length) + log Birth( +  address owner=kittyIndexToOwner[_matronId], +  uint256 clownId=kitty.length, +  uint256 matronId=_matronId << 224, +  uint256 sireId=kitty[_matronId].field_256, +  uint256 genes=ext_call.return_data[0]) + balanceOf[stor7[_matronId]]++ + kittyIndexToOwner[stor6.length] = kittyIndexToOwner[_matronId] + log Transfer( +  address from=0, +  address to=kittyIndexToOwner[_matronId], +  uint256 value=kitty.length) + kitty[_matronId].field_448 = 0 + pregnantKitties-- + call caller with: + value autoBirthFee wei + gas 2300 * is_zero(value) wei + return kitty.length + diff --git a/benchmark/0x06a6a7aF298129E3a2AB396c9C06F91D3C54aBA8.pan b/benchmark/0x06a6a7aF298129E3a2AB396c9C06F91D3C54aBA8.pan new file mode 100644 index 00000000..8180cbee --- /dev/null +++ b/benchmark/0x06a6a7aF298129E3a2AB396c9C06F91D3C54aBA8.pan @@ -0,0 +1,3077 @@ +# +# Panoramix 4 Oct 2019 +# Decompiled source of 0x06a6a7aF298129E3a2AB396c9C06F91D3C54aBA8 +# +# Let's make the world open source +#  + +const unknownbdf22c66 = 10000 + +def storage: + ownerOf is mapping of addr at storage 0 + approved is mapping of addr at storage 1 + balanceOf is mapping of uint256 at storage 2 + stor3 is mapping of uint8 at storage 3 + name is array of uint256 at storage 4 + symbol is array of uint256 at storage 5 + tokenOfOwnerByIndex is array of uint256 at storage 6 + stor7 is mapping of uint256 at storage 7 + tokenByIndex is array of uint256 at storage 8 + stor9 is mapping of uint256 at storage 9 + tokenURI is array of uint256 at storage 10 + owner is addr at storage 11 + stor12 is mapping of uint8 at storage 12 + treasurerAddress is addr at storage 13 + paused is uint8 at storage 13 offset 160 + unknown26c1e750 is array of struct at storage 15 + stor16 is mapping of struct at storage 16 + stor17 is mapping of uint256 at storage 17 + unknowneb822fe5 is mapping of uint256 at storage 18 + unknown414b5064 is mapping of uint256 at storage 19 + unknown7c0f0ac9Address is addr at storage 20 + unknown30f18fb7Address is addr at storage 21 + saleAuctionAddress is addr at storage 22 + unknown35bde22c is mapping of uint256 at storage 23 + unknown64384a99 is mapping of uint256 at storage 24 + promoCreatedCount is uint256 at storage 25 + stor1413 is uint256 at storage 0x1413ff7a3b1d5b6c016c061d48e2c7014700c777a29fcd068fff04265813d5d + +def promoCreatedCount(): # not payable + return promoCreatedCount + +def name(): # not payable + return name[0 len name.length] + +def getApproved(uint256 _tokenId): # not payable + return approved[_tokenId] + +def totalSupply(): # not payable + return tokenByIndex.length + +def unknown26c1e750(uint256 _param1): # not payable + require _param1 < unknown26c1e750.length + return unknown26c1e750[_param1].field_0 + +def tokenOfOwnerByIndex(address _owner, uint256 _index): # not payable + require _owner + require _index < balanceOf[addr(_owner)] + require _index < tokenOfOwnerByIndex[addr(_owner)] + return tokenOfOwnerByIndex[addr(_owner)][_index] + +def unknown30f18fb7(): # not payable + return unknown30f18fb7Address + +def unknown35bde22c(addr _param1): # not payable + return unknown35bde22c[_param1] + +def unknown414b5064(uint256 _param1): # not payable + return unknown414b5064[_param1] + +def exists(uint256 _tokenId): # not payable + return bool(ownerOf[_tokenId]) + +def tokenByIndex(uint256 _index): # not payable + require _index < tokenByIndex.length + return tokenByIndex[_index] + +def paused(): # not payable + return bool(paused) + +def ownerOf(uint256 _tokenId): # not payable + require ownerOf[_tokenId] + return ownerOf[_tokenId] + +def unknown64384a99(addr _param1): # not payable + return unknown64384a99[_param1] + +def balanceOf(address _owner): # not payable + require _owner + return balanceOf[addr(_owner)] + +def unknown7c0f0ac9(): # not payable + return unknown7c0f0ac9Address + +def owner(): # not payable + return owner + +def symbol(): # not payable + return symbol[0 len symbol.length] + +def whitelist(address _param1): # not payable + return bool(stor12[_param1]) + +def tokenURI(uint256 _tokenId): # not payable + return tokenURI[_tokenId][0 len tokenURI[_tokenId].length] + +def saleAuction(): # not payable + return saleAuctionAddress + +def isApprovedForAll(address _owner, address _operator): # not payable + return bool(stor3[addr(_owner)][addr(_operator)]) + +def unknowneb822fe5(uint256 _param1, uint256 _param2): # not payable + return unknowneb822fe5[_param1][_param2] + +def treasurer(): # not payable + return treasurerAddress + +# +# Regular functions +# + +def _fallback() payable: # default function + require stor12[caller] + +def unknown611e68d4(addr _param1): # not payable + require caller == owner + if _param1: + treasurerAddress = _param1 + +def unknownf6d016dc(): # not payable + require stor12[caller] + promoCreatedCount++ + if 10000 <= promoCreatedCount + 1: + revert with 0, 'Promo planet limit is reached' + +def transferOwnership(address _newOwner): # not payable + require caller == owner + require _newOwner + log OwnershipTransferred( +  address previousOwner=owner, +  address newOwner=_newOwner) + owner = _newOwner + +def withdrawBalance(): # not payable + if treasurerAddress != caller: + revert with 0, 'Only treasurer' + call treasurerAddress with: + value eth.balance(this.address) wei + gas 2300 * is_zero(value) wei + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + +def pause(): # not payable + if owner != caller: + if treasurerAddress != caller: + revert with 0x8c379a000000000000000000000000000000000000000000000000000000000, 'Only owner and treasure have access' + require caller == owner + require not paused + paused = 1 + log Pause() + +def unknown672815c2(uint256 _param1, uint256 _param2): # not payable + if stor17[_param1][_param2] < 1000: + return (-(100 * stor17[_param1][_param2] / 1000) + 100) + else: + return 0 + +def addAddressToWhitelist(address _addr): # not payable + require caller == owner + if stor12[addr(_addr)]: + return 0 + stor12[addr(_addr)] = 1 + log WhitelistedAddressAdded(address addr=_addr) + return 1 + +def removeAddressFromWhitelist(address _addr): # not payable + require caller == owner + if not stor12[addr(_addr)]: + return 0 + stor12[addr(_addr)] = 0 + log WhitelistedAddressRemoved(address addr=_addr) + return 1 + +def setApprovalForAll(address _to, bool _approved): # not payable + require _to != caller + stor3[caller][addr(_to)] = uint8(_approved) + log ApprovalForAll( +  address owner=_approved, +  address operator=caller, +  bool approved=_to) + +def unknown4bbe9547(addr _param1): # not payable + require caller == owner + require ext_code.size(_param1) + call _param1.0x51f403f with: + gas gas_remaining wei + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if not ext_call.return_data[0]: + revert with 0, 'Incorrect address param' + unknown7c0f0ac9Address = _param1 + +def unknown9272eb8b(addr _param1): # not payable + require caller == owner + require ext_code.size(_param1) + call _param1.0x9bd593e3 with: + gas gas_remaining wei + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if not ext_call.return_data[0]: + revert with 0, 'Incorrect address param' + unknown30f18fb7Address = _param1 + +def withdrawAuctionBalances(): # not payable + if owner != caller: + if treasurerAddress != caller: + revert with 0x8c379a000000000000000000000000000000000000000000000000000000000, 'Only owner and treasure have access' + require ext_code.size(saleAuctionAddress) + call saleAuctionAddress.withdrawBalance() with: + gas gas_remaining wei + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + +def setSaleAuctionAddress(address _address): # not payable + require caller == owner + require ext_code.size(_address) + call _address.isSaleClockAuction() with: + gas gas_remaining wei + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if not ext_call.return_data[0]: + revert with 0, 'Incorrect address param' + saleAuctionAddress = _address + +def unpause(): # not payable + require caller == owner + require paused + if not saleAuctionAddress: + revert with 0x8c379a000000000000000000000000000000000000000000000000000000000, 'SaleClock contract should be defined' + if not unknown7c0f0ac9Address: + revert with 0x8c379a000000000000000000000000000000000000000000000000000000000, 'Balance contract should be defined' + require caller == owner + require paused + paused = 0 + log Unpause() + +def approve(address _spender, uint256 _value): # not payable + require ownerOf[_value] + require ownerOf[_value] != _spender + if ownerOf[_value] != caller: + require stor3[stor0[_value]][caller] + if approved[_value]: + approved[_value] = _spender + log Approval( +  address owner=_value, +  address spender=ownerOf[_value], +  uint256 value=_spender) + else: + if _spender: + approved[_value] = _spender + log Approval( +  address owner=_value, +  address spender=ownerOf[_value], +  uint256 value=_spender) + +def addAddressesToWhitelist(address[] _addrs): # not payable + mem[128 len 32 * _addrs.length] = call.data[_addrs + 36 len 32 * _addrs.length] + require caller == owner + idx = 0 + s = 0 + while idx < _addrs.length: + require idx < _addrs.length + require caller == owner + mem[0] = mem[(32 * idx) + 140 len 20] + mem[32] = 12 + if stor12[mem[(32 * idx) + 140 len 20]]: + idx = idx + 1 + s = s + continue  + mem[0] = mem[(32 * idx) + 140 len 20] + mem[32] = 12 + stor12[addr(mem[(32 * idx) + 128])] = 1 + mem[(32 * _addrs.length) + 128] = mem[(32 * idx) + 140 len 20] + log WhitelistedAddressAdded(address addr=mem[(32 * _addrs.length) + 128]) + idx = idx + 1 + s = 1 + continue  + return bool(s) + +def removeAddressesFromWhitelist(address[] _addrs): # not payable + mem[128 len 32 * _addrs.length] = call.data[_addrs + 36 len 32 * _addrs.length] + require caller == owner + idx = 0 + s = 0 + while idx < _addrs.length: + require idx < _addrs.length + require caller == owner + mem[0] = mem[(32 * idx) + 140 len 20] + mem[32] = 12 + if not stor12[mem[(32 * idx) + 140 len 20]]: + idx = idx + 1 + s = s + continue  + mem[0] = mem[(32 * idx) + 140 len 20] + mem[32] = 12 + stor12[addr(mem[(32 * idx) + 128])] = 0 + mem[(32 * _addrs.length) + 128] = mem[(32 * idx) + 140 len 20] + log WhitelistedAddressRemoved(address addr=mem[(32 * _addrs.length) + 128]) + idx = idx + 1 + s = 1 + continue  + return bool(s) + +def unknownb568ee2b(uint256 _param1): # not payable + require _param1 < unknown26c1e750.length + mem[2656 len 160] = code.data[17173 len 160] + idx = 0 + while idx < 5: + mem[(32 * idx) + 2656] = uint8(unknown26c1e750[_param1].field_48 / 2^(8 * idx)) + idx = idx + 1 + continue  + idx = 0 + while idx < 5: + mem[(32 * idx) + 2816] = uint8(unknown26c1e750[_param1].field_88 / 2^(8 * idx)) + idx = idx + 1 + continue  + mem[2976 len 160] = code.data[17173 len 160] + idx = 0 + while idx < 5: + mem[(32 * idx) + 2976] = uint16(stor16[_param1].field_88 / 2^(16 * idx)) + idx = idx + 1 + continue  + return stor16[_param1].field_0, mem[2656 len 160], mem[2976 len 160] + +def getPlanet(uint256 _planetId): # not payable + require _planetId < unknown26c1e750.length + mem[2656 len 160] = code.data[17173 len 160] + idx = 0 + while idx < 5: + mem[(32 * idx) + 2656] = uint8(unknown26c1e750[_planetId].field_48 / 2^(8 * idx)) + idx = idx + 1 + continue  + mem[2816 len 160] = code.data[17173 len 160] + idx = 0 + while idx < 5: + mem[(32 * idx) + 2816] = uint8(unknown26c1e750[_planetId].field_88 / 2^(8 * idx)) + idx = idx + 1 + continue  + idx = 0 + while idx < 5: + mem[(32 * idx) + 2976] = uint16(stor16[_planetId].field_88 / 2^(16 * idx)) + idx = idx + 1 + continue  + return unknown26c1e750[_planetId].field_144, + unknown26c1e750[_planetId].field_0, + unknown26c1e750[_planetId].field_128, + unknown26c1e750[_planetId].field_136, + mem[2656 len 160], + mem[2816 len 160] + +def createSaleAuction(uint256 _artworkId, uint256 _startingPrice, uint256 _endingPrice, uint256 _duration): # not payable + require not paused + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x55909f87 with: + gas gas_remaining wei + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if ext_call.return_data[0]: + require ext_code.size(saleAuctionAddress) + call saleAuctionAddress.0xb7edea3 with: + gas gas_remaining wei + args caller, _artworkId + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require ownerOf[_artworkId] + if ownerOf[_artworkId] != caller: + revert with 0, 'Not owner' + require ownerOf[_artworkId] + require ownerOf[_artworkId] != saleAuctionAddress + if ownerOf[_artworkId] != caller: + require stor3[stor0[_artworkId]][caller] + if approved[_artworkId]: + approved[_artworkId] = saleAuctionAddress + log Approval( +  address owner=_artworkId, +  address spender=ownerOf[_artworkId], +  uint256 value=saleAuctionAddress) + else: + if saleAuctionAddress: + approved[_artworkId] = saleAuctionAddress + log Approval( +  address owner=_artworkId, +  address spender=ownerOf[_artworkId], +  uint256 value=saleAuctionAddress) + require ext_code.size(saleAuctionAddress) + call saleAuctionAddress.createAuction(uint256 tokenId, uint256 startingPrice, uint256 endingPrice, uint256 duration, address seller) with: + gas gas_remaining wei + args 0, uint32(_artworkId), _startingPrice, _endingPrice, _duration, caller + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + +def transferFrom(address _from, address _to, uint256 _value): # not payable + require ownerOf[_value] + if ownerOf[_value] != caller: + if approved[_value] != caller: + require stor3[stor0[_value]][caller] + require _from + require _to + require ownerOf[_value] + require ownerOf[_value] == _from + if approved[_value]: + approved[_value] = 0 + log Approval( +  address owner=_value, +  address spender=_from, +  uint256 value=0) + require ownerOf[_value] + require ownerOf[_value] == _from + require 1 <= balanceOf[addr(_from)] + balanceOf[addr(_from)]-- + ownerOf[_value] = 0 + require 1 <= tokenOfOwnerByIndex[addr(_from)] + require tokenOfOwnerByIndex[addr(_from)] - 1 < tokenOfOwnerByIndex[addr(_from)] + require stor7[_value] < tokenOfOwnerByIndex[addr(_from)] + tokenOfOwnerByIndex[addr(_from)][stor7[_value]] = tokenOfOwnerByIndex[addr(_from)][tokenOfOwnerByIndex[addr(_from)]] + require tokenOfOwnerByIndex[addr(_from)] - 1 < tokenOfOwnerByIndex[addr(_from)] + tokenOfOwnerByIndex[addr(_from)][tokenOfOwnerByIndex[addr(_from)]] = 0 + tokenOfOwnerByIndex[addr(_from)]-- + if tokenOfOwnerByIndex[addr(_from)] > tokenOfOwnerByIndex[addr(_from)] - 1: + idx = tokenOfOwnerByIndex[addr(_from)] - 1 + while tokenOfOwnerByIndex[addr(_from)] > idx: + tokenOfOwnerByIndex[addr(_from)][idx] = 0 + idx = idx + 1 + continue  + stor7[_value] = 0 + stor7[stor6[addr(_from)][stor6[addr(_from)]]] = stor7[_value] + require not ownerOf[_value] + ownerOf[_value] = _to + require balanceOf[addr(_to)] + 1 >= balanceOf[addr(_to)] + balanceOf[addr(_to)]++ + tokenOfOwnerByIndex[addr(_to)]++ + tokenOfOwnerByIndex[addr(_to)][tokenOfOwnerByIndex[addr(_to)]] = _value + stor7[_value] = tokenOfOwnerByIndex[addr(_to)] + log Transfer( +  address from=_value, +  address to=_from, +  uint256 value=_to) + +def unknownc58b1bdd(addr _param1, uint256 _param2, uint256 _param3, uint256 _param4, uint256 _param5): # not payable + require stor12[caller] + mem[2656 len 160] = code.data[17173 len 160] + require ext_code.size(unknown30f18fb7Address) + call unknown30f18fb7Address.0x570bb139 with: + gas gas_remaining wei + args _param2 + mem[2816 len 320] = ext_call.return_data[0 len 320] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 320 + mem[2656] = _param5 + mem[ceil32(return_data.size) + 2816] = _param2 + mem[ceil32(return_data.size) + 2848] = block.timestamp + mem[ceil32(return_data.size) + 2880] = block.timestamp + mem[ceil32(return_data.size) + 2912] = _param3 + mem[ceil32(return_data.size) + 2944] = _param4 + mem[ceil32(return_data.size) + 2976] = 2816 + mem[ceil32(return_data.size) + 3008] = 2976 + mem[ceil32(return_data.size) + 3040] = 2656 + idx = 0 + s = 0 + while idx < 5: + idx = idx + 1 + s = 2^(8 * idx) * mem[(32 * idx) + 2816] or s + continue  + idx = 0 + s = 0 + while idx < 5: + _26 = mem[(32 * idx) + 2976] + idx = idx + 1 + s = 2^(8 * idx) * mem[(32 * idx) + 2976] or s + continue  + idx = 0 + s = 0 + while idx < 5: + idx = idx + 1 + s = 2^(8 * idx) * mem[(32 * idx) + 2816] or s + continue  + idx = 0 + s = 0 + while idx < 5: + _33 = mem[(32 * idx) + 2656] + idx = idx + 1 + s = 2^(16 * idx) * mem[(32 * idx) + 2656] or s + continue  + unknown26c1e750.length++ + unknown26c1e750[unknown26c1e750.length].field_0 = Mask(168, 0, 5 * 2^40 * _26) + unknown26c1e750[unknown26c1e750.length].field_168 = 0 + stor16[stor15.length].field_0 = Mask(168, 0, 5 * 2^80 * _33) + stor16[stor15.length].field_168 = 0 + if unknown26c1e750.length >= 10^6: + revert with 0, 'No more planets' + log 0xf54657dd: _param3, _param4, _param2, ext_call.return_data[0 len 160], ext_call.return_data[160 len 160], _param5, _param1, unknown26c1e750.length + stor17[_param3][_param4]++ + if 3 == _param2: + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x7944013a with: + gas gas_remaining wei + args _param3, _param4 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + unknowneb822fe5[ext_call.return_data[0]][3]++ + if 4 == _param2: + stor1413++ + require _param1 + require not ownerOf[stor15.length] + ownerOf[stor15.length] = _param1 + require balanceOf[addr(_param1)] + 1 >= balanceOf[addr(_param1)] + balanceOf[addr(_param1)]++ + tokenOfOwnerByIndex[addr(_param1)]++ + tokenOfOwnerByIndex[addr(_param1)][tokenOfOwnerByIndex[addr(_param1)]] = unknown26c1e750.length + stor7[stor15.length] = tokenOfOwnerByIndex[addr(_param1)] + log Transfer( +  address from=unknown26c1e750.length, +  address to=0, +  uint256 value=_param1) + stor9[stor15.length] = tokenByIndex.length + tokenByIndex.length++ + tokenByIndex[tokenByIndex.length] = unknown26c1e750.length + return unknown26c1e750.length + +def safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes _data): # not payable + require ownerOf[_tokenId] + if ownerOf[_tokenId] != caller: + if approved[_tokenId] != caller: + require stor3[stor0[_tokenId]][caller] + require ownerOf[_tokenId] + if ownerOf[_tokenId] != caller: + if approved[_tokenId] != caller: + require stor3[stor0[_tokenId]][caller] + require _from + require _to + require ownerOf[_tokenId] + require ownerOf[_tokenId] == _from + if approved[_tokenId]: + approved[_tokenId] = 0 + log Approval( +  address owner=_tokenId, +  address spender=_from, +  uint256 value=0) + require ownerOf[_tokenId] + require ownerOf[_tokenId] == _from + require 1 <= balanceOf[addr(_from)] + balanceOf[addr(_from)]-- + ownerOf[_tokenId] = 0 + require 1 <= tokenOfOwnerByIndex[addr(_from)] + require tokenOfOwnerByIndex[addr(_from)] - 1 < tokenOfOwnerByIndex[addr(_from)] + require stor7[_tokenId] < tokenOfOwnerByIndex[addr(_from)] + tokenOfOwnerByIndex[addr(_from)][stor7[_tokenId]] = tokenOfOwnerByIndex[addr(_from)][tokenOfOwnerByIndex[addr(_from)]] + require tokenOfOwnerByIndex[addr(_from)] - 1 < tokenOfOwnerByIndex[addr(_from)] + tokenOfOwnerByIndex[addr(_from)][tokenOfOwnerByIndex[addr(_from)]] = 0 + tokenOfOwnerByIndex[addr(_from)]-- + if tokenOfOwnerByIndex[addr(_from)] > tokenOfOwnerByIndex[addr(_from)] - 1: + idx = tokenOfOwnerByIndex[addr(_from)] - 1 + while tokenOfOwnerByIndex[addr(_from)] > idx: + tokenOfOwnerByIndex[addr(_from)][idx] = 0 + idx = idx + 1 + continue  + stor7[_tokenId] = 0 + stor7[stor6[addr(_from)][stor6[addr(_from)]]] = stor7[_tokenId] + require not ownerOf[_tokenId] + ownerOf[_tokenId] = _to + require balanceOf[addr(_to)] + 1 >= balanceOf[addr(_to)] + balanceOf[addr(_to)]++ + tokenOfOwnerByIndex[addr(_to)]++ + tokenOfOwnerByIndex[addr(_to)][tokenOfOwnerByIndex[addr(_to)]] = _tokenId + stor7[_tokenId] = tokenOfOwnerByIndex[addr(_to)] + log Transfer( +  address from=_tokenId, +  address to=_from, +  uint256 value=_to) + if ext_code.size(_to) > 0: + require ext_code.size(_to) + call _to.onERC721Received(address from, uint256 tokenId, bytes data) with: + gas gas_remaining wei + args addr(_from), _tokenId, Array(len=_data.length, data=_data[all]) + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + require 0xf0b9e5ba00000000000000000000000000000000000000000000000000000000 == Mask(32, 224, ext_call.return_data[0]) + +def safeTransferFrom(address _from, address _to, uint256 _tokenId): # not payable + require ownerOf[_tokenId] + if ownerOf[_tokenId] != caller: + if approved[_tokenId] != caller: + require stor3[stor0[_tokenId]][caller] + require ownerOf[_tokenId] + if ownerOf[_tokenId] != caller: + if approved[_tokenId] != caller: + require stor3[stor0[_tokenId]][caller] + require ownerOf[_tokenId] + if ownerOf[_tokenId] != caller: + if approved[_tokenId] != caller: + require stor3[stor0[_tokenId]][caller] + require _from + require _to + require ownerOf[_tokenId] + require ownerOf[_tokenId] == _from + if approved[_tokenId]: + approved[_tokenId] = 0 + log Approval( +  address owner=_tokenId, +  address spender=_from, +  uint256 value=0) + require ownerOf[_tokenId] + require ownerOf[_tokenId] == _from + require 1 <= balanceOf[addr(_from)] + balanceOf[addr(_from)]-- + ownerOf[_tokenId] = 0 + require 1 <= tokenOfOwnerByIndex[addr(_from)] + require tokenOfOwnerByIndex[addr(_from)] - 1 < tokenOfOwnerByIndex[addr(_from)] + require stor7[_tokenId] < tokenOfOwnerByIndex[addr(_from)] + tokenOfOwnerByIndex[addr(_from)][stor7[_tokenId]] = tokenOfOwnerByIndex[addr(_from)][tokenOfOwnerByIndex[addr(_from)]] + require tokenOfOwnerByIndex[addr(_from)] - 1 < tokenOfOwnerByIndex[addr(_from)] + tokenOfOwnerByIndex[addr(_from)][tokenOfOwnerByIndex[addr(_from)]] = 0 + tokenOfOwnerByIndex[addr(_from)]-- + if tokenOfOwnerByIndex[addr(_from)] > tokenOfOwnerByIndex[addr(_from)] - 1: + idx = tokenOfOwnerByIndex[addr(_from)] - 1 + while tokenOfOwnerByIndex[addr(_from)] > idx: + tokenOfOwnerByIndex[addr(_from)][idx] = 0 + idx = idx + 1 + continue  + stor7[_tokenId] = 0 + stor7[stor6[addr(_from)][stor6[addr(_from)]]] = stor7[_tokenId] + require not ownerOf[_tokenId] + ownerOf[_tokenId] = _to + require balanceOf[addr(_to)] + 1 >= balanceOf[addr(_to)] + balanceOf[addr(_to)]++ + tokenOfOwnerByIndex[addr(_to)]++ + tokenOfOwnerByIndex[addr(_to)][tokenOfOwnerByIndex[addr(_to)]] = _tokenId + stor7[_tokenId] = tokenOfOwnerByIndex[addr(_to)] + log Transfer( +  address from=_tokenId, +  address to=_from, +  uint256 value=_to) + if ext_code.size(_to) > 0: + require ext_code.size(_to) + call _to.onERC721Received(address from, uint256 tokenId, bytes data) with: + gas gas_remaining wei + args addr(_from), _tokenId, 96, 0 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + require 0xf0b9e5ba00000000000000000000000000000000000000000000000000000000 == Mask(32, 224, ext_call.return_data[0]) + +def unknown3e573168(uint256 _param1): # not payable + mem[96 len 160] = code.data[17173 len 160] + mem[256] = 0 + mem[288] = 0 + mem[320] = 0 + mem[352] = 0 + mem[384] = 0 + mem[896 len 160] = code.data[17173 len 160] + mem[416] = 896 + mem[1056 len 160] = code.data[17173 len 160] + mem[448] = 1056 + mem[1216 len 160] = code.data[17173 len 160] + mem[480] = 1216 + mem[1376 len 160] = code.data[17173 len 160] + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 34 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + mem[2176 len 160] = code.data[17173 len 160] + mem[2336 len 160] = code.data[17173 len 160] + mem[2496 len 160] = code.data[17173 len 160] + require _param1 < unknown26c1e750.length + mem[0] = _param1 + mem[32] = 16 + mem[1568] = unknown26c1e750[_param1].field_0 + mem[2656 len 160] = code.data[17173 len 160] + idx = 0 + while idx < 5: + mem[(32 * idx) + 2656] = uint8(unknown26c1e750[_param1].field_48 / 2^(8 * idx)) + idx = idx + 1 + continue  + mem[1696] = 2656 + mem[2816 len 160] = code.data[17173 len 160] + idx = 0 + while idx < 5: + mem[(32 * idx) + 2816] = uint8(unknown26c1e750[_param1].field_88 / 2^(8 * idx)) + idx = idx + 1 + continue  + mem[1728] = 2816 + mem[1632] = unknown26c1e750[_param1].field_128 + mem[1664] = unknown26c1e750[_param1].field_136 + mem[1536] = unknown26c1e750[_param1].field_144 + mem[1600] = stor16[_param1].field_0 + mem[64] = 3136 + mem[2976 len 160] = code.data[17173 len 160] + idx = 0 + while idx < 5: + mem[(32 * idx) + 2976] = uint16(stor16[_param1].field_88 / 2^(16 * idx)) + idx = idx + 1 + continue  + mem[1760] = 2976 + mem[3140] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[3136] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + idx = 0 + while idx < 5: + if mem[(32 * idx) + 2816]: + require idx < 5 + require idx < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1728]]) - (stor16[_param1].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1728]]) % 24 * 3600 < 12 * 3600: + mem[(32 * idx) + mem[1760]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1728]]) - (stor16[_param1].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1728]]) / 24 * 3600) + mem[(32 * idx) + mem[1760]] + else: + mem[(32 * idx) + mem[1760]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1728]]) - (stor16[_param1].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1728]]) / 24 * 3600) + mem[(32 * idx) + mem[1760]] + 1 + if mem[(32 * idx) + mem[1760]] > mem[(32 * idx) + mem[1728]] * ext_call.return_data[0]: + require idx < 5 + mem[(32 * idx) + mem[1760]] = mem[(32 * idx) + mem[1728]] * ext_call.return_data[0] + idx = idx + 1 + continue  + mem[mem[64] len 160] = mem[mem[1760] len 160] + return memory + from mem[64] + len 160 + +def initialize(address _sender): # not payable + require caller == owner + if unknown26c1e750.length: + revert with 0, 'Earth was created' + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0xbe8fb1c1 with: + gas gas_remaining wei + args 20 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 64 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0xe4c5efe9 with: + gas gas_remaining wei + args 21 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 96 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0xe4c5efe9 with: + gas gas_remaining wei + args 22 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 96 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0xe4c5efe9 with: + gas gas_remaining wei + args 24 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 96 + mem[(6 * ceil32(return_data.size)) + 1824] = ext_call.return_data[0] + mem[(6 * ceil32(return_data.size)) + 1856] = ext_call.return_data[32] + mem[(6 * ceil32(return_data.size)) + 1888] = ext_call.return_data[64] + mem[(6 * ceil32(return_data.size)) + 1920] = 0 + mem[(6 * ceil32(return_data.size)) + 1952] = 0 + mem[(6 * ceil32(return_data.size)) + 1984] = ext_call.return_data[0] + mem[(6 * ceil32(return_data.size)) + 2016] = ext_call.return_data[32] + mem[(6 * ceil32(return_data.size)) + 2048] = ext_call.return_data[64] + mem[(6 * ceil32(return_data.size)) + 2080] = 0 + mem[(6 * ceil32(return_data.size)) + 2112] = 0 + mem[(6 * ceil32(return_data.size)) + 2144] = ext_call.return_data[0] + mem[(6 * ceil32(return_data.size)) + 2176] = ext_call.return_data[32] + mem[(6 * ceil32(return_data.size)) + 2208] = ext_call.return_data[64] + mem[(6 * ceil32(return_data.size)) + 2240] = 0 + mem[(6 * ceil32(return_data.size)) + 2272] = 0 + idx = 0 + s = 0 + while idx < 5: + idx = idx + 1 + s = 2^(8 * idx) * mem[(32 * idx) + (6 * ceil32(return_data.size)) + 1824] or s + continue  + idx = 0 + s = 0 + while idx < 5: + _47 = mem[(32 * idx) + (6 * ceil32(return_data.size)) + 1984] + idx = idx + 1 + s = 2^(8 * idx) * mem[(32 * idx) + (6 * ceil32(return_data.size)) + 1984] or s + continue  + idx = 0 + s = 0 + while idx < 5: + idx = idx + 1 + s = 2^(8 * idx) * mem[(32 * idx) + (6 * ceil32(return_data.size)) + 1824] or s + continue  + idx = 0 + s = 0 + while idx < 5: + _54 = mem[(32 * idx) + (6 * ceil32(return_data.size)) + 2144] + idx = idx + 1 + s = 2^(16 * idx) * mem[(32 * idx) + (6 * ceil32(return_data.size)) + 2144] or s + continue  + unknown26c1e750.length++ + unknown26c1e750[unknown26c1e750.length].field_0 = Mask(168, 0, 5 * 2^40 * _47) + unknown26c1e750[unknown26c1e750.length].field_168 = 0 + stor16[stor15.length].field_0 = Mask(168, 0, 5 * 2^80 * _54) + stor16[stor15.length].field_168 = 0 + if unknown26c1e750.length >= 10^6: + revert with 0, 'No more planets' + log 0xf54657dd: ext_call.return_data[0], ext_call.return_data[32], 3, 0, 0, ext_call.return_data[0], _sender, unknown26c1e750.length + stor17[ext_call.return_data[0]][ext_call.return_data[32]]++ + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x7944013a with: + gas gas_remaining wei + args ext_call.return_data[0], ext_call.return_data[32] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + unknowneb822fe5[ext_call.return_data[0]][3]++ + require _sender + require not ownerOf[stor15.length] + ownerOf[stor15.length] = _sender + require balanceOf[addr(_sender)] + 1 >= balanceOf[addr(_sender)] + balanceOf[addr(_sender)]++ + tokenOfOwnerByIndex[addr(_sender)]++ + tokenOfOwnerByIndex[addr(_sender)][tokenOfOwnerByIndex[addr(_sender)]] = unknown26c1e750.length + stor7[stor15.length] = tokenOfOwnerByIndex[addr(_sender)] + log Transfer( +  address from=unknown26c1e750.length, +  address to=0, +  uint256 value=_sender) + stor9[stor15.length] = tokenByIndex.length + tokenByIndex.length++ + tokenByIndex[tokenByIndex.length] = unknown26c1e750.length + +def unknown5a71e6d3(addr _param1): # not payable + mem[96] = 0 + mem[128] = 0 + mem[160] = 0 + mem[192] = 0 + mem[224] = 0 + mem[736 len 160] = code.data[17173 len 160] + mem[256] = 736 + mem[896 len 160] = code.data[17173 len 160] + mem[288] = 896 + mem[64] = 1216 + mem[1056 len 160] = code.data[17173 len 160] + mem[320] = 1056 + mem[1220] = 34 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 34 + mem[1216] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + mem[0] = _param1 + mem[32] = 6 + s = 0 + s = 96 + idx = 0 + s = 24 * 3600 * unknown35bde22c[addr(_param1)] + while idx < tokenOfOwnerByIndex[addr(_param1)]: + mem[32] = 6 + require idx < tokenOfOwnerByIndex[addr(_param1)] + mem[0] = sha3(addr(_param1), 6) + _56 = mem[64] + mem[64] = mem[64] + 640 + mem[_56] = 0 + mem[_56 + 32] = 0 + mem[_56 + 64] = 0 + mem[_56 + 96] = 0 + mem[_56 + 128] = 0 + _57 = mem[64] + mem[64] = mem[64] + 160 + mem[_57 len 160] = code.data[17173 len 160] + mem[_56 + 160] = _57 + _58 = mem[64] + mem[64] = mem[64] + 160 + mem[_58 len 160] = code.data[17173 len 160] + mem[_56 + 192] = _58 + _59 = mem[64] + mem[64] = mem[64] + 160 + mem[_59 len 160] = code.data[17173 len 160] + mem[_56 + 224] = _59 + require tokenOfOwnerByIndex[addr(_param1)][idx] < unknown26c1e750.length + mem[0] = tokenOfOwnerByIndex[addr(_param1)][idx] + mem[32] = 16 + mem[_56 + 32] = unknown26c1e750[stor6[addr(_param1)][idx]].field_0 + _62 = mem[64] + mem[64] = mem[64] + 160 + mem[_62 len 160] = code.data[17173 len 160] + t = 0 + while t < 5: + mem[(32 * t) + _62] = uint8(unknown26c1e750[stor6[addr(_param1)][idx]].field_48 / 2^(8 * t)) + t = t + 1 + continue  + mem[_56 + 160] = _62 + _63 = mem[64] + mem[64] = mem[64] + 160 + mem[_63 len 160] = code.data[17173 len 160] + t = 0 + while t < 5: + mem[(32 * t) + _63] = uint8(unknown26c1e750[stor6[addr(_param1)][idx]].field_88 / 2^(8 * t)) + t = t + 1 + continue  + mem[_56 + 192] = _63 + mem[_56 + 96] = unknown26c1e750[stor6[addr(_param1)][idx]].field_128 + mem[_56 + 128] = unknown26c1e750[stor6[addr(_param1)][idx]].field_136 + mem[_56] = unknown26c1e750[stor6[addr(_param1)][idx]].field_144 + mem[_56 + 64] = stor16[stor6[addr(_param1)][idx]].field_0 + _64 = mem[64] + mem[64] = mem[64] + 160 + mem[_64 len 160] = code.data[17173 len 160] + t = 0 + while t < 5: + mem[(32 * t) + _64] = uint16(stor16[stor6[addr(_param1)][idx]].field_88 / 2^(16 * t)) + t = t + 1 + continue  + mem[_56 + 224] = _64 + mem[0] = _param1 + mem[32] = 24 + if mem[_56 + 64] > unknown64384a99[addr(_param1)]: + _67 = mem[_56 + 64] + mem[mem[64] + 4] = 17 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 17 + mem[mem[64]] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if (block.timestamp * ext_call.return_data[0] * mem[mem[_56 + 192]]) - (_67 * ext_call.return_data[0] * mem[mem[_56 + 192]]) % 48 * 24 * 3600 < 24 * 3600: + s = (block.timestamp * ext_call.return_data[0]) - (_67 * ext_call.return_data[0]) + s = _56 + idx = idx + 1 + s = (block.timestamp * ext_call.return_data[0] * mem[_64] * ext_call.return_data[0]) - (_67 * ext_call.return_data[0] * mem[_64] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_56 + 192]]) - (_67 * ext_call.return_data[0] * mem[mem[_56 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (_67 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_56 + 192]]) - (_67 * ext_call.return_data[0] * mem[mem[_56 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) + s + continue  + s = (block.timestamp * ext_call.return_data[0]) - (_67 * ext_call.return_data[0]) + s = _56 + idx = idx + 1 + s = (block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (_67 * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * mem[_64] * ext_call.return_data[0]) - (_67 * ext_call.return_data[0] * mem[_64] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_56 + 192]]) - (_67 * ext_call.return_data[0] * mem[mem[_56 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (_67 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_56 + 192]]) - (_67 * ext_call.return_data[0] * mem[mem[_56 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) + s + continue  + mem[0] = _param1 + mem[32] = 24 + mem[mem[64] + 4] = 17 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 17 + mem[mem[64]] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if (block.timestamp * ext_call.return_data[0] * mem[mem[_56 + 192]]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[mem[_56 + 192]]) % 48 * 24 * 3600 < 24 * 3600: + s = (block.timestamp * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0]) + s = _56 + idx = idx + 1 + s = (block.timestamp * ext_call.return_data[0] * mem[_64] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[_64] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_56 + 192]]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[mem[_56 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_56 + 192]]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[mem[_56 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) + s + continue  + s = (block.timestamp * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0]) + s = _56 + idx = idx + 1 + s = (block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * mem[_64] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[_64] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_56 + 192]]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[mem[_56 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_56 + 192]]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[mem[_56 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) + s + continue  + if s % 24 * 3600 < 12 * 3600: + return (s / 24 * 3600) + return ((s / 24 * 3600) + 1) + +def unknown8b17b33b(addr _param1, uint256 _param2): # not payable + mem[96 len 64] = code.data[17173 len 64] + mem[160] = 0 + mem[192] = 0 + mem[224] = 0 + mem[256] = 0 + mem[288] = 0 + mem[800 len 160] = code.data[17173 len 160] + mem[320] = 800 + mem[960 len 160] = code.data[17173 len 160] + mem[352] = 960 + mem[1120 len 160] = code.data[17173 len 160] + mem[384] = 1120 + mem[0] = _param1 + mem[32] = 6 + mem[1280] = 5 * tokenOfOwnerByIndex[addr(_param1)] + if not 5 * tokenOfOwnerByIndex[addr(_param1)]: + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0xccf9bbb6 with: + gas gas_remaining wei + args _param2 + mem[(32 * 5 * tokenOfOwnerByIndex[addr(_param1)]) + 1312 len 64] = ext_call.return_data[0 len 64] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + mem[64] = (32 * 5 * tokenOfOwnerByIndex[addr(_param1)]) + ceil32(return_data.size) + 1312 + require return_data.size >= 64 + s = 0 + t = 0 + t = 160 + idx = 0 + while idx < tokenOfOwnerByIndex[addr(_param1)]: + mem[32] = 6 + require idx < tokenOfOwnerByIndex[addr(_param1)] + mem[0] = sha3(addr(_param1), 6) + _58 = mem[64] + mem[64] = mem[64] + 640 + mem[_58] = 0 + mem[_58 + 32] = 0 + mem[_58 + 64] = 0 + mem[_58 + 96] = 0 + mem[_58 + 128] = 0 + _60 = mem[64] + mem[64] = mem[64] + 160 + mem[_60 len 160] = code.data[17173 len 160] + mem[_58 + 160] = _60 + _62 = mem[64] + mem[64] = mem[64] + 160 + mem[_62 len 160] = code.data[17173 len 160] + mem[_58 + 192] = _62 + _64 = mem[64] + mem[64] = mem[64] + 160 + mem[_64 len 160] = code.data[17173 len 160] + mem[_58 + 224] = _64 + require tokenOfOwnerByIndex[addr(_param1)][idx] < unknown26c1e750.length + mem[0] = tokenOfOwnerByIndex[addr(_param1)][idx] + mem[32] = 16 + mem[_58 + 32] = unknown26c1e750[stor6[addr(_param1)][idx]].field_0 + _70 = mem[64] + mem[64] = mem[64] + 160 + mem[_70 len 160] = code.data[17173 len 160] + t = 0 + while t < 5: + mem[(32 * t) + _70] = uint8(unknown26c1e750[stor6[addr(_param1)][idx]].field_48 / 2^(8 * t)) + t = t + 1 + continue  + mem[_58 + 160] = _70 + _72 = mem[64] + mem[64] = mem[64] + 160 + mem[_72 len 160] = code.data[17173 len 160] + t = 0 + while t < 5: + mem[(32 * t) + _72] = uint8(unknown26c1e750[stor6[addr(_param1)][idx]].field_88 / 2^(8 * t)) + t = t + 1 + continue  + mem[_58 + 192] = _72 + mem[_58 + 96] = unknown26c1e750[stor6[addr(_param1)][idx]].field_128 + mem[_58 + 128] = unknown26c1e750[stor6[addr(_param1)][idx]].field_136 + mem[_58] = unknown26c1e750[stor6[addr(_param1)][idx]].field_144 + mem[_58 + 64] = stor16[stor6[addr(_param1)][idx]].field_0 + _74 = mem[64] + mem[64] = mem[64] + 160 + mem[_74 len 160] = code.data[17173 len 160] + t = 0 + while t < 5: + mem[(32 * t) + _74] = uint16(stor16[stor6[addr(_param1)][idx]].field_88 / 2^(16 * t)) + t = t + 1 + continue  + mem[_58 + 224] = _74 + u = s + t = 1 + v = 0 + while t < 5: + _84 = mem[(32 * t) + mem[_58 + 160]] + if not mem[(32 * t) + mem[_58 + 160]]: + s = mem[(32 * t) + mem[_58 + 160]] + t = t + t = _58 + idx = idx + 1 + continue  + if mem[(32 * t) + mem[_58 + 160]] < ext_call.return_data[0]: + u = mem[(32 * t) + mem[_58 + 160]] + t = t + 1 + v = v + continue  + if mem[(32 * t) + mem[_58 + 160]] > ext_call.return_data[0] + ext_call.return_data[32] - 1: + u = mem[(32 * t) + mem[_58 + 160]] + t = t + 1 + v = v + continue  + require v < mem[1280] + mem[(32 * v) + 1312] = mem[(32 * t) + mem[_58 + 160]] + u = _84 + t = t + 1 + v = v + 1 + continue  + s = u + t = t + t = _58 + idx = idx + 1 + continue  + else: + mem[1312 len 32 * 5 * tokenOfOwnerByIndex[addr(_param1)]] = code.data[17173 len 32 * 5 * tokenOfOwnerByIndex[addr(_param1)]] + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0xccf9bbb6 with: + gas gas_remaining wei + args _param2 + mem[(32 * 5 * tokenOfOwnerByIndex[addr(_param1)]) + 1312 len 64] = ext_call.return_data[0 len 64] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + mem[64] = (32 * 5 * tokenOfOwnerByIndex[addr(_param1)]) + ceil32(return_data.size) + 1312 + require return_data.size >= 64 + s = 0 + t = 0 + t = 160 + idx = 0 + while idx < tokenOfOwnerByIndex[addr(_param1)]: + mem[32] = 6 + require idx < tokenOfOwnerByIndex[addr(_param1)] + mem[0] = sha3(addr(_param1), 6) + _59 = mem[64] + mem[64] = mem[64] + 640 + mem[_59] = 0 + mem[_59 + 32] = 0 + mem[_59 + 64] = 0 + mem[_59 + 96] = 0 + mem[_59 + 128] = 0 + _61 = mem[64] + mem[64] = mem[64] + 160 + mem[_61 len 160] = code.data[17173 len 160] + mem[_59 + 160] = _61 + _63 = mem[64] + mem[64] = mem[64] + 160 + mem[_63 len 160] = code.data[17173 len 160] + mem[_59 + 192] = _63 + _65 = mem[64] + mem[64] = mem[64] + 160 + mem[_65 len 160] = code.data[17173 len 160] + mem[_59 + 224] = _65 + require tokenOfOwnerByIndex[addr(_param1)][idx] < unknown26c1e750.length + mem[0] = tokenOfOwnerByIndex[addr(_param1)][idx] + mem[32] = 16 + mem[_59 + 32] = unknown26c1e750[stor6[addr(_param1)][idx]].field_0 + _71 = mem[64] + mem[64] = mem[64] + 160 + mem[_71 len 160] = code.data[17173 len 160] + t = 0 + while t < 5: + mem[(32 * t) + _71] = uint8(unknown26c1e750[stor6[addr(_param1)][idx]].field_48 / 2^(8 * t)) + t = t + 1 + continue  + mem[_59 + 160] = _71 + _73 = mem[64] + mem[64] = mem[64] + 160 + mem[_73 len 160] = code.data[17173 len 160] + t = 0 + while t < 5: + mem[(32 * t) + _73] = uint8(unknown26c1e750[stor6[addr(_param1)][idx]].field_88 / 2^(8 * t)) + t = t + 1 + continue  + mem[_59 + 192] = _73 + mem[_59 + 96] = unknown26c1e750[stor6[addr(_param1)][idx]].field_128 + mem[_59 + 128] = unknown26c1e750[stor6[addr(_param1)][idx]].field_136 + mem[_59] = unknown26c1e750[stor6[addr(_param1)][idx]].field_144 + mem[_59 + 64] = stor16[stor6[addr(_param1)][idx]].field_0 + _75 = mem[64] + mem[64] = mem[64] + 160 + mem[_75 len 160] = code.data[17173 len 160] + t = 0 + while t < 5: + mem[(32 * t) + _75] = uint16(stor16[stor6[addr(_param1)][idx]].field_88 / 2^(16 * t)) + t = t + 1 + continue  + mem[_59 + 224] = _75 + u = s + t = 1 + v = 0 + while t < 5: + _85 = mem[(32 * t) + mem[_59 + 160]] + if not mem[(32 * t) + mem[_59 + 160]]: + s = mem[(32 * t) + mem[_59 + 160]] + t = t + t = _59 + idx = idx + 1 + continue  + if mem[(32 * t) + mem[_59 + 160]] < ext_call.return_data[0]: + u = mem[(32 * t) + mem[_59 + 160]] + t = t + 1 + v = v + continue  + if mem[(32 * t) + mem[_59 + 160]] > ext_call.return_data[0] + ext_call.return_data[32] - 1: + u = mem[(32 * t) + mem[_59 + 160]] + t = t + 1 + v = v + continue  + require v < mem[1280] + mem[(32 * v) + 1312] = mem[(32 * t) + mem[_59 + 160]] + u = _85 + t = t + 1 + v = v + 1 + continue  + s = u + t = t + t = _59 + idx = idx + 1 + continue  + return -1 + +def unknownf97fd58a(addr _param1, uint256 _param2): # not payable + mem[96] = 0 + mem[128] = 0 + mem[160] = 0 + mem[192] = 0 + mem[224] = 0 + mem[736 len 160] = code.data[17173 len 160] + mem[256] = 736 + mem[896 len 160] = code.data[17173 len 160] + mem[288] = 896 + mem[64] = 1216 + mem[1056 len 160] = code.data[17173 len 160] + mem[320] = 1056 + require stor12[caller] + if _param2 <= unknown35bde22c[addr(_param1)]: + unknown35bde22c[addr(_param1)] = 0 + unknown64384a99[addr(_param1)] = block.timestamp + stop + mem[1220] = 34 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 34 + mem[1216] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + mem[0] = _param1 + mem[32] = 6 + s = 0 + s = 96 + idx = 0 + s = 24 * 3600 * unknown35bde22c[addr(_param1)] + while idx < tokenOfOwnerByIndex[addr(_param1)]: + mem[32] = 6 + require idx < tokenOfOwnerByIndex[addr(_param1)] + mem[0] = sha3(addr(_param1), 6) + _64 = mem[64] + mem[64] = mem[64] + 640 + mem[_64] = 0 + mem[_64 + 32] = 0 + mem[_64 + 64] = 0 + mem[_64 + 96] = 0 + mem[_64 + 128] = 0 + _65 = mem[64] + mem[64] = mem[64] + 160 + mem[_65 len 160] = code.data[17173 len 160] + mem[_64 + 160] = _65 + _66 = mem[64] + mem[64] = mem[64] + 160 + mem[_66 len 160] = code.data[17173 len 160] + mem[_64 + 192] = _66 + _67 = mem[64] + mem[64] = mem[64] + 160 + mem[_67 len 160] = code.data[17173 len 160] + mem[_64 + 224] = _67 + require tokenOfOwnerByIndex[addr(_param1)][idx] < unknown26c1e750.length + mem[0] = tokenOfOwnerByIndex[addr(_param1)][idx] + mem[32] = 16 + mem[_64 + 32] = unknown26c1e750[stor6[addr(_param1)][idx]].field_0 + _70 = mem[64] + mem[64] = mem[64] + 160 + mem[_70 len 160] = code.data[17173 len 160] + t = 0 + while t < 5: + mem[(32 * t) + _70] = uint8(unknown26c1e750[stor6[addr(_param1)][idx]].field_48 / 2^(8 * t)) + t = t + 1 + continue  + mem[_64 + 160] = _70 + _71 = mem[64] + mem[64] = mem[64] + 160 + mem[_71 len 160] = code.data[17173 len 160] + t = 0 + while t < 5: + mem[(32 * t) + _71] = uint8(unknown26c1e750[stor6[addr(_param1)][idx]].field_88 / 2^(8 * t)) + t = t + 1 + continue  + mem[_64 + 192] = _71 + mem[_64 + 96] = unknown26c1e750[stor6[addr(_param1)][idx]].field_128 + mem[_64 + 128] = unknown26c1e750[stor6[addr(_param1)][idx]].field_136 + mem[_64] = unknown26c1e750[stor6[addr(_param1)][idx]].field_144 + mem[_64 + 64] = stor16[stor6[addr(_param1)][idx]].field_0 + _72 = mem[64] + mem[64] = mem[64] + 160 + mem[_72 len 160] = code.data[17173 len 160] + t = 0 + while t < 5: + mem[(32 * t) + _72] = uint16(stor16[stor6[addr(_param1)][idx]].field_88 / 2^(16 * t)) + t = t + 1 + continue  + mem[_64 + 224] = _72 + mem[0] = _param1 + mem[32] = 24 + if mem[_64 + 64] > unknown64384a99[addr(_param1)]: + _75 = mem[_64 + 64] + mem[mem[64] + 4] = 17 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 17 + mem[mem[64]] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if (block.timestamp * ext_call.return_data[0] * mem[mem[_64 + 192]]) - (_75 * ext_call.return_data[0] * mem[mem[_64 + 192]]) % 48 * 24 * 3600 < 24 * 3600: + if (block.timestamp * ext_call.return_data[0] * mem[_72] * ext_call.return_data[0]) - (_75 * ext_call.return_data[0] * mem[_72] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_64 + 192]]) - (_75 * ext_call.return_data[0] * mem[mem[_64 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (_75 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_64 + 192]]) - (_75 * ext_call.return_data[0] * mem[mem[_64 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) + s < 24 * 3600 * _param2: + s = (block.timestamp * ext_call.return_data[0]) - (_75 * ext_call.return_data[0]) + s = _64 + idx = idx + 1 + s = (block.timestamp * ext_call.return_data[0] * mem[_72] * ext_call.return_data[0]) - (_75 * ext_call.return_data[0] * mem[_72] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_64 + 192]]) - (_75 * ext_call.return_data[0] * mem[mem[_64 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (_75 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_64 + 192]]) - (_75 * ext_call.return_data[0] * mem[mem[_64 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) + s + continue  + else: + if (block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (_75 * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * mem[_72] * ext_call.return_data[0]) - (_75 * ext_call.return_data[0] * mem[_72] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_64 + 192]]) - (_75 * ext_call.return_data[0] * mem[mem[_64 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (_75 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_64 + 192]]) - (_75 * ext_call.return_data[0] * mem[mem[_64 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) + s < 24 * 3600 * _param2: + s = (block.timestamp * ext_call.return_data[0]) - (_75 * ext_call.return_data[0]) + s = _64 + idx = idx + 1 + s = (block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (_75 * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * mem[_72] * ext_call.return_data[0]) - (_75 * ext_call.return_data[0] * mem[_72] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_64 + 192]]) - (_75 * ext_call.return_data[0] * mem[mem[_64 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (_75 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_64 + 192]]) - (_75 * ext_call.return_data[0] * mem[mem[_64 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) + s + continue  + else: + mem[0] = _param1 + mem[32] = 24 + mem[mem[64] + 4] = 17 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 17 + mem[mem[64]] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if (block.timestamp * ext_call.return_data[0] * mem[mem[_64 + 192]]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[mem[_64 + 192]]) % 48 * 24 * 3600 < 24 * 3600: + if (block.timestamp * ext_call.return_data[0] * mem[_72] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[_72] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_64 + 192]]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[mem[_64 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_64 + 192]]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[mem[_64 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) + s < 24 * 3600 * _param2: + s = (block.timestamp * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0]) + s = _64 + idx = idx + 1 + s = (block.timestamp * ext_call.return_data[0] * mem[_72] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[_72] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_64 + 192]]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[mem[_64 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_64 + 192]]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[mem[_64 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) + s + continue  + else: + if (block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * mem[_72] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[_72] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_64 + 192]]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[mem[_64 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_64 + 192]]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[mem[_64 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) + s < 24 * 3600 * _param2: + s = (block.timestamp * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0]) + s = _64 + idx = idx + 1 + s = (block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * mem[_72] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[_72] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_64 + 192]]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[mem[_64 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_64 + 192]]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[mem[_64 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) + s + continue  + unknown35bde22c[addr(_param1)] = 0 + unknown64384a99[addr(_param1)] = block.timestamp + stop + revert with 0, 'NotEnoughKnowledge' + +def unknownaac0b776(addr _param1, uint256 _param2): # not payable + mem[96] = 0 + mem[128] = 0 + mem[160] = 0 + mem[192] = 0 + mem[224] = 0 + mem[736 len 160] = code.data[17173 len 160] + mem[256] = 736 + mem[896 len 160] = code.data[17173 len 160] + mem[288] = 896 + mem[1056 len 160] = code.data[17173 len 160] + mem[320] = 1056 + require stor12[caller] + mem[1856 len 160] = code.data[17173 len 160] + mem[2016 len 160] = code.data[17173 len 160] + mem[2176 len 160] = code.data[17173 len 160] + require _param2 < unknown26c1e750.length + mem[1248] = unknown26c1e750[_param2].field_0 + mem[2336 len 160] = code.data[17173 len 160] + idx = 0 + while idx < 5: + mem[(32 * idx) + 2336] = uint8(unknown26c1e750[_param2].field_48 / 2^(8 * idx)) + idx = idx + 1 + continue  + mem[1376] = 2336 + mem[2496 len 160] = code.data[17173 len 160] + idx = 0 + while idx < 5: + mem[(32 * idx) + 2496] = uint8(unknown26c1e750[_param2].field_88 / 2^(8 * idx)) + idx = idx + 1 + continue  + mem[1408] = 2496 + mem[1312] = unknown26c1e750[_param2].field_128 + mem[1344] = unknown26c1e750[_param2].field_136 + mem[1216] = unknown26c1e750[_param2].field_144 + mem[1280] = stor16[_param2].field_0 + mem[2656 len 160] = code.data[17173 len 160] + idx = 0 + while idx < 5: + mem[(32 * idx) + 2656] = uint16(stor16[_param2].field_88 / 2^(16 * idx)) + idx = idx + 1 + continue  + mem[1440] = 2656 + mem[2816] = 0 + mem[2848] = 0 + mem[2880] = 0 + mem[2912] = 0 + mem[2944] = 0 + mem[3456 len 160] = code.data[17173 len 160] + mem[2976] = 3456 + mem[3616 len 160] = code.data[17173 len 160] + mem[3008] = 3616 + mem[64] = 3936 + mem[3776 len 160] = code.data[17173 len 160] + mem[3040] = 3776 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 34 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 17 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + mem[0] = _param1 + mem[32] = 23 + if stor16[_param2].field_0 > unknown64384a99[addr(_param1)]: + if (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) % 48 * 24 * 3600 < 24 * 3600: + if (block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) % 24 * 3600 < 12 * 3600: + unknown35bde22c[addr(_param1)] += (block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600 + mem[3940] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[3936] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + idx = 0 + while idx < 5: + if mem[(32 * idx) + mem[1408]]: + require idx < 5 + require idx < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) % 24 * 3600 < 12 * 3600: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + else: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + 1 + if mem[(32 * idx) + mem[1440]] > mem[(32 * idx) + mem[1408]] * ext_call.return_data[0]: + require idx < 5 + mem[(32 * idx) + mem[1440]] = mem[(32 * idx) + mem[1408]] * ext_call.return_data[0] + idx = idx + 1 + continue  + mem[1280] = block.timestamp + idx = 0 + s = 0 + while idx < 5: + idx = idx + 1 + s = 2^(8 * idx) * mem[(32 * idx) + mem[1376]] or s + continue  + idx = 0 + s = 0 + while idx < 5: + _431 = mem[(32 * idx) + mem[1440]] + idx = idx + 1 + s = 2^(16 * idx) * mem[(32 * idx) + mem[1440]] or s + continue  + stor16[_param2].field_0 = Mask(168, 0, 5 * 2^80 * _431) + else: + unknown35bde22c[addr(_param1)] = ((block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600) + unknown35bde22c[addr(_param1)] + 1 + mem[3940] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[3936] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + idx = 0 + while idx < 5: + if mem[(32 * idx) + mem[1408]]: + require idx < 5 + require idx < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) % 24 * 3600 < 12 * 3600: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + else: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + 1 + if mem[(32 * idx) + mem[1440]] > mem[(32 * idx) + mem[1408]] * ext_call.return_data[0]: + require idx < 5 + mem[(32 * idx) + mem[1440]] = mem[(32 * idx) + mem[1408]] * ext_call.return_data[0] + idx = idx + 1 + continue  + mem[1280] = block.timestamp + idx = 0 + s = 0 + while idx < 5: + idx = idx + 1 + s = 2^(8 * idx) * mem[(32 * idx) + mem[1376]] or s + continue  + idx = 0 + s = 0 + while idx < 5: + _432 = mem[(32 * idx) + mem[1440]] + idx = idx + 1 + s = 2^(16 * idx) * mem[(32 * idx) + mem[1440]] or s + continue  + stor16[_param2].field_0 = Mask(168, 0, 5 * 2^80 * _432) + else: + if (block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) % 24 * 3600 < 12 * 3600: + unknown35bde22c[addr(_param1)] += (block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600 + mem[3940] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[3936] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + idx = 0 + while idx < 5: + if mem[(32 * idx) + mem[1408]]: + require idx < 5 + require idx < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) % 24 * 3600 < 12 * 3600: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + else: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + 1 + if mem[(32 * idx) + mem[1440]] > mem[(32 * idx) + mem[1408]] * ext_call.return_data[0]: + require idx < 5 + mem[(32 * idx) + mem[1440]] = mem[(32 * idx) + mem[1408]] * ext_call.return_data[0] + idx = idx + 1 + continue  + mem[1280] = block.timestamp + idx = 0 + s = 0 + while idx < 5: + idx = idx + 1 + s = 2^(8 * idx) * mem[(32 * idx) + mem[1376]] or s + continue  + idx = 0 + s = 0 + while idx < 5: + _433 = mem[(32 * idx) + mem[1440]] + idx = idx + 1 + s = 2^(16 * idx) * mem[(32 * idx) + mem[1440]] or s + continue  + stor16[_param2].field_0 = Mask(168, 0, 5 * 2^80 * _433) + else: + unknown35bde22c[addr(_param1)] = ((block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600) + unknown35bde22c[addr(_param1)] + 1 + mem[3940] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[3936] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + idx = 0 + while idx < 5: + if mem[(32 * idx) + mem[1408]]: + require idx < 5 + require idx < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) % 24 * 3600 < 12 * 3600: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + else: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + 1 + if mem[(32 * idx) + mem[1440]] > mem[(32 * idx) + mem[1408]] * ext_call.return_data[0]: + require idx < 5 + mem[(32 * idx) + mem[1440]] = mem[(32 * idx) + mem[1408]] * ext_call.return_data[0] + idx = idx + 1 + continue  + mem[1280] = block.timestamp + idx = 0 + s = 0 + while idx < 5: + idx = idx + 1 + s = 2^(8 * idx) * mem[(32 * idx) + mem[1376]] or s + continue  + idx = 0 + s = 0 + while idx < 5: + _434 = mem[(32 * idx) + mem[1440]] + idx = idx + 1 + s = 2^(16 * idx) * mem[(32 * idx) + mem[1440]] or s + continue  + stor16[_param2].field_0 = Mask(168, 0, 5 * 2^80 * _434) + else: + if (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) % 48 * 24 * 3600 < 24 * 3600: + if (block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) % 24 * 3600 < 12 * 3600: + unknown35bde22c[addr(_param1)] += (block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600 + mem[3940] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[3936] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + idx = 0 + while idx < 5: + if mem[(32 * idx) + mem[1408]]: + require idx < 5 + require idx < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) % 24 * 3600 < 12 * 3600: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + else: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + 1 + if mem[(32 * idx) + mem[1440]] > mem[(32 * idx) + mem[1408]] * ext_call.return_data[0]: + require idx < 5 + mem[(32 * idx) + mem[1440]] = mem[(32 * idx) + mem[1408]] * ext_call.return_data[0] + idx = idx + 1 + continue  + mem[1280] = block.timestamp + idx = 0 + s = 0 + while idx < 5: + idx = idx + 1 + s = 2^(8 * idx) * mem[(32 * idx) + mem[1376]] or s + continue  + idx = 0 + s = 0 + while idx < 5: + _435 = mem[(32 * idx) + mem[1440]] + idx = idx + 1 + s = 2^(16 * idx) * mem[(32 * idx) + mem[1440]] or s + continue  + stor16[_param2].field_0 = Mask(168, 0, 5 * 2^80 * _435) + else: + unknown35bde22c[addr(_param1)] = ((block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600) + unknown35bde22c[addr(_param1)] + 1 + mem[3940] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[3936] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + idx = 0 + while idx < 5: + if mem[(32 * idx) + mem[1408]]: + require idx < 5 + require idx < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) % 24 * 3600 < 12 * 3600: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + else: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + 1 + if mem[(32 * idx) + mem[1440]] > mem[(32 * idx) + mem[1408]] * ext_call.return_data[0]: + require idx < 5 + mem[(32 * idx) + mem[1440]] = mem[(32 * idx) + mem[1408]] * ext_call.return_data[0] + idx = idx + 1 + continue  + mem[1280] = block.timestamp + idx = 0 + s = 0 + while idx < 5: + idx = idx + 1 + s = 2^(8 * idx) * mem[(32 * idx) + mem[1376]] or s + continue  + idx = 0 + s = 0 + while idx < 5: + _436 = mem[(32 * idx) + mem[1440]] + idx = idx + 1 + s = 2^(16 * idx) * mem[(32 * idx) + mem[1440]] or s + continue  + stor16[_param2].field_0 = Mask(168, 0, 5 * 2^80 * _436) + else: + if (block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) % 24 * 3600 < 12 * 3600: + unknown35bde22c[addr(_param1)] += (block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600 + mem[3940] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[3936] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + idx = 0 + while idx < 5: + if mem[(32 * idx) + mem[1408]]: + require idx < 5 + require idx < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) % 24 * 3600 < 12 * 3600: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + else: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + 1 + if mem[(32 * idx) + mem[1440]] > mem[(32 * idx) + mem[1408]] * ext_call.return_data[0]: + require idx < 5 + mem[(32 * idx) + mem[1440]] = mem[(32 * idx) + mem[1408]] * ext_call.return_data[0] + idx = idx + 1 + continue  + mem[1280] = block.timestamp + idx = 0 + s = 0 + while idx < 5: + idx = idx + 1 + s = 2^(8 * idx) * mem[(32 * idx) + mem[1376]] or s + continue  + idx = 0 + s = 0 + while idx < 5: + _437 = mem[(32 * idx) + mem[1440]] + idx = idx + 1 + s = 2^(16 * idx) * mem[(32 * idx) + mem[1440]] or s + continue  + stor16[_param2].field_0 = Mask(168, 0, 5 * 2^80 * _437) + else: + unknown35bde22c[addr(_param1)] = ((block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600) + unknown35bde22c[addr(_param1)] + 1 + mem[3940] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[3936] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + idx = 0 + while idx < 5: + if mem[(32 * idx) + mem[1408]]: + require idx < 5 + require idx < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) % 24 * 3600 < 12 * 3600: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + else: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + 1 + if mem[(32 * idx) + mem[1440]] > mem[(32 * idx) + mem[1408]] * ext_call.return_data[0]: + require idx < 5 + mem[(32 * idx) + mem[1440]] = mem[(32 * idx) + mem[1408]] * ext_call.return_data[0] + idx = idx + 1 + continue  + mem[1280] = block.timestamp + idx = 0 + s = 0 + while idx < 5: + idx = idx + 1 + s = 2^(8 * idx) * mem[(32 * idx) + mem[1376]] or s + continue  + idx = 0 + s = 0 + while idx < 5: + _438 = mem[(32 * idx) + mem[1440]] + idx = idx + 1 + s = 2^(16 * idx) * mem[(32 * idx) + mem[1440]] or s + continue  + stor16[_param2].field_0 = Mask(168, 0, 5 * 2^80 * _438) + stor16[_param2].field_168 = 0 + +def unknown189052f1(addr _param1, uint256 _param2, uint256 _param3, uint256 _param4): # not payable + mem[96] = 0 + mem[128] = 0 + mem[160] = 0 + mem[192] = 0 + mem[224] = 0 + mem[736 len 160] = code.data[17173 len 160] + mem[256] = 736 + mem[896 len 160] = code.data[17173 len 160] + mem[288] = 896 + mem[1056 len 160] = code.data[17173 len 160] + mem[320] = 1056 + require stor12[caller] + if not _param1: + revert with 0, 'Owner param should be defined' + if _param4 <= 0: + revert with 0, 'ResourceValue param should be bigger that zero' + mem[1856 len 160] = code.data[17173 len 160] + mem[2016 len 160] = code.data[17173 len 160] + mem[2176 len 160] = code.data[17173 len 160] + require _param2 < unknown26c1e750.length + mem[1248] = unknown26c1e750[_param2].field_0 + mem[2336 len 160] = code.data[17173 len 160] + idx = 0 + while idx < 5: + mem[(32 * idx) + 2336] = uint8(unknown26c1e750[_param2].field_48 / 2^(8 * idx)) + idx = idx + 1 + continue  + mem[1376] = 2336 + mem[2496 len 160] = code.data[17173 len 160] + idx = 0 + while idx < 5: + mem[(32 * idx) + 2496] = uint8(unknown26c1e750[_param2].field_88 / 2^(8 * idx)) + idx = idx + 1 + continue  + mem[1408] = 2496 + mem[1312] = unknown26c1e750[_param2].field_128 + mem[1344] = unknown26c1e750[_param2].field_136 + mem[1216] = unknown26c1e750[_param2].field_144 + mem[1280] = stor16[_param2].field_0 + mem[2656 len 160] = code.data[17173 len 160] + idx = 0 + while idx < 5: + mem[(32 * idx) + 2656] = uint16(stor16[_param2].field_88 / 2^(16 * idx)) + idx = idx + 1 + continue  + mem[1440] = 2656 + mem[2816] = 0 + mem[2848] = 0 + mem[2880] = 0 + mem[2912] = 0 + mem[2944] = 0 + mem[3456 len 160] = code.data[17173 len 160] + mem[2976] = 3456 + mem[3616 len 160] = code.data[17173 len 160] + mem[3008] = 3616 + mem[64] = 3936 + mem[3776 len 160] = code.data[17173 len 160] + mem[3040] = 3776 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 34 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 17 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + mem[0] = _param1 + mem[32] = 23 + if stor16[_param2].field_0 > unknown64384a99[addr(_param1)]: + if (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) % 48 * 24 * 3600 < 24 * 3600: + if (block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) % 24 * 3600 < 12 * 3600: + unknown35bde22c[addr(_param1)] += (block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600 + mem[3940] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[3936] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + idx = 0 + while idx < 5: + if mem[(32 * idx) + mem[1408]]: + require idx < 5 + require idx < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) % 24 * 3600 < 12 * 3600: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + else: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + 1 + if mem[(32 * idx) + mem[1440]] > mem[(32 * idx) + mem[1408]] * ext_call.return_data[0]: + require idx < 5 + mem[(32 * idx) + mem[1440]] = mem[(32 * idx) + mem[1408]] * ext_call.return_data[0] + idx = idx + 1 + continue  + require _param3 < 5 + if mem[(32 * _param3) + mem[1440]] < _param4: + revert with 0, 'Resource current should be bigger that ResourceValue' + require _param3 < 5 + mem[(32 * _param3) + mem[1440]] = mem[(32 * _param3) + mem[1440]] - _param4 + mem[1280] = block.timestamp + idx = 0 + s = 0 + while idx < 5: + idx = idx + 1 + s = 2^(8 * idx) * mem[(32 * idx) + mem[1376]] or s + continue  + idx = 0 + s = 0 + while idx < 5: + _483 = mem[(32 * idx) + mem[1440]] + idx = idx + 1 + s = 2^(16 * idx) * mem[(32 * idx) + mem[1440]] or s + continue  + stor16[_param2].field_0 = Mask(168, 0, 5 * 2^80 * _483) + else: + unknown35bde22c[addr(_param1)] = ((block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600) + unknown35bde22c[addr(_param1)] + 1 + mem[3940] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[3936] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + idx = 0 + while idx < 5: + if mem[(32 * idx) + mem[1408]]: + require idx < 5 + require idx < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) % 24 * 3600 < 12 * 3600: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + else: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + 1 + if mem[(32 * idx) + mem[1440]] > mem[(32 * idx) + mem[1408]] * ext_call.return_data[0]: + require idx < 5 + mem[(32 * idx) + mem[1440]] = mem[(32 * idx) + mem[1408]] * ext_call.return_data[0] + idx = idx + 1 + continue  + require _param3 < 5 + if mem[(32 * _param3) + mem[1440]] < _param4: + revert with 0, 'Resource current should be bigger that ResourceValue' + require _param3 < 5 + mem[(32 * _param3) + mem[1440]] = mem[(32 * _param3) + mem[1440]] - _param4 + mem[1280] = block.timestamp + idx = 0 + s = 0 + while idx < 5: + idx = idx + 1 + s = 2^(8 * idx) * mem[(32 * idx) + mem[1376]] or s + continue  + idx = 0 + s = 0 + while idx < 5: + _484 = mem[(32 * idx) + mem[1440]] + idx = idx + 1 + s = 2^(16 * idx) * mem[(32 * idx) + mem[1440]] or s + continue  + stor16[_param2].field_0 = Mask(168, 0, 5 * 2^80 * _484) + else: + if (block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) % 24 * 3600 < 12 * 3600: + unknown35bde22c[addr(_param1)] += (block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600 + mem[3940] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[3936] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + idx = 0 + while idx < 5: + if mem[(32 * idx) + mem[1408]]: + require idx < 5 + require idx < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) % 24 * 3600 < 12 * 3600: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + else: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + 1 + if mem[(32 * idx) + mem[1440]] > mem[(32 * idx) + mem[1408]] * ext_call.return_data[0]: + require idx < 5 + mem[(32 * idx) + mem[1440]] = mem[(32 * idx) + mem[1408]] * ext_call.return_data[0] + idx = idx + 1 + continue  + require _param3 < 5 + if mem[(32 * _param3) + mem[1440]] < _param4: + revert with 0, 'Resource current should be bigger that ResourceValue' + require _param3 < 5 + mem[(32 * _param3) + mem[1440]] = mem[(32 * _param3) + mem[1440]] - _param4 + mem[1280] = block.timestamp + idx = 0 + s = 0 + while idx < 5: + idx = idx + 1 + s = 2^(8 * idx) * mem[(32 * idx) + mem[1376]] or s + continue  + idx = 0 + s = 0 + while idx < 5: + _485 = mem[(32 * idx) + mem[1440]] + idx = idx + 1 + s = 2^(16 * idx) * mem[(32 * idx) + mem[1440]] or s + continue  + stor16[_param2].field_0 = Mask(168, 0, 5 * 2^80 * _485) + else: + unknown35bde22c[addr(_param1)] = ((block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (stor16[_param2].field_0 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600) + unknown35bde22c[addr(_param1)] + 1 + mem[3940] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[3936] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + idx = 0 + while idx < 5: + if mem[(32 * idx) + mem[1408]]: + require idx < 5 + require idx < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) % 24 * 3600 < 12 * 3600: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + else: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + 1 + if mem[(32 * idx) + mem[1440]] > mem[(32 * idx) + mem[1408]] * ext_call.return_data[0]: + require idx < 5 + mem[(32 * idx) + mem[1440]] = mem[(32 * idx) + mem[1408]] * ext_call.return_data[0] + idx = idx + 1 + continue  + require _param3 < 5 + if mem[(32 * _param3) + mem[1440]] < _param4: + revert with 0, 'Resource current should be bigger that ResourceValue' + require _param3 < 5 + mem[(32 * _param3) + mem[1440]] = mem[(32 * _param3) + mem[1440]] - _param4 + mem[1280] = block.timestamp + idx = 0 + s = 0 + while idx < 5: + idx = idx + 1 + s = 2^(8 * idx) * mem[(32 * idx) + mem[1376]] or s + continue  + idx = 0 + s = 0 + while idx < 5: + _486 = mem[(32 * idx) + mem[1440]] + idx = idx + 1 + s = 2^(16 * idx) * mem[(32 * idx) + mem[1440]] or s + continue  + stor16[_param2].field_0 = Mask(168, 0, 5 * 2^80 * _486) + else: + if (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) % 48 * 24 * 3600 < 24 * 3600: + if (block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) % 24 * 3600 < 12 * 3600: + unknown35bde22c[addr(_param1)] += (block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600 + mem[3940] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[3936] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + idx = 0 + while idx < 5: + if mem[(32 * idx) + mem[1408]]: + require idx < 5 + require idx < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) % 24 * 3600 < 12 * 3600: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + else: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + 1 + if mem[(32 * idx) + mem[1440]] > mem[(32 * idx) + mem[1408]] * ext_call.return_data[0]: + require idx < 5 + mem[(32 * idx) + mem[1440]] = mem[(32 * idx) + mem[1408]] * ext_call.return_data[0] + idx = idx + 1 + continue  + require _param3 < 5 + if mem[(32 * _param3) + mem[1440]] < _param4: + revert with 0, 'Resource current should be bigger that ResourceValue' + require _param3 < 5 + mem[(32 * _param3) + mem[1440]] = mem[(32 * _param3) + mem[1440]] - _param4 + mem[1280] = block.timestamp + idx = 0 + s = 0 + while idx < 5: + idx = idx + 1 + s = 2^(8 * idx) * mem[(32 * idx) + mem[1376]] or s + continue  + idx = 0 + s = 0 + while idx < 5: + _487 = mem[(32 * idx) + mem[1440]] + idx = idx + 1 + s = 2^(16 * idx) * mem[(32 * idx) + mem[1440]] or s + continue  + stor16[_param2].field_0 = Mask(168, 0, 5 * 2^80 * _487) + else: + unknown35bde22c[addr(_param1)] = ((block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600) + unknown35bde22c[addr(_param1)] + 1 + mem[3940] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[3936] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + idx = 0 + while idx < 5: + if mem[(32 * idx) + mem[1408]]: + require idx < 5 + require idx < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) % 24 * 3600 < 12 * 3600: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + else: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + 1 + if mem[(32 * idx) + mem[1440]] > mem[(32 * idx) + mem[1408]] * ext_call.return_data[0]: + require idx < 5 + mem[(32 * idx) + mem[1440]] = mem[(32 * idx) + mem[1408]] * ext_call.return_data[0] + idx = idx + 1 + continue  + require _param3 < 5 + if mem[(32 * _param3) + mem[1440]] < _param4: + revert with 0, 'Resource current should be bigger that ResourceValue' + require _param3 < 5 + mem[(32 * _param3) + mem[1440]] = mem[(32 * _param3) + mem[1440]] - _param4 + mem[1280] = block.timestamp + idx = 0 + s = 0 + while idx < 5: + idx = idx + 1 + s = 2^(8 * idx) * mem[(32 * idx) + mem[1376]] or s + continue  + idx = 0 + s = 0 + while idx < 5: + _488 = mem[(32 * idx) + mem[1440]] + idx = idx + 1 + s = 2^(16 * idx) * mem[(32 * idx) + mem[1440]] or s + continue  + stor16[_param2].field_0 = Mask(168, 0, 5 * 2^80 * _488) + else: + if (block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) % 24 * 3600 < 12 * 3600: + unknown35bde22c[addr(_param1)] += (block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600 + mem[3940] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[3936] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + idx = 0 + while idx < 5: + if mem[(32 * idx) + mem[1408]]: + require idx < 5 + require idx < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) % 24 * 3600 < 12 * 3600: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + else: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + 1 + if mem[(32 * idx) + mem[1440]] > mem[(32 * idx) + mem[1408]] * ext_call.return_data[0]: + require idx < 5 + mem[(32 * idx) + mem[1440]] = mem[(32 * idx) + mem[1408]] * ext_call.return_data[0] + idx = idx + 1 + continue  + require _param3 < 5 + if mem[(32 * _param3) + mem[1440]] < _param4: + revert with 0, 'Resource current should be bigger that ResourceValue' + require _param3 < 5 + mem[(32 * _param3) + mem[1440]] = mem[(32 * _param3) + mem[1440]] - _param4 + mem[1280] = block.timestamp + idx = 0 + s = 0 + while idx < 5: + idx = idx + 1 + s = 2^(8 * idx) * mem[(32 * idx) + mem[1376]] or s + continue  + idx = 0 + s = 0 + while idx < 5: + _489 = mem[(32 * idx) + mem[1440]] + idx = idx + 1 + s = 2^(16 * idx) * mem[(32 * idx) + mem[1440]] or s + continue  + stor16[_param2].field_0 = Mask(168, 0, 5 * 2^80 * _489) + else: + unknown35bde22c[addr(_param1)] = ((block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2656] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[2496]) - (unknown64384a99[addr(_param1)] * ext_call.return_data[0] * mem[2496]) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600) + unknown35bde22c[addr(_param1)] + 1 + mem[3940] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[3936] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + idx = 0 + while idx < 5: + if mem[(32 * idx) + mem[1408]]: + require idx < 5 + require idx < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) % 24 * 3600 < 12 * 3600: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + else: + mem[(32 * idx) + mem[1440]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) - (stor16[_param2].field_0 * ext_call.return_data[0] * mem[(32 * idx) + mem[1408]]) / 24 * 3600) + mem[(32 * idx) + mem[1440]] + 1 + if mem[(32 * idx) + mem[1440]] > mem[(32 * idx) + mem[1408]] * ext_call.return_data[0]: + require idx < 5 + mem[(32 * idx) + mem[1440]] = mem[(32 * idx) + mem[1408]] * ext_call.return_data[0] + idx = idx + 1 + continue  + require _param3 < 5 + if mem[(32 * _param3) + mem[1440]] < _param4: + revert with 0, 'Resource current should be bigger that ResourceValue' + require _param3 < 5 + mem[(32 * _param3) + mem[1440]] = mem[(32 * _param3) + mem[1440]] - _param4 + mem[1280] = block.timestamp + idx = 0 + s = 0 + while idx < 5: + idx = idx + 1 + s = 2^(8 * idx) * mem[(32 * idx) + mem[1376]] or s + continue  + idx = 0 + s = 0 + while idx < 5: + _490 = mem[(32 * idx) + mem[1440]] + idx = idx + 1 + s = 2^(16 * idx) * mem[(32 * idx) + mem[1440]] or s + continue  + stor16[_param2].field_0 = Mask(168, 0, 5 * 2^80 * _490) + stor16[_param2].field_168 = 0 + +def unknowncd216f0e(): # not payable + mem[96] = 0 + mem[128] = 0 + mem[160] = 0 + mem[192] = 0 + mem[224] = 0 + mem[736 len 160] = code.data[17173 len 160] + mem[256] = 736 + mem[896 len 160] = code.data[17173 len 160] + mem[288] = 896 + mem[64] = 1216 + mem[1056 len 160] = code.data[17173 len 160] + mem[320] = 1056 + require stor12[caller] + mem[0] = addr(cd[4]) + mem[32] = 6 + s = 0 + s = 0 + idx = 0 + while idx < 5: + if cd[((32 * idx) + 196)]: + t = 0 + t = 0 + t = 96 + t = 0 + s = 0 + while s < tokenOfOwnerByIndex[addr(cd[4])]: + if not cd[((32 * idx) + 196)]: + if cd[((32 * idx) + 196)] > 0: + revert with 0, 'NotEnoughResources' + s = cd[((32 * idx) + 196)] + s = cd[((32 * idx) + 36)] + idx = idx + 1 + continue  + mem[32] = 6 + require s < tokenOfOwnerByIndex[addr(cd[4])] + mem[0] = sha3(addr(cd[4]), 6) + _493 = mem[64] + mem[64] = mem[64] + 640 + mem[_493] = 0 + mem[_493 + 32] = 0 + mem[_493 + 64] = 0 + mem[_493 + 96] = 0 + mem[_493 + 128] = 0 + _494 = mem[64] + mem[64] = mem[64] + 160 + mem[_494 len 160] = code.data[17173 len 160] + mem[_493 + 160] = _494 + _495 = mem[64] + mem[64] = mem[64] + 160 + mem[_495 len 160] = code.data[17173 len 160] + mem[_493 + 192] = _495 + _496 = mem[64] + mem[64] = mem[64] + 160 + mem[_496 len 160] = code.data[17173 len 160] + mem[_493 + 224] = _496 + require tokenOfOwnerByIndex[addr(cd[4])][s] < unknown26c1e750.length + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + mem[_493 + 32] = unknown26c1e750[stor6[addr(cd[4])][s]].field_0 + _499 = mem[64] + mem[64] = mem[64] + 160 + mem[_499 len 160] = code.data[17173 len 160] + t = 0 + while t < 5: + mem[(32 * t) + _499] = uint8(unknown26c1e750[stor6[addr(cd[4])][s]].field_48 / 2^(8 * t)) + t = t + 1 + continue  + mem[_493 + 160] = _499 + _500 = mem[64] + mem[64] = mem[64] + 160 + mem[_500 len 160] = code.data[17173 len 160] + t = 0 + while t < 5: + mem[(32 * t) + _500] = uint8(unknown26c1e750[stor6[addr(cd[4])][s]].field_88 / 2^(8 * t)) + t = t + 1 + continue  + mem[_493 + 192] = _500 + mem[_493 + 96] = unknown26c1e750[stor6[addr(cd[4])][s]].field_128 + mem[_493 + 128] = unknown26c1e750[stor6[addr(cd[4])][s]].field_136 + mem[_493] = unknown26c1e750[stor6[addr(cd[4])][s]].field_144 + mem[_493 + 64] = stor16[stor6[addr(cd[4])][s]].field_0 + _501 = mem[64] + mem[64] = mem[64] + 160 + mem[_501 len 160] = code.data[17173 len 160] + t = 0 + while t < 5: + mem[(32 * t) + _501] = uint16(stor16[stor6[addr(cd[4])][s]].field_88 / 2^(16 * t)) + t = t + 1 + continue  + mem[_493 + 224] = _501 + t = 0 + while t < 5: + if cd[((32 * idx) + 36)] != mem[(32 * t) + mem[_493 + 160]]: + t = t + 1 + continue  + if t != 9999: + _730 = mem[64] + mem[64] = mem[64] + 640 + mem[_730] = 0 + mem[_730 + 32] = 0 + mem[_730 + 64] = 0 + mem[_730 + 96] = 0 + mem[_730 + 128] = 0 + _731 = mem[64] + mem[64] = mem[64] + 160 + mem[_731 len 160] = code.data[17173 len 160] + mem[_730 + 160] = _731 + _732 = mem[64] + mem[64] = mem[64] + 160 + mem[_732 len 160] = code.data[17173 len 160] + mem[_730 + 192] = _732 + _733 = mem[64] + mem[64] = mem[64] + 160 + mem[_733 len 160] = code.data[17173 len 160] + mem[_730 + 224] = _733 + mem[mem[64] + 4] = 34 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 34 + mem[mem[64]] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + mem[0] = addr(cd[4]) + mem[32] = 24 + if mem[_493 + 64] > unknown64384a99[addr(cd[4])]: + _740 = mem[_493 + 64] + mem[mem[64] + 4] = 17 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 17 + mem[mem[64]] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + _752 = mem[mem[_493 + 192]] + if (block.timestamp * ext_call.return_data[0] * mem[mem[_493 + 192]]) - (_740 * ext_call.return_data[0] * mem[mem[_493 + 192]]) % 48 * 24 * 3600 < 24 * 3600: + _755 = mem[mem[_493 + 224]] + mem[0] = addr(cd[4]) + mem[32] = 23 + if (block.timestamp * ext_call.return_data[0] * mem[mem[_493 + 224]] * ext_call.return_data[0]) - (_740 * ext_call.return_data[0] * mem[mem[_493 + 224]] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_493 + 192]]) - (_740 * ext_call.return_data[0] * mem[mem[_493 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (_740 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_493 + 192]]) - (_740 * ext_call.return_data[0] * mem[mem[_493 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) % 24 * 3600 < 12 * 3600: + unknown35bde22c[addr(cd[4])] += (block.timestamp * ext_call.return_data[0] * _755 * ext_call.return_data[0]) - (_740 * ext_call.return_data[0] * _755 * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * _752) - (_740 * ext_call.return_data[0] * _752) / 48 * 24 * 3600 * ext_call.return_data[0]) - (_740 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * _752) - (_740 * ext_call.return_data[0] * _752) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600 + _763 = mem[_493 + 64] + mem[mem[64] + 4] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[mem[64]] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + u = 0 + while u < 5: + if mem[(32 * u) + mem[_493 + 192]]: + require u < 5 + require u < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_763 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) % 24 * 3600 < 12 * 3600: + mem[(32 * u) + mem[_493 + 224]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_763 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) / 24 * 3600) + mem[(32 * u) + mem[_493 + 224]] + else: + mem[(32 * u) + mem[_493 + 224]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_763 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) / 24 * 3600) + mem[(32 * u) + mem[_493 + 224]] + 1 + if mem[(32 * u) + mem[_493 + 224]] > mem[(32 * u) + mem[_493 + 192]] * ext_call.return_data[0]: + require u < 5 + mem[(32 * u) + mem[_493 + 224]] = mem[(32 * u) + mem[_493 + 192]] * ext_call.return_data[0] + u = u + 1 + continue  + require t < 5 + if mem[(32 * t) + mem[_493 + 224]] <= 0: + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1314 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1314) + else: + require t < 5 + require t < 5 + if mem[(32 * t) + mem[_493 + 224]] < cd[((32 * idx) + 196)]: + mem[(32 * t) + mem[_493 + 224]] = 0 + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1315 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1315) + else: + mem[(32 * t) + mem[_493 + 224]] = mem[(32 * t) + mem[_493 + 224]] - cd[((32 * idx) + 196)] + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1316 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1316) + else: + unknown35bde22c[addr(cd[4])] = ((block.timestamp * ext_call.return_data[0] * _755 * ext_call.return_data[0]) - (_740 * ext_call.return_data[0] * _755 * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * _752) - (_740 * ext_call.return_data[0] * _752) / 48 * 24 * 3600 * ext_call.return_data[0]) - (_740 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * _752) - (_740 * ext_call.return_data[0] * _752) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600) + unknown35bde22c[addr(cd[4])] + 1 + _765 = mem[_493 + 64] + mem[mem[64] + 4] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[mem[64]] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + u = 0 + while u < 5: + if mem[(32 * u) + mem[_493 + 192]]: + require u < 5 + require u < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_765 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) % 24 * 3600 < 12 * 3600: + mem[(32 * u) + mem[_493 + 224]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_765 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) / 24 * 3600) + mem[(32 * u) + mem[_493 + 224]] + else: + mem[(32 * u) + mem[_493 + 224]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_765 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) / 24 * 3600) + mem[(32 * u) + mem[_493 + 224]] + 1 + if mem[(32 * u) + mem[_493 + 224]] > mem[(32 * u) + mem[_493 + 192]] * ext_call.return_data[0]: + require u < 5 + mem[(32 * u) + mem[_493 + 224]] = mem[(32 * u) + mem[_493 + 192]] * ext_call.return_data[0] + u = u + 1 + continue  + require t < 5 + if mem[(32 * t) + mem[_493 + 224]] <= 0: + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1317 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1317) + else: + require t < 5 + require t < 5 + if mem[(32 * t) + mem[_493 + 224]] < cd[((32 * idx) + 196)]: + mem[(32 * t) + mem[_493 + 224]] = 0 + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1318 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1318) + else: + mem[(32 * t) + mem[_493 + 224]] = mem[(32 * t) + mem[_493 + 224]] - cd[((32 * idx) + 196)] + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1319 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1319) + else: + _757 = mem[mem[_493 + 224]] + mem[0] = addr(cd[4]) + mem[32] = 23 + if (block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (_740 * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * mem[mem[_493 + 224]] * ext_call.return_data[0]) - (_740 * ext_call.return_data[0] * mem[mem[_493 + 224]] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_493 + 192]]) - (_740 * ext_call.return_data[0] * mem[mem[_493 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (_740 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_493 + 192]]) - (_740 * ext_call.return_data[0] * mem[mem[_493 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) % 24 * 3600 < 12 * 3600: + unknown35bde22c[addr(cd[4])] += (block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (_740 * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * _757 * ext_call.return_data[0]) - (_740 * ext_call.return_data[0] * _757 * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * _752) - (_740 * ext_call.return_data[0] * _752) / 48 * 24 * 3600 * ext_call.return_data[0]) - (_740 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * _752) - (_740 * ext_call.return_data[0] * _752) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600 + _767 = mem[_493 + 64] + mem[mem[64] + 4] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[mem[64]] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + u = 0 + while u < 5: + if mem[(32 * u) + mem[_493 + 192]]: + require u < 5 + require u < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_767 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) % 24 * 3600 < 12 * 3600: + mem[(32 * u) + mem[_493 + 224]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_767 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) / 24 * 3600) + mem[(32 * u) + mem[_493 + 224]] + else: + mem[(32 * u) + mem[_493 + 224]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_767 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) / 24 * 3600) + mem[(32 * u) + mem[_493 + 224]] + 1 + if mem[(32 * u) + mem[_493 + 224]] > mem[(32 * u) + mem[_493 + 192]] * ext_call.return_data[0]: + require u < 5 + mem[(32 * u) + mem[_493 + 224]] = mem[(32 * u) + mem[_493 + 192]] * ext_call.return_data[0] + u = u + 1 + continue  + require t < 5 + if mem[(32 * t) + mem[_493 + 224]] <= 0: + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1320 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1320) + else: + require t < 5 + require t < 5 + if mem[(32 * t) + mem[_493 + 224]] < cd[((32 * idx) + 196)]: + mem[(32 * t) + mem[_493 + 224]] = 0 + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1321 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1321) + else: + mem[(32 * t) + mem[_493 + 224]] = mem[(32 * t) + mem[_493 + 224]] - cd[((32 * idx) + 196)] + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1322 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1322) + else: + unknown35bde22c[addr(cd[4])] = ((block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (_740 * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * _757 * ext_call.return_data[0]) - (_740 * ext_call.return_data[0] * _757 * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * _752) - (_740 * ext_call.return_data[0] * _752) / 48 * 24 * 3600 * ext_call.return_data[0]) - (_740 * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * _752) - (_740 * ext_call.return_data[0] * _752) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600) + unknown35bde22c[addr(cd[4])] + 1 + _769 = mem[_493 + 64] + mem[mem[64] + 4] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[mem[64]] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + u = 0 + while u < 5: + if mem[(32 * u) + mem[_493 + 192]]: + require u < 5 + require u < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_769 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) % 24 * 3600 < 12 * 3600: + mem[(32 * u) + mem[_493 + 224]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_769 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) / 24 * 3600) + mem[(32 * u) + mem[_493 + 224]] + else: + mem[(32 * u) + mem[_493 + 224]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_769 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) / 24 * 3600) + mem[(32 * u) + mem[_493 + 224]] + 1 + if mem[(32 * u) + mem[_493 + 224]] > mem[(32 * u) + mem[_493 + 192]] * ext_call.return_data[0]: + require u < 5 + mem[(32 * u) + mem[_493 + 224]] = mem[(32 * u) + mem[_493 + 192]] * ext_call.return_data[0] + u = u + 1 + continue  + require t < 5 + if mem[(32 * t) + mem[_493 + 224]] <= 0: + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1323 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1323) + else: + require t < 5 + require t < 5 + if mem[(32 * t) + mem[_493 + 224]] < cd[((32 * idx) + 196)]: + mem[(32 * t) + mem[_493 + 224]] = 0 + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1324 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1324) + else: + mem[(32 * t) + mem[_493 + 224]] = mem[(32 * t) + mem[_493 + 224]] - cd[((32 * idx) + 196)] + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1325 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1325) + else: + mem[0] = addr(cd[4]) + mem[32] = 24 + mem[mem[64] + 4] = 17 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 17 + mem[mem[64]] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + _753 = mem[mem[_493 + 192]] + if (block.timestamp * ext_call.return_data[0] * mem[mem[_493 + 192]]) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * mem[mem[_493 + 192]]) % 48 * 24 * 3600 < 24 * 3600: + _759 = mem[mem[_493 + 224]] + mem[0] = addr(cd[4]) + mem[32] = 23 + if (block.timestamp * ext_call.return_data[0] * mem[mem[_493 + 224]] * ext_call.return_data[0]) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * mem[mem[_493 + 224]] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_493 + 192]]) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * mem[mem[_493 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_493 + 192]]) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * mem[mem[_493 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) % 24 * 3600 < 12 * 3600: + unknown35bde22c[addr(cd[4])] += (block.timestamp * ext_call.return_data[0] * _759 * ext_call.return_data[0]) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * _759 * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * _753) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * _753) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * _753) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * _753) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600 + _771 = mem[_493 + 64] + mem[mem[64] + 4] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[mem[64]] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + u = 0 + while u < 5: + if mem[(32 * u) + mem[_493 + 192]]: + require u < 5 + require u < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_771 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) % 24 * 3600 < 12 * 3600: + mem[(32 * u) + mem[_493 + 224]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_771 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) / 24 * 3600) + mem[(32 * u) + mem[_493 + 224]] + else: + mem[(32 * u) + mem[_493 + 224]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_771 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) / 24 * 3600) + mem[(32 * u) + mem[_493 + 224]] + 1 + if mem[(32 * u) + mem[_493 + 224]] > mem[(32 * u) + mem[_493 + 192]] * ext_call.return_data[0]: + require u < 5 + mem[(32 * u) + mem[_493 + 224]] = mem[(32 * u) + mem[_493 + 192]] * ext_call.return_data[0] + u = u + 1 + continue  + require t < 5 + if mem[(32 * t) + mem[_493 + 224]] <= 0: + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1326 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1326) + else: + require t < 5 + require t < 5 + if mem[(32 * t) + mem[_493 + 224]] < cd[((32 * idx) + 196)]: + mem[(32 * t) + mem[_493 + 224]] = 0 + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1327 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1327) + else: + mem[(32 * t) + mem[_493 + 224]] = mem[(32 * t) + mem[_493 + 224]] - cd[((32 * idx) + 196)] + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1328 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1328) + else: + unknown35bde22c[addr(cd[4])] = ((block.timestamp * ext_call.return_data[0] * _759 * ext_call.return_data[0]) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * _759 * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * _753) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * _753) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * _753) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * _753) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600) + unknown35bde22c[addr(cd[4])] + 1 + _773 = mem[_493 + 64] + mem[mem[64] + 4] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[mem[64]] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + u = 0 + while u < 5: + if mem[(32 * u) + mem[_493 + 192]]: + require u < 5 + require u < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_773 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) % 24 * 3600 < 12 * 3600: + mem[(32 * u) + mem[_493 + 224]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_773 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) / 24 * 3600) + mem[(32 * u) + mem[_493 + 224]] + else: + mem[(32 * u) + mem[_493 + 224]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_773 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) / 24 * 3600) + mem[(32 * u) + mem[_493 + 224]] + 1 + if mem[(32 * u) + mem[_493 + 224]] > mem[(32 * u) + mem[_493 + 192]] * ext_call.return_data[0]: + require u < 5 + mem[(32 * u) + mem[_493 + 224]] = mem[(32 * u) + mem[_493 + 192]] * ext_call.return_data[0] + u = u + 1 + continue  + require t < 5 + if mem[(32 * t) + mem[_493 + 224]] <= 0: + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1329 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1329) + else: + require t < 5 + require t < 5 + if mem[(32 * t) + mem[_493 + 224]] < cd[((32 * idx) + 196)]: + mem[(32 * t) + mem[_493 + 224]] = 0 + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1330 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1330) + else: + mem[(32 * t) + mem[_493 + 224]] = mem[(32 * t) + mem[_493 + 224]] - cd[((32 * idx) + 196)] + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1331 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1331) + else: + _761 = mem[mem[_493 + 224]] + mem[0] = addr(cd[4]) + mem[32] = 23 + if (block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * mem[mem[_493 + 224]] * ext_call.return_data[0]) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * mem[mem[_493 + 224]] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_493 + 192]]) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * mem[mem[_493 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * mem[mem[_493 + 192]]) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * mem[mem[_493 + 192]]) / 48 * 24 * 3600 * ext_call.return_data[0]) % 24 * 3600 < 12 * 3600: + unknown35bde22c[addr(cd[4])] += (block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * _761 * ext_call.return_data[0]) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * _761 * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * _753) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * _753) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * _753) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * _753) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600 + _775 = mem[_493 + 64] + mem[mem[64] + 4] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[mem[64]] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + u = 0 + while u < 5: + if mem[(32 * u) + mem[_493 + 192]]: + require u < 5 + require u < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_775 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) % 24 * 3600 < 12 * 3600: + mem[(32 * u) + mem[_493 + 224]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_775 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) / 24 * 3600) + mem[(32 * u) + mem[_493 + 224]] + else: + mem[(32 * u) + mem[_493 + 224]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_775 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) / 24 * 3600) + mem[(32 * u) + mem[_493 + 224]] + 1 + if mem[(32 * u) + mem[_493 + 224]] > mem[(32 * u) + mem[_493 + 192]] * ext_call.return_data[0]: + require u < 5 + mem[(32 * u) + mem[_493 + 224]] = mem[(32 * u) + mem[_493 + 192]] * ext_call.return_data[0] + u = u + 1 + continue  + require t < 5 + if mem[(32 * t) + mem[_493 + 224]] <= 0: + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1332 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1332) + else: + require t < 5 + require t < 5 + if mem[(32 * t) + mem[_493 + 224]] < cd[((32 * idx) + 196)]: + mem[(32 * t) + mem[_493 + 224]] = 0 + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1333 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1333) + else: + mem[(32 * t) + mem[_493 + 224]] = mem[(32 * t) + mem[_493 + 224]] - cd[((32 * idx) + 196)] + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1334 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1334) + else: + unknown35bde22c[addr(cd[4])] = ((block.timestamp * ext_call.return_data[0] * ext_call.return_data[0]) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * _761 * ext_call.return_data[0]) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * _761 * ext_call.return_data[0]) + (block.timestamp * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * _753) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * _753) / 48 * 24 * 3600 * ext_call.return_data[0]) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * (block.timestamp * ext_call.return_data[0] * _753) - (unknown64384a99[addr(cd[4])] * ext_call.return_data[0] * _753) / 48 * 24 * 3600 * ext_call.return_data[0]) / 24 * 3600) + unknown35bde22c[addr(cd[4])] + 1 + _777 = mem[_493 + 64] + mem[mem[64] + 4] = 18 + require ext_code.size(unknown7c0f0ac9Address) + call unknown7c0f0ac9Address.0x64c66395 with: + gas gas_remaining wei + args 18 + mem[mem[64]] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + u = 0 + while u < 5: + if mem[(32 * u) + mem[_493 + 192]]: + require u < 5 + require u < 5 + if (block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_777 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) % 24 * 3600 < 12 * 3600: + mem[(32 * u) + mem[_493 + 224]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_777 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) / 24 * 3600) + mem[(32 * u) + mem[_493 + 224]] + else: + mem[(32 * u) + mem[_493 + 224]] = ((block.timestamp * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) - (_777 * ext_call.return_data[0] * mem[(32 * u) + mem[_493 + 192]]) / 24 * 3600) + mem[(32 * u) + mem[_493 + 224]] + 1 + if mem[(32 * u) + mem[_493 + 224]] > mem[(32 * u) + mem[_493 + 192]] * ext_call.return_data[0]: + require u < 5 + mem[(32 * u) + mem[_493 + 224]] = mem[(32 * u) + mem[_493 + 192]] * ext_call.return_data[0] + u = u + 1 + continue  + require t < 5 + if mem[(32 * t) + mem[_493 + 224]] <= 0: + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1335 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1335) + else: + require t < 5 + require t < 5 + if mem[(32 * t) + mem[_493 + 224]] < cd[((32 * idx) + 196)]: + mem[(32 * t) + mem[_493 + 224]] = 0 + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1336 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1336) + else: + mem[(32 * t) + mem[_493 + 224]] = mem[(32 * t) + mem[_493 + 224]] - cd[((32 * idx) + 196)] + mem[_493 + 64] = block.timestamp + idx = 0 + u = 0 + while idx < 5: + idx = idx + 1 + u = 2^(8 * idx) * mem[(32 * idx) + mem[_493 + 160]] or u + continue  + idx = 0 + u = 0 + while idx < 5: + _1337 = mem[(32 * idx) + mem[_493 + 224]] + idx = idx + 1 + u = 2^(16 * idx) * mem[(32 * idx) + mem[_493 + 224]] or u + continue  + mem[0] = tokenOfOwnerByIndex[addr(cd[4])][s] + mem[32] = 16 + stor16[stor6[addr(cd[4])][s]].field_0 = Mask(168, 0, 5 * 2^80 * _1337) + stor16[stor6[addr(cd[4])][s]].field_168 = 0 + t = t + t = t + t = _493 + t = tokenOfOwnerByIndex[addr(cd[4])][s] + s = s + 1 + continue  + t = t + t = 9999 + t = _493 + t = tokenOfOwnerByIndex[addr(cd[4])][s] + s = s + 1 + continue  + if cd[((32 * idx) + 196)] > 0: + revert with 0, 'NotEnoughResources' + s = cd[((32 * idx) + 196)] + s = cd[((32 * idx) + 36)] + idx = idx + 1 + continue  + diff --git a/benchmark/0x165cFb9cCf8b185E03205Ab4118eA6afBdbA9203.pan b/benchmark/0x165cFb9cCf8b185E03205Ab4118eA6afBdbA9203.pan new file mode 100644 index 00000000..e5819117 --- /dev/null +++ b/benchmark/0x165cFb9cCf8b185E03205Ab4118eA6afBdbA9203.pan @@ -0,0 +1,225 @@ +# +# Panoramix 4 Oct 2019 +# Decompiled source of 0x165cFb9cCf8b185E03205Ab4118eA6afBdbA9203 +# +# Let's make the world open source +#  + +const NOT_AUDITED = 0 +const MAX_COMMISSION = 9 +const MIN_AUDIT_TIME = (24 * 3600) +const MAX_AUDIT_TIME = (672 * 24 * 3600) + +def storage: + owner is addr at storage 0 + paused is uint8 at storage 0 offset 160 + newContractAddress is addr at storage 1 + totalRequestsAmount is uint256 at storage 2 + availableCommission is uint256 at storage 3 + commission is uint256 at storage 4 + solidStampRegisterAddress is addr at storage 5 + rewards is mapping of uint256 at storage 6 + auditRequests is mapping of struct at storage 7 + +def Rewards(bytes32 _param1): # not payable + return rewards[_param1] + +def AuditRequests(bytes32 _param1): # not payable + return auditRequests[_param1].field_0, auditRequests[_param1].field_256 + +def Commission(): # not payable + return commission + +def SolidStampRegisterAddress(): # not payable + return solidStampRegisterAddress + +def paused(): # not payable + return bool(paused) + +def AvailableCommission(): # not payable + return availableCommission + +def newContractAddress(): # not payable + return newContractAddress + +def owner(): # not payable + return owner + +def TotalRequestsAmount(): # not payable + return totalRequestsAmount + +# +# Regular functions +# + +def _fallback() payable: # default function + revert + +def pause(): # not payable + require caller == owner + require not paused + paused = 1 + log Pause() + +def renounceOwnership(): # not payable + require caller == owner + log OwnershipRenounced(address previousOwner=owner) + owner = 0 + +def setNewAddress(address _v2Address): # not payable + require caller == owner + require paused + require _v2Address + newContractAddress = _v2Address + log ContractUpgrade(address newContract=_v2Address) + +def unpause(): # not payable + require caller == owner + require paused + if newContractAddress: + revert with 0, 'new contract cannot be 0x0' + require caller == owner + require paused + paused = 0 + log Unpause() + +def transferOwnership(address _newOwner): # not payable + require caller == owner + require _newOwner + log OwnershipTransferred( +  address previousOwner=owner, +  address newOwner=_newOwner) + owner = _newOwner + +def changeCommission(uint256 _newCommission): # not payable + require caller == owner + require not paused + if _newCommission > 9: + revert with 0x8c379a000000000000000000000000000000000000000000000000000000000, 'commission should be <= MAX_COMMISSION' + if _newCommission == commission: + revert with 0, '_newCommission==Commmission' + commission = _newCommission + log NewCommission(uint256 commmission=_newCommission) + +def withdrawCommission(uint256 _amount): # not payable + require caller == owner + if _amount > availableCommission: + revert with 0x8c379a000000000000000000000000000000000000000000000000000000000, 'Cannot withdraw more than available' + availableCommission -= _amount + call caller with: + value _amount wei + gas 2300 * is_zero(value) wei + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + +def withdrawRequest(address _auditor, bytes32 _codeHash): # not payable + require ext_code.size(solidStampRegisterAddress) + call solidStampRegisterAddress.getAuditOutcome(address auditor, bytes32 codeHash) with: + gas gas_remaining wei + args addr(_auditor), _codeHash + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if uint8(ext_call.return_data[0]): + revert with 0, 'contract already audited' + if auditRequests[_auditor][caller][_codeHash].field_0 <= 0: + revert with 0, 'nothing to withdraw' + if block.timestamp <= auditRequests[_auditor][caller][_codeHash].field_256: + revert with 0, 'cannot withdraw before request.expireDate' + auditRequests[_auditor][caller][_codeHash].field_0 = 0 + auditRequests[_auditor][caller][_codeHash].field_256 = 0 + require auditRequests[_auditor][caller][_codeHash].field_0 <= rewards[_auditor][_codeHash] + rewards[_auditor][_codeHash] -= auditRequests[_auditor][caller][_codeHash].field_0 + require auditRequests[_auditor][caller][_codeHash].field_0 <= totalRequestsAmount + totalRequestsAmount -= auditRequests[_auditor][caller][_codeHash].field_0 + log RequestWithdrawn( +  address auditor=addr(_auditor), +  address bidder=caller, +  bytes32 codeHash=_codeHash, +  uint256 amount=auditRequests[_auditor][caller][_codeHash].field_0) + call caller with: + value auditRequests[_auditor][caller][_codeHash].field_0 wei + gas 2300 * is_zero(value) wei + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + +def auditContract(address _auditor, bytes32 _codeHash, bytes _reportIPFS, bool _isApproved): # not payable + require not paused + if solidStampRegisterAddress != caller: + revert with 0, 'can be only run by SolidStampRegister contract' + require rewards[_auditor][_codeHash] <= totalRequestsAmount + totalRequestsAmount -= rewards[_auditor][_codeHash] + if not rewards[_auditor][_codeHash]: + require availableCommission >= availableCommission + log ContractAudited( +  address auditor=addr(_auditor), +  bytes32 codeHash=_codeHash, +  bytes reportIPFS=Array(len=_reportIPFS.length, data=_reportIPFS[all]), +  bool isApproved=_isApproved, +  uint256 reward=rewards[_auditor][_codeHash]) + require 0 <= rewards[_auditor][_codeHash] + call _auditor with: + value rewards[_auditor][_codeHash] wei + gas 2300 * is_zero(value) wei + else: + require commission * rewards[_auditor][_codeHash] / rewards[_auditor][_codeHash] == commission + require (commission * rewards[_auditor][_codeHash] / 100) + availableCommission >= availableCommission + availableCommission += commission * rewards[_auditor][_codeHash] / 100 + log ContractAudited( +  address auditor=addr(_auditor), +  bytes32 codeHash=_codeHash, +  bytes reportIPFS=Array(len=_reportIPFS.length, data=_reportIPFS[all]), +  bool isApproved=_isApproved, +  uint256 reward=rewards[_auditor][_codeHash]) + require commission * rewards[_auditor][_codeHash] / 100 <= rewards[_auditor][_codeHash] + call _auditor with: + value rewards[_auditor][_codeHash] - (commission * rewards[_auditor][_codeHash] / 100) wei + gas 2300 * is_zero(value) wei + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + +def requestAudit(address _auditor, bytes32 _codeHash, uint256 _auditTime) payable: + require not paused + if not _auditor: + revert with 0, '_auditor cannot be 0x0' + if _auditTime < 24 * 3600: + revert with 0x8c379a000000000000000000000000000000000000000000000000000000000, '_auditTime should be >= MIN_AUDIT_TIME' + if _auditTime > 672 * 24 * 3600: + revert with 0x8c379a000000000000000000000000000000000000000000000000000000000, '_auditTime should be <= MIN_AUDIT_TIME' + if call.value <= 0: + revert with 0, 'msg.value should be >0' + require ext_code.size(solidStampRegisterAddress) + call solidStampRegisterAddress.getAuditOutcome(address auditor, bytes32 codeHash) with: + gas gas_remaining wei + args addr(_auditor), _codeHash + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if ext_call.return_data[31 len 1]: + revert with 0, 'contract already audited' + require _auditTime + block.timestamp >= block.timestamp + require call.value + rewards[_auditor][_codeHash] >= rewards[_auditor][_codeHash] + rewards[_auditor][_codeHash] += call.value + require call.value + totalRequestsAmount >= totalRequestsAmount + totalRequestsAmount += call.value + if not auditRequests[_auditor][caller][_codeHash].field_0: + auditRequests[_auditor][caller][_codeHash].field_0 = call.value + auditRequests[_auditor][caller][_codeHash].field_256 = _auditTime + block.timestamp + log AuditRequested( +  address auditor=addr(_auditor), +  address bidder=caller, +  bytes32 codeHash=_codeHash, +  uint256 amount=call.value, +  uint256 expireDate=_auditTime + block.timestamp) + else: + require call.value + auditRequests[_auditor][caller][_codeHash].field_0 >= auditRequests[_auditor][caller][_codeHash].field_0 + auditRequests[_auditor][caller][_codeHash].field_0 += call.value + if _auditTime + block.timestamp > auditRequests[_auditor][caller][_codeHash].field_256: + auditRequests[_auditor][caller][_codeHash].field_256 = _auditTime + block.timestamp + log AuditRequested( +  address auditor=addr(_auditor), +  address bidder=caller, +  bytes32 codeHash=_codeHash, +  uint256 amount=auditRequests[_auditor][caller][_codeHash].field_0, +  uint256 expireDate=auditRequests[_auditor][caller][_codeHash].field_256) + diff --git a/benchmark/0x53F955c424F1378D67Bb5e05F728476dC75fB4bA.pan b/benchmark/0x53F955c424F1378D67Bb5e05F728476dC75fB4bA.pan new file mode 100644 index 00000000..d6a13a6d --- /dev/null +++ b/benchmark/0x53F955c424F1378D67Bb5e05F728476dC75fB4bA.pan @@ -0,0 +1,29 @@ +# +# Panoramix 4 Oct 2019 +# Decompiled source of 0x53F955c424F1378D67Bb5e05F728476dC75fB4bA +# +# Let's make the world open source +#  + +def storage: + stor0 is addr at storage 0 + +def _fallback() payable: # default function + stop + +def tokenFallback(address _param1, uint256 _param2, bytes _param3): # not payable + stop + +def sweep(address _token, uint256 _amount): # not payable + require ext_code.size(stor0) + call stor0.sweeperOf(address token) with: + gas gas_remaining - 710 wei + args _token + require ext_call.success + delegate addr(ext_call.return_data[0]) with: + funct call.data[0 len 4] + gas gas_remaining - 710 wei + args call.data[4 len calldata.size - 4] + require delegate.return_code + return bool(delegate.return_data[0]) + diff --git a/benchmark/0x6025F65f6b2f93d8eD1efeDc752acfd4bdbCec3E.pan b/benchmark/0x6025F65f6b2f93d8eD1efeDc752acfd4bdbCec3E.pan new file mode 100644 index 00000000..0e90fbfd --- /dev/null +++ b/benchmark/0x6025F65f6b2f93d8eD1efeDc752acfd4bdbCec3E.pan @@ -0,0 +1,102 @@ +# +# Panoramix 4 Oct 2019 +# Decompiled source of 0x6025F65f6b2f93d8eD1efeDc752acfd4bdbCec3E +# +# Let's make the world open source +#  + +def storage: + balanceOf is mapping of uint256 at storage 0 + allowance is mapping of uint256 at storage 1 + totalSupply is uint256 at storage 2 + name is array of uint256 at storage 3 + decimals is uint8 at storage 4 + symbol is array of uint256 at storage 5 + version is array of uint256 at storage 6 + +def name() payable: + return name[0 len name.length] + +def totalSupply() payable: + return totalSupply + +def decimals() payable: + return decimals + +def version() payable: + return version[0 len version.length] + +def balanceOf(address _owner) payable: + return balanceOf[addr(_owner)] + +def symbol() payable: + return symbol[0 len symbol.length] + +def allowance(address _owner, address _spender) payable: + return allowance[addr(_owner)][addr(_spender)] + +# +# Regular functions +# + +def _fallback() payable: # default function + revert + +def approve(address _spender, uint256 _value) payable: + allowance[caller][addr(_spender)] = _value + log Approval( +  address owner=_value, +  address spender=caller, +  uint256 value=_spender) + return 1 + +def transfer(address _to, uint256 _value) payable: + if balanceOf[caller] < _value: + return 0 + if _value <= 0: + return 0 + balanceOf[caller] -= _value + balanceOf[_to] += _value + log Transfer( +  address from=_value, +  address to=caller, +  uint256 value=_to) + return 1 + +def transferFrom(address _from, address _to, uint256 _value) payable: + if balanceOf[addr(_from)] < _value: + return 0 + if allowance[addr(_from)][caller] < _value: + return 0 + if _value <= 0: + return 0 + balanceOf[addr(_to)] += _value + balanceOf[_from] -= _value + allowance[addr(_from)][caller] -= _value + log Transfer( +  address from=_value, +  address to=_from, +  uint256 value=_to) + return 1 + +def approveAndCall(address _spender, uint256 _value, bytes _data) payable: + allowance[caller][addr(_spender)] = _value + log Approval( +  address owner=_value, +  address spender=caller, +  uint256 value=_spender) + mem[ceil32(_data.length) + 228 len _data.length] = _data[all] + if not _data.length % 32: + call _spender with: + funct Mask(32, 224, sha3('receiveApproval(address,uint256,', 'address,bytes)')) >> 224 + gas gas_remaining - 25050 wei + args caller, _value, addr(this.address), _data[all] + else: + mem[floor32(_data.length) + ceil32(_data.length) + 228] = mem[floor32(_data.length) + ceil32(_data.length) + -(_data.length % 32) + 260 len _data.length % 32] + call _spender with: + funct Mask(32, 224, sha3('receiveApproval(address,uint256,', 'address,bytes)')) >> 224 + gas gas_remaining - 25050 wei + args caller, _value, addr(this.address), _data[all], mem[ceil32(_data.length) + _data.length + 228 len -(_data.length % 32) + 32] + require ext_call.success + return 1 + diff --git a/benchmark/0x68Cb858247ef5c4A0D0Cde9d6F68Dce93e49c02A.pan b/benchmark/0x68Cb858247ef5c4A0D0Cde9d6F68Dce93e49c02A.pan new file mode 100644 index 00000000..1cc7d344 --- /dev/null +++ b/benchmark/0x68Cb858247ef5c4A0D0Cde9d6F68Dce93e49c02A.pan @@ -0,0 +1,71 @@ +# +# Panoramix 4 Oct 2019 +# Decompiled source of 0x68Cb858247ef5c4A0D0Cde9d6F68Dce93e49c02A +# +# Let's make the world open source +#  + +def storage: + unknown74e3fb3e is array of uint256 at storage 0 + owners is array of addr at storage 1 + +def owners(uint256 _param1): # not payable + require calldata.size - 4 >= 32 + require _param1 < owners.length + return owners[_param1] + +def unknown74e3fb3e(uint256 _param1): # not payable + require calldata.size - 4 >= 32 + require _param1 < unknown74e3fb3e.length + return unknown74e3fb3e[_param1] + +# +# Regular functions +# + +def _fallback() payable: # default function + revert + +def unknown4214352d(uint256 _param1, uint256 _param2): # not payable + require calldata.size - 4 >= 64 + require _param2 < unknown74e3fb3e.length + unknown74e3fb3e[_param2] = _param1 + +def unknown2918435f(addr _param1) payable: + require calldata.size - 4 >= 32 + idx = 0 + s = 0 + while idx < owners.length: + mem[0] = 1 + if owners[idx] != caller: + idx = idx + 1 + s = s + continue  + idx = idx + 1 + s = 1 + continue  + require s + mem[64] = ceil32(ext_code.size(_param1)) + 97 + mem[96] = ext_code.size(_param1) + mem[128 len ext_code.size(_param1)] = ext_code.copy(_param1, 0 len ext_code.size(_param1)) + idx = 0 + while idx < ext_code.size(_param1): + require idx < ext_code.size(_param1) + require Mask(8, 248, mem[idx + 128]) != 0xf000000000000000000000000000000000000000000000000000000000000000 + require idx < ext_code.size(_param1) + require Mask(8, 248, mem[idx + 128]) != 0xf100000000000000000000000000000000000000000000000000000000000000 + require idx < ext_code.size(_param1) + require Mask(8, 248, mem[idx + 128]) != 0xf200000000000000000000000000000000000000000000000000000000000000 + require idx < ext_code.size(_param1) + require Mask(8, 248, mem[idx + 128]) != 0xf400000000000000000000000000000000000000000000000000000000000000 + require idx < ext_code.size(_param1) + require Mask(8, 248, mem[idx + 128]) != 0xfa00000000000000000000000000000000000000000000000000000000000000 + require idx < ext_code.size(_param1) + require Mask(8, 248, mem[idx + 128]) != 0xff00000000000000000000000000000000000000000000000000000000000000 + idx = idx + 1 + continue  + delegate _param1 with: + gas gas_remaining wei + args + require delegate.return_code + diff --git a/benchmark/0x7b220AC85B7ae8Af1CECCC44e183A862dA2eD517.pan b/benchmark/0x7b220AC85B7ae8Af1CECCC44e183A862dA2eD517.pan new file mode 100644 index 00000000..ae3e6e8f --- /dev/null +++ b/benchmark/0x7b220AC85B7ae8Af1CECCC44e183A862dA2eD517.pan @@ -0,0 +1,1070 @@ +# +# Panoramix 4 Oct 2019 +# Decompiled source of 0x7b220AC85B7ae8Af1CECCC44e183A862dA2eD517 +# +# Let's make the world open source +#  + +def storage: + addressToAbility is mapping of bool at storage 0 + proxies is array of addr at storage 1 + stor2 is mapping of uint8 at storage 2 + stor3 is mapping of uint8 at storage 3 + +def addressToAbility(address _param1) payable: + require calldata.size - 4 >=′ 32 + return uint256(addressToAbility[_param1]) + +def orderCancelled(bytes32 _param1) payable: + require calldata.size - 4 >=′ 32 + return bool(stor2[_param1]) + +def proxies(uint256 _param1) payable: + require calldata.size - 4 >=′ 32 + require _param1 < proxies.length + return proxies[_param1] + +def orderPerformed(bytes32 _param1) payable: + require calldata.size - 4 >=′ 32 + return bool(stor3[_param1]) + +# +# Regular functions +# + +def _fallback() payable: # default function + revert + +def isAble(address _target, uint256 _abilities) payable: + require calldata.size - 4 >=′ 64 + if not _abilities: + revert with 0, '', 0 + return (_abilities == _abilities and uint256(addressToAbility[addr(_target)])) + +def removeProxy(uint256 _index) payable: + require calldata.size - 4 >=′ 32 + if Mask(1, 1, uint256(addressToAbility[caller])) != 2: + revert with 0, '', 0 + require _index < proxies.length + proxies[_index] = 0 + log ProxyChange( +  uint256 index=0, +  address proxy=_index) + +def addProxy(address _proxy) payable: + require calldata.size - 4 >=′ 32 + if Mask(1, 1, uint256(addressToAbility[caller])) != 2: + revert with 0, '', 0 + proxies.length++ + proxies[proxies.length] = _proxy + log ProxyChange( +  uint256 index=_proxy, +  address proxy=proxies.length) + +def grantAbilities(address _target, uint256 _abilities) payable: + require calldata.size - 4 >=′ 64 + if bool(addressToAbility[caller]) != 1: + revert with 0, '', 0 + uint256(addressToAbility[addr(_target)]) = _abilities or uint256(addressToAbility[addr(_target)]) + log GrantAbilities( +  address target=_target, +  uint256 abilities=_abilities) + +def revokeAbilities(address _target, uint256 _abilities, bool _allowSuperRevoke) payable: + require calldata.size - 4 >=′ 96 + if bool(addressToAbility[caller]) != 1: + revert with 0, '', 0 + if not _allowSuperRevoke: + if _target == caller: + if bool(_abilities): + revert with 0, '', 0 + uint256(addressToAbility[addr(_target)]) = !_abilities and uint256(addressToAbility[addr(_target)]) + log RevokeAbilities( +  address target=_target, +  uint256 abilities=_abilities) + +def isValidSignature(address _signer, bytes32 _claim, tuple _signature) payable: + require calldata.size - 4 >=′ 192 + require calldata.size - 68 >=′ 128 + require cd[164] < 3 + require cd[164] <= 2 + if cd[164]: + require cd[164] <= 2 + if cd[164] != 1: + require cd[164] <= 2 + if cd[164] != 2: + revert with 0, '', 0 + else: + signer = erecover(_claim, cd[132] << 248, _signature, cd[100]) # precompiled + if not erecover.result: + revert with ext_call.return_data[0 len return_data.size] + else: + return (_signer == addr(signer)) + else: + signer = erecover(sha3(0x19457468657265756d205369676e6564204d65737361, _claim), cd[132] << 248, _signature, cd[100]) # precompiled + if not erecover.result: + revert with ext_call.return_data[0 len return_data.size] + else: + return (_signer == addr(signer)) + else: + signer = erecover(sha3(Mask(224, 32, '\x19Ethereum Signed Message:\n32') >> 32, _claim), cd[132] << 248, _signature, cd[100]) # precompiled + if not erecover.result: + revert with ext_call.return_data[0 len return_data.size] + else: + return (_signer == addr(signer)) + +def getOrderDataClaim(tuple _orderData) payable: + require calldata.size - 4 >=′ 32 + require _orderData <= 18446744073709551615 + require calldata.size + -_orderData - 4 >=′ 160 + mem[96] = addr(_orderData.length) + mem[128] = addr(cd[(_orderData + 36)]) + require cd[(_orderData + 68)] <= 18446744073709551615 + require calldata.size >′ _orderData + cd[(_orderData + 68)] + 35 + require cd[(_orderData + cd[(_orderData + 68)] + 4)] <= 18446744073709551615 + require (32 * cd[(_orderData + cd[(_orderData + 68)] + 4)]) + 288 >= 256 and (32 * cd[(_orderData + cd[(_orderData + 68)] + 4)]) + 288 <= 18446744073709551615 + mem[64] = (32 * cd[(_orderData + cd[(_orderData + 68)] + 4)]) + 288 + mem[256] = cd[(_orderData + cd[(_orderData + 68)] + 4)] + require _orderData + cd[(_orderData + 68)] + (192 * cd[(_orderData + cd[(_orderData + 68)] + 4)]) + 36 <= calldata.size + idx = 0 + s = _orderData + cd[(_orderData + 68)] + 36 + t = 288 + while idx < cd[(_orderData + cd[(_orderData + 68)] + 4)]: + require calldata.size - s >=′ 192 + _44 = mem[64] + require mem[64] + 192 >= mem[64] and mem[64] + 192 <= 18446744073709551615 + mem[64] = mem[64] + 192 + require cd[s] < 2 + mem[_44] = cd[s] + mem[_44 + 32] = uint32(cd[(s + 32)]) + mem[_44 + 64] = addr(cd[(s + 64)]) + mem[_44 + 96] = cd[(s + 96)] + mem[_44 + 128] = addr(cd[(s + 128)]) + mem[_44 + 160] = cd[(s + 160)] + mem[t] = _44 + idx = idx + 1 + s = s + 192 + t = t + 32 + continue  + mem[160] = 256 + mem[192] = cd[(_orderData + 100)] + mem[224] = cd[(_orderData + 132)] + _86 = mem[256] + idx = 0 + s = 0 + while idx < _86: + require idx < mem[mem[160]] + _95 = mem[mem[mem[160] + (32 * idx) + 32]] + require idx < mem[mem[160]] + _99 = mem[mem[(32 * idx) + mem[160] + 32] + 32] + require idx < mem[mem[160]] + _103 = mem[mem[(32 * idx) + mem[160] + 32] + 64] + require idx < mem[mem[160]] + _107 = mem[mem[(32 * idx) + mem[160] + 32] + 96] + require idx < mem[mem[160]] + _111 = mem[mem[(32 * idx) + mem[160] + 32] + 128] + require idx < mem[mem[160]] + _115 = mem[mem[(32 * idx) + mem[160] + 32] + 160] + _116 = mem[64] + mem[mem[64] + 32] = s + require _95 < 2 + mem[mem[64] + 64] = _95 << 248 + mem[mem[64] + 65] = _99 << 224 + mem[_116 + 69] = addr(_103) + mem[_116 + 89] = _107 + mem[_116 + 121] = addr(_111) + mem[_116 + 141] = _115 + _122 = mem[64] + mem[mem[64]] = _116 + -mem[64] + 141 + mem[64] = _116 + 173 + _86 = mem[mem[160]] + idx = idx + 1 + s = sha3(mem[_122 + 32 len mem[_122]]) + continue  + _88 = mem[128] + _89 = mem[192] + _90 = mem[224] + _91 = mem[64] + mem[mem[64] + 32] = addr(this.address) + mem[mem[64] + 52] = addr(mem[96]) + mem[mem[64] + 72] = addr(_88) + mem[mem[64] + 92] = _124 * _86 + mem[mem[64] + 124] = _89 + mem[mem[64] + 156] = _90 + _117 = mem[64] + mem[mem[64]] = 156 + mem[64] = mem[64] + 188 + mem[_91 + 188] = sha3(mem[_117 + 32 len mem[_117]]) + return memory + from _91 + 188 + len 32 + +def cancel(tuple _data) payable: + require calldata.size - 4 >=′ 32 + require _data <= 18446744073709551615 + require calldata.size + -_data - 4 >=′ 160 + mem[96] = addr(_data.length) + mem[128] = addr(cd[(_data + 36)]) + require cd[(_data + 68)] <= 18446744073709551615 + require calldata.size >′ _data + cd[(_data + 68)] + 35 + require cd[(_data + cd[(_data + 68)] + 4)] <= 18446744073709551615 + require (32 * cd[(_data + cd[(_data + 68)] + 4)]) + 288 >= 256 and (32 * cd[(_data + cd[(_data + 68)] + 4)]) + 288 <= 18446744073709551615 + mem[64] = (32 * cd[(_data + cd[(_data + 68)] + 4)]) + 288 + mem[256] = cd[(_data + cd[(_data + 68)] + 4)] + require _data + cd[(_data + 68)] + (192 * cd[(_data + cd[(_data + 68)] + 4)]) + 36 <= calldata.size + idx = 0 + s = _data + cd[(_data + 68)] + 36 + t = 288 + while idx < cd[(_data + cd[(_data + 68)] + 4)]: + require calldata.size - s >=′ 192 + _61 = mem[64] + require mem[64] + 192 >= mem[64] and mem[64] + 192 <= 18446744073709551615 + mem[64] = mem[64] + 192 + require cd[s] < 2 + mem[_61] = cd[s] + mem[_61 + 32] = uint32(cd[(s + 32)]) + mem[_61 + 64] = addr(cd[(s + 64)]) + mem[_61 + 96] = cd[(s + 96)] + mem[_61 + 128] = addr(cd[(s + 128)]) + mem[_61 + 160] = cd[(s + 160)] + mem[t] = _61 + idx = idx + 1 + s = s + 192 + t = t + 32 + continue  + mem[160] = 256 + mem[192] = cd[(_data + 100)] + mem[224] = cd[(_data + 132)] + _62 = mem[96] + _63 = mem[64] + mem[64] = mem[64] + 64 + mem[_63] = 6 + mem[_63 + 32] = 0x3031353030390000000000000000000000000000000000000000000000000000 + if addr(_62) != caller: + revert with 0, '', 0 + _120 = mem[256] + idx = 0 + s = 0 + while idx < _120: + require idx < mem[mem[160]] + _130 = mem[mem[mem[160] + (32 * idx) + 32]] + require idx < mem[mem[160]] + _134 = mem[mem[(32 * idx) + mem[160] + 32] + 32] + require idx < mem[mem[160]] + _138 = mem[mem[(32 * idx) + mem[160] + 32] + 64] + require idx < mem[mem[160]] + _142 = mem[mem[(32 * idx) + mem[160] + 32] + 96] + require idx < mem[mem[160]] + _146 = mem[mem[(32 * idx) + mem[160] + 32] + 128] + require idx < mem[mem[160]] + _150 = mem[mem[(32 * idx) + mem[160] + 32] + 160] + _151 = mem[64] + mem[mem[64] + 32] = s + require _130 < 2 + mem[mem[64] + 64] = _130 << 248 + mem[mem[64] + 65] = _134 << 224 + mem[_151 + 69] = addr(_138) + mem[_151 + 89] = _142 + mem[_151 + 121] = addr(_146) + mem[_151 + 141] = _150 + _167 = mem[64] + mem[mem[64]] = _151 + -mem[64] + 141 + mem[64] = _151 + 173 + _120 = mem[mem[160]] + idx = idx + 1 + s = sha3(mem[_167 + 32 len mem[_167]]) + continue  + _122 = mem[128] + _123 = mem[192] + _124 = mem[224] + _125 = mem[64] + mem[mem[64] + 32] = addr(this.address) + mem[mem[64] + 52] = addr(mem[96]) + mem[mem[64] + 72] = addr(_122) + mem[mem[64] + 92] = _169 * _120 + mem[mem[64] + 124] = _123 + mem[mem[64] + 156] = _124 + _154 = mem[64] + mem[mem[64]] = 156 + mem[64] = mem[64] + 188 + _156 = sha3(mem[_154 + 32 len mem[_154]]) + mem[0] = sha3(mem[_154 + 32 len mem[_154]]) + mem[_125 + 188] = 6 + mem[_125 + 220] = 0x3031353030380000000000000000000000000000000000000000000000000000 + if stor3[mem[0]]: + revert with 0, '', 0 + stor2[_156] = 1 + mem[_125 + 252] = _156 + log Cancel( +  address maker=_156, +  address taker=mem[108 len 20], +  bytes32 claim=mem[140 len 20]) + +def perform(tuple _data, tuple _signature) payable: + require calldata.size - 4 >=′ 160 + require _data <= 18446744073709551615 + require calldata.size + -_data - 4 >=′ 160 + mem[96] = addr(_data.length) + mem[128] = addr(cd[(_data + 36)]) + require cd[(_data + 68)] <= 18446744073709551615 + require calldata.size >′ _data + cd[(_data + 68)] + 35 + require cd[(_data + cd[(_data + 68)] + 4)] <= 18446744073709551615 + require (32 * cd[(_data + cd[(_data + 68)] + 4)]) + 288 >= 256 and (32 * cd[(_data + cd[(_data + 68)] + 4)]) + 288 <= 18446744073709551615 + mem[64] = (32 * cd[(_data + cd[(_data + 68)] + 4)]) + 288 + mem[256] = cd[(_data + cd[(_data + 68)] + 4)] + require _data + cd[(_data + 68)] + (192 * cd[(_data + cd[(_data + 68)] + 4)]) + 36 <= calldata.size + idx = 0 + s = _data + cd[(_data + 68)] + 36 + t = 288 + while idx < cd[(_data + cd[(_data + 68)] + 4)]: + require calldata.size - s >=′ 192 + _440 = mem[64] + require mem[64] + 192 >= mem[64] and mem[64] + 192 <= 18446744073709551615 + mem[64] = mem[64] + 192 + require cd[s] < 2 + mem[_440] = cd[s] + mem[_440 + 32] = uint32(cd[(s + 32)]) + mem[_440 + 64] = addr(cd[(s + 64)]) + mem[_440 + 96] = cd[(s + 96)] + mem[_440 + 128] = addr(cd[(s + 128)]) + mem[_440 + 160] = cd[(s + 160)] + mem[t] = _440 + idx = idx + 1 + s = s + 192 + t = t + 32 + continue  + mem[160] = 256 + mem[192] = cd[(_data + 100)] + mem[224] = cd[(_data + 132)] + require calldata.size - 36 >=′ 128 + _441 = mem[64] + require mem[64] + 128 >= mem[64] and mem[64] + 128 <= 18446744073709551615 + mem[64] = mem[64] + 128 + mem[_441] = _signature + mem[_441 + 32] = cd[68] + mem[_441 + 64] = uint8(cd[100]) + require cd[132] < 3 + mem[_441 + 96] = cd[132] + _442 = mem[128] + _443 = mem[64] + mem[64] = mem[64] + 64 + mem[_443] = 6 + mem[_443 + 32] = 0x3031353030330000000000000000000000000000000000000000000000000000 + if addr(_442) != caller: + revert with 0, '', 0 + else: + _445 = mem[64] + mem[64] = mem[64] + 64 + mem[_445] = 6 + mem[_445 + 32] = 0x3031353030350000000000000000000000000000000000000000000000000000 + if block.timestamp > cd[(_data + 132)]: + revert with 0, '', 0 + else: + _942 = mem[256] + idx = 0 + s = 0 + while idx < _942: + require idx < mem[mem[160]] + _953 = mem[mem[mem[160] + (32 * idx) + 32]] + require idx < mem[mem[160]] + _957 = mem[mem[(32 * idx) + mem[160] + 32] + 32] + require idx < mem[mem[160]] + _961 = mem[mem[(32 * idx) + mem[160] + 32] + 64] + require idx < mem[mem[160]] + _965 = mem[mem[(32 * idx) + mem[160] + 32] + 96] + require idx < mem[mem[160]] + _969 = mem[mem[(32 * idx) + mem[160] + 32] + 128] + require idx < mem[mem[160]] + _973 = mem[mem[(32 * idx) + mem[160] + 32] + 160] + _974 = mem[64] + mem[mem[64] + 32] = s + require _953 < 2 + mem[mem[64] + 64] = _953 << 248 + mem[mem[64] + 65] = _957 << 224 + mem[_974 + 69] = addr(_961) + mem[_974 + 89] = _965 + mem[_974 + 121] = addr(_969) + mem[_974 + 141] = _973 + _996 = mem[64] + mem[mem[64]] = _974 + -mem[64] + 141 + mem[64] = _974 + 173 + _942 = mem[mem[160]] + idx = idx + 1 + s = sha3(mem[_996 + 32 len mem[_996]]) + continue  + _944 = mem[128] + _945 = mem[192] + _946 = mem[224] + _947 = mem[64] + mem[mem[64] + 32] = addr(this.address) + mem[mem[64] + 52] = addr(mem[96]) + mem[mem[64] + 72] = addr(_944) + mem[mem[64] + 92] = _998 * _942 + mem[mem[64] + 124] = _945 + mem[mem[64] + 156] = _946 + _979 = mem[64] + mem[mem[64]] = 156 + mem[64] = mem[64] + 188 + _981 = sha3(mem[_979 + 32 len mem[_979]]) + require mem[_441 + 96] <= 2 + if mem[_441 + 96]: + require mem[_441 + 96] <= 2 + if mem[_441 + 96] != 1: + require mem[_441 + 96] <= 2 + if mem[_441 + 96] != 2: + revert with 0, '', 0 + else: + _990 = mem[_441 + 64] + _991 = mem[_441] + _992 = mem[_441 + 32] + mem[_947 + 284] = mem[_441] + mem[_947 + 316] = _992 + signer = erecover(_981, _990 << 248, _991, _992) # precompiled + mem[_947 + 188] = signer + if not erecover.result: + revert with ext_call.return_data[0 len return_data.size] + else: + mem[_947 + 220] = 6 + mem[_947 + 252] = 0x3031353030360000000000000000000000000000000000000000000000000000 + if mem[108 len 20] != addr(signer): + revert with 0, '', 0 + else: + mem[_947 + 284] = 6 + mem[_947 + 316] = 0x3031353030370000000000000000000000000000000000000000000000000000 + if stor2[_981]: + revert with 0, '', 0 + else: + mem[64] = _947 + 412 + mem[_947 + 348] = 6 + mem[_947 + 380] = 0x3031353030380000000000000000000000000000000000000000000000000000 + if stor3[_981]: + revert with 0, '', 0 + else: + mem[0] = _981 + mem[32] = 3 + stor3[_981] = 1 + _1433 = mem[mem[160]] + idx = 0 + while idx < _1433: + require idx < mem[mem[160]] + _1458 = mem[mem[(32 * idx) + mem[160] + 32] + 32] + require mem[mem[(32 * idx) + mem[160] + 32] + 60 len 4] < proxies.length + mem[0] = 1 + _1470 = mem[64] + mem[64] = mem[64] + 64 + mem[_1470] = 6 + mem[_1470 + 32] = 0x3031353030320000000000000000000000000000000000000000000000000000 + if proxies[uint32(_1458)]: + require idx < mem[mem[160]] + require mem[mem[mem[160] + (32 * idx) + 32]] <= 1 + if mem[mem[mem[160] + (32 * idx) + 32]]: + require idx < mem[mem[160]] + require mem[mem[mem[160] + (32 * idx) + 32]] <= 1 + if mem[mem[mem[160] + (32 * idx) + 32]] != 1: + _1433 = mem[mem[160]] + idx = idx + 1 + continue  + else: + require idx < mem[mem[160]] + _1553 = mem[mem[(32 * idx) + mem[160] + 32] + 96] + if mem[mem[(32 * idx) + mem[160] + 32] + 108 len 8] == mem[108 len 20]: + _1561 = mem[64] + mem[64] = mem[64] + 64 + mem[_1561] = 6 + mem[_1561 + 32] = 0x3031353030340000000000000000000000000000000000000000000000000000 + require idx < mem[mem[160]] + _1580 = mem[mem[(32 * idx) + mem[160] + 32] + 32] + require mem[mem[(32 * idx) + mem[160] + 32] + 60 len 4] < proxies.length + mem[0] = 1 + require idx < mem[mem[160]] + _1610 = mem[mem[(32 * idx) + mem[160] + 32] + 64] + require idx < mem[mem[160]] + _1631 = mem[mem[(32 * idx) + mem[160] + 32] + 128] + require idx < mem[mem[160]] + _1658 = mem[mem[(32 * idx) + mem[160] + 32] + 160] + mem[mem[64]] = 0x239aee0600000000000000000000000000000000000000000000000000000000 + mem[mem[64] + 4] = addr(_1610) + mem[mem[64] + 36] = Mask(64, 96, _1553) >> 96 + mem[mem[64] + 68] = addr(_1631) + mem[mem[64] + 100] = _1658 + require ext_code.size(proxies[uint32(_1580)]) + call proxies[uint32(_1580)].0x239aee06 with: + gas gas_remaining wei + args addr(_1610), Mask(64, 96, _1553) << 96, addr(_1631), _1658 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + else: + _1433 = mem[mem[160]] + idx = idx + 1 + continue  + else: + _1562 = mem[128] + _1569 = mem[64] + mem[64] = mem[64] + 64 + mem[_1569] = 6 + mem[_1569 + 32] = 0x3031353030340000000000000000000000000000000000000000000000000000 + if Mask(64, 96, _1553) >> 96 == addr(_1562): + require idx < mem[mem[160]] + _1598 = mem[mem[(32 * idx) + mem[160] + 32] + 32] + require mem[mem[(32 * idx) + mem[160] + 32] + 60 len 4] < proxies.length + mem[0] = 1 + require idx < mem[mem[160]] + _1635 = mem[mem[(32 * idx) + mem[160] + 32] + 64] + require idx < mem[mem[160]] + _1661 = mem[mem[(32 * idx) + mem[160] + 32] + 128] + require idx < mem[mem[160]] + _1679 = mem[mem[(32 * idx) + mem[160] + 32] + 160] + mem[mem[64]] = 0x239aee0600000000000000000000000000000000000000000000000000000000 + mem[mem[64] + 4] = addr(_1635) + mem[mem[64] + 36] = Mask(64, 96, _1553) >> 96 + mem[mem[64] + 68] = addr(_1661) + mem[mem[64] + 100] = _1679 + require ext_code.size(proxies[uint32(_1598)]) + call proxies[uint32(_1598)].0x239aee06 with: + gas gas_remaining wei + args addr(_1635), Mask(64, 96, _1553) << 96, addr(_1661), _1679 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + else: + _1433 = mem[mem[160]] + idx = idx + 1 + continue  + else: + _1583 = mem[64] + mem[mem[64]] = 0x8c379a000000000000000000000000000000000000000000000000000000000 + mem[mem[64] + 4] = 32 + mem[mem[64] + 36] = 6 + idx = 0 + while idx < 6: + mem[idx + _1583 + 68] = mem[idx + _1569 + 32] + _1433 = mem[mem[160]] + idx = idx + 32 + continue  + mem[_1583 + 74] = 0 + revert with memory + from mem[64] + len _1583 + -mem[64] + 100 + else: + require idx < mem[mem[160]] + _1520 = mem[mem[(32 * idx) + mem[160] + 32] + 64] + mem[mem[64] + 4] = mem[108 len 20] + mem[mem[64] + 36] = 32 + require ext_code.size(addr(_1520)) + static call addr(_1520).isAble(address target, uint256 abilities) with: + gas gas_remaining wei + args mem[mem[64] + 4], 32 + mem[mem[64]] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + else: + _1699 = mem[64] + mem[64] = mem[64] + ceil32(return_data.size) + require return_data.size >=′ 32 + _1702 = mem[_1699] + _1711 = mem[64] + mem[64] = mem[64] + 64 + mem[_1711] = 6 + mem[_1711 + 32] = 0x3031353031300000000000000000000000000000000000000000000000000000 + if _1702: + require idx < mem[mem[160]] + _1724 = mem[mem[(32 * idx) + mem[160] + 32] + 32] + require mem[mem[(32 * idx) + mem[160] + 32] + 60 len 4] < proxies.length + mem[0] = 1 + require idx < mem[mem[160]] + _1739 = mem[mem[(32 * idx) + mem[160] + 32] + 64] + require idx < mem[mem[160]] + _1754 = mem[mem[(32 * idx) + mem[160] + 32] + 128] + require idx < mem[mem[160]] + _1766 = mem[mem[(32 * idx) + mem[160] + 32] + 160] + require idx < mem[mem[160]] + _1778 = mem[mem[(32 * idx) + mem[160] + 32] + 96] + mem[mem[64]] = 0x46009b0e00000000000000000000000000000000000000000000000000000000 + mem[mem[64] + 4] = addr(_1739) + mem[mem[64] + 36] = addr(_1754) + mem[mem[64] + 68] = _1766 + mem[mem[64] + 100] = _1778 + require ext_code.size(proxies[uint32(_1724)]) + call proxies[uint32(_1724)].0x46009b0e with: + gas gas_remaining wei + args addr(_1739), addr(_1754), _1766, _1778 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + else: + _1433 = mem[mem[160]] + idx = idx + 1 + continue  + else: + _1716 = mem[64] + mem[mem[64]] = 0x8c379a000000000000000000000000000000000000000000000000000000000 + mem[mem[64] + 4] = 32 + mem[mem[64] + 36] = 6 + idx = 0 + while idx < 6: + mem[idx + _1716 + 68] = mem[idx + _1711 + 32] + _1433 = mem[mem[160]] + idx = idx + 32 + continue  + mem[_1716 + 74] = 0 + revert with memory + from mem[64] + len _1716 + -mem[64] + 100 + else: + _1477 = mem[64] + mem[mem[64]] = 0x8c379a000000000000000000000000000000000000000000000000000000000 + mem[mem[64] + 4] = 32 + mem[mem[64] + 36] = 6 + idx = 0 + while idx < 6: + mem[idx + _1477 + 68] = mem[idx + _1470 + 32] + _1433 = mem[mem[160]] + idx = idx + 32 + continue  + mem[_1477 + 74] = 0 + revert with memory + from mem[64] + len _1477 + -mem[64] + 100 + log Perform( +  address maker=_981, +  address taker=mem[108 len 20], +  bytes32 claim=mem[140 len 20]) + stop + else: + mem[_947 + 220] = 0x19457468657265756d205369676e6564204d6573736167653a0a200000000000 + mem[_947 + 247] = _981 + mem[_947 + 188] = 59 + mem[64] = _947 + 279 + _1011 = mem[_441 + 64] + _1012 = mem[_441] + _1013 = mem[_441 + 32] + mem[_947 + 375] = mem[_441] + mem[_947 + 407] = _1013 + signer = erecover(sha3(0x19457468657265756d205369676e6564204d65737361, _981), _1011 << 248, _1012, _1013) # precompiled + mem[_947 + 279] = signer + if not erecover.result: + revert with ext_call.return_data[0 len return_data.size] + else: + mem[_947 + 311] = 6 + mem[_947 + 343] = 0x3031353030360000000000000000000000000000000000000000000000000000 + if mem[108 len 20] != addr(signer): + revert with 0, '', 0 + else: + mem[_947 + 375] = 6 + mem[_947 + 407] = 0x3031353030370000000000000000000000000000000000000000000000000000 + if stor2[_981]: + revert with 0, '', 0 + else: + mem[64] = _947 + 503 + mem[_947 + 439] = 6 + mem[_947 + 471] = 0x3031353030380000000000000000000000000000000000000000000000000000 + if stor3[_981]: + revert with 0, '', 0 + else: + mem[0] = _981 + mem[32] = 3 + stor3[_981] = 1 + _1435 = mem[mem[160]] + idx = 0 + while idx < _1435: + require idx < mem[mem[160]] + _1463 = mem[mem[(32 * idx) + mem[160] + 32] + 32] + require mem[mem[(32 * idx) + mem[160] + 32] + 60 len 4] < proxies.length + mem[0] = 1 + _1472 = mem[64] + mem[64] = mem[64] + 64 + mem[_1472] = 6 + mem[_1472 + 32] = 0x3031353030320000000000000000000000000000000000000000000000000000 + if proxies[uint32(_1463)]: + require idx < mem[mem[160]] + require mem[mem[mem[160] + (32 * idx) + 32]] <= 1 + if mem[mem[mem[160] + (32 * idx) + 32]]: + require idx < mem[mem[160]] + require mem[mem[mem[160] + (32 * idx) + 32]] <= 1 + if mem[mem[mem[160] + (32 * idx) + 32]] != 1: + _1435 = mem[mem[160]] + idx = idx + 1 + continue  + else: + require idx < mem[mem[160]] + _1556 = mem[mem[(32 * idx) + mem[160] + 32] + 96] + if mem[mem[(32 * idx) + mem[160] + 32] + 108 len 8] == mem[108 len 20]: + _1563 = mem[64] + mem[64] = mem[64] + 64 + mem[_1563] = 6 + mem[_1563 + 32] = 0x3031353030340000000000000000000000000000000000000000000000000000 + require idx < mem[mem[160]] + _1585 = mem[mem[(32 * idx) + mem[160] + 32] + 32] + require mem[mem[(32 * idx) + mem[160] + 32] + 60 len 4] < proxies.length + mem[0] = 1 + require idx < mem[mem[160]] + _1617 = mem[mem[(32 * idx) + mem[160] + 32] + 64] + require idx < mem[mem[160]] + _1640 = mem[mem[(32 * idx) + mem[160] + 32] + 128] + require idx < mem[mem[160]] + _1665 = mem[mem[(32 * idx) + mem[160] + 32] + 160] + mem[mem[64]] = 0x239aee0600000000000000000000000000000000000000000000000000000000 + mem[mem[64] + 4] = addr(_1617) + mem[mem[64] + 36] = Mask(64, 96, _1556) >> 96 + mem[mem[64] + 68] = addr(_1640) + mem[mem[64] + 100] = _1665 + require ext_code.size(proxies[uint32(_1585)]) + call proxies[uint32(_1585)].0x239aee06 with: + gas gas_remaining wei + args addr(_1617), Mask(64, 96, _1556) << 96, addr(_1640), _1665 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + else: + _1435 = mem[mem[160]] + idx = idx + 1 + continue  + else: + _1564 = mem[128] + _1573 = mem[64] + mem[64] = mem[64] + 64 + mem[_1573] = 6 + mem[_1573 + 32] = 0x3031353030340000000000000000000000000000000000000000000000000000 + if Mask(64, 96, _1556) >> 96 == addr(_1564): + require idx < mem[mem[160]] + _1603 = mem[mem[(32 * idx) + mem[160] + 32] + 32] + require mem[mem[(32 * idx) + mem[160] + 32] + 60 len 4] < proxies.length + mem[0] = 1 + require idx < mem[mem[160]] + _1644 = mem[mem[(32 * idx) + mem[160] + 32] + 64] + require idx < mem[mem[160]] + _1668 = mem[mem[(32 * idx) + mem[160] + 32] + 128] + require idx < mem[mem[160]] + _1683 = mem[mem[(32 * idx) + mem[160] + 32] + 160] + mem[mem[64]] = 0x239aee0600000000000000000000000000000000000000000000000000000000 + mem[mem[64] + 4] = addr(_1644) + mem[mem[64] + 36] = Mask(64, 96, _1556) >> 96 + mem[mem[64] + 68] = addr(_1668) + mem[mem[64] + 100] = _1683 + require ext_code.size(proxies[uint32(_1603)]) + call proxies[uint32(_1603)].0x239aee06 with: + gas gas_remaining wei + args addr(_1644), Mask(64, 96, _1556) << 96, addr(_1668), _1683 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + else: + _1435 = mem[mem[160]] + idx = idx + 1 + continue  + else: + _1588 = mem[64] + mem[mem[64]] = 0x8c379a000000000000000000000000000000000000000000000000000000000 + mem[mem[64] + 4] = 32 + mem[mem[64] + 36] = 6 + idx = 0 + while idx < 6: + mem[idx + _1588 + 68] = mem[idx + _1573 + 32] + _1435 = mem[mem[160]] + idx = idx + 32 + continue  + mem[_1588 + 74] = 0 + revert with memory + from mem[64] + len _1588 + -mem[64] + 100 + else: + require idx < mem[mem[160]] + _1530 = mem[mem[(32 * idx) + mem[160] + 32] + 64] + mem[mem[64] + 4] = mem[108 len 20] + mem[mem[64] + 36] = 32 + require ext_code.size(addr(_1530)) + static call addr(_1530).isAble(address target, uint256 abilities) with: + gas gas_remaining wei + args mem[mem[64] + 4], 32 + mem[mem[64]] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + else: + _1700 = mem[64] + mem[64] = mem[64] + ceil32(return_data.size) + require return_data.size >=′ 32 + _1703 = mem[_1700] + _1712 = mem[64] + mem[64] = mem[64] + 64 + mem[_1712] = 6 + mem[_1712 + 32] = 0x3031353031300000000000000000000000000000000000000000000000000000 + if _1703: + require idx < mem[mem[160]] + _1726 = mem[mem[(32 * idx) + mem[160] + 32] + 32] + require mem[mem[(32 * idx) + mem[160] + 32] + 60 len 4] < proxies.length + mem[0] = 1 + require idx < mem[mem[160]] + _1744 = mem[mem[(32 * idx) + mem[160] + 32] + 64] + require idx < mem[mem[160]] + _1758 = mem[mem[(32 * idx) + mem[160] + 32] + 128] + require idx < mem[mem[160]] + _1770 = mem[mem[(32 * idx) + mem[160] + 32] + 160] + require idx < mem[mem[160]] + _1781 = mem[mem[(32 * idx) + mem[160] + 32] + 96] + mem[mem[64]] = 0x46009b0e00000000000000000000000000000000000000000000000000000000 + mem[mem[64] + 4] = addr(_1744) + mem[mem[64] + 36] = addr(_1758) + mem[mem[64] + 68] = _1770 + mem[mem[64] + 100] = _1781 + require ext_code.size(proxies[uint32(_1726)]) + call proxies[uint32(_1726)].0x46009b0e with: + gas gas_remaining wei + args addr(_1744), addr(_1758), _1770, _1781 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + else: + _1435 = mem[mem[160]] + idx = idx + 1 + continue  + else: + _1719 = mem[64] + mem[mem[64]] = 0x8c379a000000000000000000000000000000000000000000000000000000000 + mem[mem[64] + 4] = 32 + mem[mem[64] + 36] = 6 + idx = 0 + while idx < 6: + mem[idx + _1719 + 68] = mem[idx + _1712 + 32] + _1435 = mem[mem[160]] + idx = idx + 32 + continue  + mem[_1719 + 74] = 0 + revert with memory + from mem[64] + len _1719 + -mem[64] + 100 + else: + _1480 = mem[64] + mem[mem[64]] = 0x8c379a000000000000000000000000000000000000000000000000000000000 + mem[mem[64] + 4] = 32 + mem[mem[64] + 36] = 6 + idx = 0 + while idx < 6: + mem[idx + _1480 + 68] = mem[idx + _1472 + 32] + _1435 = mem[mem[160]] + idx = idx + 32 + continue  + mem[_1480 + 74] = 0 + revert with memory + from mem[64] + len _1480 + -mem[64] + 100 + log Perform( +  address maker=_981, +  address taker=mem[108 len 20], +  bytes32 claim=mem[140 len 20]) + stop + else: + mem[_947 + 220] = '\x19Ethereum Signed Message:\n32' + mem[_947 + 248] = _981 + mem[_947 + 188] = 60 + mem[64] = _947 + 280 + _1002 = mem[_441 + 64] + _1003 = mem[_441] + _1004 = mem[_441 + 32] + mem[_947 + 376] = mem[_441] + mem[_947 + 408] = _1004 + signer = erecover(sha3(Mask(224, 32, '\x19Ethereum Signed Message:\n32') >> 32, _981), _1002 << 248, _1003, _1004) # precompiled + mem[_947 + 280] = signer + if not erecover.result: + revert with ext_call.return_data[0 len return_data.size] + else: + mem[_947 + 312] = 6 + mem[_947 + 344] = 0x3031353030360000000000000000000000000000000000000000000000000000 + if mem[108 len 20] != addr(signer): + revert with 0, '', 0 + else: + mem[_947 + 376] = 6 + mem[_947 + 408] = 0x3031353030370000000000000000000000000000000000000000000000000000 + if not stor2[_981]: + mem[64] = _947 + 504 + mem[_947 + 440] = 6 + mem[_947 + 472] = 0x3031353030380000000000000000000000000000000000000000000000000000 + if stor3[_981]: + revert with 0, '', 0 + else: + mem[0] = _981 + mem[32] = 3 + stor3[_981] = 1 + _1437 = mem[mem[160]] + idx = 0 + while idx < _1437: + require idx < mem[mem[160]] + _1468 = mem[mem[(32 * idx) + mem[160] + 32] + 32] + require mem[mem[(32 * idx) + mem[160] + 32] + 60 len 4] < proxies.length + mem[0] = 1 + _1474 = mem[64] + mem[64] = mem[64] + 64 + mem[_1474] = 6 + mem[_1474 + 32] = 0x3031353030320000000000000000000000000000000000000000000000000000 + if proxies[uint32(_1468)]: + require idx < mem[mem[160]] + require mem[mem[mem[160] + (32 * idx) + 32]] <= 1 + if mem[mem[mem[160] + (32 * idx) + 32]]: + require idx < mem[mem[160]] + require mem[mem[mem[160] + (32 * idx) + 32]] <= 1 + if mem[mem[mem[160] + (32 * idx) + 32]] != 1: + _1437 = mem[mem[160]] + idx = idx + 1 + continue  + else: + require idx < mem[mem[160]] + _1559 = mem[mem[(32 * idx) + mem[160] + 32] + 96] + if mem[mem[(32 * idx) + mem[160] + 32] + 108 len 8] == mem[108 len 20]: + _1565 = mem[64] + mem[64] = mem[64] + 64 + mem[_1565] = 6 + mem[_1565 + 32] = 0x3031353030340000000000000000000000000000000000000000000000000000 + require idx < mem[mem[160]] + _1590 = mem[mem[(32 * idx) + mem[160] + 32] + 32] + require mem[mem[(32 * idx) + mem[160] + 32] + 60 len 4] < proxies.length + mem[0] = 1 + require idx < mem[mem[160]] + _1624 = mem[mem[(32 * idx) + mem[160] + 32] + 64] + require idx < mem[mem[160]] + _1649 = mem[mem[(32 * idx) + mem[160] + 32] + 128] + require idx < mem[mem[160]] + _1672 = mem[mem[(32 * idx) + mem[160] + 32] + 160] + mem[mem[64]] = 0x239aee0600000000000000000000000000000000000000000000000000000000 + mem[mem[64] + 4] = addr(_1624) + mem[mem[64] + 36] = Mask(64, 96, _1559) >> 96 + mem[mem[64] + 68] = addr(_1649) + mem[mem[64] + 100] = _1672 + require ext_code.size(proxies[uint32(_1590)]) + call proxies[uint32(_1590)].0x239aee06 with: + gas gas_remaining wei + args addr(_1624), Mask(64, 96, _1559) << 96, addr(_1649), _1672 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + else: + _1437 = mem[mem[160]] + idx = idx + 1 + continue  + else: + _1566 = mem[128] + _1577 = mem[64] + mem[64] = mem[64] + 64 + mem[_1577] = 6 + mem[_1577 + 32] = 0x3031353030340000000000000000000000000000000000000000000000000000 + if Mask(64, 96, _1559) >> 96 == addr(_1566): + require idx < mem[mem[160]] + _1608 = mem[mem[(32 * idx) + mem[160] + 32] + 32] + require mem[mem[(32 * idx) + mem[160] + 32] + 60 len 4] < proxies.length + mem[0] = 1 + require idx < mem[mem[160]] + _1653 = mem[mem[(32 * idx) + mem[160] + 32] + 64] + require idx < mem[mem[160]] + _1675 = mem[mem[(32 * idx) + mem[160] + 32] + 128] + require idx < mem[mem[160]] + _1687 = mem[mem[(32 * idx) + mem[160] + 32] + 160] + mem[mem[64]] = 0x239aee0600000000000000000000000000000000000000000000000000000000 + mem[mem[64] + 4] = addr(_1653) + mem[mem[64] + 36] = Mask(64, 96, _1559) >> 96 + mem[mem[64] + 68] = addr(_1675) + mem[mem[64] + 100] = _1687 + require ext_code.size(proxies[uint32(_1608)]) + call proxies[uint32(_1608)].0x239aee06 with: + gas gas_remaining wei + args addr(_1653), Mask(64, 96, _1559) << 96, addr(_1675), _1687 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + else: + _1437 = mem[mem[160]] + idx = idx + 1 + continue  + else: + _1593 = mem[64] + mem[mem[64]] = 0x8c379a000000000000000000000000000000000000000000000000000000000 + mem[mem[64] + 4] = 32 + mem[mem[64] + 36] = 6 + idx = 0 + while idx < 6: + mem[idx + _1593 + 68] = mem[idx + _1577 + 32] + _1437 = mem[mem[160]] + idx = idx + 32 + continue  + mem[_1593 + 74] = 0 + revert with memory + from mem[64] + len _1593 + -mem[64] + 100 + else: + require idx < mem[mem[160]] + _1540 = mem[mem[(32 * idx) + mem[160] + 32] + 64] + mem[mem[64] + 4] = mem[108 len 20] + mem[mem[64] + 36] = 32 + require ext_code.size(addr(_1540)) + static call addr(_1540).isAble(address target, uint256 abilities) with: + gas gas_remaining wei + args mem[mem[64] + 4], 32 + mem[mem[64]] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + else: + _1701 = mem[64] + mem[64] = mem[64] + ceil32(return_data.size) + require return_data.size >=′ 32 + _1704 = mem[_1701] + _1713 = mem[64] + mem[64] = mem[64] + 64 + mem[_1713] = 6 + mem[_1713 + 32] = 0x3031353031300000000000000000000000000000000000000000000000000000 + if _1704: + require idx < mem[mem[160]] + _1728 = mem[mem[(32 * idx) + mem[160] + 32] + 32] + require mem[mem[(32 * idx) + mem[160] + 32] + 60 len 4] < proxies.length + mem[0] = 1 + require idx < mem[mem[160]] + _1749 = mem[mem[(32 * idx) + mem[160] + 32] + 64] + require idx < mem[mem[160]] + _1762 = mem[mem[(32 * idx) + mem[160] + 32] + 128] + require idx < mem[mem[160]] + _1774 = mem[mem[(32 * idx) + mem[160] + 32] + 160] + require idx < mem[mem[160]] + _1784 = mem[mem[(32 * idx) + mem[160] + 32] + 96] + mem[mem[64]] = 0x46009b0e00000000000000000000000000000000000000000000000000000000 + mem[mem[64] + 4] = addr(_1749) + mem[mem[64] + 36] = addr(_1762) + mem[mem[64] + 68] = _1774 + mem[mem[64] + 100] = _1784 + require ext_code.size(proxies[uint32(_1728)]) + call proxies[uint32(_1728)].0x46009b0e with: + gas gas_remaining wei + args addr(_1749), addr(_1762), _1774, _1784 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + else: + _1437 = mem[mem[160]] + idx = idx + 1 + continue  + else: + _1722 = mem[64] + mem[mem[64]] = 0x8c379a000000000000000000000000000000000000000000000000000000000 + mem[mem[64] + 4] = 32 + mem[mem[64] + 36] = 6 + idx = 0 + while idx < 6: + mem[idx + _1722 + 68] = mem[idx + _1713 + 32] + _1437 = mem[mem[160]] + idx = idx + 32 + continue  + mem[_1722 + 74] = 0 + revert with memory + from mem[64] + len _1722 + -mem[64] + 100 + else: + _1483 = mem[64] + mem[mem[64]] = 0x8c379a000000000000000000000000000000000000000000000000000000000 + mem[mem[64] + 4] = 32 + mem[mem[64] + 36] = 6 + idx = 0 + while idx < 6: + mem[idx + _1483 + 68] = mem[idx + _1474 + 32] + _1437 = mem[mem[160]] + idx = idx + 32 + continue  + mem[_1483 + 74] = 0 + revert with memory + from mem[64] + len _1483 + -mem[64] + 100 + log Perform( +  address maker=_981, +  address taker=mem[108 len 20], +  bytes32 claim=mem[140 len 20]) + stop + else: + var177001 = 32 + revert with 0, '', 0 + diff --git a/benchmark/0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7.pan b/benchmark/0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7.pan new file mode 100644 index 00000000..8771f77a --- /dev/null +++ b/benchmark/0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7.pan @@ -0,0 +1,102 @@ +# +# Panoramix 4 Oct 2019 +# Decompiled source of 0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7 +# +# Let's make the world open source +#  + +def storage: + owner is addr at storage 0 + stor0 is uint256 at storage 0 + name is array of uint256 at storage 1 + symbol is array of uint256 at storage 2 + decimals is uint8 at storage 3 + totalSupply is uint256 at storage 4 + balanceOf is mapping of uint256 at storage 5 + frozenAccount is mapping of uint8 at storage 6 + allowance is mapping of uint256 at storage 7 + spentAllowance is mapping of uint256 at storage 8 + +def name() payable: + return name[0 len name.length] + +def totalSupply() payable: + return totalSupply + +def decimals() payable: + return decimals + +def balanceOf(address _owner) payable: + return balanceOf[_owner] + +def owner() payable: + return addr(owner) + +def symbol() payable: + return symbol[0 len symbol.length] + +def frozenAccount(address _param1) payable: + return uint8(frozenAccount[_param1]) + +def spentAllowance(address _param1, address _param2) payable: + return spentAllowance[_param1][_param2] + +def allowance(address _owner, address _spender) payable: + return allowance[_owner][_spender] + +# +# Regular functions +# + +def _fallback() payable: # default function + revert + +def approve(address _spender, uint256 _value) payable: + allowance[caller][addr(_spender)] = _value + return 0 + +def transferOwnership(address _newOwner) payable: + require addr(owner) == caller + uint256(stor0) = _newOwner or Mask(96, 160, uint256(stor0)) + +def mintToken(address _target, uint256 _amount) payable: + require addr(owner) == caller + balanceOf[addr(_target)] += _amount + totalSupply += _amount + log Transfer( +  address from=_amount, +  address to=addr(owner), +  uint256 value=_target) + +def freezeAccount(address _target, bool _freeze) payable: + require addr(owner) == caller + uint256(frozenAccount[addr(_target)]) = _freeze or Mask(248, 8, uint256(frozenAccount[addr(_target)])) + log FrozenFunds( +  address target=addr(_target), +  bool freeze=_freeze) + +def transfer(address _to, uint256 _value) payable: + require balanceOf[caller] >= _value + require _value + balanceOf[_to] >= balanceOf[_to] + require not uint8(frozenAccount[caller]) + balanceOf[caller] -= _value + balanceOf[addr(_to)] += _value + log Transfer( +  address from=_value, +  address to=caller, +  uint256 value=_to) + +def transferFrom(address _from, address _to, uint256 _value) payable: + require balanceOf[addr(_from)] >= _value + require balanceOf[_to] + _value >= balanceOf[_to] + require not uint8(frozenAccount[addr(_from)]) + require _value + spentAllowance[addr(_from)][caller] <= allowance[addr(_from)][caller] + balanceOf[addr(_from)] -= _value + balanceOf[addr(_to)] += _value + spentAllowance[addr(_from)][caller] += _value + log Transfer( +  address from=_value, +  address to=caller, +  uint256 value=_to) + return 0 + diff --git a/benchmark/0x8d12A197cB00D4747a1fe03395095ce2A5CC6819.pan b/benchmark/0x8d12A197cB00D4747a1fe03395095ce2A5CC6819.pan new file mode 100644 index 00000000..a20d6d10 --- /dev/null +++ b/benchmark/0x8d12A197cB00D4747a1fe03395095ce2A5CC6819.pan @@ -0,0 +1,535 @@ +# +# Panoramix 4 Oct 2019 +# Decompiled source of 0x8d12A197cB00D4747a1fe03395095ce2A5CC6819 +# +# Let's make the world open source +#  + +def storage: + adminAddress is addr at storage 0 + feeAccount is addr at storage 1 + accountLevelsAddr is addr at storage 2 + feeMake is uint256 at storage 3 + feeTake is uint256 at storage 4 + feeRebate is uint256 at storage 5 + tokens is mapping of uint256 at storage 6 + stor7 is mapping of uint8 at storage 7 + orderFills is mapping of uint256 at storage 8 + +def orderFills(address _param1, bytes32 _param2): # not payable + return orderFills[_param1][_param2] + +def amountFilled(address _tokenGet, uint256 _amountGet, address _tokenGive, uint256 _amountGive, uint256 _expires, uint256 _nonce, address _user, uint8 _v, bytes32 _r, bytes32 _s): # not payable + hash = sha256hash(addr(this.address), 0, uint64(_amountGet), _tokenGive, _amountGive, _expires, _nonce) # precompiled + require sha256hash.result + return orderFills[addr(_user)][hash] + +def tokens(address _param1, address _param2): # not payable + return tokens[_param1][_param2] + +def feeMake(): # not payable + return feeMake + +def feeAccount(): # not payable + return feeAccount + +def feeRebate(): # not payable + return feeRebate + +def orders(address _param1, bytes32 _param2): # not payable + return bool(stor7[_param1][_param2]) + +def feeTake(): # not payable + return feeTake + +def accountLevelsAddr(): # not payable + return accountLevelsAddr + +def balanceOf(address _token, address _user): # not payable + return tokens[addr(_token)][addr(_user)] + +def admin(): # not payable + return adminAddress + +# +# Regular functions +# + +def _fallback(): # not payable, default function + revert + +def changeAdmin(address _who): # not payable + require adminAddress == caller + adminAddress = _who + +def changeFeeAccount(address _newWallet): # not payable + require adminAddress == caller + feeAccount = _newWallet + +def changeAccountLevelsAddr(address _accountLevelsAddr): # not payable + require adminAddress == caller + accountLevelsAddr = _accountLevelsAddr_ + +def changeFeeMake(uint256 _feeMake): # not payable + require adminAddress == caller + require _feeMake_ <= feeMake + feeMake = _feeMake_ + +def changeFeeTake(uint256 _newFee): # not payable + require adminAddress == caller + require _newFee <= feeTake + require _newFee >= feeRebate + feeTake = _newFee + +def changeFeeRebate(uint256 _feeRebate): # not payable + require adminAddress == caller + require _feeRebate_ >= feeRebate + require _feeRebate_ <= feeTake + feeRebate = _feeRebate_ + +def deposit() payable: + require call.value + tokens[0][caller] >= tokens[0][caller] + require call.value + tokens[0][caller] >= call.value + tokens[0][caller] += call.value + log Deposit( +  address token=0, +  address user=caller, +  uint256 amount=call.value, +  uint256 balance=call.value + tokens[0][caller]) + +def withdraw(uint256 _amount): # not payable + require tokens[0][caller] >= _amount + require _amount <= tokens[0][caller] + tokens[0][caller] -= _amount + call caller with: + value _amount wei + gas gas_remaining - 34050 wei + require ext_call.success + log Withdraw( +  address token=0, +  address user=caller, +  uint256 amount=_amount, +  uint256 balance=tokens[0][caller]) + +def order(address _tokenGet, uint256 _amountGet, address _tokenGive, uint256 _amountGive, uint256 _expires, uint256 _nonce): # not payable + hash = sha256hash(addr(this.address), 0, uint64(_amountGet), _tokenGive, _amountGive, _expires, _nonce) # precompiled + require sha256hash.result + stor7[caller][hash] = 1 + log Order( +  address tokenGet=_tokenGet, +  uint256 amountGet=0, +  address tokenGive=addr(_tokenGive), +  uint256 amountGive=_amountGive, +  uint256 expires=_expires, +  uint256 nonce=_nonce, +  address user=caller) + +def withdrawToken(address _token, uint256 _amount): # not payable + require _token + require tokens[addr(_token)][caller] >= _amount + require _amount <= tokens[addr(_token)][caller] + tokens[addr(_token)][caller] -= _amount + require ext_code.size(_token) + call _token.transfer(address to, uint256 value) with: + gas gas_remaining - 50 wei + args caller, _amount + require ext_call.success + require ext_call.return_data[0] + log Withdraw( +  address token=addr(_token), +  address user=caller, +  uint256 amount=_amount, +  uint256 balance=tokens[addr(_token)][caller]) + +def depositToken(address _token, uint256 _amount): # not payable + require _token + require ext_code.size(_token) + call _token.transferFrom(address from, address to, uint256 value) with: + gas gas_remaining - 50 wei + args caller, addr(this.address), _amount + require ext_call.success + require ext_call.return_data[0] + require _amount + tokens[addr(_token)][caller] >= tokens[addr(_token)][caller] + require _amount + tokens[addr(_token)][caller] >= _amount + tokens[addr(_token)][caller] += _amount + log Deposit( +  address token=addr(_token), +  address user=caller, +  uint256 amount=_amount, +  uint256 balance=_amount + tokens[addr(_token)][caller]) + +def cancelOrder(address _tokenGet, uint256 _amountGet, address _tokenGive, uint256 _amountGive, uint256 _expires, uint256 _nonce, uint8 _v, bytes32 _r, bytes32 _s): # not payable + hash = sha256hash(addr(this.address), 0, uint64(_amountGet), _tokenGive, _amountGive, _expires, _nonce) # precompiled + require sha256hash.result + if stor7[caller][hash]: + orderFills[caller][hash] = _amountGet + log Cancel( +  address tokenGet=_tokenGet, +  uint256 amountGet=0, +  address tokenGive=addr(_tokenGive), +  uint256 amountGive=_amountGive, +  uint256 expires=_expires, +  uint256 nonce=_nonce, +  address user=caller, +  uint8 v=_v << 248, +  bytes32 r=_r, +  bytes32 s=_s) + else: + signer = erecover(sha3(Mask(224, 32, '\x19Ethereum Signed Message:\n32') >> 32, hash), _v << 248, _r, _s) # precompiled + require erecover.result + require addr(signer) == caller + orderFills[caller][hash] = _amountGet + log Cancel( +  address tokenGet=addr(_tokenGet), +  uint256 amountGet=_amountGet, +  address tokenGive=addr(_tokenGive), +  uint256 amountGive=_amountGive, +  uint256 expires=_expires, +  uint256 nonce=_nonce, +  address user=caller, +  uint8 v=_v << 248, +  bytes32 r=_r, +  bytes32 s=_s) + +def availableVolume(address _tokenGet, uint256 _amountGet, address _tokenGive, uint256 _amountGive, uint256 _expires, uint256 _nonce, address _user, uint8 _v, bytes32 _r, bytes32 _s): # not payable + hash = sha256hash(addr(this.address), 0, uint64(_amountGet), _tokenGive, _amountGive, _expires, _nonce) # precompiled + require sha256hash.result + if not stor7[addr(_user)][hash]: + signer = erecover(sha3(Mask(224, 32, '\x19Ethereum Signed Message:\n32') >> 32, hash), _v << 248, _r, _s) # precompiled + require erecover.result + if addr(signer) != _user: + return 0 + if block.number > _expires: + return 0 + require orderFills[addr(_user)][hash] <= _amountGet + if tokens[addr(_tokenGive)][addr(_user)]: + require tokens[addr(_tokenGive)][addr(_user)] + require _amountGet * tokens[addr(_tokenGive)][addr(_user)] / tokens[addr(_tokenGive)][addr(_user)] == _amountGet + require _amountGive + if _amountGet - orderFills[addr(_user)][hash] >= _amountGet * tokens[addr(_tokenGive)][addr(_user)] / _amountGive: + return (_amountGet * tokens[addr(_tokenGive)][addr(_user)] / _amountGive) + return (_amountGet - orderFills[addr(_user)][hash]) + +def testTrade(address _tokenGet, uint256 _amountGet, address _tokenGive, uint256 _amountGive, uint256 _expires, uint256 _nonce, address _user, uint8 _v, bytes32 _r, bytes32 _s, uint256 _amount, address _sender): # not payable + if tokens[addr(_tokenGet)][addr(_sender)] >= _amount: + hash = sha256hash(addr(this.address), 0, uint64(_amountGet), _tokenGive, _amountGive, _expires, _nonce) # precompiled + require sha256hash.result + if stor7[addr(_user)][hash]: + if block.number > _expires: + if 0 >= _amount: + return 1 + else: + require orderFills[addr(_user)][hash] <= _amountGet + if tokens[addr(_tokenGive)][addr(_user)]: + require tokens[addr(_tokenGive)][addr(_user)] + require _amountGet * tokens[addr(_tokenGive)][addr(_user)] / tokens[addr(_tokenGive)][addr(_user)] == _amountGet + require _amountGive + if _amountGet - orderFills[addr(_user)][hash] >= _amountGet * tokens[addr(_tokenGive)][addr(_user)] / _amountGive: + if _amountGet * tokens[addr(_tokenGive)][addr(_user)] / _amountGive >= _amount: + return 1 + else: + if _amountGet - orderFills[addr(_user)][hash] >= _amount: + return 1 + else: + signer = erecover(sha3(Mask(224, 32, '\x19Ethereum Signed Message:\n32') >> 32, hash), _v << 248, _r, _s) # precompiled + require erecover.result + if addr(signer) != _user: + if 0 >= _amount: + return 1 + else: + if block.number > _expires: + if 0 >= _amount: + return 1 + else: + require orderFills[addr(_user)][hash] <= _amountGet + if tokens[addr(_tokenGive)][addr(_user)]: + require tokens[addr(_tokenGive)][addr(_user)] + require _amountGet * tokens[addr(_tokenGive)][addr(_user)] / tokens[addr(_tokenGive)][addr(_user)] == _amountGet + require _amountGive + if _amountGet - orderFills[addr(_user)][hash] >= _amountGet * tokens[addr(_tokenGive)][addr(_user)] / _amountGive: + if _amountGet * tokens[addr(_tokenGive)][addr(_user)] / _amountGive >= _amount: + return 1 + else: + if _amountGet - orderFills[addr(_user)][hash] >= _amount: + return 1 + return 0 + else: + return 0 + +def trade(address _tokenGet, uint256 _amountGet, address _tokenGive, uint256 _amountGive, uint256 _expires, uint256 _nonce, address _user, uint8 _v, bytes32 _r, bytes32 _s, uint256 _amount): # not payable + hash = sha256hash(addr(this.address), 0, uint64(_amountGet), _tokenGive, _amountGive, _expires, _nonce) # precompiled + require sha256hash.result + if stor7[addr(_user)][hash]: + require block.number <= _expires + require _amount + orderFills[addr(_user)][hash] >= orderFills[addr(_user)][hash] + require _amount + orderFills[addr(_user)][hash] >= _amount + require _amount + orderFills[addr(_user)][hash] <= _amountGet + if _amount: + require _amount + require feeMake * _amount / _amount == feeMake + if _amount: + require _amount + require feeTake * _amount / _amount == feeTake + if not accountLevelsAddr: + require (feeTake * _amount / 10^18) + _amount >= _amount + require (feeTake * _amount / 10^18) + _amount >= feeTake * _amount / 10^18 + require (feeTake * _amount / 10^18) + _amount <= tokens[addr(_tokenGet)][caller] + tokens[addr(_tokenGet)][caller] = tokens[addr(_tokenGet)][caller] - (feeTake * _amount / 10^18) - _amount + require _amount >= _amount + require _amount >= 0 + require feeMake * _amount / 10^18 <= _amount + require _amount - (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][_user] >= tokens[addr(_tokenGet)][_user] + require tokens[addr(_tokenGet)][_user] >= 0 + tokens[addr(_tokenGet)][addr(_user)] = _amount - (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][_user] + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) >= feeMake * _amount / 10^18 + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) >= feeTake * _amount / 10^18 + require 0 <= (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][stor1] >= tokens[addr(_tokenGet)][stor1] + require tokens[addr(_tokenGet)][stor1] >= 0 + tokens[addr(_tokenGet)][stor1] = (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][stor1] + else: + require ext_code.size(accountLevelsAddr) + call accountLevelsAddr.accountLevel(address user) with: + gas gas_remaining - 50 wei + args _user + require ext_call.success + if ext_call.return_data[0] != 1: + require (feeTake * _amount / 10^18) + _amount >= _amount + require (feeTake * _amount / 10^18) + _amount >= feeTake * _amount / 10^18 + require (feeTake * _amount / 10^18) + _amount <= tokens[addr(_tokenGet)][caller] + tokens[addr(_tokenGet)][caller] = tokens[addr(_tokenGet)][caller] - (feeTake * _amount / 10^18) - _amount + if ext_call.return_data[0] != 2: + require _amount >= _amount + require _amount >= 0 + require feeMake * _amount / 10^18 <= _amount + require _amount - (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][_user] >= tokens[addr(_tokenGet)][_user] + require tokens[addr(_tokenGet)][_user] >= 0 + tokens[addr(_tokenGet)][addr(_user)] = _amount - (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][_user] + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) >= feeMake * _amount / 10^18 + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) >= feeTake * _amount / 10^18 + require 0 <= (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][stor1] >= tokens[addr(_tokenGet)][stor1] + require tokens[addr(_tokenGet)][stor1] >= 0 + tokens[addr(_tokenGet)][stor1] = (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][stor1] + else: + require (feeTake * _amount / 10^18) + _amount >= _amount + require (feeTake * _amount / 10^18) + _amount >= feeTake * _amount / 10^18 + require feeMake * _amount / 10^18 <= (feeTake * _amount / 10^18) + _amount + require (feeTake * _amount / 10^18) + _amount - (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][_user] >= tokens[addr(_tokenGet)][_user] + require tokens[addr(_tokenGet)][_user] >= 0 + tokens[addr(_tokenGet)][addr(_user)] = (feeTake * _amount / 10^18) + _amount - (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][_user] + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) >= feeMake * _amount / 10^18 + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) >= feeTake * _amount / 10^18 + require feeTake * _amount / 10^18 <= (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) + require (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][stor1] >= tokens[addr(_tokenGet)][stor1] + require (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][stor1] >= feeMake * _amount / 10^18 + tokens[addr(_tokenGet)][stor1] += feeMake * _amount / 10^18 + else: + if _amount: + require _amount + require feeRebate * _amount / _amount == feeRebate + require (feeTake * _amount / 10^18) + _amount >= _amount + require (feeTake * _amount / 10^18) + _amount >= feeTake * _amount / 10^18 + require (feeTake * _amount / 10^18) + _amount <= tokens[addr(_tokenGet)][caller] + tokens[addr(_tokenGet)][caller] = tokens[addr(_tokenGet)][caller] - (feeTake * _amount / 10^18) - _amount + if ext_call.return_data[0] != 2: + require (feeRebate * _amount / 10^18) + _amount >= _amount + require (feeRebate * _amount / 10^18) + _amount >= feeRebate * _amount / 10^18 + require feeMake * _amount / 10^18 <= (feeRebate * _amount / 10^18) + _amount + require (feeRebate * _amount / 10^18) + _amount - (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][_user] >= tokens[addr(_tokenGet)][_user] + require tokens[addr(_tokenGet)][_user] >= 0 + tokens[addr(_tokenGet)][addr(_user)] = (feeRebate * _amount / 10^18) + _amount - (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][_user] + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) >= feeMake * _amount / 10^18 + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) >= feeTake * _amount / 10^18 + require feeRebate * _amount / 10^18 <= (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) - (feeRebate * _amount / 10^18) + tokens[addr(_tokenGet)][stor1] >= tokens[addr(_tokenGet)][stor1] + require tokens[addr(_tokenGet)][stor1] >= 0 + tokens[addr(_tokenGet)][stor1] = (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) - (feeRebate * _amount / 10^18) + tokens[addr(_tokenGet)][stor1] + else: + require (feeTake * _amount / 10^18) + _amount >= _amount + require (feeTake * _amount / 10^18) + _amount >= feeTake * _amount / 10^18 + require feeMake * _amount / 10^18 <= (feeTake * _amount / 10^18) + _amount + require (feeTake * _amount / 10^18) + _amount - (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][_user] >= tokens[addr(_tokenGet)][_user] + require tokens[addr(_tokenGet)][_user] >= 0 + tokens[addr(_tokenGet)][addr(_user)] = (feeTake * _amount / 10^18) + _amount - (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][_user] + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) >= feeMake * _amount / 10^18 + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) >= feeTake * _amount / 10^18 + require feeTake * _amount / 10^18 <= (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) + require (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][stor1] >= tokens[addr(_tokenGet)][stor1] + require (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][stor1] >= feeMake * _amount / 10^18 + tokens[addr(_tokenGet)][stor1] += feeMake * _amount / 10^18 + if _amountGive: + require _amountGive + require _amount * _amountGive / _amountGive == _amount + require _amountGet + require _amount * _amountGive / _amountGet <= tokens[addr(_tokenGive)][addr(_user)] + tokens[addr(_tokenGive)][addr(_user)] -= _amount * _amountGive / _amountGet + if _amountGive: + require _amountGive + require _amount * _amountGive / _amountGive == _amount + require _amountGet + require (_amount * _amountGive / _amountGet) + tokens[addr(_tokenGive)][caller] >= tokens[addr(_tokenGive)][caller] + require (_amount * _amountGive / _amountGet) + tokens[addr(_tokenGive)][caller] >= _amount * _amountGive / _amountGet + tokens[addr(_tokenGive)][caller] = (_amount * _amountGive / _amountGet) + tokens[addr(_tokenGive)][caller] + require _amount + orderFills[addr(_user)][hash] >= orderFills[addr(_user)][hash] + require _amount + orderFills[addr(_user)][hash] >= _amount + orderFills[addr(_user)][hash] += _amount + require _amountGet + log Trade( +  address tokenGet=_tokenGet, +  uint256 amountGet=0, +  address tokenGive=addr(_tokenGive), +  uint256 amountGive=_amount * _amountGive / _amountGet, +  address get=addr(_user), +  address give=caller) + else: + signer = erecover(sha3(Mask(224, 32, '\x19Ethereum Signed Message:\n32') >> 32, hash), _v << 248, _r, _s) # precompiled + require erecover.result + require addr(signer) == _user + require block.number <= _expires + require _amount + orderFills[addr(_user)][hash] >= orderFills[addr(_user)][hash] + require _amount + orderFills[addr(_user)][hash] >= _amount + require _amount + orderFills[addr(_user)][hash] <= _amountGet + if _amount: + require _amount + require feeMake * _amount / _amount == feeMake + if _amount: + require _amount + require feeTake * _amount / _amount == feeTake + if not accountLevelsAddr: + require (feeTake * _amount / 10^18) + _amount >= _amount + require (feeTake * _amount / 10^18) + _amount >= feeTake * _amount / 10^18 + require (feeTake * _amount / 10^18) + _amount <= tokens[addr(_tokenGet)][caller] + tokens[addr(_tokenGet)][caller] = tokens[addr(_tokenGet)][caller] - (feeTake * _amount / 10^18) - _amount + require _amount >= _amount + require _amount >= 0 + require feeMake * _amount / 10^18 <= _amount + require _amount - (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][_user] >= tokens[addr(_tokenGet)][_user] + require tokens[addr(_tokenGet)][_user] >= 0 + tokens[addr(_tokenGet)][addr(_user)] = _amount - (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][_user] + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) >= feeMake * _amount / 10^18 + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) >= feeTake * _amount / 10^18 + require 0 <= (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][stor1] >= tokens[addr(_tokenGet)][stor1] + require tokens[addr(_tokenGet)][stor1] >= 0 + tokens[addr(_tokenGet)][stor1] = (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][stor1] + if _amountGive: + require _amountGive + require _amount * _amountGive / _amountGive == _amount + require _amountGet + require _amount * _amountGive / _amountGet <= tokens[addr(_tokenGive)][addr(_user)] + tokens[addr(_tokenGive)][addr(_user)] -= _amount * _amountGive / _amountGet + if _amountGive: + require _amountGive + require _amount * _amountGive / _amountGive == _amount + require _amountGet + require (_amount * _amountGive / _amountGet) + tokens[addr(_tokenGive)][caller] >= tokens[addr(_tokenGive)][caller] + require (_amount * _amountGive / _amountGet) + tokens[addr(_tokenGive)][caller] >= _amount * _amountGive / _amountGet + tokens[addr(_tokenGive)][caller] = (_amount * _amountGive / _amountGet) + tokens[addr(_tokenGive)][caller] + require _amount + orderFills[addr(_user)][hash] >= orderFills[addr(_user)][hash] + require _amount + orderFills[addr(_user)][hash] >= _amount + orderFills[addr(_user)][hash] += _amount + require _amountGet + log Trade( +  address tokenGet=addr(_tokenGet), +  uint256 amountGet=_amount, +  address tokenGive=addr(_tokenGive), +  uint256 amountGive=_amount * _amountGive / _amountGet, +  address get=addr(_user), +  address give=caller) + else: + require ext_code.size(accountLevelsAddr) + call accountLevelsAddr.accountLevel(address user) with: + gas gas_remaining - 50 wei + args _user + require ext_call.success + if ext_call.return_data[0] != 1: + require (feeTake * _amount / 10^18) + _amount >= _amount + require (feeTake * _amount / 10^18) + _amount >= feeTake * _amount / 10^18 + require (feeTake * _amount / 10^18) + _amount <= tokens[addr(_tokenGet)][caller] + tokens[addr(_tokenGet)][caller] = tokens[addr(_tokenGet)][caller] - (feeTake * _amount / 10^18) - _amount + if ext_call.return_data[0] != 2: + require _amount >= _amount + require _amount >= 0 + require feeMake * _amount / 10^18 <= _amount + require _amount - (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][_user] >= tokens[addr(_tokenGet)][_user] + require tokens[addr(_tokenGet)][_user] >= 0 + tokens[addr(_tokenGet)][addr(_user)] = _amount - (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][_user] + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) >= feeMake * _amount / 10^18 + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) >= feeTake * _amount / 10^18 + require 0 <= (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][stor1] >= tokens[addr(_tokenGet)][stor1] + require tokens[addr(_tokenGet)][stor1] >= 0 + tokens[addr(_tokenGet)][stor1] = (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][stor1] + else: + require (feeTake * _amount / 10^18) + _amount >= _amount + require (feeTake * _amount / 10^18) + _amount >= feeTake * _amount / 10^18 + require feeMake * _amount / 10^18 <= (feeTake * _amount / 10^18) + _amount + require (feeTake * _amount / 10^18) + _amount - (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][_user] >= tokens[addr(_tokenGet)][_user] + require tokens[addr(_tokenGet)][_user] >= 0 + tokens[addr(_tokenGet)][addr(_user)] = (feeTake * _amount / 10^18) + _amount - (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][_user] + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) >= feeMake * _amount / 10^18 + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) >= feeTake * _amount / 10^18 + require feeTake * _amount / 10^18 <= (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) + require (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][stor1] >= tokens[addr(_tokenGet)][stor1] + require (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][stor1] >= feeMake * _amount / 10^18 + tokens[addr(_tokenGet)][stor1] += feeMake * _amount / 10^18 + else: + if _amount: + require _amount + require feeRebate * _amount / _amount == feeRebate + require (feeTake * _amount / 10^18) + _amount >= _amount + require (feeTake * _amount / 10^18) + _amount >= feeTake * _amount / 10^18 + require (feeTake * _amount / 10^18) + _amount <= tokens[addr(_tokenGet)][caller] + tokens[addr(_tokenGet)][caller] = tokens[addr(_tokenGet)][caller] - (feeTake * _amount / 10^18) - _amount + if ext_call.return_data[0] != 2: + require (feeRebate * _amount / 10^18) + _amount >= _amount + require (feeRebate * _amount / 10^18) + _amount >= feeRebate * _amount / 10^18 + require feeMake * _amount / 10^18 <= (feeRebate * _amount / 10^18) + _amount + require (feeRebate * _amount / 10^18) + _amount - (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][_user] >= tokens[addr(_tokenGet)][_user] + require tokens[addr(_tokenGet)][_user] >= 0 + tokens[addr(_tokenGet)][addr(_user)] = (feeRebate * _amount / 10^18) + _amount - (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][_user] + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) >= feeMake * _amount / 10^18 + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) >= feeTake * _amount / 10^18 + require feeRebate * _amount / 10^18 <= (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) - (feeRebate * _amount / 10^18) + tokens[addr(_tokenGet)][stor1] >= tokens[addr(_tokenGet)][stor1] + require tokens[addr(_tokenGet)][stor1] >= 0 + tokens[addr(_tokenGet)][stor1] = (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) - (feeRebate * _amount / 10^18) + tokens[addr(_tokenGet)][stor1] + else: + require (feeTake * _amount / 10^18) + _amount >= _amount + require (feeTake * _amount / 10^18) + _amount >= feeTake * _amount / 10^18 + require feeMake * _amount / 10^18 <= (feeTake * _amount / 10^18) + _amount + require (feeTake * _amount / 10^18) + _amount - (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][_user] >= tokens[addr(_tokenGet)][_user] + require tokens[addr(_tokenGet)][_user] >= 0 + tokens[addr(_tokenGet)][addr(_user)] = (feeTake * _amount / 10^18) + _amount - (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][_user] + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) >= feeMake * _amount / 10^18 + require (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) >= feeTake * _amount / 10^18 + require feeTake * _amount / 10^18 <= (feeTake * _amount / 10^18) + (feeMake * _amount / 10^18) + require (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][stor1] >= tokens[addr(_tokenGet)][stor1] + require (feeMake * _amount / 10^18) + tokens[addr(_tokenGet)][stor1] >= feeMake * _amount / 10^18 + tokens[addr(_tokenGet)][stor1] += feeMake * _amount / 10^18 + if _amountGive: + require _amountGive + require _amount * _amountGive / _amountGive == _amount + require _amountGet + require _amount * _amountGive / _amountGet <= tokens[addr(_tokenGive)][addr(_user)] + tokens[addr(_tokenGive)][addr(_user)] -= _amount * _amountGive / _amountGet + if _amountGive: + require _amountGive + require _amount * _amountGive / _amountGive == _amount + require _amountGet + require (_amount * _amountGive / _amountGet) + tokens[addr(_tokenGive)][caller] >= tokens[addr(_tokenGive)][caller] + require (_amount * _amountGive / _amountGet) + tokens[addr(_tokenGive)][caller] >= _amount * _amountGive / _amountGet + tokens[addr(_tokenGive)][caller] = (_amount * _amountGive / _amountGet) + tokens[addr(_tokenGive)][caller] + require _amount + orderFills[addr(_user)][hash] >= orderFills[addr(_user)][hash] + require _amount + orderFills[addr(_user)][hash] >= _amount + orderFills[addr(_user)][hash] += _amount + require _amountGet + log Trade( +  address tokenGet=_tokenGet, +  uint256 amountGet=0, +  address tokenGive=addr(_tokenGive), +  uint256 amountGive=_amount * _amountGive / _amountGet, +  address get=addr(_user), +  address give=caller) + diff --git a/benchmark/0xF835A0247b0063C04EF22006eBe57c5F11977Cc4.pan b/benchmark/0xF835A0247b0063C04EF22006eBe57c5F11977Cc4.pan new file mode 100644 index 00000000..39ce5249 --- /dev/null +++ b/benchmark/0xF835A0247b0063C04EF22006eBe57c5F11977Cc4.pan @@ -0,0 +1,142 @@ +# +# Panoramix 4 Oct 2019 +# Decompiled source of 0xF835A0247b0063C04EF22006eBe57c5F11977Cc4 +# +# Let's make the world open source +#  + +def storage: + stor0 is uint256 at storage 0 + owner is addr at storage 0 + stor1 is uint256 at storage 1 + curatorAddress is addr at storage 1 + daoAddress is addr at storage 2 + stor2 is uint256 at storage 2 + counter is uint256 at storage 3 + unknown371fa854 is uint256 at storage 4 + unknown26f5a8c9 is uint256 at storage 5 + stor6 is uint256 at storage 6 + nextAddress is addr at storage 6 + unknown5970c915Address is addr at storage 7 + stor7 is uint256 at storage 7 + +def unknown26f5a8c9() payable: + return unknown26f5a8c9 + +def unknown371fa854() payable: + return unknown371fa854 + +def dao() payable: + return addr(daoAddress) + +def next() payable: + return addr(nextAddress) + +def unknown5970c915() payable: + return addr(unknown5970c915Address) + +def counter() payable: + return counter + +def owner() payable: + return addr(owner) + +def curator() payable: + return addr(curatorAddress) + +# +# Regular functions +# + +def unknown7f9f519f(uint256 _param1) payable: + require caller == addr(owner) + unknown26f5a8c9 = _param1 + log 0xbab6859b: _param1 + return 1 + +def setOwner(address _owner) payable: + require caller == addr(owner) + log NewOwner(address owner=_owner_) + uint256(stor0) = _owner_ or Mask(96, 160, uint256(stor0)) + return 1 + +def setDao(address _newDaoAddress) payable: + require caller == addr(owner) + uint256(stor2) = _newDaoAddress or Mask(96, 160, uint256(stor2)) + log 0xce6a5015: _newDaoAddress + return 1 + +def vote(uint256 _proposalNumber, bool _supportsProposal) payable: + require caller == addr(owner) + call addr(daoAddress).vote(uint256 proposalNumber, bool supportsProposal) with: + gas gas_remaining - 25050 wei + args _proposalNumber, _supportsProposal + require ext_call.success + log 0x8bfa1f40: _proposalNumber, _supportsProposal + return 1 + +def transfer(address _to, uint256 _value) payable: + require caller == addr(owner) + call addr(daoAddress).transfer(address to, uint256 value) with: + gas gas_remaining - 25050 wei + args addr(_to), _value + require ext_call.success + log Transfer( +  address to=addr(_to), +  uint256 value=_value) + return 1 + +def unknown625e847d() payable: + require caller == addr(owner) + counter = 1 + call addr(unknown5970c915Address) with: + value eth.balance(this.address) wei + gas 0 wei + call addr(daoAddress).splitDAO(uint256 proposalID, address newCurator) with: + gas gas_remaining - 25050 wei + args unknown371fa854, addr(curatorAddress) + require ext_call.success + return 1 + +def unknownc4463c80(uint256 _param1, uint256 _param2, uint256 _param3, uint256 _param4, uint256 _param5) payable: + require caller == addr(owner) + uint256(stor6) = _param3 or Mask(96, 160, uint256(stor6)) + counter = 1 + uint256(stor1) = _param2 or Mask(96, 160, uint256(stor1)) + unknown371fa854 = _param1 + unknown26f5a8c9 = _param4 + uint256(stor7) = _param5 or Mask(96, 160, uint256(stor7)) + call addr(_param5) with: + value eth.balance(this.address) wei + gas 0 wei + call addr(daoAddress).splitDAO(uint256 proposalID, address newCurator) with: + gas gas_remaining - 25050 wei + args unknown371fa854, addr(curatorAddress) + require ext_call.success + log 0xa1ab7317: unknown371fa854, addr(nextAddress) + return 1 + +def _fallback() payable: # default function + call addr(daoAddress).rewardAccount() with: + gas gas_remaining - 25050 wei + require ext_call.success + if ext_call.return_data[12 len 20] != caller: + log 0xa6af7265: ext_call.return_data[0], caller, 15, 'constuctor fail' + else: + if counter <= unknown26f5a8c9 - 1: + counter++ + call addr(daoAddress).splitDAO(uint256 proposalID, address newCurator) with: + gas gas_remaining - 25050 wei + args unknown371fa854, addr(curatorAddress) + require ext_call.success + else: + call addr(daoAddress).balanceOf(address owner) with: + gas gas_remaining - 25050 wei + args this.address + require ext_call.success + call addr(daoAddress).transfer(address to, uint256 value) with: + gas gas_remaining - 25050 wei + args addr(nextAddress), ext_call.return_data[0] + counter = 1 + return 1 + diff --git a/benchmark/0xe2F42B417337fd9fD22631cad54DB8178655Fcd1.pan b/benchmark/0xe2F42B417337fd9fD22631cad54DB8178655Fcd1.pan new file mode 100644 index 00000000..79f5859f --- /dev/null +++ b/benchmark/0xe2F42B417337fd9fD22631cad54DB8178655Fcd1.pan @@ -0,0 +1,318 @@ +# +# Panoramix 4 Oct 2019 +# Decompiled source of 0xe2F42B417337fd9fD22631cad54DB8178655Fcd1 +# +# Let's make the world open source +#  + +def storage: + signers is array of addr at storage 0 + safeMode is uint8 at storage 1 + stor2 is array of uint256 at storage 2 + stor12 is addr at storage 12 + stor13 is addr at storage 13 + +def signers(uint256 _param1): # not payable + require _param1 < signers.length + return signers[_param1] + +def safeMode(): # not payable + return bool(safeMode) + +# +# Regular functions +# + +def _fallback() payable: # default function + if call.value > 0: + log Deposited( +  address from=caller, +  uint256 value=call.value, +  bytes data=Array(len=calldata.size, data=call.data[0 len calldata.size])) + +def isSigner(address _account): # not payable + idx = 0 + while idx < signers.length: + mem[0] = 0 + if signers[idx] != _account: + idx = idx + 1 + continue  + return 1 + return 0 + +def activateSafeMode(): # not payable + require 0 < signers.length + idx = 0 + while signers[idx] != caller: + require idx + 1 < signers.length + mem[0] = 0 + idx = idx + 1 + continue  + safeMode = 1 + log SafeModeActivated(address msgSender=caller) + +def getNextSequenceId(): # not payable + idx = 0 + s = 0 + while idx < 10: + if stor2[idx] <= s: + idx = idx + 1 + s = s + continue  + require idx < 10 + idx = idx + 1 + s = stor2[idx] + continue  + return (s + 1) + +def createForwarder(): # not payable + require ext_code.size(stor12) + call stor12.createProxy(address masterCopy, bytes data) with: + gas gas_remaining - 710 wei + args stor13, 64, 0 + require ext_call.success + require ext_code.size(addr(ext_call.return_data[0])) + call addr(ext_call.return_data[0]).initialize() with: + gas gas_remaining - 710 wei + require ext_call.success + log ForwarderCreated(address to=addr(ext_call.return_data[0])) + return addr(ext_call.return_data[0]) + +def flushForwarderTokens(address _forwarderAddress, address _tokenContractAddress): # not payable + require 0 < signers.length + idx = 0 + while signers[idx] != caller: + require idx + 1 < signers.length + mem[0] = 0 + idx = idx + 1 + continue  + require ext_code.size(_forwarderAddress) + call _forwarderAddress.flushTokens(address tokenContractAddress) with: + gas gas_remaining - 710 wei + args _tokenContractAddress + require ext_call.success + +def sendMultiSigToken(address _toAddress, uint256 _value, address _tokenContractAddress, uint256 _expireTime, uint256 _sequenceId, bytes _signature): # not payable + mem[128 len _signature.length] = _signature[all] + require 0 < signers.length + idx = 0 + while signers[idx] != caller: + require idx + 1 < signers.length + mem[0] = 0 + idx = idx + 1 + continue  + mem[ceil32(_signature.length) + 128] = 'ERC20' + mem[ceil32(_signature.length) + 133] = addr(_toAddress) + mem[ceil32(_signature.length) + 153] = _value + mem[ceil32(_signature.length) + 185] = addr(_tokenContractAddress) + require _signature.length == 65 + if mem[192 len 1] >= 27: + signer = erecover(sha3('ERC20', _toAddress, _value, _tokenContractAddress, _expireTime, _sequenceId), mem[161] << 248, mem[128], mem[160]) # precompiled + else: + signer = erecover(sha3('ERC20', _toAddress, _value, _tokenContractAddress, _expireTime, _sequenceId), mem[192 len 1] + 27 << 248, mem[128], mem[160]) # precompiled + require erecover.result + if safeMode: + require 0 < signers.length + idx = 0 + while signers[idx] != _toAddress: + require idx + 1 < signers.length + mem[0] = 0 + idx = idx + 1 + continue  + require _expireTime >= block.timestamp + require 0 < signers.length + idx = 0 + while signers[idx] != caller: + require idx + 1 < signers.length + mem[0] = 0 + idx = idx + 1 + continue  + idx = 0 + while idx < 10: + require stor2[idx] != _sequenceId + require idx < 10 + idx = idx + 1 + continue  + require _sequenceId >= stor2.length + require _sequenceId <= stor2.length + 10000 + stor2.length = _sequenceId + require 0 < signers.length + idx = 0 + while signers[idx] != addr(signer): + require idx + 1 < signers.length + mem[0] = 0 + idx = idx + 1 + continue  + require addr(signer) != caller + require ext_code.size(_tokenContractAddress) + call _tokenContractAddress.transfer(address to, uint256 value) with: + gas gas_remaining - 710 wei + args addr(_toAddress), _value + require ext_call.success + require ext_call.return_data[0] + +def sendMultiSig(address _toAddress, uint256 _value, bytes _data, uint256 _expireTime, uint256 _sequenceId, bytes _signature): # not payable + mem[128 len _data.length] = _data[all] + mem[ceil32(_data.length) + 128] = _signature.length + mem[ceil32(_data.length) + 160 len _signature.length] = _signature[all] + require 0 < signers.length + idx = 0 + while signers[idx] != caller: + require idx + 1 < signers.length + mem[0] = 0 + idx = idx + 1 + continue  + mem[ceil32(_data.length) + ceil32(_signature.length) + 160] = 'ETHER' + mem[ceil32(_data.length) + ceil32(_signature.length) + 165] = addr(_toAddress) + mem[ceil32(_data.length) + ceil32(_signature.length) + 185] = _value + mem[ceil32(_data.length) + ceil32(_signature.length) + 217 len floor32(_data.length)] = call.data[_data + 36 len floor32(_data.length)] + mem[ceil32(_data.length) + ceil32(_signature.length) + floor32(_data.length) + -(_data.length % 32) + 249 len _data.length % 32] = mem[floor32(_data.length) + -(_data.length % 32) + 160 len _data.length % 32] + mem[_data.length + ceil32(_data.length) + ceil32(_signature.length) + 217] = _expireTime + mem[_data.length + ceil32(_data.length) + ceil32(_signature.length) + 249] = _sequenceId + _66 = sha3('ETHER', _toAddress, _value, call.data[_data + 36 len floor32(_data.length)], mem[ceil32(_data.length) + ceil32(_signature.length) + floor32(_data.length) + 217 len (_data.length % 32) + 32], _sequenceId, None) + require _signature.length == 65 + _70 = mem[ceil32(_data.length) + 192] + _71 = mem[ceil32(_data.length) + 193] + mem[ceil32(_data.length) + ceil32(_signature.length) + 192] = sha3('ETHER', _toAddress, _value, call.data[_data + 36 len floor32(_data.length)], mem[ceil32(_data.length) + ceil32(_signature.length) + floor32(_data.length) + 217 len (_data.length % 32) + 32], _sequenceId, None) + if mem[ceil32(_data.length) + 224 len 1] >= 27: + mem[ceil32(_data.length) + ceil32(_signature.length) + 224] = uint8(_71) + mem[ceil32(_data.length) + ceil32(_signature.length) + 256] = mem[ceil32(_data.length) + 160] + mem[ceil32(_data.length) + ceil32(_signature.length) + 288] = _70 + signer = erecover(_66, _71 << 248, mem[ceil32(_data.length) + 160], _70) # precompiled + require erecover.result + if safeMode: + require 0 < signers.length + idx = 0 + while signers[idx] != _toAddress: + require idx + 1 < signers.length + mem[0] = 0 + idx = idx + 1 + continue  + require _expireTime >= block.timestamp + require 0 < signers.length + idx = 0 + while signers[idx] != caller: + require idx + 1 < signers.length + mem[0] = 0 + idx = idx + 1 + continue  + idx = 0 + while idx < 10: + require stor2[idx] != _sequenceId + require idx < 10 + idx = idx + 1 + continue  + require _sequenceId >= stor2.length + require _sequenceId <= stor2.length + 10000 + stor2.length = _sequenceId + require 0 < signers.length + idx = 0 + while signers[idx] != addr(signer): + require idx + 1 < signers.length + mem[0] = 0 + idx = idx + 1 + continue  + require addr(signer) != caller + mem[ceil32(_data.length) + ceil32(_signature.length) + 192 len ceil32(_data.length)] = _data[all], mem[_data.length + 128 len ceil32(_data.length) - _data.length] + else: + mem[ceil32(_data.length) + ceil32(_signature.length) + 224] = uint8(uint8(_71) + 27) + mem[ceil32(_data.length) + ceil32(_signature.length) + 256] = mem[ceil32(_data.length) + 160] + mem[ceil32(_data.length) + ceil32(_signature.length) + 288] = _70 + signer = erecover(_66, uint8(_71) + 27 << 248, mem[ceil32(_data.length) + 160], _70) # precompiled + require erecover.result + if not safeMode: + require _expireTime >= block.timestamp + require 0 < signers.length + idx = 0 + while signers[idx] != caller: + require idx + 1 < signers.length + mem[0] = 0 + idx = idx + 1 + continue  + idx = 0 + while idx < 10: + require stor2[idx] != _sequenceId + require idx < 10 + idx = idx + 1 + continue  + require _sequenceId >= stor2.length + require _sequenceId <= stor2.length + 10000 + stor2.length = _sequenceId + require 0 < signers.length + idx = 0 + while signers[idx] != addr(signer): + require idx + 1 < signers.length + mem[0] = 0 + idx = idx + 1 + continue  + require addr(signer) != caller + mem[ceil32(_data.length) + ceil32(_signature.length) + 192 len ceil32(_data.length)] = _data[all], mem[_data.length + 128 len ceil32(_data.length) - _data.length] + var58001 = ceil32(_data.length) + else: + require 0 < signers.length + idx = 0 + while signers[idx] != _toAddress: + require idx + 1 < signers.length + mem[0] = 0 + idx = idx + 1 + continue  + require _expireTime >= block.timestamp + require 0 < signers.length + idx = 0 + while signers[idx] != caller: + require idx + 1 < signers.length + mem[0] = 0 + idx = idx + 1 + continue  + idx = 0 + while idx < 10: + require stor2[idx] != _sequenceId + require idx < 10 + idx = idx + 1 + continue  + require _sequenceId >= stor2.length + require _sequenceId <= stor2.length + 10000 + stor2.length = _sequenceId + require 0 < signers.length + idx = 0 + while signers[idx] != addr(signer): + require idx + 1 < signers.length + mem[0] = 0 + idx = idx + 1 + continue  + require addr(signer) != caller + mem[ceil32(_data.length) + ceil32(_signature.length) + 192 len ceil32(_data.length)] = _data[all], mem[_data.length + 128 len ceil32(_data.length) - _data.length] + if not _data.length % 32: + call _toAddress.mem[ceil32(_data.length) + ceil32(_signature.length) + 192 len 4] with: + value _value wei + gas gas_remaining - 34710 wei + args mem[ceil32(_data.length) + ceil32(_signature.length) + 196 len _data.length - 4] + else: + mem[floor32(_data.length) + ceil32(_data.length) + ceil32(_signature.length) + 192] = mem[floor32(_data.length) + ceil32(_data.length) + ceil32(_signature.length) + -(_data.length % 32) + 224 len _data.length % 32] + call _toAddress.mem[ceil32(_data.length) + ceil32(_signature.length) + 192 len 4] with: + value _value wei + gas gas_remaining - 34710 wei + args mem[ceil32(_data.length) + ceil32(_signature.length) + 196 len floor32(_data.length) + 28] + require ext_call.success + mem[ceil32(_data.length) + ceil32(_signature.length) + 192] = caller + mem[ceil32(_data.length) + ceil32(_signature.length) + 224] = addr(signer) + mem[ceil32(_data.length) + ceil32(_signature.length) + 256] = _66 + mem[ceil32(_data.length) + ceil32(_signature.length) + 288] = _toAddress + mem[ceil32(_data.length) + ceil32(_signature.length) + 320] = _value + mem[ceil32(_data.length) + ceil32(_signature.length) + 352] = 192 + mem[ceil32(_data.length) + ceil32(_signature.length) + 384] = _data.length + mem[ceil32(_data.length) + ceil32(_signature.length) + 416 len ceil32(_data.length)] = _data[all], mem[_data.length + 128 len ceil32(_data.length) - _data.length] + if not _data.length % 32: + log Transacted(address msgSender, address otherSigner, bytes32 operation, address toAddress, uint256 value, bytes data): +  mem[ceil32(_data.length) + ceil32(_signature.length) + 192 len _data.length + 25], +  _expireTime, +  _sequenceId, +  mem[ceil32(_data.length) + ceil32(_signature.length) + _data.length + 281 len 135], + else: + mem[floor32(_data.length) + ceil32(_data.length) + ceil32(_signature.length) + 416] = mem[floor32(_data.length) + ceil32(_data.length) + ceil32(_signature.length) + -(_data.length % 32) + 448 len _data.length % 32] + log Transacted(address msgSender, address otherSigner, bytes32 operation, address toAddress, uint256 value, bytes data): +  mem[ceil32(_data.length) + ceil32(_signature.length) + 192 len _data.length + 25], +  _expireTime, +  _sequenceId, +  mem[ceil32(_data.length) + ceil32(_signature.length) + _data.length + 281 len -(_data.length % 32) + 167], + diff --git a/benchmark/0xf91546835f756DA0c10cFa0CDA95b15577b84aA7.pan b/benchmark/0xf91546835f756DA0c10cFa0CDA95b15577b84aA7.pan new file mode 100644 index 00000000..93a8d368 --- /dev/null +++ b/benchmark/0xf91546835f756DA0c10cFa0CDA95b15577b84aA7.pan @@ -0,0 +1,723 @@ +# +# Panoramix 4 Oct 2019 +# Decompiled source of 0xf91546835f756DA0c10cFa0CDA95b15577b84aA7 +# +# Let's make the world open source +#  + +const NAME = 'Ledger Channel' +const VERSION = '0.0.1' + +def storage: + numChannels is uint256 at storage 0 + virtualChannels is mapping of struct at storage 1 + channels is array of struct at storage 2 + +def virtualChannels(bytes32 _param1): # not payable + return bool(virtualChannels[_param1].field_0), + bool(virtualChannels[_param1].field_8), + virtualChannels[_param1].field_256, + virtualChannels[_param1].field_512, + virtualChannels[_param1].field_768, + virtualChannels[_param1].field_1024, + virtualChannels[_param1].field_1280, + virtualChannels[_param1].field_1536, + virtualChannels[_param1].field_3328 + +def numChannels(): # not payable + return numChannels + +def Channels(bytes32 _param1): # not payable + return channels[_param1].field_3072, + channels[_param1].field_3328, + channels[_param1].field_3584, + channels[_param1].field_3840, + channels[_param1].field_4096, + bool(channels[_param1].field_4352), + bool(channels[_param1].field_4360), + channels[_param1].field_4608, + channels[_param1].field_4864 + +# +# Regular functions +# + +def _fallback() payable: # default function + revert + +def joinChannel(bytes32 _lcID, uint256[2] _balances) payable: + require not channels[_lcID].field_4352 + require caller == channels[_lcID].field_256 + if call.data[36]: + if call.data[36] != call.value: + revert with 0, 'state balance does not match sent value' + channels[_lcID].field_768 = call.value + if call.data[68]: + require ext_code.size(channels[_lcID].field_4864) + call channels[_lcID].field_4864.transferFrom(address from, address to, uint256 value) with: + gas gas_remaining wei + args caller, this.address, call.data[68] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if not ext_call.return_data[0]: + revert with 0, 'joinChannel: token transfer failure' + channels[_lcID].field_1792 = call.data[68] + channels[_lcID].field_2560 += call.data[36] + channels[_lcID].field_2816 += call.data[68] + channels[_lcID].field_4352 = 1 + numChannels++ + log DidLCJoin( +  bytes32 channelId=call.data[36], +  uint256 ethBalanceI=call.data[68], +  uint256 tokenBalanceI=_lcID) + +def getVirtualChannel(bytes32 _id): # not payable + idx = 1344 + s = 7 + while 1408 > idx + 32: + mem[idx + 32] = virtualChannels[_id][s].field_256 + idx = idx + 32 + s = s + 1 + continue  + idx = 1408 + s = 9 + while 1472 > idx + 32: + mem[idx + 32] = virtualChannels[_id][s].field_256 + idx = idx + 32 + s = s + 1 + continue  + idx = 1472 + s = 11 + while 1536 > idx + 32: + mem[idx + 32] = virtualChannels[_id][s].field_256 + idx = idx + 32 + s = s + 1 + continue  + return bool(virtualChannels[_id].field_0), + bool(virtualChannels[_id].field_8), + virtualChannels[_id].field_256, + virtualChannels[_id].field_512, + virtualChannels[_id].field_768, + virtualChannels[_id].field_1024, + virtualChannels[_id].field_1280, + virtualChannels[_id].field_1536, + mem[1376] >> 256, + virtualChannels[_id].field_2304, + mem[1440], + mem[1504] >> 256 + +def getChannel(bytes32 _id): # not payable + idx = 1952 + s = 0 + while 2016 > idx + 32: + mem[idx + 32] = channels[_id][s].field_256 + idx = idx + 32 + s = s + 1 + continue  + idx = 2016 + s = 2 + while 2144 > idx + 32: + mem[idx + 32] = channels[_id][s].field_256 + idx = idx + 32 + s = s + 1 + continue  + idx = 2144 + s = 6 + while 2272 > idx + 32: + mem[idx + 32] = channels[_id][s].field_256 + idx = idx + 32 + s = s + 1 + continue  + idx = 2272 + s = 10 + while 2336 > idx + 32: + mem[idx + 32] = channels[_id][s].field_256 + idx = idx + 32 + s = s + 1 + continue  + return channels[_id].field_0, + mem[1984], + mem[2048 len 96] >> 768, + channels[_id].field_1536, + mem[2176 len 96], + mem[2304] >> 256, + channels[_id].field_3072, + channels[_id].field_3328, + channels[_id].field_3584, + channels[_id].field_3840, + channels[_id].field_4096, + bool(channels[_id].field_4352), + bool(channels[_id].field_4360), + channels[_id].field_4608 + +def closeVirtualChannel(bytes32 _lcID, bytes32 _vcID): # not payable + if not channels[_lcID].field_4352: + revert with 0, 'LC is closed.' + if not virtualChannels[_vcID].field_8: + revert with 0, 'VC is not in settlement state.' + if block.timestamp <= virtualChannels[_vcID].field_768: + revert with 0x8c379a000000000000000000000000000000000000000000000000000000000, 'Update vc timeout has not elapsed.' + if virtualChannels[_vcID].field_0: + revert with 0, 'VC is already closed' + channels[_lcID].field_4608-- + virtualChannels[_vcID].field_0 = 1 + if channels[_lcID].field_0 == virtualChannels[_vcID].field_1024: + channels[_lcID].field_512 += virtualChannels[_vcID].field_1792 + channels[_lcID].field_768 += virtualChannels[_vcID].field_2048 + channels[_lcID].field_1536 += virtualChannels[_vcID].field_2304 + channels[_lcID].field_1792 += virtualChannels[_vcID].field_2560 + else: + if channels[_lcID].field_0 == virtualChannels[_vcID].field_1280: + channels[_lcID].field_512 += virtualChannels[_vcID].field_2048 + channels[_lcID].field_768 += virtualChannels[_vcID].field_1792 + channels[_lcID].field_1536 += virtualChannels[_vcID].field_2560 + channels[_lcID].field_1792 += virtualChannels[_vcID].field_2304 + log DidVCClose( +  bytes32 lcId=virtualChannels[_vcID].field_2304, +  bytes32 vcId=virtualChannels[_vcID].field_2560, +  uint256 balanceA=_lcID, +  uint256 balanceB=_vcID) + +def createChannel(bytes32 _lcID, address _partyI, uint256 _confirmTime, address _token, uint256[2] _balances) payable: + mem[96 len 64] = call.data[132 len 64] + if channels[_lcID].field_0: + revert with 0, 'Channel has already been created.' + if not _partyI: + revert with 0, 'No partyI address provided to LC creation' + if 0 > call.data[132]: + revert with 0, 'Balances cannot be negative' + if 0 > call.data[164]: + revert with 0, 'Balances cannot be negative' + channels[_lcID].field_0 = caller + channels[_lcID].field_256 = _partyI + if call.data[132]: + if call.data[132] != call.value: + revert with 0, 'Eth balance does not match sent value' + channels[_lcID].field_512 = call.value + if call.data[164]: + channels[_lcID].field_4864 = _token + require ext_code.size(_token) + call _token.transferFrom(address from, address to, uint256 value) with: + gas gas_remaining wei + args caller, this.address, call.data[164] + mem[160] = ext_call.return_data[0] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if not ext_call.return_data[0]: + revert with 0, 'CreateChannel: token transfer failure' + channels[_lcID].field_1536 = call.data[164] + channels[_lcID].field_3072 = 0 + channels[_lcID].field_3328 = _confirmTime + channels[_lcID].field_3840 = _confirmTime + block.timestamp + s = 10 + idx = 96 + while 160 > idx: + channels[_lcID][s].field_0 = mem[idx] + s = s + 1 + idx = idx + 32 + continue  + idx = 12 + while 12 > idx: + channels[_lcID][idx].field_0 = 0 + idx = idx + 1 + continue  + log DidLCOpen( +  bytes32 channelId=call.data[132], +  address partyA=addr(_token), +  address partyI=call.data[164], +  uint256 ethBalanceA=channels[_lcID].field_3840, +  address token=_lcID, +  uint256 tokenBalanceA=caller, +  uint256 LCopenTimeout=_partyI) + +def deposit(bytes32 _lcID, address _recipient, uint256 _balance, bool _isToken) payable: + if bool(channels[_lcID].field_4352) != 1: + revert with 0x8c379a000000000000000000000000000000000000000000000000000000000, 'Tried adding funds to a closed channel' + if channels[_lcID].field_0 != _recipient: + require channels[_lcID].field_256 == _recipient + if channels[_lcID].field_0 == _recipient: + if not _isToken: + if _balance != call.value: + revert with 0x8c379a000000000000000000000000000000000000000000000000000000000, 'state balance does not match sent value' + channels[_lcID].field_1024 += call.value + else: + require ext_code.size(channels[_lcID].field_4864) + call channels[_lcID].field_4864.transferFrom(address from, address to, uint256 value) with: + gas gas_remaining wei + args caller, this.address, _balance + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if not ext_call.return_data[0]: + revert with 0, 'deposit: token transfer failure' + channels[_lcID].field_2048 += _balance + if channels[_lcID].field_256 == _recipient: + if not _isToken: + if _balance != call.value: + revert with 0x8c379a000000000000000000000000000000000000000000000000000000000, 'state balance does not match sent value' + channels[_lcID].field_1280 += call.value + else: + require ext_code.size(channels[_lcID].field_4864) + call channels[_lcID].field_4864.transferFrom(address from, address to, uint256 value) with: + gas gas_remaining wei + args caller, this.address, _balance + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if not ext_call.return_data[0]: + revert with 0, 'deposit: token transfer failure' + channels[_lcID].field_2304 += _balance + log DidLCDeposit( +  bytes32 channelId=_balance, +  address recipient=_isToken, +  uint256 deposit=_lcID, +  bool isToken=_recipient) + +def LCOpenTimeout(bytes32 _lcID): # not payable + require caller == channels[_lcID].field_0 + require not channels[_lcID].field_4352 + require block.timestamp > channels[_lcID].field_3840 + if channels[_lcID].field_2560: + call channels[_lcID].field_0 with: + value channels[_lcID].field_512 wei + gas 2300 * is_zero(value) wei + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + if channels[_lcID].field_2816: + require ext_code.size(channels[_lcID].field_4864) + call channels[_lcID].field_4864.transfer(address to, uint256 value) with: + gas gas_remaining wei + args channels[_lcID].field_0, channels[_lcID].field_1536 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if not ext_call.return_data[0]: + revert with 0x8c379a000000000000000000000000000000000000000000000000000000000, 'CreateChannel: token transfer failure' + log DidLCClose( +  bytes32 channelId=0, +  uint256 sequence=channels[_lcID].field_512, +  uint256 ethBalanceA=channels[_lcID].field_1536, +  uint256 tokenBalanceA=0, +  uint256 ethBalanceI=0, +  uint256 tokenBalanceI=_lcID) + channels[_lcID].field_0 = 0 + channels[_lcID].field_256 = 0 + channels[_lcID].field_512 = 0 + channels[_lcID].field_768 = 0 + channels[_lcID].field_1024 = 0 + channels[_lcID].field_1280 = 0 + channels[_lcID].field_1536 = 0 + channels[_lcID].field_1792 = 0 + channels[_lcID].field_2048 = 0 + channels[_lcID].field_2304 = 0 + channels[_lcID].field_2560 = 0 + channels[_lcID].field_2816 = 0 + channels[_lcID].field_3072 = 0 + channels[_lcID].field_3328 = 0 + channels[_lcID].field_3584 = 0 + channels[_lcID].field_3840 = 0 + channels[_lcID].field_4096 = 0 + channels[_lcID].field_4352 = 0 + channels[_lcID].field_4608 = 0 + channels[_lcID].field_4864 = 0 + +def settleVC(bytes32 _lcID, bytes32 _vcID, uint256 _updateSeq, address _partyA, address _partyB, uint256[4] _updateBal, string _sigA): # not payable + if not channels[_lcID].field_4352: + revert with 0, 'LC is closed.' + if virtualChannels[_vcID].field_0: + revert with 0, 'VC is closed.' + if _updateSeq <= virtualChannels[_vcID].field_256: + revert with 0, 'VC sequence is higher than update sequence.' + if virtualChannels[_vcID].field_2048 >= call.data[196]: + revert with 0, 'State updates may only increase recipient balance.' + if virtualChannels[_vcID].field_2560 >= call.data[260]: + revert with 0, 'State updates may only increase recipient balance.' + if call.data[164] + call.data[196] != virtualChannels[_vcID].field_2816: + revert with 0, 'Incorrect balances for bonded amount' + if call.data[260] + call.data[228] != virtualChannels[_vcID].field_3072: + revert with 0, 'Incorrect balances for bonded amount' + require block.timestamp > channels[_lcID].field_4096 + require ext_code.size(0xcb107c7d2a93e638b20342f46b10b9b6d81377bf) + delegate 0xcb107c7d2a93e638b20342f46b10b9b6d81377bf.0xdca95419 with: + gas gas_remaining wei + args sha3(_vcID, _updateSeq, _partyA, _partyB, virtualChannels[_vcID].field_2816, virtualChannels[_vcID].field_3072, call.data[164], call.data[196], call.data[228], call.data[260 len 24], call.data[284 len 8]), Array(len=('cd', 292).length, data=call.data[cd[292] + 36 len ('cd', 292).length]) + if not delegate.return_code: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + require addr(delegate.return_data[0]) == virtualChannels[_vcID].field_1024 + virtualChannels[_vcID].field_512 = caller + virtualChannels[_vcID].field_256 = _updateSeq + virtualChannels[_vcID].field_1792 = call.data[164] + virtualChannels[_vcID].field_2048 = call.data[196] + virtualChannels[_vcID].field_2304 = call.data[228] + virtualChannels[_vcID].field_2560 = call.data[260] + virtualChannels[_vcID].field_768 = channels[_lcID].field_3328 + block.timestamp + log DidVCSettle( +  bytes32 lcId=_updateSeq, +  bytes32 vcId=call.data[164], +  uint256 updateSeq=call.data[196], +  uint256 updateBalA=caller, +  uint256 updateBalB=virtualChannels[_vcID].field_768, +  address challenger=_lcID, +  uint256 updateVCtimeout=_vcID) + +def updateLCstate(bytes32 _lcID, uint256[6] _updateParams, bytes32 _VCroot, string _sigA, string _sigI): # not payable + require channels[_lcID].field_4352 + require channels[_lcID].field_3072 < call.data[36] + require channels[_lcID].field_768 + channels[_lcID].field_512 >= call.data[132] + call.data[100] + require channels[_lcID].field_1792 + channels[_lcID].field_1536 >= call.data[196] + call.data[164] + if 1 == bool(channels[_lcID].field_4360): + require block.timestamp < channels[_lcID].field_4096 + require ext_code.size(0xcb107c7d2a93e638b20342f46b10b9b6d81377bf) + delegate 0xcb107c7d2a93e638b20342f46b10b9b6d81377bf.0xdca95419 with: + gas gas_remaining wei + args sha3(_lcID, 0, call.data[36], call.data[68], cd[228], channels[_lcID].field_0, channels[_lcID].field_256, call.data[100], call.data[132], call.data[164], call.data[196 len 23], call.data[219 len 9]), Array(len=('cd', 260).length, data=call.data[cd[260] + 36 len ('cd', 260).length]) + if not delegate.return_code: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + require channels[_lcID].field_0 == addr(delegate.return_data[0]) + require ext_code.size(0xcb107c7d2a93e638b20342f46b10b9b6d81377bf) + delegate 0xcb107c7d2a93e638b20342f46b10b9b6d81377bf.0xdca95419 with: + gas gas_remaining wei + args sha3(_lcID, 0, call.data[36], call.data[68], cd[228], channels[_lcID].field_0, channels[_lcID].field_256, call.data[100], call.data[132], call.data[164], call.data[196 len 23], call.data[219 len 9]), Array(len=('cd', 292).length, data=call.data[cd[292] + 36 len ('cd', 292).length]) + if not delegate.return_code: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + require channels[_lcID].field_256 == addr(delegate.return_data[0]) + channels[_lcID].field_3072 = call.data[36] + channels[_lcID].field_4608 = call.data[68] + channels[_lcID].field_512 = call.data[100] + channels[_lcID].field_768 = call.data[132] + channels[_lcID].field_1536 = call.data[164] + channels[_lcID].field_1792 = call.data[196] + channels[_lcID].field_3584 = cd[228] + channels[_lcID].field_4360 = 1 + channels[_lcID].field_4096 = block.timestamp + channels[_lcID].field_3328 + log DidLCUpdateState( +  bytes32 channelId=call.data[36], +  uint256 sequence=call.data[68], +  uint256 numOpenVc=call.data[100], +  uint256 ethBalanceA=call.data[132], +  uint256 tokenBalanceA=call.data[164], +  uint256 ethBalanceI=call.data[196], +  uint256 tokenBalanceI=cd[228], +  bytes32 vcRoot=channels[_lcID].field_4096, +  uint256 updateLCtimeout=_lcID) + +def consensusCloseChannel(bytes32 _lcID, uint256 _sequence, uint256[4] _balances, string _sigA, string _sigI): # not payable + require 1 == bool(channels[_lcID].field_4352) + require channels[_lcID].field_1280 + channels[_lcID].field_1024 + channels[_lcID].field_2560 == call.data[100] + call.data[68] + require channels[_lcID].field_2048 + channels[_lcID].field_2816 + channels[_lcID].field_2304 == call.data[132] + call.data[164] + require ext_code.size(0xcb107c7d2a93e638b20342f46b10b9b6d81377bf) + delegate 0xcb107c7d2a93e638b20342f46b10b9b6d81377bf.0xdca95419 with: + gas gas_remaining wei + args sha3(_lcID, 0, _sequence, 0, 0, channels[_lcID].field_0, channels[_lcID].field_256, call.data[68], call.data[100], call.data[132], call.data[164 len 23], call.data[187 len 9]), Array(len=('cd', 196).length, data=call.data[cd[196] + 36 len ('cd', 196).length]) + if not delegate.return_code: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + require addr(delegate.return_data[0]) == channels[_lcID].field_0 + require ext_code.size(0xcb107c7d2a93e638b20342f46b10b9b6d81377bf) + delegate 0xcb107c7d2a93e638b20342f46b10b9b6d81377bf.0xdca95419 with: + gas gas_remaining wei + args sha3(_lcID, 0, _sequence, 0, 0, channels[_lcID].field_0, channels[_lcID].field_256, call.data[68], call.data[100], call.data[132], call.data[164 len 23], call.data[187 len 9]), Array(len=('cd', 228).length, data=call.data[cd[228] + 36 len ('cd', 228).length]) + if not delegate.return_code: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + require addr(delegate.return_data[0]) == channels[_lcID].field_256 + channels[_lcID].field_4352 = 0 + if call.data[68]: + call channels[_lcID].field_0 with: + value call.data[68] wei + gas 2300 * is_zero(value) wei + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + call channels[_lcID].field_256 with: + value call.data[100] wei + gas 2300 * is_zero(value) wei + else: + if call.data[100]: + call channels[_lcID].field_0 with: + value call.data[68] wei + gas 2300 * is_zero(value) wei + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + call channels[_lcID].field_256 with: + value call.data[100] wei + gas 2300 * is_zero(value) wei + if call.data[132]: + require ext_code.size(channels[_lcID].field_4864) + call channels[_lcID].field_4864.transfer(address to, uint256 value) with: + gas gas_remaining wei + args channels[_lcID].field_0, call.data[132] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if not ext_call.return_data[0]: + revert with 0, 'happyCloseChannel: token transfer failure' + require ext_code.size(channels[_lcID].field_4864) + call channels[_lcID].field_4864.transfer(address to, uint256 value) with: + gas gas_remaining wei + args channels[_lcID].field_256, call.data[164] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if not ext_call.return_data[0]: + revert with 0, 'happyCloseChannel: token transfer failure' + else: + if call.data[164]: + require ext_code.size(channels[_lcID].field_4864) + call channels[_lcID].field_4864.transfer(address to, uint256 value) with: + gas gas_remaining wei + args channels[_lcID].field_0, call.data[132] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if not ext_call.return_data[0]: + revert with 0, 'happyCloseChannel: token transfer failure' + require ext_code.size(channels[_lcID].field_4864) + call channels[_lcID].field_4864.transfer(address to, uint256 value) with: + gas gas_remaining wei + args channels[_lcID].field_256, call.data[164] + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if not ext_call.return_data[0]: + revert with 0, 'happyCloseChannel: token transfer failure' + numChannels-- + log DidLCClose( +  bytes32 channelId=_sequence, +  uint256 sequence=call.data[68], +  uint256 ethBalanceA=call.data[100], +  uint256 tokenBalanceA=call.data[132], +  uint256 ethBalanceI=call.data[164], +  uint256 tokenBalanceI=_lcID) + +def byzantineCloseChannel(bytes32 _lcID): # not payable + if not channels[_lcID].field_4352: + revert with 0, 'Channel is not open' + require 1 == bool(channels[_lcID].field_4360) + require not channels[_lcID].field_4608 + if block.timestamp <= channels[_lcID].field_4096: + revert with 0, 'LC timeout over.' + if channels[_lcID].field_512 + channels[_lcID].field_768 >= channels[_lcID].field_2560 + channels[_lcID].field_1024 + channels[_lcID].field_1280: + require channels[_lcID].field_2560 + channels[_lcID].field_1024 + channels[_lcID].field_1280 == channels[_lcID].field_512 + channels[_lcID].field_768 + else: + channels[_lcID].field_512 += channels[_lcID].field_1024 + channels[_lcID].field_768 += channels[_lcID].field_1280 + if channels[_lcID].field_1536 + channels[_lcID].field_1792 >= channels[_lcID].field_2816 + channels[_lcID].field_2048 + channels[_lcID].field_2304: + require channels[_lcID].field_2816 + channels[_lcID].field_2048 + channels[_lcID].field_2304 == channels[_lcID].field_1536 + channels[_lcID].field_1792 + else: + channels[_lcID].field_1536 += channels[_lcID].field_2048 + channels[_lcID].field_1792 += channels[_lcID].field_2304 + channels[_lcID].field_512 = 0 + channels[_lcID].field_768 = 0 + channels[_lcID].field_1536 = 0 + channels[_lcID].field_1792 = 0 + if channels[_lcID].field_512: + call channels[_lcID].field_0 with: + value channels[_lcID].field_512 wei + gas 2300 * is_zero(value) wei + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + call channels[_lcID].field_256 with: + value channels[_lcID].field_768 wei + gas 2300 * is_zero(value) wei + else: + if channels[_lcID].field_768: + call channels[_lcID].field_0 with: + value channels[_lcID].field_512 wei + gas 2300 * is_zero(value) wei + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + call channels[_lcID].field_256 with: + value channels[_lcID].field_768 wei + gas 2300 * is_zero(value) wei + if channels[_lcID].field_1536: + require ext_code.size(channels[_lcID].field_4864) + call channels[_lcID].field_4864.transfer(address to, uint256 value) with: + gas gas_remaining wei + args channels[_lcID].field_0, channels[_lcID].field_1536 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if not ext_call.return_data[0]: + revert with 0x8c379a000000000000000000000000000000000000000000000000000000000, 'byzantineCloseChannel: token transfer failure' + require ext_code.size(channels[_lcID].field_4864) + call channels[_lcID].field_4864.transfer(address to, uint256 value) with: + gas gas_remaining wei + args channels[_lcID].field_256, channels[_lcID].field_1792 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if not ext_call.return_data[0]: + revert with 0x8c379a000000000000000000000000000000000000000000000000000000000, 'byzantineCloseChannel: token transfer failure' + else: + if channels[_lcID].field_1792: + require ext_code.size(channels[_lcID].field_4864) + call channels[_lcID].field_4864.transfer(address to, uint256 value) with: + gas gas_remaining wei + args channels[_lcID].field_0, channels[_lcID].field_1536 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if not ext_call.return_data[0]: + revert with 0x8c379a000000000000000000000000000000000000000000000000000000000, 'byzantineCloseChannel: token transfer failure' + require ext_code.size(channels[_lcID].field_4864) + call channels[_lcID].field_4864.transfer(address to, uint256 value) with: + gas gas_remaining wei + args channels[_lcID].field_256, channels[_lcID].field_1792 + if not ext_call.success: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + if not ext_call.return_data[0]: + revert with 0x8c379a000000000000000000000000000000000000000000000000000000000, 'byzantineCloseChannel: token transfer failure' + channels[_lcID].field_4352 = 0 + numChannels-- + log DidLCClose( +  bytes32 channelId=channels[_lcID].field_3072, +  uint256 sequence=channels[_lcID].field_512, +  uint256 ethBalanceA=channels[_lcID].field_768, +  uint256 tokenBalanceA=channels[_lcID].field_1536, +  uint256 ethBalanceI=channels[_lcID].field_1792, +  uint256 tokenBalanceI=_lcID) + +def initVCstate(bytes32 _lcID, bytes32 _vcID, bytes _proof, address _partyA, address _partyB, uint256[2] _bond, uint256[4] _balances, string _sigA): # not payable + mem[96] = _proof.length + mem[128 len _proof.length] = _proof[all] + mem[ceil32(_proof.length) + 128 len 64] = call.data[164 len 64] + mem[ceil32(_proof.length) + 192 len 128] = call.data[228 len 128] + mem[ceil32(_proof.length) + 320] = ('cd', 356).length + mem[ceil32(_proof.length) + 352 len ('cd', 356).length] = call.data[cd[356] + 36 len ('cd', 356).length] + if not channels[_lcID].field_4352: + revert with 0, 'LC is closed.' + if virtualChannels[_vcID].field_0: + revert with 0, 'VC is closed.' + if block.timestamp <= channels[_lcID].field_4096: + revert with 0, 'LC timeout not over.' + require not virtualChannels[_vcID].field_768 + mem[ceil32(_proof.length) + ceil32(('cd', 356).length) + 384] = _vcID + mem[ceil32(_proof.length) + ceil32(('cd', 356).length) + 416] = 0 + mem[ceil32(_proof.length) + ceil32(('cd', 356).length) + 448] = addr(_partyA) + mem[ceil32(_proof.length) + ceil32(('cd', 356).length) + 468] = addr(_partyB) + mem[ceil32(_proof.length) + ceil32(('cd', 356).length) + 488] = call.data[164] + mem[ceil32(_proof.length) + ceil32(('cd', 356).length) + 520] = call.data[196] + mem[ceil32(_proof.length) + ceil32(('cd', 356).length) + 552] = call.data[228] + mem[ceil32(_proof.length) + ceil32(('cd', 356).length) + 584] = call.data[260] + mem[ceil32(_proof.length) + ceil32(('cd', 356).length) + 616] = call.data[292] + mem[ceil32(_proof.length) + ceil32(('cd', 356).length) + 648] = call.data[324] + mem[ceil32(_proof.length) + ceil32(('cd', 356).length) + 352] = 296 + mem[64] = ceil32(_proof.length) + ceil32(('cd', 356).length) + 680 + mem[ceil32(_proof.length) + ceil32(('cd', 356).length) + 680 len 288] = _vcID, 0, _partyA, _partyB, call.data[164], call.data[196], call.data[228], call.data[260], call.data[292], call.data[324 len 24] + mem[ceil32(_proof.length) + ceil32(('cd', 356).length) + 968 len 8] = call.data[348 len 8] + mem[ceil32(_proof.length) + ceil32(('cd', 356).length) + 684] = sha3(_vcID, 0, _partyA, _partyB, call.data[164], call.data[196], call.data[228], call.data[260], call.data[292], call.data[324 len 24], call.data[348 len 8]) + mem[ceil32(_proof.length) + ceil32(('cd', 356).length) + 716] = 64 + mem[ceil32(_proof.length) + ceil32(('cd', 356).length) + 748] = ('cd', 356).length + mem[ceil32(_proof.length) + ceil32(('cd', 356).length) + 780 len ceil32(('cd', 356).length)] = call.data[cd[356] + 36 len ('cd', 356).length], mem[ceil32(_proof.length) + ('cd', 356).length + 352 len ceil32(('cd', 356).length) - ('cd', 356).length] + require ext_code.size(0xcb107c7d2a93e638b20342f46b10b9b6d81377bf) + delegate 0xcb107c7d2a93e638b20342f46b10b9b6d81377bf.0xdca95419 with: + gas gas_remaining wei + args sha3(_vcID, 0, _partyA, _partyB, call.data[164], call.data[196], call.data[228], call.data[260], call.data[292], call.data[324 len 24], call.data[348 len 8]), Array(len=('cd', 356).length, data=call.data[cd[356] + 36 len ('cd', 356).length]) + mem[ceil32(_proof.length) + ceil32(('cd', 356).length) + 680] = delegate.return_data[0] + if not delegate.return_code: + revert with ext_call.return_data[0 len return_data.size] + require return_data.size >= 32 + require addr(delegate.return_data[0]) == _partyA + mem[0] = _lcID + mem[32] = 2 + idx = 64 + s = 0 + t = 0 + while idx <= _proof.length: + _280 = mem[idx + 96] + if t + sha3(_vcID, 0, _partyA, _partyB, call.data[164], call.data[196], call.data[228], call.data[260], call.data[292], call.data[324 len 24], call.data[348 len 8]) >= mem[idx + 96]: + _282 = mem[64] + mem[mem[64] + 32] = mem[idx + 96] + mem[mem[64] + 64] = t + sha3(_vcID, 0, _partyA, _partyB, call.data[164], call.data[196], call.data[228], call.data[260], call.data[292], call.data[324 len 24], call.data[348 len 8]) + _283 = mem[64] + mem[mem[64]] = 64 + mem[64] = mem[64] + 96 + _284 = mem[_283] + t = _283 + 32 + v = _282 + 96 + u = mem[_283] + while u + sha3(_vcID, 0, _partyA, _partyB, call.data[164], call.data[196], call.data[228], call.data[260], call.data[292], call.data[324 len 24], call.data[348 len 8]) >= 32: + mem[v] = mem[t] + t = t + 32 + v = v + 32 + u = u - 32 + continue  + mem[_282 + floor32(mem[_283]) + 96] = mem[_283 + floor32(mem[_283]) + -(mem[_283] % 32) + 64 len mem[_283] % 32] or Mask(8 * -(mem[_283] % 32) + 32, -(8 * -(mem[_283] % 32) + 32) + 256, mem[_282 + floor32(mem[_283]) + 96]) + t = t + 32 + v = _280 + u = sha3(mem[mem[64] len _284 + _282 + -mem[64] + 96]) + continue  + _285 = mem[64] + mem[mem[64] + 32] = t + sha3(_vcID, 0, _partyA, _partyB, call.data[164], call.data[196], call.data[228], call.data[260], call.data[292], call.data[324 len 24], call.data[348 len 8]) + mem[mem[64] + 64] = _280 + _286 = mem[64] + mem[mem[64]] = 64 + mem[64] = mem[64] + 96 + _287 = mem[_286] + t = _286 + 32 + v = _285 + 96 + u = mem[_286] + while u + sha3(_vcID, 0, _partyA, _partyB, call.data[164], call.data[196], call.data[228], call.data[260], call.data[292], call.data[324 len 24], call.data[348 len 8]) >= 32: + mem[v] = mem[t] + t = t + 32 + v = v + 32 + u = u - 32 + continue  + mem[_285 + floor32(mem[_286]) + 96] = mem[_286 + floor32(mem[_286]) + -(mem[_286] % 32) + 64 len mem[_286] % 32] or Mask(8 * -(mem[_286] % 32) + 32, -(8 * -(mem[_286] % 32) + 32) + 256, mem[_285 + floor32(mem[_286]) + 96]) + t = t + 32 + v = _280 + u = sha3(mem[mem[64] len _287 + _285 + -mem[64] + 96]) + continue  + require 1 == t == channels[_lcID].field_3584 + virtualChannels[_vcID].field_1024 = _partyA + virtualChannels[_vcID].field_1280 = _partyB + virtualChannels[_vcID].field_256 = 0 + virtualChannels[_vcID].field_1792 = mem[ceil32(_proof.length) + 192] + virtualChannels[_vcID].field_2048 = mem[ceil32(_proof.length) + 224] + virtualChannels[_vcID].field_2304 = mem[ceil32(_proof.length) + 256] + virtualChannels[_vcID].field_2560 = mem[ceil32(_proof.length) + 288] + s = 11 + idx = ceil32(_proof.length) + 128 + while ceil32(_proof.length) + 192 > idx: + virtualChannels[_vcID][s].field_0 = mem[idx] + s = s + 1 + idx = idx + 32 + continue  + idx = 13 + while 13 > idx: + virtualChannels[_vcID][idx].field_0 = 0 + idx = idx + 1 + continue  + mem[0] = _vcID + mem[32] = 1 + virtualChannels[_vcID].field_768 = channels[_lcID].field_3328 + block.timestamp + virtualChannels[_vcID].field_8 = 1 + _394 = mem[ceil32(_proof.length) + 192] + _395 = mem[ceil32(_proof.length) + 224] + _396 = mem[64] + mem[mem[64] + 32] = 0 + mem[mem[64] + 64] = _partyA + mem[mem[64] + 96] = _partyB + mem[mem[64] + 128] = _394 + mem[mem[64] + 160] = _395 + mem[mem[64]] = 192 + mem[_396 + 192] = mem[96] + mem[_396 + 224 len ceil32(mem[96])] = mem[128 len ceil32(mem[96])] + if not mem[96] % 32: + log DidVCInit(bytes32 lcId, bytes32 vcId, bytes proof, uint256 sequence, address partyA, address partyB, uint256 balanceA, uint256 balanceB): +  mem[mem[64] len mem[96] + _396 + -mem[64] + 224], +  _lcID, +  _vcID, + else: + mem[floor32(mem[96]) + _396 + 224] = mem[floor32(mem[96]) + _396 + -(mem[96] % 32) + 256 len mem[96] % 32] + log DidVCInit(bytes32 lcId, bytes32 vcId, bytes proof, uint256 sequence, address partyA, address partyB, uint256 balanceA, uint256 balanceB): +  mem[mem[64] len floor32(mem[96]) + _396 + -mem[64] + 256], +  _lcID, +  _vcID, + diff --git a/bulk_compare.py b/bulk_compare.py new file mode 100644 index 00000000..1e085b31 --- /dev/null +++ b/bulk_compare.py @@ -0,0 +1,62 @@ +''' + + Half-baked regression testing. + + Run it to decompile a bunch of contracts and compare them with versions stored in the benchmark directory. + + Needs way more work. + +''' + +from subprocess import call +import urllib.request + +from utils.helpers import cache_fname + + +import coloredlogs +import logging + +logger = logging.getLogger(__name__) +log_level = logging.INFO + +coloredlogs.install(level=log_level, + fmt='%(asctime)s %(message)s', datefmt='%H:%M:%S', + field_styles={'asctime': {'color': 'white', 'faint': True}}) + + +addr_list = { + 'sweeper': '0x53F955c424F1378D67Bb5e05F728476dC75fB4bA', + 'ctf': '0x68cb858247ef5c4a0d0cde9d6f68dce93e49c02a', + 'unicorn': '0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7', + 'solidstamp': '0x165cfb9ccf8b185e03205ab4118ea6afbdba9203', + 'loops': '0xe2F42B417337fd9fD22631cad54DB8178655Fcd1', + 'kitties': '0x06012c8cf97BEaD5deAe237070F9587f8E7A266d', + 'etherdelta': '0x8d12A197cB00D4747a1fe03395095ce2A5CC6819', + 'ledger': '0xf91546835f756DA0c10cFa0CDA95b15577b84aA7', + 'buggy': '0x6025F65f6b2f93d8eD1efeDc752acfd4bdbCec3E', + 'modern': '0x7b220AC85B7ae8Af1CECCC44e183A862dA2eD517', + 'dao': '0xF835A0247b0063C04EF22006eBe57c5F11977Cc4' + } + +print(f'ctx count: {len(addr_list)}') + +i = 0 + +from time import sleep + +from filecmp import cmp, clear_cache + +for name, addr in addr_list.items(): + i += 1 + logger.info(f'#{i} of {len(addr_list)}: {addr} ({name})') + call(['python3.8','panoramix.py', addr, '--silent']) + sleep(0.1) + clear_cache() + + if not cmp(cache_fname(addr,'pan'), f'benchmark/{addr}.pan', shallow=False): + logger.error('comparison fail') + call(['diff', cache_fname(addr,'pan'), f'benchmark/{addr}.pan']) + else: + logger.info('looks good') + diff --git a/bulk_decompile.py b/bulk_decompile.py new file mode 100644 index 00000000..7ca29390 --- /dev/null +++ b/bulk_decompile.py @@ -0,0 +1,109 @@ +''' + + A helper script that takes all the binary code from cache_code, + and decompiles all of it, saving results into cache_pan + + It uses multi-processing. + + This is useful with testing new changes to the decompiler. Run the decompilation + on a few hundred contracts and look for any serious bugs / weird results. + + I also use it to decompile all the contracts with a new Eveem release. + Just fetch all the bytecodes into cache_code (e.g. from BigQuery), and then run + it through all of them. The strongest AWS instance can handle ~100 processes, + and after ~24h it should have all the bytecodes decompiled. + + It would be rather easy to optimise this with some kind of a database and + bytecode deduplication, but it would make the code more complex and dependency-ridden. + +''' + +import json + +from subprocess import call + +from queue import Queue + +import sys + +import threading +import time +import logging + +import os + +from various import addr_list, random_addresses + +logging.basicConfig(level=logging.DEBUG, + format='(%(threadName)-9s) %(message)s',) + +stuff = [] + +path = 'cache_code/' + +''' +uncomment to decompile all contracts in cache_code + +for dname in os.listdir(path): + if not os.path.isdir(path+dname): + continue + + for fname in os.listdir(path+dname): + addr = fname[:-4] + full_fname = path+dname+'/'+fname + + if os.stat(full_fname).st_size > 0: + stuff.append(addr) +''' + +stuff = random_addresses # or addr_list for more complex examples + +print('binaries found:', len(stuff)) + +if len(sys.argv)<3: + print("bulk_decompile start_loc end_loc num_threads [--force]") + exit() + +def queued(q): + while True: + addr = q.get() + if addr == 'die': + logging.debug('end of queue') + break + + logging.debug('addr: %s' % addr) + + call(['python3.8','panoramix.py', addr])#, '--upload']) + +stuff = sorted(stuff) + +if __name__ == '__main__': + + queue = Queue() + + threads = [] + + for i in range(int(sys.argv[3])): + t = threading.Thread(target=queued, name='thread_'+str(i), args=[queue]) + t.start() + threads.append(t) + + mini_queue = [] + + for addr in stuff[int(sys.argv[1]):int(sys.argv[2])]: + if '--force' not in sys.argv and os.path.isfile('cache_pan/'+addr[:5]+'/'+addr+'.pan'): + print('skipping '+addr) + continue + + mini_queue.append(addr) + if len(mini_queue) > 10: + queue.put(','.join(mini_queue)) + mini_queue = [] + + queue.put(','.join(mini_queue)) + + for i in range(int(sys.argv[3])): + queue.put('die') + + print('waiting for threads..') + diff --git a/core/__init__.py b/core/__init__.py new file mode 100644 index 00000000..a3e721ca --- /dev/null +++ b/core/__init__.py @@ -0,0 +1 @@ +import tilde.tilde \ No newline at end of file diff --git a/core/algebra.py b/core/algebra.py new file mode 100644 index 00000000..a1be2be3 --- /dev/null +++ b/core/algebra.py @@ -0,0 +1,1093 @@ +# coding: tilde + +# +# Algebra handles symbolic operations and comparisons +# +# E.g. if you want to figure out if ('ADD', 2, 'x') is bigger than 'x'., +# you call lt_op(add_op(2,'x'), 'x') +# +# The code here is crazy-fragile and inconsistent. +# Modifying it is akin to playing kal-toh ( https://www.youtube.com/watch?v=8i2idMe142s ) +# +# Should be refactored together with arithmetic.py +# I considered replacing it with a generic SMT-solver, but I'm quite sure a generic +# solution would be way slower. +# +# A better option would be to use a system similar to ACL2. +# + + +from utils.helpers import opcode, EasyCopy, all_concrete, to_exp2, cached + +from .variants import variants + +class CannotCompare(Exception): + pass + + +# copied from other modules, need to rework module structures +# to avoid circular dependencies +def mask_to_int(size, offset): + if offset < 0: + size = size + offset + if size < 1: + return 0 + return (2**size-1) + + return (2**size-1)*(2**offset) + + +@cached +def simplify(exp): + + if exp ~ ('max', *terms): + els = [simplify(e) for e in terms] + res = -2**256 + for e in els: + try: + res = max_op(res, e) + except: + return ('max', ) + tuple(els) + return res + + if exp ~ ('mask_shl', :size, :offset, :shl, :val): + size, offset, shl, val = simplify(size), simplify(offset), simplify(shl), simplify(val) + + if all_concrete(size, offset, shl, val): + return apply_mask(val, size, offset, shl) + + if (size, offset, shl) == (256, 0, 0): + return val + + if exp ~ ('add', *terms): + + res = 0 + for e in terms: + res = add_op(res, simplify(e)) + + assert type(res) != list + + return res + + if exp ~ ('mul', *terms): + res = 1 + for e in exp[1:]: + res = mul_op(res, simplify(e)) + assert type(res) != list + + return res + + return exp + + +def calc_max(exp): + if type(exp) != tuple: + return exp + + exp = (opcode(exp), ) + tuple(calc_max(e) for e in exp[1:]) + + if exp ~ ('max', *terms): + m = -(2**256) + + for e in terms: + if type(e) != int: + break + m = max(m, e) + else: + return m + + return exp + +@cached +def add_ge_zero(exp): + ''' + technically, it can return wrong results, e.g.: + + (sub (mask 4, 4, -4, 'sth') (mask 4, 0, 'sth')) + for sth 11...111 == 0 + for sth 0 == 0 + for sth 00010011 < 0 + + in practice it (hopefully) doesn't happen -- need to fix "variants" + to deliver more variants based on masks and other expressions? + + ''' + + assert opcode(exp) == 'add', exp + assert len(exp) > 2, exp + + exp = simplify(exp) + if type(exp) == int: + return exp >= 0 + +# print(exp) + var = tuple(simplify(calc_max(e)) for e in variants(exp)) + + if not all_concrete(*var): + return None + + if all(v >= 0 for v in var): + return True + + if all(v < 0 for v in var): + return False + + return None + + +def minus_op(exp): + + return mul_op(-1, exp) + + +def sub_op(left, right): + + if (type(left), type(right)) == (int, int): + return left-right # optimisation + + if left == 0: + return minus_op(right) + + if right == 0: + return left + + return add_op(left, minus_op(right)) + +exp = ('add', ) +assert exp ~ ('add', ...) +assert exp ~ ('add', *terms) + +def flatten_adds(exp): + res = exp + + while len([a for a in res if opcode(a) == 'add']) > 0: + exp = [] + for r in res: + if r ~ ('add', *terms): + assert len(r[1:]) > 1 + exp += r[1:] + else: + exp.append(r) + + res = exp + + return res + + +def max_to_add(exp): + if opcode(exp) != 'max': + return exp + + exp = exp[1:] + + for e in exp: + if opcode(e) != 'add' and type(e) != int: + return simplify_max(('max', )+exp) + + for e in exp: + if type(e) == int: +# print(exp) + m = min(x if type(x) == int else (x[1] if type(x) == tuple and len(x)>1 and type(x[1]) == int else 0) for x in exp) + # used to be x[1] but 0x0000136DAE58AFCF1EDd2071973d4a7a6fbe98A5 didn't work + res = ('max', e - m) + for e2 in exp: + if e2 != e: + res += (sub_op(e2, m), ) + + return ('add', m, res) + + m = 10**20 + for e in exp: + if type(e[1]) != int: + m = 0 + break + else: + m = min(m, e[1]) + + common = [] + first = exp[0] + for f in first: + if all(f in e[1:] for e in exp[1:]): + common.append(f) + + + if len(common) > 0: + a = add_op(m, *common) + else: + a = m + + res = [] + for e in exp: + res.append(sub_op(e, a)) + + if type(a) == int: + prefix = (a, ) + else: + prefix = a[1:] + + return ('add', ) + prefix + (simplify_max(('max', ) + tuple(res)), ) + + + + +@cached +def add_op(*args): + if len(args) == 1: + return args[0] + elif len(args) == 0: + return 0 + + assert len(args) > 1 + assert 'mul' not in args # some old bug, it's ok for ['mul'..] to be in args, but not 'mul' directly + + # speed optimisation + real = 0 + for r in args: + if type(r) in (int, float): + real += r + else: + break + else: + return real + # / speed + + res = flatten_adds(list(args)) + + for idx, r in enumerate(res): + if opcode(r) != 'mul': + res[idx] = mul_op(1, r) + + real = 0 + symbolic = [] + + for r in res: + assert opcode(r) != 'add' + + if type(r) in [int,float]: + real += r + continue + + assert opcode(r) == 'mul' + + # look at all the previously found symbolic expressions + # perhaps you can add to the previous one - if so, do it + # else, add this as a new symbolic exp + for idx, rr in enumerate(symbolic): + + tried = try_add(r, rr) or try_add(rr, r) + + if tried is not None and tried != 0: + + if tried ~ ('mul', ...): + symbolic[idx] = tried + + elif tried ~ ('mask_shl', int:size, 0, 256-size, :val): + symbolic[idx] = ('mul', 2**(256-size), val) + + else: + assert tried ~ ('add', int:num, :term), tried + + symbolic[idx] = term + real += num + + break + + else: + symbolic.append(r) + + symbolic = tuple(s for s in symbolic if s[1] != 0) + + # rem mul_1 + symbolic = tuple(s[2] if opcode(s)=='mul' and len(s) == 3 and s[1]==1 else s for s in symbolic) + + if real == 0: + res = symbolic + else: + if real > 0: + real = real % (2**256) + res = (real, ) + symbolic + + if len(res) == 0: + return 0 + + if len(res) == 1: + return res[0] + + + return ('add', ) + res + + +def bits(exp): + return mul_op(exp, 8) + +def mul_op(*args): + # super common + if len(args) == 1: + return args[0] + + if args ~ (int:num1, int:num2): + return args[0] * args[1] + + for a in args: + assert type(a) != list + + if p := to_exp2(a): + + rest = list(args) + rest.remove(a) + + exp = mul_op(*rest) + + assert type(exp) != list, exp + return mask_op(exp, size=256-p, shl=p) + + # flatten muls + res = tuple() + for a in args: + assert type(a) != list + if a ~ ('mul', *terms): + res += terms + else: + res += (a, ) + + # convert (mul (add x y) z) into (add (mul x z) (mul y z)) + # bc we're trying to keep a flat ordered hierarchy + + add_list = tuple(a for a in res if opcode(a) == 'add') + if len(add_list) > 0: + el = add_list[0] + assert opcode(el) == 'add' + + without = list(res) + without.remove(el) + + ret = tuple(mul_op(x, *without) for x in el[1:]) + return add_op(*ret) + + # multiply real numbers, add symbolic ones to output + real = 1 + symbolic = tuple() + for r in res: + assert opcode(r) != 'add' + + if r == 0: + return 0 + elif type(r) in (int, float): + real = int(real * r) # arithmetic, or regular? + else: + symbolic += (r, ) + + assert len(symbolic) == 0 or symbolic[0] != 'mul' # some old bug + + if len(symbolic) == 0: + return real + else: + return ('mul', real, ) + symbolic + +def get_sign(exp): + + if exp == 0: + return 0 + + elif ge_zero(sub_op(exp,1)) == True: + return 1 + + elif ge_zero(exp) == False: + return -1 + + else: + return None + +def safe_gt_zero(exp): + return safe_ge_zero(sub_op(exp, 1)) + +def safe_ge_zero(exp): + try: + return ge_zero(exp) + except CannotCompare: + return None + + +def to_bytes(exp): + if type(exp) == int: + return (exp+7) // 8, exp % 8 + + if type(exp) == tuple and exp[:4] == ('mask_shl', 253, 0, 3): + return exp[4], 0 + + if exp ~ ('mask_shl', int:size, int:offset, int:shl, :val) and shl >= 3: +# if opcode(exp) == 'mask_shl' and type(exp[1]) == int and type(exp[3]) == int and exp[3]>=3:# == ('mask_shl', 253, 0, 3): + return ('mask_shl', size, offset, shl - 3, val), 0 + + + if opcode(exp) == 'mul' and len(exp) == 3: + if exp[1] % 8 == 0: + return to_bytes(exp[1]) + + if opcode(exp) == 'add': + res = [] + for e in exp[1:]: + if type(e) == int or opcode(e) == 'mask_shl': + by, bi = to_bytes(e) + if bi == 0: + res.append(by) + else: + raise + + elif opcode(e) == 'mul' and len(e) == 3: + if e[1] % 8 == 0: + res.append(('mul', e[1] // 8, e[2])) + elif opcode(e[2]) == 'mask_shl' and e[2][:4] == ('mask_shl', 253, 0, 3): + res.append(('mul', e[1], e[2][4])) + else: + raise + + else: + raise + + return ('add', ) + tuple(res), 0 + + return mask_op(exp, shr=3), 0 + + +def divisible_bytes(exp): + # returns true if an expression can be divided by 8 (into bytes) without exceptions raised etc + try: + return True if to_bytes(exp)[1]==0 else False + except: + return False + +assert to_bytes(('add', ('mask_shl', 253, 0, 3, ('cd', ('add', 4, ('cd', 36)))), ('mul', -1, ('mask_shl', 253, 0, 3, ('add', 36, ('cd', 36)))))) == (('add', ('cd', ('add', 4, ('cd', 36))), ('mul', -1, ('add', 36, ('cd', 36)))), 0) + + +ge_zero_cache = {} +def ge_zero(exp): + if type(exp) == int: + return exp >= 0 + + if exp in ge_zero_cache: + if ge_zero_cache[exp] is None: + raise CannotCompare + return ge_zero_cache[exp] + + ge_zero_cache[exp] = _ge_zero(exp) + if ge_zero_cache[exp] is None: + raise CannotCompare + return ge_zero_cache[exp] + +def _ge_zero(exp): + # returns True if exp>=0, False if exp<=0, CannotCompare if it doesn't know + + if type(exp) in (int, float): + return exp >= 0 + + if type(exp) == str: + return True + + if opcode(exp) == 'mul': + counter = 1 + for e in exp[1:]: + c = ge_zero(e) + if c == True: + counter *= 1 + elif c == False: + counter *= -1 + + return counter >= 0 + + if opcode(exp) == 'bool': + return True + + if opcode(exp) == 'mask_shl': + return ge_zero(exp[4]) + + if opcode(exp) in ['cd', 'storage','msize']: + return True + + if opcode(exp) in ['add', 'or']: + return add_ge_zero(exp) + + if opcode(exp) in ('var', 'ext_call.return_data'): + return True + + raise CannotCompare + +@cached +def lt_op(left, right): # left < right + if left ~ ('add', int:num, ('max', *terms)): + left = ('max', ) + tuple(add_op(t, num) for t in terms) + + if right ~ ('add', int:num, ('max', *terms)): + right = ('max', ) + tuple(add_op(t, num) for t in terms) + + if type(left) == int and type(right) == int: + return left < right + + if right ~ ('max', ...): + left, right = right, left + + if left ~ ('max', ...): + results = [lt_op(l, right) for l in left[1:]] + + if all([r is True for r in results]): + return True + + if any([r is False for r in results]): + return False + + return None + + if left ~ ('add', _, ('var', :num)) and \ + right ~ ('add', _, ('var', :num2)) and \ + num != num2: + raise CannotCompare + + sleft = str(left) + sright = str(right) + + if left ~ ('var', :num) and str(left) not in sright: + raise CannotCompare + + if right ~ ('var', :num) and str(right) not in sleft: + raise CannotCompare + + if type(right) == int and left ~ ('add', int:num, ('var', _)): + if right >= num: + return False + else: + raise CannotCompare + + if type(left) == int and right ~ ('add', int:num, ('var', _)): + if left < num: + return True + else: + raise CannotCompare + + return lt2(left, right) + +def lt2(left, right): + subbed = sub_op(right, left) + + sgn = get_sign(subbed) + + if sgn is None: + raise CannotCompare + else: + return sgn > 0 + +def safe_lt_op(left, right): + + try: + return lt_op(left, right) + except CannotCompare: + return None + +def safe_le_op(left, right): + try: + return le_op(left, right) + except CannotCompare: + return None + +def simplify_max(exp): + if opcode(exp) != 'max': + return exp + + if opcode(exp) == 'max': + res = ('max', ) + for e in exp[1:]: + if opcode(e) == 'max': + res += e[1:] + else: + res += (e, ) + + return res + +@cached +def le_op(left, right): # left <= right + +# right = add_op(1, right) +# return lt_op(left, right) + + if opcode(left) == 'max': + left = max_to_add(left) + + if opcode(right) == 'max': + right = max_to_add(right) + + if type(left) in (int, float) and type(right) in (int, float): + return left <= right + + subbed = sub_op(right, left) + + return ge_zero(subbed) + + +def max_op(left, right): + try: + + if le_op(left, right): + return right + else: + return left + + except CannotCompare: + + if le_op(right, left): + return left + else: + return right + +def safe_max_op(left, right): + try: + return max_op(left, right) + except CannotCompare: + return None + + +def _max_op(base, what): + # compares base with what, different from algebra's max because it can return (max, x,y,z) + if opcode(base) != 'max': + + if safe_lt_op(what, base) is True: + return base + + if safe_lt_op(what, base) is False: + return what + + return ('max', base, what) + + else: # opcode(base) == 'max': + res = [] + for b in base[1:]: + cmp = safe_lt_op(what, b) + + if cmp is True: + return base + + if cmp is False: + res.append(what) + + if cmp is None: + res.append(b) + + res.append(what) + + res = tuple(set(res)) + if len(res)>1: + return ('max',)+res + else: + return res[0] + +assert _max_op(('max', 128, 'unknown'), 200) == ('max', 200, 'unknown') +assert _max_op(('max', 128, 'unknown'), 64) == ('max', 128, 'unknown') + + +def div_op(a, b): + assert type(a) != list + assert type(b) != list + if b == 1: + return a + + if type(a) != int and type(b) == int: + if b < 0: + a = mul_op(-1, a) + b = -b + + if to_exp2(b): + return mask_op(a, size=256-to_exp2(b), shr=to_exp2(b)) + + + if type(a) != int or type(b) != int: +# return None + return ('div', a, b) + + else: + return a // b + + +def safe_min_op(left, right): + try: + return min_op(left, right) + except CannotCompare: + return None + +def min_op(left, right): + + try: + if le_op(left, right): + + return left + else: + return right + + except CannotCompare: + if le_op(right, left): + return right + else: + return left + + + +def or_op(*args): + if len(args) == 1: + return args[0] +# assert len(args) > 1 + + res = tuple() + + for r in args: + if r == 0: + pass + + elif r ~ ('or', *terms): + assert len(terms)>1 + res += terms + + elif r not in res: + res += (r, ) + + if len(res) == 0: + return 0 + + if len(res) == 1: + return res[0] + + assert len(res)>1 + + return ('or', ) + res + +def neg_mask_op(exp, size, offset): + exp1 = mask_op(exp, size = sub_op(256, add_op(size, offset)), offset = add_op(offset,size)) + exp2 = mask_op(exp, size = offset, offset = 0) + + return or_op(exp1, exp2) + +def strategy_concrete(size, offset, shl, exp_size, exp_offset, exp_shl, exp): + ''' + This is an optimised version of strategy_1, the program would + work correctly without it, but much slower, since concrete values + for masks are very common + ''' + + outer_left = offset + size + outer_right = offset + + inner_left = exp_offset + exp_size + exp_shl + inner_right = exp_offset + exp_shl + + left, right = min(outer_left, inner_left), max(outer_right, inner_right) + + if inner_left <= inner_right: + return 0 + if inner_left <= outer_right: + return 0 + + new_offset = right - exp_shl + new_size = left - right + new_shl = shl + exp_shl + + if new_size > 0: + return mask_op(exp, size=new_size, offset=new_offset, shl=new_shl) + else: + return 0 + +def strategy_0(size, offset, shl, exp_size, exp_offset, exp_shl, exp): + return 0 if exp == 0 else None + +def strategy_1(size, offset, shl, exp_size, exp_offset, exp_shl, exp): + # default one + + outer_left = add_op(offset, size) + outer_right = offset + + inner_left = add_op(exp_offset, exp_size, exp_shl) + inner_right = add_op(exp_offset, exp_shl) + + left, right = safe_min_op(outer_left, inner_left), safe_max_op(outer_right, inner_right) + + if safe_le_op(inner_left, inner_right) is True: + return 0 + if safe_le_op(inner_left, outer_right) is True: + return 0 + + if None not in (left, right): + new_offset = sub_op(right, exp_shl) + new_size = sub_op(left, right) + new_shl = add_op(shl, exp_shl) + + gezero = safe_ge_zero(new_size) + + if gezero is not False and new_size != 0: + return mask_op(exp, size=new_size, offset=new_offset, shl=new_shl) + + elif gezero is False or new_size==0: + return 0 + +def strategy_2(size, offset, shl, exp_size, exp_offset, exp_shl, exp): + # move inner left by size, apply mask, and move back + + return strategy_1(size, sub_op(offset, exp_size), + add_op(shl, exp_size), exp_size, exp_offset, + sub_op(exp_shl, exp_size), exp) + +def strategy_3(size, offset, shl, exp_size, exp_offset, exp_shl, exp): + # move inner left by it's shl, apply mask, move back + + return strategy_1(size, sub_op(offset, exp_shl), add_op(shl, exp_shl), exp_size, exp_offset, 0, exp) + + +def strategy_final(size, offset, shl, exp_size, exp_offset, exp_shl, exp): + + return ('mask_shl', size, offset, shl, ('mask_shl', exp_size, exp_offset, exp_shl, exp)) + + +def mask_mask_op(size, offset, shl, exp_size, exp_offset, exp_shl, exp): + if all_concrete(offset, shl, exp_offset, exp_shl, exp_size, size): + return strategy_concrete(size, offset, shl, exp_size, exp_offset, exp_shl, exp) + + strategies = (strategy_0, strategy_1, strategy_2, strategy_3, strategy_final) + + for s in strategies: + res = s(size, offset, shl, exp_size, exp_offset, exp_shl, exp) + if res is not None: + return res + + assert False + +mask_dict = {} +def mask_op(exp, size = 256, offset = 0, shl = 0, shr = 0): + if size == 0: + return 0 + + idx = size, offset, shl, shr, exp + if idx in mask_dict: + return mask_dict[idx] + + ret = _mask_op(exp, size, offset, shl, shr) + mask_dict[idx] = ret + return ret + +def _mask_op(exp, size = 256, offset = 0, shl = 0, shr = 0): + if size == 0: + return 0 +# if (size, offset, shl, shr) == (256, 0, 0, 0): +# return exp + + if exp ~ ('div', :num, 1): + exp = num # should be done somewhere else, but it's 0:37 at night + + shl = sub_op(shl, shr) + shr = 0 + + + if exp ~ ('storage', :stor_size, :stor_offset, :stor_idx): + # trimming the storage inside + + #if safe_le_op(offset, minus_op(shl)): + # offset = minus_op(shl) + + # for shl > 0, we are either dealing with multiplication (e.g. store * 32 - happens often) + # or with trimming the storage and moving around (store << 96) + # e.g. 0xfF18DBc487b4c2E3222d115952bABfDa8BA52F5F, setupToken + # below heuristics handle all this, and deliver good results in practice + # but may be incorrect in some unusual cases + + if type(shl) == int and (shl > 0 and shl < 8): + pass + + elif type(shl) == int and shl >= 8 and size == 256 \ + and (new_exp := apply_mask_to_storage(exp, size-shl, offset, shl)): + return new_exp + + elif new_exp := apply_mask_to_storage(exp, size, offset, shl): + return new_exp + + if exp ~ ('or', *rest): + return or_op( *[mask_op(e, size, offset, shl, shr) for e in rest]) + + if exp ~ ('mask_shl', *params): + shl = sub_op(shl, shr) + double_mask = mask_mask_op(size, offset, shl, *params) + + return double_mask + + if type(size) != int or size > 0: + return ('mask_shl', size, offset, sub_op(shl,shr), exp) + else: + return 0 + +def apply_mask_to_storage(exp, size, offset, shl): + assert exp ~ ('storage', :stor_size, :stor_offset, :stor_idx) + +# shr = minus_op(shl) + + stor_offset = add_op(stor_offset, offset) + stor_size = sub_op(stor_size, offset) + shl = add_op(shl, offset) + offset = 0 + + if safe_lt_op(size, stor_size): + stor_size = size + + if safe_le_op(stor_size, 0) is True: + return 0 + + res = ('storage', stor_size, stor_offset, stor_idx) + + shr = 0 + + if shl == 0: + return res + else: + if res ~ ('storage', size, 0, :stor_idx) and offset == 0: + shr = minus_op(shl) + return ('storage', size, shr, stor_idx) + +def apply_mask(val, size, offset=0, shl=0): + assert all_concrete(val, size, offset, shl) + + mask = mask_to_int(size, offset) + val = val & mask + + if shl > 0: + val = val << shl + + if shl < 0: + val = val >> -shl + + return val + + +def try_add(self, other): + if (res := _try_add(self, other)) is not None: + return res + + return __try_add(self, other) + + +def __try_add(self, other): + if self ~ ('mul', :num, ('mask_shl', int:size, int:off, int:shl, :val)) and shl > 0: + self = ('mul', num + 2**shl, ('mask_shl', size+shl, off, 0, val)) + + if other ~ ('mul', :num, ('mask_shl', int:size, int:off, int:shl, :val)) and shl > 0: + other = ('mul', num + 2**shl, ('mask_shl', size+shl, off, 0, val)) + + return _try_add(self, other) + +def _try_add(self, other): + # tries to add (mul a x) (mul b y) + # 'self' name to be refactored + +# so proud of this /s + + assert self ~ ('mul', int, *terms) + if len(terms) > 1: + return None + + assert other ~ ('mul', int, *terms) + if len(terms) > 1: + return None + + + if self ~ ('mul', -1, :val) and \ + other ~ ('mul', :mul, ('mask_shl', int:other_size, 0, int:shl, val)) and other_size == 256 - shl: + mul *= 2**shl - 1 + return mul_op(mul, val) + +# if self, other == mul(x, exp), mul(y, exp) +# => mul(x+y, exp) + + if self ~ ('mul', int:x, :exp) and \ + other ~ ('mul', int:y, exp): + return ('mul', x + y, exp) + + +# if self, other == mul(x, mask_shl(256-y, y, 0, exp)), +# mul(x, mask_shl(y, 0, 0, exp)) +# => mul(x, (mask_shl, 256, 0, 0, exp)) + + if self ~ ('mul', :x, :self_mask) and \ + other ~ ('mul', x, :other_mask): + + if opcode(self_mask) == 'mask_shl' and opcode(other_mask) == 'mask_shl' and\ + self_mask[1]+self_mask[2]==256 and self_mask[2] == other_mask[1] and other_mask[2] == 0 and\ + self_mask[3] == other_mask[3] and self_mask[4] == other_mask[4]: + return mul_op(self[1], mask_op(self_mask[4], size=256, offset=0, shl=self_mask[3])) + + +# if self, other == mul(x, mask_shl(256-y, y, 0, ADD(2**y - 1, mul(1, exp)))), +# mul(-x, exp) +# => mul(x, 2**y - mask_op(exp, size=y)) + + ''' + to be tested: + + if other ~ ('mul', :x, :exp) and \ + self ~ ('mul', -x, ('mask_shl', 256-y, int:y, 0, ('add', 2**y-1, ('mul', 1, exp)))): + return mul_op(-x, sub_op(2**y, mask_op(x, size=y))) + ''' + + if opcode(self[2]) == 'mask_shl' and opcode(other[2]) != 'mask_shl' and self[1] == minus_op(other[1]): + x = other[2] + for y in [3,4,5,6,7,8,16,32,64,128]: + m = ('mask_shl', 256-y, y, 0, ('add', 2**y-1, ('mul', 1, x) ) )# - x #== 2**y-1 - Mask(y,0,0, x) + if self[2] == m: + return mul_op(self[1], sub_op(2**y, mask_op(x, size=y))) + + + +# if self, other == mul(-x, mask_shl(256-y, y, 0, exp), +# mul(x, exp) +# => mul(x, mask_op(exp, size=y)) + + if opcode(self[2]) == 'mask_shl' and opcode(other[2]) != 'mask_shl' and self[1] == minus_op(other[1]): + x = other[2] + for y in [3,4,5,6,7,8,16,32,64,128]: + m = ('mask_shl', 256-y, y, 0, x)# - x #== 2**y-1 - Mask(y,0,0, x) + if self[2] == m: + return mul_op(other[1], mask_op(x, size=y)) + m = ('mask_shl', 251-y, y, 0, x) # close enough ;) + # happens in memcopy loops, shouldn't cause significant bugs + # and helps clean up the code a lot + + if self[2] == m: + return mul_op(other[1], mask_op(x, size=y)) + + # other + + if self ~ ('mul', :num, ('mask_shl', 256, 0, 0, :exp)): + self = ('mul', num, exp) + + if other ~ ('mul', :num, ('mask_shl', 256, 0, 0, :exp)): + other = ('mul', num, exp) + + assert self ~ ('mul', int:num1, :exp1) + assert other ~ ('mul', int:num2, :exp2) + + if exp1 == exp2: + return mul_op(num1 + num2, exp1) + + # mask 256,0,0,x - 6,0,0,x == 250,6,0,x + + if num1 == -num2 and exp2 ~ ('mask_shl', int:size, 0, 0, exp1): + return ('mul', num1, ('mask_shl', 256-size, size, 0, exp1)) + + if num1 == -num2 and \ + exp1 ~ ('mask_shl', int:size1, 0, 0, :x) and \ + exp2 ~ ('mask_shl', int:size2, 0, 0, x) and \ + size2 < size1: + return ('mul', num1, ('mask_shl', size1-size2, 0, 0, x)) + + return None + +def erase_op(exp, size, shl): + assert False + +assert max_to_add(('max', 480, ('add', 356, ('cd', ('add', 4, ('cd', 36)))))) == ('add', 356, ('max', 124, ('cd', ('add', 4, ('cd', 36))))) +assert add_op(64, ('var', 4)) == ('add', 64, ('var', 4)) + +l = ('add', 128, ('cd', ('add', 4, ('cd', 36)))) +r = ('add', 128, ('mask_shl', 251, 5, 0, ('add', 31, ('cd', ('add', 4, ('cd', 36)))))) +assert le_op(l, r) == True, le_op(r, l) + +#exit() diff --git a/core/arithmetic.py b/core/arithmetic.py new file mode 100644 index 00000000..faaf0288 --- /dev/null +++ b/core/arithmetic.py @@ -0,0 +1,560 @@ +# coding: tilde + +# +# Originally taken from py-evm/eth/vm/logic/arithmetic.py +# +# Then, comments removed to make it more in line with the rest of the codebase. +# +# And then modified to do things it was never designed to do. +# +# Now it's a part of the Panoramix code base. +# It was assimilated, resistance was futile. +# +# If you look closely, you'll still see the original peeking through +# screaming inside: "what have you done to meeeeeee!" +# + +from utils.helpers import opcode +import logging + + +from copy import copy +import core.algebra as algebra +from .masks import get_bit + +logger = logging.getLogger(__name__) + +UINT_256_CEILING = 2**256 +UINT_255_MAX = 2**255 - 1 +UINT_256_MAX = 2**256 - 1 + +def to_real_int(exp): + if type(exp) == int and get_bit(exp, 255): + return -sub(0, exp) + else: + return exp + +def unsigned_to_signed(value): + if value <= UINT_255_MAX: + return value + else: + return value - UINT_256_CEILING + + +def simplify_bool(exp): + if exp ~ ('iszero', :term): + inside = simplify_bool(term) + + if inside ~ ('iszero', :t2): + return t2 + else: + return is_zero(inside) # this had a bug and it went on unnoticed + # does this check ever get executed? + + if opcode(exp) == 'bool': + return exp[1] + + return exp + + +def and_op(*args): + + assert len(args) > 1 + left = args[0] + + if len(args) > 2: + right = and_op(*args[1:]) + else: + right = args[1] + + if type(left) == int and type(right) == int: + return left & right + + res = tuple() + + if opcode(left) == 'and': + res += left[1:] + else: + res += (left, ) + + if opcode(right) == 'and': + res += right[1:] + else: + res += (right, ) + + return ('and', ) + res + +def comp_bool(left, right): + if left == right: + return True + if left == ('bool', right): + return True + if ('bool', left) == right: + return True + + return None + +def is_zero(exp): + + if type(exp) == int: + return exp == 0 + + if type(exp) != tuple: + return ('iszero', exp) + + if opcode(exp) == 'iszero': + if opcode(exp[1]) == 'eq': + return exp[1] + elif opcode(exp[1]) == 'iszero': + return is_zero(exp[1][1]) + else: + return ('bool', exp[1]) + + if opcode(exp) == 'bool': + return is_zero(exp[1]) + + if opcode(exp) == 'or': + res = [] + for r in exp[1:]: + res.append(is_zero(r)) + return and_op(*res) + + if opcode(exp) == 'and': + res = [] + for r in exp[1:]: + res.append(is_zero(r)) + return algebra.or_op(*res) + + if opcode(exp) == 'le': + return ('gt', exp[1], exp[2]) + + if opcode(exp) == 'lt': + return ('ge', exp[1], exp[2]) + + if opcode(exp) == 'ge': + return ('lt', exp[1], exp[2]) + + if opcode(exp) == 'gt': + return ('le', exp[1], exp[2]) + + if opcode(exp) == 'sle': + return ('sgt', exp[1], exp[2]) + + if opcode(exp) == 'slt': + return ('sge', exp[1], exp[2]) + + if opcode(exp) == 'sge': + return ('slt', exp[1], exp[2]) + + if opcode(exp) == 'sgt': + return ('sle', exp[1], exp[2]) + + + return ('iszero', exp) + + +def eval_bool(exp, known_true = True, symbolic=True): + + if exp == known_true: + return True + + if is_zero(exp) == known_true: + return False + + if exp == is_zero(known_true): + return False + + if type(exp) == int: + return exp > 0 + + if exp in (True, False): + return True + + + if opcode(exp) == 'bool': + return eval_bool(exp[1], known_true=known_true, symbolic=symbolic) + + + if opcode(exp) == 'iszero': + e = eval_bool(exp[1], known_true=known_true, symbolic=symbolic) + if e != None: + return not e + + + if opcode(exp) == 'or': + res = 0 + for e in exp[1:]: + ev = eval_bool(e, known_true=known_true, symbolic=symbolic) + if type(ev) != None: + res = res or ev + else: + return None + + return res + + #'ge', 'gt', 'eq' - tbd + if opcode(exp) in ['le', 'lt'] and \ + opcode(exp) == opcode(known_true): + if exp[1] == known_true[1]: + # ('le', x, sth) while ('le', x, sth2) is known to be true + if eval_bool((opcode(exp), known_true[2], exp[2])) is True: + return True + + if not symbolic: + r = eval(exp) + if type(r) == int: + return r != 0 + else: + return None + + + if opcode(exp) == 'le': + left = eval(exp[1]) + right = eval(exp[2]) + + if left == right: + return True + + if type(left) == int and type(right) == int: + return left <= right + try: + return algebra.le_op(left, right) + except: + return None + + + if opcode(exp) == 'lt': + left = eval(exp[1]) + right = eval(exp[2]) + + if left == right: + return False + + if type(left) == int and type(right) == int: + return left < right + + try: + return algebra.lt_op(left, right) + except: + return None + + + if opcode(exp) == 'gt': + left = eval(exp[1]) + right = eval(exp[2]) + + if type(left) == int and type(right) == int: + return left > right + + if left == right: + return False + else: +# return None + try: #a > b iff b < a iff b+1 <= a + le = algebra.lt_op(add_op(left, 1), right,1) + logger.debug('le %s %s %s', le, left, right) + + if le == True: + return False + if le == False: + return True + if le is None: + return None + except: + pass + + if opcode(exp) == 'ge': + left = eval(exp[1]) + right = eval(exp[2]) + + if type(left) == int and type(right) == int: + return left >= right + + if left == right: + return True + else: + try: + lt = algebra.lt_op(left, right) + if lt == True: + return False + if lt == False: + return True + if lt is None: + return None + except: + pass + + + if opcode(exp) == 'eq': + left = eval(exp[1]) + right = eval(exp[2]) + + if left == right: + return True + + if algebra.sub_op(left, right) == 0: + return True + + aeval = eval(exp) + + return None + + + +def add(left, right): + return (left + right) & UINT_256_MAX + +def addmod(left, right, mode): + if mod == 0: + return 0 + else: + return (left + right) % mod + +def sub(left, right): + if left == right: + return 0 + else: + return (left - right) & UINT_256_MAX + +def mod(value, mod): + if mod == 0: + return 0 + else: + return value % mod + +def smod(value, mod): + value, mod = map( + unsigned_to_signed, + (value, mod), + ) + + pos_or_neg = -1 if value < 0 else 1 + + if mod == 0: + return 0 + else: + return (abs(value) % abs(mod) * pos_or_neg) & UINT_256_MAX + +def mul(left, right): + if left == 0 or right == 0: + return 0 + + return (left * right) & UINT_256_MAX + +def mulmod(left, right, mod): + if mod == 0: + return 0 + else: + return (left * right) % mod + +def div(numerator, denominator): + if numerator == 0: + return 0 + + elif denominator == 0: + return 0 + else: + return (numerator // denominator) & UINT_256_MAX + +def not_op(exp): + return UINT_256_MAX - exp + +def sdiv(numerator, denominator): + numerator, denominator = map( + unsigned_to_signed, + (numerator, denominator), + ) + + pos_or_neg = -1 if numerator * denominator < 0 else 1 + + if denominator == 0: + return 0 + else: + return (pos_or_neg * (abs(numerator) // abs(denominator))) + + return signed_to_unsigned(result) + +def exp(base, exponent): + + if exponent == 0: + return 1 + elif base == 0: + return 0 + else: + return pow(base, exponent, UINT_256_CEILING) + +def signextend(bits, value): + + if bits <= 31: + testbit = bits * 8 + 7 + sign_bit = (1 << testbit) + if value & sign_bit: + return value | (UINT_256_CEILING - sign_bit) + else: + return value & (sign_bit - 1) + else: + return value + +def shl(shift_length, value): + + if shift_length >= 256: + return 0 + else: + return (value << shift_length) & UINT_256_MAX + + +def shr(shift_length, value): + + if shift_length >= 256: + return 0 + else: + return (value >> shift_length) & UINT_256_MAX + + +def sar(shift_length, value): + + value = unsigned_to_signed(value) + + if shift_length >= 256: + return 0 if value >= 0 else UINT_255_NEGATIVE_ONE + else: + return (value >> shift_length) & UINT_256_MAX + +opcodes = { + 'add': add, + 'addmod': addmod, + 'sub': sub, + 'mod': mod, + 'smod': smod, + 'mul': mul, + 'mulmod': mulmod, + 'div': div, + 'sdiv': sdiv, + 'exp': exp, + 'signextend': signextend, + 'shl': shl, + 'shr': shr, + 'sar': sar, +} + + +def or_op(left, right=0): + + return left | right + +def xor(left, right): + + return left ^ right + +def byte_op(position, value): + + if position >= 32: + return 0 + else: + return (value // pow(256, 31 - position)) % 256 + +def lt(left, right): + + if left < right: + result = 1 + else: + result = 0 + + return result + +def gt(left, right): + + if left > right: + result = 1 + else: + result = 0 + + return result + +def le(left, right): + return lt(left, right) | eq(left, right) + +def ge(left, right): + return gt(left, right) | eq(left, right) + +def sle(left, right): + + return slt(left, right) | eq(left, right) + +def sge(left, right): + + return sge(left, right) | eq(left, right) + +def slt(left, right): + + left = unsigned_to_signed(left) + right = unsigned_to_signed(right) + + if left < right: + result = 1 + else: + result = 0 + + return result + + +def sgt(left, right): + + left = unsigned_to_signed(left) + right = unsigned_to_signed(right) + + if left > right: + result = 1 + else: + result = 0 + + return result + + +def eq(left, right): + + if left == right: + result = 1 + else: + result = 0 + + return result + + +def eval(exp): + exp = copy(exp) + + if type(exp) != tuple: + return exp + + for i,p in enumerate(exp[1:]): + if opcode(p) in opcodes: + exp = exp[:i+1] + (eval(p),) + exp[i+2:] + + for p in exp[1:]: + if type(p) != int: + return exp + + if exp[0] in opcodes: + return opcodes[exp[0]](*exp[1:]) + + return exp + + +opcodes.update({ + 'and': and_op, + 'or': or_op, + 'xor': xor, + 'not': not_op, + 'byte': byte_op, + 'eq': eq, + 'lt': lt, + 'le': le, + 'gt': gt, + 'sgt': sgt, + 'slt': slt, + 'ge': ge, + 'gt': gt, + 'sge': sge, + 'sle': sle, + }) diff --git a/core/masks.py b/core/masks.py new file mode 100644 index 00000000..1e16f2de --- /dev/null +++ b/core/masks.py @@ -0,0 +1,176 @@ +from utils.helpers import opcode, to_exp2, cleanup_mul_1, cached +from .algebra import sub_op, mul_op + +def type_to_mask(s): + lookup = { + 'bool': 1, + 'uint8': 8, + 'uint16': 16, + 'uint32': 32, + 'uint64': 64, + 'int8': 8, + 'bytes1': 1, + + 'int16': 16, + 'bytes2': 16, + + 'int32': 32, + 'bytes4': 32, + + 'int64': 64, + 'bytes8': 64, + + + 'int128': 128, + 'uint128': 128, + 'bytes16': 128, + + 'addr': 160, + 'address': 160, + + 'uint256': 256, + 'bytes32': 256, + 'int256': 256, + 'int': 256, + 'uint': 256, + } + + if s in lookup: + return lookup[s] + else: + return None + +def mask_to_type(num, force = False): + # force causes to always return some soft of type, + # if it's not found in the lookup then the smallest + # one that fits + + lookup = { + 1: 'bool', + 8: 'uint8', + 16: 'uint16', + 32: 'uint32', + 64: 'uint64', + 128: 'uint128', + 160: 'addr', + 256: 'uint256', + } + + if type(num) != int: + return num + + if num in lookup: + return lookup[num] + + elif force: + if num>256: + logger.warn(f'big type {num} bytes') + return 'big' + str(num) + + for mask, res in lookup.items(): + if mask > num: + return lookup[mask] + + assert False + + else: + return None + +def get_bit(num, pos): + return 1 if num & 2**pos > 0 else 0 + + +def mask_to_int(size, offset): + assert type(size) in (int, float), size + assert type(offset) in (int, float), offset + + return (2**size-1) * (2**offset) + +def find_mask(num): + # finds a mask that encompasses the number + + assert type(num) == int + + i = 0 + while get_bit(num, i)==0 and i<256: + i += 1 + mask_pos = i - i % 8 + + mask_pos_plus_len = 256 + + while i<256: + if get_bit(num, i) != 0: + mask_pos_plus_len = i - i%8+8 + i += 1 + + return (mask_pos_plus_len - mask_pos, mask_pos) + +assert find_mask(0xfabba10000) == (24,16) +assert find_mask(0xfabba10000) == (24,16) +assert find_mask(0x7ABBA20000) == (24,16) # rounding to eights + +@cached +def to_mask(num): + num = cleanup_mul_1(num) + + if opcode(num) == 'not': + return to_neg_mask(num[1]) + + if opcode(num) == 'sub': + if opcode(num[1]) == 'exp' and num[2] == 1: + mul = to_exp2(num[1][1]) + if mul == None: + return None + + mask_pos = 0 + mask_len = mul_op(mul, num[1][2]) + + # add type assert, that num[1][2] < 32? + + return (mask_len, sub_op(256,mask_len)) + + if opcode(num) == 'add' and num[1] == -1: + return to_mask(('sub', num[2], 1,)) + + if type(num) != int: + return None + + i = 0 + while get_bit(num, i)==0 and i<256: + i += 1 + mask_pos = i + + while get_bit(num, i)==1 and i<256: + i += 1 + mask_pos_plus_len = i + + while i<256: + if get_bit(num, i) != 0: + return None + i += 1 + + return mask_pos_plus_len - mask_pos, mask_pos + + +def to_neg_mask(num): + if opcode(num) == 'not': + return to_mask(num[1]) + + if type(num) != int: + return None + + i = 0 + while get_bit(num, i)==1 and i<256: + i += 1 + mask_pos = i + + while get_bit(num, i)==0 and i<256: + i += 1 + mask_pos_plus_len = i + + while i<256: + if get_bit(num, i) != 1: + return None + i += 1 + + return mask_pos_plus_len - mask_pos, mask_pos diff --git a/core/memloc.py b/core/memloc.py new file mode 100644 index 00000000..15b60fe5 --- /dev/null +++ b/core/memloc.py @@ -0,0 +1,639 @@ +# coding: tilde +from .algebra import add_op, lt_op, le_op, CannotCompare, to_bytes, safe_gt_zero, safe_le_op +from utils.helpers import opcode, cached, before_after, contains, replace, is_array +import logging + +import sys + +from utils.profiler import checkpoint, checkpoint_start + +logger = logging.getLogger(__name__) + +logger.level = logging.CRITICAL# INFO + +from .masks import find_mask + +opcode = opcode + +from .algebra import simplify, calc_max, add_ge_zero, minus_op, sub_op, flatten_adds, max_to_add +from .algebra import add_op, bits, mul_op, get_sign, safe_ge_zero, ge_zero, lt_op, safe_lt_op, safe_le_op +from .algebra import simplify_max, le_op, max_op, safe_max_op, safe_min_op, min_op, or_op, neg_mask_op, mask_op +from .algebra import apply_mask_to_storage, apply_mask, try_add, all_concrete +from .algebra import lt_op + +def apply_mask_to_range(memloc, size, offset): + assert memloc ~ ('range', :range_pos, :range_len) + + size_bytes, size_bits = to_bytes(size) + offset_bytes, offset_bits = to_bytes(offset) + + assert offset_bits == size_bits == 0, (offset_bits, size_bits) # for now + assert safe_le_op(add_op(size_bytes, offset_bytes), range_len) is True, (size_bytes, offset_bytes, range_len) # otherwise we need to learn to handle that + + range_pos = add_op(range_pos, sub_op(range_len, add_op(size_bytes, offset_bytes))) + range_len = size_bytes #sub_op(range_len, add_op(offset_bytes, size_bytes)) + + return ('range', range_pos, range_len) + +assert apply_mask_to_range(('range', 212, 32), 160, 0) == ('range', 224, 20) +assert apply_mask_to_range(('range', 212, 32), 160, 96) == ('range', 212, 20) + +def cmp_to_key(mycomp): + class K: + def __init__(self, obj): + self.obj = obj + + def __lt__(self, other): + return lt_op(self.obj, other.obj) + + return K(mycomp) + +def split_or(value): + orig_value = value + + if opcode(value) not in ('or', 'mask_shl'): + return [(256,0,value)] + + if opcode(value) == 'mask_shl': + value = ('or', value) + + assert value ~ ('or', *terms) + + + ret_rows = [] + + for row in terms: + if row ~ ('bool', :arg): + row = ('mask_shl', 8, 0, 0, ('bool', arg)) # does weird things if size == 1, in loops.activateSafeMode + + if row == 'caller': + row = ('mask_shl', 160, 0, 0, 'caller') # does weird things if size == 1, in loops.activateSafeMode + + if row == 'block.timestamp': + row = ('mask_shl', 64, 0, 0, 'caller') # does weird things if size == 1, in loops.activateSafeMode + + + if row ~ ('mul', 1, :val): + row = val + + if opcode(row) == 'mask_shl' and all_concrete(row): + row = apply_mask(row[4] if row[4]<256 else 256, row[1], row[2], row[3]) + + if type(row) in [int, float]: + size, offset = find_mask(row) + shl = 0 + row = ('mask_shl', size, offset, 0, row) + + if row ~ ('mem', :mem_idx): + if opcode(mem_idx) != 'range': + mem_idx = ('range', mem_idx, 32) + + mem_begin = mem_idx[1] + mem_len = mem_idx[2] + ret_rows.append((bits(mem_len), 0, row)) + continue + + if row ~ ('storage', :size, :off, :idx): + ret_rows.append((size, 0, row)) + continue + + if opcode(row) != 'mask_shl': + return [(256,0, value)] + + assert row ~ ('mask_shl', :size, :offset, :shl, :value) + + stor_size = size + stor_offset = add_op(offset, shl) + shl = sub_op(shl, stor_offset) + if type(value) == int: + value = apply_mask(value, size, offset, shl) + + elif value ~ ('mem', :idx) and add_op(offset, shl) == 0: + new_memloc = apply_mask_to_range(idx, size, offset) + value = ('mem', new_memloc) + + else: + value = mask_op(value, size=size, offset=offset, shl=shl) + + + ret_rows.append((stor_size, stor_offset, value, )) + + if len(ret_rows) == 2: + ''' + a special case where rows are symbolic and complimentary. happens often + (('mask_shl', 5, 0, 3, ('cd', ('add', 4, ('cd', 68)))), 0, ('mem', ('range', ('add', 160, ('mask_shl', 251, 5, 0, ('cd', ('add', 4, ('cd', 68)))), ('mul', -1, ('mask_shl', 5, 0, 0, ('cd', ('add', 4, ('cd', 68)))))), ('mask_shl', 5, 0, 0, ('cd', ('add', 4, ('cd', 68))))))) + (('mask_shl', 253, 0, 3, ('add', 32, ('mul', -1, ('mask_shl', 5, 0, 0, ('cd', ('add', 4, ('cd', 68))))))), ('add', 256, ('mul', -1, ('mask_shl', 253, 0, 3, ('add', 32, ('mul', -1, ('mask_shl', 5, 0, 0, ('cd', ('add', 4, ('cd', 68))))))))), ('mem', ('range', ('add', 185, ('mask_shl', 251, 5, 0, ('add', 31, ('cd', ('add', 4, ('cd', 68))))), ('mask_shl', 251, 5, 0, ('add', 31, ('cd', ('add', 4, ('cd', 164))))), ('mask_shl', 251, 5, 0, ('cd', ('add', 4, ('cd', 68)))), ('mask_shl', 251, 0, 5, 1)), ('add', 32, ('mul', -1, ('mask_shl', 5, 0, 0, ('cd', ('add', 4, ('cd', 68))))))))) + ''' + first = ret_rows[0] + second = ret_rows[1] + if first[1] != 0 and second[1] == 0: + second, first = first, second + + f_size, f_off, f_val = first + s_size, s_off, s_val = second + + if f_off == 0 \ + and s_off ~ ('add', 256, ('mul', -1, ('mask_shl', 253, 0, 3, ('add', 32, ('mul', -1, ('mask_shl', 5, 0, 0, f_size[4])))))): + assert s_size ~ ('mask_shl', _, _, _, ('add', 32, ('mul', -1, ...))), s_size + return ret_rows + + try: + ret_rows.sort(key = lambda row: cmp_to_key(row[1])) # sort by offsets, descending + except: + return [(256,0, orig_value)] + + # insert zeroes into empty spaces + + result = [] + + pos = 0 + + for r in ret_rows: + if type(r[1]) != int or type(r[0]) != int: + return [(256,0, value)] + if r[1] > pos: + result.append((r[1]-pos, pos, 0)) + + result.append(r) + pos = r[1]+r[0] + + if pos < 256: + result.append((256-pos, pos, 0)) + + return result + +def sizeof(exp): # returns size of expression in *bits* + if exp ~ ('storage', :size, ...): + return size + + if exp ~ ('mask_shl', :size, :off, :shl, _): + return add_op(size, off, shl) + + if exp ~ (:op, _, :size_bytes) and is_array(op): + return bits(size_bytes) + + if exp ~ ('mem', ('range', _, :size_bytes)): + return bits(size_bytes) + + if exp ~ ('mem', :idx): + assert False + + if exp ~ ('arr', :l, _): + assert False, exp + +# if exp ~ ('bytes', :l, _): +# return bits(l) + if type(exp) == int and exp > 2**256: + return bits(((exp).bit_length() + 7) // 8) # number of bytes needed to contain the number, rounded up + + return 256 + return None + +def split_setmem(line): + if opcode(line) != 'setmem': + return [line] + + assert line ~ ('setmem', :mem_idx, :mem_val) + + if opcode(mem_val) != 'or': + return [line] + + post_split = split_or(mem_val) + + res = [] + for size, offset, split_val in post_split: + try: + + split_idx = apply_mask_to_range(mem_idx, size, offset) + except: + logger.warning('problem with split_setmem') + return [line] + res.append(('setmem', split_idx, split_val)) + + return res + +def split_store(line): + if line ~ ('store', 256, 0, int:idx, ('mask_shl', int:size, int:off, 0, ('storage', 256, 0, idx))) and size < 256: + + lines = [] + if off > 0: + lines.append(('store', off, 0, idx, 0)) +# lines.append(('store', size, off, idx, ('storage', size, off, idx))) + if size + off < 256: + lines.append(('store', (256 - size - off), size + off, idx, 0)) + + return lines + + if line ~ ('store', :size, :off, :idx, :val) and size==256 and off==0: + + splitted = split_or(val) + + res = [] + for s_size, s_off, s_val in splitted: + if safe_le_op(off, s_off) and safe_le_op(add_op(s_size, s_off), add_op(off, size)): + if s_val != ('storage', s_size, s_off, idx): # ignore writing the same to the same storage + res.append(('store', s_size, s_off, idx, s_val)) + else: + logger.warning('unusual store') + return [line] + + return res + else: + return [line] + + +def memloc_overwrite(memloc, split): + # returns mem ranges excluding the ones that are *for sure* overwritten by 'split' + # e.g. overwrites(('range', 64, 32), ('range', 70, 10)) -> [('range', 64, 6), (range, 80, 16)] + # e.g. overwrites(('range', 64, 32), ('range', 70, 'unknown')) -> [('range', 64, 32)], bc. 'unknown' can be 0 + + assert memloc ~ ('range', :m_left, :m_len) + assert split ~ ('range', :s_left, :s_len) + + m_right = add_op(m_left, m_len) + s_right = add_op(s_left, s_len) + + if safe_le_op(m_right, s_left) is True: # split after memory - no overlap + return [memloc] + if safe_le_op(s_right, m_left) is True: # split before memory - no overlap + return [memloc] + + left_len = sub_op(s_left, m_left) + right_len = sub_op(m_right, s_right) + + range_left = ('range', m_left, left_len) + range_right = ('range', s_right, right_len) + + left_ge_zero, right_ge_zero = safe_ge_zero(left_len), safe_ge_zero(right_len) + + if left_ge_zero is None or right_ge_zero is None: + # we can't compare some numbers, conservatively return whole range + return [memloc] + + res = [] + + if safe_ge_zero(left_len) is True and left_len != 0: + res.append(range_left) + + if safe_ge_zero(right_len) is True and right_len != 0: + res.append(range_right) + + return res + +assert memloc_overwrite(('range', 64, 32), ('range', 70, 10)) == [('range', 64, 6), ('range', 80, 16)] +assert memloc_overwrite(('range', 64, 32), ('range', 70, add_op('unknown',100))) == [('range', 64, 6)] +assert memloc_overwrite(('range', 64, 'x'), ('range', 70, add_op('unknown',100))) == [('range', 64, 'x')] + +def slice_exp(exp, left, right): + size = sub_op(right, left) + + logger.info(f'slicing {exp}, offset {left} bytes, until {right} bytes') + # e.g. mem[32 len 10], 2, 4 == mem[34,2] + + if exp ~ ('mem', ('range', :rleft, :rlen)): + if safe_le_op(add_op(left, size), rlen): + return ('mem', ('range', add_op(rleft, left), size)) + else: + return None + + if exp ~ (:op, :rleft, :rlen) and is_array(op): + if safe_le_op(add_op(left, size), rlen):#, (rleft, rlen, left, size, right) + return (op, add_op(rleft, left), size) + else: + return None + + logger.info(f"sizeof exp {sizeof(exp)}") + off = sub_op(sizeof(exp), bits(right)) + logger.info(f"applying mask, size 8*{size}, offset {off}") + + m = mask_op(exp, size=bits(size), offset=off, shr=off) + logger.info(f"result {m}") + return m + +assert slice_exp(('mem', ('range', 32, 10)), 2, 4) == ('mem', ('range', 34, 2)) +assert slice_exp(('mask_shl', 32, 0, 0, ('cd', 0)), 0, 4) == ('mask_shl', 32, 0, 0, ('cd', 0)) +assert slice_exp(('mask_shl', 32, 0, 0, ('cd', 0)), 2, 4) == ('mask_shl', 16, 0, 0, ('cd', 0)) +assert slice_exp(('mask_shl', 32, 0, 0, ('cd', 0)), 0, 2) == ('mask_shl', 16, 16, -16, ('cd', 0)) + + +def splits_mem(memloc, split, memval, split_val=None): + # returns memory values we can be confident of, after overwriting the split part of memory + + assert memloc ~ ('range', :m_left, :m_len) + assert split ~ ('range', :s_left, :s_len) + + m_right = add_op(m_left, m_len) + s_right = add_op(s_left, s_len) + + logger.info(f'applying split [{s_left} (len {s_len}) {s_right}]') + logger.info(f' to [{m_left} (len {m_len}) {m_right}]') + + if not safe_ge_zero(s_len): + s_len = 'undefined' + s_right = add_op(s_left, s_len) + + if safe_le_op(m_right, s_left) is True: # split after memory - no overlap + return [(memloc, memval)] + + if safe_le_op(s_right, m_left) is True: # split before memory - no overlap + return [(memloc, memval)] + + + left = safe_max_op(s_left, m_left) + right = safe_min_op(s_right, m_right) + + logger.info(f'split overwrites memory from {left} to {right}') + + # left/right relative to beginning of memory location + in_left = sub_op(left, m_left) + in_right = sub_op(right, m_left) + + logger.info(f'that is, relative to memloc {in_left} to {in_right}') + if safe_le_op(in_left, m_len) is not True or left is None: + logger.info(f'we are not sure that m_len: {m_len} is bigger than beginning of split, returning []') + return [] + + assert in_left == 0 if safe_le_op(right, m_left) else True + + val_left = slice_exp(memval, 0, in_left) if left is not None else None + val_right = slice_exp(memval, in_right, sub_op(m_right, m_left)) if right is not None else None + res = [] + + left_len = sub_op(left, m_left) #sizeof(val_left) + right_len = sub_op(m_right, right) + + if safe_ge_zero(left_len) is True and left_len != 0 and val_left is not None: + res.append((('range', m_left, left_len), val_left)) + + if split_val is not None: + center_left = safe_max_op(m_left, s_left) + center_right = safe_min_op(m_right, s_right) + + center_len = sub_op(center_right, center_left) + + if is_array(opcode(split_val)):# in ARRAY_OPCODES: + #mem[a len b] = calldata[x len b] + #log mem[c len d] + # -> calldata[x+ c - a, center_len] + arr_offset, arr_len = split_val[1:] + center_offset = add_op(arr_offset, sub_op(center_left, s_left)) + center_val = (opcode(split_val), center_offset, center_len) + + else: + center_offset = sub_op(s_right, center_right) + center_val = mask_op(split_val, size=mul_op(center_len,8), offset=mul_op(center_offset, 8), shr=mul_op(center_offset, 8) ) + + center_range = ('range', center_left, center_len) + + + if safe_ge_zero(center_len) and center_len != 0: + res.append((center_range, center_val)) + + if safe_ge_zero(right_len) is True and right_len != 0 and val_right is not None: + res.append((('range', right, right_len), val_right)) + + return res + +assert splits_mem(('range', 66, 32), ('range', 65, 32), 'a') == [(('range', 97, 1), ('mask_shl', 8, 0, 0, 'a'))], splits_mem(('range', 66, 32), ('range', 65, 32), 'a') +assert splits_mem(('range', 64, 32), ('range', 65, 32), 'a') == [(('range', 64, 1), ('mask_shl', 8, 248, -248, 'a'))], splits_mem(('range', 64, 32), ('range', 65, 32), 'a') +assert splits_mem(('range', 4, 32), ('range', 65, 32), 'a') == [(('range', 4, 32), 'a')] +assert splits_mem(('range', 104, 32), ('range', 65, 32), 'a') == [(('range', 104, 32), 'a')] +assert splits_mem(('range', 64, 32), ('range', 65, 30), 'a') == [(('range', 64, 1), ('mask_shl', 8, 248, -248, 'a')), (('range', 95, 1), ('mask_shl', 8, 0, 0, 'a'))] + +assert splits_mem(('range', 64, 32), ('range', 'x', 32), 'a') == [] # not sure means return empty +assert splits_mem(('range', 64, 32), ('range', 65, 'x'), 'a') == [(('range', 64, 1), ('mask_shl', 8, 248, -248, 'a'))] +assert splits_mem(('range', 64, 'x'), ('range', 65, sub_op('x',2)), 'a') == [], splits_mem(('range', 64, 'x'), ('range', 65, sub_op('x',2)), 'a') # because it's either '1' if x>=1 or '0' if x == 0 + +assert splits_mem(('range', 64, 32), ('range', 65, 30), 'a','b') == [(('range', 64, 1), ('mask_shl', 8, 248, -248, 'a')), (('range', 65, 30), ('mask_shl', 240, 0, 0, 'b')), (('range', 95, 1), ('mask_shl', 8, 0, 0, 'a'))] + +assert splits_mem(('range', 'x', 32),('range', 'y', 32),'a','b') == [] + +assert splits_mem(('range', 64, 32), ('range', 65, 32), 'a','b') == [(('range', 64, 1), ('mask_shl', 8, 248, -248, 'a')), (('range', 65, 31), ('mask_shl', 248, 8, -8, 'b'))] +assert splits_mem(('range', 64, 32), ('range', 63, 32), 'a','b') == [(('range', 64, 31), ('mask_shl', 248, 0, 0, 'b')), (('range', 95, 1), ('mask_shl', 8, 0, 0, 'a'))] +assert splits_mem(('range', 64, 32), ('range', 630, 32), 'a','b') == [(('range', 64, 32), 'a')] +assert splits_mem(('range', 64, 32), ('range', 1, 32), 'a','b') == [(('range', 64, 32), 'a')] + +#assert splits_mem(('range', 64, 'x'), ('range', 64, 'x'), 'a', ('array', 10, 'x')) == [(('range', 64, 'x'), ('array', 10, 'x'))] + +''' +#assert splits_mem(('range', 64, 32), ('add', 10, 'x')), ('range', 70, ('add', 10, 'x')), 'a', ('array', 10, ('add', 10, 'x'))) == [(('range', 64, 6), ('mask_shl', 48, ('mask_shl', 253, 0, 3, ('add', 4, 'x')), ('mul', -1, ('mask_shl', 253, 0, 3, ('add', 4, 'x'))), 'a')), (('range', 70, ('add', 4, 'x')), ('array', 10, ('add', 4, 'x')))] +''' + +def splits_len(split_list): + sum_range = 0 + for el in split_list: + el_range = el[0] + sum_range = add_op(sum_range, el_range[2]) + + return sum_range + +''' +test_s = splits_mem(('range', 64, 32), ('range', 65, 32), 'a','b') +assert splits_len(test_s) == 32 + +test_s = splits_mem(('range', 64, 32), ('range', 65, 30), 'a','b') +assert splits_len(test_s) == 32, test_s +''' +def replace_max_with_MAX(exp): + if opcode(exp) != 'max': + return exp, None + + exp = max_to_add(exp) + + res = exp + + for e in exp: + if opcode(e) == 'max': + res = e + + exp = replace(exp, res, 'MAX') + exp = simplify(exp) + return exp, res + + +strict = '--strict' in sys.argv + +#@cached +def fill_mem(exp, mem_idx, mem_val): + + # speed - if exp contains a variable used in mem_idx + # or mem_idx contains a variable not used in exp + # there can be no match. + # + # ugly, but shaves off 15% exec time + logger.info(f'filling mem: {exp} with mem[{mem_idx}] == {mem_val}') + + if mem_idx ~ ('range', ('var', :num), _) and \ + not contains(exp, ('var', num)): + assert not strict or __fill_data(exp, mem_idx, mem_val) == exp + return exp + + if exp ~ ('mem', ('range', ('var', :num), _)) and \ + not contains(mem_idx, ('var', num)): + assert not strict or __fill_data(exp, mem_idx, mem_val) == exp + return exp + + logger.info(f'no speed improvements') + + + # /speed + + f = _fill_mem(exp, mem_idx, mem_val) + return f + + + +def _fill_mem(exp, split, split_val): + if exp == ('mem', split): + return split_val + + assert exp ~ ('mem', :memloc), exp + assert memloc ~ ('range', :m_left, :m_len) + assert split ~ ('range', :s_left, :s_len) + + + m_right = add_op(m_left, m_len) + s_right = add_op(s_left, s_len) + + logger.debug(f'orig memloc: {m_left} len {m_len} right {m_right}') + logger.debug(f'split memloc: {s_left} len {s_len} right {s_right}') + + + if safe_le_op(m_right, s_left) is not False: # if the split is before memory, or we can't compare - not replacing + logger.info(f"split before memory or can't compare - not replacing") + return exp + + if safe_le_op(s_right, m_left) is not False: # -,,- after memory + logger.info(f"split after memory or can't compare - not replacing") + return exp + + left = safe_max_op(s_left, m_left) + right = safe_min_op(s_right, m_right) + + logger.info(f"split begins at {left} ends at {right}") + + if left is None or right is None: + return exp # if we can't figure out which one is smaller/larger, we're not replacing + + memloc, memloc_max = replace_max_with_MAX(memloc) + split, split_max = replace_max_with_MAX(split) + # 'max' op tends to mess up with all the algebra stuff, so we're replacing + # it with a variable 'MAX' for the time being + + if split_max != memloc_max: + logger.warn('different maxes') + return exp + + # by now we know: + # - the split overlaps memory for sure + # - we know the boundaries of split + # - so we now return data (before_split, split_val, after_split) + + res_left = slice_exp(exp, 0, sub_op(left, m_left)) + if res_left is None: + return exp + logger.info(f"value left untouched on left: {res_left}") + + res_right = slice_exp(exp, sub_op(right, m_left), sub_op(m_right, m_left)) + if res_right is None: + return exp + + logger.info(f"value right untouched on right: {res_right}") + + res = [] + + if safe_gt_zero(sizeof(res_left)) is True: + logger.info(f'size of left untouched > 0, adding to output') + res.append(res_left) + + elif safe_gt_zero(sizeof(res_left)) is None: + logger.info(f"we don't know if left size > 0, aborting") + return exp + + center_in_start = sub_op(left, s_left) + center_in_len = sub_op(right, s_left) + + logger.info(f"inserted value offset {center_in_start}, length {center_in_len}") + logger.info(f"cutting this out of {split_val}") + + res_center = slice_exp(split_val, center_in_start, center_in_len) + + logger.info(f"inserted value after slicing: {res_center}") + + if res_center is None: + return exp + + if safe_ge_zero(sizeof(res_center)) is True: + res.append(res_center) + else: + assert False, sizeof(res_center) # this shouldn't happen considering the above checks? + + if safe_ge_zero(sizeof(res_right)) is True: + if sizeof(res_right) != 0: + res.append(res_right) + elif safe_ge_zero(sizeof(res_right)) is None: + return exp + + assert None not in res + + return ('data', ) + tuple(res) + + +@cached +def range_overlaps(range1, range2): + assert range1 ~ ('range', :r1_begin, :r1_len) + assert range2 ~ ('range', :r2_begin, :r2_len) + + r1_end = add_op(r1_begin, r1_len) + r2_end = add_op(r2_begin, r2_len) + + try: + if lt_op(r2_begin, r1_begin): + r1_begin, r1_end, r2_begin, r2_end = r2_begin, r2_end, r1_begin, r1_end + + # r1 begins before r2 for sure now + if le_op(r1_end, r2_begin) is True: + return False + else: + return True + + except CannotCompare: + return None + + +@cached +def range_contains(outer, inner): + # checks if outer range *fully* contains inner range + assert outer ~ ('range', :outer_begin, :outer_len) + assert inner ~ ('range', :inner_begin, :inner_len) + + outer_end = add_op(outer_begin, outer_len) + inner_end = add_op(inner_begin, inner_len) + + try: + if not le_op(outer_begin, inner_begin): + return False + + if not le_op(inner_end, outer_end): + return False + + return True + + except CannotCompare: + return None + + +assert range_overlaps(('range', ('add', 256, ('mask_shl', 246, 5, 0, ('ext_call.return_data', 128, 32))), 32), ('range', 160, 96)) == False + +assert range_overlaps(('range', 260, 32), ('range', 292, 32)) == False +assert range_overlaps(('range', 324, 32), ('range', 292, 32)) == False +assert range_overlaps(('range', 292, 32), ('range', 300, 32)) == True +assert range_overlaps(('range', 300, 32), ('range', 292, 32)) == True + + +assert range_contains(('range', 64,10), ('range', 64, 32)) == False +assert range_contains(('range', 64, 32), ('range', 64, 10)) == True +assert range_contains(('range', 64, 32), ('range', 64, 32)) == True +assert range_contains(('range', 64, 32), ('range', ('var', 1), 32)) == None +assert range_contains(('range', 10, 32), ('range', 100, 'x')) == False + diff --git a/core/variants.py b/core/variants.py new file mode 100644 index 00000000..9adc2842 --- /dev/null +++ b/core/variants.py @@ -0,0 +1,102 @@ +from utils.helpers import opcode, is_array + +''' + + for a given expression, returns variants of it with each value being 0 or 2^256-1 + + e.g. for ('ADD', ('mem', 64), ('mem', 256)) + returns + ('ADD', 0, 0), + ('ADD', 0, MAX_number), + ('ADD', MAX_number, 0), + ('ADD', MAX_number, MAX_number) + +''' + + + + +MAX_number = 2**230 - 1 +MAX_number2 = 2**230 - 1 + +def variants(exp): + var = extract_variables(exp) + for p in possibilities(list(var)): + yield replace_dict(exp, p) + + +''' + + implementation + +''' + +def extract_variables(exp): + if type(exp) == int: + return set() + + if opcode(exp) in ('var', 'mem', 'cd', 'storage', 'call.data', 'sha3', 'calldatasize', ) or is_array(opcode(exp)): + return set([exp]) + + if type(exp) == str and exp in ('x', 'y', 'z', 'sth', 'unknown', 'undefined', 'callvalue', 'number','timestamp', 'address'): + return set([exp]) + + if type(exp) == str and exp != 'data' and 'data' in exp: + return set([exp]) + + if type(exp) != tuple: + return set([exp]) + + res = set() + for e in exp[1:]: + res = res.union(extract_variables(e)) + + return res + + +def possibilities(var): + if len(var) > 0: + + current = var[0] + if len(var) == 1: + yield {current: MAX_number} + yield {current: MAX_number2} + + if current == ('mem', ('range', 64, 32)): + yield {current: 96} + elif current == 'calldatasize': + yield {current: 6} # nasty hack for `sweeper` contract, try to remove and see what happens + else: # can theoretically cause bugs in other ones + yield {current: 0} + + else: + for p in possibilities(var[1:]): + p[current] = MAX_number + yield p + p[current] = MAX_number2 + yield p + + if current == ('mem', ('range', 64, 32)): + yield {current: 96} + elif current == 'calldatasize': + p[current] = 6 + else: + p[current] = 0 + yield p + + +def replace(exp, idx, val): + if exp == idx: + return val + + if type(exp) != tuple: + return exp + + return tuple(replace(e, idx, val) for e in exp) + + +def replace_dict(exp, dic): + for idx, val in dic.items(): + exp = replace(exp, idx, val) + + return exp diff --git a/pano/__init__.py b/pano/__init__.py new file mode 100644 index 00000000..a3e721ca --- /dev/null +++ b/pano/__init__.py @@ -0,0 +1 @@ +import tilde.tilde \ No newline at end of file diff --git a/pano/contract.py b/pano/contract.py new file mode 100644 index 00000000..7f0ad5b5 --- /dev/null +++ b/pano/contract.py @@ -0,0 +1,269 @@ +# coding: tilde +import json + +import pano.sparser as sparser + +from .sparser import get_loc, get_name + +from .function import Function + +from pano.prettify import pprint_trace, pretty_stor, pprint_ast, prettify + +from utils.helpers import replace_f, tuplify, COLOR_GREEN, ENDC, replace_lines, find_f_list, to_exp2 + +import pano.folder as folder +import logging + +logger = logging.getLogger(__name__) + +def deserialize(trace): + res = [] + for line in trace: + line_t = tuple(line) + + if line_t ~ ('while', :cond, :path, :lid, :setvars): + cond = tuplify(cond) + setvars = tuplify(setvars) + assert type(lid) == str + + path = deserialize(path) + res.append(('while', cond, path, lid, setvars)) + + elif line_t ~ ('if', :cond, :if_true, :if_false): + cond = tuplify(cond) + if_true = deserialize(if_true) + if_false = deserialize(if_false) + res.append(('if', cond, if_true, if_false)) + + else: + res.append(tuplify(line)) + + return res + + +class Contract(): + + def __init__(self, addr, network, functions, problems, ver): + self.addr = addr + self.network = network + self.ver = ver + self.problems = problems + self.functions = [] + for func in functions.values(): + self.functions.append(func) + + self.stor_defs = {} + + def json(self): + return json.dumps({ + 'addr': self.addr, + 'network': self.network, + 'ver': self.ver, + 'problems': self.problems, + 'stor_defs': self.stor_defs, + 'functions': [f.serialize() for f in self.functions], + }) + + def load(self, data): + self.addr = data['addr'] + self.network = data['network'] + self.ver = data['ver'] + self.problems = data['problems'] + self.functions = [] + self.stor_defs = data['stor_defs'] if 'stor_defs' in data else {} + + for func in data['functions']: + self.functions.append(Function(hash=func['hash'], + trace=deserialize(func['trace']))) + + return self + + def postprocess(self): + try: + self.stor_defs = sparser.rewrite_functions(self.functions) + except: + # this is critical, because it causes full contract to display very + # badly, and cannot be limited in scope to just one affected function + logger.critical('Storage postprocessing failed. This is very bad!') + self.stor_defs = {} + + for func in self.functions: + def replace_names(exp): + if (exp ~ ('cd', int:idx)) and idx in func.params: + return ('param', func.params[idx][1]) + return exp + + func.trace = replace_f(func.trace, replace_names) + + # const list, sort by putting all-caps consts at the end - looks way better this way + self.consts = [f for f in self.functions if f.const and f.name.upper() != f.name] + \ + [f for f in self.functions if f.const and f.name.upper() == f.name] + + self.make_asts() + + def make_asts(self): + ''' + we need to do ast creation from the contract, not function level, + because some simplifications (type/field removal) require insight to all the functions, + not just a single one + ''' + + for func in self.functions: + func.ast = self.make_ast(func.trace) + + def find_stor_masks(exp): + if exp ~ ('type', ...): + return [exp] + else: + return [] + + mlist = set(find_f_list([f.ast for f in self.functions], find_stor_masks)) + + def cleanup(exp): + + if exp ~ ('field', 0, ('stor', ('length', :idx))): + return ('stor', ('length', idx)) + + if exp ~ ('type', 256, ('field', 0, ('stor', ('length', :idx)))): + return ('stor', ('length', idx)) + + if exp ~ ('type', 256, ('stor', ('length', :idx))): + return ('stor', ('length', idx)) + + if exp ~ ('type', :e_type, ('field', :e_field, ('stor', ('name', :e_name, :loc)))): + for m in mlist: + if get_name(m) == e_name: + assert get_loc(m) == loc # otherwise, two locs with the same name? + + assert m ~ ('type', :m_type, ('field', :m_field, _)) + if m_field != e_field or m_type != e_type: + return exp + else: + return ('stor', ('name', e_name, loc)) + + + elif exp ~ ('type', :e_type, :stor): + e_loc = get_loc(stor) + + for m in mlist: + if m ~ ('type', 256, ('field', 0, ('stor', ('length', _)))): + continue + + if get_loc(m) == e_loc: + assert m ~ ('type', :m_type, _) + if m_type != e_type: + return exp + else: + return stor + + elif exp ~ ('field', :e_off, :stor): + e_loc = get_loc(stor) + + for m in mlist: + if m ~ ('type', 256, ('field', 0, ('stor', ('length', _)))): + continue + + if get_loc(m) == e_loc: + assert m ~ ('type', _, ('field', :m_off, _)) + if m_off != e_off: + return exp + else: + return stor + + else: + return exp + + for f in self.functions: + f.ast = replace_f(f.ast, cleanup) + + + def make_ast(self, trace): + trace = folder.fold(trace) + + def store_to_set(line): + if line ~ ('store', :size, :off, :idx, :val): + return ('set', ('stor', size, off, idx), val) + else: + return line + + def loc_to_name(exp): + if exp ~ ('loc', int:num): + if num < 1000: + return ('name', 'stor' + str(num), num) + else: + return ('name', 'stor' + hex(num)[2:6].upper(), num) + + if exp ~ ('loc', :num): + return ('name', 'stor' + prettify(num, add_color=False, parentheses=True), num) + else: + return exp + + def arr_rem_mul(exp): + if exp ~ ('array', ('mask_shl', :size, :off, int:shl, :idx), :loc): + r = 2 ** shl + e_loc = get_loc(loc) + + for s in self.stor_defs: + assert s ~ ('def', _, :d_loc, :d_def) + if s ~ ('def', _, e_loc, ('array', ('struct', r))): + return ('array', ('mask_shl', size, off, 0, idx), loc) + +# exit() + + + elif exp ~ ('array', ('mul', int:r, :idx), :loc): + e_loc = get_loc(loc) + + for s in self.stor_defs: + assert s ~ ('def', _, :d_loc, :d_def) + if s ~ ('def', _, e_loc, ('array', ('struct', r))): + return ('array', idx, loc) + return exp + + + def mask_storage(exp): + if exp ~ ('stor', :size, :off, :idx): + if off < 0: + off = 0 + return ('type', size, ('field', off, ('stor', idx))) + else: + return exp + + def other_1(exp): + if exp ~ ('mask_shl', int:size, 256-size, size-256, str:val) and \ + size + 16 == len(val) * 8 and len(val)>0 and val[0] == val[-1] == "'": # +16 because '' in strings + return val + else: + return exp + + def other_2(exp): + if exp ~ ('if', ('eq', :a, :b), :if_true) and \ + if_true == [('return', ('eq', a, b))]: + return ('if', ('eq', a, b), [('return', ('bool', 1))]) + + elif exp ~ ('mask_shl', 160, 0, 0, str:e) and \ + e in ('address','coinbase', 'caller', 'origin',): + return e + + elif exp ~ ('mask_shl', int:size, int:off, -off, :e) and \ + off in range(1, 9) and size+off in [8,16,32,64,128,256]: + return ('div', ('mask', size+off, 0, e), 2**off) + + elif exp == ('mask_shl', 32, 224, 0, ('cd', 0)): + return ('cd', 0) + + elif exp ~ ('mask_shl', 160, 0, 96, :val): + # nasty hack for stuff like 0xF8DFaC6CAe56736FD2a05e45108490C6Cb40147D approve + return ('mask_shl', 160, 0, 0, val) + + else: + return exp + + trace = replace_f(trace, store_to_set) + trace = replace_f(trace, loc_to_name) + trace = replace_f(trace, arr_rem_mul) + trace = replace_f(trace, mask_storage) + trace = replace_f(trace, other_1) + trace = replace_f(trace, other_2) + return trace + diff --git a/pano/folder.py b/pano/folder.py new file mode 100644 index 00000000..93d6d461 --- /dev/null +++ b/pano/folder.py @@ -0,0 +1,622 @@ +# coding: tilde + +from utils.helpers import opcode, replace_f, car +from utils.helpers import COLOR_HEADER, COLOR_BLUE, COLOR_OKGREEN, COLOR_WARNING, FAIL, ENDC, COLOR_BOLD, COLOR_UNDERLINE, COLOR_GREEN, COLOR_GRAY +from pano.prettify import prettify + +from core.arithmetic import is_zero, comp_bool + +import logging +logger = logging.getLogger(__name__) + + +''' + + Folder_aux is a set of additional rules for a last-minute folding, + after everything else is done. + + Done especially with + 0xd883209C4DCd497f24633C627a4E451013424841 , sendFoods + in mind. + + +''' + + +def fold(trace): + + ''' + as_paths unfolds the trace into a list of branchless paths that the contract can + possibly take. 'if's are converted into condition assertions, e.g. + + [x,y,z,(if, cond, [a,b,c...,q,w,e], [d,e,f..,q,w,e] )] + + gets turned into + + [ + [x,y,z,cond,a,b,c...,q,w,e], + [x,y,z,is_zero(cond),d,e,f...,q,w,e] + ] + + + ''' + + try: + paths = as_paths(trace) + + + ''' + + fold_paths turns those paths again into folded ones, but also with simple + ifs (that is, not if-else), and not necessarily ifs ending a trace, so - in the + example above + [x,y,z,(if, cond, [a,b,c..], [d,e,f..]),q,w,e] + + it is a more succint form of the program display (q,w,e doesn't get repeated + any more, etc), but it's more difficult to parse automatically (you're now + dealing with an acyclic graph instead of a tree), so Panoramix uses this form + only for a display that's readable to humans. Api/json output has the original + form. + + ''' + + log = meta_fold_paths(paths) + + ''' + some additional folding below, e.g. when there is an if-else like below: + (if, cond, [revert], [a,b,c]) # if cond revert else a,b,c + it can be turned into + (if, cond, [revert]), [a,b,c] # if cond revert; a,b,c + - no need to use 'else', since the 'if-true' trace always returns, and 'else' + is unnecessary + + ''' + log = fold_aux(log) + + return log + + except: + # make folder fail gracefuly + logger.error(f'folder failed in a function.') + return trace + +def make_fands(exp): + # see `ferlan.getOrderDataClaim` for why it's necessary +# raise + if exp ~ ('or', *terms): + return ('for', ) + terms + elif exp ~ ('and', *terms): + return ('fand', ) + terms + else: + return exp + +def unmake_fands(exp): + if exp ~ ('for', *terms): + return ('or', ) + terms + + if exp ~ ('fand', *terms): + return ('and',) + terms + else: + return exp + + + +def as_paths(trace, path = None): + assert type(trace) == list + + path = path or tuple() + +# self.find_offsets() + + trace = replace_f(trace, make_fands) + + for line in trace: + if opcode(line) == 'if': + # assumes 'ifs' end trace + cond, if_true, if_false = line[1], line[2], line[3] + return as_paths(if_true, path + (cond, )) + as_paths(if_false, path + (is_zero(cond), )) + + if opcode(line) == 'LOOP': + path += (('LOOP', line[2]), ) + return as_paths(line[1], path) + + path += (line, ) + +# pprint_logic() + + + return (list(path), ) + +TERMINATING = ('return', 'stop', 'selfdestruct', 'invalid', 'assert_fail', 'revert', 'continue', 'undefined') + +def fold_aux(trace): + if type(trace) != list: + trace = trace.trace + assert type(trace) == list, type(trace) + + out = [] + + for idx, line in enumerate(trace): + if opcode(line) == 'while': + cond, trace, jds, setvars = line[1:] + + trace = fold(trace) + line = ('while', cond, trace, jds, setvars) + + out.append(line) + + elif opcode(line) == 'if': + if line ~ ('if', :cond, :if_true): + if_false = trace[idx+1:] + last_true = if_true[-1] + if if_false == [('return', 0)] or if_false == [('revert', 0)] or opcode(car(if_false)) == 'invalid':# and\ + if opcode(last_true) not in TERMINATING:# or (last_true ~ ('if', _, _))): + if_true.append(if_false[0]) + + if_true = fold_aux(if_true) + line = ('if', cond, if_true, if_false) + + out.append(line) + return out + + else: + if_true = fold_aux(if_true) + + line = ('if', cond, if_true) + out.append(line) + + elif line ~ ('if', :cond, :if_true, :if_false): + if_true = fold_aux(if_true) + if_false = fold_aux(if_false) + + if len(if_true) > 0 and len(if_false) > 0 and \ + len(if_true) > 0 and \ + opcode(if_true[-1]) in TERMINATING and \ + opcode(car(if_false)) not in ('invalid', 'revert'): + # ^ should be some more generic check that all if_true + # paths end with exit + + line = ('if', cond, if_true) + out.append(line) + out.extend(if_false) + + else: + line = ('if', cond, if_true, if_false) + out.append(line) + else: + assert False, line + + else: + out.append(line) + + return out + + +''' + + Folder (proper) takes takes all the execution paths, and merges them into + one, as concise as possible. + + In other words - it turns intermediate representation available + through API/.json, that is easy to parse by machine, and turns + it into a representation that is easy to read by humans, and + a much shorter one. + + There is probably a neater way of doing this, but this should do for + the time being... + +''' + +''' + + Helper functions, similar to prettify and algebra, but different + enough to have a separate implementation here. + +''' + +def pprint(exp): + for e in exp: + print(' ', prettify(e, add_color=True)) + + +def pprint_logic(exp, indent=2): + if type(exp) == list: + for idx, line in enumerate(exp): + pprint_logic(line, indent) + + elif opcode(exp) == 'or' and len(exp)>1: + print(' '*indent + 'if') + pprint_logic(exp[1], indent+2) + for line in exp[2:]: + print(' '*indent + 'or') + pprint_logic(line, indent+2) + else: + print(' '*indent + prettify(exp, add_color=True)) + +def sorted_tuple(line): + row = list(line[1:]) + row.sort(key=lambda x: len(x) if type(x) == list else 0) + + return ('or', ) + tuple(row) + +def or_op(*args): + ret = tuple() + for r in args: + if type(r) == list: + r = and_op(*r) + + if opcode(r) == 'or': + ret += r[1:] + else: + ret += (r, ) + + return sorted_tuple(('or', ) + ret) + +def and_op(*args): + # returns a list that doesn't have elements with 'or' inside + idx = 0 + + ret = [] + for idx, exp in enumerate(args): + if type(exp) == list: + ret += exp + else: + ret.append(exp) + + if opcode(exp) == 'or': + ret = [] + for variant in exp[1:]: + ret.append(and_op(*(args[:idx] + (variant, ) + args[idx+1:]))) + + return or_op(*ret) + + return ret + +assert and_op(1,2,3,4) == [1,2,3,4] +assert or_op(1,2) == ('or', 1, 2) + +c = and_op(1,2,3) +d = or_op(c,c) +assert d == ('or', [1,2,3], [1,2,3]) + +b = or_op(1,2) +a = and_op('a',b,'c') + +assert a == ('or', ['a',1,'c'], ['a', 2, 'c']) + +def starting_with(or_tuple, starting): + ret = [] + + for exp in or_tuple[1:]: + if exp[:len(starting)] == starting: + ret.append(exp[len(starting):]) + + return ret + + +def ending_with(or_tuple, ending): + assert len(ending) > 0 + + ret = [] + + for exp in or_tuple[1:]: + if exp[-len(ending):] == ending: + ret.append(exp[:-len(ending)]) + + return ret + + +''' + + Folder proper. + + It merges the beginnings and endings of paths recursively in + fold_paths, and then does some cleanup operations. + + If you have trouble visualising how the algorithm + works, welcome to the club. I wrote it, but I don't + fully get it myself. + + It works similarly to the algorithms that do diffs + of two files, but with way more than two objects + to compare at the same time. + +''' + + +def meta_fold_paths(paths): + for_merge = [] + for r in paths: + assert type(r) in (tuple, list) + if len(r)>0: + for_merge.append(r) + + output = fold_paths(for_merge) + + assert type(output) == list + + output = flatten(output) # converts if-else statements into if statements, if possible + output = cleanup_ors(output) + output = make_ifs(output) + output = merge_ifs(output) + + output = replace_f(output, unmake_fands) + + + return output + +def flatten(path): + + def ends_exec(path): # check if all the subpaths end execution + + # only checking the last line, previous ones may end execution as well + # but at least one leading up to the last line didn't - otherwise + # we wouldn't see it + + line = path[-1] + + if opcode(line) in ('return', 'stop', 'selfdestruct', 'invalid', 'assert_fail', 'revert', 'continue', 'undefined'): + return True + elif opcode(line) == 'or': + assert len(line) == 3 + return ends_exec(line[1]) and ends_exec(line[2]) + elif opcode(line) == 'while': + # well, 'while True' ends execution, but all the other + # ones most likely don't. if we miss some cases nothing + # bad will happen - just slightly less readable code + return False + else: + return False + + res = [] + + for idx, line in enumerate(path): + + if opcode(line) != 'or': + res.append(line) + continue + + assert len(line) == 3, line # ('or', [exec1], [exec2]) - we're dealing only with if-else at this stage + + if len(line[1]) == 1 and len(line[2]) == 1: + # sometimes, after folding, both paths are identical, + # so we can skip 'ifs', e.g. fallback func in 0xBe46324018124B2d604c2f3eaE91D3De9b388b09 + continue + + elif ends_exec(line[1]): + res.extend(try_merge(flatten(line[1]), flatten(line[2]))) + +# elif idx == len(path) - 1: + # the last or = we're flattening +# res.extend(try_merge(flatten(line[1]), flatten(line[2]))) + else: + res.append(('or', flatten(line[1]), flatten(line[2]))) + + return res + + +def try_merge(one, two): + assert (type(one), type(two)) == (list, list) + + if len(one) > len(two): + shorter = two + else: + shorter = one + + idx = 1 + while one[-idx] == two[-idx] and idx < len(shorter): + idx += 1 + + idx -= 1 + + if idx > 0: + return [('or', one[:-idx], two[:-idx])] + one[-idx:] + else: + return [('or', one)] + two + +def cleanup_ors(path): + assert type(path) == list + ret = [] + + idx = 0 + while idx < len(path): + if type(path[idx]) == list: + + path = path[:idx] + path[idx] + path[idx+1:] + + line = path[idx] + + if opcode(line) != 'or': + ret.append(line) + + elif len(line) == 2: # one-sided or + # clean up the inside, skip the next line in the main path + condition = line[1][0] + line = ('or', cleanup_ors(line[1])) + ret.append(line) + idx += 1 + + elif len(line) == 3: # two-sided or + if len(line[1]) == 1: + assert comp_bool(line[1][0],is_zero(line[2][0])) + line = ('or', cleanup_ors(line[2])) + ret.append(line) + else: + assert comp_bool(is_zero(line[1][0]), line[2][0]) + line = ('or', cleanup_ors(line[1]), cleanup_ors(line[2][1:])) + ret.append(line) + + else: # three-sided ors? madness! + assert False + + idx += 1 + + return ret + +def make_ifs(path): + assert type(path) == list + + ret = [] + for line in path: + if opcode(line) != 'or': + ret.append(line) + + elif len(line) == 2: + ret.append(('if', line[1][0], make_ifs(line[1][1:]))) + + elif len(line) == 3: + ret.append(('if', line[1][0], make_ifs(line[1][1:]), make_ifs(line[2]))) + + else: + assert False + + return ret + + +def try_merge_ifs(cond, if_true, if_false): + + idx = 0 + while idx < min(len(if_true), len(if_false)) and if_true[idx] == if_false[idx]: + idx += 1 + + assert if_true[:idx] == if_false[:idx] + + if idx > 0: + lines = if_true[:idx] + merged = ('if', cond, if_true[idx:], if_false[idx:]) + else: + lines = [] + merged = ('if', cond, if_true, if_false) + + return lines, merged + +def merge_ifs(path): + # detects if-else sections that have the same beginnings, and moves + # if upstream, merging some of the code + + assert type(path) == list + ret = [] + + for idx, line in enumerate(path): + assert type(line) != list + if opcode(line) != 'if': + ret.append(line) + continue + elif len(line) == 3: + # one-sided if + cond, if_true, if_false = line[1], merge_ifs(line[2]), merge_ifs(path[idx+1:]) + lines, merged = try_merge_ifs(cond, if_true, if_false) + ret.extend(lines) + ret.append(merged[:3]) + ret.extend(merged[3]) + break + else: + assert len(line) == 4 + cond, if_true, if_false = line[1], merge_ifs(line[2]), merge_ifs(line[3]) + lines, merged = try_merge_ifs(cond, if_true, if_false) + ret.extend(lines) + ret.append(merged) + # don't break + + return ret + +def fold_paths(for_merge): + + if len(for_merge) == 0: + return [] + + if len(for_merge) == 1: + return for_merge[0] + + for_merge.sort(key = lambda r: -len(r)) + + or_paths = ('or', ) + tuple(for_merge) + + # merge beginnings + + idx = 0 + while len(starting_with(or_paths, for_merge[0][:idx])) == len(for_merge): + idx += 1 + + begin_offset = idx - 1 + + # merge endings + + idx = 1 + while len(ending_with(or_paths, for_merge[0][-idx:])) == len(for_merge): + idx += 1 + + end_offset = idx - 1 # for_merge[0][:idx] + + s_with = starting_with(or_paths, for_merge[0][:begin_offset]) + if end_offset > 0: + e_with = ending_with(('or', ) + tuple(s_with), for_merge[0][-end_offset:]) + merged = for_merge[0][:begin_offset] + [ or_op(*e_with) ] + for_merge[0][-end_offset:] + else: + merged = for_merge[0][:begin_offset] + [ or_op(*s_with) ] + + output = [] + for line in merged: + if opcode(line) != 'or': + output.append(line) + + else: + + def fold_or(line): + longest = line[-1] + + i = 1 + while longest[0] == line[i][0]: + i += 1 + + shortest = line[i] + + assert longest[0] != shortest[0], (longest, shortest) + + for l in line[1:]: + if l[0] in (longest[0], shortest[0]): + pass + else: + pprint_logic(shortest) + print() + pprint_logic(longest) + print() + pprint_logic(l[0]) + raise +# , (longest[0], shortest[0], l[0]) + + # find two longest stretches that split the line or into exactly two parts + + best = None + + for idx1 in range(1, len(shortest)): + s1 = starting_with(line, shortest[:idx1]) + for idx2 in range(1, len(longest)): + + s2 = starting_with(line, longest[:idx2]) + if best == None and s1 == s2 and len(s1) + len(s2) + 1 == len(line): + best = (idx1, idx2) + best_s = s1 + + if best is not None: + return or_op(shortest[:best[0]], longest[:best[1]]), best_s + + # cut the first line, merge the remaining paths if possible + s1 = starting_with(line, [ shortest[0] ]) + s2 = starting_with(line, [ longest[0] ]) + + s1 = fold_paths(s1) + s2 = fold_paths(s2) + + shorter_path = and_op(shortest[0], s1) + longer_path = and_op(longest[0], s2) + + return ('or', ) + ( shorter_path, ) + (longer_path,) , [] + + + ors, paths = fold_or(line) + + output.append(ors) + output += fold_paths(paths) + + return output diff --git a/pano/function.py b/pano/function.py new file mode 100644 index 00000000..c68214cf --- /dev/null +++ b/pano/function.py @@ -0,0 +1,412 @@ +# coding: tilde + +from utils.helpers import COLOR_HEADER, COLOR_BLUE, COLOR_OKGREEN, COLOR_WARNING, FAIL +from utils.helpers import ENDC, COLOR_BOLD, COLOR_UNDERLINE, COLOR_GREEN, COLOR_GRAY + +from utils.helpers import color, C#.header, blue, okgreen, warning, red, bold, underline, green, gray, endc + +from utils.helpers import EasyCopy, opcode, find_f_list, find_f, replace_f + +from pano.prettify import prettify, pprint_logic + +from core.arithmetic import simplify_bool + +import collections + +from copy import deepcopy + +from utils.signatures import get_func_name, set_func, get_abi_name, get_func_params, set_func_params_if_none + +import pano.folder +from core.masks import mask_to_type, type_to_mask + +import json + +import logging +logger = logging.getLogger(__name__) + +def find_parents(exp, child): + if type(exp) not in (list, tuple): + return [] + + res = [] + + for e in exp: + if e == child: + res.append(exp) + res.extend(find_parents(e, child)) + + return res + + +class Function(EasyCopy): + + def __init__(self, hash, trace): + self.hash = hash + self.name = get_func_name(hash) + self.color_name = get_func_name(hash, add_color=True) + self.abi_name = get_abi_name(hash) + + self.const = None + self.read_only = None + self.payable = None + + self.hash = hash + + self.trace = deepcopy(trace) + self.orig_trace = deepcopy(self.trace) + + self.params = self.make_params() + + if 'unknown' in self.name: + self.make_names() + + self.trace = self.cleanup_masks(self.trace) + self.ast = None + + self.analyse() + + assert self.payable is not None + + self.is_regular = self.const is None and \ + self.getter is None + + def cleanup_masks(self, trace): + def rem_masks(exp): + if exp ~ ('bool', ('cd', int:idx)): + if idx in self.params and \ + self.params[idx][0] == 'bool': + return ('cd', idx) + + elif exp ~ ('mask_shl', :size, 0, 0, ('cd', int:idx)): + if idx in self.params: + kind = self.params[idx][0] + def_size = type_to_mask(kind) + if size == def_size: + return ('cd', idx) + + return exp + + return replace_f(trace, rem_masks) + + def make_names(self): + new_name = self.name.split('(')[0] + + self.name = '{}({})'.format(new_name, + ', '.join((p[0]+' '+p[1]) for p in self.params.values())) + self.color_name = '{}({})'.format(new_name, + ', '.join((p[0]+' '+COLOR_GREEN+p[1]+ENDC) for p in self.params.values())) + + + self.abi_name = '{}({})'.format(new_name, + ','.join(p[0] for p in self.params.values())) + + + def ast_length(self): + if self.trace is not None: + return len((self.print().split('\n'))), len(self.print()) + else: + return 0, 0 + + def priority(self): + # sorts functions in this order: + # - self-destructs + # - (read-only? would be nice, but some read-only funcs can be very long, e.g. etherdelta) + # - length + + if self.trace is None: + return 0 + + if 'selfdestruct' in str(self.trace): + return -1 + + else: + return self.ast_length()[1] + + def make_params(self): + ''' + figures out parameter types from the decompiled function code. + + does so by looking at all 'cd'/calldata occurences and figuring out + how they are accessed - are they masked? are they used as pointers? + + ''' + + params = get_func_params(self.hash) + if len(params) > 0: + res = {} + idx = 4 + for p in params: + res[idx] = (p['type'], p['name']) + idx += 32 + else: + # good testing: solidstamp, auditContract + # try to find all the references to parameters and guess their types + occurences = find_f_list(self.trace, lambda exp: [exp] if (exp ~ ('mask_shl',_,_,_,('cd',_)) or exp ~ ('cd',_)) else []) + sizes = {} + for o in occurences: + o ~ ('mask_shl', :size, _, _, ('cd', :idx)) + + if o ~ ('cd', :idx): + size = 256 + + if idx == 0: + continue + + if idx ~ ('add', 4, ('cd', :in_idx)): + # this is a mark of 'cd' being used as a pointer + sizes[in_idx] = -1 + continue + + if idx not in sizes: + sizes[idx] = size + + elif size < sizes[idx]: + sizes[idx] == size + + + for idx in sizes: + if type(idx) != int or (idx-4) % 32 != 0: + logger.warning('unusual cd') + return {} + + # for every idx check if it's a bool by any chance + for idx in sizes: + li = find_parents(self.trace, ('cd', idx)) + for e in li: + if opcode(e) not in ('bool', 'if', 'iszero'): + break + + if e ~ ('mask_shl', _, :off, _, :val): + assert val == ('cd', idx) + if off != 0: + sizes[idx] = -2 # it's a tuple! + else: + sizes[idx] = 1 + + res = {} + count = 1 + for k in sizes: + + if type(k) != int: + logger.warning(f'unusual calldata reference {k}') + return {} + + for idx in sorted(sizes.keys()): + size = sizes[idx] + + if size == -2: + kind = 'tuple' + elif size == -1: + kind = 'array' + elif size == 1: + kind = 'bool' + else: + kind = mask_to_type(size, force=True ) + + assert kind != None, size + + res[idx] = (kind, f'_param{count}') + count += 1 + + return res + + def serialize(self): + trace = self.trace + + res = { + 'hash': self.hash, + 'name': self.name, + 'color_name': self.color_name, + 'abi_name': self.abi_name, + 'length': self.ast_length(), + 'getter': self.getter, + 'const': self.const, + 'payable': self.payable, + 'print': self.print(), + 'trace': trace, + 'params': self.params, + } + try: + assert json.dumps(res) # check if serialisation works well + except: + logger.error('failed serialization %s', self.name) + raise + + return res + + + def print(self): + out = self._print() + return "\n".join(out) + + def _print(self): + set_func(self.hash) + set_func_params_if_none(self.params) + + if self.const is not None: + + val = self.const + val ~ ('return', :val) + + return [COLOR_HEADER+"const "+ENDC+str(self.color_name.split('()')[0])+" = "+COLOR_BOLD+prettify(val)+ENDC] + + else: + comment = "" + + if not self.payable: + comment = "# not payable" + + if self.name == "_fallback()": + if self.payable: + comment = "# default function" + else: + comment = "# not payable, default function" # qweqw + + header = [ color("def ", C.header) + self.color_name + + (color(" payable", C.header) if self.payable else "") + ': ' + + color(comment, C.gray) ] + + if self.ast is not None: + res = list(pprint_logic(self.ast)) + else: + res = list(pprint_logic(self.trace)) + + if len(res) == 0: + res = [' stop'] + + return header + res + + def simplify_string_getter_from_storage(self): + ''' + a heuristic for finding string getters and replacing them + with a simplified version + + test cases: unicorn + 0xF7dF66B1D0203d362D7a3afBFd6728695Ae22619 name + 0xf8e386EDa857484f5a12e4B5DAa9984E06E73705 version + + if you want to see how it works, turn this func off + and see how test cases decompile + ''' + + if not self.read_only: + return + + if len(self.returns) == 0: + return + + for r in self.returns: + test = r ~ ('return', ('data', ('arr', ('storage', 256, 0, ('length', :loc)), ...))) + + if not test: + return + + self.trace = [('return', ('storage', 256, 0, ('array', ('range', 0, ('storage', 256, 0, ('length', loc))), loc)))] + self.getter = self.trace[0][1] + + + def analyse(self): + assert len(self.trace) > 0 + + def find_returns(exp): + if opcode(exp) == 'return': + return [exp] + else: + return [] + + self.returns = find_f_list(self.trace, find_returns) + + first = self.trace[0] + + if opcode(first) == 'if' and simplify_bool(first[1]) == 'callvalue' \ + and (first[2][0] == ('revert', 0) or opcode(first[2][0]) == 'invalid'): + self.trace = self.trace[0][3] + self.payable = False + elif opcode(first) == 'if' and simplify_bool(first[1]) == ('iszero', 'callvalue') \ + and (first[3][0] == ('revert', 0) or opcode(first[3][0]) == 'invalid'): + self.trace = self.trace[0][2] + self.payable = False + else: + self.payable = True + + self.read_only = True + for op in ['store', 'selfdestruct', 'call', 'delegatecall', 'codecall', 'create']: + if f"'{op}'" in str(self.trace): + self.read_only = False + + + ''' + const func detection + ''' + + self.const = self.read_only + for exp in ['storage', 'calldata', 'calldataload', 'store', 'cd']: + if exp in str(self.trace) or len(self.returns)!=1: + self.const = False + + if self.const: + self.const = self.returns[0] + if len(self.const) == 3 and opcode(self.const[2]) == 'data': + self.const = self.const[2] + if len(self.const) == 3 and opcode(self.const[2]) == 'mask_shl': + self.const = self.const[2] + if len(self.const) == 3 and type(self.const[2]) == int: + self.const = self.const[2] + else: + self.const = None + + ''' + getter detection + ''' + + self.getter = None + self.simplify_string_getter_from_storage() + if self.const is None and \ + self.read_only and \ + len(self.returns) == 1: + ret = self.returns[0][1] + if ret ~ ('bool', ('storage', _, _, :loc)): + self.getter = ret # we have to be careful when using this for naming purposes, + # because sometimes the storage can refer to array length + + elif opcode(ret) == 'mask_shl' and opcode(ret[4]) == 'storage': + self.getter = ret[4] + elif opcode(ret) == 'storage': + self.getter = ret + elif ret ~ ('data', *terms): + # for structs, we check if all the parts of the struct are storage from the same + # location. if so, we return the location number + + t0 = terms[0] # 0xFAFfea71A6da719D6CAfCF7F52eA04Eb643F6De2 - documents + if t0 ~ ('storage', 256, 0, :loc): + for e in terms[1:]: + if not (e ~ ('storage', 256, 0, ('add', _, loc))): + break + else: + self.getter = t0 + + # kitties getKitten - with more cases this and the above could be uniformed + if self.getter is None: + prev_loc = -1 + for e in terms: + def l2(x): + if x ~ ('sha3', ('data', _, int:l)): + if l < 1000: + return l + if x ~ ('sha3', int:l) and l < 1000: + return l + return None + + loc = find_f(e, l2) + if not loc or (prev_loc != -1 and prev_loc != loc): + break + prev_loc = loc + + else: + self.getter = ('struct', ('loc', loc)) + + else: + pass + + return self diff --git a/pano/loader.py b/pano/loader.py new file mode 100644 index 00000000..43864e9f --- /dev/null +++ b/pano/loader.py @@ -0,0 +1,304 @@ +# coding: tilde + +from utils.profiler import checkpoint + +from utils.opcode_dict import opcode_dict +from utils.helpers import pretty_bignum, padded_hex, EasyCopy, assure_dir_exists +import logging +import json +import traceback + +from utils.signatures import make_abi, get_func_name + +from utils.supplement import fetch_sig + +from utils.helpers import colorize, COLOR_GRAY, find_f_list, find_f, ENDC +import secret + +import os.path + +logger = logging.getLogger(__name__) + +cache_sigs = { + True: {}, + False: {}, +} + + +def code_fetch(address, network='mainnet'): + logger.info('fetching code from %s', network) + + if network == 'goerli': + url = f"https://goerli.infura.io/v3/{secret.INFURA_KEY}" + else: + url = f"https://{network}.infura.io/" + + from web3 import Web3, HTTPProvider + + w3 = Web3(HTTPProvider(url)) + code = w3.eth.getCode(address).hex()[2:] + + return code + +class Loader(EasyCopy): + signatures = {} + + lines = {} # global, let's assume one loader for now + binary = [] # array of ints, each int represents a byte in the source file + + @staticmethod + def find_sig(sig, add_color=False): + if '???' in sig: + return None + + if sig in Loader.signatures: + if 'unknown' not in Loader.signatures[sig]: + return Loader.signatures[sig] + + if sig in cache_sigs[add_color]: + return cache_sigs[add_color][sig] + + if len(sig) < 8: + return None + + a = fetch_sig(sig) + if a is None: + return None + + # duplicate of get_func_name from signatures + if 'params' in a: + res = '{}({})'.format(a['name'],', '.join([colorize(x['type'], COLOR_GRAY, add_color)+' '+x['name'][1:] for x in a['params']])) + else: + res = a['folded_name'] + + cache_sigs[add_color][sig] = res + return res + + def __init__(self): + self.last_line = None + self.jump_dests = [] + self.func_dests = {} # func_name -> jumpdest + self.hash_targets = {} + self.func_list = [] + + self.addr = None + self.binary = None + + + def load(self, this_addr): + if len(this_addr) > 30: + self.load_addr(this_addr) + else: + self.load_stdin(this_addr) + + def load_stdin(self, hash_id): + assure_dir_exists('cache_stdin') + + fname = f'cache_stdin/{hash_id}.bin' + address = hash_id + self.addr = hash_id + with open(fname) as f: + code = f.read() + self.network = 'stdin' + + self.load_binary(code) + + def load_addr(self, address): + if address == address.lower(): + logger.warning('Address not checksummed. Fixed, but needed to import web3 (+0.6s exec time)') + from web3 import Web3 # only here, because Web3 + address = Web3.toChecksumAddress(address) + + self.addr = address + + fname = None + code = None + + dir_name = 'cache_code/'+address[:5]+'/' + + assure_dir_exists(dir_name) + + cache_fname = f'{dir_name}{address}.bin' + + if address == address.lower() and os.path.isfile(cache_fname.lower()): + print('addr not checksummed, but found a checksummed one in cache, using that one') + cache_fname = cache_fname.lower() + + if os.path.isfile(cache_fname): + logger.info('Code for %s found in cache...', address) + + with open(cache_fname) as source_file: + code = source_file.read() + self.network = 'mainnet' + + else: + logger.info('Fetching code for %s...', address) + + code = '' + for network in 'mainnet', 'goerli', 'ropsten', 'kovan', 'rinkeby': + code = code_fetch(address, network) + if len(code)>0: + self.network = network + break + else: + self.network = 'none' + + with open(cache_fname, "w+") as f: + f.write(code) + + fname = cache_fname + + self.load_binary(code) + + def run(self, VM): + assert self.binary is not None, "Did you run load_*() first?" + + try: + # decompiles the code, starting from location 0 + # and running VM in a special mode that returns 'funccal' + # in places where it looks like there is a func call + + trace = VM.run(0) + + def func_calls(exp): + if exp ~ ('funccall', :fx_hash, :target): + return [(fx_hash, target)] + else: + return [] + + func_list = find_f_list(trace, func_calls) + + for fx_hash, target in func_list: + self.add_func(target=target, hash=fx_hash) + + # find default + + def find_default(exp): + if exp ~ ('if', :cond, :if_true, :if_false) and str(('cd', 0)) in str(cond): + if find_f_list(if_false, func_calls) == []: + fi = if_false[0] + if fi ~ ('jd', :jd): + return int(jd) + + if find_f_list(if_true, func_calls) == []: + fi = if_true[0] + if fi ~ ('jd', :jd): + return int(jd) + + else: + return None + + default = find_f(trace, find_default) + self.add_func(default or 0, name='_fallback()') + + except: + logger.error(f"Loader issues%s\n%s", ENDC, traceback.format_exc()) + self.add_func(0, name='_fallback()') + + abi = make_abi(self.hash_targets) + for hash, target in self.hash_targets.items(): + fname = get_func_name(hash) + self.func_list.append((hash, fname ,target, )) + + def next_line(self, i): + i += 1 + while i not in self.lines and self.last_line > i: + i += 1 + + if i <= self.last_line: + return i + else: + return None + + def add_func(self, target, hash=None, name=None): + + assert hash is not None or name is not None # we need at least one + assert not (hash is not None and name is not None) # we don't want both + + if hash != None: + padded = padded_hex(hash,8)#lines[i-12][2] + if padded in self.signatures: + name = self.signatures[padded] + else: + name = "unknown_{}(?????)".format(padded) + self.signatures[padded] = name + + if hash is None: + self.hash_targets[name] = target + else: + self.hash_targets[padded_hex(hash, 8)] = target + + + self.func_dests[name] = target + + def disasm(self): + for line_no, op, param in self.parsed_lines: + yield f"{hex(line_no)}, {op}, {hex(param) if param is not None else ''}" + + def load_binary(self, source): + stack = [] + self.binary = [] + + if source[:2] == '0x': + source = source[2:] + + while len(source[:2]) >0: + num = int('0x'+source[:2],16) + self.binary.append(num) + stack = [num]+stack + source = source[2:] + + line = 0 + + parsed_lines = [] + + while len(stack)>0: + popped = stack.pop() + + orig_line = line + + if popped not in opcode_dict: + op = 'UNKNOWN' + param = popped + + else: + param = None + op = opcode_dict[popped] + + if op == 'jumpdest': + self.jump_dests.append(line) + + if op[:4] == 'push': + num_words = int(op[4:]) + + param = 0 + for i in range(num_words): + try: + param = param*0x100 + stack.pop() + line += 1 + except: + break + + parsed_lines.append((orig_line, op, param)) + line += 1 + + self.parsed_lines = parsed_lines + self.last_line = line + self.lines = {} + + for line_no, op, param in parsed_lines: + if op[:4] == 'push' and param > 1000000000000000: + param = pretty_bignum(param) # convert big numbers into strings if possibble + # should be moved to prettify really + + if op[:3] == 'dup': + param = int(op[3:]) + op = 'dup' + + if op[:4] == 'swap': + param = int(op[4:]) + op = 'swap' + + self.lines[line_no] = (line_no, op, param) + + return self.lines diff --git a/pano/postprocess.py b/pano/postprocess.py new file mode 100644 index 00000000..e7ad7863 --- /dev/null +++ b/pano/postprocess.py @@ -0,0 +1,95 @@ +''' + + Postprocess goes through a function trace, and removes all the unnecessary memcalls. + + Assumes that the trace is a tree structure (that is - needs to be run *before* the folder) + +''' +from utils.helpers import opcode +from core.algebra import minus_op + +def cleanup_mul_1(trace): + + def cleanup_exp(exp): + if type(exp) != tuple: + + return exp + + # mask_shl storage -> storage + if opcode(exp) == 'mask_shl' and \ + opcode(exp[4]) == 'storage' and \ + exp[1] == exp[4][1] and \ + type(exp[2]) == int and \ + exp[2] == minus_op(exp[3]) and \ + exp[2] == exp[4][2]: + return cleanup_exp(exp[4]) + + if exp[:4] == ('mask_shl', 160, 0, 0) and \ + exp[4] in ('caller', ): + return exp[4] + + if opcode(exp) == 'bool' and type(exp[1]) == int: + return 1 if exp[1] != 0 else 0 + + # mask_shl, 200, 56, 0, "'supportsInterface(bytes4)'" -> supportsInterface(bytes4) + if opcode(exp) == 'mask_shl' and \ + type(exp[1]) == int and \ + type(exp[2]) == int and \ + type(exp[3]) == int and \ + type(exp[4]) == str and \ + exp[1]+exp[2] == 256 and \ + exp[3] == 0 and \ + exp[4][0] == exp[4][-1] == "'": + s = exp[4][1:-1] + if len(s)*8 == exp[1]: + return s + + + if exp[:4] == ('mask_shl', 256, 0, 0): + e = cleanup_exp(exp[4]) + + if type(e) == int and e < 0x100 ** 32: + return e + + if opcode(e) == 'sha3': + return e + + # ^ should be more generic + +# if opcode(exp) == 'iszero' and \ +# opcode(exp[1]) == 'eq': +# return ('Neq', ) + cleanup_exp(exp[1][1:]) + + + if opcode(exp) == 'mul' and exp[1] == 1: + if len(exp) == 3: + assert False + return cleanup_exp(exp[2]) + else: + assert len(exp)>3, exp + return ('mul', )+tuple([cleanup_exp(x) for x in exp[2:]]) + + return tuple([cleanup_exp(x) for x in exp]) + + res = [] + + for line in trace: + if opcode(line) == 'if': + cond, if_true, if_false = line[1:] + res.append(('if', cleanup_exp(cond), cleanup_mul_1(if_true), cleanup_mul_1(if_false))) + + elif opcode(line) == 'while': + cond, tr, jd, setvars = line[1], line[2], line[3], line[4] + res.append(('while', cleanup_exp(cond), cleanup_mul_1(tr), jd, cleanup_exp(setvars))) + + elif opcode(line) == 'LOOP': + tr, jd = line[1:] + res.append(('LOOP', cleanup_mul_1(tr), jd)) + + else: + res.append(cleanup_exp(line)) + + return res + + + diff --git a/pano/prettify.py b/pano/prettify.py new file mode 100644 index 00000000..274f250f --- /dev/null +++ b/pano/prettify.py @@ -0,0 +1,1287 @@ +# coding: tilde +from utils.helpers import COLOR_HEADER, COLOR_BLUE, COLOR_OKGREEN, COLOR_WARNING, FAIL, ENDC, COLOR_BOLD, COLOR_UNDERLINE, COLOR_GREEN, COLOR_GRAY + +import logging +import core.arithmetic as arithmetic +from utils.helpers import opcode, padded_hex, pretty_bignum, all_concrete, replace_lines, replace_f +from utils.helpers import clean_color, C, is_array + +from core.algebra import lt_op, mul_op, minus_op, ge_zero, safe_ge_zero, sub_op, add_op, apply_mask, safe_le_op, to_bytes + +from copy import deepcopy + +from core.arithmetic import simplify_bool, is_zero + +from core.masks import get_bit, mask_to_type + +from utils.helpers import precompiled + +from utils.helpers import colorize, to_exp2 + +from utils.signatures import get_param_name + +from functools import partial + +from pano.loader import Loader + +logger = logging.getLogger(__name__) + + +def make_ast(trace): + def store_to_set(line): + if line ~ ('store', :size, :off, :idx, :val): + return ('set', ('stor', size, off, idx), val) + else: + return line + + def mask_storage(exp): + if exp ~ ('stor', :size, :off, :idx): + return ('mask_shl', size, 0, 0, exp) + else: + return exp + + trace = replace_lines(trace, store_to_set) + trace = replace_f(trace, mask_storage) + + return trace + + + +def format_exp(exp): + if type(exp) == str: + return f'"{exp}"' + if type(exp) == int: + if exp > 10 ** 6 and exp % 10 ** 6 != 0: + return hex(exp) + else: + return str(exp) + elif type(exp) != list: + return str(exp) + else: + if len(exp) == 0: + return COLOR_GRAY + "[]" + ENDC + if type(opcode(exp)) == list: + return ( + COLOR_GRAY + + "[" + + ENDC + + f"{COLOR_GRAY}, {ENDC}".join([format_exp(e) for e in exp]) + + COLOR_GRAY + + "]" + + ENDC + ) + else: + return ( + COLOR_GRAY + + "[" + + ENDC + + f"{COLOR_GRAY}, {ENDC}".join( + [opcode(exp)] + [format_exp(e) for e in exp[1:]] + ) + + COLOR_GRAY + + "]" + + ENDC + ) + +def pprint_repr(trace, indent=0): + for line in trace: + + if opcode(line) == "if": + cond, if_true, if_false = line[1:] + print(indent * " ", f"[if, {format_exp(cond)}, [") + pprint_repr(if_true, indent + 2) + print(indent * " ", "],[") + pprint_repr(if_false, indent + 2) + print(indent * " ", "] ") + + elif opcode(line) == "while": + cond, tr = line[1], line[2] + print(indent * " ", f"[while, {format_exp(cond)}, [") + pprint_repr(tr, indent + 2) + print(indent * " ", "], ") + + else: + print(indent * " ", format_exp(line) + f"{COLOR_GRAY}, {ENDC}") + +''' +def pprint_repr(exp): + print(repr(exp)) + return + print(pretty_repr(exp)) +''' + +def pretty_repr(exp, indent=0): + if type(exp) not in (tuple, list): + return repr(exp) + elif type(exp) == list: + res = ', \n'.join([' '*indent + pretty_repr(e, indent) for e in exp]) + res = indent*' '+'['+res[:-3]+']' + return res + elif type(exp) == tuple: + res = ', '.join([pretty_repr(e) for e in exp]) + if len(res) > 40 and exp ~ (:op, :first, *rest): + indent += len(pretty_repr(op)+', ')+1 + res = pretty_repr(op)+', '+pretty_repr(first, indent)+',\n' + for r in rest: + res += indent*' '+pretty_repr(r, indent)+', \n' + res = res[:-3] # removes ', \n' + + return '('+res+')' + elif type(exp) == list: + res = (',\n'+' '*indent).join([pretty_repr(e, indent) for e in exp]) + return f'[{res}]' + +#print(pretty_repr(('data', ('mem', ('range', ('add', 32, 'QQ', ('mask_shl', 251, 5, 0, ('add', 31, ('ext_call.return_data', 128, 32)))), 32)), 'yy', ('data', ('mem', ('range', ('add', 32, 'QQ'), ('ext_call.return_data', 128, 32))), ('mem', ('range', ('add', 96, 'QQ', ('mask_shl', 251, 5, 0, ('add', 31, ('ext_call.return_data', 128, 32))), ('ext_call.return_data', 128, 32)), 0)))))) +#exit() + +def pformat_trace(trace): + return '\n'.join(pprint_logic(trace)) + "\n\n" + +def pprint_trace(trace): + trace = make_ast(trace) + pprint_ast(trace) + +def pprint_ast(trace): + empty = True + + for l in pprint_logic(trace): + print(l) + empty = False + + if empty: + print(' stop') + print() + print() + +def pprint_logic(exp, indent=2): + INDENT_LEN = 4 + + if opcode(exp) == 'while': + if len(exp) == 5: + cond, path, jd, vars = exp[1], exp[2], exp[3], exp[4] + else: + cond, path = exp[1], exp[2] + jd, vars = None, [] + + for v in vars: + yield ' '*indent + list(pretty_line(('setvar', v[1],v[2]), add_color=True))[0] + + yield ' '*indent + COLOR_GREEN+ 'while ' + ENDC+prettify(cond, add_color=True, parentheses=False, rem_bool=True) + COLOR_GREEN+':'+ ENDC#+COLOR_GREEN+': # '+str(jd)+ENDC + if type(path) != list: + path = path.trace + + for l in pprint_logic(path, indent + INDENT_LEN): + yield l + + elif exp ~ ('require', :cond): + yield ' '*indent + 'require ' + prettify(exp[1], add_color=True, parentheses=False, rem_bool=True) + '' + + elif exp ~ ('if', :cond, :if_true): # one-sided ifs, only after folding + if len(if_true) == 1 and (first := if_true[0]) and \ + ((first == ('revert', 0)) or (first ~ ('invalid', ...))): + yield ' '*indent + 'require '+prettify(is_zero(exp[1]), add_color=True, parentheses=False, rem_bool=True) + else: + yield ' '*indent + 'if ' + prettify(exp[1], add_color=True, parentheses=False, rem_bool=True) + ':' + for l in pprint_logic(if_true, indent + INDENT_LEN): + yield l + + + elif exp ~ ('if', :cond, :if_true, :if_false): + if len(if_false) == 1 and (first := if_false[0]) and \ + ((first == ('revert', 0)) or \ + (first ~ ('invalid', ...))): + yield ' '*indent + 'require '+prettify(exp[1], add_color=True, parentheses=False, rem_bool=True) + + for l in pprint_logic(exp[2], indent): + yield l + + elif len(if_true) == 1 and (first := if_true[0]) and \ + ((first == ('revert', 0)) or \ + (first ~ ('invalid', ...))): + yield ' '*indent + 'require '+prettify(is_zero(exp[1]), add_color=True, parentheses=False, rem_bool=True) + + for l in pprint_logic(exp[3], indent): + yield l + + else: + yield ' '*indent + 'if ' + prettify(exp[1], add_color=True, parentheses=False, rem_bool=True) + ':' + + for l in pprint_logic(if_true, indent + INDENT_LEN): + yield l + ''' + while len(if_false) == 1 and opcode(if_false) == 'if' and len(if_false) == 4: + first = if_false[0] + assert first ~ ('if', :c, :i_t, :if_false) + + yield ' '*indent + 'elif ' + prettify(c, add_color=True, parentheses=False, rem_bool=True) + ':' + + for l in pprint_logic(i_t, indent + INDENT_LEN): + yield l''' + + yield ' '*indent + 'else:' + for l in pprint_logic(if_false, indent + INDENT_LEN): + yield l + + + elif type(exp) == list: + for idx, line in enumerate(exp): + if idx == len(exp)-1 and indent == 2 and line==('stop', ): + pass # don't print the last stop + else: + for l in pprint_logic(line, indent): + yield l + + elif opcode(exp) == 'or' and len(exp)>1: + yield ' '*indent + 'if' + for l in pprint_logic(exp[1], indent + INDENT_LEN): + yield l + + for line in exp[2:]: + yield ' '*indent + 'or' + for l in pprint_logic(line, indent + INDENT_LEN): + yield l + + else: + for l in pretty_line(exp): + yield ' '* indent + l + +def to_real_int(exp): + if type(exp) == int and get_bit(exp, 255): + return -arithmetic.sub(0, exp) + else: + return exp + +def pretty_line(r, add_color=True): + col = partial(colorize, add_color=add_color) + pret = partial(prettify, parentheses=False, add_color=add_color) + + if type(r) is str: + yield COLOR_GRAY + "# " + r + ENDC + +# elif r ~ ('jumpdest', ...): +# pass + + elif r ~ ('comment', :text): + yield COLOR_GRAY + "# " + prettify(text, add_color=False) + ENDC + + elif r ~ ('log', :params, *events): + # solidstamp and dao cover most of those cases + res_params = pretty_memory(params, add_color=False) + + for e in events: + if type(e) != int: + for e in events[1:]: + res_params = res_params + (prettify(e, add_color=False, parentheses=False), ) + events = [events[0]] + break + # breaks with more than one proper event + + + res_events = tuple(pretty_fname(e, add_color=False, force=True) for e in events) +# print(res_events) + res_events = tuple((x[:10] if x[:2] == '0x' else x) for x in res_events) + for e in res_events: + if '(' not in e: + yield col(f"log {e}{':' if len(res_params)>0 else ''} {', '.join(res_params)}", COLOR_GRAY) + else: + fname, fparams = e.split('(') + + assert fparams[-1] == ')' + fparams = fparams[:-1] + + fparams = fparams.split(', ') + + if fparams == [''] or len(res_params) == 0: + yield col(f'log {e}', COLOR_GRAY) + + elif len(fparams) == len(res_params): + p_list = [] + try: + for idx, ptype, pname in [f"{idx} {p}".split(' ') for idx, p in enumerate(fparams)]: + p_list.append((ptype, pname, res_params[int(idx)])) + except: + logger.warning(f'weird log {e} {fparams}') + yield(f'log {e}') + return + + + if len(p_list) == 1: + yield col(f"log {fname}({p_list[0][0]} {p_list[0][1]}={pret(p_list[0][2], add_color=False, parentheses=False)})", COLOR_GRAY) + else: + ind = len(f'log ') + first = p_list[0] + last = p_list[-1] + pline = lambda p: f'{p[0]} {p[1]}={pret(p[2], add_color=False, parentheses=False)}' # spaces around = not pep8 compliant + # but without them it was less readable + + yield col(f"log {fname}(", COLOR_GRAY)# + yield col(f" {pline(first)},", COLOR_GRAY) + + for p in p_list[1:-1]: + yield col(' '*ind + f"{pline(p)},", COLOR_GRAY) + + yield col(' '*ind + f"{pline(last)})", COLOR_GRAY) +# elif len(res_params) == 0: +# yield col(f'log {e}', COLOR_GRAY) + else: + yield col(f'log {e}:', COLOR_GRAY) + ind = ' ' * len(f'log {fname}(') + for p in res_params: + yield col(ind + p + ',', COLOR_GRAY) +# print(repr(len(fparams)), len(res_params)) + + elif r ~ ('callcode', :gas, :addr, :wei, :fname, :fparams): + fname = pretty_fname(fname, add_color=add_color) + + if type(addr) == int: + addr = hex(addr) + addr = prettify(addr, add_color = add_color) + gas = prettify(gas, parentheses = False, add_color = add_color) + fparams = pretty_memory(fparams, add_color = add_color) + + if fname is not None: + + if type(fname) == str: + fname = pretty_fname(fname, add_color = add_color) + yield f"{COLOR_WARNING}codecall{ENDC} {addr}.{fname} with:" + + else: + yield f"{COLOR_WARNING}codecall{ENDC} {addr} with:" + yield " funct " + prettify(fname, add_color=add_color) + + else: + yield f"{COLOR_WARNING}codecall{ENDC} {addr} with:" + + if wei != 0: + wei = prettify(wei, parentheses=False, add_color=add_color) + yield f" value {wei} {COLOR_GRAY}wei{ENDC}" + + yield f" gas {gas} {COLOR_GRAY}wei{ENDC}" + + if fparams is not None: + yield " args {}".format(', '.join(fparams)) + + elif r ~ ('delegatecall', :gas, :addr, :fname, :fparams): + fname = pretty_fname(fname, add_color=add_color) + + if type(addr) == int: + addr = hex(addr) + addr = prettify(addr, add_color = add_color) + gas = prettify(gas, parentheses = False, add_color = add_color) + fparams = pretty_memory(fparams, add_color = add_color) + + if fname is not None: + + if type(fname) == str: + fname = pretty_fname(fname, add_color = add_color) + yield f"{COLOR_WARNING}delegate{ENDC} {addr}.{fname} with:" + + else: + yield f"{COLOR_WARNING}delegate{ENDC} {addr} with:" + yield " funct " + prettify(fname, add_color=add_color) + + else: + yield f"{COLOR_WARNING}delegate{ENDC} {addr} with:" + + + yield f" gas {gas} {COLOR_GRAY}wei{ENDC}" + + if fparams is not None: + yield " args {}".format(', '.join(fparams)) + + elif r ~ ('selfdestruct', :addr): + yield col('selfdestruct(', COLOR_WARNING)+col(pret(addr, add_color=False, parentheses=False), FAIL)+col(')', COLOR_WARNING) + + elif r ~ ('precompiled', :var_name, :func_name, :params): + yield "{} = {}({}) {}".format(col(var_name, COLOR_BLUE), func_name, prettify(params, add_color=add_color, parentheses=False), + COLOR_GRAY+'# precompiled'+ENDC) + + elif r ~ ('create', :wei, :code): + yield f"create contract with {wei} wei" + yield f" code: {prettify(code)}" + + elif r ~ ('create2', :wei, :code, :salt): + yield f"create2 contract with {wei} wei" + yield f" salt: {prettify(salt)}" + yield f" code: {prettify(code)}" + + + elif r ~ ('call', :gas, :addr, :wei, :fname, :fparams): + + if type(addr) == int: + if len(hex(addr)) > 22+2: + addr = padded_hex(addr, 40) # todo: padded hex + else: + addr = hex(addr) # if it's longer, padded hex returns '???' + + addr = pret(addr) + gas = pretty_gas(gas, wei, add_color) + + + if fname is None: + yield f"call {addr} with:" + + else: + fname = pretty_fname(fname, add_color=add_color) + + if fname == '0x0': + yield f"call {addr} with:" + + elif type(fname) == str: + yield f"call {addr}.{pret(fname)} with:" + + else: + yield f"call {addr} with:" + yield f" funct {pret(fname)}" + + if wei != 0: + wei = prettify(wei, parentheses=False, add_color=add_color) + yield f" value {wei} {COLOR_GRAY}wei{ENDC}" + + yield f" gas {gas} {COLOR_GRAY}wei{ENDC}" + + if fparams is not None: + fparams = pretty_memory(fparams, add_color=add_color) + yield " args {}".format(', '.join(fparams)) + + elif r ~ ('staticcall', :gas, :addr, :wei, :fname, :fparams): + + if type(addr) == int: + addr = hex(addr) + + addr = prettify(addr, add_color=add_color, parentheses=False) + gas = pretty_gas(gas, wei, add_color) + + if fname is not None: + fanme = fname + fname = pretty_fname(fname, add_color=add_color) + + if fname == '0x0': + yield f"static call {addr} with:" + elif type(fname) == str and fname != '0x0': + yield f"static call {addr}.{pret(fname)} with:" + else: + yield f"static call {addr} with:" + yield f" funct {pret(fname)}" + + else: + yield f"static call {addr} with:" + + yield f" gas {gas} {COLOR_GRAY}wei{ENDC}" + + if fparams is not None: + fparams = pretty_memory(fparams, add_color=add_color) + yield " args {}".format(', '.join(fparams)) + + elif r ~ ('label', :name, :setvars): + yield COLOR_GREEN + f'label {str(name)} setvars: {str(setvars)}' + ENDC + + elif r ~ ('goto', *rest): + yield COLOR_GREEN + f'goto {str(rest)}'+ENDC + + elif r ~ ('continue', :jd, :setvars): + for v in setvars: + yield str(list(pretty_line(v, add_color=True))[0]) + yield COLOR_GREEN + 'continue ' + ENDC # +str(jd)+ENDC + + elif r ~ ('setvar', ...): + yield prettify(r, add_color=add_color) + + elif r ~ ('setmem', ...): + yield prettify(r, add_color=add_color) + + elif r ~ ('set', :idx, :val): + if val ~ ('add', int:v, idx): + assert v != 0 + + if v == -1: + yield prettify(idx, add_color=add_color) + '--' + elif v == 1: + yield prettify(idx, add_color=add_color) + '++' + + elif v < 0: + yield prettify(idx, add_color=add_color) + ' -= ' + prettify(-v, add_color=add_color, parentheses=False) + else: + yield prettify(idx, add_color=add_color) + ' += ' + prettify(v, add_color=add_color, parentheses=False) + + elif val ~ ('add', idx, ('mul', -1, :v)): + yield prettify(idx, add_color=add_color) + ' -= ' + prettify(v, add_color=add_color, parentheses=False) + elif val ~ ('add', idx, :v): + yield prettify(idx, add_color=add_color) + ' += ' + prettify(v, add_color=add_color, parentheses=False) + elif val ~ ('add', ('mul', -1, :v), idx): + yield prettify(idx, add_color=add_color) + ' -= ' + prettify(v, add_color=add_color, parentheses=False) + elif val ~ ('add', :v, idx): + yield prettify(idx, add_color=add_color) + ' += ' + prettify(v, add_color=add_color, parentheses=False) + + else: + yield prettify(idx, add_color=add_color) + ' = ' + prettify(val, add_color=add_color, parentheses=False) + + elif r ~ ('stop', ...): + yield 'stop' + + elif r ~ ('undefined', *params): + yield COLOR_WARNING + '...' + ENDC + COLOR_GRAY + f' # unusual jump {params}, couldn\'t decompile, sorry' + ENDC + + elif r ~ ('invalid', *rest): + if len(rest) > 0: + yield "revert "# + COLOR_GRAY + f"# {rest}" + ENDC + else: + yield "revert" + + elif r ~ ('invalid', ...): + yield "revert " + (COLOR_GRAY + f"# {rest}" + ENDC) + + elif (r ~ ('revert', 0)) or \ + (r ~ ('revert', ('mem', 0, 0))): + + yield "revert" + + elif r ~ (:op, ('mem', ('range', :mem_idx, :mem_len))) and \ + op in ('revert', 'return'): + + if op == 'revert': + yield 'revert with memory' + else: + yield op + ' memory' + + if len ~ ('sub', :mem_until, mem_idx): + yield f" from {pret(mem_idx)}" + yield f" to {pret(mem_until)}" + else: + yield " from " + pret(mem_idx) + yield " " + col('len', COLOR_WARNING) + " " + pret(mem_len) + + elif r ~ (:op, :param) and op in ('return', 'revert'): + + if op == 'revert': + op = 'revert with' + + res_mem = pretty_memory(param, add_color=True) + ret_val = ', '.join(res_mem) + + if len(clean_color(ret_val)) < 120 or opcode(param) != 'data': + yield f'{op} {ret_val}' + else: + # split long returns into lines. e.g. kitties.getKitten, or kitties.tokenMetadata +# yield str(len(ret_val)) + res_mem = list(res_mem) + if res_mem[0] == '32': + res_mem.pop(0) + res_mem[0] = '32, ' + res_mem[0] # happens often, this is probably an array structure, + # and sole `32` in first line looks ugly + + yield f'{op} {res_mem[0]}, ' + for idx, l in enumerate(res_mem[1:]): + yield ' '*len(op) + ' ' + l + (',' if idx != len(res_mem) - 2 else '') + +# assert op == 'revert' +# yield "{} with {}".format(op, ret_val) # adding 'with' to make it more readable + + + elif r ~ ('store', :size, :off, :idx, :val): + stor_addr = prettify(('stor', size, off, idx), add_color=add_color) + stor_val = prettify(val, add_color=add_color, parentheses=False) + + yield "{} = {}".format(stor_addr, stor_val) + + elif type(r) == list and len(r) > 1: + yield "{} {}".format(r[0], ', '.join([prettify(x, True, False, add_color = add_color) for x in r[1:]])) + + elif type(r) == list: + yield str(r[0]) + + else: + yield str(r) + +def pretty_type(t): + + if t ~ ('def', :name, :loc, ('mask', :size, :off)): + return pretty_type(('def', name, loc, size)) + COLOR_GRAY + (f' offset {off}' if off > 0 else '') + ENDC + + elif t ~ ('def', :name, :loc, :bts): + if type(loc) == int and loc > 1000: + loc = hex(loc) + return f' {COLOR_GREEN}{name}{ENDC} is {pretty_type(bts)} {COLOR_GRAY}at storage {loc}{ENDC}' + + elif t ~ ('struct', 1): + return 'struct' + + elif t == 'struct': + return 'struct' + + elif t ~ ('struct', int:num): + return f'struct {num} bytes' + + elif t ~ ('array', :bts): + return f'array of '+pretty_type(bts) + + elif t ~ ('mapping', :bts): + return f'mapping of '+pretty_type(bts) + + elif type(t) == int: + return mask_to_type(t, force=True) + + else: + assert False, f'unknown type {t}' + +def pretty_stor(exp, add_color=True): + col = partial(colorize, color=COLOR_GREEN, add_color=add_color) + stor = partial(pretty_stor, add_color=add_color) + pret = partial(prettify, parentheses=False, add_color=add_color) + + if exp ~ ('stor', ('length', :idx)): + return stor(idx) + col('.length') + + if exp ~ ('loc', :loc): + return col(f'stor_l{loc}') + + if exp ~ ('name', :name, :loc): + return col(name) + +# if exp ~ ('stor', (:op, :param)) and op in ('loc', 'name'): + # with top-level fields, it's just a different stor + # variable. with lower-level we treat it as a struct +# return stor((op, param)) + + if exp ~ ('stor', :loc): + # with top-level fields, it's just a different stor + # variable. with lower-level we treat it as a struct + return stor(loc) + + if exp ~ ('field', :off, :loc): + return stor(loc) + col(f'.field_{pret(off, add_color=False)}') + + if exp ~ ('type', :size, :loc): + if size == 256: + # prettify removes 256 masks by default, force it + return col('uint256(', color=COLOR_GRAY) + stor(loc) + col(')', color=COLOR_GRAY) + else: + return pret(('mask', size, 0, stor(loc))) + + def pr_idx(idx): + if idx ~ ('data', *terms): + return col('][').join([pret(t) for t in terms]) + else: + return pret(idx) + + if exp ~ ('map', :idx, :var): + return stor(var) + col('[') + pr_idx(idx) + col(']') + + if exp ~ ('array', ('mul', int:_, :idx), :var): + exp = ('array', idx, var) # nasty hack to not display storage[2*idx] for storages + # that are structs + # this should be handled in sparser really +# return stor(var) + col('[') + pr_idx(idx) + col(']') + + + if exp ~ ('array', :idx, :var): + return stor(var) + col('[') + pr_idx(idx) + col(']') + + if exp ~ ('length', :var): + return stor(var) + col('.length') + + if exp ~ ('stor', :loc): + return col('stor[') + pret(loc) + col(']') + + if exp ~ ('stor', :size, :off, :loc): + return pret(('mask', size, off, col('stor[') + pret(loc) + col(']'))) + + return col('stor[') + pret(exp) + col(']') + + +def pretty_num(exp, add_color): + col = partial(colorize, add_color=True) + + if type(exp) == float: + if exp - int(exp) == 0: + exp = int(exp) + + if type(exp) == int and exp > 8**50: + return hex(exp) # dealing with binary data probably, usually in call code - display in hex + + if type(exp) == int and exp !=0: + count = 18 + while count >= 9: + + if exp % (10**count) == 0: + if exp // (10**count) == 1: + return f'10^{count}' + else: + return f'{exp // (10**count)} * 10^{count}' + + count -= 1 + + count = 6 + if exp % (10**count) == 0: + if exp // (10**count) == 1: + return f'10^{count}' + else: + return f'{exp // (10**count)} * 10^{count}' + + if type(exp) == int: + if try_fname(exp, add_color) != None: + return try_fname(exp, add_color) + + elif type(exp) == int and (exp&2**256-1)<8**30: # if it's larger than 30 bytes, it's probably + # an address, not a negative number + return str(to_real_int(exp)) + + elif exp>0: + return hex(exp) + + else: + return str(exp) + + # print('warn: weird float exp', exp) + return str(exp) + + +def prettify(exp, rem_bool=False, parentheses=True, top_level=False, add_color=False): + + col = partial(colorize, add_color=add_color) + pret = partial(prettify, add_color=add_color, parentheses=False) + + if rem_bool: + exp = simplify_bool(exp) + if opcode(exp) == 'bool': + return prettify(exp, rem_bool=rem_bool, parentheses=parentheses, top_level=top_level, add_color=add_color) + + if type(exp) == int and exp % (24 * 3600) == 0 and exp > 24 * 3600: + exp = ('mul', exp//3600, 24, 3600) + + if type(exp) == int and exp % 3600 == 0 and exp > 3600: + exp = ('mul', exp//3600, 3600) + # also tried return col('seconds(', COLOR_GRAY) + '1 hour' + col(')', COLOR_GRAY) + # but seemed less intuitive, e.g. 0xf64B584972FE6055a770477670208d737Fff282f calcMaxWithdraw + # and 3600 every programmer should know, by heart, means 1 hour :) + # + # also, not tackling single minutes because too often they are not time related + + if type(exp) in (int, float): + return pretty_num(exp, add_color) + + if opcode(exp) in precompiled.values(): + return f'{exp[0]}({pret(exp[1])})' + + if exp ~ ('arr', int:num, ('mask_shl', _, _, _, str:s)) \ + and len(s) == num+2: + return s + + if exp ~ ('param', :name): + return col(name, COLOR_GREEN) + + if exp ~ ('range', :loc, :size): + return '{} {} {}'.format(pret(loc), + col('len', COLOR_HEADER), + pret(size)) + + if exp ~ ('data', ...): + return ', '.join(pretty_memory(exp, add_color=add_color)) + + if exp ~ ('arr', :l, *terms): + return col('Array(len=', COLOR_GRAY) + pret(l)+ col(', data=', COLOR_GRAY) + pret(('data', )+terms) + col(')', COLOR_GRAY) + + if exp ~ ('blockhash', :number): + return f'block.hash({pret(number)})' + + if exp ~ ('extcodehash', :addr): + return f'ext_code.hash({pret(addr)})' + + if exp ~ ('extcodesize', :addr): + return f'ext_code.size({pret(addr)})' + + if exp ~ ('extcodecopy', :addr, :loc): + return f'ext_code.copy({pret(addr)}, {pret(loc)})' + + + if exp ~ ('max', *terms): + return 'max({})'.format(', '.join([pret(e) for e in terms])) + + if exp == 'number': + return 'block.number' + + if exp == 'calldatasize': + return 'calldata.size' + + if exp == 'returndatasize': + return 'return_data.size' + + if exp == 'difficulty': + return 'block.difficulty' + + if exp == 'gasprice': + return 'block.gasprice' + + if exp == 'timestamp': + return 'block.timestamp' + + if exp == 'coinbase': + return 'block.coinbase' + + if exp == 'gaslimit': + return 'block.gas_limit' + + if exp == 'callvalue': + return 'call.value' + + if exp == 'address': + return 'this.address' + + if exp ~ ('mask_shl', 160, 0, 0, 'caller'): + return 'caller' + + if exp == 'caller': + return 'caller' + + if exp ~ ('mask_shl', 160, 0, 0, 'origin'): + return 'tx.origin' + + if exp ~ ('mulmod', :a, :b, :c): + return f'mulmod({pret(a)}, {pret(b)}, {pret(c)})' # mulmod should really be replaced by mul & mod in other stages + # but this is rare enough to ignore for now + + if exp == 'origin': + return 'tx.origin' + + if exp == 'gas': + return 'gas_remaining' + + if exp == ('bool', 1): + return 'True' + + if exp == ('bool', 0): + return 'False' + + if exp ~ ('code.data', :c_start, :c_len): + return f'code.data[{pret(c_start)} len {pret(c_len)}]' + + if exp ~ ('balance', :addr): + return f'eth.balance({pret(addr)})' + + if exp ~ ('sha3', *terms): + return 'sha3({})'.format(', '.join([pret(e) for e in terms])) + +# if exp ~ ('mask_shl', 251, 5, 0, :val): +# return pret(('mul', 32, val)) + + if (exp ~ ('mask_shl', :size, 5, 0, :val) or + exp ~ ('mask', :size, 5, :val)) and size > 245: + if val ~ ('add', 31, :num): + return f'ceil32({pret(num)})' + else: + return f'floor32({pret(val)})' + + if exp ~ ('call.data', ('add', 36, ('param', :p_name)), :size) and \ + size == ('cd', ('add', 4, ('param', p_name))): + return f"{col(p_name+'[', C.green)}"+'all'+col(']', C.green) + + if exp ~ (:name, :offset, :size) and is_array(name):# in ('call.data', 'ext_call.return_data'): + if size == 32: + return name+f'[{pret(offset)}]' + else: + return name+f'[{pret(offset)} len {pret(size)}]' + + if exp ~ ('mask_shl', :size, :offset, :shl, ('stor', :s_size, :s_off, :s_idx)) and \ + safe_le_op(s_size, size) and shl == 0: + return pret(('stor', s_size, s_off, s_idx)) + + if exp ~ ('stor', ...): + return pretty_stor(exp, add_color=add_color) + + if exp ~ ('type', ...): + return pretty_stor(exp, add_color=add_color) + + if exp ~ ('field', ...): + return pretty_stor(exp, add_color=add_color) + + if exp ~ ('cd', :num): + if num == 0: + return(col('call.func_hash', C.green)) + parsed_exp = get_param_name(exp, add_color=add_color) + + if type(parsed_exp) != str: + return 'cd['+prettify(parsed_exp[1], add_color=add_color)+']' + else: + return parsed_exp + + if exp ~ ('var', int:idx): + nice_names = ['idx','s','t','u','v','w','x','y','z','a','b','c','d','e','f','g','h'] # 'i','j','k','l','m','n','o','p','q','r', + if idx < len(nice_names): + name = nice_names[idx] + else: + name = 'var'+str(idx) + + return col(name, COLOR_BLUE) + + if exp ~ ('var', :name): + return col(str(name), COLOR_BLUE) + + if exp ~ ('mem', ('range', :loc, 32)): + exp = ('mem', loc) + + if exp ~ ('mem', ('range', :loc, :size)): + return col('mem[', COLOR_HEADER) + \ + pret(loc) + \ + col(' len ', COLOR_HEADER) + \ + pret(size) + col(']', COLOR_HEADER) + + elif exp ~ ('mem', :idx): + assert opcode(idx) != 'range' + + return col('mem[', COLOR_HEADER) + \ + pret(idx) + \ + col(']', COLOR_HEADER) + + + if exp ~ ('setvar', :idx, :val): # shouldn't be pretty line? + return pret(('var', idx)) + ' = ' + pret(val, parentheses=False) + + if exp ~ ('setmem', :idx, :val): # --,,-- + return pret(('mem', idx)) + ' = ' + \ + pret(val) + + if exp ~ ('mask_shl', :size, :offset, :shl, :val): + + if all_concrete(size, offset, shl) and \ + exp[1] + exp[2] == 256 and exp[2] == -exp[3] and exp[2] < 8: + # e.g. (Mask(255, 1, eth.balance(this.address)) >> 1 + # --> eth.balance(this.address) / 2 + # for offsets smaller than 8 + + if exp[3] <= 8: + return pret(('div', exp[4], 2**-exp[3]), parentheses=parentheses) + else: + return pret(('shr', exp[3], exp[4]), parentheses=parentheses) + + if (type(exp[1]), type(exp[2]), type(exp[3])) == (int, int, int) and\ + exp[2] == exp[3] and exp[2] < 8: + # e.g. (Mask(255, 1, eth.balance(this.address)) << x + # --> eth.balance(this.address) * 2**x + # for offsets smaller than 8 + + if size + offset != 256 and opcode(val) != 'store': # opcode=store - hotfix for 0x000000000045Ef846Ac1cB7fa62cA926D5701512 + val = ('mask', size + offset, 0, val) # 0 because exp2 == exp3 + + if exp[3] == 0: + return pret(val, parentheses=parentheses) + elif exp[3] <= 8 and exp[3] >= -8: + return pret(('mul', val, 2**exp[3]), parentheses=parentheses) + elif exp[3] > 0: + return pret(('shl', exp[3], val, ), parentheses=parentheses) + else: + return pret(('shr', -exp[3], val, ), parentheses=parentheses) + + + + if all_concrete(size, offset, shl, val): + return pret(apply_mask(exp[4], exp[1], exp[2], exp[3])) + + if shl == 0: + exp = ('mask', size, offset, val) + + elif safe_ge_zero(shl) is not False: + + if all_concrete(size, offset, shl) and \ + size+shl == 256 and offset == 0 and shl > -8: + exp = ('mul', 2**exp[3], exp[4]) + else: + if type(exp[3]) == int and exp[3] < 7 and exp[3] >= -8: + exp = ('mul', 2**exp[3], ('mask', exp[1], exp[2], exp[4])) + + elif type(exp[3]) == int and exp[3] < 0: + exp = ('shr', -exp[3], ('mask', exp[1], exp[2], exp[4])) + else: + exp = ('shl', exp[3], ('mask', exp[1], exp[2], exp[4])) + + else: + exp = ('shr', mul_op(-1, exp[3]), ('mask', exp[1], exp[2], exp[4])) + + if exp ~ ('mask', :size, 0, :val): + if size == 256: + return pret(val) + + if type(size) == int: + if size == 255: + type_name = 'uint255' + else: + type_name = mask_to_type(size) + + if type_name is not None: + return col(type_name + '(', COLOR_GRAY) + \ + pret(val) + \ + col(")", COLOR_GRAY) + + if exp ~ ('bool', :val): + if opcode(val) in ('lt', 'gt', 'iszero', 'le', 'ge', 'bool'): + return pret(val, parentheses=parentheses) + else: + return "bool("+pret(val)+")" + + if exp ~ ('mask', :size, :offset, :val): + if type(size) == int and offset == 0 and size < 64: + return pret(('mod', val, 2**size), parentheses=parentheses) + else: + return 'Mask({}, {}, {})'.format(pret(size), pret(offset), pret(val)) + +# if opcode(exp) in ('byte', 'bytes8', 'uint16', 'bytes4', 'addr', 'int256'): +# return prettify('{}({})'.format(opcode(exp).lower(), prettify(exp[1], add_color=add_color)), add_color=add_color) + + opcode_to_arithm = { + 'sub': ' - ', # todo - parentheses? + 'div': ' / ', + 'mul': ' * ', + 'gt': ' > ', + 'lt': ' < ', + 'le': ' <= ', + 'ge': ' >= ', + 'or': ' or ', + 'eq': ' == ', + 'mod': ' % ', + 'shl': ' << ', + 'shr': ' >> ', + 'exp': '^', + 'and': ' and ', + 'sge': ' >=′ ', + 'sle': ' <=′ ', + 'sgt': ' >′ ', + 'slt': ' <′ ', + 'sadd': ' +′ ', + 'smul': ' *′ ', + 'sdiv': ' /′ ', + + 'xor': ' xor ', + } + + def pretty_adds(exp): + if opcode(exp) != 'add': + return prettify(exp, add_color = add_color) + + if type(exp[1]) == float: + real = exp[1] + if int(real) == real: + real = int(real) # 32.0 -> 32 + + symbolic = exp[2:] + res = ' + '.join([prettify(x, add_color=add_color) for x in symbolic]) + if real > 0: + res += ' + ' + prettify(real) + elif real < 0: + res += ' - ' + prettify(-real) + + + elif type(exp[1]) == int: + real = to_real_int(exp[1]) + symbolic = exp[2:] + res = ' + '.join([prettify(x, add_color=add_color) for x in symbolic]) + if real > 0: + res += ' + ' + prettify(real) + elif real < 0: + res += ' - ' + prettify(-real) + + else: + res = '' + for x in exp[1:]: + if res == '': + res = prettify(x, add_color=add_color) + elif opcode(x) == 'mul' and type(x[1]) == int and x[1] < 0: + res += ' - ' + prettify(minus_op(x), add_color=add_color) + else: + res += ' + ' + prettify(x, add_color=add_color) + + if parentheses: + return f'({res})' + else: + return res + + if opcode(exp) == 'not': + return COLOR_BOLD+'!'+ENDC+prettify(exp[1], add_color = add_color) + + if opcode(exp) == 'add': + return pretty_adds(exp) + + if opcode(exp) == 'mul' and len(exp)==3 and to_exp2(exp[1]) != None and to_exp2(exp[1]) > 32: + exp = ('shl', to_exp2(exp[1]), exp[2]) + + if exp ~ ('mul', -1, :val): + return "-" + pret(val, parentheses=parentheses) + + if exp ~ ('mul', 1, :val): + return pret(val, parentheses=parentheses) + + if exp ~ ('mul', 1, *rest): + return pret(('mul', ) + rest, parentheses=parentheses) + + if exp ~ ('div', :num, 1): + return pret(num, parentheses=parentheses) + + if exp ~ ('exp', :a, :n): + return pret(a, parentheses=True) + '^' + pret(n, parentheses=True) + + if opcode(exp) in opcode_to_arithm: + + if opcode(exp) in ['shl', 'shr']: + exp = exp[0], exp[2], exp[1] + + form = '{}' + if parentheses and not top_level: + form = '({})' + + op_form = opcode_to_arithm[opcode(exp)] + if add_color: + op_form = COLOR_BOLD + op_form + ENDC + + + def fold_ands(exp): + assert opcode(exp) == 'and' + + res = tuple() + for e in exp[1:]: + if opcode(e) == 'and': + e = fold_ands(e) + res += e[1:] + else: + res += (e, ) + + return ('and', ) + res + + if opcode(exp) == 'and': + exp = fold_ands(exp) + return form.format(op_form.join(pret(e, rem_bool=True) for e in exp[1:])) + else: + return form.format(op_form.join(pret(e) for e in exp[1:])) + + if exp ~ ('iszero', :val): + + if val ~ ('gt', :left, :right): + return pret(left) + ' <= ' + pret(right) + + if val ~ ('lt', :left, :right): + return pret(left) + ' >= ' + pret(right) + + if val ~ ('eq', :left, :right): + if type(left) in (str, int): + return pret(right) + ' != ' + pret(left) + else: + return pret(left) + ' != ' + pret(right) + + return 'not '+pret(val) + + return str(exp) + + +def pretty_gas(gas, value, add_color): + if gas ~ ('mul', 2300, ('iszero', _)): + # should check if _ == value + return '2300 * is_zero(value)' + else: + return prettify(gas, add_color=add_color, parentheses=False) + +def try_fname(exp, add_color=False): + if Loader.find_sig(hex(exp)[:10]): + return Loader.find_sig(hex(exp)[:10], add_color) + + elif len(hex(exp))>=63 and Loader.find_sig(padded_hex(exp,64)[:10], add_color):# in Loader.signatures: # if three last letters are "0"s, but no more, so there is + # a low chance for mistaking a random number for function sig + return Loader.find_sig(padded_hex(exp,64)[:10], add_color) + + elif len(hex(exp))>=8 and Loader.find_sig(padded_hex(exp,8)[:10], add_color):# in Loader.signatures: + return Loader.find_sig(padded_hex(exp,8)[:10], add_color) + + else: + return None + +def pretty_fname(exp, add_color=False, force=False): + if type(exp) == int: + if try_fname(exp) and 'unknown_' not in try_fname(exp): + return try_fname(exp, add_color) + else: + return hex(exp) + + elif opcode(exp) == 'mem' or force: + return prettify(exp, add_color=add_color) + + return exp + +def pretty_memory(exp, add_color=False): + if exp is None: + return tuple() + + if exp == 'mem': + return prettify(exp, add_color=add_color) + + if opcode(exp) != 'data': + return (prettify(exp, add_color=add_color), ) + + exp = exp[1:] + + if len(exp) == 0: + return 'empty()' + assert len(exp) > 0, exp + + res = [] + + idx = 0 + + def unmask(exp): + if opcode(exp) == 'mask_shl': + return exp[4] + + return exp + + # merge things that look like string into a string + + while idx < len(exp): + + if idx == 0 and type(exp[0]) == tuple and exp[0][:4] == ('mask_shl', 32, 224, 0) and type(exp[0][4]) == int: + # This happens often in Log and Revert, first + # memory result being an 8-byte identifier. + # Definitely deserves a more generic solution. + # + # example: 0xd883209C4DCd497f24633C627a4E451013424841, sendFoods function + v = exp[0][4] >> 224 + res.append( pretty_fname(v, add_color) ) + idx += 1 + continue + + el = exp[idx] + + # detect a potential string + + out_str = None + + if unmask(el) == 32 and len(exp)>idx+1: + + length = unmask(exp[idx+1]) + if type(length) == int: + byte_length = ((length-1) >> 5) +1 + out_str = '' + + if type(length) == int and len(exp) > idx + 1 + byte_length: + for i in range(byte_length): + if type(unmask(exp[idx+2+i])) == str: + out_str += unmask(exp[idx+2+i])[1:-1] + elif type(pretty_bignum(unmask(exp[idx+2+i]))) == str: + # could also make sure that the length of string is the same + # as expected + out_str += pretty_bignum(unmask(exp[idx+2+i]))[1:-1] + else: + out_str = None + break + + if out_str != None: + idx = idx + 1 + byte_length + else: + out_str = None + else: + out_str = None + + if out_str != None: + res.append("'"+out_str+"'") + else: + res.append(prettify(el, add_color=add_color, parentheses=False)) + + idx = idx+1 + + + return tuple(res) + diff --git a/pano/rewriter.py b/pano/rewriter.py new file mode 100644 index 00000000..8df6da91 --- /dev/null +++ b/pano/rewriter.py @@ -0,0 +1,517 @@ +# coding: tilde + +from copy import copy +import core.arithmetic as arithmetic +import logging +import collections + +from core.memloc import range_overlaps, splits_mem, memloc_overwrite, split_setmem, apply_mask_to_range, split_store, sizeof + +from utils.helpers import contains, rewrite_trace_multiline, opcode, cached, walk_trace, to_exp2, replace, find_f_set, find_f_list, rewrite_trace, rewrite_trace_full, replace_f, is_array + +from core.algebra import simplify, calc_max, add_ge_zero, minus_op, sub_op, flatten_adds, max_to_add, divisible_bytes, _max_op +from core.algebra import add_op, bits, mul_op, get_sign, safe_ge_zero, ge_zero, lt_op, safe_lt_op, safe_le_op, simplify_max, le_op, max_op, safe_max_op, safe_min_op, min_op, or_op, neg_mask_op, mask_op, apply_mask_to_storage, apply_mask, try_add, to_bytes + +from core.arithmetic import is_zero, to_real_int + +from pano.prettify import pformat_trace, pprint_trace + +from utils.profiler import checkpoint, checkpoint_start, print_checkpoints, func_caller + +from core.masks import get_bit + +from time import gmtime, strftime + +from core.masks import to_mask, to_neg_mask + +import utils.profiler + +from utils.profiler import * + + +def postprocess_exp(exp): + if exp ~ ('data', *terms): + # make arrays in data + concrete = [t for t in terms if type(t) == int and t % 32 == 0] + if len(concrete) == 1: + # potential array in data? + assert concrete[0] % 32 == 0 + loc = concrete[0] // 32 + if loc + 1 < len(terms) and loc > terms.index(concrete[0]): + arr = ('arr', ) + terms[loc:] + + # heuristics for cleaning up various misprocessed stuff + + if arr ~ ('arr', :l, (:op, _, l), ...) and is_array(op): + arr = arr[:3] + + elif arr ~ ('arr', :l, ('mask_shl', ('mask_shl', 253, 0, 3, l), _, _, ('data', (:op, :st, l), ...), ...)) and is_array(op): + arr = ('arr', l, (op, st, l)) + + t2 = tuple([arr if t == loc*32 else t for t in terms[:loc]]) + return ('data', ) + t2 + + # this would really require debugging as to why such thing happens, and a nicer cleanup. + # but it's last minute fixes again :) + + if exp ~ ('arr', :l, ('mask_shl', ('mask_shl', _, 0, 3, l), ('add', 256, _), ('add', -256, _), ('data', ('call.data', :s, l), ...), ...)): + return ('arr', l, ('call.data', s, l)) + + + return exp + + +def postprocess_trace(line): + ''' + let's find all the stuff like + + if (some_len % 32) == 0: + return Array(some_len, some_stuff) + else: + mem[...] = leftover + return Array(some_len, some_stuff, leftover) + + and replace it with just return Array(some_len, some_stuff) + + in theory this is incorrect, because perhaps program does something totally different + in the one branch, andd something entirely different in another. + but this cleans up tremendous amounts of output, and didn't find a counterexample yet. + ''' + +# if line ~ ('setmem', ('range', :s, ('mask_shl', 251, 5, 0, ('add', 31, ('cd', ('add', 4, :param))))), ('data', ('call.data', ('add', 36, param), ('cd', ('add', 4, param))), ('mem', ...))): +# lin = ('setmem', ('range', s, ('cd', ('add', 4, param))), ('call.data', ('add', 36, param), ('cd', ('add', 4, param)))) +# return [lin] + + if line ~ ('if', ('iszero', ('storage', 5, 0, :l)), :if_true, :if_false): + def find_arr_l(exp): + if exp ~ ('arr', ('storage', 256, 0, l), ...): + return [exp] + + true_arr = find_f_list(if_true, find_arr_l) + false_arr = find_f_list(if_true, find_arr_l) + + if len(true_arr) > 0 and len(true_arr) == len(false_arr): + return if_true + + + if line ~ ('if', ('iszero', ('mask_shl', 5, 0, 0, :l)), :if_true, :if_false): + def find_arr_l(exp): + if exp ~ ('arr', l, ...): + return [exp] + true_arr = find_f_list(if_true, find_arr_l) + false_arr = find_f_list(if_true, find_arr_l) + + if len(true_arr) > 0 and len(true_arr) == len(false_arr): + return if_true + + + ''' + When writing strings to storage, there are usually three cases - when string is 0, + when string is < 31 (special format that takes just one storage slot), and when string >= 32. + + e.g. 0xf97187f566eC6374cB08470CCe593fF0Dd36d8A9, baseURI + 0xFcD0d8E3ae34922A2921f7E7065172e5317f8ad8, name + + The below hides the cases for < 31, and for 0, and shows only for >31. Technically incorrect, + and I'm not super comfortable with this, but some of the code would be very unreadable without it. + + + ''' + + if line ~ ('if', ('lt', 31, :some_len), :if_true, :if_false): + if len(if_true) == 2: + first, second = if_true[0], if_true[1] + if first ~ ('store', ...) and contains(first, some_len) \ + and second ~ ('if', ('iszero', some_len), :deep_true, :deep_false): + return [first] + deep_false + + if line ~ ('if', ('iszero', ('mask_shl', 255, 1, 0, ('and', ('storage', 256, 0, :loc), ('add', -1, ('mask_shl', 248, 0, 8, ('iszero', ('storage', 1, 0, loc))))))), :if_true, :if_false) \ + or line ~ ('if', ('iszero', ('mask_shl', 255, 1, 0, ('and', ('add', -1, ('mask_shl', 248, 0, 8, ('iszero', ('storage', 1, 0, :loc)))), ('storage', 256, 0, loc)))), :if_true, :if_false): + + if len(if_false) == 1: + first = if_false[0] + + if first ~ ('if', ('lt', 31, ('storage', 256, 0, ('length', loc))), :deep_true, :deep_false) \ + or first ~ ('if', ('lt', 31, ('storage', 256, 0, ('length', ('loc', loc)))), :deep_true, :deep_false): + return deep_true + + return [line] + +def rewrite_string_stores(lines): + # ugly af, and not super-precise. it should be split into 2 parts, + # converting array->storage writes in loop_to_setmem_from_storage + # and then relying on those storage writes here for cleanup + + assert len(lines) == 3 + l1, l2, l3 = lines[0], lines[1], lines[2] + if l1 ~ ('store', 256, 0, :idx, ('add', 1, ('mask_shl', 255, 0, 1, :src))) \ + and l2 ~ ('while', ('gt', _, _), :path2, _, :setvars) \ + and l3 ~ ('while', ('gt', ...), :path3, ...) \ + and len(path2) == 2 and (x:= path2[0]) \ + and x ~ ('store', 256, 0, ('add', ('var', _), _), ('mem', ('range', ('var', :v), 32))): + return [ + ('store', 256, 0, ('array', '', ('sha3', idx)), ('arr', src, ('mem', ('range', setvars[1][2], src)))) + ] + + return None + +def rewrite_memcpy(lines): # 2 + assert len(lines) == 2 + l1 = lines[0] + l2 = lines[1] + + if l1 ~ ('setmem', ('range', :s, ('mask_shl', 251, 5, 0, ('add', 31, ('cd', ('add', 4, :param))))), ('data', ('call.data', ('add', 36, param), ('cd', ('add', 4, param))), ('mem', ...))): + return ('setmem', ('range', s, ('cd', ('add', 4, param))), ('call.data', ('add', 36, param), ('cd', ('add', 4, param)))) +#(setmem (range (add 128 (mask_shl 251 5 0 (add 31 (cd (add 4 (cd 68)))))) (mask_shl 251 5 0 (add 31 (cd (add 4 (cd 68)))))) (data (call.data (add 36 (cd 68)) (cd (add 4 (cd 68)))) (mem (range (add 128 (cd (add 4 (cd 68)))) (add (mask_shl 251 5 0 (add 31 (cd (add 4 (cd 68))))) (mul -1 (cd (add 4 (cd 68))))))))) +# (if (iszero (mask_shl 5 0 0 (cd (add 4 (cd 68))))) (t + + +''' + + test case for above: + + (store 256 0 0 (add 1 (mask_shl 255 0 1 (cd (add 4 (cd 36)))))) + (while (gt (add 160 (mask_shl 251 5 0 (add 31 (cd (add 4 (cd 4))))) (cd (add 4 (cd 36)))) (var 0)) (t + (store 256 0 (add (var 1) (sha3 0)) (mem (range (var 0) 32))) + (continue id8785 ((setvar 1 (add 1 (var 1))) (setvar 0 (add 32 (var 0))))) + ) id8785 [('setvar', 1, 0), ('setvar', 0, ('add', 160, ('mask_shl', 251, 5, 0, ('add', 31, ('cd', ('add', 4, ('cd', 4)))))))]) + (while (gt (mask_shl 251 5 -5 (add 31 (storage 256 0 (length (loc 0))))) (var 0)) (t + (store 256 0 (add (var 0) (sha3 0)) 0) + (continue id3054 ((setvar 0 (add 1 (var 0))))) + ) id3054 [('setvar', 0, ('mask_shl', 251, 0, -5, ('add', 31, ('cd', ('add', 4, ('cd', 36))))))]) + (store 256 0 1 (add 1 (mask_shl 255 0 1 (cd (add 4 (cd 4)))))) + (while (gt (add 128 (cd (add 4 (cd 4)))) (var 0)) (t + (store 256 0 (add (var 1) (sha3 1)) (mem (range (var 0) 32))) + (continue id2702 ((setvar 1 (add 1 (var 1))) (setvar 0 (add 32 (var 0))))) + ) id2702 [('setvar', 1, 0), ('setvar', 0, 128)]) + (while (gt (mask_shl 251 5 -5 (add 31 (storage 256 0 (length (loc 1))))) (var 0)) (t + (store 256 0 (add (var 0) (sha3 1)) 0) + (continue id6799 ((setvar 0 (add 1 (var 0))))) + ) id6799 [('setvar', 0, ('mask_shl', 251, 0, -5, ('add', 31, ('cd', ('add', 4, ('cd', 4))))))]) + +''' + + + +@cached +def simplify_exp(exp): + + if type(exp) == list: + return exp + + if exp ~ ('mask_shl', 246, 5, 0, :exp): + exp = ('mask_shl', 251, 5, 0, exp) # mathematically incorrect, but this appears as an artifact of other + # ops often. + + if exp ~ ('and', *terms): + real = 2**256-1 + symbols = [] + for t in terms: + if type(t) == int and t >=0: + real = real & t + elif t ~ ('and', *tms): + symbols += tms + else: + symbols.append(t) + + if real != 2**256-1: + res = (real, ) + else: + res = tuple() + + res += tuple(symbols) + exp = ('and', ) + res + + if exp ~ ('data', *terms) and \ + all([t == 0 for t in terms]): + return 0 + + if exp ~ ('mask_shl', int:size, int:off, -off, ('cd', int:num)) and \ + size in (8, 16, 32, 64, 128) and off > 0: + return ('mask_shl', size, 0, 0, ('cd', num)) # calldata params are left-padded usually, it seems + + if exp ~ ('bool', ('bool', :e)): + exp = ('bool', e) + + if exp ~ ('eq', :sth, 0) or \ + exp ~ ('eq', 0, sth): + exp = ('iszero', sth) + + if exp ~ ('mask_shl', int:size, 5, 0, ('add', int:num, *terms)) and \ + size > 240 and num % 32 == 31 and num > 32: + exp = ('add', num//32, ('mask_shl', 256, 5, 0, ('add', 31, )+terms)) + + if exp ~ ('iszero', ('mask_shl', :size, :off, :shl, :val)): + exp = ('iszero', ('mask_shl', size, off, 0, val)) + + if exp ~ ('max', :single): + exp = single + + if exp ~ ('mem', ('range', _, 0)): + return None # sic. this happens usually in params to logs etc, we probably want None here + + if exp ~ ('mod', :exp2, int:num) and (size:=to_exp2(num)): + return mask_op(exp2, size=size) + + # same thing is added in both expressions ? + if exp ~ (:op, ('add', *e1), ('add', *e2)) and op in ('lt', 'le', 'gt', 'ge'): + t1 = tuple(t for t in e1 if t not in e2) + t2 = tuple(t for t in e2 if t not in e1) + exp = (op, add_op(*t1), add_op(*t2)) + + if exp ~ ('add', :e): +# print('single add') + return simplify_exp(e) + + if exp ~ ('mul', 1, :e): + return simplify_exp(e) + + if exp ~ ('div', :e, 1): + return simplify_exp(e) + + if exp ~ ('mask_shl', 256, 0, 0, :val): + return simplify_exp(val) + + if exp ~ ('mask_shl', int:size, int:offset, int:shl, :e): + exp = mask_op(simplify_exp(e), size, offset, shl) + + if exp ~ ('mask_shl', :size, 0, 0, ('div', :expr, ('exp', 256, :shr))): + exp = mask_op(simplify_exp(expr), size, 0, shr=bits(shr)) + + if exp ~ ('mask_shl', _, _, :shl, ('storage', :size, _, _)) and \ + safe_le_op(size, minus_op(shl)): + return 0 + + if exp ~ ('or', :sth, 0): + return sth + + if exp ~ ('add', *terms): + res = 0 + for el in terms: + el = simplify_exp(el) + if el ~ ('add', ...:pms): + for e in pms: + res = add_op(res, e) + else: + res = add_op(res, el) + exp = res + + if exp ~ ('mask_shl', ...): + exp = cleanup_mask_data(exp) + + if exp ~ ('mask_shl', :size, 0, 0, ('mem', ('range', :mem_loc, :mem_size))): + if divisible_bytes(size) and safe_le_op(to_bytes(size)[0], mem_size): + return ('mem', apply_mask_to_range(('range', mem_loc, mem_size), size, 0)) + + if exp ~ ('mask_shl', :size, :off, :shl, ('mem', ('range', :mem_loc, :mem_size))) and shl == minus_op(off): + if divisible_bytes(size) and safe_le_op(to_bytes(size)[0], mem_size) and divisible_bytes(off): + return ('mem', apply_mask_to_range(('range', mem_loc, mem_size), size, off)) + + + if exp ~ ('data', *params): + res = [] + + # simplify inner expressions, and remove nested 'data's + for e in params: + e = simplify_exp(e) # removes further nested datas, and does other simplifications + if opcode(e) == 'data': + res.extend(e[1:]) + else: + res.append(e) + + # sometimes we may have on expression split into two masks next to each other + # e.g. (mask_shl 192 64 -64 (cd 36)))) (mask_shl 64 0 0 (cd 36)) + # (in solidstamp withdrawRequest) + # merge those into one. + + res2 = res + res = None + while res2 != res: # every swipe merges up to two elements next to each other. repeat until there are no new merges + # there's a more efficient way of doing this for sure. + res, res2 = res2, [] + idx = 0 + while idx < len(res): + el = res[idx] + if idx == len(res) -1: + res2.append(el) + break + + next_el = res[idx+1] + idx += 1 + + if el ~ ('mask_shl', :size, :offset, :shl, :val) \ + and next_el == ('mask_shl', offset, 0, 0, val) \ + and add_op(offset, shl) == 0: + res2.append(('mask_shl', add_op(size, offset), 0, 0, val)) + idx += 1 + + else: + res2.append(el) + + res = res2 + + # could do the same for mem slices, but no case of that happening yet + + if len(res) == 1: + return res[0] + else: + return ('data', ) + tuple(res) + + if exp ~ ('mul', -1, ('mask_shl', :size, :offset, :shl, ('mul', -1, :val))): + return ('mask_shl', simplify_exp(size), simplify_exp(offset), simplify_exp(shl), simplify_exp(val)) + + if type(exp) == int and to_real_int(exp)>-(8**22): # if it's larger than 30 bytes, it's probably + # an address, not a negative number + return to_real_int(exp) + + if exp ~ ('and', :num, :num2): + num = arithmetic.eval(num) + num2 = arithmetic.eval(num2) + if type(num) == int or type(num2) == int: + return simplify_mask(exp) + + if type(exp) == list: + r = simplify_exp(tuple(exp)) + return list(r) + + if type(exp) != tuple: + return exp + + if exp ~ ('mask_shl', int:size, int:offset, int:shl, int:val): + return apply_mask(val, size, offset, shl) + + if exp ~ ('mask_shl', :size, 5, :shl, ('add', 31, ('mask_shl', 251, 0, 5, :val))): + return simplify_exp(('mask_shl', size, 5, shl, val)) + + if exp ~ ('mul', *terms): + res = 1 + for e in terms: + res = mul_op(res, simplify_exp(e)) + + res ~ ('mul', 1, res) + + return res + + if exp ~ ('max', *terms): + els = [simplify_exp(e) for e in terms] + res = 0 + for e in els: + res = _max_op(res, e) + return res + + res = tuple() + for e in exp: + res += (simplify_exp(e), ) + + return res + + +def simplify_mask(exp): + op = opcode(exp) + + if op in arithmetic.opcodes: + exp = arithmetic.eval(exp) + + if exp ~ ('and', :left, :right): + + if mask := to_mask(left): + exp = mask_op(right, *mask) + + elif mask := to_mask(right): + exp = mask_op(left, *mask) + + elif bounds := to_neg_mask(left): + exp = neg_mask_op(right, *bounds) + + elif bounds := to_neg_mask(right): + exp = neg_mask_op(left, *bounds) + + elif exp ~ ('div' , :left, int:right) and (shift := to_exp2(right)): + exp = mask_op(left, size = 256-shift, offset = shift, shr = shift) + + elif exp ~ ('mul', int:left, :right) and (shift := to_exp2(left)): + exp = mask_op(right, size=256-shift, shl = shift) + + elif exp ~ ('mul', :left, int:right) and (shift := to_exp2(right)): + exp = mask_op(left, size=256-shift, shl = shift) + + return exp + + +def cleanup_mask_data(exp): + # if there is a mask over some data, + # it removes pieces of data that for sure won't + # fit into the mask (doesn't truncate if something) + # partially fits + # e.g. mask(200, 8, 8 data(123, mask(201, 0, 0, sth), mask(8,0, 0, sth_else))) + # -> mask(200, 0, 0, mask(201, 0, 0, sth)) + + def _cleanup_right(exp): + # removes elements that are cut off by offset + assert exp ~ ('mask_shl', :size, :offset, :shl, :val) + + if opcode(val) != 'data': + return exp + + last = val[-1] + if sizeof(last) is not None and safe_le_op(sizeof(last), offset): + offset = sub_op(offset, sizeof(last)) + shl = add_op(shl, sizeof(last)) + if len(val) == 3: + val = val[1] + else: + val = val[:-1] + + return mask_op(val, size, offset, shl) + + return exp + + def _cleanup_left(exp): + # removes elements that are cut off by size+offset + assert exp ~ ('mask_shl', :size, :offset, :shl, :val) + + if opcode(val) != 'data': + return exp + + total_size = add_op(size, offset) # simplify_exp + + sum_sizes = 0 + val = list(val[1:]) + res = [] + while len(val) > 0: + last = val.pop() + if sizeof(last) is None: + return exp + sum_sizes = simplify_exp(add_op(sum_sizes, sizeof(last))) + res.insert(0, last) + if safe_le_op(total_size, sum_sizes): + return exp[:4] + (('data', )+tuple(res), ) + + + return exp + + assert opcode(exp) == 'mask_shl' + + prev_exp = None + while prev_exp != exp: + prev_exp = exp + exp = _cleanup_right(exp) + + prev_exp = None + while prev_exp != exp: + prev_exp = exp + exp = _cleanup_left(exp) + + if exp ~ ('mask_shl', :size, 0, 0, ('data', *terms)): + # if size of data is size of mask, we can remove the mask altogether + sum_sizes = 0 + for e in terms: + s = sizeof(e) + if s is None: + return exp + sum_sizes = add_op(sum_sizes, s) + + if sub_op(sum_sizes, size) == 0: + return ('data', ) + terms + + return exp diff --git a/pano/sparser.py b/pano/sparser.py new file mode 100644 index 00000000..ede9e737 --- /dev/null +++ b/pano/sparser.py @@ -0,0 +1,730 @@ +# coding: tilde + +from pano.prettify import pprint_trace, pretty_stor + +from utils.helpers import opcode, find_f, replace, replace_f, tuplify, replace_lines, get_op, hashable, to_exp2 + +from core.algebra import minus_op, safe_le_op, divisible_bytes, to_bytes + +from core.masks import mask_to_type + +from utils.helpers import COLOR_GREEN, COLOR_GRAY, ENDC + +import logging + +logger = logging.getLogger(__name__) + + +''' + helpers + +''' + +def get_loc(exp): + def f(exp): + if type(exp) != tuple: + return None + + elif opcode(exp) in ('storage', 'stor'): +# elif exp ~ ('storage', ...): # if we have a storage reference within the storage + # don't follow this one when looking for loc + return None + + elif exp ~ ('loc', :num): + return num + + elif exp ~ ('name', _, :num): + return num + + else: + for e in exp: + if (loc:=f(e)) is not None: + return loc + + return None + + if exp ~ ('type', _, ('field', _, :m_idx)): + exp = m_idx + + if exp ~ ('storage', _, _, :e): + exp = e + + if exp ~ ('stor', _, _, :e): + exp = e + + if exp ~ ('stor', :e): + exp = e + + + return f(exp) + +def get_name_full(exp): + def f(exp): + if type(exp) != tuple: + return None + + elif opcode(exp) in ('stor', 'storage'):#exp ~ ('storage', ...): # if we have a storage reference within the storage + # don't follow this one when looking for loc + return None + + elif exp ~ ('name', :name, _): + return exp + + else: + for e in exp: + if (loc:=f(e)) is not None: + return loc + + return None + + if exp ~ ('type', _, ('field', _, :m_idx)): + exp = m_idx + + if exp ~ ('storage', _, _, :e): + exp = e + + if exp ~ ('stor', _, _, :e): + exp = e + + if exp ~ ('stor', :e): + exp = e + + return f(exp) + +def get_name(exp): + r = get_name_full(exp) + if r is None: + return None + else: + assert r ~ ('name', :name, _) + return name + + +def find_stores(exp): + + if exp ~ ('store', :size, :off, :idx, :val): + res = set([('storage', size, off, idx)]) + + elif exp ~ ('storage', ...): + res = set([exp]) + + else: + res = set() + + if type(exp) == tuple or type(exp) == list: + for e in exp: + res = res.union(find_stores(e)) + + return res + + +''' + proper + +''' + +def rewrite_functions(functions): + ''' + rewrites functions, putting storage names there, + then detects storage types and returns a list of those in a form of + ('def', name, loc, type) + that can be displayed by pretty_type from prettify + + ''' + + storages = list(find_stores([f.trace for f in functions])) + + # (storage 256 0 (sha3 (cd 4) 7) + # -> (storage 256 0 (array (cd 4) (loc 7))) + + # (storage 256 0 6) + # (storage 256 0 (loc 6)) + # (storage 256 0 (length (loc 6)) + # (storage 256 0 (array idx (loc 6)) + # (storage 256 0 (map idx (loc 6)) + + + storages_assoc = _sparser(storages) + + names = find_storage_names(functions) + replace_names_in_assoc(names, storages_assoc) + replace_names_in_assoc_bool(names, storages_assoc) + + + for func in functions: + func.trace = repl_stor(func.trace, storages_assoc) + + stordefs = {} + + for src, dest in storages_assoc.items(): + d = dest + loc = get_loc(d) + if loc is None: loc = 99 + + assert loc is not None, d + if loc not in stordefs: + stordefs[loc] = set() + + if type(loc) != int: + continue + + stordefs[loc].add(d) + + def get_type(stordefs): + sizes = set() + offsets = set() + for s in stordefs: + if s ~ ('stor', :size, :off, (:op, :idx, ...)) and op in ('map', 'array'): + sizes.add(size) + if safe_le_op(0, off) is True: + offsets.add(off) + + if 256 in sizes: + sizes.remove(256) + + if 0 in offsets: + offsets.remove(0) + + if len(offsets) > 0: + # for structs, find their size by looking at how the index is multiplied + + for s in stordefs: + if s ~ ('stor', _, _, (:op, :idx, ...)) and op in ('map', 'array'): + if idx ~ ('stor', _, int:siz, ('length', ...)) and siz < 0: + return ('struct', -siz + 1) + + return 'struct' + + if len(sizes) == 0: + return 256 + else: + return min(sizes) + + defs = [] + + try: + sorted_keys = sorted(stordefs.keys()) + except: + logger.warn('unusual storage location') + sorted_keys = stordefs.keys() + + for loc in sorted_keys: + for l in stordefs[loc]: + if (l ~ ('stor', int, int, ('loc', _))) or \ + (l ~ ('stor', int, int, ('name', ...))): + pass + + else: + name = get_name(l) + if name is None: + name = 'stor' + str(loc) + + assert l ~ ('stor', int, int, :idx) + + if opcode(idx) == 'map': + defs.append(('def', name, loc, ('mapping', get_type(stordefs[loc])))) + elif opcode(idx) in ('array', 'length'): + defs.append(('def', name, loc, ('array', get_type(stordefs[loc])))) + else: + pass # it's length probably + + break + else: + # all stor references are not arrays/maps, let's just print them out + for l in stordefs[loc]: + name = get_name(l) + + if name is None: + if type(loc) == int and loc >= 1000: + name = 'stor' + hex(loc)[2:6].upper() + else: + name = 'stor' + str(loc) + + defs.append(('def', name, loc, ('mask', l[1], l[2]))) + + + return defs + + +def to_stordef(exp): + return exp + if opcode(exp) in ('mask_shl', 'cd', 'storage', 'var'): + return 'idx' + + if type(exp) == tuple: + return tuple(to_stordef(e) for e in exp) + else: + return exp + + +''' + +''' + +def repl_stor(exp, assoc): + if type(exp) == list: + return [repl_stor(e, assoc) for e in exp] + + if exp ~ ('store', :size, :off, :idx, :val): + + dest = assoc[('storage', size, off, idx)] + return ('store', ) + dest[1:] + (repl_stor(val, assoc), ) + + elif hashable(exp) and exp in assoc: + return assoc[exp] + + elif type(exp) == tuple: + return tuple([repl_stor(e, assoc) for e in exp]) + + else: + return exp + + +used_locs = set() + +def replace_names_in_assoc_bool(names, storages_assoc): + # we are replacing bools only after the other storage + # because in some situations there are functions like + # areTokensAvailable == bool(tokens.length) + # and, if possible we don't want to use this getter's name for such purpose + # + # there are better/more precise ways to handle this though + for getter, name in names.items(): + if not (getter ~ ('bool', ('storage', :size, :off, int:loc))): + continue + + if ('array', loc) in used_locs: + pass + elif ('stor', size, off, loc) in used_locs: + pass + else: + for src, pattern in storages_assoc.items(): +# print(pattern) + if pattern == ('stor', size, off, ('loc', loc)): + storages_assoc[src] = ('stor', size, off, ('name', name, loc)) + + +def replace_names_in_assoc(names, storages_assoc): + for pattern, name in names.items(): + + if pattern ~ ('bool', ...): + continue + + if pattern ~ ('struct', ...): + stor_id = pattern + else: + stor_id = storages_assoc[pattern] + + if stor_id ~ ('stor', :size, :off, ('loc', :num)): + # if we found a simple getter for a storage number, + # we need to check first if a given location is only accessed + # this way. otherwise it may be a function like getLength, that + # returns the array length, and we don't want to use it as a storage name + if all([pattern ~ ('stor', _, _, ('loc', _)) \ + for pattern in storages_assoc if get_loc(pattern) == num]): + + used_locs.add(stor_id) + + for src, pattern in storages_assoc.items(): + if pattern == stor_id: + storages_assoc[src] = ('stor', size, off, ('name', name, num)) + + elif stor_id ~ ('stor', _, _, ('map', _, :loc)) or \ + stor_id ~ ('stor', _, _, ('array', _, :loc)) or \ + stor_id ~ ('struct', :loc): + + # for arrays, we don't want 'address' at the end of the name. looks better + new_name = name.split('Address')[0] + if len(new_name) > 0: + name = new_name + + loc_id = get_loc(loc) + used_locs.add(('array', loc_id)) + + for src, pattern in storages_assoc.items(): + if get_loc(pattern) == loc_id: + pattern = replace(pattern, ('loc', loc_id), ('name', name, loc_id)) + storages_assoc[src] = pattern + + else: + logger.warning('storage pattern not found') + + +''' + find storage names based on function name +''' + +def find_storage_names(functions): + + res = {} + + for func in functions: + trace = func.trace + + if func.getter: + getter = func.getter + + assert opcode(getter) in ('storage', 'struct', 'bool') + + # func name into potential storage name + + new_name = func.name + + if new_name[:3] == 'get' and len(new_name.split('(')[0])>3: + new_name = new_name[3:] + + if new_name != new_name.upper(): + # otherwise we get stuff like bILLIONS in 0xF0160428a8552AC9bB7E050D90eEADE4DDD52843 + new_name = new_name[0].lower() + new_name[1:] + + new_name = new_name.split('(')[0] + + if getter ~ ('storage', 160, ...): + if ('address' not in new_name.lower()) and \ + ('addr' not in new_name.lower()) and \ + ('account' not in new_name.lower()) and \ + ('owner' not in new_name.lower()): + new_name += 'Address' + + res[getter] = new_name + + return res + +''' + sparser proper +''' + + + +def mask_to_mul(exp): + if exp ~ ('mask_shl', int:size, int:offset, int:shl, :val): + if shl > 0 and offset == 0 and size == 256 - shl: + if shl <= 8: + return ('mul', 2**shl, val) + + if shl < 0 and offset == -shl and size == 256 - offset: + if shl >= -8: + return ('div', 2**shl, val) + + return exp + +def stor_replace_f(storages, f): + def internal_f(exp, f): + if exp ~ ('storage', ...): + return exp + + if type(exp) == tuple: + exp = tuple(internal_f(e, f) for e in exp) + + return f(exp) + + + res = [] + for stor in storages: + assert stor ~ ('stor', :size, :off, :idx) + + res.append(('stor', size, off, internal_f(idx, f))) + + return res + +def _sparser(orig_storages): + + storages = [] + for idx, s in enumerate(orig_storages): + storages.append(('stor', )+s[1:]) + + def simplify_sha3(e): + e = rainbow_sha3(e) + + if e ~ ('sha3', ('data', *terms)): + e = ('sha3', ) + terms + if e ~ ('sha3', int:loc): + return ('loc', e[1]) + elif e ~ ('sha3', ('sha3', *terms), int:loc): + return ('map', ('data', *terms), ('loc', loc)) + elif e ~ ('sha3', :idx, int:loc): + return ('map', idx, ('loc', loc)) + else: + return e + + storages = stor_replace_f(storages, simplify_sha3) + + ''' + is add a struct or a loc? + ''' + + res = [] + for s in storages: + if s ~ ('stor', :size, ('mask_shl', :o_size, :o_off, :o_shl, :arr_idx), :idx) and size == 2**o_shl: + new_osize = minus_op(o_size) + if idx ~ ('add', int:num, _): + idx = num + s = ('stor', size, 0, ('array', ('mask_shl', o_size+o_shl, o_off, 0, arr_idx), ('loc', idx))) + + res.append(s) + + storages = res + + storages = stor_replace_f(storages, mask_to_mul) + + res = [] + for s in storages: + assert s ~ ('stor', :size, :offset, :idx) + + if idx ~ ('add', int:num, *terms) and get_loc(terms) is not None: + offset += 256 * num + idx = ('add', ) + terms + + res.append(('stor', size, offset, idx)) + + storages = res + + ''' + + array is when you add to a loc + + ''' + + def add_to_arr(exp): + if exp ~ ('add', :left, :right): + if opcode(left) == 'loc': + right, left = left, right + + if opcode(right) == 'loc': + return ('array', left, right) + + return exp + + storages = replace_f(storages, add_to_arr) + + ''' + + (s 256 0 (add 3 (mul 0.125 idx))) + + ''' + + res = [] + for s in storages: + assert s ~ ('stor', :size, :offset, :idx) + + idx ~ ('add', :idx) + + if idx ~ ('add', ...) and get_loc(idx) is None: + if idx ~ ('add', int:loc, :pos): + s = ('stor', size, offset, ('array', pos, ('loc', loc))) + else: + logger.warning(f'Weird storage index, {idx}') + + res.append(s) + + storages = res + + ''' + + convert regular storages into lengths or locs + + that is - find all the idxs that (stor _ _ idx) exists + but are also referenced through (stor _ _ (sth... (loc idx))) + + ''' + + assert len(storages) == len(orig_storages) + + + res = [] + for s in storages: + assert s ~ ('stor', :size, :offset, :idx) + + if type(idx) == int: + + if str(('loc', idx)) in str(storages): + assert type(idx) == int, idx + idx = ('length', ('loc', idx)) + + else: + idx = ('loc', idx) + + res.append(('stor', size, offset, idx)) + storages = res + + ''' + cleanup of nested maps + + ''' + res = [] + for s in storages: + + if s ~ ('stor', :size, :off, ('range', ('array', :beg, :loc), :end)): + s = ('stor', size, off, ('array', ('range', beg, end), loc)) + + res.append(s) + + storages = res + + + def double_map(exp): + exp ~ ('add', :exp) # remove 'add' with just one term. should be somewhere else + if exp ~ ('sha3', ('map', *terms)): + return ('map', *terms) # this is sth weird, see 0xf97187f566eC6374cB08470CCe593fF0Dd36d8A9, transferFrom + if exp ~ ('sha3', :idx, ('map', *terms)): + return ('map', idx, ('map', *terms)) + + if exp ~ ('add', :idx, ('map', *terms)): + return ('array', idx, ('map', *terms)) + if exp ~ ('sha3', :idx, :loc): + return ('map', idx, loc) + + + # this all should be recursive + if exp ~ ('add', ('sha3', ('array', *terms)), :num): + return ('array', num, ('array', )+terms) + if exp ~ ('add', :num, ('sha3', ('array', *terms))): + return ('array', num, ('array', )+terms) + + if exp ~ ('sha3', ('array', :idx, :loc)): + return ('array', idx, loc) + + + if exp ~ ('add', ('map', *terms), :num): + return ('array', num, ('map', )+terms) + if exp ~ ('add', :num, ('map', *terms)): + return ('array', num, ('map', )+terms) + + if exp ~ ('add', ('var', :x), ('array', :idx, :loc)): + return ('array', ('add', idx, ('var', x)), loc) + + if exp ~ ('add', ('array', :idx, :loc), ('var', :x)): + return ('array', ('add', idx, ('var', x)), loc) + + + return exp + + storages = replace_f(storages, double_map) + + assert len(storages) == len(orig_storages) + + ''' + + let's make a dict with output... + + ''' + + res = {} + for idx, s in enumerate(orig_storages): + dst = storages[idx] + res[s] = dst + + ''' + + and replace storage definitions in it recursively + + ''' + + def repl_res(exp): + if type(exp) != tuple: + return exp + + if exp in res: + exp = res[exp] + + return tuple([repl_res(e) for e in exp]) + + new_res = {} + for src, dest in res.items(): + new_res[src] = repl_res(dest) + assert 'storage' not in str(new_res[src]) + + res = new_res + + return res + + +''' + data +''' + +# to generate additional +# Web3.sha3(b'\x00'*31+b'\x01') == ('loc', 1) +''' +>>> for i in range(20): +... h = Web3.sha3(b'\x00'*31 + bytes([i])).hex() +... print(f"'{h}': ('loc':{i}),") +... ''' + + +def rainbow_sha3(exp): + if type(exp) == int and len(hex(exp)) > 40: + for src, dest in sha3_loc_table.items(): + if hex(exp) in src: + # sometimes the last bytes of exp are cut off (e.g. in ED), that's why + # we're not doing exact matches. should check why they are cut off though + # perhaps there's some problem with mask processing in whiles + return dest + + return exp + +sha3_loc_table = { +#0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3 == ('sha3', 'org.zeppelinos.proxy.implementation') + # source: https://github.com/zeppelinos/labs/blob/master/initializer_with_sol_editing/contracts/UpgradeabilityProxy.sol + # ^ not replacing, because rest of the system doesn't handle arbitrary string locations + '0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563': ('loc', 0), + '0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6': ('loc', 1), + '0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace': ('loc', 2), + '0xc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b': ('loc', 3), + '0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b': ('loc', 4), + '0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0': ('loc', 5), + '0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f': ('loc', 6), + '0xa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688': ('loc', 7), + '0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3': ('loc', 8), + '0x6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af': ('loc', 9), + '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a8': ('loc', 10), + '0x0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9': ('loc', 11), + '0xdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7': ('loc', 12), + '0xd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb5': ('loc', 13), + '0xbb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c3fd': ('loc', 14), + '0x8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac802': ('loc', 15), + '0x1b6847dc741a1b0cd08d278845f9d819d87b734759afb55fe2de5cb82a9ae672': ('loc', 16), + '0x31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c68': ('loc', 17), + '0xbb8a6a4669ba250d26cd7a459eca9d215f8307e33aebe50379bc5a3617ec3444': ('loc', 18), + '0x66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a090': ('loc', 19), + '0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5' : ('map', 0, ('loc', 0)), + '0xa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49' : ('map', 0, ('loc', 1)), + '0xac33ff75c19e70fe83507db0d683fd3465c996598dc972688b7ace676c89077b' : ('map', 0, ('loc', 2)), + '0x3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff' : ('map', 0, ('loc', 3)), + '0x17ef568e3e12ab5b9c7254a8d58478811de00f9e6eb34345acd53bf8fd09d3ec' : ('map', 0, ('loc', 4)), + '0x05b8ccbb9d4d8fb16ea74ce3c29a41f1b461fbdaff4714a0d9a8eb05499746bc' : ('map', 0, ('loc', 5)), + '0x54cdd369e4e8a8515e52ca72ec816c2101831ad1f18bf44102ed171459c9b4f8' : ('map', 0, ('loc', 6)), + '0x6d5257204ebe7d88fd91ae87941cb2dd9d8062b64ae5a2bd2d28ec40b9fbf6df' : ('map', 0, ('loc', 7)), + '0x5eff886ea0ce6ca488a3d6e336d6c0f75f46d19b42c06ce5ee98e42c96d256c7' : ('map', 0, ('loc', 8)), + '0xec8156718a8372b1db44bb411437d0870f3e3790d4a08526d024ce1b0b668f6b' : ('map', 0, ('loc', 9)), + '0x13da86008ba1c6922daee3e07db95305ef49ebced9f5467a0b8613fcc6b343e3' : ('map', 0, ('loc', 10)), + '0xdf7de25b7f1fd6d0b5205f0e18f1f35bd7b8d84cce336588d184533ce43a6f76' : ('map', 0, ('loc', 11)), + '0x13649b2456f1b42fef0f0040b3aaeabcd21a76a0f3f5defd4f583839455116e8' : ('map', 0, ('loc', 12)), + '0x81955a0a11e65eac625c29e8882660bae4e165a75d72780094acae8ece9a29ee' : ('map', 0, ('loc', 13)), + '0xe710864318d4a32f37d6ce54cb3fadbef648dd12d8dbdf53973564d56b7f881c' : ('map', 0, ('loc', 14)), + '0xf4803e074bd026baaf6ed2e288c9515f68c72fb7216eebdd7cae1718a53ec375' : ('map', 0, ('loc', 15)), + '0x6e0956cda88cad152e89927e53611735b61a5c762d1428573c6931b0a5efcb01' : ('map', 0, ('loc', 16)), + '0x4ad3b33220dddc71b994a52d72c06b10862965f7d926534c05c00fb7e819e7b7' : ('map', 0, ('loc', 17)), + '0x7e7fa33969761a458e04f477e039a608702b4f924981d6653935a8319a08ad7b' : ('map', 0, ('loc', 18)), + '0x8fa6efc3be94b5b348b21fea823fe8d100408cee9b7f90524494500445d8ff6c' : ('map', 0, ('loc', 19)), + '0xada5013122d395ba3c54772283fb069b10426056ef8ca54750cb9bb552a59e7d' : ('map', 1, ('loc', 0)), + '0xcc69885fda6bcc1a4ace058b4a62bf5e179ea78fd58a1ccd71c22cc9b688792f' : ('map', 1, ('loc', 1)), + '0xe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e0' : ('map', 1, ('loc', 2)), + '0xa15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054c' : ('map', 1, ('loc', 3)), + '0xabd6e7cb50984ff9c2f3e18a2660c3353dadf4e3291deeb275dae2cd1e44fe05' : ('map', 1, ('loc', 4)), + '0x1471eb6eb2c5e789fc3de43f8ce62938c7d1836ec861730447e2ada8fd81017b' : ('map', 1, ('loc', 5)), + '0x3e5fec24aa4dc4e5aee2e025e51e1392c72a2500577559fae9665c6d52bd6a31' : ('map', 1, ('loc', 6)), + '0xb39221ace053465ec3453ce2b36430bd138b997ecea25c1043da0c366812b828' : ('map', 1, ('loc', 7)), + '0xad67d757c34507f157cacfa2e3153e9f260a2244f30428821be7be64587ac55f' : ('map', 1, ('loc', 8)), + '0x92e85d02570a8092d09a6e3a57665bc3815a2699a4074001bf1ccabf660f5a36' : ('map', 1, ('loc', 9)), + '0xbbc70db1b6c7afd11e79c0fb0051300458f1a3acb8ee9789d9b6b26c61ad9bc7' : ('map', 1, ('loc', 10)), + '0x72c6bfb7988af3a1efa6568f02a999bc52252641c659d85961ca3d372b57d5cf' : ('map', 1, ('loc', 11)), + '0xd421a5181c571bba3f01190c922c3b2a896fc1d84e86c9f17ac10e67ebef8b5c' : ('map', 1, ('loc', 12)), + '0xfd54ff1ed53f34a900b24c5ba64f85761163b5d82d98a47b9bd80e45466993c5' : ('map', 1, ('loc', 13)), + '0xa7c5ba7114a813b50159add3a36832908dc83db71d0b9a24c2ad0f83be958207' : ('map', 1, ('loc', 14)), + '0x169f97de0d9a84d840042b17d3c6b9638b3d6fd9024c9eb0c7a306a17b49f88f' : ('map', 1, ('loc', 15)), + '0x8c6065603763fec3f5742441d3833f3f43b982453612d76adb39a885e3006b5f' : ('map', 1, ('loc', 16)), + '0x17bc176d2408558f6e4111feebc3cab4e16b63e967be91cde721f4c8a488b552' : ('map', 1, ('loc', 17)), + '0x71a67924699a20698523213e55fe499d539379d7769cd5567e2c45d583f815a3' : ('map', 1, ('loc', 18)), + '0x4155c2f711f2cdd34f8262ab8fb9b7020a700fe7b6948222152f7670d1fdf34d' : ('map', 1, ('loc', 19)), +} diff --git a/pano/stack.py b/pano/stack.py new file mode 100644 index 00000000..9d2d2b96 --- /dev/null +++ b/pano/stack.py @@ -0,0 +1,199 @@ +from copy import copy +import logging + +import core.arithmetic as arithmetic + +from utils.helpers import opcode, to_exp2, EasyCopy +from core.masks import to_mask, to_neg_mask + +from core.algebra import neg_mask_op, mask_op + +from pano.prettify import prettify + +logger = logging.getLogger(__name__) + +def fold_stacks(self, latter, depth): + assert len(self) == len(latter), (self, latter) + vars = [] + + first = list(self) + second = list(latter) + + for idx, el in reversed(list(enumerate(copy(first)))): + el2 = second[idx] + + if el != el2: + temp_var_counter = len(first) - idx + depth*1000 + + vars.append(('var', temp_var_counter, first[idx], idx)) + first[idx] = ('var', temp_var_counter) + + return first, vars + + +class Stack(EasyCopy): + + def __init__(self, val=None): + if type(val) == tuple: + val = list(val) + + self.stack = val or [] + + def __str__(self): + return "[" + (", ".join([prettify(el, parentheses=False) for el in self.stack])) + "]" + + def light_copy(self): + ret = copy(self) + ret.stack = copy(self.stack) + return ret + + def peek(self): + if self.len() > 0: + return self.stack[-1] + else: + return None + + def append(self, el): + assert el is not None, self.stack + + self.stack.append(self.simplify(el)) + + def pop(self): + return self.stack.pop() + + def len(self): + return len(self.stack) + + def swap(self, idx): + pos = -1 * idx - 1 + self.stack[-1], self.stack[pos] = self.stack[pos], self.stack[-1] + + def dup(self, idx): + self.stack.append(self.stack[-idx]) + + def folded_with(self, latter, loader_jds, depth): + assert self.len() == latter.len() + vars = [] + + first = copy(self.stack) + second = copy(latter.stack) + jumpdests = self.jump_dests(loader_jds) + + for idx, el in reversed(list(enumerate(copy(first)))): + el2 = second[idx] + + if el != el2: + temp_var_counter = len(first) - idx + depth*1000 + + vars.append(('var', temp_var_counter, first[idx], idx)) + first[idx] = ('var', temp_var_counter) + + return Stack(first), vars + + def unfold(self, vars): + first = self.stack + + for idx, el in vars.items(): + first[len(first) - idx] = el + + def jump_dests(self, jump_dests): + + res = [] + + for el in self.stack: + if type(el) == int and el in jump_dests or (type(el) == int and el > 2000 and el < 5000): + res.append(str(el)) + + return res + + simplify_cache = {} + + @staticmethod + def simplify(exp): + if type(exp) == int: + return exp & arithmetic.UINT_256_MAX + if type(exp) != tuple: + return exp + + if exp in Stack.simplify_cache: + return Stack.simplify_cache[exp] + + ret = Stack._simplify(exp) + + Stack.simplify_cache[exp] = ret + + return ret + + @staticmethod + def _simplify(exp): + op = opcode(exp) + + if op in arithmetic.opcodes: + exp = arithmetic.eval(exp) + + if op in ('and', 'div', 'mul'): + left = exp[1] + right = exp[2] + + if op == 'and': + left_mask = to_mask(left) + + if left_mask: + (m1, m2) = left_mask + exp = mask_op(right, m1, m2) + + else: # could be 'elif to_mask(right)', but that's slower bc we have to call to_mask twice then + right_mask = to_mask(right) + + if right_mask: + (m1, m2) = right_mask + exp = mask_op(left, m1, m2) + + elif to_neg_mask(left): + bounds = to_neg_mask(left) + exp = neg_mask_op(right, *bounds) + + elif to_neg_mask(right): + bounds = to_neg_mask(right) + exp = neg_mask_op(left, *bounds) + + elif op == 'div' and len(exp) == 3 and to_exp2(right): + shift = to_exp2(right) + exp = mask_op(left, size = 256-shift, offset = shift, shr = shift) + + elif op == 'mul' and len(exp)==3 and to_exp2(left): + shift = to_exp2(left) + exp = mask_op(right, size = 256-shift, shl = shift) + + elif op == 'mul' and len(exp) == 3 and to_exp2(right): + shift = to_exp2(right) + exp = mask_op(left, size = 256-shift, shl = shift) + + return exp + + def cleanup(self): + + stack = self.stack + + for i, s in enumerate(stack): + if type(stack[i]) == tuple: + if s[0] == 'lt' and type(s[1]) == int and type(s[2]) == int: + if s[1] < s[2]: + stack[i] = ('bool', 1) + else: + stack[i] = ('bool', 0) + + elif s[0] == 'iszero' and type(s[1]) == int: + if s[1] == 0: + stack[i] = ('bool', 1) + else: + stack[i] = ('bool', 0) + + elif s[0] == 'iszero' and opcode(s[1]) == 'bool' and type(s[1][1]) == int: + stack[i] = ('bool', 1 - s[1][1]) + + elif stack[i][0] == 'iszero' and opcode(stack[i][1]) == 'iszero': + if opcode(stack[i][1][1]) in ('iszero', 'eq', 'lt', 'gt', 'slt', 'sgt'): + stack[i] = stack[i][1][1] + else: + stack[i] = ('bool', stack[i][1][1]) diff --git a/pano/vm.py b/pano/vm.py new file mode 100644 index 00000000..23955779 --- /dev/null +++ b/pano/vm.py @@ -0,0 +1,814 @@ +# coding: tilde + +from copy import copy +import core.arithmetic as arithmetic +import logging +from utils.helpers import opcode, EasyCopy, all_concrete + +from core.algebra import sub_op, add_op, or_op, lt_op, mask_op, mul_op, bits, minus_op, to_bytes + +from .stack import Stack, fold_stacks + +import utils.opcode_dict as opcode_dict + +from core.arithmetic import is_zero, simplify_bool + +from pano.prettify import pprint_trace + +from utils.helpers import precompiled, precompiled_var_names + +import sys + +loader_dests = None +logger = logging.getLogger(__name__) + + +def mem_load(pos, size=32): + return ('mem', ('range', pos, size)) + + +def find_nodes(node, f): + assert type(node) == Node + + if f(node): + res = [node] + else: + res = [] + + for n in node.next: + res.extend(find_nodes(n, f)) + + return res + + +node_count = 0 + + +class Node(): + + def __str__(self): + return f'id{str(hash(str(self.jd)))[-4:]}' + + + def __repr__(self): + # technically not a proper _repr_, but some trace printout functions use this + # instead of a proper str + return self.__str__() + + + def __init__(self, vm, start, safe, stack, condition=True, trace=None): + global node_count + + node_count += 1 +# if node_count % 1000 == 0: +# print(node_count) + + if node_count > 100_000: + raise RuntimeError("Too many nodes / function too big.") + + self.vm = vm + self.prev = [] + self.next = [] + self.trace = trace + self.start = start + self.safe = safe + self.stack = stack + self.history = {} + self.depth = 0 + self.label_history = {} + self.label = None + + self.condition = condition + + stack_obj = Stack(stack) + self.jd = (start, len(stack), tuple(stack_obj.jump_dests(loader_dests))) + + def apply_vars(var_list): + for orig_name, new_name in var_list: + assert orig_name ~ ('var', _) + assert new_name ~ ('var', int) + + self.trace = replace(self.trace, orig_name, new_name) + + for n in self.next: + n.apply_vars(var_list) + + def make_trace(self): + if self.trace is None: + return ['nil'] + + + begin_vars = [] + if self.is_label(): + for _, var_idx, var_val, _ in self.label.begin_vars: + begin_vars.append(('setvar', var_idx, var_val)) + + if self.vm.just_fdests and self.trace != [('revert', 0)]: + t = self.trace[0] + if t ~ ('jump', :target_node, ...): + begin = [('jd', str(self.jd[0]))]#, str(self.trace))] + else: + begin = ['?'] + else: + begin = [] + + begin += [('label', self, tuple(begin_vars))] if self.is_label() else [] + + last = self.trace[-1] + + if opcode(last) == 'jump': + return begin + self.trace[:-1] + last[1].make_trace() + + if opcode(last) == 'if': + cond, if_true, if_false = last[1:] + if_true = if_true.make_trace() + if_false = if_false.make_trace() + return begin + self.trace[:-1] + [('if', cond, if_true, if_false)] + + return begin + self.trace + + def set_label(self, loop_dest, vars, stack): + self.label = loop_dest + loop_dest.begin_vars = vars + + assert len(self.stack) == len(stack) + self.stack = stack + loop_dest.prev_trace = loop_dest.trace + loop_dest.trace = [('jump', self)] + loop_dest.next = [] + self.set_prev(loop_dest) + + def set_prev(self, prev): + self.prev = prev + self.depth = prev.depth + 1 + + self.history = copy(prev.history) + self.history[prev.jd] = prev + + self.label_history = copy(prev.label_history) + if prev.label: + self.label_history[prev.jd] = prev.label + + prev.next.append(self) + + def is_label(self): + return self.label is not None + + def run(self): + self.prev_trace = self.trace + self.trace = self.vm._run(self.start, self.safe, self.stack, self.condition) + + last = self.trace[-1] + + if opcode(last) == 'jump': + n = last[1] + + n.set_prev(self) + + if opcode(last) == 'if': + if_true, if_false = last[2], last[3] + + if_true.set_prev(self) + if_false.set_prev(self) + + +class VM(EasyCopy): + + def __init__(self, loader, just_fdests=False): + + global loader_dests + loader_dests = loader.jump_dests + + self.loader = loader + self.lines = loader.lines # a shortcut + + self.just_fdests = just_fdests + + self.counter = 0 + global node_count + node_count = 0 + + def run(self, start, history = {}, condition = None, re_run = False): + + + func_node = Node(vm=self, start=start, safe=True, stack=[]) + trace = [('setmem', ('range', 0x40, 32), 0x60), ('jump', func_node, 'safe', tuple())] + + + root = Node(vm=self, trace=trace, start=start, safe=True, stack=[]) + func_node.set_prev(root) + + ''' + + BFS symbolic execution, ends up with a decompiled + code, with labels and gotos + + ''' + + for j in range(20): # 20 + + for i in range(200): # 300 + ''' + + Find all the jumps, and expand them until + the next jump. + + ''' + + self.expand_trace(root) + + ''' + find all the jumps that lead to an already + reached jumpdest (with similar stack, otherwise + we'd catch function calls as all). + + replace them with 'loop' identifier + ''' + + self.replace_loops(root) + + ''' + repeat until there are no more jumps + to explore (so, until the trace didn't change) + + ''' + + nodes = find_nodes(root, lambda n: n.trace == None) + + if len(nodes) == 0: + break + + trace = self.continue_loops(root) + + tr = root.make_trace() + nodes = find_nodes(root, lambda n: n.trace == None) + + if len(nodes) == 0: + break + + tr = root.make_trace() + return tr + + def expand_trace(self, root): + nodes = find_nodes(root, lambda n: n.trace is None) + + for node in nodes: + node.run() + + def replace_loops(self, root): + nodes = find_nodes(root, lambda n: n.trace is None) + + for node in nodes: + if node.jd in node.history and node.jd[1] > 0: # jd[1] == stack_len + folded, vars = fold_stacks(node.history[node.jd].stack, node.stack, node.depth) + loop_line = ('loop', node.history[node.jd], node.stack, folded, tuple(vars)) + node.trace = [loop_line] + + + def continue_loops(self, root): + + loop_list = find_nodes(root, lambda n: n.trace is not None and \ + len(n.trace) == 1 and \ + opcode(n.trace[0]) == 'loop') + + for node in loop_list: + assert node.trace is not None + assert len(node.trace) == 1 + assert opcode(node.trace[0]) == 'loop' + + line = node.trace[0] + loop_dest, stack, new_stack, vars = line[1:] + + if loop_dest.is_label(): + old_stack = loop_dest.stack + beginvars = loop_dest.label.begin_vars + set_vars = [] + + for _, var_idx, val, stack_pos in beginvars: + sv = ('setvar', var_idx, stack[stack_pos]) + set_vars.append(sv) + + if len(list(set_vars)) == 0: + folded, var_list = fold_stacks(old_stack, stack, loop_dest.label.depth) + node.trace = None + node.set_label(loop_dest, tuple(var_list), folded) + continue + + node.trace = [('goto', loop_dest, tuple(set_vars))] + + else: + node.trace = None + node.set_label(loop_dest, tuple(vars), new_stack) + + def _run(self, start, safe, stack, condition): + self.stack = Stack(stack) + trace = [] + + i = start + lines = self.lines + + if i not in lines: + if type(i) != int: + return [('undefined', 'remco jump', i)] + else: + return [('invalid', 'jumdest', i)] + + if not safe: + if lines[i][1] != 'jumpdest': + return [('invalid', 'jump')] + else: + i = self.loader.next_line(i) + if i not in lines: + return [('invalid', 'eof?')] + + + while True: + line = lines[i] + + res = self.handle_jumps(trace, line, condition) + if res is not None: + return res + + if line[1] == 'jumpdest': + n = Node(self, start=i, safe=False, stack=tuple(self.stack.stack), condition=condition) + trace.append(('jump', n)) + return trace + + else: + self.apply_stack(trace, line) + + i = self.loader.next_line(i) + + assert False + + def handle_jumps(self, trace, line, condition): + + i, op = line[0], line[1] + stack = self.stack + + if op == 'jump': + target = stack.pop() + + n = Node(self, start=target, safe=False, stack=tuple(self.stack.stack), condition=condition) + + trace.append(('jump', n)) + return trace + + if op == 'jumpi': + target = stack.pop() + if_condition = simplify_bool(stack.pop()) + + tuple_stack = tuple(self.stack.stack) + n_true = Node(self, start=target, safe=False, stack=tuple_stack, condition=if_condition) + n_false = Node(self, start=self.loader.next_line(i), safe=True, stack=tuple_stack, condition=is_zero(if_condition)) + + if self.just_fdests: + if if_condition ~ ('eq', int:fx_hash, :is_cd) and str(('cd', 0)) in str(is_cd): + n_true.trace=[('funccall', fx_hash, target)] + if if_condition ~ ('eq', :is_cd, int:fx_hash) and str(('cd', 0)) in str(is_cd): + n_true.trace=[('funccall', fx_hash, target)] + + if_true = ('jump', n_true) + if_false = ('jump', n_false) + + bool_condition = arithmetic.eval_bool(if_condition, condition, symbolic=False) + + if bool_condition != None: + if bool_condition: + trace.append(('jump', n_true)) + return trace # res, False + + else: + trace.append(('jump', n_false)) + return trace + + trace.append(('if', if_condition, n_true, n_false, )) + return trace + + if op == 'selfdestruct': + trace.append(('selfdestruct', stack.pop(), )) + return trace + + if op in ['stop', 'assert_fail', 'invalid']: + trace.append((op, )) + return trace + + if op in ['return', 'revert']: + p = stack.pop() + n = stack.pop() + + if n == 0: + trace.append((op, 0)) + else: + return_data = mem_load(p, n) + trace.append((op, return_data,)) + + return trace + + return None + + def apply_stack(self, ret, line): + + def trace(exp, *format_args): + if '--verbose' in sys.argv: # otherwise breaks sometimes, e.g. 0x00a159d41a5bc12dce2f8AcA8e5BB5Beb8F6ABc8.update + logger.debug("Trace: %s", str(exp).format(*format_args)) + + if type(exp) == str: + ret.append(exp.format(*format_args)) + else: + ret.append(exp) + + def trace_extend(l): + assert type(l) == list + + for r in l: + trace(r) + + stack = self.stack + + op = line[1] + + previous_len = stack.len() + + if logging.getLogger().isEnabledFor(logging.DEBUG): + trace(str(stack)) + + if "push" not in op and "dup" not in op and "swap" not in op: + trace('[{}] {}',line[0],op) + else: + if type(line[2]) == str: + trace('[{}] {} {}',line[0],op," ”"+line[2]+"”") + elif line[2] > 0x1000000000: + trace('[{}] {} {}',line[0],op,hex(line[2])) + else: + trace('[{}] {} {}',line[0],op,str(line[2])) + + assert op not in ['jump', 'jumpi', 'revert', 'return', 'stop', 'jumpdest'] + + param = 0 + if len(line)>2: + param = line[2] + + if op in ['exp', 'and', 'eq', 'div', 'lt', 'gt', 'slt', 'sgt', 'mod', 'xor', 'signextend', 'smod', 'sdiv']: + stack.append(arithmetic.eval((op, stack.pop(), stack.pop(),))) + + if op in ['mulmod', 'addmod']: + stack.append(('mulmod', stack.pop(), stack.pop(), stack.pop())) + + if op == 'mul': + stack.append(mul_op(stack.pop(), stack.pop())) + + if op == 'or': + stack.append(or_op(stack.pop(), stack.pop())) + + if op == 'shl': + off = stack.pop() + exp = stack.pop() + if all_concrete(off, exp): + stack.append(exp << off) + else: + stack.append(mask_op(exp, shl = off)) + + if op == 'shr': + off = stack.pop() + exp = stack.pop() + if all_concrete(off, exp): + stack.append(exp >> off) + else: + stack.append(mask_op(exp, offset=minus_op(off), shr = off)) + + if op == 'add': + stack.append(add_op(stack.pop(), stack.pop())) + + if op == 'sub': + left = stack.pop() + right = stack.pop() + + if type(left) == int and type(right) == int: + stack.append(arithmetic.sub(left, right)) + else: + stack.append(sub_op(left, right)) + + elif op in ['not', 'iszero']: + stack.append((op, stack.pop())) + + elif op == 'sha3': + p = stack.pop() + n = stack.pop() + res = mem_load(p, n) + + self.counter += 1 + vname = f'_{self.counter}' + vval = ('sha3', res, ) + trace(('setvar', vname, vval)) + stack.append(('var', vname)) + + elif op == 'calldataload': + stack.append(('cd', stack.pop(),)) + + elif op == 'byte': + val = stack.pop() + num = stack.pop() + off = sub_op(256, to_bytes(num)) + stack.append(mask_op(val, 8, off, shr=off)) + + elif op == 'balance': + addr = stack.pop() + if opcode(addr) == 'mask_shl' and addr[:4] == ('mask_shl', 160, 0, 0): + stack.append(('balance', addr[4],)) + else: + stack.append(('balance', addr,)) + + elif op == 'swap': + stack.swap(param) + + + elif op[:3] == 'log': + p = stack.pop() + s = stack.pop() + topics = [] + param = int(op[3]) + for i in range(param): + el = stack.pop() + topics.append(el) + + trace(('log', mem_load(p, s), ) + tuple(topics)) + + elif op == 'sload': + sloc = stack.pop() + stack.append(('storage', 256, 0, sloc)) + + elif op == 'sstore': + sloc = stack.pop() + val = stack.pop() + trace(('store', 256, 0, sloc, val)) + + elif op == 'mload': + memloc = stack.pop() + loaded = mem_load(memloc) + + self.counter += 1 + vname = f'_{self.counter}' + trace(('setvar', vname, ('mem', ('range', memloc, 32)))) + stack.append(('var',vname)) + + elif op == 'mstore': + memloc = stack.pop() + val = stack.pop() + trace(('setmem', ('range', memloc, 32), val,)) + + elif op == 'mstore8': + memloc = stack.pop() + val = stack.pop() + + trace(('setmem', ('range', memloc, 8), val,)) + + + elif op == 'extcodecopy': + addr = stack.pop() + mem_pos = stack.pop() + code_pos = stack.pop() + data_len = stack.pop() + + trace(('setmem', ('range', mem_pos, data_len), ('extcodecopy', addr, ('range', code_pos, data_len)))) + + elif op == 'codecopy': + mem_pos = stack.pop() + call_pos = stack.pop() + data_len = stack.pop() + + if (type(call_pos), type(data_len)) == (int, int) and call_pos+data_len < len(self.loader.binary): + res = 0 + for i in range(call_pos-1, call_pos+data_len-1): + res = res << 8 + res += self.loader.binary[i] # this breaks with out of range for some contracts + # may be because we're usually getting compiled code binary + # and not runtime binary + trace(('setmem', ('range', mem_pos, data_len), res))# ('bytes', data_len, res))) + + else: + + trace(('setmem', ('range', mem_pos, data_len), ('code.data', call_pos, data_len, ),)) + + + elif op == 'codesize': + stack.append(len(self.loader.binary)) + + elif op == 'calldatacopy': + mem_pos = stack.pop() + call_pos = stack.pop() + data_len = stack.pop() + + if data_len != 0: + call_data = ('call.data', call_pos, data_len) +# call_data = mask_op(('call.data', bits(add_op(data_len, call_pos))), size=bits(data_len), shl=bits(call_pos)) + trace(('setmem', ('range', mem_pos, data_len), call_data)) + + + elif op == 'returndatacopy': + mem_pos = stack.pop() + ret_pos = stack.pop() + data_len = stack.pop() + + if data_len != 0: + return_data = ('ext_call.return_data', ret_pos, data_len) +# return_data = mask_op(('ext_call.return_data', bits(add_op(data_len, ret_pos))), size=bits(data_len), shl=bits(ret_pos)) + trace(('setmem', ('range', mem_pos, data_len), return_data)) + + elif op == 'call': + self.handle_call(op, trace) + + elif op == 'staticcall': + self.handle_call(op, trace) + + + elif op == 'delegatecall': + gas = stack.pop() + addr = stack.pop() + + arg_start = stack.pop() + arg_len = stack.pop() + ret_start = stack.pop() + ret_len = stack.pop() + + call_trace = ('delegatecall', gas, addr, ) # arg_start, arg_len, ret_start, ret_len) + + if arg_len == 0: + fname = None + fparams = None + + elif arg_len == 4: + fname = mem_load( arg_start, 4 ) + fparams = 0 + + else: + fname = mem_load( arg_start, 4 ) + fparams = mem_load( add_op(arg_start, 4), sub_op(arg_len, 4)) + + call_trace += (fname, fparams) + + trace(call_trace) + + self.call_len = ret_len + stack.append('delegate.return_code') + + if 0 != ret_len: + return_data = ('delegate.return_data', 0, ret_len) + + trace(('setmem', ('range', ret_start, ret_len), return_data)) + + + elif op == 'callcode': + gas = stack.pop() + addr = stack.pop() + value = stack.pop() + + arg_start = stack.pop() + arg_len = stack.pop() + ret_start = stack.pop() + ret_len = stack.pop() + + call_trace = ('callcode', gas, addr, value, ) + + if arg_len == 0: + fname = None + fparams = None + + elif arg_len == 4: + fname = mem_load( arg_start, 4 ) + fparams = 0 + + else: + fname = mem_load( arg_start, 4 ) + fparams = mem_load( add_op(arg_start, 4), sub_op(arg_len, 4)) + + call_trace += (fname, fparams) + + trace(call_trace) + + self.call_len = ret_len + stack.append('callcode.return_code') + + if 0 != ret_len: + return_data = ('callcode.return_data', 0, ret_len) + + trace(('setmem', ('range', ret_start, ret_len), return_data)) + + + elif op == 'create': + wei, mem_start, mem_len = stack.pop(), stack.pop(), stack.pop() + + call_trace = ('create', wei) + + code = mem_load(mem_start, mem_len) + call_trace += (code, ) + + trace(call_trace) + + stack.append('create.new_address') + + elif op == 'create2': + wei, mem_start, mem_len, salt = stack.pop(), stack.pop(), stack.pop(), stack.pop() + + call_trace = ('create2', wei, ('mem', ('range', mem_start, mem_len)), salt) + + trace(call_trace) + + stack.append('create2.new_address') + + elif op[:4] == 'push': + stack.append(param) + + elif op == 'pc': + stack.append(line[0]) + + elif op == 'pop': + stack.pop() + + elif op == 'dup': + stack.dup(param) + + elif op == 'msize': + self.counter += 1 + vname = f'_{self.counter}' + trace(('setvar', vname, 'msize')) + stack.append(('var',vname)) + + elif op in ('extcodesize', 'extcodehash', 'blockhash'): + stack.append((op, stack.pop(),)) + + elif op in ['callvalue', 'caller', 'address', 'number', 'gas', 'origin', 'timestamp', + 'difficulty', 'gasprice', 'coinbase', 'gaslimit', 'calldatasize', 'returndatasize']: + stack.append(op) + + + if stack.len() - previous_len != opcode_dict.stack_diffs[op]: + logger.error('line: %s', line) + logger.error('stack: %s', stack) + logger.error('expected %s, got %s stack diff', opcode_dict.stack_diffs[op], stack.len() - org_len) + assert False, f'opcode {op} not processed correctly' + + stack.cleanup() + + def handle_call(self, op, trace): + stack = self.stack + + gas = stack.pop() + addr = stack.pop() + if op == 'call': + wei = stack.pop() + else: + assert op == 'staticcall' + wei = 0 + + arg_start = stack.pop() + arg_len = stack.pop() + ret_start = stack.pop() + ret_len = stack.pop() + + if addr == 4: # Identity + + m = mem_load(arg_start, arg_len) + trace(('setmem', ('range', ret_start, arg_len), m)) + + stack.append('memcopy.success') + + elif type(addr) == int and addr in precompiled: + + m = mem_load(arg_start, arg_len) + args = mem_load(arg_start, arg_len) + var_name = precompiled_var_names[addr] + + trace(("precompiled", var_name, precompiled[addr], args)) + + trace(('setmem', ('range', ret_start, ret_len), ('var', var_name))) + + stack.append('{}.result'.format(precompiled[addr])) + + else: + assert op in ('call', 'staticcall') + call_trace = (op, gas, addr, wei, ) + + if arg_len == 0: + call_trace += None, None + + elif arg_len == 4: + + call_trace += mem_load(arg_start, 4), None + + else: + fname = mem_load( arg_start, 4) + fparams = mem_load(add_op(arg_start, 4), sub_op(arg_len, 4)) + call_trace += fname, fparams + + trace(call_trace) + # trace(('comment', mem_load(arg_start, arg_len))) + + self.call_len = ret_len + + stack.append('ext_call.success') + + if lt_op(0, ret_len): + return_data = ('ext_call.return_data', 0, ret_len) + + trace(('setmem', ('range', ret_start, ret_len), return_data)) diff --git a/pano/whiles.py b/pano/whiles.py new file mode 100644 index 00000000..b17eff19 --- /dev/null +++ b/pano/whiles.py @@ -0,0 +1,1698 @@ +# coding: tilde + +from copy import copy +import core.arithmetic as arithmetic +import logging +import collections + +from core.memloc import range_overlaps, splits_mem, fill_mem, memloc_overwrite, split_setmem, apply_mask_to_range, split_store + +from utils.helpers import rewrite_trace_multiline, opcode, cached, walk_trace, to_exp2, replace, find_op_list +from utils.helpers import contains, find_f_set, find_f_list, rewrite_trace, rewrite_trace_full, replace, replace_f, replace_f_stop, rewrite_trace_ifs + +from core.algebra import simplify, calc_max, add_ge_zero, minus_op, sub_op, flatten_adds, max_to_add, divisible_bytes, _max_op, div_op +from core.algebra import add_op, bits, mul_op, get_sign, safe_ge_zero, ge_zero, lt_op, safe_lt_op, safe_le_op, simplify_max, le_op, max_op, safe_max_op, safe_min_op, min_op, or_op, neg_mask_op, mask_op, apply_mask_to_storage, apply_mask, try_add, to_bytes + +from core.arithmetic import is_zero, to_real_int + +from pano.prettify import pformat_trace, pprint_trace, pprint_repr + +from .postprocess import cleanup_mul_1 + +from utils.profiler import checkpoint, checkpoint_start, log_checkpoints, func_caller + +from core.masks import get_bit + +from core.masks import to_mask, to_neg_mask + +from .rewriter import simplify_exp, postprocess_exp, postprocess_trace, rewrite_string_stores + +from pano.prettify import pretty_repr + +import sys + +logger = logging.getLogger(__name__) +logger.level = logging.CRITICAL # switch to INFO for detailed + +''' + + Rube Goldberg would be proud. + +''' + +def make_whiles(trace): + + logger.info('making') + trace = make(trace) + logger.info('cleaning up jumpdests') + + # clean up jumpdests + trace = rewrite_trace(trace, lambda line: [] if line ~ ('jumpdest', ...) else [line]) + + old_trace = None + count = 0 + while trace != old_trace: + count += 1 + if count > 40: + break + + old_trace = trace + trace = replace_f(trace, simplify_exp) + trace = cleanup_vars(trace) + trace = cleanup_mems(trace) + trace = rewrite_trace(trace, split_setmem) + trace = rewrite_trace_full(trace, split_store) + trace = cleanup_vars(trace) + trace = replace_f(trace, simplify_exp) + trace = cleanup_mul_1(trace) + trace = cleanup_msize(trace) + trace = replace_bytes_or_string_length(trace) + trace = cleanup_conds(trace) + trace = rewrite_trace(trace, loop_to_setmem) + trace = propagate_storage_in_loops(trace) + + # final lightweight postprocessing + # introduces new variables, simplifies code for human readability + # and does other stuff that would break the above loop + + trace = replace_f(trace, max_to_add) + + trace = replace_f(trace, postprocess_exp) + trace = replace_f(trace, postprocess_exp) + + trace = rewrite_trace_ifs(trace, postprocess_trace) + + trace = rewrite_trace_multiline(trace, rewrite_string_stores, 3) + trace = cleanup_mems(trace) + trace = cleanup_mems(trace) + trace = cleanup_mems(trace) + trace = cleanup_conds(trace) + + def fix_storages(exp): + if exp ~ ('storage', :size, int:off, :loc) and off < 0: + return ('storage', size, 0, loc) + return exp + + trace = replace_f(trace, fix_storages) + trace = cleanup_conds(trace) + + logger.debug('readability') + trace = readability(trace) + + trace = cleanup_mul_1(trace) + + return trace + + +def replace_while_var(rest, counter_idx, new_idx): + while contains(rest, ('var', new_idx)): + new_idx += 1 + + def r(exp): + if exp == ('var', counter_idx): + return ('var', new_idx) + + elif exp ~ ('setvar', counter_idx, :val): + return ('setvar', new_idx, val) + + else: + return exp + + return simplify_exp(replace_f(rest, r)), new_idx + + +def canonise_max(exp): + if opcode(exp) == 'max': + args = [] + for e in exp[1:]: + + if e ~ ('mul', 1, :num): + args.append(num) + else: + args.append(e) + + args.sort(key=lambda x: str(x) if type(x) != int else ' ' + str(x)) + return ('max', ) + tuple(args) + else: + return exp + + +assert canonise_max(('max', ('mul', 1, ('x','y')), 4)) == ('max', 4, ('x', 'y')) + + +def readability(trace): + ''' + - replaces variable names with nicer ones, + - fixes empty memory in calls + - replaces 'max..' in setmems with msize variable + (max can only appear because of this) + ''' + + trace = replace_f(trace, canonise_max) + + res = [] + for idx, line in enumerate(trace): + + if line ~ ('setmem', ('range', ('add', *add_params), _), :mem_val): + for m in add_params: + if m ~ ('max', ...): + res.append(('setvar','_msize', m)) + + def x(line): + return [replace(line, m, ('var','_msize'))] + + rest = rewrite_trace(trace[idx:], x) + res.extend(readability(rest)) + return res + + elif line ~ ('if', :cond, :if_true, :if_false): + + # if if_false ~ [('revert', ...)]: # no lists in Tilde... yet :,) + if len(if_false) == 1 and opcode(if_false[0]) == 'revert': + res.append(('if', is_zero(cond), readability(if_false), readability(if_true))) + else: + res.append(('if', cond, readability(if_true), readability(if_false))) + continue + + elif line ~ ('while', ...): + # for whiles, normalize variable names + + a = parse_counters(line) + + rest = trace[idx:] + + if 'counter' in a: + counter_idx = a['counter'] + rest, _ = replace_while_var(rest, counter_idx, 0) + + else: + counter_idx = -1 + + new_idx = 1 + + cond, path, jds, vars = line[1:] + + for _, v_idx, _ in vars: + if v_idx != counter_idx: + rest, new_idx = replace_while_var(rest, v_idx, new_idx) + + line, rest = rest[0], rest[1:] + cond, path, jds, vars = line[1:] + + path = readability(path) + res.append(('while', cond, path, jds, vars)) + + res.extend(readability(rest)) + return res + + + res.append(line) + + return res + + +def replace_bytes_or_string_length(trace): + def replace(expr): + key = None + expr ~ ('mask_shl', :size, :offset, -1, ('and', ('storage', _, 0, :key), ('add', -1, ('mask_shl', _, _, _, ('iszero', ('storage', _, 0, :key2)))))) + expr ~ ('mask_shl', :size, :offset, -1, ('and', ('add', -1, ('mask_shl', _, _, _, ('iszero', ('storage', _, 0, :key2)))), ('storage', _, 0, :key))) + if key is None or key != key2: + return + + if type(key) == int: + key = ('loc', key) + + if size == 255 and offset == 1: + return ('storage', 256, 0, ('length', key)) + assert offset >= 1 + return ('mask_shl', size, offset - 1, 0, ('storage', 256, 0, ('length', key))) + + return replace_f_stop(trace, replace) + + +def loop_to_setmem(line): + + if line ~ ('while', ...): + r = _loop_to_setmem(line) + + if r is not None: + return r + + r = loop_to_setmem_from_storage(line) + + if r is not None: + return r + + return [line] + + +def vars_in_expr(expr): + if expr ~ ('var', :var_id): + return frozenset([var_id]) + + s = frozenset() + + if type(expr) not in (tuple, list): + return s + + for e in expr: + s = s | vars_in_expr(e) + return s + +def only_add_in_expr(op): + if op ~ ('setvar', :idx, :val): + return only_add_in_expr(val) + if op ~ ('add', *terms): + return all(only_add_in_expr(o) for o in terms) + if op ~ ('var', _): + return True + if op ~ ('sha3', :term): # sha3(constant) is allowed. + return opcode(term) is None + if opcode(op) is not None: + return False + return True + +assert only_add_in_expr(('setvar', 100, ('mul', ('var', 100), 1))) is False +assert only_add_in_expr(('setvar', 100, ('add', ('var', 100), 1))) is True + +def propagate_storage_in_loop(line): + + assert line ~ ('while', :cond, :path, :jds, :setvars) + + def storage_sha3(value): + if value ~ ('add', *terms): + for op in terms: + if storage_sha3(op) is not None: + return storage_sha3(op) + + if value ~ ('sha3', :val): + if type(val) != int or val < 1000: # used to be int:val here, why? + return value + + def path_only_add_in_continue(path): + for op in path: + if opcode(op) == 'continue': + _, _, instrs = op + if any(not only_add_in_expr(instr) for instr in instrs): + return False + return True + + new_setvars = [] + + for setvar in setvars: + assert setvar ~ ('setvar', :var_id, :value) + + sha3 = storage_sha3(value) + if not sha3: + new_setvars.append(setvar) + continue + + # If the "continue" instructions don't only add stuff to the index variable, + # it's not safe to proceed. If we would do "i = i * 2", then it doesn't + # make sense to substract a constant to "i". + if not path_only_add_in_continue(path): + new_setvars.append(setvar) + continue + + new_setvars.append(('setvar', var_id, sub_op(value, sha3))) + + def add_sha3(t): + # We replace occurrences of var by "var + sha3" + if t == ('var', var_id): + return add_op(t, sha3) + # Important: for "continue" we don't want to touch the variable. + # TODO: This is only valid if the "continue" contains only + # operators like "+" or "-". We should check that. + if opcode(t) == 'continue': + return t + + path = replace_f_stop(path, add_sha3) + cond = replace_f_stop(cond, add_sha3) + + return [('while', cond, path, jds, new_setvars)] + + +def propagate_storage_in_loops(trace): + def touch(line): + if line ~ ('while', ...): + + r = propagate_storage_in_loop(line) + if r is not None: + return r + + return [line] + + return rewrite_trace(trace, touch) + + +def _loop_to_setmem(line): + def memidx_to_memrange(mem_idx, setvars, stepvars, endvars): + mem_idx_next = mem_idx + for v in stepvars: + assert v ~ ('setvar', :v_idx, :v_val) + mem_idx_next = replace(mem_idx_next, ('var', v_idx), v_val) + + diff = sub_op(mem_idx_next, mem_idx) + + if diff not in (32, -32): + return None, None + + mem_idx_last = mem_idx + for v_idx, v_val in endvars.items(): + + mem_idx_last = replace(mem_idx_last, ('var', v_idx), v_val) + + mem_idx_first = mem_idx + for v in setvars: + assert v ~ ('setvar', :v_idx, :v_val) + + mem_idx_first = replace(mem_idx_first, ('var', v_idx), v_val) + + if diff == 32: + mem_len = sub_op(mem_idx_last, mem_idx_first) + + return ('range', mem_idx_first, mem_len), diff + + else: + assert diff == -32 + + mem_idx_last = add_op(32, mem_idx_last) + mem_idx_first = add_op(32, mem_idx_first) + + mem_len = sub_op(mem_idx_first, mem_idx_last) + + return ('range', mem_idx_lasst, mem_len), diff + + assert line ~ ('while', :cond, :path, :jds, :setvars) + + if len(path) != 2: + return None + + if opcode(path[1]) != 'continue': + return None + + if opcode(path[0]) != 'setmem': + return None + + setmem = path[0] + cont = path[1] + + mem_idx, mem_val = setmem[1], setmem[2] + + assert mem_idx ~ ('range', :i, :l) + if l != 32: + return None + mem_idx = i + + stepvars = cont[2] + + a = parse_counters(line) + if 'endvars' not in a: + return None + + setvars, endvars = a['setvars'], a['endvars'] + + rng, diff = memidx_to_memrange(mem_idx, setvars, stepvars, endvars) + + if rng is None: + return None + + if mem_val == 0: + res = [('setmem', rng, 0)] + + elif opcode(mem_val) == 'mem': + mem_val_idx = mem_val[1] + assert mem_val_idx ~ ('range', :i, :l) + mem_val_idx = i + if l != 32: + return None + + val_rng, val_diff = memidx_to_memrange(mem_val_idx, setvars, stepvars, endvars) + + if val_rng is None: + return None + + if val_diff != diff: + return None # possible but unsupported + + # we should check for overwrites here, but skipping for now + # if the part of memcopy loop overwrites source before it's copied, + # we can end up with unexpected behaviour, could at least show some warning, + # or set that mem to 'complicated' or sth + + res = [('setmem', rng, ('mem', val_rng))] + + else: + return None + + for v_idx, v_val in endvars.items(): + res.append(('setvar', v_idx, v_val)) + + return res + +def loop_to_setmem_from_storage(line): + assert opcode(line) == 'while' + cond, path, jds, setvars = line[1:] + + if len(path) != 2 \ + or opcode(path[0]) != 'setmem' \ + or opcode(path[1]) != 'continue': + return None + + setmem = path[0] + cont = path[1] + + logger.debug("loop_to setmem_from_storage: %s\n%s\n%s", setmem, cont, cond) + + # (setmem, mem_idx, mem_val) + mem_idx, mem_val = setmem[1], setmem[2] + + # Extract the interesting variable from mem_idx + vars_in_idx = vars_in_expr(mem_idx) + if len(vars_in_idx) != 1: + return + if not only_add_in_expr(mem_idx): + return + memory_index_var = next(iter(vars_in_idx)) + + # Same from mem_val + vars_in_val = vars_in_expr(mem_val) + if len(vars_in_val) != 1: + return + storage_key_var = next(iter(vars_in_val)) + if opcode(mem_val) != 'storage': + return + if mem_val[1] != 256 or mem_val[2] != 0: + return + storage_key = mem_val[3] + if not only_add_in_expr(storage_key): + return + + logger.debug("now look at the continue") + update_memory_index = ('setvar', memory_index_var, ('add', 32, ('var', memory_index_var))) + update_storage_key = ('setvar', storage_key_var, ('add', 1, ('var', storage_key_var))) + if set(cont[2]) != {update_memory_index, update_storage_key}: + return + + logger.debug("setvars") + memory_index_start = None + storage_key_start = None + memory_index_init = None + for setvar in setvars: + if setvar[1] == memory_index_var: + memory_index_start = replace(mem_idx, ('var', memory_index_var), setvar[2]) + memory_index_init = setvar[2] + elif setvar[1] == storage_key_var: + storage_key_start = replace(storage_key, ('var', storage_key_var), setvar[2]) + else: + return + + + logger.debug("while condition") + if memory_index_var not in vars_in_expr(cond): + return + if opcode(cond) != 'gt': + return + + mem_count = ('add', cond[1], ('mul', -1, cond[2])) + mem_count = replace(mem_count, ('var', memory_index_var), memory_index_init) + + mem_rng = ('range', memory_index_start, mem_count) + storage_rng = ('range', storage_key_start, ('div', mem_count, 32)) + + logger.debug("mem_rng: %s, storage_rng: %s", mem_rng, storage_rng) + return [('setmem', mem_rng, ('storage', 256, 0, storage_rng))] + + +''' + + simplifier + +''' + +def apply_constraint(exp, constr): + # for constraints like "isZero XX % 32", applies them to expression + + return exp + + if constr ~ ('mask_shl', 5, 0, 0, :val): + def f(x): + if x ~ ('mask_shl', int:size, 5, int:shl, ('add', 31, :val)): + return ('add', 32 * (2**shl), ('mask_shl', size, 5, shl, val)) + if x ~ ('mask_shl', int:size, 5, 0, :val): + return ('mask_shl', size, 5, 0, val) + + return x + + return replace_f(exp, f) + + + if constr ~ ('iszero', ('mask_shl', 5, 0, 0, :val)): + def f(x): + if x ~ ('mask_shl', int:size, 5, 0, ('add', 31, :val)): + return ('mask_shl', size+5, 0, 0, val) + if x ~ ('mask_shl', int:size, 5, 0, :val): + return ('mask_shl', size+5, 0, 0, val) + if x ~ ('mask_shl', 5, 0, 0, :val): + return 0 + + return x + + return replace_f(exp, f) + + return exp + + +def cleanup_conds(trace): + ''' + + removes ifs/whiles with conditions that are obviously true + and replace variables that need to be equal to a constant by that constant + + ''' + + res = [] + + for line in trace: + if line ~ ('while', :cond, :path, :jds, :setvars): + # we're not evaluating symbolically, otherwise stuff like + # stor0 <= stor0 + 1 gets evaluated to `True` - this happens + # because we're truncating mask256. it should really be + # mask(256, stor0) <= mask(256, stor0 + 1) + # which is not always true + # see 0x014B50466590340D41307Cc54DCee990c8D58aa8.transferFrom + path = cleanup_conds(path) + ev = arithmetic.eval_bool(cond, symbolic=False) + if ev is True: + res.append(('while', ('bool', 1), path, jds, setvars)) + elif ev is False: + pass # removing loop altogether + else: + res.append(('while', cond, path, jds, setvars)) + + elif line ~ ('if', :cond, :if_true, :if_false): + if_true = cleanup_conds(if_true) + if_false = cleanup_conds(if_false) + + # If the condition is always true/false, remove the if. + ev = arithmetic.eval_bool(cond, symbolic=False) + if ev is True: + res.extend(if_true) + elif ev is False: + res.extend(if_false) + else: + res.append(('if', cond, if_true, if_false)) + + else: + res.append(line) + + return res + +def sizeof(exp): # returns size of expression in *bits* + if exp ~ ('storage', :size, ...): + return size + + if exp ~ ('mask_shl', :size, ...): + return size + + if exp ~ (:op, _, :size_bytes) and is_array(op): + return bits(size_bytes) + + if exp ~ ('mem', ('range', _, :size_bytes)): + return bits(size_bytes) + + if exp ~ ('mem', :idx): + assert False + + return None + +assert sizeof(('mask_shl', 96, 160, 0, 'x')) == 96 +assert sizeof(('mem', ('range', 64, 32))) == 32*8 +assert sizeof('x') == None + +@cached +def find_mems(exp): + def f(exp): + if exp ~ ('mem', ...): + return set([exp]) + else: + return set() + + return find_f_set(exp, f) + +test_e = ('x', 'sth',('mem',4),('t', ('mem', 4), ('mem',8),('mem',('mem',64)))) +assert find_mems(test_e) == {('mem', 64), ('mem', ('mem', 64)), ('mem', 4), ('mem', 8)}, find_mems(test_e) + + +def _eval_msize(cond): + if opcode(cond) not in ('lt', 'le', 'gt', 'ge'): + return None + + left, right = cond[1], cond[2] + + if opcode(left) != 'max' and opcode(right) != 'max': + return None + + if opcode(left) == 'max' and opcode(right) == 'max': + return None + + if opcode(right) == 'max': + cond = swap_cond(cond) + left, right = cond[1], cond[2] + + assert opcode(left) == 'max' + + if opcode(cond) in ('lt', 'le'): + + if opcode(cond) == 'le': + cond = ('lt', left, add_op(1, right)) + left, right = cond[1], cond[2] + + # max(2,3) <= 3 + # max(2,3) < 4 + + # cond == (lt, max(....), right) + # any .... > right -> True + # any .... ? right -> ? + # else -> all ... < right -> False + + if all([safe_lt_op(l, right) is True for l in left[1:]]): + return False + + if any([safe_lt_op(right, l) is False for l in left[1:]]): + return False + + if all([safe_le_op(right, l) is True for l in left[1:]]): + return True + + if opcode(cond) in ('gt', 'ge'): + assert False, cond # unsupported yet + + return None + + +def cleanup_msize(trace, current_msize=0): + res = [] + + for line in trace: + if opcode(line) == 'setmem': + line = replace(line, 'msize', current_msize) + + mem_right = memloc_right(line) + + current_msize = _max_op(current_msize, mem_right) + + res.append(line) + + elif opcode(line) == 'while': + new_one = while_max_memidx(line) + current_msize = _max_op(current_msize, new_one) + res.append(line) + + elif opcode(line) == 'if': + cond, if_true, if_false = line[1:] + if 'msize' in str(cond) and opcode(current_msize) == 'max': + tmp_cond = replace(cond, 'msize', current_msize) + + tmp_evald = _eval_msize(tmp_cond) + + if tmp_evald is not None: + cond = 1 if tmp_evald is True else 0 + + else: + new_msize = max_to_add(current_msize) + cond = replace(cond, 'msize', new_msize) + + if_true = cleanup_msize(if_true, current_msize) + if_false = cleanup_msize(if_false, current_msize) + res.append(('if', cond, if_true, if_false)) + + else: + line = replace(line, 'msize', current_msize) + res.append(line) + +# print('done') + return res + + +def overwrites_mem(line, mem_idx): + ''' + for a given line, returns True if it potentially + overwrites *any part* of memory index, False if it *for sure* doesn't + + ''' + if line ~ ('setmem', :set_idx, _): + if range_overlaps(set_idx, mem_idx) is not False: + return True + else: + return False + + if line ~ ('while', ...): + return while_touches_mem(line, mem_idx) + + return False + + +def affects(line, exp): + if type(exp) != tuple and exp != 'msize': + return False + + s = str(exp) + + if 'msize' in s: + if overwrites_mem(line, ('range', 0, 'undefined')): + return True + + if 'mem' not in s: + return False + + mems = find_mems(exp) + + for m in mems: + m_idx = m[1] + if overwrites_mem(line, m_idx): + return True + + return False + +line_test = ('setmem', ('range', 65, 32), 'x') +exp_test = ('mul', 8, ('mem', ('range', 64, 32))) +assert affects(line_test, exp_test) == True +exp_test = ('mul', 8, ('mem', ('range', 100, 32))) +assert affects(line_test, exp_test) == False + +line_test = ('setmem', ('range', 65, 32), 'x') +exp_test = ('mul', 8, ('mem', ('range', 64, 32))) +assert affects(line_test, exp_test) == True +exp_test = ('mul', 8, ('mem', ('range', 100, 32))) +assert affects(line_test, exp_test) == False + +line_test = ('setmem', ('range', 65, 'sth'), 'x') +exp_test = ('mul', 8, ('mem', ('range', 64, 32))) +assert affects(line_test, exp_test) == True +exp_test = ('mul', 8, ('mem', ('range', 100, 32))) +assert affects(line_test, exp_test) == True + +line_test = ('setmem', ('range', 65, 32), 'x') +exp_test = ('mul', 8, ('mem', ('range', 64, 1))) +assert affects(line_test, exp_test) == False +exp_test = ('mul', 8, ('mem', ('range', 64, 'sth'))) +assert affects(line_test, exp_test) == True + + +''' + + Memory cleanup + +''' + +def trace_uses_mem(trace, mem_idx): + ''' + + checks if memory is used anywhere in the trace + + ''' + + for idx, line in enumerate(trace): + + if line ~ ('setmem', :memloc, :memval): + memval = simplify_exp(memval) + + if exp_uses_mem(memval, mem_idx): + return True + + split = memloc_overwrite(mem_idx, memloc) # returns range that we're confident wasn't overwritten by memloc + res2 = trace[idx+1:] + for s_idx in split: + if trace_uses_mem(res2, s_idx): + return True + + return False + + elif line ~ ('while', ...): + if while_uses_mem(line, mem_idx): + return True + + elif line ~ ('if', :cond, :if_true, :if_false): + + if exp_uses_mem(cond, mem_idx) or \ + trace_uses_mem(if_true, mem_idx) or \ + trace_uses_mem(if_false, mem_idx): + return True + + elif line ~ ('continue', ...): + return True + + else: + if exp_uses_mem(line, mem_idx): + return True + + return False + + +def cleanup_mems(trace, in_loop=False): + ''' + for every setmem, replace future occurences of it with it's value, + if possible + + ''' + + #pprint_trace(trace) + + res = [] + + for idx, line in enumerate(trace): +# print(line) + if line ~ ('setmem', :rng, ('mem', rng)): + continue + + if opcode(line) in ['call', 'staticcall', 'delegatecall', 'codecall']: + fname, fdata = line[-2:] + + if fdata ~ ('mem', ('range', _, -4)): + line = line[:-2] + (None, None) + + res.append(line) + + elif line ~ ('setmem', :mem_idx, :mem_val): + # find all the future occurences of var and replace if possible + if not affects(line, mem_val): + remaining_trace = replace_mem(trace[idx+1:], mem_idx, mem_val) + else: + remaining_trace = trace[idx+1:] + + if in_loop or trace_uses_mem(remaining_trace, mem_idx): + res.append(line) + + res.extend(cleanup_mems(remaining_trace)) + + break + + elif line ~ ('while', :cond, :path, *rest): + + path = cleanup_mems(path) + res.append(('while', cond, path, ) + rest) + + elif line ~ ('if', :cond, :if_true, :if_false): + if_true = cleanup_mems(if_true) + if_false = cleanup_mems(if_false) + res.append(('if', cond, if_true, if_false)) + + else: + res.append(line) + + return res + +cache_replace_mem_exp = {} + +@cached +def replace_mem_exp(exp, mem_idx, mem_val): + if type(exp) != tuple: + return exp + + res = tuple(replace_mem_exp(e, mem_idx, mem_val) if type(e) == tuple else e for e in exp) + + if opcode(mem_val) not in ('mem', 'var', 'data'): + if res ~ ('delegatecall', :gas, :addr, ('mem', :func), ('mem', :args)): + assert func ~ ('range', :f_begin, :f_len) + assert args ~ ('range', :a_begin, :a_len) + if f_len == 4 and sub_op(add_op(f_begin, f_len), a_begin) == 0: #: + # we have a situation when inside memory is sth like: (range 96 4) (100 ...) + # let's merge those two memories, and try to replace with mem exp + res_range = simplify_exp(('range', f_begin, add_op(f_len, a_len))) + if res_range == mem_idx: + res = ('delegatecall', gas, addr, None, mem_val) + + if res ~ ('call', :gas, :addr, :value, ('mem', :func), ('mem', :args)): + assert func ~ ('range', :f_begin, :f_len) + assert args ~ ('range', :a_begin, :a_len) + if f_len == 4 and sub_op(add_op(f_begin, f_len), a_begin) == 0: #: + # we have a situation when inside memory is sth like: (range 96 4) (100 ...) + # let's merge those two memories, and try to replace with mem exp + res_range = simplify_exp(('range', f_begin, add_op(f_len, a_len))) + + if res_range == mem_idx: + + res = ('call', gas, addr, value, None, mem_val) + + + if res != exp: + res = simplify_exp(res) + + if res ~ ('mem', ...): + assert res ~ ('mem', _), res + res = fill_mem(res, mem_idx, mem_val) + + return res + + +def replace_mem(trace, mem_idx, mem_val): + ''' + + replaces any reference to mem_idx in the trace + with a value of mem_val, up until a point of that mem being + overwritten + + mem[64] = 'X' + log mem[64] + mem[65] = 'Y' + log mem[64 len 1] + log mem[65] + mem[63] = 'Z' + ... + + into + + mem[64] = 'X' + log 'X' + mem[65] = 'Y' + log mask(1, 'X') + log mem[65] + ... (the rest unchanged) + + ''' + mem_idx = simplify_exp(mem_idx) + mem_val = simplify_exp(mem_val) + mem_id = ('mem', mem_idx) + + if type(mem_val) is tuple and opcode(mem_val) != 'mem': + mem_val = arithmetic.eval(mem_val) + + res = [] + + for idx, line in enumerate(trace): + + if line ~ ('setmem', :memloc, _): + memloc = simplify_exp(memloc) + # replace in val + res.append(replace_mem_exp(line, mem_idx, mem_val)) + if range_overlaps(memloc, mem_idx): + split = splits_mem(mem_idx, memloc, mem_val) + res2 = trace[idx+1:] + for s in split: + res2 = replace_mem(res2, s[0], s[1]) + + res.extend(res2) + return res + if affects(line, mem_val): + res.extend(copy(trace[idx+1:])) + return res + + elif affects(line, mem_val) or affects(line, mem_id): + res.extend(copy(trace[idx:])) + return res + + elif line ~ ('while', :cond, :path, :jds, :vars): + # shouldn't this go above the affects if above? and also update vars even if + # the loops affects the memidx? + + xx = [] + for v in vars: + xx.append(replace_mem_exp(v, mem_idx, mem_val)) + vars = xx + + if not affects(line, ('mem', mem_idx)) and \ + not affects(line, (mem_val)): + cond = replace_mem_exp(cond, mem_idx, mem_val) + path = replace_mem(path, mem_idx, mem_val) + + res.append(('while', cond, path, jds, vars)) + + + elif line ~ ('if', :cond, :if_true, :if_false): + + cond = replace_mem_exp(cond, mem_idx, mem_val) + mem_idx_true = apply_constraint(mem_idx, cond) + mem_val_true = apply_constraint(mem_val, cond) + mem_idx_false = apply_constraint(mem_idx, is_zero(cond)) + mem_val_false = apply_constraint(mem_val, is_zero(cond)) + + + if_true = replace_mem(if_true, mem_idx, mem_val) + if_false = replace_mem(if_false, mem_idx, mem_val) + + res.append(('if', cond, if_true, if_false)) + + + else: + # speed + test = 'mem' in str(line) + if test and mem_idx ~ ('add', _, ('var', :num)) and \ + str(('var', num)) not in str(line): + test = False + # / speed + + if test: + l = replace_mem_exp(line, mem_idx, mem_val) + else: + l = line + + res.append(l) + + return res + + +''' + + Variables cleanup + +''' + +def cleanup_vars(trace, required_after = None): + required_after = required_after or [] + ''' + for every var = mem declaration, replace future + occurences of it, if possible + + var1 = mem[64] + log var1 + mem[65] = 'Y' + log var1 + + into + + var1 = mem[64] + log mem[64] + mem[65] = 'Y' + log var1 + + for var declarations that are no longer in use, remove them + + ''' + + res = [] + + for idx, line in enumerate(trace): + if line ~ ('setvar', :var_idx, :var_val): + # find all the future occurences of var and replace if possible + + remaining_trace = replace_var(trace[idx+1:], var_idx, var_val) + if contains(remaining_trace, ('var', var_idx)) or \ + ('var', var_idx) in required_after: + res.append(line) + + res.extend(cleanup_vars(remaining_trace, required_after=required_after)) + return res + + + elif line ~ ('while', :cond, :path, *rest): + path = cleanup_vars(path, required_after=required_after + find_op_list(trace[idx+1:], 'var')) + res.append(('while', cond, path, ) + rest) + + a = parse_counters(line) + + if 'endvars' in a: + remaining_trace = trace[idx+1:] + for var_idx, var_val in a['endvars'].items(): + remaining_trace = replace_var(remaining_trace, var_idx, var_val) + + res.extend(cleanup_vars(remaining_trace, required_after=required_after + find_op_list(trace[idx+1:], 'var'))) + return res + + elif line ~ ('if', :cond, :if_true, :if_false): + if_true = cleanup_vars(if_true, required_after=required_after) + if_false = cleanup_vars(if_false, required_after=required_after) + res.append(('if', cond, if_true, if_false)) + else: + res.append(line) + + return res + +def replace_var(trace, var_idx, var_val): + ''' + replace occurences of var, if possible + + ''' + + var_id = ('var', var_idx) + res = [] + + for idx, line in enumerate(trace): + + if line ~ ('setmem', :mem_idx, _): + # this all seems incorrect, (plus 'affects' checks below, needs to be revisited) + memloc = ('mem', mem_idx) + # replace in val + res.append(replace(line, var_id, var_val)) + + if affects(line, var_val): + res.extend(copy(trace[idx+1:])) + return res + else: + continue + + if line ~ ('while', :cond, :path, :jd, :setvars): + setvars = replace(setvars, var_id, var_val) + + if not affects(line, var_val): #and not find_f(path, lambda e: e ~ ('setvar', var_idx, _)): + cond = replace(cond, var_id, var_val) + path = replace_var(path, var_idx, var_val) + + + line = ('while', cond, path, jd, setvars) + + if affects(line, var_val): + res.append(line) + res.extend(copy(trace[idx+1:])) + return res + + elif opcode(line) == 'while': + assert not affects(line, var_val) + res.append(line) # could replace vars inside of while, skipping for now + + elif line ~ ('if', :cond, :if_true, :if_false): + cond = replace(cond, var_id, var_val) + if_true = replace_var(if_true, var_idx, var_val) + if_false = replace_var(if_false, var_idx, var_val) + res.append(('if', cond, if_true, if_false)) + + else: + res.append(replace(line, var_id, var_val)) + + return res + + +''' + + loop parsing + +''' + + +def find_conts(trace): + def check(line): + if opcode(line) == 'continue': + return [line] + else: + return [] + + return find_f_list(trace, check) + + +def swap_cond(cond): + replacement = { + 'lt': 'gt', + 'le': 'ge', + 'gt': 'lt', + 'ge': 'le', + } + + return (replacement[cond[0]], cond[2], cond[1]) + + + +def move_right(left, right, exp): + assert type(right) != list + assert type(left) != list + if left == exp: + return right + + if left ~ ('add', *terms): + assert exp in terms, terms # deep embedding unsupported + for e in terms: + if e != exp: + if type(e) == int: + e = to_real_int(e) + right = sub_op(right, e) + + return right + + if left ~ ('mul', *terms): + assert exp in terms # deep embedding unsupported + for e in terms: + if e != exp: + assert type(e) != list + assert type(right) != list + right = div_op(right, e) + + return right + +def normalize(cond): + cond = tuple(cleanup_mul_1(cond)) + + if opcode(cond) not in ('lt', 'le', 'gt', 'ge'): + cond = ('lt', 0, cond) + return normalize(cond) + + left, right = cond[1], cond[2] + vars_left = find_op_list(left, 'var') + vars_right = find_op_list(right, 'var') + + left_vars = tuple([e for e in vars_left if e ~ ('var', int)]) # int = loop vars + right_vars = tuple([e for e in vars_right if e ~ ('var', int)]) + + if len(left_vars) + len(right_vars) != 1: + return None + + if len(right_vars) == 1: + return normalize(swap_cond(cond)) + + assert len(left_vars) == 1 and len(right_vars) == 0, cond + + var = left_vars[0] + + if opcode(left) != 'var': + assert type(right) != list + assert type(left) != list + right = move_right(left, right, var) + left = var + cond = (cond[0], left, right) + + if cond ~ ('lt', :left, :right): + cond = ('le', left, sub_op(right, 1)) + + if cond ~ ('gt', :left, :right): + cond = ('ge', left, add_op(right, 1)) + + return cond # we end up with (gt/lt (var int) sth) + + +def find_setmems(trace): + def check(line): + if line ~ ('while', _, :path, ...): + + sm = find_setmems(path) + if len(sm) == 0: + return [] + + for s in sm: + s_idx = s[1] +# if 'var' in str(s_idx): +# print(s_idx) +# assert False + + return sm + + elif line ~ ('setmem', ...): + return [line] + + else: + return [] + + return walk_trace(trace, check) + +def memloc_left(setmem): + assert opcode(setmem) in ('setmem', 'mem') + memloc = setmem[1] + assert memloc ~ ('range', :loc, _) + return loc + +def memloc_right(setmem): + assert opcode(setmem) in ('setmem', 'mem') + memloc = setmem[1] + + assert memloc ~ ('range', :loc, :rlen) + return add_op(loc, rlen) + +def make_range(left, right): + r_len = sub_op(right, left) + + if safe_ge_zero(r_len) is False: + return ('range', left, 0) + else: + return ('range', left, r_len) + +def while_max_memidx(line): + # returns the rightmost memory index for a setmem + + a = parse_counters(line) + assert line ~ ('while', :cond, :path, :jds, :setvars) + + try: + setmems = find_setmems(path) + except: + return 'unknown' + + if len(setmems) == 0: + return 0 + + collected = 0 + + if 'endvars' not in a: + for s in setmems: + collected = _max_op(collected, memloc_right(s)) + + return collected + + setmems_begin = setmems_end = setmems + + + for v in a['setvars']: + v_idx, v_start = v[1], v[2] + v_end = a['endvars'][v_idx] + + setmems_begin = replace_var(setmems_begin, v_idx, v_start) + setmems_end = replace_var(setmems_end, v_idx, v_end) + + for idx, _ in enumerate(setmems): + collected = _max_op(collected, memloc_right(setmems_begin[idx])) + collected = _max_op(collected, memloc_right(setmems_end[idx])) + + + return collected + +def extract_paths(while_exp): + assert while_exp ~ ('while', _, :trace, :jd, :setvars) + + paths = [] + def f(trace, jd, so_far): + # extract all the paths leading up to jd + if len(trace) == 0: + return [] + + line = trace[0] + + #assert opcode(line) != 'while' + + + if line ~ ('if', :cond, :if_true, :if_false): + res_true = f(if_true, jd, so_far + [('require', cond)]) + res_false = f(if_false, jd, so_far + [('require', is_zero(cond))]) + return res_true + res_false + + + if len(trace) == 1: + if line ~ ('continue', ...): + return [so_far] + else: + return [] + + + return f(trace[1:], jd, so_far + [line]) + + return f(trace, jd, []) + +def extract_setmems(while_exp): + paths = extract_paths(while_exp) + res = [] + for p in paths: + res += find_setmems(p) + return res + +def extract_mems(while_exp): + paths = extract_paths(while_exp) + res = [] + for p in paths: + res += find_mems(p) + return res + +# mems = extract_mems(path) + + +def while_touches_mem(line, mem_idx): + a = parse_counters(line) + assert line ~ ('while', :cond, :path, :jds, :setvars) + cond, path, jds, setvars = line[1:] + +# try: + setmems = extract_setmems(line) +# setmems = find_setmems(path) +# except: +# return True + + if len(setmems) == 0: + return False + + setmems_begin = setmems_end = setmems + + if 'endvars' not in a: + for s in setmems: # if no endvars, comparing just with a 'var' assumes 'var' is any natural number + if range_overlaps(mem_idx, s[1]) is not False: + return True + + return False + + for v in a['setvars']: + v_idx, v_start = v[1], v[2] + v_end = a['endvars'][v_idx] + + setmems_begin = replace_var(setmems_begin, v_idx, v_start) + setmems_end = replace_var(setmems_end, v_idx, v_end) + + for idx, _ in enumerate(setmems): + r_begin = memloc_left(setmems_begin[idx]) + r_end = memloc_right(setmems_end[idx]) + + r = make_range(r_begin, r_end) + if range_overlaps(mem_idx, r) is not False: + return True + + r_begin = memloc_left(setmems_end[idx]) + r_end = memloc_right(setmems_begin[idx]) + + r = make_range(r_begin, r_end) + if range_overlaps(mem_idx, r) is not False: + return True + + return False + +def while_uses_mem(line, mem_idx): + assert line ~ ('while', :cond, :path, :jds, :setvars) + a = parse_counters(line) + + mems = find_mems(line) + +# mems = extract_mems(line) + + if len(mems) == 0: + return False + + mems_begin = mems_end = mems + + if 'endvars' not in a: + + for s in mems: + if range_overlaps(mem_idx, s[1]) is not False: + return True + + return False + + for v in a['setvars']: + v_idx, v_start = v[1], v[2] + v_end = a['endvars'][v_idx] + + mems_begin = replace_var(mems_begin, v_idx, v_start) + mems_end = replace_var(mems_end, v_idx, v_end) + + for idx, _ in enumerate(mems): + r_begin = memloc_left(mems_begin[idx]) + r_end = memloc_right(mems_end[idx]) + + r = make_range(r_begin, r_end) + if range_overlaps(mem_idx, r) is not False: + return True + + r_begin = memloc_left(mems_end[idx]) + r_end = memloc_right(mems_begin[idx]) + + r = make_range(r_begin, r_end) + if range_overlaps(mem_idx, r) is not False: + return True + + return False + +def exp_uses_mem(exp, mem_idx): + mems = find_mems([exp]) + + for m in mems: + assert m ~ ('mem', :m_idx) + if range_overlaps(m_idx, mem_idx) is not False: + return True + + return False + + +def parse_counters(line): + + a = {} + assert line ~ ('while', :cond, :path, :jds, :setvars) + + a['setvars'] = setvars + a['jds'] = jds + + conts = find_conts(path) +# print(conts) +# print(find_op_list(path, 'continue')) + assert conts == find_op_list(path, 'continue') + + startvars = {} + for v in setvars: + assert v ~ ('setvar', :vidx, :vval) + startvars[vidx] = vval + + cond = normalize(cond) + if cond is None: + return {} + + cont = conts[0] + + stepvars = {} + for v in cont[2]: + var_idx, var_val = v[1], v[2] + stepvars[var_idx] = var_val + + a['stepvars'] = stepvars + + counter = cond[1][1] + counter_stop = cond[2] + counter_start = startvars[counter] + a['counter'] = counter + a['start'] = counter_start + a['stop'] = counter_stop + if counter not in stepvars: + logger.warn(f'counter not in stepvars') + counter_diff = 0 + else: + counter_diff = stepvars[counter] + + if len(conts) > 1: + return a + + if opcode(counter_diff) != 'add': + return a + + assert type(counter_diff[1]) == int + + counter_diff = (counter_diff[0], to_real_int(counter_diff[1]), counter_diff[2]) + + # counter_diff[2] ~ ('mul', 1, X) -> counter_diff[2] = X + if opcode(counter_diff[2]) == 'mul' and counter_diff[2][1] == 1: + counter_diff = (counter_diff[0], counter_diff[1], counter_diff[2][2]) + + assert counter_diff[2] == ('var', counter), counter_diff + + counter_step = to_real_int(counter_diff[1]) + a['step'] = counter_step + + num_loops = div_op(add_op(sub_op(counter_stop, counter_start), counter_step), counter_step) + + if num_loops ~ ('div', ...): # so, no obvious divider + a['counter_stop'] = counter_stop + a['counter_start'] = counter_start + a['counter_step'] = counter_step + return a + + a['num_loops'] = num_loops + + a['endvars'] = {} + for v in setvars: + var_idx, var_val = v[1], to_real_int(v[2]) + var_diff = to_real_int(stepvars[var_idx][1]) + assert type(num_loops) != list + var_stop = add_op(var_val, mul_op(var_diff, num_loops)) + a['endvars'][var_idx] = var_stop + + return a + +''' + + make whiles + +''' + + +def make(trace): + res = [] + + for idx, line in enumerate(trace): + if line ~ ('if', :cond, :if_true, :if_false): + res.append(('if', cond, make(if_true), make(if_false))) + + elif line ~ ('label', :jd, :vars, ...): + try: + before, inside, remaining, cond = to_while(trace[idx+1:], jd) + except: + continue + return trace + + inside = inside #+ [str(inside)] + + inside = make(inside) + remaining = make(remaining) + + for _, v_idx, v_val in vars: + before = replace(before, ('var', v_idx), v_val) + before = make(before) + + + res.extend(before) + res.append(('while', cond, inside, repr(jd), vars)) + res.extend(remaining) + + return res + + elif line ~ ('goto', :jd, :setvars): + res.append(('continue', repr(jd), setvars)) + + else: + res.append(line) + + return res + + +def get_jds(line): + if line ~ ('goto', :jd, ...): + return [jd] + + else: + return [] + +def is_revert(trace): + if len(trace) > 1: + return False + + line = trace[0] + if (line ~ ('return', 0)) or \ + (line ~ ('revert', ...)) or \ + (line ~ ('invalid', ...)): + return True + + return False + +def to_while(trace, jd, path = None): + path = path or [] + + while True: + if trace == []: + raise + line = trace[0] + trace = trace[1:] + + if (line ~ ('if', :cond, :if_true, :if_false)): + if is_revert(if_true): + path.append(('require', is_zero(cond))) + trace = if_false + continue + + if is_revert(if_false): + path.append(('require', cond)) + trace = if_true + continue + + jds_true = find_f_list(if_true, get_jds) + jds_false = find_f_list(if_false, get_jds) + + assert (jd in jds_true) != (jd in jds_false), (jds_true, jds_false) + + def add_path(line): + if line ~ ('goto', _, :svs): + path2 = path + for _, v_idx, v_val in svs: + path2 = replace(path2, ('var', v_idx), v_val) + + return path2 + [line] + else: + return [line] + + if jd in jds_true: + if_true = rewrite_trace(if_true, add_path) + return path, if_true, if_false, cond + else: + if_false = rewrite_trace(if_false, add_path) + return path, if_false, if_true, is_zero(cond) + + else: + path.append(line) + + + assert False, f'no if after label?{jd}' diff --git a/panoramix.py b/panoramix.py new file mode 100644 index 00000000..65fabd86 --- /dev/null +++ b/panoramix.py @@ -0,0 +1,431 @@ +# +# This is Panoramix. +# + +import sys + +if (sys.version_info[0], sys.version_info[1]) < (3, 8): + print() + print(f' Panoramix requires python3.8, not python{sys.version_info[0]}.{sys.version_info[1]}') + print() + print(' Sorry about that, but there was no way to do all of this in Python<3.8') + print() + # 3.8 requirement is a bit painful, but this was the only way to implement Tilde + # and Tilde gives a custom pattern matching syntax that makes a ton of code simpler. + + exit() + +import os +import json +from contextlib import redirect_stdout +import random +import traceback + +import coloredlogs +import logging + +logger = logging.getLogger(__name__) + +if '--verbose' in sys.argv: + log_level = logging.DEBUG +elif '--silent' in sys.argv: + log_level = logging.CRITICAL +elif '--errors' in sys.argv: + log_level = logging.ERROR +else: + log_level = logging.INFO + +coloredlogs.install(level=log_level, + fmt='%(asctime)s %(message)s', datefmt='%H:%M:%S', + field_styles={'asctime': {'color': 'white', 'faint': True}}) + +import timeout_decorator + +try: + import secret +except: + with open('secret.py','w+') as f: + f.write('# a file for private api keys etc') + + +import various +from utils.helpers import C, cache_fname +from pano.loader import Loader +from pano.whiles import make_whiles +from pano.function import Function +from pano.vm import VM +from pano.contract import Contract +from pano.prettify import pprint_trace, pretty_type, pprint_repr + +VER = '4 Oct 2019' + +addr_shortcuts = { + 'kitties': '0x06012c8cf97BEaD5deAe237070F9587f8E7A266d', + # main contract for testing - most of the common edge cases here + + 'mixgenes': '0xf97e0A5b616dfFC913e72455Fde9eA8bBe946a2B', + # the convoluted one people can't decompile + + + 'etherdelta': '0x8d12A197cB00D4747a1fe03395095ce2A5CC6819', + 'ledger': '0xf91546835f756DA0c10cFa0CDA95b15577b84aA7', + + 'solidstamp': '0x165cfb9ccf8b185e03205ab4118ea6afbdba9203', + # a basic contract - not too complex, not too simple + # with some edge cases + + 'buggy': '0x6025F65f6b2f93d8eD1efeDc752acfd4bdbCec3E', + # weird results in approveAndCall + # and storage getters (allowance) seem badly processed + + 'sweeper': '0x53F955c424F1378D67Bb5e05F728476dC75fB4bA', + # a small contract, useful for testing dynamic memory + + 'zrx': '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', + # fails a lot, because of all the complicated data structures + + 'ctf': '0x68cb858247ef5c4a0d0cde9d6f68dce93e49c02a', + # https://medium.com/consensys-diligence/consensys-diligence-ether-giveaway-1-4985627b7726 + 'ctf2': '0xefa51bc7aafe33e6f0e4e44d19eab7595f4cca87', + # https://medium.com/consensys-diligence/consensys-diligence-ethereum-hacking-challenge-2-bf3dfff639e0 + # selfdestructed, if you see empty results, you need to find the old version + + 'unicorn': '0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7', + # EF's unicorn token. a basic token that has symbol() and name() + + 'loops': '0xe2F42B417337fd9fD22631cad54DB8178655Fcd1', + # many nice kinds of loops + + 'ferlan': '0x7b220AC85B7ae8Af1CECCC44e183A862dA2eD517', + # a ctx with modern solidity, bst dispatch and multiple edge cases + + 'ugly': '0x06a6a7aF298129E3a2AB396c9C06F91D3C54aBA8', + 'dao': '0xF835A0247b0063C04EF22006eBe57c5F11977Cc4' + } + + +''' + + Main decompilation code + +''' + +def decompile(this_addr, only_func_name=None): + + if '--fast' not in sys.argv: + from web3 import Web3 # web3 adds 0.5s to execution time + this_addr = Web3.toChecksumAddress(this_addr) + + ''' + Fetch code from Web3, and disassemble it. + + Loader holds the disassembled line by line code, + and the list of functions within the contract. + ''' + + loader = Loader() + loader.load(this_addr) + + loader.run(VM(loader, just_fdests=True)) + + if len(loader.lines) == 0: + this_fname = cache_fname(this_addr, 'pan') + f = open(this_fname, 'w') + with redirect_stdout(f): + print() + print(C.gray + "#") + print(f"# Panoramix {VER} ") + print("# Address " + C.end + loader.addr + C.gray) + print("# ") + print("# There seems to be no bytecode for this address. ") + print("# It's either not a contract, or it's been destroyed.") + print("# ") + print("# If you think it's an error, e-mail kolinko@gmail.com ") + print("# " + C.end) + + f.close() + + if '--silent' not in sys.argv: + print(open(this_fname).read()) + + return + + ''' + + Main decompilation loop + + ''' + + problems = {} + functions = {} + + for (hash, fname, target) in loader.func_list: + ''' + hash contains function hash + fname contains function name + target contains line# for the given function + ''' + + if only_func_name is not None and not fname.startswith(only_func_name): + # if user provided a function_name in command line, + # skip all the functions that are not it + continue + + logger.info(C.green + f"Parsing {fname}..." + C.end) # this absolutely needs to be green! :D + + try: + if target > 1: + assert loader.lines[target][1] == 'jumpdest', loader.lines[target] + target += 1 + + @timeout_decorator.timeout(180, use_signals=True) + def dec(): + trace = VM(loader).run(target) + trace = make_whiles(trace) + return trace + + trace = dec() + + functions[hash] = Function(hash, trace) + + + except Exception as e: + problems[hash] = fname + + logger.error(f"Problem with %s%s\n%s", fname, C.end, traceback.format_exc()) + + if '--silent' not in sys.argv: + print() + print() + + if '--strict' in sys.argv: + raise + + ''' + + Store decompiled contract into .json + + ''' + + contract = Contract(addr=this_addr, + network=loader.network, + ver=VER, + problems=problems, + functions=functions) + + contract.postprocess() + + try: + json_fname = cache_fname(this_addr, 'json') + with open(json_fname, 'w') as f: + f.write(contract.json()) + except: + # .json is a nice to have, whatever crazy error happens we should + # still proceed with the rest of decompilation + logger.error('failed contract serialization') + + asm_name = cache_fname(this_addr, 'asm') + with open(asm_name, 'w') as f: + for l in loader.disasm(): + f.write(l+'\n') + + ''' + + All the output gets printed to a .pan file in cache dir, + and then displayed on console + + ''' + + + this_fname = cache_fname(this_addr, 'pan') + pan_fd = open(this_fname, 'w') + with redirect_stdout(pan_fd): + + ''' + Print out decompilation header + ''' + + assert loader.network != 'none' # otherwise, the code is empty, and we caught it before + + print(C.gray+"#") + print(f"# Panoramix {VER} ") + + if loader.network != 'mainnet': + pretty_addr = f"{C.end + C.okgreen}{loader.network}:{loader.addr}{C.end + C.gray}" + else: + pretty_addr = f'{C.end}{loader.addr}{C.gray}' + + print("# Decompiled source of "+pretty_addr) + print("# ") + print("# Let's make the world open source ") + print("# " + C.end) + + if len(problems)>0: + print(C.gray+"#") + print("# I failed with these: ") + for p in problems.values(): + print(f'{C.end}{C.gray}# - {C.end}{C.fail}{p}{C.end}{C.gray}') + print('# All the rest is below.') + print('#'+C.end) + + print() + + + ''' + Print out constants & storage + ''' + + shown_already = set() + + for func in contract.consts: + shown_already.add(func.hash) + print(func.print()) + + if len(shown_already) > 0: + print() + + if len(contract.stor_defs) > 0: + print(f'{C.green}def {C.end}storage:') + + storage = {} + + for s in contract.stor_defs: + print(pretty_type(s)) + + print() + + ''' + Print out getters + ''' + + for hash, func in functions.items(): + if func.getter is not None: + shown_already.add(hash) + print(func.print()) + + if '--repr' in sys.argv: + print() + pprint_repr(func.trace) + + print() + + ''' + Print out regular functions + ''' + + func_list = list(contract.functions) + func_list.sort(key= lambda f:f.priority()) # sort func list by length, with some caveats + + if len([f for f in func_list if f.hash not in shown_already]) > 0: + if len(shown_already) > 0: # otherwise no irregular functions, so this is not needed :) + print("" + C.gray + '#\n# Regular functions\n#' + C.end +"\n") + + else: + print("\n" + C.gray + '#\n# No regular functions. That\'s it.\n#' + C.end + "\n\n") + + for func in func_list: + hash = func.hash + + if hash not in shown_already: + shown_already.add(hash) + + print(func.print()) + + if '--returns' in sys.argv: + for r in func.returns: + print(r) + + if '--repr' in sys.argv: + pprint_repr(func.orig_trace) + + print() + + ''' + + Wrap up + + ''' + + pan_fd.close() + + if '--silent' not in sys.argv: + print('\n') + print(open(this_fname).read()) + +def decompile_bulk(addr_list): + i = 0 + for addr in addr_list: + i += 1 + print(f'{i}, {addr}') + decompile(addr) + + +''' + + Command line initialisation + +''' + +bulk_list = None +function_name = None + +if len(sys.argv) == 1: + print(f""" + python3 panoramix.py [address|shortcut|stdin|random] [func_name] [--verbose] [--silent] + + address: {C.gray}e.g. 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d + you can provide multiple, separating with comma{C.end} + + shortcut: {C.gray}e.g. kitties, unicorn, solidstamp{C.end} + random: {C.gray}a random smart contract{C.end} + stdin: {C.gray}bytecode from stdin{C.end} + + --silent: {C.gray}writes output only to the ./cache_pan/ directory{C.end} + + """) + + exit() + +if sys.argv[1] == 'stdin': + body_full = sys.stdin.read().strip() + if not os.path.isdir('cache_stdin'): + os.mkdir('cache_stdin') + + this_addr = None + bulk_list = [] + for body in body_full.split(' '): + + addr = hex(abs(hash(body))) + + fname = f'cache_stdin/{addr}.bin' + bulk_list.append(addr) + + with open(fname, 'w') as f: + f.write(body) + + decompile_bulk(bulk_list) + +elif sys.argv[1] == 'bulk': + decompile_bulk(various.addr_list[:100]) + +elif ',' in sys.argv[1]: + decompile_bulk(sys.argv[1].split(',')) + +else: + this_addr = sys.argv[1] + + if this_addr.lower() in addr_shortcuts: + this_addr = addr_shortcuts[this_addr.lower()] + + elif this_addr.lower() == 'random': + from various import random_addresses + this_addr = random_addresses[random.randint(0, len(random_adddresses))] + + if len(sys.argv) > 2: + if not sys.argv[2].startswith('--'): + function_name = sys.argv[2] + else: + function_name = None + + decompile(this_addr, function_name) + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..12001c4e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +coloredlogs +requests +web3 +timeout_decorator \ No newline at end of file diff --git a/tilde/LICENSE b/tilde/LICENSE new file mode 100644 index 00000000..00436921 --- /dev/null +++ b/tilde/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 eveem-org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/tilde/README.md b/tilde/README.md new file mode 100644 index 00000000..db4f11a2 --- /dev/null +++ b/tilde/README.md @@ -0,0 +1,12 @@ +# tilde + +requires python3.8: +``` +https://www.python.org/downloads/release/python-380a2/ +``` + +then: +``` +python3.8 run_test.py +``` + diff --git a/tilde/__init__.py b/tilde/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tilde/run_test.py b/tilde/run_test.py new file mode 100644 index 00000000..52e96602 --- /dev/null +++ b/tilde/run_test.py @@ -0,0 +1,4 @@ +import tilde +import test + +print('works!') \ No newline at end of file diff --git a/tilde/test.py b/tilde/test.py new file mode 100644 index 00000000..85833f69 --- /dev/null +++ b/tilde/test.py @@ -0,0 +1,97 @@ +# -*- encoding: tilde -*- + +#import tilde + + +''' + + exp ~ (1, some_var, (2,3), 'hello') + is the same as exp == (1, some_var, (2,3), 'hello') + + exp ~ (1, ...) + three dots at the end - any number of lasst parameters + + exp ~ (int, _, (2, _), 'hello') + int|str|list|tuple -> matches only things of that type + _ -> matches anything + + + exp ~ (int:x, _, (2, x+1), ...) + int:x -> matches to integer, and writes output to x + x+1 -> matches to x set before + equivalent to: + type(exp) == tuple and len(exp)>=3 and x:=exp[0] and and exp[2][0] == 0 and exp[2][1]==x+1 + + exp ~ (:x, _, :y, ...) + :var_name - matches anything and writes to :x + + +''' + +exp = ('something', 1,0,3) + +res = exp ~ ('something', 1, 0 , ... ) + +EXPR = ('mask_shl', 255, 1, -1, ('and', ('storage', 1, 2, 3), ('add', 1, 2))) +print(EXPR ~ ('mask_shl', 255, 1, -1, ('and', ('storage', ...), ('add', ...)))) + + +#print(res) + +exit() + +scores = [ + ('John', 'chess', 10), + ('Alice', 'chess', 20), + ('Bob', 'sudoku', 500), + ('Arnold', 'backgammon', 250), +] + +for s in scores: + + if s ~ (:name, 'chess', :score) and score > 10: + print(name, 'is good at chess:', score) + + if s ~ (:name, :game, :score) and game != 'chess': + print(f'{name} is good at {game}: {score}') + + +''' + + test2 + +''' + +def run(test, test2): + + if test ~ (int:x, _, x + 2): + print('first:', x) + + elif test ~ (:x, ...) and \ + test2 ~ (_, (x+1, :y), ...): + + print('second: ', x, y) + + elif test2 ~ (str:x, (2, y), x + 'a', int:y): + print('third:', x, y) + + else: + print('fail') + +test = (1,2,3) +test2 = ('hah',(2,3),'hahx') + +run(test, test2) +# first: 1 + +test = (1,2,1) +test2 = ('hah',(2,3),'hahx') + +run(test, test2) +# second: 1 3 + +test = (9,0,0) +test2 = ('hah',(2, 3),'haha', 3) + +run(test, test2) +# third: hah 3 \ No newline at end of file diff --git a/tilde/tilde.py b/tilde/tilde.py new file mode 100644 index 00000000..564314aa --- /dev/null +++ b/tilde/tilde.py @@ -0,0 +1,208 @@ +from tokenize import tokenize, untokenize, NUMBER, STRING, NAME, OP, NL, NEWLINE +from io import BytesIO + +''' + python pattern matching + + exp ~ (int, :x, int:y, z, 99, ...) + exp ~ ('sth', *rest) + exp ~ (int:x, x + 5) + exp ~ ('sth', ('sth', *rest)) + + usage: + - in the main project file do 'import tilde.tilde' + - every module imported after that can have '# coding: tilde' at the beginning and use tilde + + missing features: + - matching lists + - breaks now when there is a function call inside of an exp (e.g. ~ (sth(sth))) +''' + +import tokenize + +def translate(readline): + prev_type = None + prev_name = None + tokens = [] + for t in tokenize.tokenize(readline): + tokens.append((t[0], t[1], t[2][0], t[3][0])) + + last_line = 1 + + while len(tokens) > 0: + type, name, line_no, end_line_no = tokens.pop(0) + + if type in (NL, NEWLINE): + last_line += 1 + + if type == STRING and '\n' in name: + last_line = end_line_no + + while line_no > last_line: + yield NL, "\\\n" + last_line += 1 + + if name == "tilde": + yield type, 'utf-8' + + if prev_type == tokenize.NAME and type == tokenize.OP and name == '~': + var = name + + type, name = tokens.pop(0)[:2] + assert (type, name) == (tokenize.OP, '(') + par_count = 1 + out = [(type, name)] + while par_count > 0: + type, name = tokens.pop(0)[:2] + out.append((type, name)) + if (type, name) == (tokenize.OP, '('): + par_count += 1 + if (type, name) == (tokenize.OP, ')'): + par_count -= 1 + + tuples = make_tuples(out) + + rule_0 = 'True' # f'(__x := {prev_name}) or True' + ruleset = [rule_0] + make_ruleset(tuples, prev_name) + + out_str = " and ".join(f'({r})' if r != 'is not None' else r for r in ruleset) + yield STRING, ' is not None and (' + out_str + ')' + + else: + yield type, name + + prev_type = type + prev_name = name + + +def make_ruleset(tuples, base): + + res = [] + res.append(f'type({base}) == tuple') + tuples = list(tuples) + + last = tuples[-1] + if last[0] == '...' or last[0] == '*': + res.append(f'len({base}) >= {len(tuples)-1}') # ... means 0 or more ending args + if last[1] is not None: + res.append(f'({last[1]} := {base}[{len(tuples)-1}:]) or True') # copy all the rest into the last name + tuples.pop() + + else: + res.append(f'len({base}) == {len(tuples)}') + + for idx, t in enumerate(tuples): + if t == (None, None, None): + continue + + el_id = f'{base}[{idx}]' + if t[0] is not None and t[0] != '~': + assert t[0]!='...' # can only be at the end + res.append(f'type({el_id}) == {t[0]}') + + if type(t[1]) == str: + res.append(f'({t[1]} := {el_id}) or True') # otherwise it fails for 0 + elif type(t[1]) == tuple: + pass # skipping for now + + if t[2] is not None: + res.append(f'{el_id} == {t[2]}') + + for idx, t in enumerate(tuples): + el_id = f'{base}[{idx}]' + if t[0] == '~': +# res.append(f'__x := {el_id}') + res.extend(make_ruleset(t[1], el_id)) + + return res + # first find all nontuples + + +def make_tuples(out): + assert out[0] == (OP, '(') + out.pop(0) + + res = [] + exp = [] + while len(out) > 0: + el = out[0] + + if el == (OP, '('): + res.append(('~',make_tuples(out),None)) + elif el == (OP, ')'): + out.pop(0) + if len(exp)> 0: + res.append(make_exp(exp)) + return tuple(res) + elif el == (OP, ','): + out.pop(0) + if len(exp)>0: + res.append(make_exp(exp)) + exp = [] + else: + out.pop(0) + exp.append(el) + + + assert False + +def make_exp(exp): + check_type, out_name, concrete = None, None, None + + if len(exp) == 1 and exp[0][1] == '_': + return (None, None, None) + if len(exp) == 2 and exp[0][1] == '*' and exp[1][0] == NAME: + exp = [(OP, '...'), (OP,':'), exp[1]] + + if exp[0][1] in ('...', 'int', 'str', 'list', 'tuple'): + check_type = exp[0][1] + exp.pop(0) + + if len(exp) == 2 and exp[0][1] == ':': + out_name = exp[1][1] + + elif len(exp)>0: + concrete = tokenize.untokenize(exp)#.decode() + + return (check_type, out_name, concrete) + +def untilde(x): + return str(tokenize.untokenize(translate(BytesIO(x.encode('utf-8')).readline)).decode()) + +def tilde_decode(s): + x = BytesIO(s).read().decode('utf-8') + assert x[0] == '#' + x = x[1:] + x = untilde(x.replace('tilde','xilde')) + x = '# '+x.replace('xilde', 'tilde') + + return x, len(x) + +''' + + register `tilde` codec + +''' + +import codecs, io, encodings +from encodings import utf_8 + +class StreamReader(utf_8.StreamReader): + def __init__(self, *args, **kwargs): + codecs.StreamReader.__init__(self, *args, **kwargs) + data = tokenize.untokenize(translate(self.stream.readline)) + self.stream = i.StringIO(data) + +def search_function(s): + if s!='tilde': return None + utf8=encodings.search_function('utf8') # Assume utf8 encoding + return codecs.CodecInfo( + name='tilde', + encode = utf8.encode, + decode = tilde_decode, + incrementalencoder=utf8.incrementalencoder, + incrementaldecoder=utf8.incrementaldecoder, + streamreader=StreamReader, + streamwriter=utf8.streamwriter) + +codecs.register(search_function) \ No newline at end of file diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 00000000..a3e721ca --- /dev/null +++ b/utils/__init__.py @@ -0,0 +1 @@ +import tilde.tilde \ No newline at end of file diff --git a/utils/helpers.py b/utils/helpers.py new file mode 100644 index 00000000..bd3da5b6 --- /dev/null +++ b/utils/helpers.py @@ -0,0 +1,594 @@ +# coding: tilde +import string + +from copy import deepcopy, copy +from .profiler import * + +import os +import re + +COLOR_HEADER = '\033[95m' +COLOR_BLUE = '\033[94m' +COLOR_OKGREEN = '\033[92m' +COLOR_WARNING = '\033[93m' +FAIL = '\033[91m' +ENDC = '\033[0m' +COLOR_BOLD = '\033[1m' +COLOR_UNDERLINE = '\033[4m' +COLOR_GREEN = '\033[32m' +COLOR_GRAY = '\033[38;5;8m' + +''' + slowly refactoring into low-caps names, + the issue is that some names (e.g. `fail`) may + cause conflicts, but then they happen so often that + it's still better for readability than shutting them in + a namespace + +''' + + +colors = { + # code_ANSI #code_html_rgb + "\033[95m": "235, 97, 247", # COLOR_HEADER + "\033[91m": "236, 89, 58", # fail + "\033[38;5;8m": "111, 110, 111", # gray + "\033[32m": "107, 194, 76", # green + "\033[93m": "239, 236, 84", # warning + "\033[92m": "119, 232, 81", # okgreen + "\033[94m": "184, 90, 190", # "blue" +} + +def convert(text): + + for asci, html in colors.items(): + text = text.replace(asci, '') + + text = text.replace("\033[1m", '') + + text = re.sub( + r"»#(.*)\n", '#\\1\n', text + ) + + text = text.replace("»", "»") + + text = text.replace("\033[0m", "") + + return text + + + +class C(): + header = '\033[95m' + blue = '\033[94m' + okgreen = '\033[92m' + warning = '\033[93m' + red = '\033[91m' + bold = '\033[1m' + underline = '\033[4m' + green = '\033[32m' + gray = '\033[38;5;8m' + endc = '\033[0m' + fail = '\033[91m' + end = endc + + every = set([header, blue, okgreen, warning, red, bold, underline, green, gray, endc]) + +def color(exp, color, add_color=True): + if add_color: + if exp == "": + return '' + + return color + exp + C.endc + else: + return str(exp) + +colorize = color # alias, refactoring this into `col` slowly, but such a short name = conflicts? + +def clean_color(s): + for c in C.every: + s = s.replace(c, '') + + return s + +precompiled = { + 1: "erecover", # msg_hash =, v = , r = , s = + 2: "sha256hash", + 3: "ripemd160hash", + #4: "memcpy", -- handled separately + 5: "bigModExp", + 6: "bn256Add", + 7: "bn256ScalarMul", + 8: "bn256Pairing", +} + +precompiled_var_names = { + 1: "signer", + 2: "hash", + 3: "hash", + #4: "memcpy", -- handled separately + 5: "mod_exp", + 6: "bn_add", + 7: "bn_scalar_mul", + 8: "bn_pairing", +} + + +def cache_fname(addr, ext, dir='cache_pan'): + dir_name = dir + '/' + addr[:5] + '/' + + if not os.path.isdir(dir): + os.mkdir(dir) + + if not os.path.isdir(dir_name): + os.mkdir(dir_name) + + return dir_name + addr + '.' + ext + +def before_after(func): + + def wrapper(*args): + res = func(*args) + + print('in', args) + print(func.__name__) + print('out', res) + return res + + return wrapper + +cached_dict = {} +def cached(func): + name = func.__name__ + + if name not in cached_dict: + cached_dict[name] = {} + + def wrapper(*args): + try: + if args in cached_dict[name]: + return cached_dict[name][args] + except: + pass + + ret = func(*args) + try: + cached_dict[name][args] = ret + except: + pass + + return ret + + return wrapper + +ARRAY_OPCODES = ['call.data', 'ext_call.return_data', 'delegate.return_data', 'callcode.return_data', 'staticcall.return_data', 'code.data'] + +def assure_dir_exists(dir_name): + if dir_name[-1:] == '/': + dir_name = dir_name[:-1] + + dir_so_far = '' + for d in dir_name.split('/'): + dir_so_far += d + '/' + if not os.path.isdir(dir_so_far): + os.mkdir(dir_so_far) + + assert os.path.isdir(dir_name), dir_name + +def is_array(op): + return op in ARRAY_OPCODES + +# car/cdr convention borrowed from functional languages. shorter than 'first', 'rest' +def car(exp): + if type(exp) in (tuple, list) and len(exp) > 0: + return exp[0] + else: + return None + +def cdr(exp): + if type(exp) in (tuple, list) and len(exp) > 0: + return exp[1:] + else: + return None + + +def rewrite_trace(trace, f): + res = [] + + for line in trace: + if opcode(line) != 'if': + res.extend(f(line)) + else: + cond, if_true, if_false = line[1:] + res.append(('if', cond, rewrite_trace(if_true, f), rewrite_trace(if_false, f))) + + return res + + +def rewrite_trace_full(trace, f): + res = [] + + for line in trace: + if line ~ ('if', :cond, :if_true, :if_false): + if_true = rewrite_trace_full(if_true, f) + if_false = rewrite_trace_full(if_false, f) + res.append(('if', cond, if_true, if_false)) + + elif line ~ ('while', :cond, :tr, :jds, :setvars): + tr = rewrite_trace_full(tr, f) + res.append(('while', cond, tr, jds, setvars)) + + else: + res.extend(f(line)) + + return res + +def rewrite_trace_multiline(trace, f, number): + res = [] + idx = 0 + + while idx < len(trace): + if idx + number <= len(trace): + if (r2 := f(trace[idx:idx+number])) is not None: + res.extend(r2) + idx += number + continue + + line = trace[idx] + + if line ~ ('if', :cond, :if_true, :if_false): + if_true = rewrite_trace_multiline(if_true, f, number) + if_false = rewrite_trace_multiline(if_false, f, number) + res.append(('if', cond, if_true, if_false)) + + elif line ~ ('while', :cond, :tr, :jds, :setvars): + tr = rewrite_trace_multiline(tr, f, number) + res.append(('while', cond, tr, jds, setvars)) + + else: + res.append(line) + + idx += 1 + + return res + +test_trace = [ + 'a','b','c','d' + ] +def test_f(lines): + if lines == ['b','c']: + return ['q','w','e'] + +assert rewrite_trace_multiline(test_trace, test_f, 2) == ['a','q','w','e','d'] + +def rewrite_trace_ifs(trace, f): + res = [] + + for line in trace: + if line ~ ('if', :cond, :if_true, :if_false): + new_lines = f(line) + if new_lines == [line]: + if_true = rewrite_trace_ifs(if_true, f) + if_false = rewrite_trace_ifs(if_false, f) + res.append(('if', cond, if_true, if_false)) + else: + res.extend(rewrite_trace_ifs(new_lines, f)) + + elif line ~ ('while', :cond, :tr, :jds, :setvars): + tr = rewrite_trace_ifs(tr, f) + res.append(('while', cond, tr, jds, setvars)) + + else: + res.extend(f(line)) + + return res + +def print_cached(): + print(repr(cached_dict)) + +class EasyCopy: + + def __init__(self): + self.value = None + + def __copy__(self): + cls = self.__class__ + result = cls.__new__(cls) + result.__dict__.update(self.__dict__) + return result + + def __deepcopy__(self, memo): + cls = self.__class__ + result = cls.__new__(cls) + memo[id(self)] = result + for k, v in self.__dict__.items(): + setattr(result, k, deepcopy(v, memo)) + return result + + def light_copy(self): + cls = self.__class__ + result = cls.__new__(cls) + for k, v in self.__dict__.items(): + setattr(result, k, copy(v)) + return result + +def get_op(exp, op, default=None): + # finds the first expression within expression that has a given opcode, + # or returns None + + if opcode(exp) == op: + return exp + + if type(exp) in (list, tuple): + for e in exp[1:]: + if x := get_op(e, op, default): + return x + + return default + +def find_op_list(exp, op, default=None): + # finds a list of expressions with a given opcode + + if opcode(exp) == op: + return [exp] + + res = [] + + if type(exp) in (list, tuple): + for e in exp: + res.extend(find_op_list(e, op, default)) + + return res + +def replace_lines(trace, f): + res = [] + + for line in trace: + if line ~ ('while', :cond, :path, :jds, :setvars): + cond = f(cond) + path = replace_lines(path, f) + setvars = f(setvars) + + res.append(('while', cond, path, jds, setvars)) + + elif line ~ ('if', :cond, :if_true, :if_false): + res.append(('if', f(cond), replace_lines(if_true, f), replace_lines(if_false, f))) + + else: + res.append(f(line)) + + return res + + +def walk_trace(trace, f): + res = [] + for line in trace: + if type(line) == list: + res += walk_trace(line, f) + continue + + res += f(line) + if opcode(line) == 'if': + if_true, if_false = line[2], line[3] + res += walk_trace(if_true, f) + res += walk_trace(if_false, f) + + return res + + +def all_concrete(*args): + for a in args: + if type(a) not in (int, float): + return False + return True + +def cleanup_mul_1(exp): + if type(exp) == list: + return [cleanup_mul_1(e) for e in exp] + + if type(exp) != tuple: + return exp + + if exp[:2] == ('mul', 1) and len(exp) == 3: + return exp[2] + + return tuple(cleanup_mul_1(e) for e in exp) + +def pretty_bignum(num): + if type(num) != int: + return num + + if num == 0x19457468657265756d205369676e6564204d6573736167653a0a333200000000: + return "'\\x19Ethereum Signed Message:\\n32'" # common, todo correct parsing of RLP encoding + # https://ethereum.stackexchange.com/questions/33349/unable-to-reproduce-keccak256-hello-world-hash-within-evm + # https://github.com/ethereum/go-ethereum/issues/14794 + # found in EtherDelta + s = '' # todo: mask? + orig_num = num + while num>0: + ch = chr(num % 0x100) + if ch not in (string.printable+string.whitespace) and num % 0x100 != 0: + return orig_num + if num % 0x100 != 0: + s = ch + s + num = num // 0x100 + + return f"'{s}'" + +def parse_data(value): + # converts binary data into a human-readable form + + if len(value) == 32*2+2: + value = int(value, 16) + + if value > 10**9 and value // 10**9 != 0: + value = hex(value) + else: + value = nice_int(value) + + return value + + else: + value = value[2:] + + out = [] + while len(value) > 0: + out.append(int('0x'+value[:32*2], 16)) + value = value[32*2:] + + out2 = [] + for o in out: + if o > 10**9 and o // 10**9 != 0: + out2.append(hex(o)) + else: + out2.append(o) + + if len(out2) == 3 and out2[0] == 32 and \ + out2[2][-64+out2[1]*2:] == '0'*(64-out2[1]*2): + out2 = pretty_bignum(int(out2[2][:out2[1]*2+2],16)) + + return str(out2) + +def to_exp2(num): # checks if num is a power of 2, and if so, returns to which power + if type(num) != int: + return None + + if num < 0: # speed optimisation + return None + + for i in range(256): + if num == 2 ** i: + return i + + return None + + +def opcode(exp): + if type(exp) != tuple or len(exp)==0: + return None + else: + return exp[0] + +def padded_hex(given_int, given_len): + if given_int >= 0: + hex_result = hex(given_int)[2:] # remove '0x' from beginning of str + else: + hex_result = hex(given_int)[3:] # remove '0x' from beginning of str + + num_hex_chars = len(hex_result) + extra_zeros = '0' * (given_len - num_hex_chars) # may not get used.. + + return ('0x' + hex_result if num_hex_chars == given_len else + '?' * given_len if num_hex_chars > given_len else + '0x' + extra_zeros + hex_result if num_hex_chars < given_len else + None) + +def find_f_set(exp, f): + ret = f(exp) + + if type(exp) in (list, tuple): + for e in exp: + ret = ret.union(find_f_set(e, f)) + + return ret + + +def find_f_list(exp, f, default=None): + ret = f(exp) or [] + + if type(exp) in (list, tuple): + for e in exp: + ret.extend(find_f_list(e, f)) + + return ret + +def find_f(exp, f, default=None): + if f(exp) is not None: + return f(exp) + + if type(exp) in (list, tuple): + for e in exp: + x = find_f(e, f) + if x is not None: + return x + + return default + + return default + +def tuplify(exp): + if type(exp) != list: + return exp + + return tuple(tuplify(e) for e in exp) + +assert tuplify(['mul', 1, ['qwe', 10]]) == ('mul', 1, ('qwe', 10)) + +def hashable(v): + """Determine whether `v` can be hashed.""" + try: + hash(v) + except TypeError: + return False + return True + +assert hashable((1,2,[1,3])) == False +assert hashable((1,2,(1,3))) == True + +def contains(exp, what): + if exp == what: + return True + + elif type(exp) in (list, tuple): + for e in exp: + if contains(e, what): + return True + + return False + + +def replace_f(in_exp, f): + if type(in_exp) not in (tuple, list): + in_exp = f(in_exp) + return in_exp + + keep_type = type(in_exp) + res = keep_type(replace_f(e, f) for e in in_exp) + + return f(res) + +def replace(in_exp, what, by_what): + if in_exp == what: + return by_what + + if type(in_exp) not in (tuple, list): + return in_exp + + res = tuple() + for e in in_exp: + res += (replace(e, what, by_what), ) + + if type(in_exp) == list: + res = list(res) + + return res + +def replace_f_stop(in_exp, f): + """Like replace_f, but the function returns None when no replacement needs + to be made. If it returns something we replace it and stop.""" + modified_in_exp = f(in_exp) + if modified_in_exp is not None: + return modified_in_exp + + if type(in_exp) not in (tuple, list): + return in_exp + + res = tuple() + for e in in_exp: + res += (replace_f_stop(e, f), ) + + if type(in_exp) == list: + res = list(res) + + return res + diff --git a/utils/opcode_dict.py b/utils/opcode_dict.py new file mode 100644 index 00000000..39f53909 --- /dev/null +++ b/utils/opcode_dict.py @@ -0,0 +1,358 @@ +''' + contains opcode_dict and stack_diffs dictionaries + + opcode_dict is used by the loader to disassemble a contract + stack_diffs is used by vm.apply_stack to test the proper parsing of opcodes + +''' + +opcode_dict = { + # + # Stop and Arithmetic + # + 0x00: 'stop', + 0x01: 'add', + 0x02: 'mul', + 0x03: 'sub', + 0x04: 'div', + 0x05: 'sdiv', + 0x06: 'mod', + 0x07: 'smod', + 0x08: 'addmod', + 0x09: 'mulmod', + 0x0a: 'exp', + 0x0b: 'signextend', + + + # + # Comparison and Bitwise Logic + # + 0x10: 'lt', + 0x11: 'gt', + 0x12: 'slt', + 0x13: 'sgt', + 0x14: 'eq', + 0x15: 'iszero', + 0x16: 'and', + 0x17: 'or', + 0x18: 'xor', + 0x19: 'not', + 0x1a: 'byte', + 0x1b: 'shl', + 0x1c: 'shr', + 0x1d: 'sar', + + + # + # Sha3 + # + 0x20: 'sha3', + + + # + # Environment Information + # + 0x30: 'address', + 0x31: 'balance', + 0x32: 'origin', + 0x33: 'caller', + 0x34: 'callvalue', + 0x35: 'calldataload', + 0x36: 'calldatasize', + 0x37: 'calldatacopy', + 0x38: 'codesize', + 0x39: 'codecopy', + 0x3a: 'gasprice', + 0x3b: 'extcodesize', + 0x3c: 'extcodecopy', + 0x3d: 'returndatasize', + 0x3e: 'returndatacopy', + 0x3f: 'extcodehash', + + # + # Block Information + # + 0x40: 'blockhash', + 0x41: 'coinbase', + 0x42: 'timestamp', + 0x43: 'number', + 0x44: 'difficulty', + 0x45: 'gaslimit', + + + # + # Stack, Memory, Storage and Flow Operations + # + 0x50: 'pop', + + 0x51: 'mload', + 0x52: 'mstore', + 0x53: 'mstore8', + 0x54: 'sload', + 0x55: 'sstore', + 0x56: 'jump', + 0x57: 'jumpi', + 0x58: 'pc', + 0x59: 'msize', + 0x5a: 'gas', + 0x5b: 'jumpdest', + + + # + # Push Operations + # + 0x60: 'push1', + 0x61: 'push2', + 0x62: 'push3', + 0x63: 'push4', + 0x64: 'push5', + 0x65: 'push6', + 0x66: 'push7', + 0x67: 'push8', + 0x68: 'push9', + 0x69: 'push10', + 0x6a: 'push11', + 0x6b: 'push12', + 0x6c: 'push13', + 0x6d: 'push14', + 0x6e: 'push15', + 0x6f: 'push16', + 0x70: 'push17', + 0x71: 'push18', + 0x72: 'push19', + 0x73: 'push20', + 0x74: 'push21', + 0x75: 'push22', + 0x76: 'push23', + 0x77: 'push24', + 0x78: 'push25', + 0x79: 'push26', + 0x7a: 'push27', + 0x7b: 'push28', + 0x7c: 'push29', + 0x7d: 'push30', + 0x7e: 'push31', + 0x7f: 'push32', + + + # + # Duplicate Operations + # + 0x80: 'dup1', + 0x81: 'dup2', + 0x82: 'dup3', + 0x83: 'dup4', + 0x84: 'dup5', + 0x85: 'dup6', + 0x86: 'dup7', + 0x87: 'dup8', + 0x88: 'dup9', + 0x89: 'dup10', + 0x8a: 'dup11', + 0x8b: 'dup12', + 0x8c: 'dup13', + 0x8d: 'dup14', + 0x8e: 'dup15', + 0x8f: 'dup16', + + + # + # Exchange Operations + # + 0x90: 'swap1', + 0x91: 'swap2', + 0x92: 'swap3', + 0x93: 'swap4', + 0x94: 'swap5', + 0x95: 'swap6', + 0x96: 'swap7', + 0x97: 'swap8', + 0x98: 'swap9', + 0x99: 'swap10', + 0x9a: 'swap11', + 0x9b: 'swap12', + 0x9c: 'swap13', + 0x9d: 'swap14', + 0x9e: 'swap15', + 0x9f: 'swap16', + + + # + # Logging + # + 0xa0: 'log0', + 0xa1: 'log1', + 0xa2: 'log2', + 0xa3: 'log3', + 0xa4: 'log4', + + + # + # System + # + 0xf0: 'create', + 0xf1: 'call', + 0xf2: 'callcode', + 0xf3: 'return', + 0xf4: 'delegatecall', + 0xf5: 'create2', + 0xfa: 'staticcall', + 0xfd: 'revert', + 0xfe: 'invalid', + 0xff: 'selfdestruct', + +} + +# stack_diffs is used when testing if an opcode +# was parsed correctly in vm.apply_stack + +stack_diffs = { + "create": -2, + "create2": -3, + "invalid": None, + "add": -1, + "addmod": -2, + "sub": -1, + "mod": -1, + "smod": -1, + "mul": -1, + "mulmod": -2, + "div": -1, + "sdiv": -1, + "exp": -1, + "signextend": -1, + "shl": -1, + "shr": -1, + "sar": -1, + "blockhash": 0, + "coinbase": 1, + "selfdestruct": -1, + "timestamp": 1, + "number": 1, + "difficulty": 1, + "gaslimit": 1, + "lt": -1, + "gt": -1, + "slt": -1, + "sgt": -1, + "eq": -1, + "iszero": 0, + "and": -1, + "or": -1, + "xor": -1, + "not": 0, + "byte": -1, + "balance": 0, + "origin": 1, + "address": 1, + "call": -6, + "callcode": -6, + "delegatecall": -5, + "staticcall": -5, + "caller": 1, + "callvalue": 1, + "calldataload": 0, + "calldatasize": 1, + "calldatacopy": -3, + "codesize": 1, + "codecopy": -3, + "gasprice": 1, + "extcodesize": 0, + "extcodehash": 0, + + "extcodecopy": -4, + "returndatasize": 1, + "returndatacopy": -3, + "dup_XX": 1, + "stop": 0, + "jump": -1, + "jumpi": -2, + "jumpdest": 0, + "pc": 1, + "gas": 1, + "log0": -2, + "log1": -3, + "log2": -4, + "log3": -5, + "log4": -6, + "mstore": -2, + "mstore8": -2, + "mload": 0, + "msize": 1, + "sha3": -1, + "pop": -1, + "push_XX": 1, + "sstore": -2, + "sload": 0, + "return": -2, + "revert": -2, + "assert_fail": 0, + "push": 1, + "dup": 1, + "swap": 0, + "push1": 1, + "push2": 1, + "push3": 1, + "push4": 1, + "push5": 1, + "push6": 1, + "push7": 1, + "push8": 1, + "push9": 1, + "push10": 1, + "push11": 1, + "push12": 1, + "push13": 1, + "push14": 1, + "push15": 1, + "push16": 1, + "push17": 1, + "push18": 1, + "push19": 1, + "push20": 1, + "push21": 1, + "push22": 1, + "push23": 1, + "push24": 1, + "push25": 1, + "push26": 1, + "push27": 1, + "push28": 1, + "push29": 1, + "push30": 1, + "push31": 1, + "push32": 1, + "dup1": 1, + "swap1": 0, + "dup2": 1, + "swap2": 0, + "dup3": 1, + "swap3": 0, + "dup4": 1, + "swap4": 0, + "dup5": 1, + "swap5": 0, + "dup6": 1, + "swap6": 0, + "dup7": 1, + "swap7": 0, + "dup8": 1, + "swap8": 0, + "dup9": 1, + "swap9": 0, + "dup10": 1, + "swap10": 0, + "dup11": 1, + "swap11": 0, + "dup12": 1, + "swap12": 0, + "dup13": 1, + "swap13": 0, + "dup14": 1, + "swap14": 0, + "dup15": 1, + "swap15": 0, + "dup16": 1, + "swap16": 0 + +} \ No newline at end of file diff --git a/utils/prettify.py b/utils/prettify.py new file mode 100644 index 00000000..274f250f --- /dev/null +++ b/utils/prettify.py @@ -0,0 +1,1287 @@ +# coding: tilde +from utils.helpers import COLOR_HEADER, COLOR_BLUE, COLOR_OKGREEN, COLOR_WARNING, FAIL, ENDC, COLOR_BOLD, COLOR_UNDERLINE, COLOR_GREEN, COLOR_GRAY + +import logging +import core.arithmetic as arithmetic +from utils.helpers import opcode, padded_hex, pretty_bignum, all_concrete, replace_lines, replace_f +from utils.helpers import clean_color, C, is_array + +from core.algebra import lt_op, mul_op, minus_op, ge_zero, safe_ge_zero, sub_op, add_op, apply_mask, safe_le_op, to_bytes + +from copy import deepcopy + +from core.arithmetic import simplify_bool, is_zero + +from core.masks import get_bit, mask_to_type + +from utils.helpers import precompiled + +from utils.helpers import colorize, to_exp2 + +from utils.signatures import get_param_name + +from functools import partial + +from pano.loader import Loader + +logger = logging.getLogger(__name__) + + +def make_ast(trace): + def store_to_set(line): + if line ~ ('store', :size, :off, :idx, :val): + return ('set', ('stor', size, off, idx), val) + else: + return line + + def mask_storage(exp): + if exp ~ ('stor', :size, :off, :idx): + return ('mask_shl', size, 0, 0, exp) + else: + return exp + + trace = replace_lines(trace, store_to_set) + trace = replace_f(trace, mask_storage) + + return trace + + + +def format_exp(exp): + if type(exp) == str: + return f'"{exp}"' + if type(exp) == int: + if exp > 10 ** 6 and exp % 10 ** 6 != 0: + return hex(exp) + else: + return str(exp) + elif type(exp) != list: + return str(exp) + else: + if len(exp) == 0: + return COLOR_GRAY + "[]" + ENDC + if type(opcode(exp)) == list: + return ( + COLOR_GRAY + + "[" + + ENDC + + f"{COLOR_GRAY}, {ENDC}".join([format_exp(e) for e in exp]) + + COLOR_GRAY + + "]" + + ENDC + ) + else: + return ( + COLOR_GRAY + + "[" + + ENDC + + f"{COLOR_GRAY}, {ENDC}".join( + [opcode(exp)] + [format_exp(e) for e in exp[1:]] + ) + + COLOR_GRAY + + "]" + + ENDC + ) + +def pprint_repr(trace, indent=0): + for line in trace: + + if opcode(line) == "if": + cond, if_true, if_false = line[1:] + print(indent * " ", f"[if, {format_exp(cond)}, [") + pprint_repr(if_true, indent + 2) + print(indent * " ", "],[") + pprint_repr(if_false, indent + 2) + print(indent * " ", "] ") + + elif opcode(line) == "while": + cond, tr = line[1], line[2] + print(indent * " ", f"[while, {format_exp(cond)}, [") + pprint_repr(tr, indent + 2) + print(indent * " ", "], ") + + else: + print(indent * " ", format_exp(line) + f"{COLOR_GRAY}, {ENDC}") + +''' +def pprint_repr(exp): + print(repr(exp)) + return + print(pretty_repr(exp)) +''' + +def pretty_repr(exp, indent=0): + if type(exp) not in (tuple, list): + return repr(exp) + elif type(exp) == list: + res = ', \n'.join([' '*indent + pretty_repr(e, indent) for e in exp]) + res = indent*' '+'['+res[:-3]+']' + return res + elif type(exp) == tuple: + res = ', '.join([pretty_repr(e) for e in exp]) + if len(res) > 40 and exp ~ (:op, :first, *rest): + indent += len(pretty_repr(op)+', ')+1 + res = pretty_repr(op)+', '+pretty_repr(first, indent)+',\n' + for r in rest: + res += indent*' '+pretty_repr(r, indent)+', \n' + res = res[:-3] # removes ', \n' + + return '('+res+')' + elif type(exp) == list: + res = (',\n'+' '*indent).join([pretty_repr(e, indent) for e in exp]) + return f'[{res}]' + +#print(pretty_repr(('data', ('mem', ('range', ('add', 32, 'QQ', ('mask_shl', 251, 5, 0, ('add', 31, ('ext_call.return_data', 128, 32)))), 32)), 'yy', ('data', ('mem', ('range', ('add', 32, 'QQ'), ('ext_call.return_data', 128, 32))), ('mem', ('range', ('add', 96, 'QQ', ('mask_shl', 251, 5, 0, ('add', 31, ('ext_call.return_data', 128, 32))), ('ext_call.return_data', 128, 32)), 0)))))) +#exit() + +def pformat_trace(trace): + return '\n'.join(pprint_logic(trace)) + "\n\n" + +def pprint_trace(trace): + trace = make_ast(trace) + pprint_ast(trace) + +def pprint_ast(trace): + empty = True + + for l in pprint_logic(trace): + print(l) + empty = False + + if empty: + print(' stop') + print() + print() + +def pprint_logic(exp, indent=2): + INDENT_LEN = 4 + + if opcode(exp) == 'while': + if len(exp) == 5: + cond, path, jd, vars = exp[1], exp[2], exp[3], exp[4] + else: + cond, path = exp[1], exp[2] + jd, vars = None, [] + + for v in vars: + yield ' '*indent + list(pretty_line(('setvar', v[1],v[2]), add_color=True))[0] + + yield ' '*indent + COLOR_GREEN+ 'while ' + ENDC+prettify(cond, add_color=True, parentheses=False, rem_bool=True) + COLOR_GREEN+':'+ ENDC#+COLOR_GREEN+': # '+str(jd)+ENDC + if type(path) != list: + path = path.trace + + for l in pprint_logic(path, indent + INDENT_LEN): + yield l + + elif exp ~ ('require', :cond): + yield ' '*indent + 'require ' + prettify(exp[1], add_color=True, parentheses=False, rem_bool=True) + '' + + elif exp ~ ('if', :cond, :if_true): # one-sided ifs, only after folding + if len(if_true) == 1 and (first := if_true[0]) and \ + ((first == ('revert', 0)) or (first ~ ('invalid', ...))): + yield ' '*indent + 'require '+prettify(is_zero(exp[1]), add_color=True, parentheses=False, rem_bool=True) + else: + yield ' '*indent + 'if ' + prettify(exp[1], add_color=True, parentheses=False, rem_bool=True) + ':' + for l in pprint_logic(if_true, indent + INDENT_LEN): + yield l + + + elif exp ~ ('if', :cond, :if_true, :if_false): + if len(if_false) == 1 and (first := if_false[0]) and \ + ((first == ('revert', 0)) or \ + (first ~ ('invalid', ...))): + yield ' '*indent + 'require '+prettify(exp[1], add_color=True, parentheses=False, rem_bool=True) + + for l in pprint_logic(exp[2], indent): + yield l + + elif len(if_true) == 1 and (first := if_true[0]) and \ + ((first == ('revert', 0)) or \ + (first ~ ('invalid', ...))): + yield ' '*indent + 'require '+prettify(is_zero(exp[1]), add_color=True, parentheses=False, rem_bool=True) + + for l in pprint_logic(exp[3], indent): + yield l + + else: + yield ' '*indent + 'if ' + prettify(exp[1], add_color=True, parentheses=False, rem_bool=True) + ':' + + for l in pprint_logic(if_true, indent + INDENT_LEN): + yield l + ''' + while len(if_false) == 1 and opcode(if_false) == 'if' and len(if_false) == 4: + first = if_false[0] + assert first ~ ('if', :c, :i_t, :if_false) + + yield ' '*indent + 'elif ' + prettify(c, add_color=True, parentheses=False, rem_bool=True) + ':' + + for l in pprint_logic(i_t, indent + INDENT_LEN): + yield l''' + + yield ' '*indent + 'else:' + for l in pprint_logic(if_false, indent + INDENT_LEN): + yield l + + + elif type(exp) == list: + for idx, line in enumerate(exp): + if idx == len(exp)-1 and indent == 2 and line==('stop', ): + pass # don't print the last stop + else: + for l in pprint_logic(line, indent): + yield l + + elif opcode(exp) == 'or' and len(exp)>1: + yield ' '*indent + 'if' + for l in pprint_logic(exp[1], indent + INDENT_LEN): + yield l + + for line in exp[2:]: + yield ' '*indent + 'or' + for l in pprint_logic(line, indent + INDENT_LEN): + yield l + + else: + for l in pretty_line(exp): + yield ' '* indent + l + +def to_real_int(exp): + if type(exp) == int and get_bit(exp, 255): + return -arithmetic.sub(0, exp) + else: + return exp + +def pretty_line(r, add_color=True): + col = partial(colorize, add_color=add_color) + pret = partial(prettify, parentheses=False, add_color=add_color) + + if type(r) is str: + yield COLOR_GRAY + "# " + r + ENDC + +# elif r ~ ('jumpdest', ...): +# pass + + elif r ~ ('comment', :text): + yield COLOR_GRAY + "# " + prettify(text, add_color=False) + ENDC + + elif r ~ ('log', :params, *events): + # solidstamp and dao cover most of those cases + res_params = pretty_memory(params, add_color=False) + + for e in events: + if type(e) != int: + for e in events[1:]: + res_params = res_params + (prettify(e, add_color=False, parentheses=False), ) + events = [events[0]] + break + # breaks with more than one proper event + + + res_events = tuple(pretty_fname(e, add_color=False, force=True) for e in events) +# print(res_events) + res_events = tuple((x[:10] if x[:2] == '0x' else x) for x in res_events) + for e in res_events: + if '(' not in e: + yield col(f"log {e}{':' if len(res_params)>0 else ''} {', '.join(res_params)}", COLOR_GRAY) + else: + fname, fparams = e.split('(') + + assert fparams[-1] == ')' + fparams = fparams[:-1] + + fparams = fparams.split(', ') + + if fparams == [''] or len(res_params) == 0: + yield col(f'log {e}', COLOR_GRAY) + + elif len(fparams) == len(res_params): + p_list = [] + try: + for idx, ptype, pname in [f"{idx} {p}".split(' ') for idx, p in enumerate(fparams)]: + p_list.append((ptype, pname, res_params[int(idx)])) + except: + logger.warning(f'weird log {e} {fparams}') + yield(f'log {e}') + return + + + if len(p_list) == 1: + yield col(f"log {fname}({p_list[0][0]} {p_list[0][1]}={pret(p_list[0][2], add_color=False, parentheses=False)})", COLOR_GRAY) + else: + ind = len(f'log ') + first = p_list[0] + last = p_list[-1] + pline = lambda p: f'{p[0]} {p[1]}={pret(p[2], add_color=False, parentheses=False)}' # spaces around = not pep8 compliant + # but without them it was less readable + + yield col(f"log {fname}(", COLOR_GRAY)# + yield col(f" {pline(first)},", COLOR_GRAY) + + for p in p_list[1:-1]: + yield col(' '*ind + f"{pline(p)},", COLOR_GRAY) + + yield col(' '*ind + f"{pline(last)})", COLOR_GRAY) +# elif len(res_params) == 0: +# yield col(f'log {e}', COLOR_GRAY) + else: + yield col(f'log {e}:', COLOR_GRAY) + ind = ' ' * len(f'log {fname}(') + for p in res_params: + yield col(ind + p + ',', COLOR_GRAY) +# print(repr(len(fparams)), len(res_params)) + + elif r ~ ('callcode', :gas, :addr, :wei, :fname, :fparams): + fname = pretty_fname(fname, add_color=add_color) + + if type(addr) == int: + addr = hex(addr) + addr = prettify(addr, add_color = add_color) + gas = prettify(gas, parentheses = False, add_color = add_color) + fparams = pretty_memory(fparams, add_color = add_color) + + if fname is not None: + + if type(fname) == str: + fname = pretty_fname(fname, add_color = add_color) + yield f"{COLOR_WARNING}codecall{ENDC} {addr}.{fname} with:" + + else: + yield f"{COLOR_WARNING}codecall{ENDC} {addr} with:" + yield " funct " + prettify(fname, add_color=add_color) + + else: + yield f"{COLOR_WARNING}codecall{ENDC} {addr} with:" + + if wei != 0: + wei = prettify(wei, parentheses=False, add_color=add_color) + yield f" value {wei} {COLOR_GRAY}wei{ENDC}" + + yield f" gas {gas} {COLOR_GRAY}wei{ENDC}" + + if fparams is not None: + yield " args {}".format(', '.join(fparams)) + + elif r ~ ('delegatecall', :gas, :addr, :fname, :fparams): + fname = pretty_fname(fname, add_color=add_color) + + if type(addr) == int: + addr = hex(addr) + addr = prettify(addr, add_color = add_color) + gas = prettify(gas, parentheses = False, add_color = add_color) + fparams = pretty_memory(fparams, add_color = add_color) + + if fname is not None: + + if type(fname) == str: + fname = pretty_fname(fname, add_color = add_color) + yield f"{COLOR_WARNING}delegate{ENDC} {addr}.{fname} with:" + + else: + yield f"{COLOR_WARNING}delegate{ENDC} {addr} with:" + yield " funct " + prettify(fname, add_color=add_color) + + else: + yield f"{COLOR_WARNING}delegate{ENDC} {addr} with:" + + + yield f" gas {gas} {COLOR_GRAY}wei{ENDC}" + + if fparams is not None: + yield " args {}".format(', '.join(fparams)) + + elif r ~ ('selfdestruct', :addr): + yield col('selfdestruct(', COLOR_WARNING)+col(pret(addr, add_color=False, parentheses=False), FAIL)+col(')', COLOR_WARNING) + + elif r ~ ('precompiled', :var_name, :func_name, :params): + yield "{} = {}({}) {}".format(col(var_name, COLOR_BLUE), func_name, prettify(params, add_color=add_color, parentheses=False), + COLOR_GRAY+'# precompiled'+ENDC) + + elif r ~ ('create', :wei, :code): + yield f"create contract with {wei} wei" + yield f" code: {prettify(code)}" + + elif r ~ ('create2', :wei, :code, :salt): + yield f"create2 contract with {wei} wei" + yield f" salt: {prettify(salt)}" + yield f" code: {prettify(code)}" + + + elif r ~ ('call', :gas, :addr, :wei, :fname, :fparams): + + if type(addr) == int: + if len(hex(addr)) > 22+2: + addr = padded_hex(addr, 40) # todo: padded hex + else: + addr = hex(addr) # if it's longer, padded hex returns '???' + + addr = pret(addr) + gas = pretty_gas(gas, wei, add_color) + + + if fname is None: + yield f"call {addr} with:" + + else: + fname = pretty_fname(fname, add_color=add_color) + + if fname == '0x0': + yield f"call {addr} with:" + + elif type(fname) == str: + yield f"call {addr}.{pret(fname)} with:" + + else: + yield f"call {addr} with:" + yield f" funct {pret(fname)}" + + if wei != 0: + wei = prettify(wei, parentheses=False, add_color=add_color) + yield f" value {wei} {COLOR_GRAY}wei{ENDC}" + + yield f" gas {gas} {COLOR_GRAY}wei{ENDC}" + + if fparams is not None: + fparams = pretty_memory(fparams, add_color=add_color) + yield " args {}".format(', '.join(fparams)) + + elif r ~ ('staticcall', :gas, :addr, :wei, :fname, :fparams): + + if type(addr) == int: + addr = hex(addr) + + addr = prettify(addr, add_color=add_color, parentheses=False) + gas = pretty_gas(gas, wei, add_color) + + if fname is not None: + fanme = fname + fname = pretty_fname(fname, add_color=add_color) + + if fname == '0x0': + yield f"static call {addr} with:" + elif type(fname) == str and fname != '0x0': + yield f"static call {addr}.{pret(fname)} with:" + else: + yield f"static call {addr} with:" + yield f" funct {pret(fname)}" + + else: + yield f"static call {addr} with:" + + yield f" gas {gas} {COLOR_GRAY}wei{ENDC}" + + if fparams is not None: + fparams = pretty_memory(fparams, add_color=add_color) + yield " args {}".format(', '.join(fparams)) + + elif r ~ ('label', :name, :setvars): + yield COLOR_GREEN + f'label {str(name)} setvars: {str(setvars)}' + ENDC + + elif r ~ ('goto', *rest): + yield COLOR_GREEN + f'goto {str(rest)}'+ENDC + + elif r ~ ('continue', :jd, :setvars): + for v in setvars: + yield str(list(pretty_line(v, add_color=True))[0]) + yield COLOR_GREEN + 'continue ' + ENDC # +str(jd)+ENDC + + elif r ~ ('setvar', ...): + yield prettify(r, add_color=add_color) + + elif r ~ ('setmem', ...): + yield prettify(r, add_color=add_color) + + elif r ~ ('set', :idx, :val): + if val ~ ('add', int:v, idx): + assert v != 0 + + if v == -1: + yield prettify(idx, add_color=add_color) + '--' + elif v == 1: + yield prettify(idx, add_color=add_color) + '++' + + elif v < 0: + yield prettify(idx, add_color=add_color) + ' -= ' + prettify(-v, add_color=add_color, parentheses=False) + else: + yield prettify(idx, add_color=add_color) + ' += ' + prettify(v, add_color=add_color, parentheses=False) + + elif val ~ ('add', idx, ('mul', -1, :v)): + yield prettify(idx, add_color=add_color) + ' -= ' + prettify(v, add_color=add_color, parentheses=False) + elif val ~ ('add', idx, :v): + yield prettify(idx, add_color=add_color) + ' += ' + prettify(v, add_color=add_color, parentheses=False) + elif val ~ ('add', ('mul', -1, :v), idx): + yield prettify(idx, add_color=add_color) + ' -= ' + prettify(v, add_color=add_color, parentheses=False) + elif val ~ ('add', :v, idx): + yield prettify(idx, add_color=add_color) + ' += ' + prettify(v, add_color=add_color, parentheses=False) + + else: + yield prettify(idx, add_color=add_color) + ' = ' + prettify(val, add_color=add_color, parentheses=False) + + elif r ~ ('stop', ...): + yield 'stop' + + elif r ~ ('undefined', *params): + yield COLOR_WARNING + '...' + ENDC + COLOR_GRAY + f' # unusual jump {params}, couldn\'t decompile, sorry' + ENDC + + elif r ~ ('invalid', *rest): + if len(rest) > 0: + yield "revert "# + COLOR_GRAY + f"# {rest}" + ENDC + else: + yield "revert" + + elif r ~ ('invalid', ...): + yield "revert " + (COLOR_GRAY + f"# {rest}" + ENDC) + + elif (r ~ ('revert', 0)) or \ + (r ~ ('revert', ('mem', 0, 0))): + + yield "revert" + + elif r ~ (:op, ('mem', ('range', :mem_idx, :mem_len))) and \ + op in ('revert', 'return'): + + if op == 'revert': + yield 'revert with memory' + else: + yield op + ' memory' + + if len ~ ('sub', :mem_until, mem_idx): + yield f" from {pret(mem_idx)}" + yield f" to {pret(mem_until)}" + else: + yield " from " + pret(mem_idx) + yield " " + col('len', COLOR_WARNING) + " " + pret(mem_len) + + elif r ~ (:op, :param) and op in ('return', 'revert'): + + if op == 'revert': + op = 'revert with' + + res_mem = pretty_memory(param, add_color=True) + ret_val = ', '.join(res_mem) + + if len(clean_color(ret_val)) < 120 or opcode(param) != 'data': + yield f'{op} {ret_val}' + else: + # split long returns into lines. e.g. kitties.getKitten, or kitties.tokenMetadata +# yield str(len(ret_val)) + res_mem = list(res_mem) + if res_mem[0] == '32': + res_mem.pop(0) + res_mem[0] = '32, ' + res_mem[0] # happens often, this is probably an array structure, + # and sole `32` in first line looks ugly + + yield f'{op} {res_mem[0]}, ' + for idx, l in enumerate(res_mem[1:]): + yield ' '*len(op) + ' ' + l + (',' if idx != len(res_mem) - 2 else '') + +# assert op == 'revert' +# yield "{} with {}".format(op, ret_val) # adding 'with' to make it more readable + + + elif r ~ ('store', :size, :off, :idx, :val): + stor_addr = prettify(('stor', size, off, idx), add_color=add_color) + stor_val = prettify(val, add_color=add_color, parentheses=False) + + yield "{} = {}".format(stor_addr, stor_val) + + elif type(r) == list and len(r) > 1: + yield "{} {}".format(r[0], ', '.join([prettify(x, True, False, add_color = add_color) for x in r[1:]])) + + elif type(r) == list: + yield str(r[0]) + + else: + yield str(r) + +def pretty_type(t): + + if t ~ ('def', :name, :loc, ('mask', :size, :off)): + return pretty_type(('def', name, loc, size)) + COLOR_GRAY + (f' offset {off}' if off > 0 else '') + ENDC + + elif t ~ ('def', :name, :loc, :bts): + if type(loc) == int and loc > 1000: + loc = hex(loc) + return f' {COLOR_GREEN}{name}{ENDC} is {pretty_type(bts)} {COLOR_GRAY}at storage {loc}{ENDC}' + + elif t ~ ('struct', 1): + return 'struct' + + elif t == 'struct': + return 'struct' + + elif t ~ ('struct', int:num): + return f'struct {num} bytes' + + elif t ~ ('array', :bts): + return f'array of '+pretty_type(bts) + + elif t ~ ('mapping', :bts): + return f'mapping of '+pretty_type(bts) + + elif type(t) == int: + return mask_to_type(t, force=True) + + else: + assert False, f'unknown type {t}' + +def pretty_stor(exp, add_color=True): + col = partial(colorize, color=COLOR_GREEN, add_color=add_color) + stor = partial(pretty_stor, add_color=add_color) + pret = partial(prettify, parentheses=False, add_color=add_color) + + if exp ~ ('stor', ('length', :idx)): + return stor(idx) + col('.length') + + if exp ~ ('loc', :loc): + return col(f'stor_l{loc}') + + if exp ~ ('name', :name, :loc): + return col(name) + +# if exp ~ ('stor', (:op, :param)) and op in ('loc', 'name'): + # with top-level fields, it's just a different stor + # variable. with lower-level we treat it as a struct +# return stor((op, param)) + + if exp ~ ('stor', :loc): + # with top-level fields, it's just a different stor + # variable. with lower-level we treat it as a struct + return stor(loc) + + if exp ~ ('field', :off, :loc): + return stor(loc) + col(f'.field_{pret(off, add_color=False)}') + + if exp ~ ('type', :size, :loc): + if size == 256: + # prettify removes 256 masks by default, force it + return col('uint256(', color=COLOR_GRAY) + stor(loc) + col(')', color=COLOR_GRAY) + else: + return pret(('mask', size, 0, stor(loc))) + + def pr_idx(idx): + if idx ~ ('data', *terms): + return col('][').join([pret(t) for t in terms]) + else: + return pret(idx) + + if exp ~ ('map', :idx, :var): + return stor(var) + col('[') + pr_idx(idx) + col(']') + + if exp ~ ('array', ('mul', int:_, :idx), :var): + exp = ('array', idx, var) # nasty hack to not display storage[2*idx] for storages + # that are structs + # this should be handled in sparser really +# return stor(var) + col('[') + pr_idx(idx) + col(']') + + + if exp ~ ('array', :idx, :var): + return stor(var) + col('[') + pr_idx(idx) + col(']') + + if exp ~ ('length', :var): + return stor(var) + col('.length') + + if exp ~ ('stor', :loc): + return col('stor[') + pret(loc) + col(']') + + if exp ~ ('stor', :size, :off, :loc): + return pret(('mask', size, off, col('stor[') + pret(loc) + col(']'))) + + return col('stor[') + pret(exp) + col(']') + + +def pretty_num(exp, add_color): + col = partial(colorize, add_color=True) + + if type(exp) == float: + if exp - int(exp) == 0: + exp = int(exp) + + if type(exp) == int and exp > 8**50: + return hex(exp) # dealing with binary data probably, usually in call code - display in hex + + if type(exp) == int and exp !=0: + count = 18 + while count >= 9: + + if exp % (10**count) == 0: + if exp // (10**count) == 1: + return f'10^{count}' + else: + return f'{exp // (10**count)} * 10^{count}' + + count -= 1 + + count = 6 + if exp % (10**count) == 0: + if exp // (10**count) == 1: + return f'10^{count}' + else: + return f'{exp // (10**count)} * 10^{count}' + + if type(exp) == int: + if try_fname(exp, add_color) != None: + return try_fname(exp, add_color) + + elif type(exp) == int and (exp&2**256-1)<8**30: # if it's larger than 30 bytes, it's probably + # an address, not a negative number + return str(to_real_int(exp)) + + elif exp>0: + return hex(exp) + + else: + return str(exp) + + # print('warn: weird float exp', exp) + return str(exp) + + +def prettify(exp, rem_bool=False, parentheses=True, top_level=False, add_color=False): + + col = partial(colorize, add_color=add_color) + pret = partial(prettify, add_color=add_color, parentheses=False) + + if rem_bool: + exp = simplify_bool(exp) + if opcode(exp) == 'bool': + return prettify(exp, rem_bool=rem_bool, parentheses=parentheses, top_level=top_level, add_color=add_color) + + if type(exp) == int and exp % (24 * 3600) == 0 and exp > 24 * 3600: + exp = ('mul', exp//3600, 24, 3600) + + if type(exp) == int and exp % 3600 == 0 and exp > 3600: + exp = ('mul', exp//3600, 3600) + # also tried return col('seconds(', COLOR_GRAY) + '1 hour' + col(')', COLOR_GRAY) + # but seemed less intuitive, e.g. 0xf64B584972FE6055a770477670208d737Fff282f calcMaxWithdraw + # and 3600 every programmer should know, by heart, means 1 hour :) + # + # also, not tackling single minutes because too often they are not time related + + if type(exp) in (int, float): + return pretty_num(exp, add_color) + + if opcode(exp) in precompiled.values(): + return f'{exp[0]}({pret(exp[1])})' + + if exp ~ ('arr', int:num, ('mask_shl', _, _, _, str:s)) \ + and len(s) == num+2: + return s + + if exp ~ ('param', :name): + return col(name, COLOR_GREEN) + + if exp ~ ('range', :loc, :size): + return '{} {} {}'.format(pret(loc), + col('len', COLOR_HEADER), + pret(size)) + + if exp ~ ('data', ...): + return ', '.join(pretty_memory(exp, add_color=add_color)) + + if exp ~ ('arr', :l, *terms): + return col('Array(len=', COLOR_GRAY) + pret(l)+ col(', data=', COLOR_GRAY) + pret(('data', )+terms) + col(')', COLOR_GRAY) + + if exp ~ ('blockhash', :number): + return f'block.hash({pret(number)})' + + if exp ~ ('extcodehash', :addr): + return f'ext_code.hash({pret(addr)})' + + if exp ~ ('extcodesize', :addr): + return f'ext_code.size({pret(addr)})' + + if exp ~ ('extcodecopy', :addr, :loc): + return f'ext_code.copy({pret(addr)}, {pret(loc)})' + + + if exp ~ ('max', *terms): + return 'max({})'.format(', '.join([pret(e) for e in terms])) + + if exp == 'number': + return 'block.number' + + if exp == 'calldatasize': + return 'calldata.size' + + if exp == 'returndatasize': + return 'return_data.size' + + if exp == 'difficulty': + return 'block.difficulty' + + if exp == 'gasprice': + return 'block.gasprice' + + if exp == 'timestamp': + return 'block.timestamp' + + if exp == 'coinbase': + return 'block.coinbase' + + if exp == 'gaslimit': + return 'block.gas_limit' + + if exp == 'callvalue': + return 'call.value' + + if exp == 'address': + return 'this.address' + + if exp ~ ('mask_shl', 160, 0, 0, 'caller'): + return 'caller' + + if exp == 'caller': + return 'caller' + + if exp ~ ('mask_shl', 160, 0, 0, 'origin'): + return 'tx.origin' + + if exp ~ ('mulmod', :a, :b, :c): + return f'mulmod({pret(a)}, {pret(b)}, {pret(c)})' # mulmod should really be replaced by mul & mod in other stages + # but this is rare enough to ignore for now + + if exp == 'origin': + return 'tx.origin' + + if exp == 'gas': + return 'gas_remaining' + + if exp == ('bool', 1): + return 'True' + + if exp == ('bool', 0): + return 'False' + + if exp ~ ('code.data', :c_start, :c_len): + return f'code.data[{pret(c_start)} len {pret(c_len)}]' + + if exp ~ ('balance', :addr): + return f'eth.balance({pret(addr)})' + + if exp ~ ('sha3', *terms): + return 'sha3({})'.format(', '.join([pret(e) for e in terms])) + +# if exp ~ ('mask_shl', 251, 5, 0, :val): +# return pret(('mul', 32, val)) + + if (exp ~ ('mask_shl', :size, 5, 0, :val) or + exp ~ ('mask', :size, 5, :val)) and size > 245: + if val ~ ('add', 31, :num): + return f'ceil32({pret(num)})' + else: + return f'floor32({pret(val)})' + + if exp ~ ('call.data', ('add', 36, ('param', :p_name)), :size) and \ + size == ('cd', ('add', 4, ('param', p_name))): + return f"{col(p_name+'[', C.green)}"+'all'+col(']', C.green) + + if exp ~ (:name, :offset, :size) and is_array(name):# in ('call.data', 'ext_call.return_data'): + if size == 32: + return name+f'[{pret(offset)}]' + else: + return name+f'[{pret(offset)} len {pret(size)}]' + + if exp ~ ('mask_shl', :size, :offset, :shl, ('stor', :s_size, :s_off, :s_idx)) and \ + safe_le_op(s_size, size) and shl == 0: + return pret(('stor', s_size, s_off, s_idx)) + + if exp ~ ('stor', ...): + return pretty_stor(exp, add_color=add_color) + + if exp ~ ('type', ...): + return pretty_stor(exp, add_color=add_color) + + if exp ~ ('field', ...): + return pretty_stor(exp, add_color=add_color) + + if exp ~ ('cd', :num): + if num == 0: + return(col('call.func_hash', C.green)) + parsed_exp = get_param_name(exp, add_color=add_color) + + if type(parsed_exp) != str: + return 'cd['+prettify(parsed_exp[1], add_color=add_color)+']' + else: + return parsed_exp + + if exp ~ ('var', int:idx): + nice_names = ['idx','s','t','u','v','w','x','y','z','a','b','c','d','e','f','g','h'] # 'i','j','k','l','m','n','o','p','q','r', + if idx < len(nice_names): + name = nice_names[idx] + else: + name = 'var'+str(idx) + + return col(name, COLOR_BLUE) + + if exp ~ ('var', :name): + return col(str(name), COLOR_BLUE) + + if exp ~ ('mem', ('range', :loc, 32)): + exp = ('mem', loc) + + if exp ~ ('mem', ('range', :loc, :size)): + return col('mem[', COLOR_HEADER) + \ + pret(loc) + \ + col(' len ', COLOR_HEADER) + \ + pret(size) + col(']', COLOR_HEADER) + + elif exp ~ ('mem', :idx): + assert opcode(idx) != 'range' + + return col('mem[', COLOR_HEADER) + \ + pret(idx) + \ + col(']', COLOR_HEADER) + + + if exp ~ ('setvar', :idx, :val): # shouldn't be pretty line? + return pret(('var', idx)) + ' = ' + pret(val, parentheses=False) + + if exp ~ ('setmem', :idx, :val): # --,,-- + return pret(('mem', idx)) + ' = ' + \ + pret(val) + + if exp ~ ('mask_shl', :size, :offset, :shl, :val): + + if all_concrete(size, offset, shl) and \ + exp[1] + exp[2] == 256 and exp[2] == -exp[3] and exp[2] < 8: + # e.g. (Mask(255, 1, eth.balance(this.address)) >> 1 + # --> eth.balance(this.address) / 2 + # for offsets smaller than 8 + + if exp[3] <= 8: + return pret(('div', exp[4], 2**-exp[3]), parentheses=parentheses) + else: + return pret(('shr', exp[3], exp[4]), parentheses=parentheses) + + if (type(exp[1]), type(exp[2]), type(exp[3])) == (int, int, int) and\ + exp[2] == exp[3] and exp[2] < 8: + # e.g. (Mask(255, 1, eth.balance(this.address)) << x + # --> eth.balance(this.address) * 2**x + # for offsets smaller than 8 + + if size + offset != 256 and opcode(val) != 'store': # opcode=store - hotfix for 0x000000000045Ef846Ac1cB7fa62cA926D5701512 + val = ('mask', size + offset, 0, val) # 0 because exp2 == exp3 + + if exp[3] == 0: + return pret(val, parentheses=parentheses) + elif exp[3] <= 8 and exp[3] >= -8: + return pret(('mul', val, 2**exp[3]), parentheses=parentheses) + elif exp[3] > 0: + return pret(('shl', exp[3], val, ), parentheses=parentheses) + else: + return pret(('shr', -exp[3], val, ), parentheses=parentheses) + + + + if all_concrete(size, offset, shl, val): + return pret(apply_mask(exp[4], exp[1], exp[2], exp[3])) + + if shl == 0: + exp = ('mask', size, offset, val) + + elif safe_ge_zero(shl) is not False: + + if all_concrete(size, offset, shl) and \ + size+shl == 256 and offset == 0 and shl > -8: + exp = ('mul', 2**exp[3], exp[4]) + else: + if type(exp[3]) == int and exp[3] < 7 and exp[3] >= -8: + exp = ('mul', 2**exp[3], ('mask', exp[1], exp[2], exp[4])) + + elif type(exp[3]) == int and exp[3] < 0: + exp = ('shr', -exp[3], ('mask', exp[1], exp[2], exp[4])) + else: + exp = ('shl', exp[3], ('mask', exp[1], exp[2], exp[4])) + + else: + exp = ('shr', mul_op(-1, exp[3]), ('mask', exp[1], exp[2], exp[4])) + + if exp ~ ('mask', :size, 0, :val): + if size == 256: + return pret(val) + + if type(size) == int: + if size == 255: + type_name = 'uint255' + else: + type_name = mask_to_type(size) + + if type_name is not None: + return col(type_name + '(', COLOR_GRAY) + \ + pret(val) + \ + col(")", COLOR_GRAY) + + if exp ~ ('bool', :val): + if opcode(val) in ('lt', 'gt', 'iszero', 'le', 'ge', 'bool'): + return pret(val, parentheses=parentheses) + else: + return "bool("+pret(val)+")" + + if exp ~ ('mask', :size, :offset, :val): + if type(size) == int and offset == 0 and size < 64: + return pret(('mod', val, 2**size), parentheses=parentheses) + else: + return 'Mask({}, {}, {})'.format(pret(size), pret(offset), pret(val)) + +# if opcode(exp) in ('byte', 'bytes8', 'uint16', 'bytes4', 'addr', 'int256'): +# return prettify('{}({})'.format(opcode(exp).lower(), prettify(exp[1], add_color=add_color)), add_color=add_color) + + opcode_to_arithm = { + 'sub': ' - ', # todo - parentheses? + 'div': ' / ', + 'mul': ' * ', + 'gt': ' > ', + 'lt': ' < ', + 'le': ' <= ', + 'ge': ' >= ', + 'or': ' or ', + 'eq': ' == ', + 'mod': ' % ', + 'shl': ' << ', + 'shr': ' >> ', + 'exp': '^', + 'and': ' and ', + 'sge': ' >=′ ', + 'sle': ' <=′ ', + 'sgt': ' >′ ', + 'slt': ' <′ ', + 'sadd': ' +′ ', + 'smul': ' *′ ', + 'sdiv': ' /′ ', + + 'xor': ' xor ', + } + + def pretty_adds(exp): + if opcode(exp) != 'add': + return prettify(exp, add_color = add_color) + + if type(exp[1]) == float: + real = exp[1] + if int(real) == real: + real = int(real) # 32.0 -> 32 + + symbolic = exp[2:] + res = ' + '.join([prettify(x, add_color=add_color) for x in symbolic]) + if real > 0: + res += ' + ' + prettify(real) + elif real < 0: + res += ' - ' + prettify(-real) + + + elif type(exp[1]) == int: + real = to_real_int(exp[1]) + symbolic = exp[2:] + res = ' + '.join([prettify(x, add_color=add_color) for x in symbolic]) + if real > 0: + res += ' + ' + prettify(real) + elif real < 0: + res += ' - ' + prettify(-real) + + else: + res = '' + for x in exp[1:]: + if res == '': + res = prettify(x, add_color=add_color) + elif opcode(x) == 'mul' and type(x[1]) == int and x[1] < 0: + res += ' - ' + prettify(minus_op(x), add_color=add_color) + else: + res += ' + ' + prettify(x, add_color=add_color) + + if parentheses: + return f'({res})' + else: + return res + + if opcode(exp) == 'not': + return COLOR_BOLD+'!'+ENDC+prettify(exp[1], add_color = add_color) + + if opcode(exp) == 'add': + return pretty_adds(exp) + + if opcode(exp) == 'mul' and len(exp)==3 and to_exp2(exp[1]) != None and to_exp2(exp[1]) > 32: + exp = ('shl', to_exp2(exp[1]), exp[2]) + + if exp ~ ('mul', -1, :val): + return "-" + pret(val, parentheses=parentheses) + + if exp ~ ('mul', 1, :val): + return pret(val, parentheses=parentheses) + + if exp ~ ('mul', 1, *rest): + return pret(('mul', ) + rest, parentheses=parentheses) + + if exp ~ ('div', :num, 1): + return pret(num, parentheses=parentheses) + + if exp ~ ('exp', :a, :n): + return pret(a, parentheses=True) + '^' + pret(n, parentheses=True) + + if opcode(exp) in opcode_to_arithm: + + if opcode(exp) in ['shl', 'shr']: + exp = exp[0], exp[2], exp[1] + + form = '{}' + if parentheses and not top_level: + form = '({})' + + op_form = opcode_to_arithm[opcode(exp)] + if add_color: + op_form = COLOR_BOLD + op_form + ENDC + + + def fold_ands(exp): + assert opcode(exp) == 'and' + + res = tuple() + for e in exp[1:]: + if opcode(e) == 'and': + e = fold_ands(e) + res += e[1:] + else: + res += (e, ) + + return ('and', ) + res + + if opcode(exp) == 'and': + exp = fold_ands(exp) + return form.format(op_form.join(pret(e, rem_bool=True) for e in exp[1:])) + else: + return form.format(op_form.join(pret(e) for e in exp[1:])) + + if exp ~ ('iszero', :val): + + if val ~ ('gt', :left, :right): + return pret(left) + ' <= ' + pret(right) + + if val ~ ('lt', :left, :right): + return pret(left) + ' >= ' + pret(right) + + if val ~ ('eq', :left, :right): + if type(left) in (str, int): + return pret(right) + ' != ' + pret(left) + else: + return pret(left) + ' != ' + pret(right) + + return 'not '+pret(val) + + return str(exp) + + +def pretty_gas(gas, value, add_color): + if gas ~ ('mul', 2300, ('iszero', _)): + # should check if _ == value + return '2300 * is_zero(value)' + else: + return prettify(gas, add_color=add_color, parentheses=False) + +def try_fname(exp, add_color=False): + if Loader.find_sig(hex(exp)[:10]): + return Loader.find_sig(hex(exp)[:10], add_color) + + elif len(hex(exp))>=63 and Loader.find_sig(padded_hex(exp,64)[:10], add_color):# in Loader.signatures: # if three last letters are "0"s, but no more, so there is + # a low chance for mistaking a random number for function sig + return Loader.find_sig(padded_hex(exp,64)[:10], add_color) + + elif len(hex(exp))>=8 and Loader.find_sig(padded_hex(exp,8)[:10], add_color):# in Loader.signatures: + return Loader.find_sig(padded_hex(exp,8)[:10], add_color) + + else: + return None + +def pretty_fname(exp, add_color=False, force=False): + if type(exp) == int: + if try_fname(exp) and 'unknown_' not in try_fname(exp): + return try_fname(exp, add_color) + else: + return hex(exp) + + elif opcode(exp) == 'mem' or force: + return prettify(exp, add_color=add_color) + + return exp + +def pretty_memory(exp, add_color=False): + if exp is None: + return tuple() + + if exp == 'mem': + return prettify(exp, add_color=add_color) + + if opcode(exp) != 'data': + return (prettify(exp, add_color=add_color), ) + + exp = exp[1:] + + if len(exp) == 0: + return 'empty()' + assert len(exp) > 0, exp + + res = [] + + idx = 0 + + def unmask(exp): + if opcode(exp) == 'mask_shl': + return exp[4] + + return exp + + # merge things that look like string into a string + + while idx < len(exp): + + if idx == 0 and type(exp[0]) == tuple and exp[0][:4] == ('mask_shl', 32, 224, 0) and type(exp[0][4]) == int: + # This happens often in Log and Revert, first + # memory result being an 8-byte identifier. + # Definitely deserves a more generic solution. + # + # example: 0xd883209C4DCd497f24633C627a4E451013424841, sendFoods function + v = exp[0][4] >> 224 + res.append( pretty_fname(v, add_color) ) + idx += 1 + continue + + el = exp[idx] + + # detect a potential string + + out_str = None + + if unmask(el) == 32 and len(exp)>idx+1: + + length = unmask(exp[idx+1]) + if type(length) == int: + byte_length = ((length-1) >> 5) +1 + out_str = '' + + if type(length) == int and len(exp) > idx + 1 + byte_length: + for i in range(byte_length): + if type(unmask(exp[idx+2+i])) == str: + out_str += unmask(exp[idx+2+i])[1:-1] + elif type(pretty_bignum(unmask(exp[idx+2+i]))) == str: + # could also make sure that the length of string is the same + # as expected + out_str += pretty_bignum(unmask(exp[idx+2+i]))[1:-1] + else: + out_str = None + break + + if out_str != None: + idx = idx + 1 + byte_length + else: + out_str = None + else: + out_str = None + + if out_str != None: + res.append("'"+out_str+"'") + else: + res.append(prettify(el, add_color=add_color, parentheses=False)) + + idx = idx+1 + + + return tuple(res) + diff --git a/utils/profiler.py b/utils/profiler.py new file mode 100644 index 00000000..7f425a16 --- /dev/null +++ b/utils/profiler.py @@ -0,0 +1,99 @@ +try: + from time import perf_counter_ns + def time_ns(): + return perf_counter_ns() +except: + # python < 3.7 + from time import perf_counter + def time_ns(): + return int(perf_counter() * (1000000)) + +import sys +import logging + +logger = logging.getLogger(__name__) + +''' + a simplified line by line profiler + + usage: + + ... + checkpoint_start() #1 + ... + checkpoint('name1') #2 + ... + checkpoint('name2') #3 + + saves information how much cummulative time + passed between #1 and #2 (name1), and #2 and #3 (name2) + + use log_checkpoints to show results + + func_caller is useful, when you want to differenciate by function + caller + + usage: + + def some_func(): + checkpoint_start() + STUFF + checkpoint('name'+func_caller()) + + def func_a(): + some_func() + + def func_b(): + some_func() + + log_checkpoints() + shows time spent in some_func, split by the time + when it was called by func_a and func_b + +''' + +results = {} +last_checkpoint = '' +last_ns = time_ns() + +def checkpoint(name): + global results + global last_ns + + new_ns = time_ns() + + if name in results: + results[name] += new_ns - last_ns + else: + results[name] = new_ns - last_ns + + last_ns = new_ns + + +def checkpoint_start(): + global last_ns + last_ns = time_ns() + + +def log_checkpoints(): + return + for name, elapsed in results.items(): + logger.info(f'Profiler: {name}\t\t- {elapsed / 1000000000}') + + for name, elapsed in counters.items(): + logger.info(f'Profiler: {name}\t\t- {elapsed}') + +print_checkpoints=log_checkpoints + +counters = {} +def count(x): + if x in counters: + counters[x] += 1 + else: + counters[x] = 1 + + +def func_caller(depth=1): + # returns caller of a function + # useful when you want to differenciate checkpoints by who the function caller is + return sys._getframe(2 + depth).f_code.co_name diff --git a/utils/signatures.py b/utils/signatures.py new file mode 100644 index 00000000..7041da2e --- /dev/null +++ b/utils/signatures.py @@ -0,0 +1,195 @@ +# coding: tilde + +from .profiler import checkpoint + +import json +from .helpers import opcode, colorize, cleanup_mul_1 + +from .helpers import COLOR_HEADER, COLOR_BLUE, COLOR_OKGREEN, COLOR_WARNING, FAIL, ENDC, COLOR_BOLD, COLOR_UNDERLINE, COLOR_GREEN, COLOR_GRAY +import hashlib +import os.path + +from .supplement import fetch_sigs + +import sys + +_current_hash = None +_abi = None +_func = None + +def set_func_params_if_none(params): + if 'params' not in _func: + res = [] + for t, n in params.values(): + res.append({'type':t, 'name':n}) + + _func['params'] = res + +def set_func(hash): + global _func + global _abi + global _current_fname + + _current_hash = hash + assert _abi is not None + _func = _abi[hash] + +def get_param_name(cd, add_color=False, func=None): + global _func + global _abi + global _current_hash + assert cd ~ ('cd', :loc) + + if _abi is None: + return cd + + if _func is None: + return cd + + if 'params' not in _func: + return cd + + if type(loc) != int: + cd = cleanup_mul_1(cd) + + if loc ~ ('add', 4, ('param', :point_loc)): + return colorize(point_loc + '.length', COLOR_GREEN, add_color) + + if loc ~ ('add', 4, ('cd', :point_loc)): + return colorize(str(get_param_name(('cd',point_loc))) + '.length', COLOR_GREEN, add_color) + + elif loc ~ ('add', int:offset, ('cd', :point_loc)): + return colorize(str(get_param_name(('cd',point_loc))) + f'[{(offset - 36)//32}]', COLOR_GREEN, add_color) + + else: + return cd + + if (loc-4) % 32 != 0: # unusual parameter + return cd # + + num = (cd[1]-4) // 32 + if num >= len(_func['params']): + return cd + + assert num 0: + candidates = [] + best_score = 0 + + for f in sigs: + score = match_score(f, hashes) + if score>best_score: + res = {'name':f['name'], + 'folded_name':f['folded_name'], + 'params':f['params'],} + + res['target'] = target + + result[h] = res + + _abi = result + + with open(cache_fname, 'w+') as f: + f.write(json.dumps(result, indent = 2)) + + return result + diff --git a/utils/supplement.py b/utils/supplement.py new file mode 100644 index 00000000..7add7abd --- /dev/null +++ b/utils/supplement.py @@ -0,0 +1,208 @@ +import sqlite3 +import json +from zipfile import ZipFile +import urllib.request +import sys +import time +import os + +from .helpers import opcode, padded_hex, cached +from .helpers import COLOR_HEADER, COLOR_BLUE, COLOR_OKGREEN, COLOR_WARNING, FAIL, ENDC, COLOR_BOLD, COLOR_UNDERLINE, COLOR_GREEN, COLOR_GRAY + + +''' + a module for management of bytes4 signatures from the database + + db schema: + + hash - 0x12345678 + name - transferFrom + folded_name - transferFrom(address,address,uint256) + cooccurs - comma-dellimeted list of hashes: `0x12312312,0xabababab...` + params - json: `[ + { + "type": "address", + "name": "_from" + }, + { + "type": "address", + "name": "_to" + }, + { + "type": "uint256", + "name": "_value" + } + ]` + +''' + +conn = None + +def check_supplements(): + if not os.path.isfile('supplement.db'): + print(COLOR_OKGREEN+'Hello, is this your first Panoramix run?'+ENDC) + print() + print('I need to fetch the signatures database from the web') + print('This will be done just once.') + print() + fetch_db() + + assert os.path.isfile('supplement.db') + + if not os.path.isfile('supp2.db'): + print('creating supp2.db...') + c = sqlite3.connect('supplement.db').cursor() + d_conn = sqlite3.connect('supp2.db') + d = d_conn.cursor() + + d.execute('create table functions (hash integer, name text, folded_name text, params text, primary KEY(hash))') + + c.execute('SELECT hash, name, folded_name, params from functions group by hash') + results = c.fetchall() + + for r in results: + insert_row = int(r[0], 16), r[1], r[2], r[3] + d.execute('INSERT INTO functions VALUES (?, ?, ?, ?)', insert_row) + + d_conn.commit() + d_conn.close() + print('done.') + + assert os.path.isfile('supp2.db') + +def _cursor(): + global conn + + check_supplements() + + if conn is None: + conn = sqlite3.connect('supplement.db') + + try: + c = conn.cursor() + except: + # fails in multi-threading, this should help + conn = sqlite3.connect('supplement.db') + return conn.cursor() + + return c + +conn2 = None + +def _cursor2(): + global conn2 + + check_supplements() + + if conn2 is None: + conn2 = sqlite3.connect('supp2.db') + + try: + c = conn2.cursor() + except: + # fails in multi-threading, this should help + conn2 = sqlite3.connect('supp2.db') + return conn2.cursor() + + return c + +@cached +def fetch_sigs(hash): + c = _cursor() + c.execute('SELECT * from functions where hash=?', (hash,)) + + results = c.fetchall() + + res = [] + for row in results: + res.append({ + 'hash': row[0], + 'name': row[1], + 'folded_name': row[2], + 'params': json.loads(row[3]), + 'cooccurs': row[4].split(','), + }) + + return res + +@cached +def fetch_sig(hash): + if type(hash) == str: + hash = int(hash, 16) + + c = _cursor2() + c.execute(f'SELECT hash, name, folded_name, params from functions where hash={hash}') + + results = c.fetchall() + if len(results) == 0: + return None + + row = results[0] + + return { + 'hash': padded_hex(row[0], 8), + 'name': row[1], + 'folded_name': row[2], + 'params': json.loads(row[3]), + } + + +def fetch_db(): + def reporthook(count, block_size, total_size): + # https://blog.shichao.io/2012/10/04/progress_speed_indicator_for_urlretrieve_in_python.html + global start_time + if count == 0: + start_time = time.time() + return + + duration = time.time() - start_time + progress_size = int(count * block_size) + speed = int(progress_size / (1024 * duration)) + percent = min(int((count * block_size * 100) / total_size), 100) + sys.stdout.write("\r%d of 27 MB, %d KB/s, %d seconds passed..." % + (progress_size / (1024 * 1024), speed, duration)) + sys.stdout.flush() + + print('fetching supplement.zip...') + url = 'http://eveem.org/static/supplement.zip' + urllib.request.urlretrieve(url, 'tmp.supplement.db.zip', reporthook) + print('') + print('unzipping into supplement.db...') + with ZipFile('tmp.supplement.db.zip') as myzip: + with myzip.open('supplement.db') as myfile: + with open('supplement.db', 'wb') as file: + file.write(myfile.read()) + + os.remove('tmp.supplement.db.zip') + print('done.') + +def create_db(): + c = _cursor() + + c.execute('''create TABLE functions + (hash text, name text, folded_name text, params text, cooccurs text)''') + + c.execute('''create INDEX hash_idx ON functions (hash)''') + + with open('supplement.json') as f: + data = json.loads(f.read()) + + num = 0 + for f_hash, val in data.items(): + for f in val: + num += 1 + print(f'adding {num}...') + row = ( + f_hash, + f['name'], + f['folded_name'], + json.dumps(f['params']), + ','.join(f['cooccurs'])) + + # print(row) + + c.execute('INSERT INTO functions VALUES (?, ?, ?, ?, ?)', row) + + conn.commit() + + diff --git a/various.py b/various.py new file mode 100644 index 00000000..b15f7753 --- /dev/null +++ b/various.py @@ -0,0 +1,598 @@ +# couldn't find a better name for this module, feel free to change + +random_addresses = ["0x62D4c04644314F35868Ba4c65cc27a77681dE7a9", "0x473319898464Ca640Af692A0534175981AB78Aa1", "0x5B8D43FfdE4a2982B9A5387cDF21D54Ead64Ac8d", "0x41f615E24fAbd2b097a320E9E6c1f448cb40521c", "0x9aeFBE0b3C3ba9Eab262CB9856E8157AB7648e09", "0x08f5a9235B08173b7569F83645d2c7fB55e8cCD8", "0x08fd34559F2ed8585d3810B4D96Ab8A05c9f97c5", "0x08f8117155aA9414B67113a47ad269D47974e9DC", "0xFF603F43946A3A28DF5E6A73172555D8C8b02386", "0xc92D6E3E64302C59d734f3292E2A13A13D7E1817", "0x814964b1bceAf24e26296D031EaDf134a2Ca4105", "0x814CAfd4782d2e728170FDA68257983F03321c58", "0x87aE38D63A6bbB63E46219F494b549e3bE7Fc400", "0x3d1BA9be9f66B8ee101911bC36D3fB562eaC2244", "0xe30e02f049957e2A5907589e06Ba646fB2c321bA", "0xC66eA802717bFb9833400264Dd12c2bCeAa34a6d", "0xA54ddC7B3CcE7FC8b1E3Fa0256D0DB80D2c10970", "0xDe39E5E5a1B0eEB3Afe717D6d011CaE88D19451e", "0xfF18DBc487b4c2E3222d115952bABfDa8BA52F5F", "0xA9877b1e05D035899131DBd1e403825166D09f92", "0x222728C202e7164DFbd127181D46409338c4328e", "0x07e3c70653548B04f0A75970C1F81B4CBbFB606f", "0x6467882316dc6e206FEef05fBa6deaA69277f155", "0x62CD07D414Ec50B68C7EcAa863a23d344f2d062f", "0x7B22938ca841aA392C93dBB7f4c42178E3d65E88", "0x87F94f2C11C8F6B24E6D54B7B7a3356ab1aD0968", "0xfcb48fdCc479B38068C06eE94249B1516adF09cB", "0xfe7B915A0bAA0E79f85c5553266513F7C1c03Ed0", "0x04cC783b450b8D11F3C7d00DD03fDF7FB51fE9F2", "0xc3bC9Eb71f75Ec439A6b6C8E8b746fCF5b62F703", "0x7A41e0517a5ecA4FdbC7FbebA4D4c47B9fF6DC63", "0x04De23E912Cec433eABf3260ecC71cfD1f9d328f", "0x2134057C0b461F898D375Cead652Acae62b59541", "0x27f706edde3aD952EF647Dd67E24e38CD0803DD6", "0xDF2C7238198Ad8B389666574f2d8bc411A4b7428", "0x28b5E12CcE51f15594B0b91d5b5AdaA70F684a02", "0x5dDAB66DA218Fb05dfeDA07f1AfC4ea0738ee234", "0xa9666166D3c7fD15e874801f99e9aD5Bfb70c5cF", "0xe06eda7435bA749b047380CEd49121ddE93334Ae", "0x81c9151de0C8bafCd325a57E3dB5a5dF1CEBf79c", "0x3B3a608c676644959DDe08fb252A7d64e71ac843", "0x3B33F4C1CCb8Af6aa911B17dc726b7Fd8f7Ff312", "0x87611cA3403a3878DfEf0da2a786e209AbfC1Eff", "0x882448f83d90B2bf477Af2eA79327fDEA1335D93", "0x84936cF7630AA3e27Dd9AfF968b140d5AEE49F5a", "0x075c60EE2cD308ff47873b38Bd9A0Fa5853382c4", "0x623B925b0A57a24EA8dE301F2E3E692cE903f0c3", "0xFFAA5ffc455d9131f8A2713A741fD1960330508B", "0xFFa93Aacf49297D51E211817452839052FDFB961", "0xa5dB1d6F7A0D5Bccc17d0bFD39D7AF32d5E5EDc6", "0xA5d1e58ECe1fC438d64E65769d2ab730143a4Caf", "0xA3149E0fA0061A9007fAf307074cdCd290f0e2Fd", "0x8713d26637CF49e1b6B4a7Ce57106AaBc9325343", "0xC9B89f6B5301F554B9Adc6d4a871C3279820De40", "0x4162178B78D6985480A308B2190EE5517460406D", "0x64A60493D888728Cf42616e034a0dfEAe38EFCF0", "0x24083Bb30072643C3bB90B44B7285860a755e687", "0x24021d38DB53A938446eCB0a31B1267764d9d63D", "0x1B9743f556D65e757c4c650B4555bAF354cB8bd3", "0x22E5F62D0FA19974749faa194e3d3eF6d89c08d7", "0xE638dc39b6aDBEE8526b5C22380b4b45dAf46d8e", "0x1dEa979ae76f26071870F824088dA78979eb91C8", "0x3a26746Ddb79B1B8e4450e3F4FFE3285A307387E", "0x3a2aEdc3B54A99e429ae36637681d4560cE5C05b", "0x82b0E50478eeaFde392D45D1259Ed1071B6fDa81", "0x82BD526bDB718C6d4DD2291Ed013A5186cAE2DCa", "0xdfBd6A960a55bCfCf59d5925351E05a51498BCeF", "0xBE11eEb186e624b8f26A5045575a1340E4054552", "0x27695E09149AdC738A978e9A678F99E4c39e9eb9", "0x9dfe4643C04078a46803edCC30a3291b76d4c20c", "0x044DD17bbbcbF1CF65f543918561BF8CF8130e7B", "0x420C42cE1370c0Ec3ca87D9Be64A7002E78e6709", "0x44e6d9Ae9053A16E9311Fd9702291c5516804359", "0x6124F98DA4788aDB4FCBd6f6651A9a66166fD506", "0x1db186898bcCDe66Fa64A50E4D81078951A30dbE", "0x1b793E49237758dBD8b752AFC9Eb4b329d5Da016", "0xA9Aad2dC3a8315caeee5F458B1d8EDc31D8467BD", "0x41875C2332B0877cDFAA699B641402b7D4642c32", "0x418CCb0dd045AF4C5e37aEE7E1639901BE9b55C4", "0xE64509F0bf07ce2d29A7eF19A8A9bc065477C1B4", "0x0886949c1b8C412860c4264Ceb8083d1365e86CF", "0xa6E7172662379f1f4C72108655869AbdBB7F7672", "0xa6E2F7f33F01fB399e72F3E044196eAb7d348012", "0xc324a2f6b05880503444451B8b27e6f9e63287Cb", "0x4270bb238f6DD8B1c3ca01f96CA65b2647c06D3C", "0x427031400f39Cc39A45E93Ec76b805Fe0d7849f2", "0x28577A6d31559bd265Ce3ADB62d0458550F7b8a7", "0xBCC394D45C3613530A83Cae62C716dC23B7f2152", "0xdFe2BD1d3Dcbb97804ACF3ee85230E832C4a7B5d", "0xFc2C4D8f95002C14eD0a7aA65102Cac9e5953b5E", "0x68d57c9a1C35f63E2c83eE8e49A64e9d70528D25", "0x68DB10ECC599D9f5E657acDAfDbf6449D658bB2D", "0xe386B139Ed3715Ca4B18Fd52671bDcea1cdFE4b1", "0xe3831c5A982B279A198456D577cfb90424cb6340", "0xe3818504c1B32bF1557b16C238B2E01Fd3149C17", "0xDF347911910b6c9A4286bA8E2EE5ea4a39eB2134", "0xDF3E88B6a29EeA886A822Cecf25802A388070eED", "0xFcD862985628b254061F7A918035B80340D045d3", "0x28c8d01FF633eA9Cd8fc6a451D7457889E698de6", "0x3d96EEC26865D0BFAc851e0640197eE713291f5b", "0x9bb1Db1445b83213a56d90d331894b3f26218e4e", "0xc51C938C4d513780C66C722a41c197D3a89Fa9A8", "0x5B09A0371C1DA44A8E24D36Bf5DEb1141a84d875", "0x5B0751713b2527d7f002c0c4e2a37e1219610A6B", "0xE9fF07809CCff05daE74990e25831d0Bc5cbe575", "0xa973E5Ebd127c9fdB28406892A19881cc81dA7a4", "0xA974c709cFb4566686553a20790685A47acEAA33", "0x1d462414fe14cf489c7A21CaC78509f4bF8CD7c0", "0x5a84969bb663fb64F6d015DcF9F622Aedc796750", "0x671AbBe5CE652491985342e85428EB1b07bC6c64", "0x6710c63432A2De02954fc0f851db07146a6c0312", "0x28dee01D53FED0Edf5f6E310BF8Ef9311513Ae40", "0xFcC4092BA380042D391019Fc2545C0977De9B65f", "0x3ADfc4999F77D04c8341BAC5F3A76f58DfF5B37A", "0x5dbe296F97B23C4A6AA6183D73e574D02bA5c719", "0x7A5024326F826e42569741202fcEE4a1b5682F6C", "0x7A5fF295Dc8239d5C2374E4D894202aAF029Cab6", "0xA0aa85b54F8A7b09C845F13a09172B08925f3d54", "0xa645264C5603E96c3b0B078cdab68733794B0A71", "0x2233799Ee2683d75dfefAcbCd2A26c78D34b470d", "0x07D9e49Ea402194bf48A8276dAfB16E4eD633317", "0xBfA4d71a51B9e0968Be4Bc299F8BA6cBb2f86789", "0x6781a0F84c7E9e846DCb84A9a5bd49333067b104", "0x5A1A29DBb6Ad6153DB764568C1289076bC876df6", "0xC0Eb85285d83217CD7c891702bcbC0FC401E2D9D", "0xc0EA6306F6360FE7dCAB65D16Bf1a3AF92C79Aa2", "0xC0E31c25Ca34c4b5Bc3380b4b1368445aD33d91a", "0xDe541488eb253BE47e357A896347B2787055aFD8", "0x5B92E96ECa5303102Af9d3eE54981917aD8226ae", "0x24A77c1F17C547105E14813e517be06b0040aa76", "0x24AEF3BF1A47561500f9430D74Ed4097C47F51F2", "0xE5Dada80Aa6477e85d09747f2842f7993D0Df71C", "0x88d50B466BE55222019D71F9E8fAe17f5f45FCA1", "0x089A6D83282Fb8988A656189F1E7A73FA6C1caC2", "0xA017ac5faC5941f95010b12570B812C974469c2C", "0x84119cb33E8F590D75c2D6Ea4e6B0741a7494EDA", "0x82D193F8Ee41D12aaA0A85cB006606D67F773E9c", "0xE9197642A9138f91dfe2fCF7059D6762Bd6E85BB", "0x419c4dB4B9e25d6Db2AD9691ccb832C8D9fDA05E", "0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b", "0x5D21eF5f25a985380B65c8e943A0082fEDa0Db84", "0x28481CdC0e4fa79164491D47E8837EDEB3993f20", "0xdfdc0D82d96F8fd40ca0CFB4A288955bECEc2088", "0x68e14bb5A45B9681327E16E528084B9d962C1a39", "0xFeFe2A311D5F5A56Fc45da5E569286506Cd3a6d3", "0x614ea929892EA43d3EA2C5e3311B01CC589bAD6C", "0x27054b13b1B798B345b591a4d22e6562d47eA75a", "0x1a95B271B0535D15fa49932Daba31BA612b52946", "0x1DD9dD6a7be0da126ABC3987BD904Fc33d36E300", "0x82Cf44bE0768A3600c4BDeA58607783A3A7c51AE", "0x846C66cf71C43f80403B51fE3906B3599D63336f", "0xC34B21f6F8e51cC965c2393B3ccFa3b82BEb2403", "0xc34F69Dab210699279d37CF423fA559c4F7F2dAe", "0x67F1a73124163888396d0A27aA07738a8d582997", "0x1a7a8BD9106F2B8D977E08582DC7d24c723ab0DB", "0x5Af2Be193a6ABCa9c8817001F45744777Db30756", "0x5aFDa18cABA69Fe3AF5E6D56E42e1C9F92C40D77", "0xbe99B09709fc753b09BCf557A992F6605D5997B0", "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "0x884e3902C4d5cFA86de4aCE7A96AA91EbC25C0Ff", "0x01F2AcF2914860331C1Cb1a9AcecDa7475e06Af8", "0x01fF50f8b7f74E4f00580d9596cd3D0d6d6E326f", "0x08711D3B02C8758F2FB3ab4e80228418a7F8e39c", "0xdC0c22285B61405aaE01Cba2530B6Dd5cD328da7", "0x48f775EFBE4F5EcE6e0DF2f7b5932dF56823B990", "0xDEe667186e7b81Ecf7Efc8713382d8D99A8b92B4", "0xe530441f4f73bDB6DC2fA5aF7c3fC5fD551Ec838", "0xE3feDAeCD47aa8EAb6b23227b0eE56F092C967a9", "0xE3Fa177AcecfB86721Cf6f9f4206bd3Bd672D7d5", "0x1A0F2aB46EC630F9FD638029027b552aFA64b94c", "0x2799D90C6d44Cb9Aa5fBC377177F16C33E056b82", "0xc9De4B7F0C3d991e967158E4D4bFA4b51Ec0b114", "0x080aa07E2C7185150d7e4DA98838A8d2feac3dfC", "0xbF18F246B9301F231e9561B35A3879769BB46375", "0x01A28ADc0EdD796b570EC4dA734e1AA809f6f1Fc", "0x24692791Bc444c5Cd0b81e3CBCaba4b04Acd1F3B", "0x2469f31A34FCaAc0debf73806cE39B2388874B13", "0x2467AA6B5A2351416fD4C3DeF8462d841feeecEC", "0x22c10728343E9d49Ef25080F74a223878A3d4052", "0x622dFfCc4e83C64ba959530A5a5580687a57581b", "0x35a69642857083BA2F30bfaB735dacC7F0bac969", "0x1063ce524265d5a3A624f4914acd573dD89ce988", "0x106Aa49295B525fcf959aA75eC3f7dCbF5352f1C", "0xAB130BC7ff83192656a4B3079741c296615899C0", "0xAb16E0d25c06CB376259cc18C1de4ACA57605589", "0xcA3Ea3061d638E02113aA960340c98343b5aCd62", "0xCa3c18a65b802eC267f8f4802545e7F53D24C75e", "0xb8327F32127aFE37a544c52B628653e222a93BaD", "0x90528aeb3a2B736B780fD1B6C478bB7E1d643170", "0x905E337c6c8645263D3521205Aa37bf4d034e745", "0x2e071D2966Aa7D8dECB1005885bA1977D6038A65", "0xb1BAFca3737268A96673A250173B6Ed8F1b5b65F", "0xeD247980396B10169BB1d36f6e278eD16700a60f", "0xca4718de42fc344E449F0A8b8f2bCA2c95d13516", "0xAB6CF87a50F17d7F5E1FEaf81B6fE9FfBe8EBF84", "0x1014613E2B3CBc4d575054D4982E580d9b99d7B1", "0x2f85E502a988AF76f7ee6D83b7db8d6c0A823bf9", "0x16d71a7470aaCEE7AF4c341D2F2eaDce41A48d1c", "0x73B534fb6F07381a29a60B01eed5ae57D4EE24D7", "0xB1eeF147028E9f480DbC5ccaA3277D417D1b85F0", "0xB70835D7822eBB9426B56543E391846C107bd32C", "0xD4CffeeF10F60eCA581b5E1146B5Aca4194a4C3b", "0xd4c435F5B09F855C3317c8524Cb1F586E42795fa", "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07", "0xf8e386EDa857484f5a12e4B5DAa9984E06E73705", "0x0F513fFb4926ff82D7F60A05069047AcA295C413", "0x0f598112679B78e17A4A9feBC83703710d33489c", "0x0F5D2fB29fb7d3CFeE444a200298f468908cC942", "0x2cb101d7dA0ebaA57D3F2fEf46D7FFB7BB64592B", "0x53FBaa187eD9EB5c67eb7b2e99674101EBcDD873", "0x7611B0AEd86DAF65Dee946C852af6874871De02E", "0x76195ffD0CFedf68625b3e5B64c7Bd904eeb9d6C", "0x8ce9411Df545d6b51A9bc52a89E0F6d1B54a06dd", "0x15bb32276f8e92FE0094A0d7f3B7A5aD330c25cA", "0x598a2a0dc06FdE68d585ef9c6d7AC1805e1fb5ab", "0x0Cf0Ee63788A0849fE5297F3407f701E122cC023", "0x0cF713b11C9b986EC40D65bD4F7fbd50F6ff2d64", "0x2F1B8C9d0A21b747D8ca370f93cb09D3DaF222EF", "0x4EBDf71E4455F99100923297c959534Af7F6435A", "0x15ef5b9447710Eab904e63e6233Ff540400d603f", "0x554FFc77F4251a9fB3c0E3590a6a205f8d4e067D", "0x554C20B7c486beeE439277b4540A434566dC4C02", "0x7928c8aBF1F74eF9F96D4D0a44e3b4209d360785", "0xd286603e0f5dE621B510a36c78C7616C015656f2", "0xaA7a9CA87d3694B5755f213B5D04094b8d0F0A6F", "0x4C0fBE1BB46612915E7967d2C3213cd4d87257AD", "0x8f8221aFbB33998d8584A2B05749bA73c37a938a", "0xf85fEea2FdD81d51177F6b8F35F0e6734Ce45F5F", "0x4F4f0Db4de903B88f2B1a2847971E231D54F8fd3", "0x36905Fc93280f52362A1CBAB151F25DC46742Fb5", "0x90b1B771d0814D607Da104b988efA39288219D62", "0xB15fE5a123e647ba594CEa7A1E648646f95EB4AA", "0xd2Fa8f92Ea72AbB35dBD6DECa57173d22db2BA49", "0xF1d9139C6512452Db91F25635457B844d7e22B8b", "0x164F64eF2A44444743c5472FA68fb3784060D286", "0xD884F9881e0aeABad79BE8A69122Cf998d067FfF", "0x56e7f2Cd7d5382506aaB084a67D70E603Cdb23f7", "0xeB7C20027172E5d143fB030d50f91Cece2D1485D", "0x59aDCF176ED2f6788A41B8eA4c4904518e62B6A4", "0xeDBaF3c5100302dCddA53269322f3730b1F0416d", "0x190e569bE071F40c704e15825F285481CB74B6cC", "0x190fB342aa6a15eB82903323ae78066fF8616746", "0xabFB11De26e9D9a57d3B7620424992310cC8Ca3A", "0xd44bb6663936CAb1310584A277f7DAa6943d4904", "0x994f0DffdbaE0BbF09b652D6f11A493fd33F42B9", "0x960b236A07cf122663c4303350609A66A7B288C0", "0xB8c77482e45F1F44dE1745F52C74426C631bDD52", "0x4f3AfEC4E5a3F2A6a1A411DEF7D7dFe50eE057bF", "0xaD4769638175F3737Ce994D9DF9636Df8Ac80432", "0xf49CDD50aD408d387d611F88A647179C3de3492b", "0x56ba2Ee7890461f463F7be02aAC3099f6d5811A8", "0x163733bcc28dbf26B41a8CfA83e369b5B3af741b", "0xB24754bE79281553dc1adC160ddF5Cd9b74361a4", "0xb4aB3237E966D4d63026c9296019F5c74D3D2BDC", "0xEB9951021698B42e4399f9cBb6267Aa35F82D59D", "0xeB9c0138d8ac10DD659640a4CC3D135C58B17B1B", "0x6F7A4bac3315B5082F793161a22e26666d22717f", "0x0C15D535F8c21d6f3A03dd46ae23A07e5d897C80", "0xEA642206310400cDA4c1c5b8E7945314Aa96b8a7", "0xEA610B1153477720748DC13ED378003941d84fAB", "0xcbeAEc699431857FDB4d37aDDBBdc20E132D4903", "0x367474F0dF2424F354Da6627670Fa9ffB8a1534D", "0x53148Bb4551707edF51a1e8d7A93698d18931225", "0x76f6e0D0EfE275DDB00F8563168e2DFeAa98AB54", "0x0C6C9bEEeB5DE377210930F09a7Ac9A99ff5E981", "0x951eBcECcd28830359B6d969808CC6E111dFB8f6", "0x2FA32a39fc1c399E0Cc7B2935868f5165De7cE97", "0x509A38b7a1cC0dcd83Aa9d06214663D9eC7c7F4a", "0x4E279D8638e8669Fad40e018Fc181D26EE780380", "0x4E260e3Ca268e40133C84b142De73108A7c1Ec99", "0xd819E892F4DF8659188E8BDA839fDf2215A513bC", "0x6f07480145d12a423bD0D1bab74d60a3e2aA9298", "0xB23be73573bC7E03DB6e5dfc62405368716d28a8", "0x55c2A0C171D920843560594dE3d6EEcC09eFc098", "0xEa11755Ae41D889CeEc39A63E6FF75a02Bc1C00d", "0xEa1f346faF023F974Eb5adaf088BbCdf02d761F4", "0xF78510eAd1514994eF8001432a435105f7729233", "0x7939882b54fcf0bCAe6b53dEc39Ad6e806176442", "0x39Bb259F66E1C59d5ABEF88375979b4D20D98022", "0x13119E34E140097a507B07a5564bDe1bC375D9e6", "0x767bA2915EC344015a7938E3eEDfeC2785195D05", "0x70b147E01E9285E7cE68B9BA437Fe3a9190E756a", "0x5554e04e76533E1d14c52f05beEF6c9d329E1E30", "0xab95E915c123fdEd5BDfB6325e35ef5515F1EA69", "0xF244176246168F24e3187f7288EdbCA29267739b", "0x6fFF3806Bbac52A20e0d79BC538d527f6a22c96b", "0x7585F835ae2d522722d2684323a0ba83401f32f5", "0x8F936fE0faF0604c9C0Ef2406bde0A65365515d6", "0x932FDEC46f4Ff4A5a941070B8be9202a6227f616", "0x55296f69f40Ea6d20E478533C15A6B08B654E758", "0x8e1b448EC7aDFc7Fa35FC2e885678bD323176E34", "0xcB3F902bf97626391bF8bA87264bbC3DC13469be", "0xb787d4eAc8899730bb8C57fc3c998c49c5244ec0", "0xF4FE95603881D0e07954fD7605E0e9a916e42C44", "0x599346779e90fc3F5F997b5ea715349820F91571", "0xf230b790E05390FC8295F4d3F60332c93BEd42e2", "0xB2Bfeb70B903F1BAaC7f2ba2c62934C7e5B974C4", "0xEBf2F9E8De960f64ec0fDCDa6Cb282423133347B", "0xD18e454D844eb0009D32E07A0Cde89E18d64CFb4", "0x19AEA60E2FD6AC54EcF2576292C8Fc7046429C37", "0x76974C7B79dC8a6a109Fd71fd7cEb9E40eff5382", "0x76960Dccd5a1fe799F7c29bE9F19ceB4627aEb2f", "0x6C2adC2073994fb2CCC5032cC2906Fa221e9B391", "0x0F4caFDCE3737601C598BcFD4Bbd69F75786bA40", "0x0F4CA92660Efad97a9a70CB0fe969c755439772C", "0x2ccbFF3A042c68716Ed2a2Cb0c544A9f1d1935E1", "0xf152FcA41BD23ff250292AF391236Db35e0e99c3", "0xcA2796F9F61dc7b238Aab043971e49c6164DF375", "0x5976F7dac1525eF3277836043bA474a35E6B4272", "0x5635ddEaBf9cdDA686995Fe90BEB5411831563FC", "0x56349223fe25f34f3E26c84A100EBa5F6e281eA0", "0x73dD069c299A5d691E9836243BcaeC9c8C1D8734", "0x8F0921f30555624143d427b340b1156914882C10", "0x107c4504cd79C5d2696Ea0030a8dD4e92601B82e", "0x16B0E62aC13a2fAeD36D18bce2356d25Ab3CfAD3", "0x16B5A0dE0520e1964a20aC8eF4034Bd7D0920d8f", "0x99ea4dB9EE77ACD40B119BD1dC4E33e1C070b80d", "0xb1c1Cb8C7c1992dba24e628bF7d38E71daD46aeB", "0x96A65609a7B84E8842732DEB08f56C3E21aC6f8a", "0x138A8752093F4f9a79AaeDF48d4B9248fab93c9C", "0x0f36A697f736e736Be543966272b81Be9526743D", "0x0F33bb20a282A7649C7B3AFf644F084a9348e933", "0x9041Fe5B3FDEA0f5e4afDC17e75180738D877A01", "0x4FBC28e3B3C1c50eE05dCD66D9fC614A0cb99705", "0x1985365e9f78359a9B6AD760e32412f4a445E862", "0x9501BFc48897DCEEadf73113EF635d2fF7ee4B97", "0x93E682107d1E9defB0b5ee701C71707a4B2E46Bc", "0x93e24cE396A9E7d7dE4A5bC616cf5fCaB0476626", "0x0Ebb614204E47c09B6C3FeB9AAeCad8EE060E23E", "0x4CF488387F035FF08c371515562CBa712f9015d4", "0x6f1A769952C60B2d03f46419Adeda91D87866dAb", "0xcbCC0F036ED4788F63FC0fEE32873d6A7487b908", "0xaAAf91D9b90dF800Df4F55c205fd6989c977E73a", "0xF7920B0768Ecb20A123fAc32311d07D193381d6f", "0xEA097A2b1dB00627B2Fa17460Ad260c016016977", "0x705EE96c1c160842C92c1aeCfCFfccc9C412e3D9", "0x55b9a11c2e8351b4Ffc7b11561148bfaC9977855", "0x55Bb6e6A27D3C36BCd8566a5345bB6BdEe30784A", "0x55BC55e7bf833747A8F278A631617FA51d09D9Eb", "0x13F1b7FDFbE1fc66676D56483e21B1ecb40b58E2", "0x13f11C9905A08ca76e3e853bE63D4f0944326C72", "0x3618516F45CD3c913F81F9987AF41077932Bc40d", "0x4cA74185532DC1789527194e5B9c866dD33F4E82", "0x6F6DEb5db0C4994A8283A01D6CFeEB27Fc3bBe9C", "0x93Ba971f1C5C3111912Bc351906fC5ecE6C5bf6C", "0x3597bfD533a99c9aa083587B074434E61Eb0A258", "0x0C04d4f331DA8dF75f9E2e271E3f3F1494C66C36", "0x957c30aB0426e0C93CD8241E2c60392d08c6aC8e", "0xF41e5Fbc2F6Aac200Dd8619E121CE1f05D150077", "0xF4134146AF2d511Dd5EA8cDB1C4AC88C57D60404", "0xd73A66B8FB26Be8B0AcD7c52Bd325054Ac7d468b", "0x30ceCB5461A449A90081F5a5F55db4e048397BAB", "0x1543d0F83489e82A1344DF6827B23d541F235A50", "0x8Ef59B92F21f9E5f21F5f71510d1A7f87A5420bE", "0x8eFFd494eB698cc399AF6231fCcd39E08fd20B15", "0x2C4e8f2D746113d0696cE89B35F0d8bF88E0AEcA", "0x4F2dEae8d3dc9cCe16cA9D4Ed023c61301f45Ab8", "0x6ceE948C9d593c58Cba5Dfa70482444899D1341c", "0x539EfE69bCDd21a83eFD9122571a64CC25e0282b", "0xF1b98C681569069717B345714eCF4852fb3975a3", "0x7348d402Fcc22d37894Fe874Ff0B423fdCbFac58", "0x75Aa7B0d02532f3833b66c7f0Ad35376d373ddF8", "0x0C91B015AbA6f7B4738dcD36E7410138b29ADC29", "0x3506424F91fD33084466F402d5D97f05F8e3b4AF", "0x1961B3331969eD52770751fC718ef530838b6dEE", "0xf485C5E679238f9304D986bb2fC28fE3379200e5", "0xEDD7c94FD7B4971b916d15067Bc454b9E1bAD980", "0xd42debE4eDc92Bd5a3FBb4243e1ecCf6d63A4A5d", "0xF70a642bD387F94380fFb90451C2c81d4Eb82CBc", "0x4f5816985263Bb4eca89177a287b8a197711b23f", "0x6cb2b8Dc6a508C9a21dB9683D1A729715969a6eE", "0x966d9DB7293eb7bd82971cb07838e5679DaDc50F", "0x2eF1aB8a26187C58BB8aAeB11B2fC6D25C5c0716", "0x2C3C1F05187dBa7A5f2Dd47Dca57281C4d4F183F", "0x90c88CCd74e57e016acaE8aD1EAA12ECf4C06F33", "0xedCd82784027001d7aF57A34501C65A25F97fEe4", "0x165cFb9cCf8b185E03205Ab4118eA6afBdbA9203", "0x501262281B2Ba043e2fbf14904980689CDDB0C78", "0xd8950fDeaa10304B7A7Fd03a2FC66BC39f3c711a", "0xD8912C10681D8B21Fd3742244f44658dBA12264E", "0x523630976eB6147621B5c31c781eBe2Ec2a806E0", "0xF99f901124CbbE180984A247BA94CfbA0C764b2e", "0xf9907392B00AC8D8838Ab63f36034Bb54bBe7c86", "0x8b0C9f462C239c963d8760105CBC935C63D85680", "0x12B306fA98F4CbB8d4457FdFf3a0A0a56f07cCdf", "0x12B19D3e2ccc14Da04FAe33e63652ce469b3F2FD", "0xb5C33F965C8899D255c34CDD2A3efA8AbCbB3DeA", "0xb3616550aBc8AF79c7A5902DEF9Efa3bC9A95200", "0x2a1dbabe65c595B0022e75208C34014139d5d357", "0x92A5C97a4b14680d2990C0a523d7bEac66917c0F", "0x944F1A04ab8D735aCDbc46505c5b283F54289152", "0x4BBbC57aF270138Ef2FF2C50DbfAD684e9E0e604", "0xd94F2778e2B3913C53637Ae60647598bE588c570", "0x37256d58E298CACAa82aA0527D56521F1b19E1F5", "0x8dB54ca569D3019A2ba126D03C37c44b5eF81EF6", "0x386Faa4703a34a7Fdb19Bec2e14Fd427C9638416", "0x386467F1f3ddbE832448650418311a479EECFC57", "0xcE59d29b09aAE565fEEEf8E52f47c3CD5368C663", "0x729F8F3b96A51037aD2c536c6a46bd9d9a0FB9e9", "0x0b4Bf990Fa74Bf6363fA28b7c5f7f2C4E3e8b369", "0x0B4BdC478791897274652DC15eF5C135cae61E60", "0x8a854288a5976036A725879164Ca3e91d30c6A1B", "0xb3104b4B9Da82025E8b9F8Fb28b3553ce2f67069", "0x4D9e23a3842fE7Eb7682B9725cF6c507C424A41B", "0x5121E348e897dAEf1Eef23959Ab290e5557CF274", "0x8a187D5285d316bcBC9ADafc08b51d70a0d8e000", "0x1829aA045E21E0D59580024A951DB48096e01782", "0x58CFD2f4c5358E02Ab1532C31b69E837199a01fE", "0x58ca3065C0F24C7c96Aee8d6056b5B5deCf9c2f8", "0xF970b8E36e23F7fC3FD752EeA86f8Be8D83375A6", "0xf97c238b2277b2E7d15E278B61B250e96B23a194", "0xF0f8B0B8DBB1124261FC8d778E2287e3Fd2Cf4f5", "0xB07ec2c28834B889b1CE527Ca0F19364cD38935c", "0xaE616e72D3d89e847f74E8ace41Ca68bbF56af79", "0x2a8E98e256f32259b5E5Cb55Dd63C8e891950666", "0x515669d308f887Fd83a471C7764F5d084886D34D", "0xb6EE9668771a79be7967ee29a63D4184F8097143", "0xB6eD7644C69416d67B522e20bC294A9a9B405B31", "0x3883f5e181fccaF8410FA61e12b59BAd963fb645", "0x986EE2B944c42D017F52Af21c4c69B84DBeA35d8", "0xD3C00772B24D997A812249ca637a921e81357701", "0x2DBE0f03f1dddbdbc87557e86dF3878AE25af855", "0x9720b467a710382A232a32F540bDCed7d662a10B", "0x8b9C35C79AF5319C70dd9A3E3850F368822ED64E", "0xb5449411a6e1e6CbfB306b64F49feC91f49135d9", "0xEe9704a1D61aA2C1401e2303Ac7E1f81c29ED860", "0xD024645809F74043cd2133C6afEb46f0DE4aD88F", "0xd9c226581D060380353a33444d86DE1c15952165", "0x9267AbDC61A6C51a069766A910a79f51bAAD9D3e", "0x92685E93956537c25Bb75D5d47fca4266dd628B8", "0xEF68e7C694F40c8202821eDF525dE3782458639f", "0xEf6B4cE8C9Bc83744fbcdE2657b32eC18790458A", "0x71F7B56F9F8641f73cA71512a93857a7868d1443", "0x71f1Bc89f38B241f3eBF0D5a013Fa2850c63a1D4", "0x7731EE8B0b0ab88977BE7922849eB767bBE8DA15", "0x773450335eD4ec3DB45aF74f34F2c85348645D39", "0x6B87999bE87358065bBdE41e8a0fe0B7b1cd2514", "0x94d6b4fB35fB08Cb34Aa716ab40049Ec88002079", "0x9214eC02CB71CbA0ADA6896b8dA260736a67ab10", "0x92185519dC5A3aB41396d1DA2bb4134eA3921172", "0x0D6DD9f68d24EC1d5fE2174f3EC8DAB52B52BaF5", "0x6a0A97E47d15aAd1D132a1Ac79a480E3F2079063", "0xb53A96bcBdD9CF78dfF20BAB6C2be7bAec8f00f8", "0x8d5A69dc82a47594881256F2eef81770274fA30f", "0x37E8789bB9996CaC9156cD5F5Fd32599E6b91289", "0xB98d4C97425d9908E66E53A6fDf673ACcA0BE986", "0x4A42d2c580f83dcE404aCad18dab26Db11a1750E", "0x0aeF06DcCCC531e581f0440059E6FfCC206039EE", "0xB91318F35Bdb262E9423Bc7c7c2A3A93DD93C92C", "0xB915ff79170D606935BceAF000d77cA4Ed92d993", "0x98F5e9b7F0e33956C0443E81bF7deB8B5b1ed545", "0x51ee82641Ac238BDe34B9859f98F5F311d6E4954", "0xEE74110fB5A1007b06282e0DE5d73A61bf41d9Cd", "0x584B44853680ee34a0F337B712a8f66d816dF151", "0xAef38fBFBF932D1AeF3B808Bc8fBd8Cd8E1f8BC5", "0x18edc1b644839eed61C69E624e96Bbd469a2eF52", "0xcFD6Ae8BF13f42DE14867351eAff7A8A3b9FbBe7", "0xac3211a5025414Af2866FF09c23FC18bc97e79b1", "0xAc3Da587eac229C9896D919aBC235CA4Fd7f72c1", "0xf6cFe53d6FEbaEEA051f400ff5fc14F0cBBDacA1", "0xF6c01343020a7F37f9A9bdc2c1A5dBe8604DE62C", "0xd341d1680Eeee3255b8C4c75bCCE7EB57f144dAe", "0xd348e07A2806505B856123045d27aeeD90924b50", "0xb62d18DeA74045E822352CE4B3EE77319DC5ff2F", "0xB62132e35a6c13ee1EE0f84dC5d40bad8d815206", "0x910Dfc18D6EA3D6a7124A6F8B5458F281060fa4c", "0xB96eB33E4a1a9ea3B8581aBC8185F9597E45E8AA", "0x0AbdAce70D3790235af448C88547603b945604ea", "0x0ABeFb7611Cb3A01EA3FaD85f33C3C934F8e2cF4", "0x0AbbE7a2e6a4316715074147caFa5BF85d624e2E", "0x4A37A91eec4C97F9090CE66d21D3B3Aadf1aE5aD", "0xAEA1C18A992984831002D0cf90E291FB52d72649", "0xEce701C76bD00D1C3f96410a0C69eA8Dfcf5f34E", "0x17fD666fa0784885fa1AFEc8AC624d9b7e72B752", "0x17f8aFB63DfcDcC90ebE6e84F060Cc306A98257D", "0x17F93475d2A978f527c3f7c44aBf44AdfBa60D5C", "0x8aA33A7899FCC8eA5fBe6A608A109c3893A1B8b2", "0x0D8775F648430679A709E98d2b0Cb6250d2887EF", "0x0d88eD6E74bbFD96B831231638b66C05571e824F", "0x342Ba159F988F24f0b033F3cc5232377eE500543", "0x342D4b16B3856cD468cf9d4d33379b8dbC289752", "0x170b275CEd089FffAEBFe927F445a350ED9160DC", "0x17052d51E954592C1046320c2371AbaB6C73Ef10", "0x8a77e40936BbC27e80E9a3F526368C967869c86D", "0x8a7b7B9B2f7d0c63F66171721339705A6188a7D5", "0x74C1E4b8caE59269ec1D85D3D4F324396048F4ac", "0x74CEDa77281b339142A36817Fa5F9E29412bAb85", "0x514910771AF9Ca656af840dff83E8264EcF986CA", "0x57Ab1E02fEE23774580C119740129eAC7081e9D3", "0x57aD67aCf9bF015E4820Fbd66EA1A21BED8852eC", "0xEEF6E90034eEa89E31Eb4B8eaCd323F28A92eaE4", "0xaE73B38d1c9A8b274127ec30160a4927C4d71824", "0x1844b21593262668B7248d0f57a220CaaBA46ab9", "0x2dCFAAc11c9EebD8C6C42103Fe9e2a6AD237aF27", "0x38968746147BBAeb882F356Ad9A57594bB158235", "0x3893b9422Cd5D70a81eDeFfe3d5A1c6A978310BB", "0x1831887fBabF783910dB128E60C41BFa016059D8", "0x58bf7df57d9DA7113c4cCb49d8463D4908C735cb", "0x58b6A8A3302369DAEc383334672404Ee733aB239", "0xeC46f8207D766012454c408De210BCBc2243E71c", "0xeEAc3F8da16bb0485a4A11c5128b0518DaC81448", "0x5136C98A80811C3f46bDda8B5c4555CFd9f812F0", "0x4d829f8C92a6691c56300D020c9e0dB984Cfe2BA", "0x4D8fc1453a0F359e99c9675954e656D80d996FbF", "0x1776e1F26f98b1A5dF9cD347953a26dd3Cb46671", "0x177d39AC676ED1C67A2b268AD7F1E58826E5B0af", "0xF629cBd94d3791C9250152BD8dfBDF380E2a3B9c", "0xd3e2f9dFff5A6feeECE5dBCEE3b86cb375fd8C98", "0x14839bf22810F09fb163AF69BD21Bd5476F445Cd", "0x9742fA8CB51d294C8267DDFEad8582E16f18e421", "0x6D5caC36c1AE39f41d52393b7a425d0A610ad9f2", "0x78fE18e41f436e1981a3a60D1557c8a7a9370461", "0x78fd5d570Ad8D2a8Af142118CD044010Ca0aD9e5", "0x9899AF5Aa1EfA90921d686212c87e70F4fbea035", "0xaf6161b24615903e8264bF948bF485e5B3D01a1A", "0x1410434b0346f5bE678d0FB554E5c7ab620f8f4a", "0xB3030869CB6F67502CE592bE2419Bb948448bf56", "0x4D11061ec8f401EDC2395b5f439A05eeE6CCFa50", "0x728781E75735dc0962Df3a51d7Ef47E798A7107E", "0x4bDDCF906f675505103c49dC158a40c9b019FfEF", "0x4bD06BB10D440dA204C3B27cE2e881cA35139d92", "0x8a95ca448A52C0ADf0054bB3402dC5e09CD6B232", "0x94298F1e0Ab2DFaD6eEFfB1426846a3c29D98090", "0x8dd5fbCe2F6a956C3022bA3663759011Dd51e73E", "0x37427576324fE1f3625c9102674772d7CF71377d", "0x8B1F49491477e0fB46a29fef53F1EA320D13c349", "0xce3d9c3F3D302436D12f18ECA97A3b00e97bE7cd", "0x78a73B6CBc5D183CE56e786f6e905CaDEC63547B", "0x0B24fDf35876bbE2A1cC925321B8c301017474D4", "0x2A05d22DB079BC40C2f77a1d1fF703a56E631cc1", "0x6Aac8CB9861E42bf8259F5AbDC6aE3Ae89909E11", "0xD0D6D6C5Fe4a677D343cC433536BB717bAe167dD", "0x2D4F4A2984eC6Fd75FF3673EceaBA5b9f23Af09d", "0x91126CFA7dB2983527B0B749CC8a61fdeFFeDC28", "0x2baac9330Cf9aC479D819195794d79AD0c7616e3", "0xB97048628DB6B661D4C2aA833e95Dbe1A905B280", "0x4a220E6096B25EADb88358cb44068A3248254675", "0xf9F7c29CFdf19FCf1f2AA6B84aA367Bcf1bD1676", "0xf9F0FC7167c311Dd2F1e21E9204F87EBA9012fB2", "0xf6Bfe607CfbCCD63309dB5C138532a0560ABd271", "0xf6b6AA0Ef0f5Edc2C1c5d925477F97eAF66303e7", "0xF6B55acBBC49f4524Aa48D19281A9A77c54DE10f", "0xB63B606Ac810a52cCa15e44bB630fd42D8d1d83d", "0xCc80C051057B774cD75067Dc48f8987C4Eb97A5e", "0x744d70FDBE2Ba4CF95131626614a1763DF805B9E", "0x72aDadb447784dd7AB1F472467750fC485e4cb2d", "0x1122B6a0E00DCe0563082b6e2953f3A943855c1F", "0xcfb98637bcae43C13323EAa1731cED2B716962fD", "0xecd570bBf74761b960Fa04Cc10fe2c4e86FfDA36", "0xB64ef51C888972c908CFacf59B47C1AfBC0Ab8aC", "0xb90E64082D00437e65A76d4c8187596BC213480a", "0xEe609fE292128Cad03b786DBb9Bc2634Ccdbe7fC", "0x327682779bAB2BF4d1337e8974ab9dE8275A7Ca8", "0x51DB5Ad35C671a87207d88fC11d593AC0C8415bd", "0xd04963dE435BD4d25B1Cc8f05870F49eDbfc8C18", "0xd6adC5e386D499361CcC5752F791b45132E7e6e4", "0x0bb217E40F8a5Cb79Adf04E1aAb60E5abd0dfC1e", "0x4DF812F6064def1e5e029f1ca858777CC98D2D81", "0x4DF47B4969B2911C966506E3592c41389493953b", "0x4b35e0AB998Ebe8414871c13cf778F9D0Bbdf609", "0x5789e2B5460caE9329d93A78511E2aC49f98a1f6", "0x5783862cef49094bE4DE1fe31280B2E33cF87416", "0x781aca570D581f51a2FAC341F768242ae2220Bf9", "0x14FffB1e001615b7Fb7c7857BDf440a610022E5B", "0x14F37B574242D366558dB61f3335289a5035c506", "0x1234567461d3f8Db7496581774Bd869C83D51c93", "0x123aB195DD38B1b40510d467a6a359b201af056f", "0x4B4e611823702285FD526D7A8A3B0Aa99aB2DBCD", "0x6A62B2ef5A3E089aFF063DD1Ce8263F43f2ACD09", "0xD0352a019e9AB9d757776F532377aAEbd36Fd541", "0x12480E24eb5bec1a9D4369CaB6a80caD3c0A377A", "0x1245ef80F4d9e02ED9425375e8F649B9221b31D8", "0x6b9e8076a536459303DB301Ba4430913a7f14C5a", "0x7728dFEF5aBd468669EB7f9b48A7f70a501eD29D", "0x540449E4D172cd9491c76320440cD74933d5691a", "0xCc13Fc627EFfd6E35D2D2706Ea3C4D7396c610ea", "0xCeD4E93198734dDaFf8492d525Bd258D49eb388E", "0xbbFF862d906E348E9946Bfb2132ecB157Da3D4b4", "0xfDBc1aDc26F0F8f8606a5d63b7D3a3CD21c22B23", "0xfB7dA9863E030495Db8b4D067d665fc8433ffF85", "0x43287C95Eb5Bb4fe8CC8F0CF1A5980A0AbD555B7", "0x056017c55aE7AE32d12AeF7C679dF83A85ca75Ff", "0x9C1d13D5a8fd4a8ac89917d31D40Db454D1ee60b", "0x264Dc2DedCdcbb897561A57CBa5085CA416fb7b4", "0x03C780cD554598592B97b7256dDAad759945b125", "0xC4Bcd64CB216D49fD3C643A32762F34626b45a1a", "0xA15C7Ebe1f07CaF6bFF097D8a589fb8AC49Ae5B3", "0xc27A2F05fa577a83BA0fDb4c38443c0718356501", "0x5E6b6d9aBAd9093fdc861Ea1600eBa1b355Cd940", "0x5e6016Ae7d7C49d347dcF834860B9f3Ee282812b", "0x263c618480DBe35C300D8d5EcDA19bbB986AcaeD", "0x03DF4C372a29376D2c8DF33A1B5F001CD8d68b0E", "0x9c6Fa42209169bCeA032e401188a6fc3e9C9f59c", "0x20F7A3DdF244dc9299975b4Da1C39F8D5D75f05A", "0x4355fC160f74328f9b383dF2EC589bB3dFd82Ba0", "0x607F4C5BB672230e8672085532f7e901544a7375", "0xdA2C424Fc98c741c2d4ef2f42897CEfed897CA75", "0x638AC149eA8EF9a1286C41B977017AA7359E6Cfa", "0xc20464e0C373486d2B3335576e83a218b1618A5E", "0x40395044Ac3c0C57051906dA938B54BD6557F212", "0x5e8F855966D638135a968861E80DdA722291B06d", "0x5e888B83B7287EED4fB7DA7b7d0A0D4c735d94b3", "0x631c0D6f503C900e969C14d80A61D94e34cb0899", "0x255Aa6DF07540Cb5d3d297f0D0D4D84cb52bc8e6", "0x093e5C256Ff8B32c7F1377f4C20e331674C77F00", "0x49bD2DA75b1F7AF1E4dFd6b1125FEcDe59dBec58", "0xdb455c71C1bC2de4e80cA451184041Ef32054001", "0x3EDD235C3E840C1F29286B2e39370a255C7B6fdb", "0x86410dB4D61c40a8e1Df9f859069d5A15896195B", "0x80A7E048F37A50500351C204Cb407766fA3baE7f", "0x5F75b1dCfb49229ea25bD05b7112706898FF3c48", "0xE2E6D4BE086c6938B53B22144855eef674281639", "0xC16b542ff490e01fcc0DC58a60e1EFdc3e357cA6", "0xc166038705FFBAb3794185b3a9D925632A1DF37D", "0xFA1a856Cfa3409CFa145Fa4e20Eb270dF3EB21ab", "0xFA1DE2Ee97e4c10C94C91Cb2b5062b89Fb140b82", "0x0947b0e6D821378805c9598291385CE7c791A6B2", "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d", "0xE477292f1B3268687A29376116B0ED27A9c76170", "0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef", "0x80fB784B7eD66730e8b1DBd9820aFD29931aab03", "0x7FC408011165760eE31bE2BF20dAf450356692Af", "0x7FCE2856899a6806eeEf70807985fc7554C66340", "0x5c4C22FD12B7461c6A929a94f4E7c1b802CB6d2a", "0x1e09BD8Cadb441632e441Db3e1D79909EE0A2256", "0x8377ee6d3545Bc6ff1425ee3015DC648B149C7B2", "0xdab0C31BF34C897Fb0Fe90D12EC9401caf5c36Ec", "0xDAb5dFa0966C3435dA991B39D205C3bA1c64fe31", "0xBB1fA4FdEB3459733bF67EbC6f893003fA976a82", "0x9e3319636e2126e3c0bc9e3134AEC5e1508A46c7", "0x9e386DA8CDfcf8b9E7490e3f2A4589c570CB2b2F", "0x662aBcAd0b7f345AB7FfB1b1fbb9Df7894f18e66", "0x43eE79e379e7b78D871100ed696e803E7893b644", "0x85e076361cc813A908Ff672F9BAd1541474402b2", "0x1e797Ce986C3CFF4472F7D38d5C4aba55DfEFE40", "0x5c3a228510D246b78a3765C20221Cbf3082b44a4", "0xe7Ad86D82F895ABFf0baC636b56F07Ba6418E434", "0xE14A603f7c77d4101A87859b8736a04CFD85C688", "0xE1479d294807379320DCa9a9e9002AC644539099", "0xc42209aCcC14029c1012fB5680D95fBd6036E2a0", "0xBa9Be322fB1DeC8dcD19ff229324Aba2921E2316", "0xba9d4199faB4f26eFE3551D490E3821486f135Ba", "0xBa9617322E920481758239F378D458f7f334750d", "0x9E46A38F5DaaBe8683E10793b06749EEF7D733d1", "0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27", "0xBDC5bAC39Dbe132B1E030e898aE3830017D7d969", "0x3cf9E0c385a5ABEC9FD2a71790AA344C4e8E3570", "0x1F103Fd7C4fA908c25387DA70eD287b632bD22A2", "0xA46B81e925F4E139A4808647365B26a7ECb80AC8", "0xE22E372217Fdb534103ddb16a032FC98f7e44108", "0x83984d6142934bb535793A82ADB0a46EF0F66B6d", "0x6531f133e6DeeBe7F2dcE5A0441aA7ef330B4e53", "0x63f584FA56E60e4D0fE8802b27C7e6E3b33E007f", "0x461733c17b0755CA5649B6DB08B3E213FCf22546", "0xdD6022077E43f26Da29821df962527157EfcF32e", "0xdD6Bf56CA2ada24c683FAC50E37783e55B57AF9F", "0xDd6C68bb32462e01705011a4e2Ad1a60740f217F", "0x09d4b4f3806aa1C22A964f9021b8A3a865C46147", "0x09DeBe702678140C1BE278213109719faB98D0d8", "0xe25b0BBA01Dc5630312B6A21927E578061A13f55", "0xe25ff6Eb959BCE67975778e46A47750C243B6B99", "0xE256BB0b2a3457E54Db3A41cF5A8B826ACa222A8", "0xe25f0974fea47682F6A7386E4217dA70512ec997", "0xe25bCec5D3801cE3a794079BF94adF1B8cCD802D", "0xC87c5dD86A3d567fF28701886fB0745aaa898da4", "0x459F7854776ED005B6Ec63a88F834fDAB0B6993e", "0xBa7DCBa2Ade319Bc772DB4df75A76BA00dFb31b0", "0xbA71B32e71a41339Aa4CEAa79528535AEFE488D8", "0xDD16eC0F66E54d453e6756713E533355989040E4", "0x9F5F3CFD7a32700C93F971637407ff17b91c7342", "0x253C7dd074f4BaCb305387F922225A4f737C08bd", "0x9fC0583220eB44fAeE9e2dc1E63F39204DDD9090", "0x06147110022B768BA8F99A8f385df11a151A9cc8", "0x65Be44C747988fBF606207698c944Df4442efE19", "0xfa05A73FfE78ef8f1a739473e462c54bae6567D9", "0xfA0eF5E034CaE1AE752d59bdb8aDcDe37Ed7aB97", "0x7cF271966F36343Bf0150F25E5364f7961c58201", "0x3eb91D237e491E0DEE8582c402D85CB440fb6b54", "0x9c9891F7795eB127BA4783B671573275fF3a83A9", "0x3c75226555FC496168d48B88DF83B95F16771F37", "0xe469c4473af82217B30CF17b10BcDb6C8c796e75", "0xDb5c44a179c646A221d6e4c33293450D0f21f2BD", "0x65e643B7B5e242be3626eb92aAa864C6B22279fb", "0x23aE3C5B39B12f0693e05435EeaA1e51d8c61530", "0x00c4B398500645eb5dA00a1a379a88B11683ba01", "0x25432dD810730331498C22FBf6b98432E7ef3E66", "0xE2D82Dc7dA0E6f882E96846451F4faBcc8f90528", "0xE41d2489571d322189246DaFA5ebDe1F4699F498", "0xA25D01d15fC0e3cDeDE1BEbEE4124394aaE0DB33", "0x865D176351f287fE1B0010805b110d08699C200A", "0x5f6E7fb7Fe92EA7822472bB0E8f1BE60D6A4EA50", "0xFB12e3CcA983B9f59D90912Fd17F8D745A8B2953", "0x03e3f0c25965f13DbbC58246738C183E27b26a56", "0xa7f976C360ebBeD4465c2855684D1AAE5271eFa9", "0xE7775A6e9Bcf904eb39DA2b68c5efb4F9360e08C", "0x7F2176cEB16dcb648dc924eff617c3dC2BEfd30d", "0x1EAb19e6623E8cBcAfc252E275F5b51C27656fAF", "0xA8BA4095833a3F924D86CB3941099C1ABB75ea13", "0x1c4481750daa5Ff521A2a7490d9981eD46465Dbd", "0xc19412f60021B60e7e6F5DBB6AdAB483D039E922", "0xfdcc07Ab60660de533b5Ad26e1457b565a9D59Bd", "0x1C3D496D9c135463944Ae0483F550E6455E5019e", "0x1Ef729B095d5C657099607a662c1aE29F932cB5a", "0x7F585B9130c64e9e9F470b618A7badD03D79cA7E", "0xE701CD3329057AeA9D54300DdD05e41b8D74727A", "0xe1e361A8A788802D45FE7ABB6fE4FFCf908F3E1f", "0x86E56f3c89a14528858e58B3De48c074538BAf2c", "0x80046305aaab08F6033b56a360c184391165dc2d", "0xA40106134c5bF4c41411554e6db99B95A15ed9d8", "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413", "0xc72fe8e3Dd5BeF0F9f31f259399F301272eF2a2D", "0x4672bAD527107471cB5067a887f4656D585a8A31", "0x231A24A88e675F2C96147A2B2D2bf658F0db31f9", "0xba6Db65CE7a226a5fAD3126302118CF8f5C50012", "0x09BcA6eBAb05Ee2ae945BE4edA51393d94Bf7b99", "0xdd007278B667F6bef52fD0a4c23604aA1f96039a", "0xe23cd160761f63FC3a1cF78Aa034b6cdF97d3E0C", "0x3E250A4f78410c29cfC39463a81f14a226690eB4", "0x494BBAf0124285E6eCB4Dfd9eAc76E18A9bf470F", "0xDd74a7A3769fA72561B3A69e65968F49748c690c", "0xBA187B09fFA8DDdc80d2571eD3cbC4Be0Af69E0c", "0x2368056f36035d01E7c2Aa2a15b87E47Cc9d2d23", "0x65292EeadF1426Cd2dF1C4793a3d7519f253913b", "0x3f4B726668da46f5e0E75aA5D478ACEc9f38210F", "0x469031B88Df2793d696F51bC54E3dDD06949d8c9", "0xbDba9386E7Cd910f13eC50c9F3Af128ab90626a4", "0x66497A283E0a007bA3974e837784C6AE323447de", "0x2008e3057BD734e10AD13c9EAe45Ff132aBc1722", "0x26E75307Fc0C021472fEb8F727839531F112f317", "0xE8c09672cfb9cFcE6E2edBB01057d9fa569F97c1", "0xE8C5E942B76099C0C6D78271BAd3ca002fA7c531", "0x5c543e7AE0A1104f78406C340E9C64FD9fCE5170", "0x5c5413BaD5f6FdB0f4fcD1457e46eAd8e01D73d3", "0xA823E6722006afe99E91c30FF5295052fe6b8E32", "0xa1ccc166faf0E998b3E33225A1A0301B1C86119D", "0xdd974D5C2e2928deA5F71b9825b8b646686BD200", "0xdd94De9cFE063577051A5eb7465D08317d8808B6", "0x809826cceAb68c387726af962713b64Cb5Cb3CCA", "0x2075d158924F5030aeCE55179848c2bD7EC5833f", "0x697beac28B09E122C4332D163985e8a73121b97F", "0xdAC17F958D2ee523a2206206994597C13D831ec7", "0xBDe8f7820b5544a49D34F9dDeaCAbEDC7C0B5adc", "0x8866d52303E372C2a2936d8Ea09AFd87BcBD8cf2", "0x8727c112C712c4a03371AC87a74dD6aB104Af768", "0x7d3E7D41DA367b4FDCe7CBE06502B13294Deb758", "0x5adc961D6AC3f7062D2eA45FEFB8D8167d44b190", "0x618E75Ac90b12c6049Ba3b27f5d5F8651b0037F6", "0xbf4A123C8124F995784f8aF11F284A86b36b3a8C", "0xBf4cFD7d1eDeeEA5f6600827411B41A21eB08abd", "0xE69a353b3152Dd7b706ff7dD40fe1d18b7802d31", "0xe6923E9b56Db1EeD1c9f430Ea761DA7565e260Fe", "0xfFe8196bc259E8dEDc544d935786Aa4709eC3E64", "0xFFE02ee4C69eDf1b340fCaD64fbd6b37a7b9e265", "0x48DF4E0296f908CEAb0428A5182D19B31fC037d6", "0x415116BAD878730F5Db008Ff381A73222128AD39", "0x4156D3342D5c385a87D264F90653733592000581", "0x9a642d6b3368ddc662CA244bAdf32cDA716005BC", "0x22F0AF8D78851b72EE799e05F54A77001586B18A", "0x7d49EAAc4c70aBC1A659122f08c0806aE44703Ef", "0x7d4b8Cce0591C9044a22ee543533b72E976E36C3", "0x5acD19b9c91e596b1f062f18e3D02da7eD8D1e50", "0x5acE17f87c7391E5792a7683069A8025B83bbd85", "0x8810C63470d38639954c6B41AaC545848C46484a", "0x881Ef48211982D01E2CB7092C915E647Cd40D85C", "0xc6B014274D7406641711Fb8889F93F4F11DEC810", "0x01C67791309c71aA4Ed373025a0C089696D7c9e4", "0x0766e79A6fD74469733e8330b3b461C0320fF059", "0x22a3D74c363379189B6Cc059D8FBd888E98Df5Ec", "0x62087245087125d3DB5B9A3D713d78E7BBc31e54", "0x8432A5A61Cf1CC5ca5Bc5aB919d0665427fb513c", "0x82fdedfB7635441aA5A92791D001fA7388da8025", "0x1dAAF3d62582639C6a7EaBb467E2db9b56faFbBD", "0xe933c0Cd9784414d5F278C114904F5A84b396919", "0x629aEe55ed49581C33ab27f9403F7992A289ffd5", "0xE0B7927c4aF23765Cb51314A0E0521A9645F0E2A", "0x9B70740e708a083C6fF38Df52297020f5DfAa5EE", "0x286BDA1413a2Df81731D4930ce2F862a35A609fE", "0x888666CA69E0f178DED6D75b5726Cee99A87D698", "0xfc14Ce8C88731Dba873c310A791d524B1832e9D0", "0xDffc3E92b1479CAEeB6B296C99651C00c17456E3", "0xE58b65d1c0C8e8b2a0e3A3AcEC633271531084ED", "0xfeDAE5642668f8636A11987Ff386bfd215F942EE", "0xC5ceA8292e514405967D958c2325106f2f48dA77", "0xa6a840E50bCaa50dA017b91A0D86B8b2d41156EE", "0xE94327D07Fc17907b4DB788E5aDf2ed424adDff6", "0xE94b04a0FeD112f3664e45adb2B8915693dD5FF3", "0x3a1Bda28AdB5B0a812a7CF10A1950c920F79BcD3", "0x1B3Fd5af61F90F70816A2DB2593d88e0901656EE", "0xfec0cF7fE078a500abf15F1284958F22049c2C7e", "0x047187e53477be70DBe8Ea5B799318f2e165052F", "0x02B9806a64cB05F02AA8dcc1C178b88159A61304", "0x275FD328C3986be83f8b60f79c73cf63Fde98Ca5", "0x44F12955189e3F01BE5daF1dd9002Ee4D774F42B", "0x44F588aEeB8C44471439D1270B3603c66a9262F1", "0x423e4322CDDa29156b49a17dfbd2aCC4b280600D", "0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe", "0x24dDFf6D8B8a42d835af3b440De91f3386554Aa4", "0x24DCc881E7Dd730546834452F21872D5cb4b5293", "0x013A06558f07d9E6F9A00c95a33f3a0E0255176b", "0xBC9395973BD35a3b4bD924F050d2778c07506EcB", "0x88aC94D5d175130347Fc95E109d77AC09dbF5ab7", "0x88A3E4F35D64aAD41A6d4030ac9AFE4356cB84fA", "0x88AE96845e157558ef59e9Ff90E766E22E480390", "0xe5a219d4DB92A701e79B6E548803C8ce55138686", "0xE5a7c12972f3bbFe70ed29521C8949b8Af6a0970", "0x7b1309c1522AfD4E66C31e1E6d0ec1319E1eba5E", "0x44830e5FBE354Af3c1C8d405170c08d3BC8A2cD9", "0x7DD7F56D697Cc0f2b52bD55C057f378F1fE6Ab4b", "0x6425c6BE902d692AE2db752B3c268AFAdb099D3b", "0x41e5560054824eA6B0732E656E3Ad64E20e94E45", "0x014B50466590340D41307Cc54DCee990c8D58aa8", "0x07Aa23BFD3e19f3A0508cA8Dc5425857C6D31488", "0x226bb599a12C826476e3A771454697EA52E9E220", "0x9af2c6B1A28D3d6BC084bd267F70e90d49741D5B", "0x9AF839687F6C94542ac5ece2e317dAAE355493A1", "0x9AF4f26941677C706cfEcf6D3379FF01bB85D5Ab", "0xBfD4a3C26996DFC9e85A951eB615aAC3b84C758B", "0xff56Cc6b1E6dEd347aA0B7676C85AB0B3D08B0FA", "0xDcB9FF81013c31FF686154B4502eF6BFaA102D2D", "0x219218f117DC9348b358b8471c55A073E5e0dA0b", "0x7dCB3B2356C822d3577D4d060D0D5D78C860488C", "0xE5F166c0D8872B68790061317BB6CcA04582C912", "0xE5f867dE1EA81346df5181b8b48DD6B0BB3357B0", "0x88FCFBc22C6d3dBaa25aF478C578978339BDe77a", "0xa578aCc0cB7875781b7880903F4594D13cFa8B98", "0xa3B5d1411905d9360B758518835Cd967718FdCcF", "0xc98e0639c6d2EC037A615341c369666B110e80E5", "0x2108E62D335Bbdc89eC3E9d8582F18DCFB0cDFf4", "0x2103D2F834A9D51443cc5bC04A083f091d3F2677", "0x6745fAB6801e376cD24F03572B9C9B0D4EdDDCcf", "0x446DCEda09D9533BD7f5A7f30b1202E5E4d18bCC", "0x4460a301f878E5d017A469672dE20FbA2814178c", "0xdF1cE35938F9ca2EAb682284F82A81a9D25665ce", "0xBC7De10AFe530843e71DfB2e3872405191e8d14A", "0x28a40acF39b1D3C932f42dD8068ad00A5Ad6448F", "0xBEB9eF514a379B997e0798FDcC901Ee474B6D9A1", "0x5B2e4a700dfBc560061e957edec8F6EeEb74a320", "0x5b26C5D0772E5bbaC8b3182AE9a13f9BB2D03765", "0xe9dE1C630753A15d7021Cc563429c21d4887506F", "0xa95592DCFfA3C080B4B40E459c5f5692F67DB7F8", "0x1bC608A5Cc1549F39edAd6FcCAdb6ebC5F545a74", "0x1BcBc54166F6bA149934870b60506199b6C9dB6D", "0x3Aa927a97594c3ab7d7bf0d47C71c3877D1DE4A1", "0x82125AFe01819Dff1535D0D6276d57045291B6c0", "0xBeef546ac8a4e0a80DC1E2d696968Ef54138f1d4", "0x28F97a66C7025F565b482Cd55a1b9Cc80B41Ff82", "0x687174f8C49ceb7729D925C3A961507ea4Ac7b28", "0xfcAC7A7515e9A9d7619fA77A1fa738111f66727e", "0xfcA47962D45ADFdfd1Ab2D972315dB4ce7CCf094", "0xDf6Ef343350780BF8C3410BF062e0C015B1DD671", "0x44197A4c44D6A059297cAf6be4F7e172BD56Caaf", "0x42d6622deCe394b54999Fbd73D108123806f6a18", "0x6733D909e10DDedB8d6181b213dE32A30cEac7ed", "0x025abAD9e518516fdaAFBDcdB9701b37fb7eF0FA", "0x1d10997e92011398a20612f9eE87E33449bC1Fe4", "0xa9240fBCAC1F0b9A6aDfB04a53c8E3B0cC1D1444", "0x84C2c31C04339c9938Adfe3F8013315c8906f071", "0x7A79ABD3905ef37b8D243c4C28ceE73a751EB076", "0x5BC7e5f0Ab8b2E10D2D0a3F21739FCe62459aeF3", "0x5d65D971895Edc438f465c17DB6992698a52318D", "0x1b22C32cD936cB97C28C5690a0695a82Abf688e6", "0x84543F868eC1b1FAC510d49d13C069f64cD2d5f9", "0xC5bBaE50781Be1669306b9e001EFF57a2957b09d", "0x9B11EFcAAA1890f6eE52C6bB7CF8153aC5d74139", "0x21aE23B882A340A22282162086bC98D3E2B73018", "0x422866a8F0b032c5cf1DfBDEf31A20F4509562b0", "0x68AA3F232dA9bdC2343465545794ef3eEa5209BD", "0xc081B1e603498D122309f799C327d64D7Ef2AcdD", "0xE0c21b3F45fEa3E5fDC811021Fb1F8842caccAd2", "0xe66cc41Dd03A170623DC087A69aD8D72E64Cb838", "0xa02e3bB9cEbc03952601B3724B4940e0845BeBcf", "0xA024E8057EEC474a9b2356833707Dd0579E26eF3", "0x1B5f21ee98eed48d292e8e2d3Ed82b40a9728A22", "0xBe428c3867F05deA2A89Fc76a102b544eaC7f772", "0x61725f3db4004AFE014745B21DAb1E1677CC328b", "0x9b6443b0fB9C241A7fdAC375595cEa13e6B7807A", "0x9b68bFaE21DF5A510931A262CECf63f41338F264", "0x21f0F0fD3141Ee9E11B3d7f13a1028CD515f459c", "0xa5a283557653f36cf9aA0d5cC74B1e30422349f2", "0xE50365f5D679CB98a1dd62D6F6e58e59321BcdDf", "0x7D5Edcd23dAa3fB94317D32aE253eE1Af08Ba14d", "0xBf256373a8e72DDB1ca00f24f8e902CD2fA5C5b1", "0xbf2179859fc6D5BEE9Bf9158632Dc51678a4100e", "0xC39E626A04C5971D770e319760D7926502975e47", "0xC3951d77737733174152532e8B0f27e2c4E9F0dC", "0x9a0242b7a33DAcbe40eDb927834F96eB39f8fBCB", "0x9a005c9a89BD72a4Bd27721E7a09A3c11D2b03C4", "0x245ef47D4d0505ECF3Ac463F4d81f41ADE8f1fd1", "0x01b3Ec4aAe1B8729529BEB4965F27d008788B0EB", "0x621d78f2EF2fd937BFca696CabaF9A779F59B3Ed", "0x9b8eb7a73a3C65FC3c892b494Ab29CB061Cf05aE", "0x3dC9a42fa7Afe57BE03c58fD7F4411b1E466C508", "0x873467738b5053f155639208b7495318fced5262", "0xe577f4F83b16CC2628f1b42f72aa07fAf88B79c0", "0xa5F8fC0921880Cb7342368BD128eb8050442B1a1", "0xa5Fd1A791C4dfcaacC963D4F73c6Ae5824149eA7", "0xC011A72400E58ecD99Ee497CF89E3775d4bd732F", "0x887834D3b8D450B6bAB109c252Df3DA286d73CE4", "0x289Fe11c6f46E28F9f1CfC72119AEE92C1dA50D0", "0xa33e729bf4fdeb868B534e1f20523463D9C46bEe", "0x64C86899bc02dD9af823B131e5ACD4369F72bD39", "0x64CdF819d3E75Ac8eC217B3496d7cE167Be42e80", "0x3A92bD396aEf82af98EbC0Aa9030D25a23B11C6b", "0xbf52F2ab39e26E0951d2a02b49B7702aBe30406a", "0x672a1AD4f667FB18A333Af13667aa0Af1F5b5bDD", "0x672b178875fAD8DDd817DA5cc4E367B63fdEe9aa", "0x818Fc6C2Ec5986bc6E2CBf00939d90556aB12ce5", "0x0249924245A19Aa2D7F5ddb0739A7132b7D094AB", "0x21692A811335301907ECD6343743791802Ba7Cfd", "0x2160E6c0aE8cA7D62fE1F57fC049F8363283Ff5f", "0xbc1234552EBea32B5121190356bBa6D3Bb225bb5", "0xfe5F141Bf94fE84bC28deD0AB966c16B17490657", "0x5DA69AaEd30B01F21884c6F51B2A7574e6a67E2C", "0xe6f74dcfa0E20883008d8C16b6d9a329189D0C30", "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "0x6810e776880C02933D47DB1b9fc05908e5386b96", "0xBec8f6D667594FB181c9d68e5c80C910888Be93D", "0xBC63aCdfafA94bd4D8C2Bb7A8552281f107242c0", "0x04F2E7221fdb1B52A68169B25793E51478fF0329", "0xC99Ddc30BB0cf76B07d90DcB6B267B8352697bEf", "0xC997d07b0bC607b6D1bCb6fB9D4a5579c466c3E5", "0x0235fE624e044A05eeD7A43E16E3083bc8A4287A", "0x27Dce1eC4d3f72C3E457Cc50354f1F975dDEf488", "0x4470BB87d77b963A013DB939BE332f927f2b992e", "0x6754e21b9EAa053c62d7854dD6561ae451B0cBCf", "0xc528c28FEC0A90C083328BC45f587eE215760A0F", "0x5Dff89a2caa4D76bc286F74D67Bd718eb834da61", "0xA94C128a138504E1F81d727cc21bcB9AE6581015", "0x1Bb9E8eA817d56eccC212CE63f7dA95298F98719", "0x08d32b0da63e2C3bcF8019c9c5d849d7a9d791e6", "0x08D109B4be6d131AcacdDd6fB6fa6F93e7eC72fe", "0x643B6870beabee941B9260a0A878bcF4A61Fb0f1", "0x41dBECc1cdC5517C6f76f6a6E836aDbEe2754DE3", "0x829A4cA1303383F1082B6B1fB937116e4b3b5605", "0x6888a16eA9792c15A4DCF2f6C623D055c8eDe792", "0xC64500DD7B0f1794807e67802F8Abbf5F8Ffb054", "0xa3C1E324CA1ce40db73eD6026c4A177F099B5770", "0x5A567e28dbFa2bBD3ef13C0a01be114745349657", "0x1D9e20e581a5468644fe74ccb6a46278ef377F9e", "0x62a56a4A2Ef4D355D34D10fBF837e747504d38d4", "0xDE11e2b7235FCb6E5039EAdAaACe5e4fF99c6B5B", "0x4824A7b64E3966B0133f4f4FFB1b9D6bEb75FFF7", "0xFf3519eeeEA3e76F1F699CCcE5E23ee0bdDa41aC", "0xBFbe5332f172d77811bC6c272844f3e54A7B23bB", "0x08CAF96D32986e65b81168485bD387FB41E198Cf", "0x5a276Aeb77bCfDAc8Ac6f31BBC7416AE1A85eEF2", "0x3D46454212c61ECb7b31248047Fa033120B88668", "0xa51D948Ff15fBAbac476aF160CBa6901cE47f4B0", "0xA517a46Baad6B054A76bD19c46844f717fe69fea", "0xA51153D9cf9d3cF6D58697b68eCCC158D1e40388", "0xA3D5c31c0b5BE106930329A96E261dB7b6f2AA3d", "0xBC86727E770de68B1060C91f6BB6945c73e10388", "0x399A0e6FbEb3d74c85357439f4c8AeD9678a5cbF", "0xB110eC7B1dcb8FAB8dEDbf28f53Bc63eA5BEdd84", "0x90fC19c31AA78B366D1C7544AD071f9F3046349a", "0xF813F3902bBc00A6DCe378634d3B79D84F9803d7", "0x6e2050CBFB3eD8A4d39b64cC9f47E711a03a5a89", "0xaBbb6bEbFA05aA13e908EaA492Bd7a8343760477", "0xaBbBB6447B68ffD6141DA77C18c7B5876eD6c5ab", "0xd780Ae2Bf04cD96E577D3D014762f831d97129d0", "0xeDF2d3e5FB70eAD2e6D8FE96845a5E59d52d2044", "0x7367A68039d4704f30BfBF6d948020C3B07DFC59", "0x0ffAB58EA5A71CC3cA40217706C3C401407fA4a8", "0x9901ed1e649C4a77C7Fff3dFd446ffE3464da747", "0xb7cB1C96dB6B22b0D3d9536E0108d062BD488F74", "0x10B123FdDde003243199aaD03522065dC05827A0", "0x4E84E9e5fb0A972628Cf4568c403167EF1D40431", "0xEda8B016efA8b1161208Cf041cD86972eeE0F31E", "0xB29678a4805a7d787dc9589E179D27F7575bB9f7", "0xd82Df0ABD3f51425Eb15ef7580fDA55727875f14", "0x4Cd988AfBad37289BAAf53C13e98E2BD46aAEa8c", "0xd7631787B4dCc87b1254cfd1e5cE48e96823dEe8", "0xf441212F3723330a8a135f94E0549c9B185A6Dc7", "0xf44745fBd41F6A1ba151df190db0564c5fCc4410", "0xb203b5495109c6C85615EbB2056F98301D470507", "0xB4EFd85c19999D84251304bDA99E90B92300Bd93", "0x2C82c73d5B34AA015989462b2948cd616a37641F", "0x151202C9c18e495656f372281F493EB7698961D5", "0x30f4A3e0aB7a76733D8b60b89DD93c3D0b4c9E2f", "0x8C65e992297d5f092A756dEf24F4781a280198Ff", "0xCd4b4b0F3284a33AC49C67961EC6e111708318Cf", "0xaaCf052428a5e8E583CA2bbaf3eacb34f161bc6B", "0x999774870A5cE35Cb9cF4D7D85437B97b49a383b", "0x999967E2Ec8A74B7c8E9dB19E039d920B31d39D0", "0x9992eC3cF6A55b00978cdDF2b27BC6882d88D1eC", "0xEA26c4aC16D4a5A106820BC8AEE85fd0b7b2b664", "0xD49ff13661451313cA1553fd6954BD1d9b6E02b9", "0xB4b1D2C217EC0776584CE08D3DD98F90EDedA44b", "0xF433089366899D83a9f26A773D59ec7eCF30355e", "0xd1D8C6cF1E6c83555D04CB05F55F142207932aA0", "0xF2fdc844160f61584c478BD9ADE99329893B20a6", "0xD850942eF8811f2A866692A623011bDE52a462C1", "0x4CC19356f2D37338b9802aa8E8fc58B0373296E7", "0x2Fe6AB85EBbf7776feE46d191eE4cEA322CeCf51", "0xea5f88E54d982Cbb0c441cde4E79bC305e5b43Bc", "0x79a86D5A5904C64458DfdA7C6807a2F870C61367", "0x39013F961c378f02C2b82A6E1d31E9812786FD9D", "0x30Aee7F259d6D1564ebEf457847c672B30f13cbC", "0x76e82406a5040B605C6D30cAF4802e7EB3184Bbc", "0x532D69ae56E07cE965eB7F8164a78F3A81c2Bf38", "0x566Fd7999B1Fc3988022bD38507A48F0bCf22c77", "0xf7B098298f7C69Fc14610bf71d5e02c60792894C", "0xB8742486C723793Cf5162bb5D3425ED9cD73D049", "0x90162f41886c0946D09999736f1C15c8a105A421", "0x2Cae18DD1223Aea3bFDFDdFEE4cfBbCB4b80Cc22", "0x2CA72c9699b92b47272c9716c664cAD6167c80B0", "0x105d97ef2E723f1cfb24519Bc6fF15a6D091a3F1", "0xCA0e7269600d353F70b14Ad118A49575455C0f2f", "0x595832F8FC6BF59c85C527fEC3740A1b7a361269", "0xEBc86d834756621444a8a26B4cF81B625fe310cD", "0x308bB08935d35E3Cdb848Ce45E1DE4072bb762db", "0xB802b24E0637c2B87D2E8b7784C055BBE921011a", "0xf7e983781609012307f2514f63D526D83D24F466", "0xB893ea9FC7229d65C626F614C493d931b5EE75cc", "0x15f173b7aca7Cd4a01d6f8360e65fb4491d270C1", "0x765f0C16D1Ddc279295c1a7C24B0883F62d33F75", "0x7654915A1b82D6D2D0AFc37c52Af556eA8983c7E", "0xF19919F76C4bfD1d640E17fE2721114beaC1A3AB", "0xCdCFc0f66c522Fd086A1b725ea3c0Eeb9F9e8814", "0x4c382F8E09615AC86E08CE58266CC227e7d4D913", "0xD8E2474f240CAC4De73B234bd8c11A4daf92CDB4", "0x2F5e044ad4Adac34C8d8dF738Fac7743edA1409C", "0x2f58eB27Bd1d9Da9441538de718c9B0e016E2745", "0xD7AA94f17d60bE06414973a45FfA77efd6443f0F", "0xF26ef5E0545384b7Dcc0f297F2674189586830DF", "0xF4c07b1865bC326A3c01339492Ca7538FD038Cc0", "0xeAb43193CF0623073Ca89DB9B712796356FA7414", "0x79650799e7899A802cB96C0Bc33a6a8d4CE4936C", "0x7627de4B93263a6a7570b8dAfa64bae812e5c394", "0xB45a50545bEEAB73F38F31E5973768C421805E5E", "0xb45d7Bc4cEBcAB98aD09BABDF8C818B2292B672c", "0x0e0989b1f9B8A38983c2BA8053269Ca62Ec9B195", "0x9375b738083101617F0642D7DBeAA89E361545E3", "0x0ce36BAD60211b40575aC03c0a4d06CccFF44614", "0x6Fc82a5fe25A5cDb58bc74600A40A69C065263f8", "0x6F59e0461Ae5E2799F1fB3847f05a63B16d0DbF8", "0x6f539a9456A5BCb6334A1A41207c3788f5825207", "0x954b5De09A55e59755aCBda29e1Eb74A45D30175", "0x9541FD8B9b5FA97381783783CeBF2F5fA793C262", "0x168296bb09e24A88805CB9c33356536B980D3fC5", "0xb4c55b5a1FaF5323e59842171c2492773a3783Dd", "0xb4C9abc8a74Bd2E0E0b7AC5ecE30792e65D86c59", "0xd70d884eC2a69aB23C2b32a674818960D2da9b77", "0x8C01aDa8e708993A891D57D1b3169479a20aCB3A", "0x4f878C0852722b0976A955d68B376E4Cd4Ae99E5", "0x4F8849C425881FA6f7B4FCaE0A367053cE3230B7", "0x533ef0984b2FAA227AcC620C67cce12aA39CD8CD", "0x55Fc04A73f058832b4F3498Dc83cEb6E53a9e314", "0x55F93985431Fc9304077687a35A1BA103dC1e081", "0x701C244b988a513c945973dEFA05de933b23Fe1D", "0xB4Dd889a924C5a30dB857b8078886b764214a56c", "0xAd8DD4c725dE1D31b9E8F8D146089e9DC6882093", "0x4E0603e2A27A30480E5e3a4Fe548e29EF12F64bE", "0x6f259637dcD74C767781E37Bc6133cd6A68aa161", "0x4CEdA7906a5Ed2179785Cd3A40A69ee8bc99C466", "0x4cE6B362Bc77A24966Dda9078f9cEF81b3B886a7", "0x998b3B82bC9dBA173990Be7afb772788B5aCB8Bd", "0xEA38eAa3C86c8F9B751533Ba2E562deb9acDED40", "0x8c709eb2eAe436607cdae2B7FDF7Ef323C11010e", "0x8C74878735C79c5fDa55284eBcC5eAc2030bA491", "0x2C974B2d0BA1716E644c1FC59982a89DDD2fF724", "0x8eb965ee9cCFBCE76c0a06264492c0afEfc2826d", "0x8eB24319393716668D768dCEC29356ae9CfFe285", "0x13EA82D5e1A811F55BDA9c86FdD6195A6bD23Aed", "0xB17DF9a3B09583a9bDCf757d6367171476D4D8a3", "0xd2d6158683aeE4Cc838067727209a0aAF4359de3", "0xF87F0D9153fea549c728Ad61cb801595a68b73de", "0xAbdf147870235FcFC34153828c769A70B3FAe01F", "0x16662F73dF3e79e54c6c5938b4313f92C524C120", "0x10c0337c42843E0b8CE743d7D5fF39b711f3aD82", "0x3543638eD4a9006E4840B105944271Bcea15605D", "0x2eb86e8fC520E0F6Bb5D9Af08F924fe70558Ab89", "0x6E34d8d84764D40f6D7b39cd569Fd017bF53177D", "0x708876f486e448Ee89eB332bFbC8E593553058b9", "0xB10F2464d9CEA5CDD51bB595084b258952D3523f", "0x737F98AC8cA59f2C68aD658E3C3d8C8963E40a4c", "0xEb2dA9FAC54284cEA731D1F10bb34EEcB3c00c14", "0x763186eB8d4856D536eD4478302971214FEbc6A9", "0x5512e1D6A7BE424b4323126B4f9E86D023F95764", "0x36B015105b8e1b74CE026c8af57f3282150eEBc2", "0xAa26B73BFdc80B5c7D2cFBFc30930038FB7FA657", "0x6fB3e0A217407EFFf7Ca062D46c26E5d60a14d69", "0x4c5601164e2048a4154DE91Fa5e0B07E626CaB7F", "0xb444208cB0516C150178fCf9a52604BC04A1aCEa", "0xF18023908a52D7f058D40277f947748ab9619ef1", "0xCDB7eCFd3403Eef3882c65B761ef9B5054890a47", "0xaA56982192589D26AE746AA64a1d5FaB20A5B4B9", "0x8e5afc69f6227A3ad75eD346c8723Bc62ce97123", "0x55648De19836338549130B1af587F16beA46F66B", "0x70a72833d6bF7F508C8224CE59ea1Ef3d0Ea3A38", "0x7641b2Ca9DDD58adDf6e3381c1F994Aac5f1A32f", "0xb2F7EB1f2c37645bE61d73953035360e768D81E6", "0xf278c1CA969095ffddDED020290cf8B5C424AcE2", "0x931684139f756C24eC0731E9F74FE50e5548dDeF", "0x95dAaaB98046846bF4B2853e23cba236fa394A31", "0x59416A25628A76b4730eC51486114c32E0B582A1", "0x594a69Eb7D76bE8ac1b0C00589389b2025D826c8", "0xEBBdf302c940c6bfd49C6b165f457fdb324649bc", "0x336F646F87D9f6bC6Ed42Dd46E8b3fD9DbD15C22", "0x16aF5bfb4Ae7E475b9aDC3Bf5Cb2f1E6a50d7940", "0x1040613788e99C1606Bd133dB0eD7f7dbdf0Cc80", "0x8f3470A7388c05eE4e7AF3d01D8C722b0FF52374", "0x9386c6c1eaA9a00C8568f9786F11d375e0Ba1E9c", "0x9389434852b94bbaD4c8AfEd5B7BDBc5Ff0c2275", "0xd2308446536a0Bad028Ab8C090D62E1eA2A51f24", "0xd234BF2410a0009dF9c3C63b610c09738f18ccD7", "0xf11a7a2F33ae990F2A847BA54F609E14D37c3d3a", "0xf11cc5138dD9494e1d15D8bf27d7bd0aeAe61E03", "0xf11A60D759a7be40a4ea982f56033b87c5e3B931", "0xd4fa1460F537bb9085d22C7bcCB5DD450Ef28e3a", "0x6c6EE5e31d828De241282B9606C8e98Ea48526E2", "0x6EC8a24CaBdc339A06a172F8223ea557055aDAa5", "0x103c3A209da59d3E7C4A89307e66521e081CFDF0", "0x0E8d6b471e332F140e7d9dbB99E5E3822F728DA6", "0x331A550a2C7f96384eb69127AA0eA9AD4b5Da099", "0x16f812Be7FfF02cAF662B85d5d58a5da6572D4Df", "0x16F4656d9E61d6B924addB706Bdc71A69Fd6681b", "0xEbeD4fF9fe34413db8fC8294556BBD1528a4DAca", "0x558EC3152e2eb2174905cd19AeA4e34A23DE9aD6", "0x9002D4485b7594e3E850F0a206713B305113f69e", "0x900b4449236a7bb26b286601dD14d2bDe7a6aC6c", "0x99a650192E81772657C7dc047d2E18f67C758E94", "0xcB97e65F07DA24D46BcDD078EBebd7C6E6E3d750", "0xCb94be6f13A1182E4A4B6140cb7bf2025d28e41B", "0xB72627650F1149Ea5e54834b2f468E5d430E67bf", "0xd248B0D48E44aaF9c49aea0312be7E13a6dc1468", "0x0D5516103752b3954D95621f470A8261151Da2e4", "0x922105fAd8153F516bCfB829f56DC097a0E1D705", "0x922aC473A3cC241fD3a0049Ed14536452D58D73c", "0x77761e63C05aeE6648FDaeaa9B94248351AF9bCd", "0x3136eF851592aCf49CA4C825131E364170FA32b3", "0x3137619705b5fc22a3048989F983905e456b59Ab", "0xaFC39788c51f0c1Ff7B55317f3e70299e521Fff6", "0xcC4eF9EEAF656aC1a2Ab886743E98e97E090ed38", "0xEF2463099360a085f1f10b076Ed72Ef625497a06", "0xEF2E9966eb61BB494E5375d5Df8d67B7dB8A780D", "0xb3Bd49E28f8F832b8d1E246106991e546c323502", "0xf333b2Ace992ac2bBD8798bF57Bc65a06184afBa", "0xD01534F4564234A4579b1BC1f3413873B7B3D9D7", "0xD01DB73E047855Efb414e6202098C4Be4Cd2423B", "0x4DC3643DbC642b72C158E7F3d2ff232df61cb6CE", "0x0D262e5dC4A06a0F1c90cE79C7a60C09DfC884E4", "0x2AEC18c5500f21359CE1BEA5Dc1777344dF4C0Dc", "0xCc34366E3842cA1BD36c1f324d15257960fCC801", "0x8d12A197cB00D4747a1fe03395095ce2A5CC6819", "0x126D8fa0b0B427ca0b708b6800858fF25E7FCC66", "0x14C926F2290044B647e1Bf2072e67B495eff1905", "0x7705FaA34B16EB6d77Dfc7812be2367ba6B0248e", "0x7703C35CfFdC5CDa8D27aa3df2F9ba6964544b6e", "0x71e8d74fF1C923E369D0e70DFb09866629C4DD35", "0xf05a9382A4C3F29E2784502754293D88b835109C", "0xd5b9A2737C9B2Ff35EcB23B884EB039303BBBb61", "0xb0D926c1BC3d78064F3e1075D5bD9A24F35Ae6C5", "0x0aA7A4482780F67c6B2862Bd68CD67A83faCe355", "0x0Aaf561eFF5BD9c8F911616933F84166A17cfE0C", "0x97ffBa9B031DE619E9852399e00788404D4817F0", "0x8d80de8A78198396329dfA769aD54d24bF90E7aa", "0x4a0134D74E9e300a49b1df1e8Df7Caca1c20fc4f", "0x779B7b713C86e3E6774f5040D9cCC2D43ad375F8", "0xAc709FcB44a43c35F0DA4e3163b117A17F3770f5", "0xD0800859D6f4bc0210B7807E770bc44A9eCE7372", "0x5748A3f36329CA187A2C6aBA0E06379cB530A7CF", "0x574B36BceD443338875d171CC377E691f7d4F887", "0xf9C9DA0C81fffd491458881410903561d1e40fD0", "0x9148AB505Fd9eaB5141b2b36Ce815E2786b7f7cd", "0x2bDC0D42996017fCe214b21607a515DA41A9E0C5", "0x0AfFa06e7Fbe5bC9a764C979aA66E8256A631f02", "0x0AF44e2784637218dD1D32A322D44e603A8f0c6A", "0x97AEB5066E1A590e868b511457BEb6FE99d329F5", "0xf028ADEe51533b1B47BEaa890fEb54a457f51E89", "0x8Ae4BF2C33a8e667de34B54938B0ccD03Eb8CC06", "0x1175a66a5c3343Bbf06AA818BB482DdEc30858E0", "0x72D32ac1c5E66BfC5b08806271f8eEF915545164", "0x72dD4b6bd852A3AA172Be4d6C5a6dbEc588cf131", "0x5732046A883704404F284Ce41FfADd5b007FD668", "0xac0ef38712138479Ed76E0184937B753239dab03", "0xAEEE1670c25955748a11d41f1fc9397B29476582", "0x12FEF5e57bF45873Cd9B62E9DBd7BFb99e32D73e", "0x12fCd6463E66974cF7bBC24FFC4d40d6bE458283", "0x54b293226000ccBFC04DF902eEC567CB4C35a903", "0xce61f5e6D1fE5a86E246F68AFF956f7757282eF0", "0xaF4DcE16Da2877f8c9e00544c93B62Ac40631F16", "0x0DB8D8b76BC361bAcbB72E2C491E06085A97Ab31", "0x92e52a1A235d9A103D970901066CE910AAceFD37", "0x0b76544F6C413a555F309Bf76260d1E02377c02A", "0xD0a4b8946Cb52f0661273bfbC6fD0E0C75Fc6433", "0xF3CeDe966FdA2198843DcdFc883C2c6ea9d00d49", "0xAf30D2a7E90d7DC361c8C4585e9BB7D2F6f15bc7", "0xefB74671eEc05de9798D63a82b8a670DDa165751", "0x8b353021189375591723E7384262F45709A3C3dC", "0xf3Db5Fa2C66B7aF3Eb0C0b782510816cbe4813b8", "0xf3dC9E88727B536A293249CD4C80bE515654EF28", "0xf3db7560E820834658B590C96234c333Cd3D5E5e", "0x92Be1007A5314422f6A96F892459134F06082961", "0x2A22e5cCA00a3D63308fa39f29202eB1b39eEf52", "0xeC18f898B4076A3E18f1089D33376CC380BDe61D", "0xEc1ABA74855def842861AcBaF7Ff24E9bA197491", "0x340D2bdE5Eb28c1eed91B2f790723E3B160613B7", "0x516E5436bAfdc11083654DE7Bb9b95382d08d5DE", "0x57C75ECCc8557136D32619a191fBCDc88560d711", "0xb0324681F0B4b28127b1A184CADa5A589bd43334", "0xce853db3359326dB6D03981C9fb42983BbCdd007", "0xF67451Dc8421F0e0afEB52faa8101034ed081Ed9", "0x6be47F64527e9665767407a13EF421e56262A0B8", "0x6BEB418Fc6E1958204aC8baddCf109B8E9694966", "0x52903256dd18D85c2Dc4a6C999907c9793eA61E3", "0x2dAEE1AA61D60A252DC80564499A69802853583A", "0x949bEd886c739f1A3273629b3320db0C5024c719", "0x181a63746d3Adcf356CBc73aCE22832FFBB1EE5A", "0xAe258D5322b59d64DF9Eb483E3b1733332C3B66c", "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d", "0xB9e7F8568e08d5659f5D29C4997173d84CdF2607", "0xf94e44D8EA46CCd8451D7E15264C6C4A78d3E10f", "0xf0Ee6b27b759C9893Ce4f094b49ad28fd15A23e4", "0xd5252FFC45200f14DddC3d1923eC7d619b468333", "0xb67734521eAbBE9C773729dB73E16CC2dfb20A58", "0xb67b88a25708a35AE7c2d736D398D268CE4f7F83", "0xF03f8D65BaFA598611C3495124093c56e8F638f0", "0xF037B7a5fAA30F6d650C1dc3Da29fAB17160FCE8", "0xD317fF47DC7e1423e5e050870A66332833E5fD88", "0x4a6058666cf1057eaC3CD3A5a614620547559fc9", "0x2d0E95bd4795D7aCe0da3C0Ff7b706a5970eb9D3", "0x116Db30642812E6ee1b9F9c6D0243676C8a67E73", "0x324A48eBCbB46e61993931eF9D35F6697CD2901b", "0x5721d31aa2DEe654cf0470E70D3505F3AA4F79a5", "0x572E6f318056ba0C5d47A422653113843D250691", "0x91205AB82497A728574edB3091F4859531C165df", "0xf04a8ac553FceDB5BA99A64799155826C136b0Be", "0x111111f7e9B1Fe072ade438F77E1Ce861C7eE4E3", "0xaeC2E87E0A235266D9C5ADc9DEb4b2E29b54D009", "0xaEc98A708810414878c3BCDF46Aad31dEd4a4557", "0x5818ef355CA154b41de857ce6a60c70310441b58", "0xd0929d411954c47438dc1d871dd6081F5C5e149c", "0xeE24f2C601A9F8a406FdB8169BA4580B3c411d74", "0xD9A12Cde03a86E800496469858De8581D3A5353d", "0xd9aBcEf171a959992738B32753cEbC8b64132555", "0x4b71AD9C1A84b9B643aa54FdD66E2deC96E8b152", "0x0d3c62d25a268B262f062A6f2c04265e711F8890", "0x1183F92A5624D68e85FFB9170F16BF0443B4c242", "0xB563300A3BAc79FC09B93b6F84CE0d4465A2AC27", "0xB561fEF0d624C0826ff869946f6076B7c4f2ba42", "0xd0059e9D822C471F394Ae5974d783e86b2Aa0853", "0xd6e354F07319e2474491D8c7c712137bEe6862a2", "0xd6e49800dECb64C0e195F791348C1e87a5864FD7", "0x5882D49d3511E09096CBbaB7E19fBFb82f65f28D", "0x588047365dF5BA589F923604AAC23d673555c623", "0x5884969Ec0480556E11d119980136a4C17eDDEd1", "0x315cE59FAFd3A8d562b7Ec1C8542382d2710b06c", "0x12759512D326303B45f1ceC8F7B6fd96F387778E", "0x4A89cD486fA996ad50c0a63C35c78702f5422a50", "0x543Ff227F64Aa17eA132Bf9886cAb5DB55DCAddf", "0x71d271f8B14adEf568F8f28f1587ce7271AC4Ca5", "0x71D01dB8d6a2fBEa7f8d434599C237980C234e4C", "0x52f7018BC6bA4D24abfBaeFCcaE4617bFB0a0b52", "0x38d1c39c3E85dbF0Fc2f2D637A4872530ad07A5f", "0xAFe60511341a37488de25Bef351952562E31fCc1", "0xb518d165398D9057eA8B73096eDda5C7754BCd62", "0x0D4170a9c6412E013729C8F35Fee729977A77152", "0x9238bfB781A55eACC3Cf05F7DF94038c198CD9B9", "0x923108a439C4e8C2315c4f6521E5cE95B44e9B4c", "0x2accaB9cb7a48c3E82286F0b2f8798D201F4eC3f", "0x4de2573e27E648607B50e1Cfff921A33E4A34405", "0x74951B677de32D596EE851A233336926e6A2cd09", "0x38c87AA89B2B8cD9B95b736e1Fa7b612EA972169", "0x38c6A68304cdEfb9BEc48BbFaABA5C5B47818bb2", "0xAFB559485401F81FCAb7Af7F5b5f6DD3B962C63f", "0x782e46eF36d10C96b29Cc86a1e514043E41e98E0", "0x52A7cB918c11A16958bE40CBA7E31e32a499a465", "0xAcfa209Fb73bF3Dd5bBfb1101B9Bc999C49062a5", "0x58a4884182d9E835597f405e5F258290E46ae7C2", "0x5102791cA02FC3595398400BFE0e33d7B6C82267", "0x3469815c608c853fBC88DCCC60844Deba571414a", "0x32c785E4E8477B277FEA2CA2301727084D79D933", "0x32Ce7Fd95F27528942d13b20C24965ACbfACF8AC", "0xF0da1186a4977226b9135d0613ee72e229EC3F4d", "0x983F6d60db79ea8cA4eB9968C6aFf8cfA04B3c63", "0x983877018633c0940B183Cd38d1b58bEE34F7301", "0xb056c38f6b7Dc4064367403E26424CD2c60655e1", "0x6D68593274bbCA4fea0ac29CE7C36Fc107E2f7e8", "0x0b8706D2cAE56d2789C551F0AB9A666BeBF48625", "0x11F8DD7699147566Cf193596083d45C8F592C4BA", "0x725B190Bc077FFde17Cf549AA8ba25e298550B18", "0xae4Bdd73d6EF497d17e4AF002457e97b23d37b8C", "0xaE4f56F072c34C0a65B3ae3E4DB797D831439D93", "0xae4191A7eB25713ac90483eA75828aE8038f94dc", "0x91e548Eda2571763dc2A8166ac1074f4236b179f", "0x0A76aad21948eA1ef447D26DEe91a54370E151e0", "0x9847345de8b614c956146bbea549336d9C8d26b6", "0xb0280743b44bF7db4B6bE482b2Ba7b75E5dA096C", "0xF660cA1e228e7BE1fA8B4f5583145E31147FB577", "0x77FAEd976e187f26b49E78bE8418Ab074A341F26", "0x31B5E97294e1afD6fff6ffe4cBa89A344555F753", "0xcCeD5B8288086BE8c38E23567e684C3740be4D48", "0x3839d8ba312751Aa0248fEd6a8bACB84308E20Ed", "0x3833ddA0AEB6947b98cE454d89366cBA8Cc55528", "0x78B7FADA55A64dD895D8c8c35779DD8b67fA8a05", "0x2a3Aa9ECA41E720Ed46B5A70D6C37EfA47f768Ac", "0xd96b9fd7586d9Ea24C950d24399be4fB65372FDD", "0xB5AE848EdB296C21259b7467331467d2647eEcDf", "0xb5A5F22694352C15B00323844aD545ABb2B11028", "0x78Eb8DC641077F049f910659b6d580E80dC4d237", "0xAf55F3B7DC65c8f9577cf00C8C5CA7b6E8Cc4433", "0x0a9A9ce600D08BF9b76F49FA4e7b38A67EBEB1E6", "0xD65960FAcb8E4a2dFcb2C2212cb2e44a02e2a57E", "0x519475b31653E46D20cD09F9FdcF3B12BDAcB4f5", "0x6aEB95F06CDA84cA345c2dE0F3B7f96923a44f4c", "0x6aEDbF8dFF31437220dF351950Ba2a3362168d1b", "0x1e49fF77c355A3e38D6651ce8404AF0E48c5395f", "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2", "0x1Ca43a170BaD619322e6f54d46b57e504dB663aA", "0x659e7FEdA7815EF7AdCBDd9E0187a659063d7cac", "0xe8Ff5C9c75dEb346acAc493C463C8950Be03Dfba", "0x5C0915cc83755FdFbd07a73e0d92476080468212", "0x60C24407d01782C2175D32fe7C8921ed732371D1", "0x4545750F39aF6Be4F237B6869D4EccA928Fd5A85", "0x9Cb9eb4BB7800BDbB017be2A4fFBECCb67454eA9", "0x2023DCf7c438c8C8C0B0F28dBaE15520B4f3Ee20", "0x202e295dF742BefA5E94e9123149360dB9d9F2DC", "0x9E77D5a1251b6F7D456722A6eaC6D2d5980bd891", "0x9E7D29bd499B6c7da2a5B2EaFCF4A39d3BD845D1", "0xfD107B473AB90e8Fbd89872144a3DC92C40Fa8C9", "0xfbd0d1c77B501796A35D86cF91d65D9778EeE695", "0x6927C69fb4daf2043fbB1Cb7b86c5661416bea29", "0xC2C63F23ec5E97efbD7565dF9Ec764FDc7d4e91d", "0xa74476443119A942dE498590Fe1f2454d7D4aC0d", "0xe8A1Df958bE379045E2B46a31A98B93A2eCDfDeD", "0x5c743a35E903F6c584514ec617ACEe0611Cf44f3", "0xa8006C4ca56F24d6836727D106349320dB7fEF82", "0x6956983F8B3Ce173B4AB84361AA0ad52f38D936f", "0x29D75277aC7F0335b2165D0895E8725cbF658d73", "0xC79d440551A03f84f863b1f259F135794C8A7190", "0xc798cd1c49db0E297312E4c682752668CE1dB2AD", "0x0371A82e4A9d0A4312f3ee2Ac9c6958512891372", "0x43F25A44511f3e0B8AabB6022c96C4094E0AAf68", "0x43F6a1BE992deE408721748490772B15143CE0a7", "0x66186008C1050627F979d464eABb258860563dbE", "0x45321004790A4dAe7bA19217A10574d55739EFc7", "0xE4c94d45f7Aef7018a5D66f44aF780ec6023378e", "0xe26517A9967299453d3F1B48Aa005E6127e67210", "0xC1E2097d788d33701BA3Cc2773BF67155ec93FC4", "0x89cbeAC5E8A13F0Ebb4C74fAdFC69bE81A501106", "0x1F54638b7737193FFd86c19Ec51907A7c41755D8", "0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C", "0x1f5060d82f872bE9bC3C56d8CB52194aeE85ae7f", "0x9e96604445Ec19fFed9a5e8dd7B50a29C899A10C", "0xfAE4Ee59CDd86e3Be9e8b90b53AA866327D7c090", "0x4650f615ba63f7204b889f46707620286Db348a1", "0x63b992e6246d88f07fc35A056d2C365E6D441A3D", "0x001F0aA5dA15585e5b2305DbaB2bac425ea71007", "0x06dBC63cc41cf45CA60cc64E87eDb2EEe8806182", "0xA89b5934863447f6E4Fc53B315a93e873bdA69a3", "0x039B5649A59967e3e936D7471f9c3700100Ee1ab", "0x039F5050dE4908f9b5ddF40A4F3Aa3f329086387", "0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359", "0xfd8971d5E8E1740cE2d0A84095fCA4De729d0c16", "0x006BeA43Baa3f7A6f765F14f10A1a1b08334EF45", "0x63Ec4c8120Cdd90153185c361c0CDdB39ac5B590", "0x63e634330A20150DbB61B15648bC73855d6CCF07", "0x20E94867794dBA030Ee287F1406E100d03C84Cd3", "0x2604FA406Be957E542BEb89E6754fCdE6815e83f", "0x66a127049A928b308db5A8d41f5Dee3bE6cFD6b3", "0xfdFE8b7aB6CF1bD1E3d14538Ef40686296C42052", "0xBD725c90B947222Af7CDB31Fe7Bc74Db39A7352c", "0x4092678e4E78230F46A1534C0fbc8fA39780892B", "0xE814aeE960a85208C3dB542C53E7D4a6C8D5f60F", "0xe81D72D14B1516e68ac3190a46C93302Cc8eD60f", "0x1EC8fE51a9B6A3a6C427D17d9ECC3060fbc4a45c", "0x0996bFb5D057faa237640E2506BE7B4f9C46de0B", "0xe755f2Fa95e47C5588C3037dD38E1268fa5FCecD", "0x69BEaB403438253f13b6e92Db91F7FB849258263", "0x69b148395Ce0015C13e36BFfBAd63f49EF874E03", "0xfB41f7b63c8e84f4BA1eCD4D393fd9daa5d14D61", "0xdA6cb58A0D0C01610a29c5A65c303e13e885887C", "0x9c23D67AEA7B95D80942e3836BCDF7E708A747C2", "0x9C23a568A32e8434eC88bDF60891A1d95FFd36cC", "0x83cee9e086A77e492eE0bB93C2B0437aD6fdECCc", "0x1ed2B1eaEd8e968bc36EB90a914660A71827A5E9", "0xe8621Cf5E9AB5da8fBa5304F7C9dCF4D477191Fc", "0xDDe12a12A6f67156e0DA672be05c374e1B0a3e57", "0xDdEd69d8e28d38d640f6244ab5294f309fd40cE1", "0xDdE2D979e8d39BB8416eAfcFC1758f3CaB2C9C72", "0xfA2632a88bd0C11535A38F98a98dB8251CCbAA9e", "0xdb0F69306FF8F949f258E83f6b87ee5D052d0b23", "0x1C83501478f1320977047008496DACBD60Bb15ef", "0x46b9Ad944d1059450Da1163511069C718F699D31", "0xA4e8C3Ec456107eA67d3075bF9e3DF3A75823DB0", "0xa206ea08E73d779cD7c429581Af81C93287cc656", "0x7cD46cf85e6137719808e931C394F46Ebc1Ae67D", "0x7CDfA222f37f5C4CCe49b3bBFC415E8C911D1cD8", "0xc8C6A31A4A806d3710A7B38b7B296D2fABCCDBA8", "0x46eeC301D2D00087145d1588282c182bd1890E5C", "0x23CB17d7D079518dBfF4FeBb6efCc0dE58d8c984", "0x2567c677473d110D75a8360C35309e63B1d52429", "0x00A0cbe98E4D110b0Fa82646152D77Babf2951D0", "0x1FE70bE734e473e5721ea57C8B5B01e6Caa52686", "0x80BC5512561c7f85A3A9508c7df7901b370Fa1DF", "0x5f450b15B5b3FaF0D65FA99b0fb6286Edd04Df94", "0x7e667525521cF61352e2E01b50FaaaE7Df39749a", "0xE2FB6529EF566a080e6d23dE0bd351311087D567", "0xe2F45f1660DC99dAF3Bd06f637ab1e4DeBc15bDe", "0xe43ac1714F7394173b15E7CfF31A63d523Ce4fB9", "0xE43E2041dc3786e166961eD9484a5539033d10fB", "0x89303500a7Abfb178B274FD89F2469C264951e1f", "0xa44E5137293E855B1b7bC7E2C6f8cD796fFCB037", "0xe4EAbdCa81E31D9AcbC4Af76B30f532b6ED7F3BF", "0xe200641890772FCe8eE6EDc5354cCEa30ac92F49", "0x7c53F13699e1F6ef5c699e893A20948BdD2E4de9", "0x7C5A0CE9267ED19B22F8cae653F198e3E8daf098", "0x03806Ce5ef69Bd9780EDFb04c29da1F23Db96294", "0xdD41fBd1Ae95C5D9B198174A28e04Be6b3d1aa27", "0xFACCD5Fc83c3E4C3c1AC1EF35D15adf06bCF209C", "0xba2184520A1cC49a6159c57e61E1844E085615B6", "0xc499eA948a1aD5D8Eaf12abd2F67975c4Dbe21aa", "0x4632091b0DD0E0902d1fe0534e16eb7b20328D70", "0x9e88613418cF03dCa54D6a2cf6Ad934A78C7A17A", "0x86Fa049857E0209aa7D9e616F7eb3b3B78ECfdb0", "0xC711348Eb06F6918F8eAE66cE3fcF4747345D78E", "0xc719d010B63E5bbF2C0551872CD5316ED26AcD83", "0xA2f4FCb0FDe2dD59f7a1873e121bc5623e3164Eb", "0x464eBE77c293E473B48cFe96dDCf88fcF7bFDAC0", "0x46492473755e8dF960F8034877F61732D718CE96", "0xBA5F11b16B155792Cf3B2E6880E8706859A8AEB6", "0xdbFb423E9bBF16294388e07696A5120E4CeBA0C5", "0xFAd572db566E5234AC9Fc3d570c4EdC0050eAA92", "0x5ecaB114315a6DD00588f4Cd23339b8bEDf0c989", "0x5c6183d10A00CD747a6Dbb5F658aD514383e9419", "0x5c6713Cf716d1D89E5aC155f501314326355da70", "0x009e864923b49263c7F10D19B7f8Ab7a9A5AAd33", "0x3f06B5D78406cD97bdf10f5C420B241D32759c80", "0x4994e81897a920c0FEA235eb8CEdEEd3c6fFF697", "0x4993CB95c7443bdC06155c5f5688Be9D8f6999a5", "0x9CDa8A60dd5AfA156c95Bd974428d91a0812e054", "0x05C3617cBf1304b9260AA61ec960F115D67beCEA", "0x45245bc59219eeaAF6cD3f382e078A461FF9De7B", "0x660B612ec57754d949AC1A09D0c2937A010dEe05", "0x660e71483785f66133548B10f6926dC332b06e61", "0xfd784DA5c740c617AAFB80399fa81B86e1Da99a5", "0x694404595e3075A942397F466AAcD462FF1a7BD0", "0xC78593C17482EA5de44Fdd84896fFd903972878E", "0x832F652761381d85D3a5203e4c715bEc21b016Fe", "0xFBE0e9846Bd736B84A0A973322AD2a1fC8d7E5CA", "0x667088b212ce3d06a1b553a7221E1fD19000d9aF", "0x6678E467FA5cCfBDC264d12f4B8b28fE4661606B", "0x0312982BE24b63344558d3B3D8c58119A22B1E63", "0x9e6B2B11542f2BC52f3029077acE37E8fD838D7F", "0x203DaD4C2Af33C0Ff1b60b4579Cf956a60A6cB23", "0x05D412CE18F24040bB3Fa45CF2C69e506586D8e8", "0xfa44e1FD08148a25D67bB01554F8d76F71AA7Cfa", "0xFA456Cf55250A839088b27EE32A424d7DAcB54Ff", "0x6339784d9478dA43106A429196772A029C2f177d", "0x859a9C0b44cb7066D956a958B0b82e54C9e44b4B", "0x23b75Bc7AaF28e2d6628C3f424B3882F8f072a3c", "0xE42Ba5558b00d2E6109CC60412d5D4c9473FE998", "0xc14830E53aA344E8c14603A91229A0b925b0B262", "0x268b7976e94e84a48bf8B2B57Ba34b59eD836A74", "0x5F54C1512d036a0dD92744EE0A55Ed183dde0484", "0x5F53f7A8075614b699Baad0bC2c899f4bAd8FBBF", "0x7CBC8Ee27fFdBA230Dd316160ea01D565F17aacb", "0x1c98eEa5FE5E15d77FEeabc0dfcFaD32314fd481", "0x65A15014964F2102Ff58647e16a16a6B9E14bCF6", "0x65a1b109d96757661Dd0734Ab93019b53b333fED", "0xBAb165dF9455AA0F2AeD1f2565520B91DDadB4c8", "0x7ce07775AE5fB2cb3a249DBFC9622628aA780F54", "0x4395796c4e12fE32129A11B58410752dab56d18b", "0xA4d17AB1eE0efDD23edc2869E7BA96B89eEcf9AB", "0x7e9d62E1FF4e34096F91Ee0153222Ab81F7184F0", "0x7e9e431a0B8c4D532C745B1043c7FA29a48D4fBa", "0x60200c0FefC1D0ade1E19A247b703cf3ccDC915A", "0x6025F65f6b2f93d8eD1efeDc752acfd4bdbCec3E", "0x45e42D659D9f9466cD5DF622506033145a9b89Bc", "0x45eDb535942a8C84D9f4b5D37e1b25F91Ea4804c", "0x430241368c1D293fdA21DBa8Bb7aF32007c59109", "0x054C64741dBafDC19784505494029823D89c3b13", "0x26607f9bf9d62A37b0c78e1D3719FCD1fa32beF9", "0x69c4BB240cF05D51eeab6985Bab35527d04a8C64", "0xA8F93FAee440644F89059a2c88bdC9BF3Be5e2ea", "0x5e4ABE6419650CA839Ce5BB7Db422b881a6064bB", "0x5CA9a71B1d01849C0a95490Cc00559717fCF0D1d", "0x5CA71Ea65ACB6293e71E62c41B720698b0Aa611C", "0x7f6715c3FC4740A02F70De85B9FD50ac6001fEd9", "0xe8780B48bdb05F928697A5e8155f672ED91462F7", "0xE73cF3F446F126228b2Db7C04a46d285ea18ab56", "0xdb8646F5b487B5Dd979FAC618350e85018F557d4", "0xc258A94789CD6F50bdc76Ce51De9E7b3c4fFB125", "0xFb2f26F266Fb2805a387230f2aa0a331b4d96Fba", "0x20DcDBb00F4F7ed518B94c55ed08694c077D191E", "0x437CF0Bf53634E3DFa5e3eAFf3104004D50FB532", "0xe1A178B681BD05964d3e3Ed33AE731577d9d96dD", "0x5cF4e9dFD975C52AA523fB5945A12235624923DC", "0x7F1E2C7d6A69bf34824D72C53B4550E895C0D8C2", "0xE8031836B241501407f3EB03070eb9329FEbd9F2", "0x5e3346444010135322268a4630d2ED5F8D09446c", "0x408e41876cCCDC0F92210600ef50372656052a38", "0x85089389C14Bd9c77FC2b8F0c3d1dC3363Bf06Ef", "0x83eEA00D838f92dEC4D1475697B9f4D3537b56E3"] + +addr_list = [ + "0xfe5F141Bf94fE84bC28deD0AB966c16B17490657", + "0xfDD9bA2CcA2c4a3550c49cE6d78a5ED6B3e54b82", + "0xFcDd036DEa7704B8aEc38A0D7D1597DF4BFCc551", + "0xFcDB7f1b5aBc8353444F5e9c5aC37b56D8Ac717E", + "0xFcD2e6Efa296ba43FE2f3D541Cd89e61116366f6", + "0xFcD0d8E3ae34922A2921f7E7065172e5317f8ad8", + "0xfc30a1a7A650d10B20500BC10b06ff8F4B650AD2", + "0xFAFfea71A6da719D6CAfCF7F52eA04Eb643F6De2", + "0xFAF56e1ec52004a457d5a1a3D7dB39e119B982E0", + "0xFAcf20e8Da6B0351132Db789bB881b59F1956e5C", + "0xFAcB821E06098c79658BA3e2d320B412066293C9", + "0xFAc6860434f1F85CBb6430dA40Ef2D383302846b", + "0xFAc5bdbD0232a93851A893F794CB1DE202638ABa", + "0xFa222Ad5ebA3F95F2A2328e05260716565627B17", + "0xF9bA0955b0509AC6138908cCc50d5Bd296E48D7D", + "0xf97e0A5b616dfFC913e72455Fde9eA8bBe946a2B", + "0xf97187f566eC6374cB08470CCe593fF0Dd36d8A9", + "0xF970b8E36e23F7fC3FD752EeA86f8Be8D83375A6", + "0xf8e386EDa857484f5a12e4B5DAa9984E06E73705", + "0xF8DFaC6CAe56736FD2a05e45108490C6Cb40147D", + "0xf884e0096f826F82d9999dC11becb836DBB7c1F7", + "0xf85fEea2FdD81d51177F6b8F35F0e6734Ce45F5F", + "0xF835A0247b0063C04EF22006eBe57c5F11977Cc4", + "0xF7dF66B1D0203d362D7a3afBFd6728695Ae22619", + "0xf6Bfe607CfbCCD63309dB5C138532a0560ABd271", + "0xf64B584972FE6055a770477670208d737Fff282f", + "0xf5744d092eC231b22BcF8f30960073366A4Cfc81", + "0xf2eEfeE3C99CDB30c11D57b487a16690E813309B", + "0xf296ccDdACd447DE03E161FA99D7BCB5aB96242D", + "0xf20E484056d9cb18733eAF6F2865257e23F92af1", + "0xF19919F76C4bfD1d640E17fE2721114beaC1A3AB", + "0xF0cf70EC3679f1eb4d923a6A17227EE93481F850", + "0xF0c69e6c7081125bc97D62271ECBb85dbC5766e4", + "0xF0c466B709FE978A01a5dd1B7F6Cb192098Af06b", + "0xF0155486A14539F784739Be1C02E93F28eB8e960", + "0xEfC9204F3Ad5909bb90867f243febb9F44F5c798", + "0xEfC0e2737ADa78709bEC8348E96eBbe08c8D7433", + "0xEfBE514dB92674946EF93cA47e00e3ADaFDf8a2c", + "0xEfBC2F37a5F306Aa53809A9624e074B6c4982eB6", + "0xEfB2A21fF6AB49CfAdc7C1531c3D320B9c141605", + "0xEfaBDDc3CA3cF59CBcb1ED1E9194659109Ff56bd", + "0xEfa51BC7AaFE33e6f0E4E44d19Eab7595F4Cca87", + "0xEFA334e443fD633E28fe686E0A6Fd62c1391765f", + "0xEFA0D894b44675E17D81e290Cb66366474bcaBc4", + "0xeF91853230f4f61F37924a47E1cED2735aCc7C33", + "0xeedEb8BfE51E3FC8162D6939402763a6D8FB70Bf", + "0xeedBa0E3b08464e69B1e0F6c165103185D544Fbc", + "0xeed20CAcE16b637665d49947dFC505A8949E1cFE", + "0xeed108b28A6CDf11d11c8C78489EBA9886392A68", + "0xeEb1f73D5566650680Ffb4c62f315de64d08315F", + "0xeEb1286410D0fCE0CCb772D6e7F7626D1eab91aa", + "0xeEb0e5A3262d3588FF89D7DedE6D704C46876350", + "0xEEa304894198110422b2667694Bb63B234f7113A", + "0xEE49f38DAb3388229D4D441e9611490Fc677166E", + "0xEdFfE8b19AF77a2677DbAc859e640fFc5bE8741F", + "0xEdFb53d83CA0579958BB66BE6E103c368e91Cfb8", + "0xede749ac3AF55575640aDd01E28760d88c9Cd6E4", + "0xeDdf2c1E97D14A1d7A807065D67C51D0434666BF", + "0xeDdD5F62d3FBe9795334833fDbc4b7D9C45D4972", + "0xeDd8cCe2Cc9fF553618eBC04bbFad01c763Bc2fF", + "0xeDd547461177ABbB9cC993e3A022252EDF143B9D", + "0xEdCD551Cfd686FcAf6b1D4C67FC7E8Fb13dfAF0E", + "0xeC46f8207D766012454c408De210BCBc2243E71c", + "0xEBBdf302c940c6bfd49C6b165f457fdb324649bc", + "0xeB9c0138d8ac10DD659640a4CC3D135C58B17B1B", + "0xeAffF1C115d5F8060531356a21C46668EfF6DA96", + "0xEaDD9CA9429af0d273395b92A0e46a36D2682F62", + "0xeaDd424357A40334269D42a2831E0F1408A9e131", + "0xeAAeD401Fcf75e4b70a7565f7964B5DDBf28b9B6", + "0xeAA86553C78695ac1645FF0e7FcEB024DC14e375", + "0xEA642206310400cDA4c1c5b8E7945314Aa96b8a7", + "0xEa11755Ae41D889CeEc39A63E6FF75a02Bc1C00d", + "0xEA097A2b1dB00627B2Fa17460Ad260c016016977", + "0xE9058c861348262D03688Db269E6Fa7a387B6682", + "0xe8Ff5C9c75dEb346acAc493C463C8950Be03Dfba", + "0xe87Dd55dB2d37A26D03b5D54c4153D13F903009F", + "0xe86EEb688844aEcf0621568000A64aaA18506Adc", + "0xE78099D5764FAdD10d7be04BB92B162913122D89", + "0xE7232a9Fd8bF427Aa41918BC008D32290e22990e", + "0xE701CD3329057AeA9D54300DdD05e41b8D74727A", + "0xE69a353b3152Dd7b706ff7dD40fe1d18b7802d31", + "0xe658E6Eb4B478da2Cf36d9e3712ba0c1b33786A1", + "0xE5EEE4f360Ca55d8E2a9C599a700E45c149aA833", + "0xE5BD8F606c39086f03387FB30e14522F1da1a1f0", + "0xe577f4F83b16CC2628f1b42f72aa07fAf88B79c0", + "0xE4D93FF62F73728fD5dA5eD08a4A0D8898E4C8bA", + "0xe464cDef2799a18251107F9D8e1C7057d3498D1a", + "0xe3831c5A982B279A198456D577cfb90424cb6340", + "0xe2F42B417337fd9fD22631cad54DB8178655Fcd1", + "0xe2F009606ba77D2e1Dd87C85185fD2998320D777", + "0xe2555854bfb5d974d0c88239EB238AcF255B5F17", + "0xE20AeDad150d74aFA1B8e5DBC7f04d05c3454300", + "0xe1Cd76CDA3D1d0b9b643d4f30B0640DAba9d0412", + "0xe1C52996164C23EDcE22E5c30a99fA25E93D39F9", + "0xe1A178B681BD05964d3e3Ed33AE731577d9d96dD", + "0xdfa6edAe2EC0cF1d4A60542422724A48195A5071", + "0xDEe667186e7b81Ecf7Efc8713382d8D99A8b92B4", + "0xDE11e2b7235FCb6E5039EAdAaACe5e4fF99c6B5B", + "0xDDcF741A75c9B8c730020B3Ab2D8C27A068970c4", + "0xDDcC9B53848e4A42a877FBdbf9ee7980F866AC8B", + "0xDDc4DD67BeF9D636eE9DC3a7e01030c60997F5F5", + "0xDdbdD1FC7b87FC74d6c30591DB0e703c4563f449", + "0xDdb7A3cEc33d8F641d5d4b903eBB79241f9149b0", + "0xDdb53b0eab309A096363E3b3ab128C723f8Bd3cF", + "0xDdb51C7fF5Be11789ffcB204027b1D1e1af4273D", + "0xDdb0D260170F11eee9415feCBdC9cfC5f9B483C3", + "0xddA26f777B6aBA5ED876cC5611638dddd6ad7da1", + "0xdD6Bf56CA2ada24c683FAC50E37783e55B57AF9F", + "0xDD16eC0F66E54d453e6756713E533355989040E4", + "0xdd007278B667F6bef52fD0a4c23604aA1f96039a", + "0xdcf421D093428b096cA501A7CD1A740855a7976f", + "0xdb8646F5b487B5Dd979FAC618350e85018F557d4", + "0xDaea62011ee438b87333C3D2c46e5e0a6616CD45", + "0xD82167D5dE0B987065504d6a5567D88F3C23AA93", + "0xD7AA94f17d60bE06414973a45FfA77efd6443f0F", + "0xd73A66B8FB26Be8B0AcD7c52Bd325054Ac7d468b", + "0xD6C8206F0ED9f041fEb5a3F8F98D88eBf10232E8", + "0xd5D10172e8D8B84AC83031c16fE093cba4c84FC6", + "0xD556dfbe9e74c376A0A24fc7b238c783A91ef33B", + "0xd4c435F5B09F855C3317c8524Cb1F586E42795fa", + "0xd4307BFA82073E4812202c2978129c10358Fe3eC", + "0xd4236f64B19d58e9d07eED439fF8E79D1dB86137", + "0xd37527F43674839fC73E55FE225B9cee115386b4", + "0xD31617A428EA97dDC4738159a0DC55E8D3AC6a75", + "0xD1CEeeef70c61da45800bd81BE3352160ad72F2a", + "0xD1CEeeeee83F8bCF3BEDad437202b6154E9F5405", + "0xD1CEeee3ecFff60d9532C37c9d24f68cA0E96453", + "0xD09c9be6C2D1878edCCcefEc2f92197a39A1c47F", + "0xd0929d411954c47438dc1d871dd6081F5C5e149c", + "0xd0059e9D822C471F394Ae5974d783e86b2Aa0853", + "0xCEFD1c46a13E56D3065B64b89e6bd837cfdED1ab", + "0xCE3767CBdd2c273734dde20eEe950C28C0DfB122", + "0xCc34366E3842cA1BD36c1f324d15257960fCC801", + "0xCAFfcd3D866a3ec8593b2f966d5B9b8614cfa18E", + "0xCAFCFf76D4aa853012e75AcFEF101CcD4918E616", + "0xCad5b0bc745cdA0822A2A87AfE1d3fAe5Bd5de72", + "0xC99Ddc30BB0cf76B07d90DcB6B267B8352697bEf", + "0xC8B55C7ad00fb9b933B0a016c6CEbcEea0293bb9", + "0xC8469575469E972347DC084385f10C4b7AA4a581", + "0xc79C71F6B35E94A605005AC738e2A807e74E60e6", + "0xC74e31b4174D27814cF0d3eE9f095B87Ae54C5aE", + "0xc735000293cb6790D8b9CC978F4dCce7018d8f7F", + "0xC66eA802717bFb9833400264Dd12c2bCeAa34a6d", + "0xC5d105E63711398aF9bbff092d4B6769C82F793D", + "0xC59aDE7F6CE5C061635D6dB788a9477a3a3bc59f", + "0xC429838F8bbd4bBe2B1EB8cbD8B696cBe92514E0", + "0xc42209aCcC14029c1012fB5680D95fBd6036E2a0", + "0xc3b5baB86D69c13023Ecc3E88C6Ad3a21205add1", + "0xc3b2AE46792547A96B9F84405E36D0E07EdcD05c", + "0xc3Ad35D351B33783f27777E2Ee1a4b6f96e4EE34", + "0xc25eAb579ccA2208FCbb2d536338a17ffaC33aE1", + "0xc2557a084aEE847B72e1D41942c323eB2D086dE2", + "0xc1EAb49CF9D2e23e43bCF23B36B2BE14fc2f8838", + "0xC1E7c74A13608Bcbb13479b59B81991CFeD96632", + "0xC19abA5148A8E8E2b813D40bE1276312FeDdB813", + "0xc168C2c103649eF6c11c60Adbf40030Cb7F28CF3", + "0xc167c7d778F1Ce2789d66A0B39080a68F990F97E", + "0xc16771e70c5a1F8a5854881DF61bE730cCa634e1", + "0xc166DdD84b021562023A6b944dec9dB1A9eb22a1", + "0xc14830E53aA344E8c14603A91229A0b925b0B262", + "0xc1128D09F0F7C0f01F5417eaF2661d8d4D80b92A", + "0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95", + "0xc081B1e603498D122309f799C327d64D7Ef2AcdD", + "0xBfFcF5A889a4cdB7E7042846Ea25B1d0BE441051", + "0xBfFcF5A889a4cdB7E7042846Ea25B1d0BE441050", + "0xBfF9713DC2c50D7d726B8C8ac1c5756D694f3E58", + "0xbfCEDe91f5157A3c42A75de340C84cA08b25C56E", + "0xBfC8C3C7E001BFEF88feb4DBAc5AB4C5eaed5Ecc", + "0xbfC5b78123fD307eC422a8434351fd5A9B7263f9", + "0xBfC526D0197Abc3be3BC719367Ec2333BC235d22", + "0xBFAcE6777AbC923c2B59c4aEB5E678cE4d2E75fe", + "0xBF87789027add06DB7A63806df3d7a75A39b539f", + "0xBf649A0723f3C58D16d8423660156C04EA70f0B3", + "0xbf2179859fc6D5BEE9Bf9158632Dc51678a4100e", + "0xbf0F568a0d8833b84a7d872d40716C7218Ddfb2c", + "0xbEFae3a5155220CA3ce2d90bf65752F5A12De506", + "0xbeBAfb11eE66fe55124a2fB762EA5970b245C5E1", + "0xBe46324018124B2d604c2f3eaE91D3De9b388b09", + "0xbcaEf9DE16aF10B92016e3c8aC5D1250c952F275", + "0xbca82049aD3a4b94dF7A78EdDe2c8C3E2633AE6e", + "0xbca236a86E7B62145Fd6EABADD737Bd6aFD05719", + "0xBBEc84399456C894240f6Da529839F6A40AE6645", + "0xBBE2e4F101d5A09DDabCFa11e459C208C1602E5f", + "0xBbddc8699ADdC61565660b1A498E1cF34285C7C8", + "0xBbd286f3cDe967716E7b534dD063f332cFA17f2E", + "0xBbd126B121e90180cc51A7A8ACf276294175967B", + "0xbb2F3971A86541801d6187193Ed6642C5f9a3a74", + "0xBB1fA4FdEB3459733bF67EbC6f893003fA976a82", + "0xBAb165dF9455AA0F2AeD1f2565520B91DDadB4c8", + "0xbA883b8386423eBDD2E05ca33C60E50abaBbE92A", + "0xbA83abC56D187440B627dCba64ec2cF95834ae82", + "0xbA71B32e71a41339Aa4CEAa79528535AEFE488D8", + "0xbA5a17f8ad40dc2C955D95C0547F3e6318Bd72e7", + "0xba5373Bb3BB87699434588C8a2E320AF3b8994C2", + "0xba27E082f0fC028E92BF6C64Cc61726Cf3Cf5b5b", + "0xBA187B09fFA8DDdc80d2571eD3cbC4Be0Af69E0c", + "0xB98d4C97425d9908E66E53A6fDf673ACcA0BE986", + "0xB97048628DB6B661D4C2aA833e95Dbe1A905B280", + "0xb90E64082D00437e65A76d4c8187596BC213480a", + "0xB8c77482e45F1F44dE1745F52C74426C631bDD52", + "0xB8742486C723793Cf5162bb5D3425ED9cD73D049", + "0xb6d09515Fb95eB7A7F72b776e737de74Aa4273D7", + "0xB63B606Ac810a52cCa15e44bB630fd42D8d1d83d", + "0xB4EFd85c19999D84251304bDA99E90B92300Bd93", + "0xb4d0FDFC8497AEF97d3c2892AE682eE06064A2BC", + "0xb444208cB0516C150178fCf9a52604BC04A1aCEa", + "0xB3775fB83F7D12A36E0475aBdD1FCA35c091efBe", + "0xB3549808e79b0967107AfEE0c97a7e1EF8cdd6A8", + "0xb3549808E79B0967107afeE0c97a7e1dF8cdD7a8", + "0xb3549808e79B0967107AFeE0C97A7e1Df8cdd6a8", + "0xB3030869CB6F67502CE592bE2419Bb948448bf56", + "0xB23be73573bC7E03DB6e5dfc62405368716d28a8", + "0xB17DF9a3B09583a9bDCf757d6367171476D4D8a3", + "0xb1690C08E213a35Ed9bAb7B318DE14420FB57d8C", + "0xB110eC7B1dcb8FAB8dEDbf28f53Bc63eA5BEdd84", + "0xB02af07163869979F7e90C397955741EF06e63f9", + "0xaf5A2d3F42DF0A05b96C18839F14D5B5E7F2B0F1", + "0xaF0D720c6734F2a81C4512BA81E5182657eD3635", + "0xAEEE1670c25955748a11d41f1fc9397B29476582", + "0xaeeD3BF0A17dE124C10d1FCC92AC3bE7325C6E41", + "0xAeEC5C2568E2C99eA7b0C9045e5D941f73c0e89d", + "0xaee2ed167106bcAA5Aa951258357f6886627Ba98", + "0xAEA1C18A992984831002D0cf90E291FB52d72649", + "0xaE4f56F072c34C0a65B3ae3E4DB797D831439D93", + "0xaDea1FD1F0A7450809015C53C00976ACc30E8E4e", + "0xadc204dF4E3f3a1F002dD0Afe049138B65aFd98E", + "0xacdf5de63ebde553CDfb358e16af277356281D87", + "0xAc2F53Dd8cE926513520FdEA9c9BabC59eaF4451", + "0xAc246E6600b2a319985e0A30F56147C0ae39404e", + "0xabFB68090312C88Bd6930A5dAf5915789bB1752a", + "0xabFB1705483817bcFeeF5a62c1E5A0665c149cDE", + "0xabFB11De26e9D9a57d3B7620424992310cC8Ca3A", + "0xABA912cCE91EBec391522C6BeF4D4CB55Ce5b3a5", + "0xABA62aDF06fAA6855d86BD7Bd76125e4B3292B55", + "0xABA3b4e336A49c6DDC7b4a4570842c233fe89dA4", + "0xAB863204887371e836b0645Aaa08A846960639e1", + "0xab7c74abC0C4d48d1bdad5DCB26153FC8780f83E", + "0xAB130BC7ff83192656a4B3079741c296615899C0", + "0xaaB611aF7BB42b01766f7b7DbeB530B70c7Ecbac", + "0xa9d3Ae9B6D23eD57689527B272f0dF9b7dc821a2", + "0xA9877b1e05D035899131DBd1e403825166D09f92", + "0xa98025b84F3D781a3E0122bCAd726c2862546db0", + "0xa96dbFd3Ef810dd8D13c330a3881E7e9C2aEb6dd", + "0xa7f976C360ebBeD4465c2855684D1AAE5271eFa9", + "0xa7df081383F6aD465aD1375d36645135C355f025", + "0xA6Ca2D1cCBc75d71de1354D549644271Ef7122ED", + "0xa6A7fCe4AFFe059548Fc39eBbc74555952A6Fb0d", + "0xA5d1e58ECe1fC438d64E65769d2ab730143a4Caf", + "0xA5534BC627B54494E610DeBC5fAB53De99e97117", + "0xA52e014B3f5Cc48287c2D483A3E026C32cc76E6d", + "0xA51153D9cf9d3cF6D58697b68eCCC158D1e40388", + "0xa4a689C90D5042202570Cf85e730A5Cb9eaD77a2", + "0xa44F953Ad054531B24E4aAC61E12deD139b89DE7", + "0xA3D5c31c0b5BE106930329A96E261dB7b6f2AA3d", + "0xA25D01d15fC0e3cDeDE1BEbEE4124394aaE0DB33", + "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "0xA0aa85b54F8A7b09C845F13a09172B08925f3d54", + "0xa07fdE770a7bD58730E816fe332a7BAe15c56C91", + "0xa06C374b7C5B2C62E7Bd515aeE1D84b12B507feD", + "0xa019c785322B921a84D086502da0D0dbdb993fba", + "0x9F63a5d92162975A08555f54752503B96277526d", + "0x9f2CED44Da5B44BD55796bA3A89F73afe73aAD38", + "0x9e96604445Ec19fFed9a5e8dd7B50a29C899A10C", + "0x9CDa8A60dd5AfA156c95Bd974428d91a0812e054", + "0x9C666C69595c278063278a604FF12c70691AB234", + "0x9C1d13D5a8fd4a8ac89917d31D40Db454D1ee60b", + "0x998c0c4EEf2E7ed64849b2C2b0868EEB18E30a18", + "0x998b3B82bC9dBA173990Be7afb772788B5aCB8Bd", + "0x99266005bF159e231CDCa727E09E5149C5b24FA6", + "0x98F5e9b7F0e33956C0443E81bF7deB8B5b1ed545", + "0x988b3A538b618C7A603e1c11Ab82Cd16dbE28069", + "0x983877018633c0940B183Cd38d1b58bEE34F7301", + "0x96E7971A35589bA02839b878d80E7698450C79f5", + "0x950325fa13D1A43a1436420D5bdc22eD92Ac2Baa", + "0x9442dF330A6D301f194d9161d5C140A1c3f1B4D9", + "0x93E682107d1E9defB0b5ee701C71707a4B2E46Bc", + "0x93e24cE396A9E7d7dE4A5bC616cf5fCaB0476626", + "0x9389434852b94bbaD4c8AfEd5B7BDBc5Ff0c2275", + "0x932ca724C232773B8c32033d08387Bdaa2450376", + "0x92E56fd4d1468a9F4c882A5394270427135538e4", + "0x926e6E18c420861DbF32CcBEe0686577a26Dd00f", + "0x916deaB80DFbc7030277047cD18B233B3CE5b4Ab", + "0x915d177C4566376eF3f3bE5af047924921054456", + "0x90b1B771d0814D607Da104b988efA39288219D62", + "0x8f8E8b3C4De76A31971Fe6a87297D8f703bE8570", + "0x8eD210af6e642333CF7Bb69bcdaCFdE68D243132", + "0x8E4BfD29615C0E58DaCc6f6E469690f7A93c7d9c", + "0x8E2040aB7A6af6BBA67e6d9b280c6feA7F930C87", + "0x8d80de8A78198396329dfA769aD54d24bF90E7aa", + "0x8d4E110963259B554517123C8f06B5c6bb83092b", + "0x8d1932B5c57469eCB8a4E2f9135130506A8d8b74", + "0x8d12A197cB00D4747a1fe03395095ce2A5CC6819", + "0x8ce9411Df545d6b51A9bc52a89E0F6d1B54a06dd", + "0x8c8504FE6bc6A55FA0c49695F9f4395fB6094773", + "0x8C65e992297d5f092A756dEf24F4781a280198Ff", + "0x8C01aDa8e708993A891D57D1b3169479a20aCB3A", + "0x8bD25c23D1D01e3216f3410146153f14775dBB6F", + "0x8BC101ABF5BcF8b6209FaaAD4D761C1ED14999Be", + "0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359", + "0x89cbeAC5E8A13F0Ebb4C74fAdFC69bE81A501106", + "0x8942595A2dC5181Df0465AF0D7be08c8f23C93af", + "0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7", + "0x88d50B466BE55222019D71F9E8fAe17f5f45FCA1", + "0x88AE96845e157558ef59e9Ff90E766E22E480390", + "0x888666CA69E0f178DED6D75b5726Cee99A87D698", + "0x882FBBE226F293037Fa5c06459b1f4e871B70E94", + "0x87d9EF8951DE64b7246fdb7c7D5a52760677f361", + "0x87a9678952B5D7A7bd450E4695d691e574E26a89", + "0x872Fa1C66A7C773A10d78d4D53F79ddEBD27164A", + "0x86a43e62Fb5e36d4BC39854b6D7Ab031Fb2E1785", + "0x863DF6BFa4469f3ead0bE8f9F2AAE51c91A907b4", + "0x85C5c26DC2aF5546341Fc1988B9d178148b4838B", + "0x8562c38485B1E8cCd82E44F89823dA76C98eb0Ab", + "0x85089389C14Bd9c77FC2b8F0c3d1dC3363Bf06Ef", + "0x84C2c31C04339c9938Adfe3F8013315c8906f071", + "0x843FcaAeb0Cce5FFaf272F5F2ddFFf3603F9c2A0", + "0x83eEA00D838f92dEC4D1475697B9f4D3537b56E3", + "0x83cee9e086A77e492eE0bB93C2B0437aD6fdECCc", + "0x83984d6142934bb535793A82ADB0a46EF0F66B6d", + "0x83141fa0226cFA15fD6EdF85eA94CeC9F7029Ee9", + "0x820d99149C21Fcabf2349ce861A5b76AaC731837", + "0x80BC5512561c7f85A3A9508c7df7901b370Fa1DF", + "0x7fCf00d297212DC0E488162aAfE21727e4c6cdBf", + "0x7F6eAC2EA38284645795996b11F9Bf227cf9551d", + "0x7De5abA7DE728950c92C57d08e20D4077161F12F", + "0x7D5Edcd23dAa3fB94317D32aE253eE1Af08Ba14d", + "0x7d3B6B1360fa83482Ed0284f9b9aC4e48eDBCAa5", + "0x7cd03C9f1D2dc95358B1992e9afc857aeaab45D5", + "0x7C5A0CE9267ED19B22F8cae653F198e3E8daf098", + "0x7c53F13699e1F6ef5c699e893A20948BdD2E4de9", + "0x7b220AC85B7ae8Af1CECCC44e183A862dA2eD517", + "0x7A79ABD3905ef37b8D243c4C28ceE73a751EB076", + "0x7777777c9A7E4E08464331113A4f29b742453963", + "0x77761e63C05aeE6648FDaeaa9B94248351AF9bCd", + "0x7703C35CfFdC5CDa8D27aa3df2F9ba6964544b6e", + "0x7619Eed82c4Fa5e6d65Bf3d02490002a1c1234A7", + "0x75bA02c5bAF9cc3E9fE01C51Df3cB1437E8690D4", + "0x75Aa7B0d02532f3833b66c7f0Ad35376d373ddF8", + "0x7585F835ae2d522722d2684323a0ba83401f32f5", + "0x75228DcE4D82566d93068A8D5d49435216551599", + "0x749B119C36F3964Af6eCae766D3DF91778Ecc4C6", + "0x749012523b5c0F634537736202e233F12E9e66bC", + "0x7365877678C744B435eD03B1Cac12AB407CBa13a", + "0x72D32ac1c5E66BfC5b08806271f8eEF915545164", + "0x72aDadb447784dd7AB1F472467750fC485e4cb2d", + "0x7294aAA42bBE486d1A201df09f20572133d38D15", + "0x729330399a680C6F8E8EBB9ED5e89A9706892228", + "0x700638e0C20656e9bB73979f78BE2C5E4059d95A", + "0x6fB3e0A217407EFFf7Ca062D46c26E5d60a14d69", + "0x6F7A4bac3315B5082F793161a22e26666d22717f", + "0x6F6DEb5db0C4994A8283A01D6CFeEB27Fc3bBe9C", + "0x6F49c475A75756cC7De92F391B5c6499258cFc52", + "0x6f1A769952C60B2d03f46419Adeda91D87866dAb", + "0x6EbeAf8e8E946F0716E6533A6f2cefc83f60e8Ab", + "0x6Eb4e133eBFeba07529F586e9392810190c12fC0", + "0x6D68593274bbCA4fea0ac29CE7C36Fc107E2f7e8", + "0x6d540C9f4357FE128c6B3300a12A16B38a5bCB3b", + "0x6ceE948C9d593c58Cba5Dfa70482444899D1341c", + "0x6c9aC05C04A7A83f8afd4164f8E932DfFdF69Ffb", + "0x6C0341A1Ed402CF8A92aA26015178b55EF1bFEf2", + "0x6aEB95F06CDA84cA345c2dE0F3B7f96923a44f4c", + "0x6aa9b5dff6241Ced841a853E530Dc8dA2d0b08c9", + "0x6A62B2ef5A3E089aFF063DD1Ce8263F43f2ACD09", + "0x6956983F8B3Ce173B4AB84361AA0ad52f38D936f", + "0x68Cb858247ef5c4A0D0Cde9d6F68Dce93e49c02A", + "0x68Ac72877Ccbb9Af05FF99634AA7A3D537E2dbA7", + "0x6810e776880C02933D47DB1b9fc05908e5386b96", + "0x67fa2C06C9c6d4332f330E14a66bDF1873eF3d2b", + "0x66a127049A928b308db5A8d41f5Dee3bE6cFD6b3", + "0x667e3e933Ed540613538E5931B6897A07c118D01", + "0x667088b212ce3d06a1b553a7221E1fD19000d9aF", + "0x65A15014964F2102Ff58647e16a16a6B9E14bCF6", + "0x6423D3767Bf629A2A343c0A2785aBFbC2a2f6950", + "0x63e634330A20150DbB61B15648bC73855d6CCF07", + "0x63C2Fb137DE77a95dE10F4e94048f1B73CBccc72", + "0x63b992e6246d88f07fc35A056d2C365E6D441A3D", + "0x62D21C75F840108A127f274d1e20eB2969Cb491a", + "0x62a56a4A2Ef4D355D34D10fBF837e747504d38d4", + "0x61EDCDf5bb737ADffE5043706e7C5bb1f1a56eEA", + "0x61d63b3b2A1f4d5Be0EecA108589A82b4Df2bB8D", + "0x61c01767fB484241E95d6435f242F9cF1246E5F2", + "0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef", + "0x6025F65f6b2f93d8eD1efeDc752acfd4bdbCec3E", + "0x60200c0FefC1D0ade1E19A247b703cf3ccDC915A", + "0x5fe6279062D271d28fCA61Ebd60dC1813185c825", + "0x5FB2F392772a6Cb5ac423CeB587B62898C06c8Cf", + "0x5F8A57710b5b2244072dC2E17fe395698e9D5EFd", + "0x5F87E8D2d98092F9839f9b52CFdE66bb4f8491B3", + "0x5F86e275bb55BF294A10c7b701Ec5296A10e8236", + "0x5f1a08554F0Dc0cF79852c564a10981Ffbd7C8AF", + "0x5ecaB114315a6DD00588f4Cd23339b8bEDf0c989", + "0x5e4ABE6419650CA839Ce5BB7Db422b881a6064bB", + "0x5Dff89a2caa4D76bc286F74D67Bd718eb834da61", + "0x5cF4e9dFD975C52AA523fB5945A12235624923DC", + "0x5c872500c00565505F3624AB435c222E558E9ff8", + "0x5B9E8728E316bBEB692d22daaAB74F6cBF2C4691", + "0x5b891F57f14c48B77aD03cf487E19Ed0f2e95fBF", + "0x5A567e28dbFa2bBD3ef13C0a01be114745349657", + "0x5A1A29DBb6Ad6153DB764568C1289076bC876df6", + "0x5976F7dac1525eF3277836043bA474a35E6B4272", + "0x59416A25628A76b4730eC51486114c32E0B582A1", + "0x58b6A8A3302369DAEc383334672404Ee733aB239", + "0x5884969Ec0480556E11d119980136a4C17eDDEd1", + "0x55ec7d5dB07C4F8dd7c51cf83B3396d9FB9B2080", + "0x540449E4D172cd9491c76320440cD74933d5691a", + "0x5177AdaAaf708A1A3dFB0d0e37D5C06755bb1fC7", + "0x50B8136519D0DC344a65e7f8E69B53AF3f863B43", + "0x4F833a24e1f95D70F028921e27040Ca56E09AB0b", + "0x4efa8e025e017229FcA98C4786263a48828b2e0B", + "0x4eF94A2ACfF7011e995631c6865cc50a7d0C7f9F", + "0x4D55F76Ce2dBBAE7B48661bef9bD144Ce0C9091b", + "0x4CFEf2c21A8a2a9135C46a1A86B5Bb5510bF4565", + "0x4cF6fC32cfD7F0D119AfCf3Fc7C06dd34dA83342", + "0x4CEdA7906a5Ed2179785Cd3A40A69ee8bc99C466", + "0x4C74b34A2E44812bA1d1C83211aB3E4e6fA9eCf3", + "0x4C24Cd0bD3c9f7C29f2B57f3722D2482A185271e", + "0x4c1738756FaABC186968893743469cFD2A1658c3", + "0x4bF4F2ea258bf5cB69e9dC0DDb4A7a46A7C10c53", + "0x4B6784fd6a926bCE420C6Ed97cFB1aC176B07AEd", + "0x4B4e611823702285FD526D7A8A3B0Aa99aB2DBCD", + "0x4AAC9f7e5094E08A8f505c37e9763B98Bc5894cC", + "0x4a220E6096B25EADb88358cb44068A3248254675", + "0x49AAa160506F7e07E6C3F6cD6316b6866025cDcB", + "0x49a6E5b6c0801D9775938A0201F85448cca11fCb", + "0x496cA09f595440f736c45028cce3E727E7A5B748", + "0x47C754433bfF9333cc34Fda2ae78ad4971791B4A", + "0x46E8e8f6f3A3299495Bb5A3Ab82D0238229871Fb", + "0x4678f0a6958e4D2Bc4F1BAF7Bc52E8F3564f3fE4", + "0x44f165Cac9A1547492a54EE59fEa18B1DD2A0dE4", + "0x448a5065aeBB8E423F0896E6c5D525C040f59af3", + "0x4460a301f878E5d017A469672dE20FbA2814178c", + "0x445f51299Ef3307dBD75036dd896565F5B4BF7A5", + "0x43eE79e379e7b78D871100ed696e803E7893b644", + "0x4235C95f5a60D743866686174e4be33653ae9d45", + "0x4230e2c8c2f8cA7e96F76609071349430065D049", + "0x41f615E24fAbd2b097a320E9E6c1f448cb40521c", + "0x41dfc15CF7143B859a681dc50dCB3767f44B6E0b", + "0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b", + "0x418CCb0dd045AF4C5e37aEE7E1639901BE9b55C4", + "0x417f1adcCBf08f54aBf7726197AE72b9586Aeba5", + "0x417d2495De6B2de859B55327E738E91b5E7FF8DF", + "0x417615a257B126fc31B7107A2F438Be306079499", + "0x41754c96F3F11d3E8B3FDA398e0649B9c6423f4c", + "0x405e80cAEc13966bC28263430ff933AeF62b17de", + "0x405934eaF00B9c7D6Ba0935071DDea02AA7DFF98", + "0x4057482B118025CD7eB8aB88D8c5bed71664b2DF", + "0x40529aACE0Af5cBfAFF09e74a08Af868F0b42A27", + "0x40518043AC9cd8e63909F3761F67f5550d4BBff7", + "0x3FCCE645D31774f34bC0DF9E4257D3d680104937", + "0x3F8A6ae82e1726398BeAc161453f5a09742cB653", + "0x3f4B726668da46f5e0E75aA5D478ACEc9f38210F", + "0x3Eec96bC34BC384D503Ebe33d46E59f4dCE0fCc2", + "0x3Ee30744DA6Bb58522f4Cadc9666735c60cb8eAE", + "0x3dC9a42fa7Afe57BE03c58fD7F4411b1E466C508", + "0x3BeafD3974c7CC1607E0432058C0b0DeA1E0C7d6", + "0x3b6ED6511dDACBC38747dD3D2F7F0D112B706FEa", + "0x3A07c96FF828bA76f173A4542e786acb1D16492B", + "0x39Bb259F66E1C59d5ABEF88375979b4D20D98022", + "0x38c6A68304cdEfb9BEc48BbFaABA5C5B47818bb2", + "0x376c3E5547C68bC26240d8dcc6729fff665A4448", + "0x37427576324fE1f3625c9102674772d7CF71377d", + "0x36348D9c4c3447917FfCE1ba93ec1CBc90Fc2EBc", + "0x3506424F91fD33084466F402d5D97f05F8e3b4AF", + "0x342D4b16B3856cD468cf9d4d33379b8dbC289752", + "0x3401CAb9bEe49bCb76E13A8A09619e53D45C0AF0", + "0x336F646F87D9f6bC6Ed42Dd46E8b3fD9DbD15C22", + "0x329bCA83b582006ca1FE2c1CF8BBd94AD0e6033a", + "0x325a2e0F3CCA2ddbaeBB4DfC38Df8D19ca165b47", + "0x30E0130141B3f113480a5941ca180Ad8C5F98612", + "0x2f85E502a988AF76f7ee6D83b7db8d6c0A823bf9", + "0x2F1B8C9d0A21b747D8ca370f93cb09D3DaF222EF", + "0x2F073F40A3463AfD4bAa8dA90cf703A9Bfb7746a", + "0x2eb86e8fC520E0F6Bb5D9Af08F924fe70558Ab89", + "0x2e498661abCa9b659C5Fdf580c799dE802dB265a", + "0x2Dd784673c8a60571863407F94b5a873d3Aa11a1", + "0x2d75F40927cb3639613c2a509926E13E53348194", + "0x2D4F4A2984eC6Fd75FF3673EceaBA5b9f23Af09d", + "0x2d0E95bd4795D7aCe0da3C0Ff7b706a5970eb9D3", + "0x2Cae18DD1223Aea3bFDFDdFEE4cfBbCB4b80Cc22", + "0x298d7fD223Bb625488e5477c6C2a0f1dDc59223a", + "0x298B394C928314d665dDFBf2dC9cE4EFedE7bC2d", + "0x293c9278df470f61eb846FDe5A5D6d611639f588", + "0x2930F316f358e5fDDFb4f7fFBc90680fe9FaBd8A", + "0x2927A4003437f521417aB41297E8e4d332e038FE", + "0x28fAA7553049D62Fd0d8C831566a4899cb2eaC7A", + "0x28df30ad4551F38c0255Ed8dD7Dd3D25C41e2527", + "0x26E75307Fc0C021472fEb8F727839531F112f317", + "0x268B907AA7ddf7Aac64Eb8413eC51fa406f8F477", + "0x263c618480DBe35C300D8d5EcDA19bbB986AcaeD", + "0x25432dD810730331498C22FBf6b98432E7ef3E66", + "0x24e02022f828C717FEd343C776Eb91d91c34F396", + "0x24DCc881E7Dd730546834452F21872D5cb4b5293", + "0x2490a0ECd0C3d1060cfB49c44BA6A69a72FD68d9", + "0x2408c65B811a4B51E426795D4f855B7B596b04D2", + "0x2305a9F02264836325A780eBfCa2AD8F28c32945", + "0x2300CC81e0169EE99445Cac5010E8903fd2251a4", + "0x2240Dab907db71e64d3E0dbA4800c83B5C502d4E", + "0x21aE23B882A340A22282162086bC98D3E2B73018", + "0x20E94867794dBA030Ee287F1406E100d03C84Cd3", + "0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C", + "0x1f28211cd78363c7e0d44A90157017422599526C", + "0x1f0E3FEf5BABcCBfD8725b5753411BBFC0B6D3a4", + "0x1ED7e99525A651775C9F981F513178ce9C111162", + "0x1dc6D3206C2f24e05CA2E775377adB6E979E74b9", + "0x1DC5b8CcBe7A3669ED59dcd3B3d5AFA0FeD4eE57", + "0x1Dc33b5995E91c4560d3267c729B437f27ae0D47", + "0x1d963688FE2209A98dB35C67A041524822Cf04ff", + "0x1d4aa340B0B179c2d3cd846a3B32a6DFD8AA3dF5", + "0x1B9743f556D65e757c4c650B4555bAF354cB8bd3", + "0x1B5f21ee98eed48d292e8e2d3Ed82b40a9728A22", + "0x1b22C32cD936cB97C28C5690a0695a82Abf688e6", + "0x1a7a8BD9106F2B8D977E08582DC7d24c723ab0DB", + "0x1993f3f6639a8e987ab94b667d326Ea9a4a87AB9", + "0x19896232eCBd3d1eBCdE9F5E33593017Dd33B7d0", + "0x1958Ad969D55063DC76226050a95bbDda012226F", + "0x18e1B664C6a2E88b93C1b71F61Cbf76a726B7801", + "0x17E575b719B043d33D93FE9445b0F8df0598Ac67", + "0x1738B62E403090666687243e758b1C29eDfFc90e", + "0x168296bb09e24A88805CB9c33356536B980D3fC5", + "0x167c7C3d434315e4415EB802f0bEb9Ea44Cd1546", + "0x165cFb9cCf8b185E03205Ab4118eA6afBdbA9203", + "0x163733bcc28dbf26B41a8CfA83e369b5B3af741b", + "0x159DDF730d23D340BBB838e45b4e8BD322665396", + "0x15964e71aC7C2659a973f54a7162725998f1eE33", + "0x157052B14B7b7AB5BCD40C9d19FD785B2E2eFb5c", + "0x151202C9c18e495656f372281F493EB7698961D5", + "0x14FffB1e001615b7Fb7c7857BDf440a610022E5B", + "0x143FD2adb9398B33fFEF9108f1Df64aB90e0BEC9", + "0x143430b59f50250Be8aFD5fA26E886A7F5aD8eB8", + "0x1434247068e0aAF0C9654fC354DabF43923B1579", + "0x143393D555918869A6bB5518e54800E7EB5E4397", + "0x1337C8b69bcb49d677D758cF541116af1F2759Ca", + "0x131c9e90F9b7faD3270971a63CCF9C4A6A6E5686", + "0x13119E34E140097a507B07a5564bDe1bC375D9e6", + "0x12FEF5e57bF45873Cd9B62E9DBd7BFb99e32D73e", + "0x1175a66a5c3343Bbf06AA818BB482DdEc30858E0", + "0x1055bE4bf7338C7606d9EFDCf80593F180BA043e", + "0x0F0269F3Fe2252446fA83dc7D00ad20406BF4686", + "0x0EC40c433cA3606f4D72b2AB61d5b9530e1c8038", + "0x0D8775F648430679A709E98d2b0Cb6250d2887EF", + "0x0D8012B7E46F36f2e02af4B99e3cb0f56E694359", + "0x0D4170a9c6412E013729C8F35Fee729977A77152", + "0x0BAb6F620063cBB684c06877957f89f1Bb10616f", + "0x0b95993A39A363d99280Ac950f5E4536Ab5C5566", + "0x0b4Bf990Fa74Bf6363fA28b7c5f7f2C4E3e8b369", + "0x0B3Dd26B80fd679aF3fEC73b1e60eb716C311b72", + "0x0b22380B7c423470979AC3eD7d3c07696773dEa1", + "0x0aA7A4482780F67c6B2862Bd68CD67A83faCe355", + "0x0a9A9ce600D08BF9b76F49FA4e7b38A67EBEB1E6", + "0x08f5a9235B08173b7569F83645d2c7fB55e8cCD8", + "0x08711D3B02C8758F2FB3ab4e80228418a7F8e39c", + "0x07D9e49Ea402194bf48A8276dAfB16E4eD633317", + "0x07Aa23BFD3e19f3A0508cA8Dc5425857C6D31488", + "0x06A981Bd291C6BFaaB9954dDcEEb782dE805b4b3", + "0x06a6a7aF298129E3a2AB396c9C06F91D3C54aBA8", + "0x06012c8cf97BEaD5deAe237070F9587f8E7A266X", + "0x06012c8cf97BEaD5deAe237070F9587f8E7A266D", + "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d", + "0x06012c8cf97BEaD5deAe237070F9587f8E7A2669", + "0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27", + "0x056017c55aE7AE32d12AeF7C679dF83A85ca75Ff", + "0x04F2E7221fdb1B52A68169B25793E51478fF0329", + "0x04eE2C9976F2d0b8F1C18C05a66c58344c27b26A", + "0x04De23E912Cec433eABf3260ecC71cfD1f9d328f", + "0x03cb0021808442Ad5EFb61197966aef72a1deF96", + "0x0312982BE24b63344558d3B3D8c58119A22B1E63", + "0x027385E9365d66d5Fdb37Ab5F96700DC3db83160", + "0x01bbec6573ed7EcA0f307a10d2B4CEB669816B4a", + "0x01b3Ec4aAe1B8729529BEB4965F27d008788B0EB", + "0x01A28ADc0EdD796b570EC4dA734e1AA809f6f1Fc", + "0x016C93c3b62e533946f666896a161b2623BEFC1a", + "0x0166636D292069e8eF9CeAc0CeFFa003FA5f2adA", + "0x015A06a433353f8db634dF4eDdF0C109882A15AB", + "0x0157CD67f9EFBE2F7D0981a2dDccd8b6c07793DC", + "0x0155CE35fE73249fA5D6a29f3B4b7B98732eb2eD", + "0x014d8EB4c7eCD16a59074c198F44C24786e27F25", + "0x014B50466590340D41307Cc54DCee990c8D58aa8", + "0x0148368E9EfD8D6a5dD56134cD2b3F941e10D953", + "0x0131ABB99B083c9Fe5810b7DD884a7c9FcAEf7Bf", + "0x010589B7c33034b802F7dbA2C88cc9cec0f46673", + "0x00fDAE9174357424A78aFAAd98da36Fd66dD9E03", + "0x00c4B398500645eb5dA00a1a379a88B11683ba01", + "0x0094110C81183740C23D561818500cE0C8222d8B", + "0x006BeA43Baa3f7A6f765F14f10A1a1b08334EF45", + "0x003151fB0bc80FB2610A33fAce4De5A6C7D1bEE1", + "0x0013e723F574bAFD47CC1542532cdCD98C1C2989", + "0x000000B6E4FeC2aB4Fa3ec2af763248c17973612", + "0x000000009042b40070C8f83BFf1293005Dc25257", + "0x0000000000C90bc353314b6911180ED7E06019A9", + "0x0000000000b3F879cb30FE243b4Dfee438691c04", + "0x000000000045Ef846Ac1cB7fa62cA926D5701512", + "0x0000000000000000000000000000000000000005", +] \ No newline at end of file