0

I'm working on a smart contract test using ethers.js, and I'm facing an issue with rounding errors when comparing ether balances of accounts after executing transactions. Specifically, the problem arises when I try to test the balance changes for an auction system.

I'm using ethers.js v6.

In my test, I am calculating the fee amount and then distributing the ether to the seller and refunding the buyer. However, after the transaction, I encounter a difference of 1 wei when comparing the balance changes for the smart contract, the buyer, and the seller. The issue seems to be related to rounding discrepancies in the calculations, and I need to account for this while testing.

Logic:

function buy(uint index) external payable activeAuction(index) {
    Auction storage auction = auctions[index];
    require(block.timestamp < auction.endAt, "Auction has ended");
    uint price = getPriceFor(index);
    require(msg.value >= price, "Not enough money");
    auction.stopped = true;
    auction.finalPrice = price;

    //refund
    uint refund = msg.value - price;
    if(refund > 0) {
    payable(msg.sender).transfer(refund);
    }

    auction.seller.transfer(price - ((price * FEE)/100));

    emit AuctionEnded(index, price, msg.sender);
}

Test method:

it("Should allow to buy and refund", async () => {
        const FEE = 10; 
        
        const oneEtherInWei = ethers.parseEther("1");  // 1 ETH = 1e18 wei
        const twoEtherInWei = ethers.parseEther("2");  // 2 ETH = 2e18 wei

        await expect(smartContract.connect(seller).createAuction(
            oneEtherInWei,  
            FEE,
            "test item",
            60
        ));
        
        const tx = await smartContract.connect(buyer).buy(0, { value: twoEtherInWei });
        await tx.wait();
        
        const feeAmount = (parseFloat(oneEtherInWei.toString()) * FEE) / 100; 
        console.log("feeAmount :", feeAmount);
        
        const moneyForSeller = parseFloat(oneEtherInWei.toString()) - feeAmount;
        console.log("moneyForSeller :", moneyForSeller);
        
        const moneyLeftOnContract = parseFloat(oneEtherInWei.toString()) - moneyForSeller;
        console.log("moneyLeftOnContract :", moneyLeftOnContract);


        await expect(tx).to.changeEtherBalance(smartContract.target, moneyLeftOnContract.toString()) // Перевірка зміни балансу смарт-контракту
        await expect(tx).to.changeEtherBalance(buyer.address, (parseFloat(twoEtherInWei) * -1).toString()) // Перевірка зміни балансу покупця
        await expect(tx).to.changeEtherBalance(seller.address, moneyForSeller.toString()) // Перевірка зміни балансу продавця

        
        
        const price = await smartContract.auctions(0).finalPrice;
        expect(tx).to.emit(smartContract, "AuctionEnded")
            .withArgs(0, price, buyer);
    });

Test result screenshot

1
  • dont use floats, they're big numbers, ideally you should be using the uniswap SDK for handling balance modifications, as doing operations even on big numbers can have issues when doing things like mulDiv. so ideally use uniswap-sdk, alternatively you can use big numbers, JSBI, or BigNumber.from(). Commented Jan 14 at 19:09

1 Answer 1

0

I would try to do the calculations in BigNumber/BigInt instead of converting all to floats. So you don't lose precision converting from strings.

// using ethers v5

//const feeAmount = (parseFloat(oneEtherInWei.toString()) * FEE) / 100;
const feeAmount = oneEtherInWei.mul(FEE).div(100);

//const moneyForSeller = parseFloat(oneEtherInWei.toString()) - feeAmount;
const moneyForSeller = oneEtherInWei.sub(feeAmount);

//const moneyLeftOnContract = parseFloat(oneEtherInWei.toString()) - moneyForSeller;
const moneyLeftOnContract = oneEtherInWei.sub(moneyForSeller);

Using ethers V6:

const feeAmount = oneEtherInWei * BigInt(FEE) / BigInt(100);
const moneyForSeller = oneEtherInWei - feeAmount;
const moneyLeftOnContract = oneEtherInWei - moneyForSeller;
Sign up to request clarification or add additional context in comments.

3 Comments

I'm using ethers.parseEther("1"); and this returns BigInt, BigNumber was in prev versions.
Sorry, I edited adding ethers v6 version. Let me know if now it works
I also tried with BigInt but it's missing 1 wei as well.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.