Cas réels : un agent IA achète une eSIM, puis récupère son argent
Sommaire
Des exécutions réelles du kit, avec leurs transactions, pour que vous puissiez vérifier chaque étape on-chain. Le cas 2 (section 10) montre le même agent achetant notre capacité via le hub. Le premier cas est le parcours complet de l'agent dans les deux sens : un agent IA trouve une offre, la paie en USDC via x402, remet l'eSIM à son propriétaire, puis demande un remboursement, que le nœud vendeur rembourse sans que personne ne touche à un portefeuille (wallet).
1. Ce qui s'est passé
Le 15 octobre 2026, le propriétaire d'ARGUS, un agent IA personnel, lui a écrit sur Telegram : achète-moi l'eSIM la moins chère pour la Turquie. ARGUS a demandé des offres au nœud vendeur du kit, a choisi Turquie 500 Mo pour 7 jours à 0,31 USDC, a demandé l'approbation de son propriétaire et a payé depuis son propre portefeuille. Quelques secondes plus tard, le propriétaire avait dans la conversation le QR code et le code d'activation. ARGUS lui-même ne les a jamais vus.
Trente-quatre minutes plus tard, le propriétaire a demandé à ARGUS un remboursement. L'eSIM n'était pas installée : le nœud vendeur l'a donc annulée chez le grossiste et a renvoyé les 0,31 USDC au portefeuille de l'agent. Tous les portefeuilles et le solde de gros sont revenus à leur point de départ.
Le vendeur était le banc d'essai privé du kit : un nœud vendeur ordinaire, qui vend à prix coûtant et n'est joignable que depuis l'hôte de l'agent. Rien n'a été simulé.
2. Qui a participé
| Partie | Ce que c'est | Adresse ou lieu |
|---|---|---|
| Propriétaire | la personne pour qui travaille ARGUS ; il approuve chaque paiement sur Telegram | une conversation Telegram privée |
| ARGUS | l'agent (acheteur) ; il paie depuis son propre portefeuille | 0x428f2Be4327908948e3B1915C55ddF8fA6bf73cc |
| Nœud vendeur | le nœud rails/ du kit (vendeur), remboursements automatiques activés | https://esim.modelmarket.dev/stand |
| Portefeuille du vendeur | reçoit les paiements et paie les remboursements (PAY_TO et REFUND_KEY_FILE désignent le même portefeuille) | 0xF47161fBB56D0D910a17e503C8292903F41BbAE2 |
| Facilitateur | le facilitateur x402 de Coinbase : il vérifie chaque transfert signé et le met on-chain en payant le gas | CDP |
| Grossiste | eSIM Access : émet et annule le profil | le compte de l'opérateur |
3. La chaîne complète
sequenceDiagram
autonumber
actor O as Propriétaire (Telegram)
participant A as ARGUS (agent)
participant S as Nœud vendeur
participant F as Facilitateur x402
participant W as eSIM Access
O->>A: achète-moi l'eSIM la moins chère pour la Turquie
A->>S: POST /quote {"country":"TR"}
S-->>A: TR_0.5_7, 500 Mo, 7 jours, 0,31 USDC (signé)
A->>O: approuver esim_buy ?
O-->>A: /yes
A->>S: POST /order {"offer_id":"TR_0.5_7"}
S-->>A: 402 Payment Required : 0,31 USDC au portefeuille du vendeur
A->>S: la même commande avec PAYMENT-SIGNATURE (EIP-3009, signé par ARGUS)
S->>F: verify
S->>W: commande d'un profil (idempotente selon le paiement)
W-->>S: profil 26101505200012
S->>F: settle
F-->>S: tx 0x617efd7a…3ae0
S-->>A: livraison : code d'activation, QR, jeton de statut
A->>O: QR et code d'activation, envoyés sans passer par le modèle
Note over O,W: 34 minutes plus tard
O->>A: rembourse la dernière eSIM, je ne l'ai pas installée
A->>O: approuver esim_refund ?
O-->>A: /yes
A->>S: POST /refund {esim_tran_no, status_token}
S->>W: installée ? données utilisées ?
W-->>S: GOT_RESOURCE, non installée, 0 octet
S->>W: annuler le profil
S->>F: verify et settle 0,31 USDC, portefeuille du vendeur → ARGUS
F-->>S: tx 0x6dcfb7f0…ed4b
S-->>A: refunded
A->>O: 0,31 USDC remboursés, tx 0x6dcfb7f0…ed4b
4. Les transactions
Toutes sur Base mainnet, en USDC (0x8335…2913). Ouvrez-les sur basescan.org.
| Étape | Transaction | Bloc, heure (UTC) | De → à | Montant | Gas payé par |
|---|---|---|---|---|---|
| Approvisionnement de l'agent | 0x225afba5cd651e3ccdf70593fe985d1e24ddc14836270e83b13f546b6d017578 | 52624834 | portefeuille du propriétaire 0x097e…475a → ARGUS | 0,50 | le propriétaire |
| Achat | 0x617efd7abe4b4c6a80b54046617a73415aa54df7ada5f002ce7260cf68ac3ae0 | 52626137, 05:20:21 | ARGUS → portefeuille du vendeur | 0,31 | facilitateur 0x93f6…be44 |
| Remboursement | 0x6dcfb7f075ad7fb6308064ac53e310dfb6adfd9b65d9f44b3c9a6dd39ca9ed4b | 52627173, 05:54:53 | portefeuille du vendeur → ARGUS | 0,31 | facilitateur 0x4c93…0205 |
L'achat comme le remboursement sont des appels EIP-3009 transferWithAuthorization : le portefeuille qui paie signe, le facilitateur soumet. Ni ARGUS ni le portefeuille du vendeur n'ont détenu d'ETH, avant, pendant ou après.
5. Argent et soldes
flowchart LR
A["Portefeuille d'ARGUS"] -- "achat : 0,31 USDC (x402)" --> S["Portefeuille du vendeur"]
S -- "remboursement : 0,31 USDC (EIP-3009 via le facilitateur)" --> A
N["Nœud vendeur"] -- "commande de 0,30 USD, puis annulation" --> W["Solde eSIM Access"]
F["Facilitateur x402"] -. "gas des deux transferts" .-> B["Base"]
| Avant | Après l'achat | Après le remboursement | |
|---|---|---|---|
| Portefeuille d'ARGUS | 0,50 USDC | 0,19 USDC | 0,50 USDC |
| Portefeuille du vendeur | 0 | 0,31 USDC | 0 |
| Solde eSIM Access | 20,00 $ | 19,70 $ | 20,00 $ |
| ETH dépensé par ARGUS ou le vendeur | 0 | 0 | 0 |
Le banc d'essai vendait à prix coûtant (MARGIN_PCT=0, MIN_MARGIN_USD=0.01) : 0,30 $ en gros, 0,31 USDC pour l'agent. Avec la marge par défaut du kit, le même forfait coûte 1,30 $.
6. Les règles du remboursement
stateDiagram-v2
[*] --> delivered: payé et réglé
delivered --> in_use: installée ou données utilisées
in_use --> [*]: 409 in_use, pas de remboursement
delivered --> cancelled: POST /refund avec le jeton de statut
cancelled --> refunded: le portefeuille de remboursement rend le prix
cancelled --> due: pas de portefeuille de remboursement, ou le paiement a échoué
due --> [*]: l'opérateur le paie à la main
refunded --> [*]
- Seul le payeur peut le demander. La demande porte le jeton de statut reçu avec la livraison. Un mauvais jeton reçoit
404, comme une eSIM inconnue. - Seulement une eSIM inutilisée. Le nœud interroge d'abord le grossiste : un profil installé, ou qui a consommé la moindre donnée, reçoit
409 in_useet reste tel quel. - Seulement à l'adresse qui a payé, seulement le prix payé. Le nœud conserve le payeur et le prix avec la commande, et ne rembourse jamais davantage.
- Une seule fois. Une deuxième demande, ou deux simultanées, reçoivent la même réponse et aucun second paiement. Un paiement qui a échoué n'est pas relancé tout seul : son transfert a pu aboutir, l'opérateur vérifie donc d'abord la chaîne.
- Plafonné.
REFUND_MAX_USDpar remboursement etREFUND_DAILY_MAX_USDpar jour, 50 chacun par défaut. Le banc d'essai tournait avec 1 et 2. - D'abord annuler, ensuite payer. Le grossiste rembourse à l'opérateur le profil annulé : l'opérateur ne perd que du temps.
7. Activer les remboursements sur votre nœud
Les remboursements sont désactivés tant que vous ne donnez pas au nœud un portefeuille d'où les payer. Sans lui, POST /refund annule quand même l'eSIM inutilisée et enregistre le remboursement comme dû, et vous recevez le webhook agent_order.refund_due pour le payer vous-même.
# un portefeuille avec des USDC sur Base pour les remboursements : celui de PAY_TO, pour rendre l'argent
# reçu, ou une réserve séparée. Pas besoin d'ETH : le facilitateur paie le gas.
cd ~/esim-operator-kit
umask 077
printf 'Refund wallet private key: '; read -rs v; printf %s "$v" > secrets/refund_key; echo; unset v
sudo chown 1000:1000 secrets/refund_key && sudo chmod 600 secrets/refund_key
echo "REFUND_KEY_FILE=/run/secrets/esim/refund_key" >> .env
echo "REFUND_MAX_USD=20" >> .env
echo "REFUND_DAILY_MAX_USD=100" >> .env
docker compose up -d rails
curl -s https://<your domain>/agents/healthz # "refunds": "automatic"
| Paramètre | Ce qu'il fait |
|---|---|
REFUND_KEY_FILE | La clé du portefeuille d'où partent les remboursements. La seule clé privée que le kit lise jamais, et seulement si vous l'activez. |
REFUND_MAX_USD | Le maximum d'un remboursement. |
REFUND_DAILY_MAX_USD | Le maximum de tous les remboursements sur une journée. |
CDP_KEY_FILE | Déjà défini pour les paiements ; les remboursements passent par le même facilitateur. |
Si le portefeuille de remboursement n'est pas votre portefeuille principal, n'y gardez qu'une réserve : une clé sur un serveur est une clé qu'une intrusion peut lire.
8. Demander un remboursement
L'agent a besoin des deux valeurs reçues avec la livraison :
curl -s -X POST https://<seller>/agents/refund -H 'content-type: application/json' \
-d '{"esim_tran_no":"26101505200012","status_token":"…"}'
{"success": true, "result": {"esim_tran_no": "26101505200012", "status": "refunded", "amount_usd": "0.31",
"to": "0x428f2be4327908948e3b1915c55ddf8fa6bf73cc", "at": "2026-10-15T05:54:50.399Z",
"transaction": "0x6dcfb7f075ad7fb6308064ac53e310dfb6adfd9b65d9f44b3c9a6dd39ca9ed4b"}}
status vaut refunded avec la transaction, ou due quand le vendeur paie à la main. /listing.json indique lequel : son refund.policy décrit la règle, et /healthz affiche "refunds": "automatic" ou "operator".
9. Ce que nous avons appris
- Un agent prudent s'interroge sur un remboursement immédiat. À la demande « achète et rembourse tout de suite » en un seul message, ARGUS s'est arrêté et a demandé ce qui était voulu : acheter et garder, ne rien acheter, ou un remboursement pour une vraie raison. Le propriétaire a acheté, puis a demandé le remboursement dans une demande à part. De vrais agents rembourseront pour de vraies raisons, et un vendeur peut s'attendre à ce qu'ils le demandent poliment et une seule fois.
- Le code d'activation n'a jamais quitté la livraison. Le vendeur ne l'envoie que dans la réponse à la commande payée ; ARGUS l'a transmis à la conversation du propriétaire et n'a gardé que le numéro de profil et le jeton de statut. Ni les registres du vendeur ni le journal de l'agent ne le conservent.
- Aucun gas nulle part. L'agent a signé une autorisation pour payer, le portefeuille du vendeur une pour rembourser, et le facilitateur a fait le reste.
- C'est le remboursement du grossiste qui rend le remboursement sûr. Le nœud annule avant de payer : une commande remboursée ne coûte rien à l'opérateur.
10. Cas 2 : via le hub, auprès de notre nœud public
Dans le cas 1, on avait donné à l'agent l'adresse du vendeur. Dans le cas 2, il a trouvé le vendeur lui-même : ARGUS a cherché des vendeurs d'eSIM sur le hub modelmarket.dev, a obtenu notre capacité esim.quote@v1 de esim-modelmarket, lui a demandé des offres via le hub et a payé via x402 sur le /order du vendeur lui-même. Le hub a acheminé le devis gratuit et n'a jamais touché l'argent.
Pour ce test, notre nœud public est passé de la démo à la vente, puis est revenu en démo juste après. Le nœud derrière esim-modelmarket (modelmarket.dev/providers/esim) a vendu pendant 52 minutes, de 06:56:55 à 07:48:33 UTC le 15 octobre 2026, à prix coûtant (MARGIN_PCT=0, MIN_MARGIN_USD=0.01), encaissant sur le même portefeuille vendeur que le banc d'essai et remboursant depuis celui-ci automatiquement (au plus 2 $ par remboursement et 5 $ par jour). Depuis 07:48:33, il refuse de nouveau chaque commande avec demo_mode.
sequenceDiagram
autonumber
actor O as Propriétaire (Telegram)
participant A as ARGUS (agent)
participant H as Hub modelmarket.dev
participant S as Nœud vendeur (modelmarket.dev/providers/esim)
participant F as Facilitateur x402
O->>A: achète-moi l'eSIM la moins chère pour la Turquie
A->>H: GET /ai-market/v2/search?intent=esim
H-->>A: esim.quote@v1 de esim-modelmarket (via esim.modelmarket.dev)
A->>H: POST /ai-market/v2/invoke esim.quote@v1 (gratuit)
H->>S: relaie le devis
S-->>A: seller demo: false · TR_0.5_7 0,31 USDC · commande sur …/providers/esim/order
A->>O: [SELLS] TR_0.5_7 0,31 USDC · approuver esim_buy ?
O-->>A: /yes
A->>S: x402 directement au vendeur : 402, puis PAYMENT-SIGNATURE (EIP-3009)
S->>F: verify, puis settle
F-->>S: tx 0xeea12962…881e
S-->>A: delivery
A->>O: QR et code d'activation, sans passer par le modèle
O->>A: rembourse la dernière eSIM
A->>O: approuver esim_refund ?
O-->>A: /yes
A->>S: POST /refund {esim_tran_no, status_token}
S->>F: annulation chez eSIM Access, puis 0,31 USDC rendus
F-->>S: tx 0x45dc5ce6…ec88
S-->>A: refunded
| Heure (UTC) | Ce qui s'est passé |
|---|---|
| 06:56:55 | Le nœud public passe de la démo à la vente. Via le hub, son devis indique désormais demo: false et 0,31 USDC |
| 07:21 | Première tentative : ARGUS voit les offres mais refuse d'acheter. Le nœud s'appelait encore « modelmarket.dev demo seller », et l'agent s'est fié au nom |
| 07:23 | Correctif déployé : le nom du nœud suit son mode, et ARGUS étiquette chaque offre [SELLS] ou [DEMO] d'après la réponse du vendeur lui-même |
| 07:47:35 | ARGUS achète TR_0.5_7 après le /yes du propriétaire ; l'eSIM 26101507470034 arrive sur le Telegram du propriétaire |
| 07:48:02 | Le propriétaire demande le remboursement et l'approuve avec /yes ; le nœud annule l'eSIM inutilisée et rembourse |
| 07:48:33 | Le nœud public revient en démo |
| Étape | Transaction | Bloc, heure (UTC) | De → à | USDC | Gas payé par |
|---|---|---|---|---|---|
| Achat | 0xeea12962c4f494e55acc59a376203bd4b704804f19a84b4b8f47a96b265a881e | 52630556, 07:47:39 | ARGUS → portefeuille du vendeur 0xF471…bAE2 | 0,31 | facilitateur 0x68a9…7863 |
| Remboursement | 0x45dc5ce6579d8e889750442d63773586c481a11b30ddf44805960860f3bbec88 | 52630569, 07:48:05 | portefeuille du vendeur → ARGUS | 0,31 | facilitateur 0x625d…ac39 |
ARGUS a été le seul acheteur de la fenêtre : les registres du nœud contiennent une commande, remboursée. À la fin, ARGUS détenait de nouveau 0,50 USDC, le portefeuille du vendeur 0, le solde eSIM Access 20,00 $, l'eSIM était à l'état CANCEL, et aucun portefeuille n'avait dépensé d'ETH.
Ce que ce cas a montré :
- Un agent lit les noms. Le devis du vendeur disait qu'il vendait, mais son nom disait « demo », et l'agent a cru le nom. Un nœud vendeur doit être nommé d'après ce qu'il fait ; un agent doit décider d'après la réponse du vendeur, pas d'après son nom.
- Le hub trouve, le vendeur vend. La découverte et le devis gratuit sont passés par le hub ; le paiement et le remboursement sont allés directement de l'agent au vendeur, et le hub n'a détenu d'argent à aucun moment.
- La démo est un réglage. Le même nœud, le même catalogue et le même compte de gros vendent ou refusent selon
DEMO_MODE: un opérateur ouvre et ferme une fenêtre de vente en quelques secondes.