International trade has a trust problem, and for decades the fix has been to insert a bank in the middle of every deal. A Buyer in one country and a Seller in another don't know each other, so a bank opens a letter of credit, holds the money, checks the paperwork, and releases funds when it's satisfied. It works, but the price is steep: commissions that can run up to 5% of the transaction, and total visibility for the bank into who is trading what, with whom, and at what price. That's the problem I set out to solve with INCOTERMS2020 Escrow — an escrow dApp on Midnight Network smart contractbahsederek
Project Story

International trade has a trust problem, and for decades the fix has been to insert a bank in the middle of every deal. A Buyer in one country and a Seller in another don't know each other, so a bank opens a leey, checks thepaperwork, and releases funds when it's satisfied. It works, but the price is steep to 5% of thetransaction, and total visibility for the bank into who is trading what, with whom, and at what price. That's the problem I set out to solve with INCOTERMS2020 Escrow — an escrow dApp on Midnight Network that replaces the bank with a smart contract and a zero-knowledge proof.

The project centesing Incoterms2020 rules — FOB, CIF, and EXW — because I didn't want a toy "lock and unlock money" demo; I wanted something that mirrors how a real shipment actually works. In my app, a Buyer picks thIncoterm that govt and a deadline, and locks real NIGHT funds into a freshly deployed Escrow contract. A Carrier, once the goods are handed over, generates a secret off-chain and gives its hash to the Buyer up front (baked into the lock transaction as a commitment) — the actual secret only reaches the Seller later, through the FCR document, once delivery is confirmed. The Seller then enters that secret into the app, which builds a local zero-knowledge proof that they know a value matching the commitment — without the secret itself ever leaving their machine or touching the network — and the contract releases the funds straight to them. If nobody claims in time, the Buyer can trigger a refund, checked against the real on-chain timestamp so nobody can fake an early refund.On top of all thaomatically carvesout a fixed 0.1% commission for the platform, split out of the same payout in th

Building the contract itself, in Compact, was where most of the real engineering happened. The state machine is simple — Empty -> Locked -> Claimed/Refunded — but making it trustworthy took real work. Compact has no division operator, so proving a 0.1% commission is cor the fee as awitness value and having the circuit verify it with two multiplication inmputing it. Fundcustody is real, not symbolic: receiveUnshielded actually pulls NIGHT out of the Buyer's wallet into the contract, and claimWithSecret pays it back out in the same atomic transaction, split two ways with two sendUnshielded calls.

The biggest twist tried to gofurther and hide the buyer, seller, and amount entirely, using Midnight's shield the transparentones I ended up shipping with. I got a full contract compiling cleanly against receiveShielded/sendShielded/sendImmediateShielded, with the commission split working elegantly off a coin's leftover change output. But when I actually ran it against a real local Midnightdevnet — not just a coin sent to a contract address gets accepted by the node but never shows up in the indexer, elling, acrossmultiple independent test runs. I dug all the way into the SDK's own transaction-building code to prove the coin really was inside the broadcast transaction; it just isn't indexable yet for contract-owned shielded recipients on this SDK version. Rather than ship a "private" feature I couldn't actually verify moves real money, to thetransparent architecture and put the commission system on top of that instead — a ship somethingthat only looks like it works.

I verified everything by writing a headless integration test that spins up a full local Midnight devnet — node, indexer, and proof server, all in Docker, with real pre-funded wallets — and runs the entire lplit) and lock →refund flows as actual transactions, checking that real balances moved correctly rutput. That habit of not trusting anything I hadn't watched actually happen on a live chain is probably the single biggest thing I take away from this build — documentation told me one story about shielded coins, and a real transaction told me another.

I built this entipact contract tothe ZK-proof flow to the React frontend, and every core flow — locking, claiming with the commission split, and timing out into a refund — is verified against a real local Midnight chain, not mocked.

Built With

Share this project:

Updates

Submission history