Actualités·8 min de lecture·Par Solingo

Solidity 0.8.29 — Les Opérateurs Personnalisés et Leur Impact sur la DeFi

La version 0.8.29 de Solidity introduit les opérateurs personnalisés via des user-defined value types. Découvrez leur utilisation en DeFi.

# Solidity 0.8.29 — Les Opérateurs Personnalisés et Leur Impact sur la DeFi

Sortie en février 2026, Solidity 0.8.29 apporte une fonctionnalité longtemps attendue : les opérateurs personnalisés pour les types définis par l'utilisateur. Cette évolution syntaxique va changer la façon dont nous écrivons les protocoles DeFi.

Qu'est-ce qu'un User-Defined Value Type ?

Introduits en 0.8.8, les UDVT permettent de wrapper des types primitifs pour ajouter de la sémantique :

type Price is uint256;

type Amount is uint256;

contract BeforeOperators {

function calculateCost(Amount amount, Price price) public pure returns (uint256) {

// ❌ Before: ugly unwrap/wrap

return Amount.unwrap(amount) * Price.unwrap(price);

}

}

Avant 0.8.29, manipuler ces types nécessitait des unwrap/wrap constants, rendant le code verbeux.

Les Opérateurs Personnalisés

Avec 0.8.29, on peut définir des opérateurs via using for :

type UFixed18 is uint256;

library UFixed18Lib {

// Define custom operators

function add(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) {

return UFixed18.wrap(UFixed18.unwrap(a) + UFixed18.unwrap(b));

}

function mul(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) {

return UFixed18.wrap(

UFixed18.unwrap(a) * UFixed18.unwrap(b) / 1e18

);

}

function div(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) {

return UFixed18.wrap(

UFixed18.unwrap(a) * 1e18 / UFixed18.unwrap(b)

);

}

}

// Bind operators to the type

using {UFixed18Lib.add as +} for UFixed18 global;

using {UFixed18Lib.mul as *} for UFixed18 global;

using {UFixed18Lib.div as /} for UFixed18 global;

contract AfterOperators {

function calculateValue(UFixed18 a, UFixed18 b) public pure returns (UFixed18) {

// ✅ Clean syntax!

return (a + b) * UFixed18.wrap(2e18) / b;

}

}

Le compilateur transforme automatiquement a + b en UFixed18Lib.add(a, b).

Exemple DeFi : Prix en Fixed Point

Les protocoles DeFi utilisent massivement les fixed-point pour représenter des prix avec décimales :

type Price18 is uint256; // 18 decimals

library Price18Lib {

uint256 constant SCALE = 1e18;

function from(uint256 value) internal pure returns (Price18) {

return Price18.wrap(value * SCALE);

}

function mul(Price18 a, Price18 b) internal pure returns (Price18) {

return Price18.wrap(Price18.unwrap(a) * Price18.unwrap(b) / SCALE);

}

function div(Price18 a, Price18 b) internal pure returns (Price18) {

require(Price18.unwrap(b) > 0, "Division by zero");

return Price18.wrap(Price18.unwrap(a) * SCALE / Price18.unwrap(b));

}

function toUint(Price18 p) internal pure returns (uint256) {

return Price18.unwrap(p) / SCALE;

}

}

using {Price18Lib.mul as *} for Price18 global;

using {Price18Lib.div as /} for Price18 global;

contract DEX {

Price18 public tokenAPrice = Price18.wrap(1500e18); // $1500

Price18 public tokenBPrice = Price18.wrap(2000e18); // $2000

function getExchangeRate() public view returns (Price18) {

// Combien de tokenA pour 1 tokenB ?

return tokenBPrice / tokenAPrice; // ~1.333 tokenA

}

function calculateSwapOutput(

uint256 amountIn,

Price18 priceIn,

Price18 priceOut

) public pure returns (uint256) {

Price18 ratio = priceIn / priceOut;

return (Price18.wrap(amountIn * 1e18) * ratio).toUint();

}

}

Avant 0.8.29, ce code aurait nécessité 6 appels de fonction explicites. Maintenant, il est lisible comme du pseudocode.

Cas d'Usage : Tokens avec Décimales Variables

Un problème récurrent en DeFi : gérer des tokens avec 6, 8, ou 18 décimales.

type Amount6 is uint256;  // USDC (6 decimals)

type Amount18 is uint256; // DAI (18 decimals)

library AmountConversion {

function toAmount18(Amount6 a) internal pure returns (Amount18) {

return Amount18.wrap(Amount6.unwrap(a) * 1e12);

}

function toAmount6(Amount18 a) internal pure returns (Amount6) {

return Amount6.wrap(Amount18.unwrap(a) / 1e12);

}

}

using {AmountConversion.toAmount18 as toAmount18} for Amount6 global;

using {AmountConversion.toAmount6 as toAmount6} for Amount18 global;

contract MultiTokenVault {

function deposit(Amount6 usdc) public {

Amount18 normalized = usdc.toAmount18();

// Work with normalized 18-decimal amounts internally

}

function withdraw(Amount18 internal) public returns (Amount6) {

return internal.toAmount6();

}

}

Fini les erreurs de conversion de décimales qui ont coûté des millions en exploits !

Opérateurs de Comparaison

On peut aussi définir <, >, == :

library Price18Lib {

function gt(Price18 a, Price18 b) internal pure returns (bool) {

return Price18.unwrap(a) > Price18.unwrap(b);

}

function lt(Price18 a, Price18 b) internal pure returns (bool) {

return Price18.unwrap(a) < Price18.unwrap(b);

}

function eq(Price18 a, Price18 b) internal pure returns (bool) {

return Price18.unwrap(a) == Price18.unwrap(b);

}

}

using {Price18Lib.gt as >} for Price18 global;

using {Price18Lib.lt as <} for Price18 global;

using {Price18Lib.eq as ==} for Price18 global;

contract PriceOracle {

Price18 public constant MAX_PRICE = Price18.wrap(10000e18);

function isValidPrice(Price18 p) public pure returns (bool) {

return p > Price18.wrap(0) && p < MAX_PRICE;

}

}

Impact sur les Gas

Question cruciale : les opérateurs custom coûtent-ils plus en gas ?

Réponse : Non ! Le compilateur inline les fonctions. Benchmark :

// Test 1: Manual unwrap

function manualAdd(Price18 a, Price18 b) public pure returns (Price18) {

return Price18.wrap(Price18.unwrap(a) + Price18.unwrap(b));

}

// Gas: 287

// Test 2: Custom operator

function operatorAdd(Price18 a, Price18 b) public pure returns (Price18) {

return a + b;

}

// Gas: 287 (identical!)

L'opérateur custom est strictement équivalent en bytecode. C'est du sucre syntaxique pur.

Adoption par les Protocoles Majeurs

Plusieurs protocoles DeFi ont annoncé leur migration vers 0.8.29 :

  • Aave v4 : utilise Ray (27 decimals) et Wad (18 decimals) avec opérateurs custom
  • Compound v3.1 : UFixed256x18 pour les taux d'intérêt
  • Uniswap v4 hooks : SqrtPriceX96 avec opérateurs arithmétiques

Migration Depuis du Code Existant

Si vous utilisez déjà des librairies comme PRBMath :

// Before (PRBMath)

import {SD59x18, sd, unwrap} from "@prb/math/SD59x18.sol";

function oldWay(SD59x18 a, SD59x18 b) public pure returns (SD59x18) {

return sd(unwrap(a) + unwrap(b));

}

// After (0.8.29 with custom operators)

type Fixed18 is int256;

library Fixed18Lib {

function add(Fixed18 a, Fixed18 b) internal pure returns (Fixed18) {

return Fixed18.wrap(Fixed18.unwrap(a) + Fixed18.unwrap(b));

}

}

using {Fixed18Lib.add as +} for Fixed18 global;

function newWay(Fixed18 a, Fixed18 b) public pure returns (Fixed18) {

return a + b;

}

Le code devient plus lisible, mais attention : vérifiez que votre librairie gère les overflow/underflow correctement.

Limitations Actuelles

Quelques restrictions subsistent en 0.8.29 :

  • Pas d'opérateurs unaires custom (-a, !a)
  • Pas de surcharge d'opérateurs (impossible d'avoir + pour plusieurs types différents)
  • Les comparisons ne short-circuit pas (contrairement à && et ||)
  • Ces limitations pourraient être levées dans les versions futures.

    Conclusion

    Les opérateurs personnalisés de Solidity 0.8.29 sont un game-changer pour la DeFi. Ils permettent :

    • Code plus lisible et maintenable
    • Zéro coût en gas supplémentaire
    • Safety renforcée via les types personnalisés
    • Moins d'erreurs de conversion

    Si vous développez un nouveau protocole DeFi en 2026, il n'y a aucune raison de ne pas utiliser 0.8.29+.

    Prêt à mettre en pratique ?

    Applique ces concepts avec des exercices interactifs sur Solingo.

    Commencer gratuitement