Analise smart-contract
07.08.2024

了解 Solidity 中常见的 ERC-20 智能合约错误

以太坊生态系统中的智能合约通常包含复杂的逻辑,以确保去中心化应用程序的安全性和功能。然而,由于各种原因,可能会出现错误,导致交易失败和潜在的漏洞。本文将探讨ERC-20代币交易中常见的一些错误,它们的原因和解决方案。

1. Pancake: K

当违反“储备产品必须保持不变”的不变量时,会发生此错误。此条件保证在交换执行后,新的储备产品(包括佣金)不会小于旧的储备产品。如果出现以下情况,这一条件可能会被违反:

  1. 输入池中的代币(amount0In或amount1In)与不变量公式预期的值不匹配。交换后的余额计算错误。
  2. 有人试图操纵池,导致代币余额发生变化,绕过标准交换过程。
  3. 如果未满足此条件,则会引发“Pancake: K”错误,表示违反数学不变量。

不变量的违反意味着确保系统正确操作的基本条件之一不再得到满足。在像PancakeSwap这样的去中心化交易所的背景下,不变量通常与一个数学方程相关,该方程必须保持为真,以保持流动性池中代币储备的平衡。


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

解决方案:
检查池中是否有足够的流动性来进行交换,并且这些值不超过储备(_reserve0,_reserve1)。持续监控池储备,并在必要时采取措施补充它们。

2. TransferHelper: TRANSFER_FROM_FAILED

此错误表示使用safeTransferFrom方法进行的代币转移失败,这是安全转移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');
  }

原因:

  1. from地址没有授予msg.sender地址转移代币的权限或没有授予足够的权限(allowance,approve)。
  2. from地址没有足够的代币来进行转移。这也可能是由于代币管理员有能力修改代币余额,在提款时投资的用户可能没有足够的余额来进行提款。
  3. 代币合约可能未实现transferFrom函数或实现不正确。
  4. 代币合约中的transferFrom函数可能包含额外的检查或限制,这可能导致交易被取消。
  5. 如果没有足够的gas来执行交易,交易可能会失败。

解决方案:

  1. 确保from地址为msg.sender授予了足够的允许。这可以通过调用代币合约的allowance函数来完成。
  2. 确保from地址有足够的代币进行转换。
  3. 验证代币合约地址是否正确,并且合约符合ERC-20标准。
  4. 检查代币合约中transferFrom函数的实现。确保其实现正确且没有任何可能导致失败的附加限制。
  5. 尝试在调用交易时增加gas限制,以确保问题不是gas不足。

3. INSUFFICIENT_LIQUIDITY

当尝试提取的流动性超过流动性池中的储备时,会发生此错误。


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

原因:

  1. 流动性池中没有足够的代币来满足请求的交换。
  2. 请求的代币提取量(amount0Out或amount1Out)超过池中可用的代币数量。
  3. 合约中的储备与池的实际状态不匹配。由于错误或操纵,合约中存储的储备可能与池中的代币实际余额不匹配。

解决方案:

  1. 检查池储备,确保其足以满足请求的交换。可以使用getReserves函数来完成。
  2. 确保amount0Out和amount1Out参数正确且不超过池中可用的代币数量。
  3. 确保合约中的储备与实际代币余额匹配。为此,可以在执行交换前添加检查和更新储备。

INSUFFICIENT_LIQUIDITY

4. APPROVE_FAILED

`APPROVE_FAILED`错误发生在执行`safeApprove`函数期间。此函数旨在设置所有者允许使用者代表其使用的代币数量。


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

原因:

  1. 代币合约可能没有`approve`函数或实现不正确。
  2. 代币合约的状态可能存在问题,例如余额或允许不足。
  3. 由于代币合约中的安全原因,`approve`函数可能会还原。

解决方案:

  1. 确保代币合约符合ERC-20标准,并且包含正确实现的`approve`函数。
  2. 检查代币合约的状态,确保账户有足够的代币进行批准。
  3. 验证传递给`safeApprove`函数的地址和数量是否正确。
  4. 通过检查代币合约中的特定错误消息或还原原因进一步调试。

5. Fail with error 'ds-math-sub-underflow'

当减法操作下溢,即减法结果小于零时,会引发`ds-math-sub-underflow`错误。


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

原因:
此错误是由于减法操作`x - y`导致负数,而Solidity中的无符号整数不允许负数。

解决方案:

  1. 确保在执行减法之前,`y`的值始终小于或等于`x`。
  2. 在代码中实现检查,以处理`y`可能大于`x`的情况,并采取适当的措施,如还原交易或调整逻辑。

6. 失败并出现错误 'ERC20: transfer amount exceeds allowance'

当尝试代表其他用户转移代币,但转移的金额超过代币所有者为使用者设定的允许值时,会发生`ERC20: transfer amount exceeds allowance`错误。

原因:
当转移的金额大于代币所有者设定的允许限额时,ERC-20代币合约的`transferFrom`函数会抛出此错误。

解决方案:

  1. 确保代币所有者使用`approve`函数为使用者设定了足够的允许值。
  2. 在尝试执行`transferFrom`操作之前检查当前的允许值。
  3. 如果需要,要求代币所有者通过调用`approve`函数并设置更高的值来增加允许值。

7. TRANSFER_FAILED

当从一个地址向另一个地址转移代币失败时,会发生此错误。`_safeTransfer`函数确保转移操作成功,并且返回的数据(如果有)解码为`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'); }`

原因:

  1. `token.call`函数没有成功执行(即`success`为`false`)。
  2. 调用返回的数据未解码为`true`,表明代币合约的`transfer`函数失败。

解决方案:

  1. 确保ERC-20合规性:验证代币合约是否符合ERC-20标准,包括正确实现`transfer`函数。
  2. 正确的参数:确保`to`地址有效且`value`在允许的范围内,考虑到发送者的余额和合约的逻辑。
  3. 额外的条件:检查代币合约是否需要额外的条件,如预先批准的使用金额(`approve`和`allowance`函数)。

INSUFFICIENT_LIQUIDITY

8. INSUFFICIENT_OUTPUT_AMOUNT

在去中心化交易所的上下文中,当代币交换的输出金额小于用户指定的最小金额时,会发生此错误。这是为了确保用户不会因交易期间的滑点或价格变化而收到少于预期的代币数量。


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

原因:

  1. 从交易发起到执行期间,市场波动影响代币价格。
  2. 高滑点设置,允许预期输出有显著偏差。
  3. 交易池中的流动性不足,导致较大的价格影响。

解决方案:

  1. 确保ERC-20合规性:验证代币合约是否符合ERC-20标准,包括正确实现`transfer`函数。
  2. 正确的参数:确保`to`地址有效且`value`在允许的范围内,考虑到发送者的余额和合约的逻辑。
  3. 额外的条件:检查代币合约是否需要额外的条件,如预先批准的使用金额(`approve`和`allowance`函数)。

9. INSUFFICIENT_INPUT_AMOUNT

当代币交换的输入金额都不大于零时,会发生此错误。它确保至少有一个输入金额(`amount0In`或`amount1In`)为正,以继续进行交换。


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

原因:

  1. 交换函数的输入参数不正确。
  2. 用户账户中的资金不足。
  3. 计算输入金额的逻辑错误。

解决方案:

  1. 验证输入金额:确保在调用交换函数之前正确设置输入金额。这涉及适当的用户输入验证和参数设置。
  2. 检查用户余额:验证用户是否有足够的代币进行交换。可以通过调用相应代币合约的`balanceOf`函数来完成。

10. Fail

原因:

  1. 交易无法执行,因为没有足够的gas来完成所有操作。
  2. 智能合约中的逻辑或条件不正确可能导致执行失败(例如,调用`require`或`assert`失败)。
  3. 尝试执行代币或加密货币交易时账户余额不足。
  4. 在处理ERC-20和ERC-721标准代币时,交易可能由于权限不足而失败。
  5. 调用智能合约可能由于未满足其中的条件(例如,使用`revert`函数)而回滚。

解决方案:

  1. 增加发送交易时的gas限制。
  2. 检查智能合约中的条件和逻辑。
  3. 确保账户有足够的资金完成交易。
  4. 在调用`transferFrom`之前,使用足够的值调用`approve`函数。
  5. 检查导致交易回滚的条件。
  6. 检查你的余额是否足以支付手续费。

结论

理解和解决这些常见的ERC-20智能合约错误需要对Solidity编程、ERC-20标准和去中心化交易所的内部工作有扎实的掌握。通过仔细审查原因并实施建议的解决方案,开发人员可以创建更健壮和可靠的智能合约,确保用户在去中心化生态系统中的无缝体验。

All posts

Connect to a wallet

Metamask