The fact that you are not setting the data field implies that you are interested only in sending ether (rather than in executing a contract function).
If the destination address is of an externally-owned account, then you only need 21000.
If the destination address is of a smart-contract account, then you need slightly more than 21000.
However, even when ...
I made an NPM package to help with this: eth-revert-reason
It is hard to decode the revert reason in a generalized manner. Many different factors, such as web3.js vs. ethers, Geth vs. Parity, etc. will result in different results for all the answers posted here. Some issues are:
For a Kovan transaction, you need a custom provider that exposes Parity trace ...
You need to import @given from Brownie, not directly from hypothesis:
from brownie.test import given
This is required because Brownie handles isolation via a fixture, but function scoped fixtures only execute once per test (not once per run). From the hypothesis documentation:
... each fixture will run once for the whole function, not once per example.
There's some leads in the transaction receipt
When status is set to 0 it means the transaction has failed.
The base transaction cost is 21000 which is close to 24300 gas used, so the contract didn't avanced very much, for example it didn't write to storage yet.
You will have to check the conditions used by requires early in ...
Your code seems to be correct.
The Ethereum network is asynchronous. The new state may have not propagated fully through network in the point you are doing read immediately after read. Also there might be minor blockchain reorganisations making transactions hop between blocks.
However none of thus should be a concern for a local chain with a single miner, ...
You can call eth.getCode to see if there is code at the address. If there is no code, there is no contract. If there is code, there is a contract associated with the address.
Using ether.js, you can see how it works below. The first call was after the contract was deployed and the second call was after it was selfdestructed.