1beam / 1swap-contracts

0 stars 2 forks source link

Return value of calculateSwapFromBase of StableSwapRouter.sol is incorrect #1

Open honglewis opened 3 years ago

honglewis commented 3 years ago

To reproduct the bug, please do as following steps :

First, try to call calculateSwapFromBase with following parameters: pool = 0x008db1Cef0958e7f87A107b58F0dede796ce7962 (BUSD meta pool address) basePool = 0xb578a396e56388CbF398a12Dea9eb6B01b7c777f (USDC/USDT/DAI base pool address) tokenIndexFrom = 0 (USDC index) tokenIndexTo = 0 (BUSD index) dx = 1000000000 (1000 USDC)

Get return value as 998604422682132896156 . This means : due to the calculateSwapFromBase call response, 1000 USDC can be converted to about 998.6 BUSD

Second, build swapFromBase transaction with following parameters: pool = 0x008db1Cef0958e7f87A107b58F0dede796ce7962 (BUSD meta pool address) basePool = 0xb578a396e56388CbF398a12Dea9eb6B01b7c777f (USDC/USDT/DAI base pool address) tokenIndexFrom = 0 (USDC index) tokenIndexTo = 0 (BUSD index) dx = 1000000000 (1000 USDC)
minDy = 998604422682132896156 (value returned by calculateSwapFromBase) deadLine = 1633077775 (current time + 5 minutes)

It will always yield exception: {'code': -32603, 'message': 'VM Exception while processing transaction: revert > slippage', 'data': '08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a3e20736c69707061676500000000000000000000000000000000000000000000'}

It seems that the minDy value returned by calculateSwapFromBase is incorrect, which is bigger than the real amount which can be converted by swapFromBase.

Testing python code can be found below :

import eth_account
from web3 import Web3
import time

w3 = Web3(Web3.HTTPProvider('https://rpc.moonriver.moonbeam.network'))

private_key = 'MY PRIVATE KEY'
wallet_address = eth_account.Account.from_key(private_key = private_key).address

route_address = '0xf741ae23D540AfC30e967cEdc362B93dd1e7b91B'
route_abi = '[{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"addLiquidity","inputs":[{"type":"address","name":"pool","internalType":"contract IStableSwap"},{"type":"address","name":"basePool","internalType":"contract IStableSwap"},{"type":"uint256[]","name":"meta_amounts","internalType":"uint256[]"},{"type":"uint256[]","name":"base_amounts","internalType":"uint256[]"},{"type":"uint256","name":"minToMint","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"calculateConvert","inputs":[{"type":"address","name":"fromPool","internalType":"contract IStableSwap"},{"type":"address","name":"toPool","internalType":"contract IStableSwap"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"availableTokenAmount","internalType":"uint256"}],"name":"calculateRemoveBaseLiquidityOneToken","inputs":[{"type":"address","name":"pool","internalType":"contract IStableSwap"},{"type":"address","name":"basePool","internalType":"contract IStableSwap"},{"type":"uint256","name":"_token_amount","internalType":"uint256"},{"type":"uint8","name":"iBase","internalType":"uint8"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"meta_amounts","internalType":"uint256[]"},{"type":"uint256[]","name":"base_amounts","internalType":"uint256[]"}],"name":"calculateRemoveLiquidity","inputs":[{"type":"address","name":"pool","internalType":"contract IStableSwap"},{"type":"address","name":"basePool","internalType":"contract IStableSwap"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"calculateSwapFromBase","inputs":[{"type":"address","name":"pool","internalType":"contract IStableSwap"},{"type":"address","name":"basePool","internalType":"contract IStableSwap"},{"type":"uint8","name":"tokenIndexFrom","internalType":"uint8"},{"type":"uint8","name":"tokenIndexTo","internalType":"uint8"},{"type":"uint256","name":"dx","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"calculateSwapToBase","inputs":[{"type":"address","name":"pool","internalType":"contract IStableSwap"},{"type":"address","name":"basePool","internalType":"contract IStableSwap"},{"type":"uint8","name":"tokenIndexFrom","internalType":"uint8"},{"type":"uint8","name":"tokenIndexTo","internalType":"uint8"},{"type":"uint256","name":"dx","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"calculateTokenAmount","inputs":[{"type":"address","name":"pool","internalType":"contract IStableSwap"},{"type":"address","name":"basePool","internalType":"contract IStableSwap"},{"type":"uint256[]","name":"meta_amounts","internalType":"uint256[]"},{"type":"uint256[]","name":"base_amounts","internalType":"uint256[]"},{"type":"bool","name":"is_deposit","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"convert","inputs":[{"type":"address","name":"fromPool","internalType":"contract IStableSwap"},{"type":"address","name":"toPool","internalType":"contract IStableSwap"},{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"minToMint","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"removeBaseLiquidityOneToken","inputs":[{"type":"address","name":"pool","internalType":"contract IStableSwap"},{"type":"address","name":"basePool","internalType":"contract IStableSwap"},{"type":"uint256","name":"_token_amount","internalType":"uint256"},{"type":"uint8","name":"i","internalType":"uint8"},{"type":"uint256","name":"_min_amount","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"},{"type":"uint256[]","name":"base_amounts","internalType":"uint256[]"}],"name":"removeLiquidity","inputs":[{"type":"address","name":"pool","internalType":"contract IStableSwap"},{"type":"address","name":"basePool","internalType":"contract IStableSwap"},{"type":"uint256","name":"_amount","internalType":"uint256"},{"type":"uint256[]","name":"min_amounts_meta","internalType":"uint256[]"},{"type":"uint256[]","name":"min_amounts_base","internalType":"uint256[]"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"swapFromBase","inputs":[{"type":"address","name":"pool","internalType":"contract IStableSwap"},{"type":"address","name":"basePool","internalType":"contract IStableSwap"},{"type":"uint8","name":"tokenIndexFrom","internalType":"uint8"},{"type":"uint8","name":"tokenIndexTo","internalType":"uint8"},{"type":"uint256","name":"dx","internalType":"uint256"},{"type":"uint256","name":"minDy","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"swapToBase","inputs":[{"type":"address","name":"pool","internalType":"contract IStableSwap"},{"type":"address","name":"basePool","internalType":"contract IStableSwap"},{"type":"uint8","name":"tokenIndexFrom","internalType":"uint8"},{"type":"uint8","name":"tokenIndexTo","internalType":"uint8"},{"type":"uint256","name":"dx","internalType":"uint256"},{"type":"uint256","name":"minDy","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"}]}]'  
route_contract = w3.eth.contract(route_address, abi = route_abi)

metapool_address = '0x008db1Cef0958e7f87A107b58F0dede796ce7962'
basepool_address = '0xb578a396e56388CbF398a12Dea9eb6B01b7c777f'

min_dy = route_contract.functions.calculateSwapFromBase(metapool_address, basepool_address, 0, 0, 1000000000).call()
print(min_dy)

try :

    txn_contract = route_contract.functions.swapFromBase(metapool_address, basepool_address, 0, 0, 1000000000, min_dy, int(time.time()) + 300)
    txn_dict = txn_contract.buildTransaction({'from' : wallet_address, 'nonce' : w3.eth.get_transaction_count(wallet_address)})

except Exception as e:

    print(e)
    pass

min_dy = route_contract.functions.calculateSwapFromBase(metapool_address, basepool_address, 0, 0, 1000000000).call()
print(min_dy)

Test Output:

998604422682132896156
{'code': -32603, 'message': 'VM Exception while processing transaction: revert > slippage', 'data': '08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a3e20736c69707061676500000000000000000000000000000000000000000000'}
998604422682132896156
tomzrx commented 3 years ago
Hayden0323 commented 2 years ago
  • calculateSwapFromBase function does not count any slippage
  • swapFromBase function: minDy must consider slippage so it cannot be exactly as the return in calculateSwapFromBase

From the example above, no other transactions affect the pool, so the calculated result should equal the real amount from require(dy >= minOutAmount, "> slippage"); in the swap function. Is that LpToken received from baseAddLiquidity will less than the result returned from the calculateTokenAmount function? Just a guess