Retour aux projets

MCN Auctions et le Juneo Hub

2024 - 2025 · Seul auteur du contrat d'enchères, de ses backends de livraison et de son subgraph. J'ai aussi conçu et construit le Juneo Hub, le portail qui expose tout ça à l'écosystème.

Des enchères hollandaises on-chain sur la chaîne EVM de Juneo, qui livrent les tokens sur sa chaîne UTXO séparée sous une multisig 2-sur-2, et le Juneo Hub, le portail que j'ai construit autour : staking, gouvernance, tokens soulbound et analytique au même endroit.

Ce que j'ai livré

  • Le contrat UtxoDutchAuction (proxy UUPS), mis à jour jusqu'à la V4 en mainnet sans redéploiement
  • Des enchères de 1 à 10 paliers de prix, avec un plafond par enchérisseur et un accès réservable aux détenteurs d'un soulbound
  • La livraison d'une chaîne à l'autre par deux backends, un émetteur (sender) et un cosignataire (signer), sous une multisig 2-sur-2
  • Une machine à états PostgreSQL qui ne paie jamais deux fois la même offre
  • Un subgraph qui indexe chaque enchère et chaque offre
  • Une suite de tests Hardhat sur les offres, le rachat et chaque chemin de mise à niveau
  • Le Juneo Hub : enchères, staking, gouvernance, tokens soulbound et statistiques derrière une seule connexion de wallet

En chiffres

version du contrat en mainnet, mise à jour sans redéploiement
V4
paliers de prix par enchère
1 à 10
multisig : deux processus signent chaque livraison
2-sur-2
états dans la machine à états de livraison
6
Interface du Juneo Hub montrant le staking, la gouvernance et l'activité des enchères
Le Juneo Hub : le portail que j'ai construit autour du système d'enchères, avec le staking, la gouvernance et l'analytique au même endroit.

Contexte

Juneo organise des enchères hollandaises pour vendre des tokens, mais les tokens mis aux enchères vivent souvent sur la chaîne UTXO de Juneo (la P-Chain), un registre séparé de la chaîne EVM où tourne le contrat d'enchères et où les offres sont placées. À côté de ça, l'écosystème avait besoin d'un seul endroit où un détenteur pouvait tout faire à la fois : staker, voter en gouvernance, miser dans une enchère, détenir un token soulbound, vérifier les chiffres derrière tout ça. Cet endroit est devenu le Juneo Hub.

Le problème

Une enchère qui se règle dans la monnaie native de la chaîne EVM doit livrer son résultat ailleurs, complètement : de vrais tokens sur une chaîne UTXO, vers une adresse UTXO, pour une offre placée sur une chaîne différente de celle qui paie. Aucun processus backend ne doit pouvoir déplacer ces fonds seul, et aucune vente ne doit jamais payer deux fois, que ce soit à cause d'une relance, d'un crash en cours de transaction, ou d'un bug dans la file qui l'alimente.

Contraintes

Le modèle d'enchère n'allait jamais rester figé. Les paliers de prix, un plafond par utilisateur et un accès réservé aux détenteurs d'un soulbound sont tous arrivés après que la première version traitait déjà de vraies offres, donc le contrat devait rester upgradable sans casser ni remigrer chaque enchère passée. La garde des fonds livrés ne pouvait dépendre ni d'une seule machine ni d'une seule clé privée restant saine. Et comme la livraison tourne sans surveillance directe, un paiement bloqué ou échoué devait rester visible et se relancer sans risque, jamais se perdre en silence.

Ce que j'ai construit, en détail

J'ai construit UtxoDutchAuction, un contrat en proxy UUPS aujourd'hui à sa quatrième version en mainnet sans redéploiement. Chaque enchère définit de 1 à 10 paliers de prix (un ratio de prix, un time lock et un seuil de tokens par palier), un plafond par enchérisseur, et un accès optionnel réservé aux détenteurs d'un soulbound, le tout couvert par une suite de tests Hardhat qui exerce les offres, le rachat et chaque chemin de mise à niveau. La livraison tourne sur une paire de backends Node.js et TypeScript. Le sender récupère les ventes non rachetées depuis un subgraph et construit la transaction de paiement côté UTXO ; le signer ne le croit pas sur parole, il relit indépendamment la même enchère directement depuis le contrat, revérifie chaque sortie (adresse, montant, time lock, monnaie rendue à la multisig), et ne cosigne qu'ensuite, sous une multisig 2-sur-2 dont le sender seul ne peut rien dépenser. Une machine à états PostgreSQL suit chaque vente depuis la mise en file jusqu'à l'attente de signature, l'attente d'envoi puis la complétion ou l'échec, avec une détection des enchères bloquées en cours de route et une erreur BidSaleAlreadyRedeemedError qui empêche de payer deux fois la même offre. Autour de tout ça se trouve le Juneo Hub, une dApp React et Node que j'ai conçue et construite de bout en bout : l'interface des enchères elle-même, le staking, la gouvernance, les tokens soulbound et l'analytique de l'écosystème, derrière une seule connexion de wallet.

Décisions

Une multisig 2-sur-2 entre sender et signer
Le sender construit et envoie la transaction de livraison ; le signer est un second processus indépendant qui redérive le même résultat directement depuis le contrat et vérifie chaque sortie avant de cosigner. Un sender compromis ou buggé ne peut déplacer aucun token seul, et un signer compromis non plus : les deux doivent être d'accord.
Impact Aucune machine ne peut déplacer seule les fonds de livraison : il faudrait compromettre à la fois le sender et le signer.
Une machine à états Postgres, pas un envoi à l'aveugle
Une livraison blockchain échoue pour des raisons banales : un pic de gas, une connexion RPC coupée, un signer brièvement hors ligne. Donner à chaque vente un statut explicite et une protection contre le retraitement d'une vente déjà rachetée a transformé un échec en élément à relancer dans la file, pas en conversation support sur l'endroit où sont passés les tokens de quelqu'un.
Impact Une livraison ratée attend dans la file pour être relancée, et une enchère déjà livrée est refusée au lieu d'être payée deux fois.
UUPS plutôt qu'un contrat figé
Les paliers, le plafond par utilisateur et l'accès soulbound sont tous arrivés après que la V1 traitait déjà de vraies offres. UUPS a permis au contrat d'atteindre la V4 en mainnet en mettant à niveau la logique sur place, avec chaque enchère passée et son historique toujours intacts.
Impact Quatre versions en mainnet, sans redéploiement ni migration des enchères passées.
Un seul Hub plutôt que cinq applications séparées
Le staking, la gouvernance, les enchères, les tokens soulbound et l'analytique auraient chacun pu devenir leur propre site avec leur propre connexion de wallet. En faire un seul Hub veut dire qu'un détenteur se connecte une fois, et que tout ce qu'il peut faire avec ses tokens vit au même endroit.
Impact Une seule connexion de wallet au lieu de cinq, pour le staking, la gouvernance, les enchères, les soulbound tokens et les statistiques.

Résultat

Le système d'enchères tourne sur le mainnet de Juneo depuis 2024, à travers quatre mises à niveau du contrat, déplaçant les offres gagnantes des enchères EVM vers des wallets sur la chaîne UTXO sans livraison perdue ni dupliquée. Le Juneo Hub est aujourd'hui le portail quotidien de l'écosystème : là où les détenteurs stakent, votent, enchérissent et vérifient ce qu'ils détiennent, tout au même endroit.

Une seule personne a construit le contrat d'enchères, les deux backends de livraison, la machine à états et le subgraph derrière eux, plus le Hub qui met tout ça devant l'écosystème : seul auteur du système d'enchères, et constructeur du Hub autour.

Stack

  • Solidity
  • Proxies UUPS
  • Hardhat
  • Node.js
  • TypeScript
  • PostgreSQL
  • The Graph
  • React
Retour aux projets