Analise smart-contract
07.08.2024

Comprendre les erreurs courantes des contrats intelligents ERC-20 dans Solidity

Les contrats intelligents dans l'écosystème Ethereum contiennent souvent une logique complexe pour assurer la sécurité et le bon fonctionnement des applications décentralisées. Cependant, des erreurs peuvent survenir pour diverses raisons, entraînant des transactions échouées et des vulnérabilités potentielles. Cet article de blog explorera certaines erreurs courantes rencontrées dans les transactions de jetons ERC-20, leurs causes et les solutions.

1. Pancake: K

L'erreur se produit si l'invariant « le produit des réserves doit rester constant » est violé. Cette condition garantit qu'après l'exécution de l'échange, le nouveau produit des réserves (y compris les commissions) ne sera pas inférieur à l'ancien produit. Une violation de cette condition peut se produire si :

  1. Les jetons introduits dans la piscine (amount0In ou amount1In) ne correspondent pas aux valeurs attendues selon la formule de l'invariant. Il y a eu une erreur dans le calcul des soldes après l'échange.
  2. Quelqu'un a tenté de manipuler la piscine, ce qui a entraîné un changement des soldes de jetons, contournant le processus d'échange standard.
  3. Si cette condition n'est pas remplie, l'erreur « Pancake: K » est déclenchée, signalant une violation de l'invariant mathématique.

Une violation de l'invariant signifie que l'une des conditions fondamentales qui assurent le bon fonctionnement du système n'est plus remplie. Dans le contexte des échanges décentralisés tels que PancakeSwap, l'invariant est généralement associé à une équation mathématique qui doit rester vraie pour maintenir l'équilibre entre les réserves de jetons dans la piscine de liquidité.


require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(10000**2), 'Pancake: K');

Solution :
Vérifiez que la piscine dispose de suffisamment de liquidités pour effectuer l'échange et que les valeurs ne dépassent pas les réserves (_reserve0, _reserve1). Surveillez en continu les réserves de la piscine et prenez des mesures pour les reconstituer si nécessaire.

2. TransferHelper: TRANSFER_FROM_FAILED

Cette erreur signifie qu'un transfert de jetons a échoué en utilisant la méthode safeTransferFrom, qui est un modèle courant pour transférer des jetons ERC-20 de manière sécurisée.


function safeTransferFrom(address token, address from, address to, uint value) 
  internal {
    // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
    (bool success, bytes memory data) = 
token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
    require(success && (data.length == 0 || abi.decode(data, (bool))), 
'TransferHelper: TRANSFER_FROM_FAILED');
  }

Cause :

  1. L'adresse d'origine n'a pas accordé ou n'a pas accordé suffisamment de permissions pour que l'adresse msg.sender puisse transférer des jetons (allowance, approve).
  2. L'adresse d'origine ne dispose pas de suffisamment de jetons pour effectuer le transfert. Cela peut également être dû au fait que les administrateurs des jetons ont la capacité de modifier le solde des jetons et, au moment du retrait, l'utilisateur ayant effectué l'investissement peut ne pas avoir suffisamment de solde pour réaliser le retrait.
  3. Le contrat de jetons peut ne pas implémenter correctement la fonction transferFrom ou ne pas l'implémenter du tout.
  4. Le contrat de jetons peut contenir des vérifications ou des restrictions supplémentaires dans la fonction transferFrom, ce qui peut entraîner l'annulation de la transaction.
  5. Si le gaz disponible est insuffisant pour exécuter la transaction, celle-ci peut échouer.

Solution :

  1. Assurez-vous que l'adresse d'origine a accordé suffisamment d'allocation pour msg.sender. Cela peut être fait en appelant la fonction d'allocation du contrat de jetons.
  2. Assurez-vous que l'adresse d'origine dispose de suffisamment de jetons pour effectuer le transfert.
  3. Vérifiez que l'adresse du contrat de jetons est correcte et que le contrat est conforme à ERC-20.
  4. Vérifiez l'implémentation de la fonction transferFrom dans le contrat de jetons. Assurez-vous qu'elle est correctement implémentée et qu'il n'y a pas de contraintes supplémentaires pouvant provoquer un échec.
  5. Essayez d'augmenter la limite de gaz lors de l'appel de la transaction pour vous assurer que le problème n'est pas un manque de gaz.

3. INSUFFICIENT_LIQUIDITY

Cette erreur se produit lorsqu'on tente de retirer plus de liquidité que ce qui est disponible dans les réserves d'une piscine de liquidité.


(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
        require(amount0Out < _reserve0 && amount1Out < _reserve1, 'Pancake:
INSUFFICIENT_LIQUIDITY');

Cause :

  1. La piscine de liquidité ne contient pas une quantité suffisante de l'un ou des deux jetons pour satisfaire l'échange demandé.
  2. La quantité demandée de jetons pour le retrait (amount0Out ou amount1Out) dépasse le nombre disponible de jetons dans la piscine.
  3. Décalage entre les réserves dans le contrat et l'état réel de la piscine. Les réserves stockées dans le contrat peuvent ne pas correspondre à l'état réel du solde des jetons dans la piscine en raison d'erreurs ou de manipulations.

Solution :

  1. Vérifiez les réserves de la piscine et assurez-vous qu'elles sont suffisantes pour satisfaire l'échange demandé. Cela peut être fait en utilisant la fonction getReserves.
  2. Assurez-vous que les paramètres amount0Out et amount1Out sont corrects et ne dépassent pas le nombre disponible de jetons dans la piscine.
  3. Assurez-vous que les réserves dans le contrat correspondent au solde réel des jetons. Pour ce faire, vous pouvez ajouter une vérification et une mise à jour des réserves avant de réaliser l'échange.

INSUFFICIENT_LIQUIDITY

4. APPROVE_FAILED

L'erreur `APPROVE_FAILED` se produit lors de l'exécution de la fonction `safeApprove`. Cette fonction est conçue pour définir le montant de jetons qu'un propriétaire autorise un dépensier à utiliser en son nom.


function safeApprove(address token, address to, uint value) internal {
  (bool success, bytes memory data) = 
token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
  require(success && (data.length == 0 || abi.decode(data, (bool))),
'TransferHelper: APPROVE_FAILED');
}

Cause :

  1. Le contrat de jetons pourrait ne pas avoir la fonction `approve` ou celle-ci pourrait être mal implémentée.
  2. Il pourrait y avoir un problème avec l'état du contrat de jetons, tel qu'un solde ou une allocation insuffisante.
  3. La fonction `approve` pourrait être annulée pour des raisons de sécurité mises en œuvre dans le contrat de jetons.

Solution :

  1. Assurez-vous que le contrat de jetons est conforme à la norme ERC-20 et inclut une fonction `approve` correctement implémentée.
  2. Vérifiez l'état du contrat de jetons et assurez-vous que le compte dispose de suffisamment de jetons pour approbation.
  3. Vérifiez que l'adresse et le montant passés à la fonction `safeApprove` sont corrects.
  4. Déboguez davantage en vérifiant le message d'erreur spécifique ou la raison de l'annulation dans le contrat de jetons.

5. Échec avec l'erreur 'ds-math-sub-underflow'

L'erreur `ds-math-sub-underflow` est lancée lorsqu'une opération de soustraction entraîne un sous-dépassement, c'est-à-dire lorsque le résultat de la soustraction est inférieur à zéro.


    function sub(uint x, uint y) internal pure returns (uint z) {
      require((z = x - y) <= x, 'ds-math-sub-underflow');
    } 

Cause :
Cette erreur se produit parce que l'opération de soustraction `x - y` donne un nombre négatif, ce qui n'est pas autorisé pour les entiers non signés en Solidity.

Solution :

  1. Assurez-vous que la valeur de `y` est toujours inférieure ou égale à `x` avant de procéder à la soustraction.
  2. Implémentez des vérifications dans votre code pour gérer les cas où `y` pourrait être supérieur à `x` et prenez des mesures appropriées, telles que l'annulation de la transaction ou l'ajustement de la logique.

6. Échec avec l'erreur 'ERC20: transfer amount exceeds allowance'

L'erreur `ERC20: transfer amount exceeds allowance` se produit lorsqu'une tentative est faite pour transférer des jetons au nom d'un autre utilisateur, mais le montant transféré dépasse l'allocation que le propriétaire des jetons a définie pour le dépensier.

Cause :
Cette erreur est générée par la fonction `transferFrom` du contrat de jetons ERC-20 lorsque le montant à transférer est supérieur à la limite autorisée définie par le propriétaire des jetons.

Solution :

  1. Assurez-vous que le propriétaire des jetons a défini une allocation adéquate pour le dépensier en utilisant la fonction `approve`.
  2. Vérifiez l'allocation actuelle avant de tenter l'opération `transferFrom`.
  3. Si nécessaire, demandez au propriétaire des jetons d'augmenter l'allocation en appelant la fonction `approve` avec une valeur plus élevée.

7. TRANSFER_FAILED

Cette erreur se produit lorsque le transfert de jetons d'une adresse à une autre échoue. La fonction `_safeTransfer` assure que l'opération de transfert réussit et que les données retournées, le cas échéant, se décryptent en `true`.


    `function _safeTransfer(address token, address to, uint value) private { (bool 
    success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to,
    value)); require(success && (data.length == 0 || abi.decode(data, (bool))),
    'Pancake: TRANSFER_FAILED'); }`

Cause :

  1. La fonction `token.call` ne s'exécute pas avec succès (c'est-à-dire que `success` est `false`).
  2. L'appel retourne des données qui ne se décryptent pas en `true`, ce qui indique un échec dans la fonction `transfer` du contrat de jetons.

Solution :

  1. Assurez-vous de la conformité ERC-20 : Vérifiez que le contrat de jetons respecte la norme ERC-20, ce qui inclut l'implémentation correcte de la fonction `transfer`.
  2. Paramètres corrects : Assurez-vous que l'adresse `to` est valide et que la `value` est dans les limites autorisées, en tenant compte du solde de l'expéditeur et de la logique du contrat.
  3. Conditions supplémentaires : Vérifiez si le contrat de jetons nécessite des conditions supplémentaires telles que l'approbation préalable du montant à dépenser (fonctions `approve` et `allowance`).

INSUFFICIENT_LIQUIDITY

8. INSUFFICIENT_OUTPUT_AMOUNT

Cette erreur se produit dans un contexte d'échange décentralisé lorsque le montant de sortie d'un échange de jetons est inférieur au montant minimum spécifié par l'utilisateur. Il s'agit d'une mesure de sécurité pour garantir que les utilisateurs ne reçoivent pas moins de jetons que prévu en raison du glissement ou des variations de prix pendant la transaction.


    `require(amounts[amounts.length - 1] >= amountOutMin,
    'PancakeRouter: INSUFFICIENT_OUTPUT_AMOUNT');`

Cause :

  1. Volatilité du marché affectant les prix des jetons entre le moment où la transaction est initiée et lorsqu'elle est exécutée.
  2. Paramètres de glissement élevés, permettant des écarts importants dans les résultats attendus.
  3. Liquidité insuffisante dans la piscine de négociation, entraînant des impacts de prix plus importants.

Solution :

  1. Assurez-vous de la conformité ERC-20 : Vérifiez que le contrat de jetons respecte la norme ERC-20, ce qui inclut l'implémentation correcte de la fonction `transfer`.
  2. Paramètres corrects : Assurez-vous que l'adresse `to` est valide et que la `value` est dans les limites autorisées, en tenant compte du solde de l'expéditeur et de la logique du contrat.
  3. Conditions supplémentaires : Vérifiez si le contrat de jetons nécessite des conditions supplémentaires telles que l'approbation préalable du montant à dépenser (fonctions `approve` et `allowance`).

9. INSUFFICIENT_INPUT_AMOUNT

Cette erreur se produit lorsque ni les montants d'entrée pour un échange de jetons ne sont supérieurs à zéro. Cela garantit qu'au moins l'un des montants d'entrée (`amount0In` ou `amount1In`) est positif pour procéder à l'échange.


    uint amount0In = ...; // Input amount of token0
    uint amount1In = ...; // Input amount of token1
    require(amount0In > 0 || amount1In > 0, 'Pancake: INSUFFICIENT_INPUT_AMOUNT');

Cause :

  1. Paramètres d'entrée incorrects pour la fonction d'échange.
  2. Fonds insuffisants dans le compte de l'utilisateur.
  3. Erreurs dans la logique de calcul des montants d'entrée.

Solution :

  1. Valider les montants d'entrée : Assurez-vous que les montants d'entrée sont correctement définis avant d'appeler la fonction d'échange. Cela implique une validation appropriée des entrées utilisateur et la définition des paramètres.
  2. Vérifiez les soldes des utilisateurs : Vérifiez que l'utilisateur dispose d'un solde suffisant des jetons destinés à l'échange. Cela peut être fait en appelant la fonction `balanceOf` des contrats de jetons respectifs.

10. Échec

Cause :

  1. Une transaction ne peut pas être exécutée car elle manque de gaz pour compléter toutes les opérations.
  2. Une logique ou des conditions incorrectes au sein d'un contrat intelligent peuvent entraîner un échec de l'exécution (par exemple, un appel à require ou assert échoue).
  3. Tentative d'exécution d'une transaction de jetons ou de crypto-monnaie lorsque le solde du compte est insuffisant.
  4. Dans le cas de l'utilisation de jetons conformes aux normes ERC-20 et ERC-721, la transaction peut échouer en raison de permissions insuffisantes.
  5. Un appel à un contrat intelligent peut être annulé en raison d'un échec à satisfaire les conditions à l'intérieur (par exemple, en utilisant la fonction revert).

Solution :

  1. Augmentez la limite de gaz lors de l'envoi de la transaction.
  2. Vérifiez les conditions et la logique à l'intérieur du contrat intelligent.
  3. Assurez-vous que le compte dispose de fonds suffisants pour compléter la transaction.
  4. Appelez la fonction approve avec une valeur suffisante avant d'appeler transferFrom.
  5. Vérifiez les conditions qui entraînent le retour de la transaction.
  6. Vérifiez votre solde pour les commissions

Conclusion

Comprendre et résoudre ces erreurs courantes dans les contrats intelligents ERC-20 nécessite une bonne maîtrise de la programmation en Solidity, de la norme ERC-20 et du fonctionnement interne des échanges décentralisés. En examinant attentivement les causes et en mettant en œuvre les solutions suggérées, les développeurs peuvent créer des contrats intelligents plus robustes et fiables, garantissant une expérience fluide pour les utilisateurs dans l'écosystème décentralisé.

All posts

Connect to a wallet

Metamask