Analise smart-contract
07.08.2024

Înțelegerea erorilor comune ale contractelor inteligente ERC-20 în Solidity

Contractele inteligente din ecosistemul Ethereum conțin adesea logică complexă pentru a asigura securitatea și funcționalitatea aplicațiilor descentralizate. Cu toate acestea, pot apărea erori din diverse motive, ceea ce poate duce la tranzacții eșuate și vulnerabilități potențiale. Acest articol va explora câteva erori comune întâlnite în tranzacțiile cu tokenuri ERC-20, cauzele acestora și soluțiile.

1. Pancake: K

Eroarea apare dacă invariantul „produsul rezervelor trebuie să fie constant” este încălcat. Această condiție garantează că, după ce schimbul este efectuat, noul produs al rezervelor (inclusiv comisioanele) nu va fi mai mic decât produsul vechi. O încălcare a acestei condiții poate apărea dacă:

  1. Tokenurile introduse în pool (amount0In sau amount1In) nu se potrivesc cu valorile așteptate conform formulei invariantului. A apărut o eroare în calculul soldurilor după schimb.
  2. Cineva a încercat să manipuleze pool-ul, ceea ce a dus la schimbarea soldurilor tokenurilor, ocolind procesul standard de schimb.
  3. Dacă această condiție nu este îndeplinită, se ridică eroarea „Pancake: K”, semnalând o încălcare a invariantului matematic.

O încălcare a invariantului înseamnă că una dintre condițiile fundamentale care asigură funcționarea corectă a sistemului nu mai este îndeplinită. În contextul schimburilor descentralizate, cum ar fi PancakeSwap, invariantul este de obicei asociat cu o ecuație matematică care trebuie să rămână adevărată pentru a menține echilibrul între rezervele de tokenuri în pool-ul de lichiditate.


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

Soluție:
Verificați că pool-ul are suficientă lichiditate pentru a efectua schimbul și că valorile nu depășesc rezervele (_reserve0, _reserve1). Monitorizați continuu rezervele pool-ului și luați măsuri pentru a le reumple, dacă este necesar.

2. TransferHelper: TRANSFER_FROM_FAILED

Această eroare semnifică o transferare eșuată a tokenurilor folosind metoda `safeTransferFrom`, care este un model comun pentru transferarea sigură a tokenurilor ERC-20.


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');
  }

Cauză:

  1. Adresa de la care se transferă nu a acordat sau nu a acordat suficient permisiuni pentru adresa `msg.sender` pentru a transfera tokenuri (allowance, approve).
  2. Adresa de la care se transferă nu are suficiente tokenuri pentru a efectua transferul. Acest lucru poate fi cauzat și de faptul că administratorii tokenurilor au capacitatea de a modifica soldul tokenului și, la momentul retragerii, utilizatorul care a efectuat investiția poate să nu aibă suficiente fonduri pentru a efectua retragerea.
  3. Contractul tokenului poate să nu implementeze funcția `transferFrom` sau să o implementeze incorect.
  4. Contractul tokenului poate conține verificări sau restricții suplimentare în funcția `transferFrom`, ceea ce poate cauza anularea tranzacției.
  5. Dacă nu există suficient gaz pentru a executa tranzacția, aceasta poate eșua.

Soluție:

  1. Asigurați-vă că adresa de la care se transferă a acordat suficient allowance pentru `msg.sender`. Acest lucru se poate face apelând funcția allowance a contractului tokenului.
  2. Asigurați-vă că adresa de la care se transferă are suficiente tokenuri pentru a efectua transferul.
  3. Verificați că adresa contractului tokenului este corectă și că contractul este conform cu ERC-20.
  4. Verificați implementarea funcției `transferFrom` în contractul tokenului. Asigurați-vă că este implementată corect și nu are constrângeri suplimentare care ar putea cauza o eșuare.
  5. Încercați să creșteți limita de gaz atunci când apelați tranzacția pentru a vă asigura că problema nu este lipsa de gaz.

3. INSUFFICIENT_LIQUIDITY

Această eroare apare atunci când încercați să retrageți mai multă lichiditate decât este disponibilă în rezervele unui pool de lichiditate.


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

Cauză:

  1. Pool-ul de lichiditate nu conține cantitatea suficientă de unul sau ambele tokenuri pentru a îndeplini schimbul solicitat.
  2. Cantitatea solicitată de tokenuri pentru retragere (amount0Out sau amount1Out) depășește numărul disponibil de tokenuri în pool.
  3. Neconcordanță între rezervele din contract și starea actuală a pool-ului. Rezervele stocate în contract pot să nu se potrivească cu starea actuală a soldurilor tokenurilor din pool din cauza erorilor sau manipulărilor.

Soluție:

  1. Verificați rezervele pool-ului și asigurați-vă că sunt suficiente pentru a îndeplini schimbul solicitat. Acest lucru se poate face folosind funcția `getReserves`.
  2. Asigurați-vă că parametrii `amount0Out` și `amount1Out` sunt corecți și nu depășesc numărul disponibil de tokenuri în pool.
  3. Asigurați-vă că rezervele din contract se potrivesc cu soldul actual al tokenurilor. Pentru aceasta, puteți adăuga verificări și actualizări ale rezervelor înainte de a efectua schimbul.

INSUFFICIENT_LIQUIDITY

4. APPROVE_FAILED

Eroarea `APPROVE_FAILED` apare în timpul executării funcției `safeApprove`. Această funcție este destinată să seteze cantitatea de tokenuri pe care un proprietar o permite unui cheltuitor să o folosească în numele său.


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');
}

Cauză:

  1. Contractul tokenului s-ar putea să nu aibă funcția `approve` sau să fie implementată incorect.
  2. Ar putea exista o problemă cu starea contractului tokenului, cum ar fi sold insuficient sau allowance.
  3. Funcția ` approve` ar putea reveni din motive de securitate implementate în contractul tokenului.

Soluție:

  1. Asigurați-vă că contractul tokenului respectă standardul ERC-20 și include o funcție `approve` implementată corect.
  2. Verificați starea contractului tokenului și asigurați-vă că contul are suficiente tokenuri pentru a aproba.
  3. Verificați că adresa și cantitatea transmise funcției `safeApprove` sunt corecte.
  4. Depanați mai departe verificând mesajul de eroare specific sau motivul revenirii în contractul tokenului.

5. Eșec cu eroarea 'ds-math-sub-underflow'

Eroarea `ds-math-sub-underflow` este generată atunci când o operație de scădere produce un subfluaj, adică atunci când rezultatul scăderii este mai mic decât zero.


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

Cauză:
Această eroare apare deoarece operația de scădere `x - y` produce un număr negativ, ceea ce nu este permis pentru numerele întregi nesemnate în Solidity.

Soluție:

  1. Asigurați-vă că valoarea lui `y` este întotdeauna mai mică sau egală cu `x` înainte de a efectua scăderea.
  2. Implementați verificări în codul dvs. pentru a gestiona cazurile în care `y` ar putea fi mai mare decât `x` și luați măsuri adecvate, cum ar fi revenirea tranzacției sau ajustarea logicii.

6. Eșec cu eroarea 'ERC20: transfer amount exceeds allowance'

Eroarea `ERC20: transfer amount exceeds allowance` apare atunci când se face o încercare de a transfera tokenuri în numele unui alt utilizator, dar suma care se transferă depășește allowance-ul pe care deținătorul tokenului l-a stabilit pentru cheltuitor.

Cauză:
Această eroare este generată de funcția `transferFrom` a contractului tokenului ERC-20 atunci când suma care trebuie transferată este mai mare decât limita permisă stabilită de deținătorul tokenului.

Soluție:

  1. Asigurați-vă că deținătorul tokenului a stabilit un allowance adecvat pentru cheltuitor folosind funcția `approve`.
  2. Verificați allowance-ul actual înainte de a încerca operația `transferFrom`.
  3. Dacă este necesar, cereți deținătorului tokenului să crească allowance-ul apelând funcția `approve` cu o valoare mai mare.

7. TRANSFER_FAILED

Această eroare apare atunci când transferul de tokenuri de la o adresă la alta eșuează. Funcția `_safeTransfer` se asigură că operația de transfer reușește și că datele returnate, dacă există, se decodează în `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'); }`

Cauză:

  1. Funcția `token.call` nu se execută cu succes (adică, `success` este `false`).
  2. Apelul returnează date care nu se decodează în `true`, indicând o eșuare în funcția `transfer` a contractului tokenului.

Soluție:

  1. Asigurați-vă că respectați standardul ERC-20: Verificați că contractul tokenului respectă standardul ERC-20, care include implementarea corectă a funcției `transfer`.
  2. Parametrii corecți: Asigurați-vă că adresa `to` este validă și că `value` se încadrează în limitele permise, având în vedere soldul expeditorului și logica contractului.
  3. Condiții suplimentare: Verificați dacă contractul tokenului necesită condiții suplimentare, cum ar fi pre-aprobarea sumei de cheltuit (`approve` și funcțiile `allowance`).

INSUFFICIENT_LIQUIDITY

8. INSUFFICIENT_OUTPUT_AMOUNT

Această eroare apare într-un context de schimb descentralizat atunci când suma de ieșire a unui schimb de tokenuri este mai mică decât suma minimă specificată de utilizator. Aceasta este o măsură de protecție pentru a se asigura că utilizatorii nu primesc mai puține tokenuri decât se așteptau din cauza alunecării de preț sau a schimbărilor de preț în timpul tranzacției.


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

Cauză:

  1. Volatilitatea pieței afectează prețurile tokenurilor între momentul în care tranzacția este inițiată și când este executată.
  2. Setările ridicate ale alunecării, care permit deviații semnificative în ieșirile așteptate.
  3. Lichiditate insuficientă în pool-ul de tranzacționare, cauzând impacturi mai mari ale prețului.

Soluție:

  1. Asigurați-vă că respectați standardul ERC-20: Verificați că contractul tokenului respectă standardul ERC-20, care include implementarea corectă a funcției `transfer`.
  2. Parametrii corecți: Asigurați-vă că adresa `to` este validă și că `value` se încadrează în limitele permise, având în vedere soldul expeditorului și logica contractului.
  3. Condiții suplimentare: Verificați dacă contractul tokenului necesită condiții suplimentare, cum ar fi pre-aprobarea sumei de cheltuit (`approve` și funcțiile `allowance`).

9. INSUFFICIENT_INPUT_AMOUNT

Această eroare apare atunci când niciuna dintre sumele de intrare pentru un schimb de tokenuri nu este mai mare decât zero. Se asigură că cel puțin una dintre sumele de intrare (`amount0In` sau `amount1In`) este pozitivă pentru a continua schimbul.


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

Cauză:

  1. Parametrii de intrare incorecți pentru funcția de schimb.
  2. Fonduri insuficiente în contul utilizatorului.
  3. Greșeli în logica care calculează sumele de intrare.

Soluție:

  1. Validați sumele de intrare: Asigurați-vă că sumele de intrare sunt setate corect înainte de a invoca funcția de schimb. Acest lucru implică validarea corectă a intrărilor utilizatorului și setarea parametrilor.
  2. Verificați soldurile utilizatorului: Verificați că utilizatorul are solduri suficiente ale tokenurilor destinate schimbului. Acest lucru se poate face apelând funcția `balanceOf` a contractelor de tokenuri respective.

10. Eșec

Cauză:

  1. O tranzacție nu poate fi executată deoarece nu are suficient gaz pentru a finaliza toate operațiile.
  2. Logica sau condițiile incorecte într-un contract inteligent pot cauza eșuarea execuției (de exemplu, un apel la `require` sau `assert` care eșuează).
  3. Încercarea de a executa o tranzacție de tokenuri sau criptomonede când soldul contului este insuficient.
  4. În cazul lucrului cu tokenuri standard ERC-20 și ERC-721, tranzacția poate eșua din cauza permisiunilor insuficiente.
  5. Un apel la un contract inteligent poate fi răsturnat din cauza eșuării îndeplinirii condițiilor sale (de exemplu, folosind funcția `revert`).

Soluție:

  1. Creșteți limita de gaz atunci când trimiteți tranzacția.
  2. Verificați condițiile și logica din cadrul contractului inteligent.
  3. Asigurați-vă că contul are suficiente fonduri pentru a finaliza tranzacția.
  4. Apelați funcția `approve` cu o valoare suficientă înainte de a apela `transferFrom`.
  5. Verificați condițiile care cauzează răsturnarea tranzacției.
  6. Verificați soldul pentru comisioane.

Concluzie

Înțelegerea și rezolvarea acestor erori comune în contractele inteligente ERC-20 necesită o înțelegere solidă a programării în Solidity, a standardului ERC-20 și a funcționării interne a schimburilor descentralizate. Prin revizuirea atentă a cauzelor și implementarea soluțiilor sugerate, dezvoltatorii pot crea contracte inteligente mai robuste și mai fiabile, asigurând o experiență fără întreruperi pentru utilizatori în ecosistemul descentralizat.

All posts

Connect to a wallet

Metamask