0

I'm trying to understand the "getAmountsOut" function from Uniswap / Pancakeswap, line by line:

// performs chained getAmountOut calculations on any number of pairs
function getAmountsOut(address factory, uint amountIn, address[] memory path) internal view returns (uint[] memory amounts) {
    require(path.length >= 2, 'PancakeLibrary: INVALID_PATH');
    amounts = new uint[](path.length);
    amounts[0] = amountIn;
    for (uint i; i < path.length - 1; i++) {
        (uint reserveIn, uint reserveOut) = getReserves(factory, path[i], path[i + 1]);
        amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
    }
}

source: https://bscscan.com/address/0x10ed43c718714eb63d5aa57b78b54704e256024e#code (line 336)

My superficial understanding is that, given an amount you are putting in, you want to find out how much you would get, given the current reserves and assuming no one else changes the reserves before you do a swap.

My Question:

But how does the code above accomplish that?

My Analysis of each line:

The require statement is just a check that makes sure we are looking at at least 2 tokens. Fair enough so far.

The amounts = new uint[](path.length) is new to me, what is this called and what is it doing?

the amounts[0] = amountsIn; is just replacing the value of amounts 0.

Also, I notice the function returns a uint[] memory amounts, however I don't see a return statement at the end of the function. Is a return statement not required? And is the variable "amounts" being declared in the signature of the function? I know that's how it works with parameters, but never assumed that the returns (...) portion also can be used in this way. Is my understanding here correct?

The rest of the lines are also a bit confusing for me. If anyone else can explain them, would mean a lot.

1 Answer 1

2

Your understanding of the return signature is correct. In Solidity you can define the return variable in the signature. You can read more here.

TL;DR

getAmountsOut calculates dynamically the swap for n tokens. The for-loop is used to calculate each swap from token i to i + 1 where the input amount starts with amountIn and the result of each swap is the next input amount for the next swap. amountsOut is an array with the values transformed in the process starting with amountIn

Explaination

The for-loop is a dynamic way of computing a swap with n Tokens, where n >= 2.
Let's inspect the getAmountOut method:

    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internalpure returns (uint amountOut) {
        require(amountIn > 0, 'PancakeLibrary: INSUFFICIENT_INPUT_AMOUNT');
        require(reserveIn > 0 && reserveOut > 0, 'PancakeLibrary: INSUFFICIENT_LIQUIDITY');
        uint amountInWithFee = amountIn.mul(9975);
        uint numerator = amountInWithFee.mul(reserveOut);
        uint denominator = reserveIn.mul(10000).add(amountInWithFee);
        amountOut = numerator / denominator;
    }

It does some fancy computation to return the output value. Note that it works with reserves which is a clever generic implementation.
Let us say, we've tokens A, B and C and we would like to swap from A to C over B. Let's assume that there exist pools for each of them and they have enough reserves.
When swapping from A to B, we use calculate the swap with amountIn. The resulting output is used for the swap from B to C. Each output is stored in amountsOut and returned to the caller.

Simple Example

amountIn = 1000
A -> B, 1000 => 991
B -> C, 991 => 1992
amountsOut = [1000, 991, 1992]

In a nutshell

When working with two tokens the for-loop will run once. I used an example with three tokens to illustrate the dynamic nature of the getAmountsOut function.

1
  • 1
    Thank you Willbrand for the explanation. I also looked a bit deeper into getAmountOut, and noticed the clever way the 0.25% fee pancakeswap (and presumably uniswap, not sure if they have the same fee) has is being implemented. Instead of multiplying amountIn by 0.9975 so as to subtract the 0.25% fee, they multiply by 9975 which won't cause any decimal issues our rounding. Though it does mean the numerator requires to be divided by 10k at some point. However since the denominator finds itself in the same situation, the last line, amountOut = numerator / denominator; does the trick May 22 at 14:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.