Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 1 #1

Open
Edward-Lo opened this issue Sep 26, 2020 · 2 comments
Open

Issue 1 #1

Edward-Lo opened this issue Sep 26, 2020 · 2 comments

Comments

@Edward-Lo
Copy link
Collaborator

Description

FlamingoSwapRouter contract provides interfaces for users to add/remove liquidity and swap tokens. The function AddLiquidity can add tokens to the pools and mint liquidity tokens for the user.

public static BigInteger[] AddLiquidity(byte[] sender, byte[] tokenA, byte[] tokenB, BigInteger amountADesired, BigInteger amountBDesired, BigInteger amountAMin, BigInteger amountBMin, BigInteger deadLine)
{
    Assert(Runtime.CheckWitness(sender), "Forbidden");

    Assert((BigInteger) Runtime.Time <= deadLine, "Exceeded the deadline");


    var reserves = GetReserves(tokenA, tokenB);
    var reserveA = reserves[0];
    var reserveB = reserves[1];
    BigInteger amountA = 0;
    BigInteger amountB = 0;
    if (reserveA == 0 && reserveB == 0)
    {
        amountA = amountADesired;
        amountB = amountBDesired;
    }
    else
    {
        var estimatedB = Quote(amountADesired, reserveA, reserveB);
        if (estimatedB <= amountBDesired)
        {
            Assert(estimatedB >= amountBMin, "Insufficient B Amount");
            amountA = amountADesired;
            amountB = estimatedB;
        }
        else
        {
            var estimatedA = Quote(amountBDesired, reserveB, reserveA);
            Assert(estimatedA >= amountAMin, "Insufficient A Amount"); 
            amountA = estimatedA;
            amountB = amountBDesired;
        }
    }

    var pairContract = GetExchangePairWithAssert(tokenA, tokenB);

    SafeTransfer(tokenA, sender, pairContract, amountA);
    SafeTransfer(tokenB, sender, pairContract, amountB);

    var liquidity = pairContract.DynamicMint(sender);//+0.03gas
    //var liquidity = ((Func<string, object[], BigInteger>)pairContract.ToDelegate())("mint", new object[] { sender });
    return new BigInteger[] { amountA.ToBigInt(), amountB.ToBigInt(), liquidity };
}

The parameters amountADesired and amountBDesired are the maximum tokens that can be transferred in. The final input token amounts amountA and amountB should be less than amountADesired and amountBDesired.

However, in the case estimatedB is bigger than amountBDesired. The contract misses a check that estimatedA should also be less than amountADesired.

Recommendation

public static BigInteger[] AddLiquidity(byte[] sender, byte[] tokenA, byte[] tokenB, BigInteger amountADesired, BigInteger amountBDesired, BigInteger amountAMin, BigInteger amountBMin, BigInteger deadLine)
{
    Assert(Runtime.CheckWitness(sender), "Forbidden");

    Assert((BigInteger) Runtime.Time <= deadLine, "Exceeded the deadline");


    var reserves = GetReserves(tokenA, tokenB);
    var reserveA = reserves[0];
    var reserveB = reserves[1];
    BigInteger amountA = 0;
    BigInteger amountB = 0;
    if (reserveA == 0 && reserveB == 0)
    {
        amountA = amountADesired;
        amountB = amountBDesired;
    }
    else
    {
        var estimatedB = Quote(amountADesired, reserveA, reserveB);
        if (estimatedB <= amountBDesired)
        {
            Assert(estimatedB >= amountBMin, "Insufficient B Amount");
            amountA = amountADesired;
            amountB = estimatedB;
        }
        else
        {
            var estimatedA = Quote(amountBDesired, reserveB, reserveA);
            Assert(estimatedA <= amountADesired, "Insufficient A Amount"); 
            Assert(estimatedA >= amountAMin, "Insufficient A Amount"); 
            amountA = estimatedA;
            amountB = amountBDesired;
        }
    }

    var pairContract = GetExchangePairWithAssert(tokenA, tokenB);

    SafeTransfer(tokenA, sender, pairContract, amountA);
    SafeTransfer(tokenB, sender, pairContract, amountB);

    var liquidity = pairContract.DynamicMint(sender);//+0.03gas
    //var liquidity = ((Func<string, object[], BigInteger>)pairContract.ToDelegate())("mint", new object[] { sender });
    return new BigInteger[] { amountA.ToBigInt(), amountB.ToBigInt(), liquidity };
}
@Ashuaidehao
Copy link
Contributor

It's just a mathematic problem. Check the formula in "Quote" function, if use amountADesired calculating estimatedB bigger than amountBDesired, in this case, use amountBDesired calculating estimatedA will never greater than amountADesired.
I'll add Assert(estimatedA <= amountADesired, "Insufficient A Amount"); for more comprehensible, thanks for your suggestion.

@heyJonBray
Copy link

No math should ever be done when it comes to assets without assertions first. I do not know where to start with the issues as I am independently auditing this repo. The handful of issues in place indicate a complete lack of understanding in the basic framework on which this is built.

I came here to look over the contracts, but there are so many issues I am not quite sure even where to begin. Both contracts are ripe with problems. The math library from C# can be bypassed due to a perceived overload from the NEO libraries that can be called from the browser; timestamps are not implemented, leaving the base libraries open for time-based attacks; and the wrappers do not correctly correlate to prices, leaving the potential for whales to swallow up one half of a pool due to extreme arbitrage opportunities.

This project, with a team of a dozen developers, should not be released for at least 6 more months... I feel sorry that it is being rushed as it could have been a viable DEX; but is in no way prepared to be a "full stack platform".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants