Arbitrage cross-protocole sur Arbitrum. Cette page tient l'état d'avancement du projet et l'instrument qui permet de contrôler soi-même la mécanique économique — montant optimal, seuil de rentabilité, coût du financement — calculée en arithmétique entière exacte, comme le contrat la calculera.
Arbitrum, blocs 500 235 577 → 500 581 177 — 345 600 blocs, soit une journée. Six paires, seuil de bruit à 0,20 $ par occasion — les cinq paires détaillées plus bas portent 13 pools, la sixième s'y ajoute. Événements Swap (V3) et Sync (V2), aucune réserve lue bloc par bloc.
Encore ouvertes quand notre transaction atterrirait. C'est le seul chiffre qui décide.
Refermées pendant le bloc même que notre transaction viserait. Course FCFS, pas une prise.
Sur toute la journée et les six paires. Personne ne laisse rien traîner.
| Paire | Pools | Événements | Blocs exploitables |
Occasions | Atteign. | Contestées | Capturées | Brut médian | Brut max |
|---|---|---|---|---|---|---|---|---|---|
| WETH/ARB | 3 | 46 050 | 27 061 | 18 | 1 | 17 | 6 | 0,4343 $ | 2,8552 $ |
| WETH/USDC | 4 | 39 849 | 16 894 | 7 | 0 | 7 | 2 | 0,2478 $ | 0,6398 $ |
| ARB/USDT0 | 2 | 4 689 | 3 503 | 10 | 1 | 9 | 2 | 0,8776 $ | 6,8219 $ |
| ARB/USDC | 2 | 3 991 | 2 798 | 4 | 0 | 4 | 0 | 0,5531 $ | 3,2291 $ |
| PENDLE/WETH | 2 | 2 585 | 2 083 | 0 | — | — | — | — | — |
Les totaux de la synthèse — 39 occasions, 10 capturées — sont exactement épuisés par ces cinq paires : la sixième n'a produit aucune occasion. Je le déduis des totaux, sa ligne ne figurait pas dans la sortie que j'ai pu lire.
WETH/USDC, la paire de référence, n'a produit aucune occasion atteignable. Les deux seules viennent de paires moins liquides — ce qui est cohérent avec l'idée que la liquidité attire la concurrence, et c'est le seul indice favorable de toute la mesure.
| Paire | Ouverture | Fermeture | Persistance | Brut | Déjà prise ? |
|---|---|---|---|---|---|
| WETH/ARB | 500 579 747 | 500 579 749 | 2 blocs | 0,7985 $ | oui, au bloc de fermeture |
| ARB/USDT0 | 500 463 576 | 500 463 578 | 2 blocs | 0,8776 $ | non |
Total : 1,68 $ de brut par jour, avant gas, en supposant les deux courses gagnées. La première était prise par une autre transaction — mais au bloc de fermeture, donc une transaction partie du bloc d'ouverture serait arrivée avant elle. Le raisonnement tient ; la taille, non.
J'avais d'abord annoncé un plafond de 9,68 $/jour, en supposant que les deux occasions atteignables étaient les plus grosses de leur paire. Vérification faite, elles sont proches de la médiane : le plafond est 1,68 $/jour, six fois moins. Le collecteur ne publiait pas le notionnel avancé ; il le publie maintenant, parce que c'est le rendement — et non le profit absolu — qui permet la seule comparaison utile.
| Paire | Notionnel médian | Notionnel max | Rendement médian | Rendement max |
|---|---|---|---|---|
| ARB/USDC | 131 $ | 320 $ | 0,4225 % | 1,0100 % |
| ARB/USDT0 | 231 $ | 755 $ | 0,3255 % | 0,9040 % |
| WETH/ARB | 300 $ | 867 $ | 0,1703 % | 0,5014 % |
| WETH/USDC | 350 $ | 1 027 $ | 0,0773 % | 0,1118 % |
| USDC/CHIP | — | — | aucune occasion | |
| PENDLE/WETH | — | — | aucune occasion | |
La sixième paire est USDC/CHIP : sans occasion, ce qui confirme la déduction faite plus haut à partir des totaux.
Les deux occasions atteignables : 0,7985 $ sur 469 $ avancés (0,1703 %) et 0,8776 $ sur 270 $ (0,3255 %). Soit 1,68 $/jour sur 739 $ immobilisés — 0,227 % par jour, 83 % par an avant gas.
Le rendement vaut brut ÷ notionnel, et les deux sont dans le même jeton : le prix indicatif et la puissance de dix s'annulent. C'est un rapport d'entiers exact. Contrairement à tous les montants en dollars de cette page, 0,1703 % et 0,3255 % ne sont pas affectés par le fait que j'ai écrit ARB à 1,00 $ de mémoire. Ce sont les chiffres les plus fiables de la mesure — seule l'approximation de taille en réserves virtuelles les touche encore.
En croisant les adresses de phase0.json avec les tiers de frais de pools.json : l'occasion WETH/ARB vendait sur le tier 5 pb et rachetait sur le tier 100 pb, il fallait donc franchir 1,0606 % d'écart. Celle d'ARB/USDT0 impliquait le tier 30 pb, seuil 0,3510 %. Le couple 5 + 5 pb, le plus surveillé, ne demande que 0,1000 %.
La lecture complète de pools.json le montre : cinq paires sur six n'offrent aucun couple à frais serrés. WETH/ARB, USDC/CHIP, ARB/USDC, PENDLE/WETH et ARB/USDT0 ne proposent que des couples impliquant un tier ≥ 30 pb. Toute occasion trouvée sur ces paires devait donc impliquer un tier élevé — la configuration ne permettait presque rien d'autre. Ce n'était pas une découverte sur le marché, c'était une propriété du jeu de pools que j'avais moi-même retenu.
Ce qui subsiste est plus mince et plus honnête : WETH/USDC est la seule paire à offrir des couples bon marché — 0,06 % et 0,10 % — et c'est celle qui a produit 7 occasions et zéro atteignable. Cohérent avec l'idée que les tiers serrés sont arbitrés en un bloc, mais sur une seule paire.
| Paire | Seuil le plus bas disponible | Occasions | Atteignables |
|---|---|---|---|
| WETH/ARB | 0,3510 % | 18 | 1 |
| ARB/USDT0 | 0,3510 % | 10 | 1 |
| WETH/USDC | 0,0600 % | 7 | 0 |
| ARB/USDC | 0,3510 % | 4 | 0 |
| USDC/CHIP | 0,3109 % | 0 | 0 |
| PENDLE/WETH | 0,3510 % | 0 | 0 |
WETH/ARB a produit 18 occasions en devant franchir au moins 0,35 %, quand WETH/USDC n'en a produit que 7 avec des couples n'exigeant que 0,06 %. Presque six fois moins cher à déclencher, et deux fois et demie moins d'occasions. Ce contraste ne découle pas du choix des pools : il dit que les paires adossées à l'ARB se désynchronisent réellement davantage.
La consigne pour l'élargissement change donc de nature. Non pas « viser les tiers à frais élevés » — c'était l'artefact — mais viser les paires volatiles de capitalisation moyenne, où la dislocation est réelle, quel que soit le tier. C'est un critère mesurable : occasions par jour rapportées au seuil le plus bas disponible.
Deux paires ARB, une WETH/USDC, trois sans occasion. Le contraste ci-dessus repose sur quatre paires productives, et l'« atteignable » sur deux événements. C'est une direction à tester, pas une règle établie. Elle a au moins le mérite d'être falsifiable : si l'élargissement montre autant d'occasions sur des paires majeures à seuil bas, elle tombe.
La collecte avait identifié 9 transactions ayant touché les deux pools d'une occasion. Leurs reçus donnent le coût réel, sans qu'aucun contrat n'ait été déployé. Les neuf touchent exactement deux pools suivis : ce sont bien des arbitrages à deux jambes, donc le gaz relevé est celui de ce qu'on construirait.
| Coût | Gaz | Calldata | |
|---|---|---|---|
| minimum | 0,0133 $ | 209 171 | — |
| médiane | 0,0165 $ | 272 390 | 72 octets |
| maximum | 0,0281 $ | 458 425 | 484 octets |
Le seuil de bascule était à 0,043 $. Le coût mesuré est de 0,0165 $ — une marge de 2,6×. Et l'opération reste positive même au coût maximum observé : 39 × 0,0281 = 1,10 $ contre 1,68 $ de revenu.
| Coût d'une tentative ratée | Gas / jour | Net / jour | Sur 739 $ immobilisés |
|---|---|---|---|
| 20 % du prix plein — revert précoce | 0,13 $ | +1,55 $ | 76 % / an |
| 100 % — hypothèse pessimiste | 0,64 $ | +1,03 $ | 51 % / an |
Positif dans toutes les colonnes, et c'est une borne inférieure : le revenu ne compte que les 2 occasions atteignables, jamais une part des 37 courses.
« Sur Arbitrum, le coût est dominé par la publication de la calldata sur L1, pas par les opcodes. » Mesure : la part L1 vaut 0,1 % du gaz, sur 9 reçus sur 9 portant le champ — soit environ 272 unités de gaz. La calldata médiane fait 72 octets. Le coût est à 99,9 % de l'exécution L2.
« Une transaction qui échoue paie presque le prix plein. » C'était une conséquence de la première, et elle tombe avec elle : une transaction qui revert tôt coûte bien moins qu'une réussie, pas autant. J'en avais déduit que « tirer sur toutes les occasions » serait ruineux — c'est l'inverse.
L'explication la plus plausible : depuis l'arrivée des blobs (EIP-4844), Arbitrum publie ses lots dans des blobs et non plus en calldata L1. « La calldata domine » était un fait d'avant Dencun que j'ai récité au présent. La mesure reste ce qui compte — ce mécanisme n'est qu'une hypothèse d'explication. Conséquence pratique : optimiser la calldata n'est pas une priorité, contrairement à ce que j'avais avancé ; c'est l'exécution L2 qu'il faudrait resserrer.
Ces deux corrections vont dans le sens qui m'arrange, et c'est précisément pour ça qu'elles sont écrites ici plutôt que passées sous silence.
Tous les notionnels supposaient que la liquidité du tick courant absorbe le trade. Verdict : INSUFFISANTE. La pire jambe est le rachat ARB/USDT0 sur le tier 5 pb — 56,75 ticks franchis contre un espacement de 10, le trade traverse cinq à six bornes de liquidité. WETH/ARB est indéterminé, avec 33,25 ticks sur un espacement de 200.
Plutôt que d'écrire le moteur de swap V3 complet — long, et qui se trompe en silence — le trade est plafonné à ce qui reste dans le tick et le profit recalculé exactement à cette taille. Dans un tick, un pool V3 est exactement un pool à produit constant : l'arithmétique y est juste au wei. Le résultat est un minorant garanti, jamais une surestimation.
| Budget de ticks | WETH/ARB | ARB/USDT0 | Brut / jour | Net / jour |
|---|---|---|---|---|
| tout l'espacement — optimiste | 0,7985 $ (100 %) | 0,2803 $ (32 %) | 1,0788 $ | +0,92 $ |
| la moitié | 0,7985 $ (100 %) | 0,1469 $ (17 %) | 0,9454 $ | +0,79 $ |
| un dixième — le seul prudent | 0,6666 $ (84 %) | 0,0304 $ (3,5 %) | 0,6970 $ | +0,54 $ |
Le net déduit 2 transactions réussies à 0,0165 $ et 37 ratées à 20 % du prix plein — justifié par la part L1 mesurée à 0,1 %, une transaction qui revert tôt consommant peu d'exécution L2. Au prix plein pour les ratées, le net au budget prudent tombe à +0,05 $/jour.
Le budget « un dixième » est le seul qui tienne si le prix est collé à une borne, puisque le test compare l'impact à l'espacement et non à la distance réelle jusqu'à la prochaine borne initialisée.
Au budget prudent, WETH/ARB conserve 84 % de son profit : cette occasion est réelle. ARB/USDT0 n'en conserve que 3,5 % — 96,5 % de son « profit » venait de l'hypothèse de profondeur infinie dans un pool 5 pb mince. Il ne reste donc qu'une occasion atteignable et solide par jour, pas deux.
J'avais écrit +1,03 à +1,55 $/jour, soit 376 à 566 $/an. Le minorant garanti donne +0,05 à +0,54 $/jour — 20 à 198 $/an. Entre 3 et 20 fois moins. Le brut de 1,68 $/jour dont je le tirais était calculé à une taille que la liquidité n'autorise pas.
Ce qui reste intact : les 39 occasions, les 2 atteignables, la persistance — la porte de rentabilité ne dépend que du prix marginal, exact en réserves virtuelles. Et le gaz, mesuré sur des reçus.
L'asymétrie de cette méthode est assumée : elle peut prouver le succès, pas l'échec. Si le minorant couvre le gaz, l'opération est rentable et le moteur V3 est inutile — c'est le cas. S'il ne l'avait pas couvert, on n'aurait rien su, le vrai optimum étant plus haut.
1. n = 2. L'intervalle de confiance à 95 % de Poisson sur deux événements observés va de 0,24 à 7,22 occasions atteignables par jour. Une journée ne tranche pas la fréquence ; elle tranche l'ordre de grandeur.
2. Les dollars reposent en partie sur des valeurs que j'ai écrites à la main. ARB est compté à 1,00 $ et WETH à 3 000 $ — ce ne sont pas des prix mesurés. Les deux plus gros chiffres du tableau sont libellés en ARB : si ARB vaut 0,40 $, le plafond passe de 1,68 à 1,15 $/jour. Ces prix ne servent qu'à l'affichage, jamais au calcul — la porte de rentabilité et le montant optimal sont en unités de jeton, exacts.
3. 100 % des occasions ont une jambe en réserves virtuelles V3. Distinction qui compte : la porte est exacte même en V3, car elle ne dépend que du prix marginal, que les réserves virtuelles donnent juste. L'existence des 39 est donc solide. C'est la taille qui est approximative — le calcul suppose que la liquidité du tick courant s'étend à l'infini, ce qui sur des pools concentrés tend à surestimer le profit.
4. L'intra-bloc n'est pas mesuré. La série ne retient qu'un état par bloc, donc une occasion ouverte et refermée dans le même bloc est invisible. Le total de 39 est sous-estimé — mais les manquantes sont exactement celles qu'aucun bot à RPC domestique ne pourrait prendre.
5. Les pools à frais dynamiques sont écartés (Algebra/Camelot V3), soit 22 % de l'activité WETH/USDC. Biais pessimiste, assumé.
6. C'est une borne supérieure. La mesure observe un monde où nous ne participons pas. Une occasion qui a survécu deux blocs a survécu à la concurrence existante ; en entrant dans la course, nous courrions contre ceux qui voient les mêmes.
Le goulot n'est pas le code. 94,9 % des occasions se ferment en un bloc, et la contrainte y est l'aller-retour de 30 à 150 ms contre moins de 5 ms pour un bot co-localisé. Ni le Yul, ni TSTORE, ni un bot en Rust, ni revm ne réduisent une latence réseau. La Phase 2 telle qu'elle était décrite ne déplace pas ce chiffre.
Mais la contrainte n'est ni le capital ni le gas — elle est la capacité. Le notionnel plafonne autour de 1 000 $ par occasion, fixé par la taille de la dislocation de prix, pas par ce qu'on veut investir. Impossible d'y déployer 100 000 $ : le rendement est excellent et la capacité négligeable. L'échelle passe donc par le nombre de paires, jamais par la mise.
« Un facteur 60 est inatteignable. » Je l'avais écrit en pensant qu'il fallait multiplier le capital. Comme chaque occasion n'absorbe que quelques centaines de dollars, l'échelle se prend en paires : 60 paires à ~1,7 $/jour font ~100 $/jour, avec un capital qui reste petit. C'est exactement le facteur 60. Je m'étais trompé sur le mécanisme.
« Le P&L attendu est nul ou négatif. » Écrit en supposant un notionnel de 20 000 $, soit 0,004 % par occasion — moins qu'un simple prêt. La réalité est 40 à 80 fois meilleure : 0,17 à 0,33 % par occasion. Le gas, qui était la dernière inconnue capable de ramener l'espérance sous zéro, a été mesuré à 0,0165 $ contre un seuil de 0,043 $ : il ne le fait pas. Net mesuré : +1,03 à +1,55 $/jour, soit 51 à 76 % par an sur 739 $.
La longue traîne : 6 paires mesurées sur 575 pools actifs. Le critère à retenir n'est pas le tier de frais — c'était un artefact du jeu de pools — mais les paires volatiles de capitalisation moyenne : WETH/ARB a produit 18 occasions en devant franchir 0,35 %, WETH/USDC seulement 7 avec des couples à 0,06 %. C'est le mécanisme d'échelle, puisque la capacité par paire est plafonnée et le capital ne l'est pas. Deux commandes, une nuit. C'est devenu la suite que je recommande, et non plus un essai de confirmation.
Écrire le contrat, le flash loan, le bot. Sur les rendements mesurés, l'espérance n'est plus nulle mais modestement positive : quelques dollars par jour sur quelques centaines immobilisés. Le Yul et TSTORE restent inutiles — ils n'achètent pas de latence. Un flash loan aussi : à 300 $ de notionnel, l'inventaire suffit.
Co-location près du séquenceur, Timeboost, ou une autre classe de stratégie que l'arbitrage spot à deux DEX. Chacune rouvre le sujet — et chacune casse une décision qu'on avait prise : « RPC public depuis chez moi, pas de co-location ».
Mon avis révisé : la suite 1, puis la 2. Élargir à la longue traîne, parce que c'est le seul mécanisme d'échelle disponible quand la capacité par paire est plafonnée ; puis écrire le contrat, dont l'espérance semble positive sur les rendements mesurés. La suite 3 n'est plus nécessaire : la co-location servirait à gagner les 37 courses, alors que les 2 occasions atteignables se prennent depuis un RPC ordinaire.
Les deux mesures qui restaient sont faites. Le gas vaut 0,0165 $ contre un seuil de bascule à 0,043 $. Et la profondeur V3 est insuffisante à la taille calculée : le profit garanti à taille plafonnée est de +0,05 à +0,54 $/jour net, soit 20 à 198 $/an — trois à vingt fois moins que ce que j'avais annoncé. Plus rien n'est en attente de mesure : la décision est entière.
Porte de rentabilité, montant optimal, profit au wei près, réserves virtuelles V3. 9 tests, 0 échec, zéro dépendance. La formule a été redérivée et vérifiée en rationnels exacts : écart 0,000000 % avec l'optimum continu. Trois défauts trouvés au passage, dont une racine entière fausse de 5 % pour un nombre de bits impair, qui faisait retomber le montant optimal à zéro même sur un écart de prix de 2 %.
L'endpoint public arb1.arbitrum.io/rpc sert l'historique et accepte des plages d'un million de blocs. Aucun RPC n'étant joignable depuis le conteneur de développement (403, politique d'organisation), tout s'exécute sur le VPS. Aucune clé Alchemy n'a été nécessaire : le collecteur lit les événements et non les réserves bloc par bloc, soit une quarantaine d'appels par jour de données au lieu de deux millions d'eth_call.
Aucune adresse n'a été écrite de mémoire — ni jeton, ni factory, ni pool. Un script part de l'activité et non des noms : 40 001 blocs lus, 38 517 événements, 575 pools actifs, puis les jetons, frais et factory lus sur les contrats. Chercher « le pool WETH/USDC » par symbole aurait aussi trouvé les imitations, nombreuses et vides ; le volume, lui, ne se contrefait pas à bas coût.
Effet de bord utile : la factory Uniswap V3 se déduit par mesure — celle qui porte 91,3 % du volume de sa famille — au lieu d'être supposée. Onze paires portent au moins deux pools, six ont été retenues. WETH/ARB est plus active que WETH/USDC (5 702 contre 3 163 événements), ce que je n'aurais pas deviné.
Les pools à frais dynamiques (Algebra/Camelot V3) sont écartés avec un motif explicite plutôt que supposés à 0,30 %. C'est 22 % de l'activité WETH/USDC en moins, donc un comptage d'occasions sous-estimé — un biais pessimiste, acceptable pour un test qui cherche à falsifier.
eth_getLogs sur Sync (V2) et Swap (V3), filtré sur les adresses déclarées. Reconstruction des réserves par bloc, porte de rentabilité exacte appliquée aux deux sens de chaque couple, puis quatre sorties : persistance, fréquence, distribution de taille, et concentration des captures.
Une occasion est identifiée par son couple de pools et son sens, jamais par son montant : le montant optimal bouge à chaque bloc pendant qu'elle reste ouverte, et la traiter comme neuve à chaque variation rendrait la persistance mesurée systématiquement nulle — l'erreur aurait rendu un chiffre plausible qui tranchait dans le mauvais sens. Une occasion encore ouverte en fin de fenêtre est bornée par la fenêtre, jamais comptée comme infinie. Une capture n'est retenue que si une transaction a touché les deux pools.
Une seule commande sur le VPS, dans mev-arb/ :
docker run --rm -v "$PWD:/w" -w /w node:22-alpine \ node --experimental-strip-types src/outils/collecter.ts --jours 1
Fait. Une journée mesurée, 345 600 blocs, plus de 97 000 événements lus, 39 occasions au-dessus de 0,20 $. Les deux occasions atteignables ont été relues une par une dans phase0.json — c'est ce qui a permis de corriger le plafond de 9,68 à 1,68 $/jour. Résultats en tête de page.
La persistance décide. Depuis un RPC grand public, le RTT vers le séquenceur est de 30 à 150 ms contre moins de 5 ms pour un bot co-localisé, et le séquenceur ordonne en FCFS : la course du bloc courant n'est pas gagnable dans cette configuration.
Le palier est à deux blocs, et non un. Voyant le bloc B, notre transaction atterrit au plus tôt dans B+1 : une occasion fermée pendant B+1 est une course en FCFS contre quelqu'un qui l'a vue avant nous, pas une prise. Il faut qu'elle soit encore ouverte à la fin de B+1 pour qu'elle soit vraiment là quand on arrive. J'avais d'abord retenu « un bloc » — décalage d'un bloc, sur la métrique dont dépend toute la décision, et qui aurait fait conclure « viable » sur des courses perdues.
La mesure est une borne supérieure et rien de plus : elle observe un monde où nous ne participons pas. Si Phase 0 montre que tout est capturé dans le bloc par cinq adresses toujours les mêmes, les Phases 1 et 2 sont un exercice d'apprentissage — et il vaut mieux le savoir avant d'écrire du Yul.
La mesure est tombée : 5,1 %. Les trois suites possibles sont détaillées en haut de page. C'est la seule chose qui reste ouverte, et elle vous revient.
Réserves en unités humaines.
Même ordre de jetons que le pool 1 — un ordre inversé produit un montant plausible et faux.
L'arbitrage n'existe que si l'écart de prix dépasse 1/(γ₁γ₂) − 1. Ce seuil n'est pas une constante : il vaut 0,603 % pour deux pools à 30 pb, mais 0,100 % à 5 pb et 0,020 % à 1 pb. Changez un tier et regardez les repères bouger — c'est précisément ce qu'un filtre écrit en dur à « rejeter tout décalage de réserve inférieur à 0,5 % » ne peut pas suivre.
Le montant est affiché en entier parce que c'est celui-là qui part sur la chaîne. Le profit reproduit la troncature de getAmountOut à chaque jambe : c'est ce qui permet d'annoncer un résultat au wei près, et donc à un require(profit ≥ minProfit) de ne pas échouer en production après une simulation optimiste.
Le flash loan prélève un pourcentage du notionnel emprunté, alors que le profit d'un arbitrage atomique se mesure en quelques points de base de ce même notionnel. Il ne prend donc pas une commission : il prend une part de la marge — et sur les tiers fins, il prend tout.
| Financement | Notionnel | Brut | Frais | Gas | Net | Part du brut |
|---|
La ligne surlignée est le couple choisi ci-dessus. Un filtre fixe à « 0,5 % de décalage de réserve » correspond à environ 1 % d'écart de prix : à peu près juste pour deux pools à 30 pb, et dix à cinquante fois trop strict pour les tiers fins — ceux qui portent le volume.
| Pool 1 | Pool 2 | Seuil d'écart de prix | ΔR équivalent | Écart courant | Porte |
|---|
Le même calcul mené en nombre à virgule flottante 64 bits — ce qu'obtient un bot écrit sans BigInt. Au-delà de 2⁵³ la précision est perdue, et le produit de quatre réserves dépasse toujours ce seuil. Selon les réserves, l'écart va de l'imperceptible au changement de signe : un montant négatif se lit « aucune occasion », et le bot passe à côté sans jamais savoir pourquoi.
| Méthode | Montant optimal | Écart relatif | Conclusion du bot |
|---|