Skip to content

Latest commit

 

History

History
635 lines (544 loc) · 23.3 KB

SwapsImplSovrynSwap.md

File metadata and controls

635 lines (544 loc) · 23.3 KB

Swaps Implementation Sovryn contract.

  • (SwapsImplSovrynSwap.sol)

View Source: contracts/swaps/connectors/SwapsImplSovrynSwap.sol

↗ Extends: State, ISwapsImpl

SwapsImplSovrynSwap contract

This contract code comes from bZx. bZx is a protocol for tokenized margin trading and lending https://bzx.network similar to the dYdX protocol.

  • This contract contains the implementation of swap process and rate calculations for Sovryn network.

Functions


getContractHexName

function getContractHexName(string source) public pure
returns(result bytes32)

Arguments

Name Type Description
source string The name of the contract.
Source Code
function getContractHexName(string memory source) public pure returns (bytes32 result) {
        assembly {
            result := mload(add(source, 32))
        }
    }

getSovrynSwapNetworkContract

function getSovrynSwapNetworkContract(address sovrynSwapRegistryAddress) public view
returns(contract ISovrynSwapNetwork)

Arguments

Name Type Description
sovrynSwapRegistryAddress address The address of the registry.
Source Code
function getSovrynSwapNetworkContract(address sovrynSwapRegistryAddress)
        public
        view
        returns (ISovrynSwapNetwork)
    {
        /// State variable sovrynSwapContractRegistryAddress is part of
        /// State.sol and set in ProtocolSettings.sol and this function
        /// needs to work without delegate call as well -> therefore pass it.
        IContractRegistry contractRegistry = IContractRegistry(sovrynSwapRegistryAddress);
        return
            ISovrynSwapNetwork(
                contractRegistry.addressOf(getContractHexName("SovrynSwapNetwork"))
            );
    }

internalSwap

⤾ overrides ISwapsImpl.internalSwap

function internalSwap(address sourceTokenAddress, address destTokenAddress, address receiverAddress, address returnToSenderAddress, uint256 minSourceTokenAmount, uint256 maxSourceTokenAmount, uint256 requiredDestTokenAmount) public payable
returns(destTokenAmountReceived uint256, sourceTokenAmountUsed uint256)

Arguments

Name Type Description
sourceTokenAddress address The address of the source tokens.
destTokenAddress address The address of the destination tokens.
receiverAddress address The address who will received the swap token results
returnToSenderAddress address The address to return unspent tokens to (when called by the protocol, it's always the protocol contract).
minSourceTokenAmount uint256 The minimum amount of source tokens to swapped (only considered if requiredDestTokens == 0).
maxSourceTokenAmount uint256 The maximum amount of source tokens to swapped.
requiredDestTokenAmount uint256 The required amount of destination tokens.
Source Code
function internalSwap(
        address sourceTokenAddress,
        address destTokenAddress,
        address receiverAddress,
        address returnToSenderAddress,
        uint256 minSourceTokenAmount,
        uint256 maxSourceTokenAmount,
        uint256 requiredDestTokenAmount
    ) public payable returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed) {
        require(sourceTokenAddress != destTokenAddress, "source == dest");
        require(
            supportedTokens[sourceTokenAddress] && supportedTokens[destTokenAddress],
            "invalid tokens"
        );

        ISovrynSwapNetwork sovrynSwapNetwork =
            getSovrynSwapNetworkContract(sovrynSwapContractRegistryAddress);

        IERC20[] memory path =
            getConversionPath(sourceTokenAddress, destTokenAddress, sovrynSwapNetwork);

        uint256 minReturn = 1;
        sourceTokenAmountUsed = minSourceTokenAmount;

        /// If the required amount of destination tokens is passed, we need to
        /// calculate the estimated amount of source tokens regardless of the
        /// minimum source token amount (name is misleading).
        if (requiredDestTokenAmount > 0) {
            sourceTokenAmountUsed = estimateSourceTokenAmount(
                sourceTokenAddress,
                destTokenAddress,
                requiredDestTokenAmount,
                maxSourceTokenAmount
            );
            /// sovrynSwapNetwork.rateByPath does not return a rate, but instead the amount of destination tokens returned.
            require(
                sovrynSwapNetwork.rateByPath(path, sourceTokenAmountUsed) >=
                    requiredDestTokenAmount,
                "insufficient source tokens provided."
            );
            minReturn = requiredDestTokenAmount;
        }

        require(sourceTokenAmountUsed > 0, "cannot swap 0 tokens");

        allowTransfer(sourceTokenAmountUsed, sourceTokenAddress, address(sovrynSwapNetwork));

        /// @dev Note: the kyber connector uses .call() to interact with kyber
        /// to avoid bubbling up. here we allow bubbling up.
        destTokenAmountReceived = sovrynSwapNetwork.convertByPath(
            path,
            sourceTokenAmountUsed,
            minReturn,
            receiverAddress,
            address(0),
            0
        );

        /// If the sender is not the protocol (calling with delegatecall),
        /// return the remainder to the specified address.
        /// @dev Note: for the case that the swap is used without the
        /// protocol. Not sure if it should, though. needs to be discussed.
        if (returnToSenderAddress != address(this)) {
            if (sourceTokenAmountUsed < maxSourceTokenAmount) {
                /// Send unused source token back.
                IERC20(sourceTokenAddress).safeTransfer(
                    returnToSenderAddress,
                    maxSourceTokenAmount - sourceTokenAmountUsed
                );
            }
        }
    }

allowTransfer

Check whether the existing allowance suffices to transfer the needed amount of tokens. If not, allows the transfer of an arbitrary amount of tokens. *

function allowTransfer(uint256 tokenAmount, address tokenAddress, address sovrynSwapNetwork) internal nonpayable

Arguments

Name Type Description
tokenAmount uint256 The amount to transfer.
tokenAddress address The address of the token to transfer.
sovrynSwapNetwork address The address of the sovrynSwap network contract.
Source Code
function allowTransfer(
        uint256 tokenAmount,
        address tokenAddress,
        address sovrynSwapNetwork
    ) internal {
        uint256 tempAllowance = IERC20(tokenAddress).allowance(address(this), sovrynSwapNetwork);
        if (tempAllowance < tokenAmount) {
            IERC20(tokenAddress).safeApprove(sovrynSwapNetwork, uint256(-1));
        }
    }

estimateSourceTokenAmount

Calculate the number of source tokens to provide in order to obtain the required destination amount. *

function estimateSourceTokenAmount(address sourceTokenAddress, address destTokenAddress, uint256 requiredDestTokenAmount, uint256 maxSourceTokenAmount) internal view
returns(estimatedSourceAmount uint256)

Arguments

Name Type Description
sourceTokenAddress address The address of the source token address.
destTokenAddress address The address of the destination token address.
requiredDestTokenAmount uint256 The number of destination tokens needed.
maxSourceTokenAmount uint256 The maximum number of source tokens to spend. *

Returns

The estimated amount of source tokens needed. Minimum: minSourceTokenAmount, maximum: maxSourceTokenAmount

Source Code
function estimateSourceTokenAmount(
        address sourceTokenAddress,
        address destTokenAddress,
        uint256 requiredDestTokenAmount,
        uint256 maxSourceTokenAmount
    ) internal view returns (uint256 estimatedSourceAmount) {
        uint256 sourceToDestPrecision =
            IPriceFeeds(priceFeeds).queryPrecision(sourceTokenAddress, destTokenAddress);
        if (sourceToDestPrecision == 0) return maxSourceTokenAmount;

        /// Compute the expected rate for the maxSourceTokenAmount -> if spending less, we can't get a worse rate.
        uint256 expectedRate =
            internalExpectedRate(
                sourceTokenAddress,
                destTokenAddress,
                maxSourceTokenAmount,
                sovrynSwapContractRegistryAddress
            );

        /// Compute the source tokens needed to get the required amount with the worst case rate.
        estimatedSourceAmount = requiredDestTokenAmount.mul(sourceToDestPrecision).div(
            expectedRate
        );

        /// If the actual rate is exactly the same as the worst case rate, we get rounding issues. So, add a small buffer.
        /// buffer = min(estimatedSourceAmount/1000 , sourceBuffer) with sourceBuffer = 10000
        uint256 buffer = estimatedSourceAmount.div(1000);
        if (buffer > sourceBuffer) buffer = sourceBuffer;
        estimatedSourceAmount = estimatedSourceAmount.add(buffer);

        /// Never spend more than the maximum.
        if (estimatedSourceAmount == 0 || estimatedSourceAmount > maxSourceTokenAmount)
            return maxSourceTokenAmount;
    }

internalExpectedRate

⤾ overrides ISwapsImpl.internalExpectedRate

Get the expected rate for 1 source token when exchanging the given amount of source tokens. *

function internalExpectedRate(address sourceTokenAddress, address destTokenAddress, uint256 sourceTokenAmount, address sovrynSwapContractRegistryAddress) public view
returns(uint256)

Arguments

Name Type Description
sourceTokenAddress address The address of the source token contract.
destTokenAddress address The address of the destination token contract.
sourceTokenAmount uint256 The amount of source tokens to get the rate for.
sovrynSwapContractRegistryAddress address
Source Code
function internalExpectedRate(
        address sourceTokenAddress,
        address destTokenAddress,
        uint256 sourceTokenAmount,
        address sovrynSwapContractRegistryAddress
    ) public view returns (uint256) {
        ISovrynSwapNetwork sovrynSwapNetwork =
            getSovrynSwapNetworkContract(sovrynSwapContractRegistryAddress);

        IERC20[] memory path =
            getConversionPath(sourceTokenAddress, destTokenAddress, sovrynSwapNetwork);

        /// Is returning the total amount of destination tokens.
        uint256 expectedReturn = sovrynSwapNetwork.rateByPath(path, sourceTokenAmount);

        /// Return the rate for 1 token with 18 decimals.
        return expectedReturn.mul(10**18).div(sourceTokenAmount);
    }

internalExpectedReturn

⤾ overrides ISwapsImpl.internalExpectedReturn

Get the expected return amount when exchanging the given amount of source tokens. *

function internalExpectedReturn(address sourceTokenAddress, address destTokenAddress, uint256 sourceTokenAmount, address sovrynSwapContractRegistry, IERC20[] defaultPath) public view
returns(expectedReturn uint256)

Arguments

Name Type Description
sourceTokenAddress address The address of the source token contract.
destTokenAddress address The address of the destination token contract.
sourceTokenAmount uint256 The amount of source tokens to get the return for.
sovrynSwapContractRegistry address The sovryn swap contract reigstry address.
defaultPath IERC20[] The default path for specific pairs.
Source Code
function internalExpectedReturn(
        address sourceTokenAddress,
        address destTokenAddress,
        uint256 sourceTokenAmount,
        address sovrynSwapContractRegistry,
        IERC20[] memory defaultPath
    ) public view returns (uint256 expectedReturn) {
        ISovrynSwapNetwork sovrynSwapNetwork =
            getSovrynSwapNetworkContract(sovrynSwapContractRegistry);

        IERC20[] memory path =
            defaultPath.length >= 3
                ? defaultPath
                : sovrynSwapNetwork.conversionPath(
                    IERC20(sourceTokenAddress),
                    IERC20(destTokenAddress)
                );

        /// Is returning the total amount of destination tokens.
        expectedReturn = sovrynSwapNetwork.rateByPath(path, sourceTokenAmount);
    }

getConversionPath

function getConversionPath(address sourceTokenAddress, address destTokenAddress, ISovrynSwapNetwork sovrynSwapNetwork) private view
returns(path contract IERC20[])

Arguments

Name Type Description
sourceTokenAddress address
destTokenAddress address
sovrynSwapNetwork ISovrynSwapNetwork
Source Code
function getConversionPath(
        address sourceTokenAddress,
        address destTokenAddress,
        ISovrynSwapNetwork sovrynSwapNetwork
    ) private view returns (IERC20[] memory path) {
        IERC20[] memory _defaultPathConversion =
            defaultPathConversion[sourceTokenAddress][destTokenAddress];

        /// will use the defaultPath if it's set, otherwise query from the SovrynSwapNetwork.
        path = _defaultPathConversion.length >= 3
            ? _defaultPathConversion
            : sovrynSwapNetwork.conversionPath(
                IERC20(sourceTokenAddress),
                IERC20(destTokenAddress)
            );
    }

Contracts