A card payment can be taken back. That is not a bug in card networks, it is most of what you are paying them for — the chargeback is the reason people will hand a stranger their card number at all.
An on-chain transfer has no equivalent. You send, it arrives, and that is the end of the story. If the person on the other side never ships the thing, your recourse is to complain. Every escrow product built on top of this starts from the same place: both parties have to remember to use it, and the person most likely to insist on escrow is the person least likely to be offered it.
So we tried inverting it. What if escrow were not a contract you opt into, but the behaviour of the token itself?
Escrow as the default, not the exception
Indemnity (INDN) is an ERC-20 where transfer() does not deliver. Tokens leave the sender’s balance and sit in on-chain escrow for seven days. After the window, the receiver claims them. If the payer thinks something went wrong, they open a dispute inside the window and an arbitrator decides who keeps the balance.
The appeal is that nobody has to remember anything. There is no “use escrow for this one” decision to get wrong, no separate contract address to paste, and the protection does not depend on the more sophisticated party agreeing to be constrained. Pay someone, and the payment is reversible for a week whether either of you thought about it or not.
That is the good version of the idea. Here is what it costs.
It breaks every exchange, and the fix is worse than it looks
An AMM swap needs the tokens to arrive inside the same transaction. A pool that receives nothing because the tokens are sitting in a seven-day escrow does not trade — it reverts. Escrow-on-transfer does not inconvenience decentralised exchanges, it makes them non-functional.
The standard answer is an allowlist: named addresses whose transfers settle instantly, so pools and routers work normally while wallet-to-wallet payments still escrow. We implemented that, and it does work.
It also hands whoever controls the allowlist a switch over who can trade. A token whose entire pitch is “you do not have to trust the person you are paying” ends up requiring you to trust an owner key not to un-allowlist the pool you are standing in. The trust problem does not disappear. It moves up one layer and gets quieter, which is worse, because now it is easy to not mention.
We never solved that. It is the honest reason this design needs a governance structure a solo project does not have.
The part that actually killed it
Disputes were going to a Kleros jury. Not us deciding who was right — a third-party court, with the two relevant interfaces published as standards: ERC-792 for arbitrable contracts and ERC-1497 for evidence. We implemented both. The contract compiles, deploys, and correctly raises a dispute against the arbitrator address baked in at deploy.
No dispute can ever be heard.
To route a case to a Kleros court, the arbitrable contract has to appear on that court’s whitelist. The function that adds it, changeArbitrableWhitelist, is governor-only. It is a governance decision, made by people with no obligation to make it, and we never asked before building. We read that the standards existed, implemented them correctly, and assumed correctness was the requirement.
It wasn’t. The requirement was permission, and permission was never ours to give ourselves.
The check that would have caught it
The mistake is easy to describe and easy to repeat. Before building on anything external, sort what you need from them into one of three buckets:
- Self-serve. A signup, an API key, a public contract call. You can have it today, so treat it as available.
- Application. A review you submit and probably pass — a store listing, a rate-limit tier, a verified badge. Budget the wait, submit it first, and don’t build the parts that depend on it until it lands.
- Discretionary grant. A governance vote, a whitelist, a partnership, an approval with no published criteria. Treat it as unavailable until it is granted in writing.
The rule is that nothing in bucket three is allowed to be load-bearing. Design so the product is useful without it, and the grant upgrades it. We put a bucket-three dependency at the centre of the product and built outward from it.
The mitigation costs almost nothing: one message, before the code, asking whether they would approve this. A no costs an hour. Finding out afterwards cost the entire token.
I have now watched this same shape land three times in one workspace. A music game integrated a streaming API whose terms turn out to forbid games. An iOS beta sat blocked on an unsigned contract nobody had read. And this. Every time, the thing that failed was not code — it was someone else’s yes, assumed.
Where it stands, plainly
The contract is on Arbitrum One at 0x9c2300F97E14D7c7478003F52E562Ff2fC14E89e, deployed 17 July 2026, one million supply. It was never launched: no liquidity was added, so there is nothing to buy, and nobody should try. The owner key is a 1-of-1 Safe where the design calls for 2-of-3. The fee treasury address is immutable and still points at the deploying wallet. Its security review is a first pass whose own conclusion is do not hold real funds.
Those are the reasons it is staying retired rather than getting a launch. The one genuinely good outcome is that because it was never announced, nobody is holding it, and no one lost anything but our own time.
One piece is worth keeping. ProofOfDelivery.sol does the useful half with none of the machinery: plain ETH locked against a hash of what was ordered, confirm() releases it to the payee, a timeout refunds the payer, and the fee is taken only on a confirmed delivery. No token. No arbitrator. No allowlist, and no owner powers to explain away. It turns out that when you remove the parts that needed permission, what is left is smaller, duller, and actually shippable.
The contract and its unresolved risks are documented on the Indemnity page. If you are building something where reversible payment matters and you would rather not repeat this, that is the kind of thing we do at Rebel Studios.
