Design notes: wallet names with a lock-based resale and no escrow (Substrate pallet)

Hello everyone,

We added a names pallet to the MADAR network (spec 7) and would value review of the design, especially the resale flow.

  • A name binds to one external wallet: EVM (EIP-191 personal_sign) or Solana (ed25519). Proof is the wallet’s own signature over a fixed, human-readable message.
  • Resale without escrow: the seller signs an offer (price, pay-to address, nonce, expiry). lock_sale locks the name for the buyer for at most 60 minutes; the buyer pays the seller directly; complete_sale moves the name with its remaining term. Unfinished locks expire on their own.
  • A per-name nonce is bumped on every ownership change, so signed offers cannot be replayed.
  • The runtime upgrade was approved 2-of-3 by a committee after a full rehearsal on an isolated copy of the live chain.

Open questions we’d like input on:

  1. Is 60 minutes a reasonable upper bound for a payment window across chains?
  2. Would you anchor the payment proof on-chain, or is registrar observation enough at this stage?

Details: madar-network.com/names

Hi @MADAR-NETWORK, it is an interesting design. I think the key tradeoff is that users need no account or funds on the MADAR chain, but the payment confirmation is trusted to the registrar (the payment watcher). The suggestions below make that trust auditable.

  1. Store the payment transaction hash on chain when the sale completes.

Example: The registrar completes a sale, and the name moves to the buyer. Later, the seller says that no payment arrived. The chain shows only that the registrar moved the name. It does not show which payment the registrar used for its decision. Nobody can check if the registrar was correct, made an error, or was compromised.

Solution: Record the hash of the payment transaction on chain with the completed sale. Then the committee, or anyone, can find that transaction on the payment chain and check it. A dispute then has evidence. The dispute can happen inside the sale flow (a challenge window that starts when the registrar confirms the payment and ends before the transfer, making the transfer optimistic) or outside it (a committee decision after the transfer).

  1. Add a random identifier to the payment amount.

Example: Buyer A locks ahmad.madar at 25 USD, paid to the seller address. Buyer A sends the payment at the end of the window, and it arrives after the lock expires. The seller offer is still valid, so Buyer B locks the same name, at the same price, with the same receiving address. Buyer A’s payment arrives during Buyer B’s window. The registrar sees a payment of 25 USD to the correct address, inside the window, and completes the sale for Buyer B. Buyer B gets the name with Buyer A’s money.

Solution: Give each lock a unique exact amount: the price plus a small random part (for example, 25.000317 for Buyer A and 25.000842 for Buyer B). The registrar accepts only a payment with the exact amount of the active lock. Thus, Buyer A’s payment cannot match Buyer B’s lock. Also record the expected exact amount on chain (for example, in the lock event), so that anyone can later verify that the registrar matched the correct payment.

A sender check also solves this, but it requires the buyer to pay from the same wallet and chain that will own the name (or a previously registered wallet from the payment chain), so it excludes payments from exchanges. The unique amount permits them, if the exchange sends the exact amount (some exchanges deduct the withdrawal fee from it).

Thank you, this is exactly the kind of review we were hoping for.

On 2 (unique amounts): this is already how it works. Each lock gets its own exact amount — the price plus a random offset in 0.0001 steps — and the watcher only accepts that exact amount, to the seller’s address, on that rail, from the block where the lock started. A payment already credited to one sale can never be matched to another.

Your scenario still found a real gap, though: once Buyer A’s lock lapsed we stopped watching it, so A’s late payment was not served, and A’s amount could in principle be issued again to a later lock. We fixed it today: a lapsed lock stays watched for 6 hours. If its payment arrives late, the name is locked again for that same buyer when it is still free, otherwise the case is flagged for a manual resolution. Its amount is not reused for the same seller during that time. The page also tells buyers that exchanges must send the full exact amount.

On 1 (anchoring the payment proof): agreed. Today the registrar keeps the payment transaction off-chain. In the next runtime upgrade we plan to record the expected exact amount and rail in the lock event, and to require the payment reference (chain id + transaction hash) in complete_sale, so anyone can audit every completed sale against the payment chain. Since locks last at most 60 minutes, we lean towards a post-transfer dispute reviewed by the upgrade committee rather than a challenge window inside the sale.

Thanks again — feedback on that plan is welcome.