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
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.
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.
No response
No response
No response
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
.
To demonstrate this vulnerability, we can simulate a scenario on Hardhat where:
- A user's token balance changes unexpectedly after a
_defiSwap
, due to an external factor. - The incorrect calculation of
ss.oAmount
leads to erroneous swap values in the subsequent stablecoin swap. Steps: - Deploy the
AmirX
contract and configure the necessary roles. - Set up a user wallet with tokens and allow it to initiate a swap.
- Simulate an external factor (such as another contract or fee deduction) that modifies the user’s token balance during the
_defiSwap
. - 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:
- User Initiates a Swap with
defiToStablecoinSwap
: The user calls thedefiToStablecoinSwap
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
. - 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. - Contract Calculates
ss.oAmount
Based on the Inaccurate Balance: The contract calculatesss.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. - Impact on Stablecoin Amount:
The next step of the swap relies on
ss.oAmount
to decide the output stablecoin amount for the user. Sincess.oAmount
is inaccurately low, the user receives fewer stablecoins than expected, resulting in a financial loss relative to their intended swap amount.
- Instead of using balance differentials, use swap event outputs to determine the exact amount swapped.
- Verify the balance change using a threshold or tolerance to detect unexpected changes.