# Predictable NFT

## Idea

Go to the contract address and decompile the bytecode:

```python
# Palkeoramix decompiler. 

def storage:
  id is uint256 at storage 0
  tokens is mapping of uint256 at storage 1

def tokens(uint256 _param1): # not payable
  require calldata.size - 4 >=ΓÇ▓ 32
  return tokens[_param1]

def id(): # not payable
  return id

#
#  Regular functions
#

def _fallback() payable: # default function
  revert

def mint() payable: 
  if call.value != 10^18:
      revert with 0, 'show me the money'
  if id > id + 1:
      revert with 0, 17
  id++
  if sha3(id, caller, block.number) % 100 > 90:
      tokens[stor0] = 3
  else:
      if sha3(id, caller, block.number) % 100 <= 80:
          tokens[stor0] = 1
      else:
          tokens[stor0] = 2
  return id
```

Here `tokens[stor0]` is the rank of the NFT minted. `tokens[stor0] = 3` means "Superior" and that is what we want. Therefore the key to this chall is handle `sha3(id, caller, block.number) % 100 > 90`.

Recall that `block.number` can't be used for the source of randomness because this information will stay the same if we recompute it within the same tx. There are two subtle details:

1. `id++` happens before `sha3()`, be aware of that.
2. We can't see if `encode()` or `encodePacked()` is used by the original contract, so try both.

## PoC

{% embed url="<https://github.com/ret2basic/QuillCTF-PoC/blob/main/PredictableNFT/test/PredictableNFT.t.sol>" %}
PredictableNFT PoC
{% endembed %}
