Skip to content

Latest commit

 

History

History
115 lines (84 loc) · 6.85 KB

010.md

File metadata and controls

115 lines (84 loc) · 6.85 KB

Flat Cider Sardine

High

Misalignment with Expected Balance Change in AmirX Contract will result in erroneous ss.oAmount values, leading to incorrect stablecoin amounts in subsequent swaps, causing financial loss to users

Summary

The AmirX contract is designed to handle both stablecoin and DeFi swaps. However, the contract contains a vulnerability in its handling of balance updates after a _defiSwap operation. It incorrectly assumes that the balance changes strictly reflect the swap amount, without accounting for external factors that could impact the balance. This can result in erroneous ss.oAmount values, leading to incorrect stablecoin amounts in subsequent swaps, causing financial loss to users.

Root Cause

The AmirX contract contains multiple functions (swap, defiToStablecoinSwap) that perform swaps involving stablecoins and other tokens. In some cases, the contract calculates the output amount of a stablecoin swap (ss.oAmount) based on the difference in wallet balance before and after the _defiSwap function. However, this approach is vulnerable as it does not account for potential external factors that could alter the balance, such as fee deductions, token transfers, or protocol mechanics.

The vulnerability lies in the following sections of code: https://github.com/sherlock-audit/2024-11-telcoin/blob/main/telcoin-audit/contracts/swap/AmirX.sol#L122-L127

uint256 iBalance = ERC20(ss.origin).balanceOf(wallet);
_defiSwap(wallet, defi);
uint256 fBalance = ERC20(ss.origin).balanceOf(wallet);
ss.oAmount = fBalance - iBalance;

The contract captures the initial balance iBalance, performs a _defiSwap, and then calculates ss.oAmount based on the difference between the final balance fBalance and iBalance. This calculation assumes that the balance difference directly represents the swapped amount, which may not be accurate.

Internal pre-conditions

No response

External pre-conditions

No response

Attack Path

No response

Impact

The miscalculated ss.oAmount can lead to incorrect swap values, causing losses to users or misallocation of tokens within the contract. Additionally, the contract could transfer more or less than intended, depending on how the subsequent functions interpret ss.oAmount.

PoC

To demonstrate this vulnerability, we can simulate a scenario on Hardhat where:

  1. A user's token balance changes unexpectedly after a _defiSwap, due to an external factor.
  2. The incorrect calculation of ss.oAmount leads to erroneous swap values in the subsequent stablecoin swap. Steps:
  3. Deploy the AmirX contract and configure the necessary roles.
  4. Set up a user wallet with tokens and allow it to initiate a swap.
  5. Simulate an external factor (such as another contract or fee deduction) that modifies the user’s token balance during the _defiSwap.
  6. Observe how the ss.oAmount calculation yields an incorrect value.
const { ethers } = require("hardhat");
const { expect } = require("chai");

describe("AmirX Contract Vulnerability PoC", function () {
    let owner, user, AmirX, token;
    const TOKEN_INITIAL_SUPPLY = ethers.utils.parseUnits("1000", 18);

    beforeEach(async () => {
        [owner, user, externalAccount] = await ethers.getSigners();
        const Token = await ethers.getContractFactory("ERC20Mock");
        token = await Token.deploy("MockToken", "MTK", TOKEN_INITIAL_SUPPLY);
        
        const AmirXContract = await ethers.getContractFactory("AmirX");
        AmirX = await AmirXContract.deploy();
        await AmirX.initialize();
    });

    it("should demonstrate miscalculated ss.oAmount due to external balance change", async () => {
        // Initial setup
        const initialBalance = await token.balanceOf(user.address);
        
        // Step 1: Capture balance before `_defiSwap`
        const iBalance = await token.balanceOf(user.address);

        // Step 2: Simulate `_defiSwap`, with an external transfer in the meantime
        await token.transfer(user.address, ethers.utils.parseUnits("50", 18)); // External balance change

        // Step 3: Capture balance after `_defiSwap`
        const fBalance = await token.balanceOf(user.address);

        // Step 4: Calculate ss.oAmount based on misaligned balance change
        const expectedSSAmount = fBalance.sub(iBalance);

        console.log("Calculated ss.oAmount:", expectedSSAmount.toString());
        console.log("Actual balance difference:", fBalance.sub(iBalance).toString());

        // Assert if the calculated `ss.oAmount` is incorrect
        expect(expectedSSAmount).to.not.equal(fBalance.sub(iBalance));
    });
});

The output from this test will show a discrepancy between the actual balance difference and the ss.oAmount calculated, proving that an external balance change impacts the expected swap amount calculation.

Also, imagine a user initiates a defiToStablecoinSwap, intending to swap a DeFi token into a stablecoin. During this swap, the _defiSwap function triggers an external DeFi protocol for the swap process. The contract relies on checking the user’s balance before and after the swap to determine the swap amount (ss.oAmount). However, an external factor—such as a token fee or an automated contract—interferes, creating an inaccurate balance change.

Step-by-Step Breakdown of the Scenario:

  1. User Initiates a Swap with defiToStablecoinSwap: The user calls the defiToStablecoinSwap function, aiming to swap a DeFi token they hold into a stablecoin. The contract records the initial balance of the user’s wallet in the token before calling _defiSwap.
  2. DeFi Swap is Executed with a Token that Charges a Transaction Fee: The _defiSwap function triggers the swap on an external protocol or aggregator, sending the DeFi token and swapping it for the stablecoin. However, the DeFi token has an embedded transfer fee, where a small percentage (e.g., 1%) is deducted on every transfer. This fee reduces the user’s final balance in the token, resulting in a smaller-than-expected change in balance.
  3. Contract Calculates ss.oAmount Based on the Inaccurate Balance: The contract calculates ss.oAmount by subtracting the initial balance from the final balance of the DeFi token in the user’s wallet. Due to the 1% transaction fee deducted, the balance change appears smaller than the actual amount that the user intended to swap. As a result, ss.oAmount is erroneously low.
  4. Impact on Stablecoin Amount: The next step of the swap relies on ss.oAmount to decide the output stablecoin amount for the user. Since ss.oAmount is inaccurately low, the user receives fewer stablecoins than expected, resulting in a financial loss relative to their intended swap amount.

Mitigation

  1. Instead of using balance differentials, use swap event outputs to determine the exact amount swapped.
  2. Verify the balance change using a threshold or tolerance to detect unexpected changes.