// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*
* _Available since v2.4.0._
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
interface PoolInterface {
function swapExactAmountIn(address, uint, address, uint, uint) external returns (uint, uint);
function swapExactAmountOut(address, uint, address, uint, uint) external returns (uint, uint);
function calcInGivenOut(uint, uint, uint, uint, uint, uint) external pure returns (uint);
function calcOutGivenIn(uint, uint, uint, uint, uint, uint) external pure returns (uint);
function getDenormalizedWeight(address) external view returns (uint);
function getBalance(address) external view returns (uint);
function swapFee() external view returns (uint);
}
interface TokenInterface {
function balanceOf(address) external view returns (uint);
function allowance(address, address) external view returns (uint);
function approve(address, uint) external returns (bool);
function transfer(address, uint) external returns (bool);
function transferFrom(address, address, uint) external returns (bool);
function deposit() external payable;
function withdraw(uint) external;
}
interface RegistryInterface {
function getBestPoolsWithLimit(address, address, uint) external view returns (address[] memory);
}
interface IFreeFromUpTo {
function freeFromUpTo(address from, uint256 value) external returns (uint256 freed);
}
contract ExchangeProxy {
using SafeMath for uint256;
IFreeFromUpTo public constant chi = IFreeFromUpTo(0x0000000000004946c0e9F43F4Dee607b0eF1fA1c);
modifier discountCHI(uint8 flag) {
if ((flag & 0x1) == 0) {
_;
} else {
uint256 gasStart = gasleft();
_;
uint256 gasSpent = 21000 + gasStart - gasleft() + 16 * msg.data.length;
chi.freeFromUpTo(msg.sender, (gasSpent + 14154) / 41130);
}
}
struct Pool {
address pool;
uint tokenBalanceIn;
uint tokenWeightIn;
uint tokenBalanceOut;
uint tokenWeightOut;
uint swapFee;
uint effectiveLiquidity;
}
struct Swap {
address pool;
address tokenIn;
address tokenOut;
uint swapAmount; // tokenInAmount / tokenOutAmount
uint limitReturnAmount; // minAmountOut / maxAmountIn
uint maxPrice;
}
TokenInterface weth;
RegistryInterface registry;
address private constant ETH_ADDRESS = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
uint private constant BONE = 10**18;
address public governance;
constructor(address _weth) public {
weth = TokenInterface(_weth);
governance = tx.origin;
}
function setGovernance(address _governance) external {
require(msg.sender == governance, "!governance");
governance = _governance;
}
function setRegistry(address _registry) external {
require(msg.sender == governance, "!governance");
registry = RegistryInterface(_registry);
}
function batchSwapExactIn(
Swap[] memory swaps,
TokenInterface tokenIn,
TokenInterface tokenOut,
uint totalAmountIn,
uint minTotalAmountOut,
uint8 flag
)
public payable discountCHI(flag)
returns (uint totalAmountOut)
{
transferFromAll(tokenIn, totalAmountIn);
for (uint i = 0; i < swaps.length; i++) {
Swap memory swap = swaps[i];
TokenInterface SwapTokenIn = TokenInterface(swap.tokenIn);
PoolInterface pool = PoolInterface(swap.pool);
if (SwapTokenIn.allowance(address(this), swap.pool) > 0) {
SwapTokenIn.approve(swap.pool, 0);
}
SwapTokenIn.approve(swap.pool, swap.swapAmount);
(uint tokenAmountOut,) = pool.swapExactAmountIn(
swap.tokenIn,
swap.swapAmount,
swap.tokenOut,
swap.limitReturnAmount,
swap.maxPrice
);
totalAmountOut = tokenAmountOut.add(totalAmountOut);
}
require(totalAmountOut >= minTotalAmountOut, "ERR_LIMIT_OUT");
transferAll(tokenOut, totalAmountOut);
transferAll(tokenIn, getBalance(tokenIn));
}
function batchSwapExactOut(
Swap[] memory swaps,
TokenInterface tokenIn,
TokenInterface tokenOut,
uint maxTotalAmountIn,
uint8 flag
)
public payable discountCHI(flag)
returns (uint totalAmountIn)
{
transferFromAll(tokenIn, maxTotalAmountIn);
for (uint i = 0; i < swaps.length; i++) {
Swap memory swap = swaps[i];
TokenInterface SwapTokenIn = TokenInterface(swap.tokenIn);
PoolInterface pool = PoolInterface(swap.pool);
if (SwapTokenIn.allowance(address(this), swap.pool) > 0) {
SwapTokenIn.approve(swap.pool, 0);
}
SwapTokenIn.approve(swap.pool, swap.limitReturnAmount);
(uint tokenAmountIn,) = pool.swapExactAmountOut(
swap.tokenIn,
swap.limitReturnAmount,
swap.tokenOut,
swap.swapAmount,
swap.maxPrice
);
totalAmountIn = tokenAmountIn.add(totalAmountIn);
}
require(totalAmountIn <= maxTotalAmountIn, "ERR_LIMIT_IN");
transferAll(tokenOut, getBalance(tokenOut));
transferAll(tokenIn, getBalance(tokenIn));
}
function multihopBatchSwapExactIn(
Swap[][] memory swapSequences,
TokenInterface tokenIn,
TokenInterface tokenOut,
uint totalAmountIn,
uint minTotalAmountOut,
uint8 flag
)
public payable discountCHI(flag)
returns (uint totalAmountOut)
{
transferFromAll(tokenIn, totalAmountIn);
for (uint i = 0; i < swapSequences.length; i++) {
uint tokenAmountOut;
for (uint k = 0; k < swapSequences[i].length; k++) {
Swap memory swap = swapSequences[i][k];
TokenInterface SwapTokenIn = TokenInterface(swap.tokenIn);
if (k == 1) {
// Makes sure that on the second swap the output of the first was used
// so there is not intermediate token leftover
swap.swapAmount = tokenAmountOut;
}
PoolInterface pool = PoolInterface(swap.pool);
if (SwapTokenIn.allowance(address(this), swap.pool) > 0) {
SwapTokenIn.approve(swap.pool, 0);
}
SwapTokenIn.approve(swap.pool, swap.swapAmount);
(tokenAmountOut,) = pool.swapExactAmountIn(
swap.tokenIn,
swap.swapAmount,
swap.tokenOut,
swap.limitReturnAmount,
swap.maxPrice
);
}
// This takes the amountOut of the last swap
totalAmountOut = tokenAmountOut.add(totalAmountOut);
}
require(totalAmountOut >= minTotalAmountOut, "ERR_LIMIT_OUT");
transferAll(tokenOut, totalAmountOut);
transferAll(tokenIn, getBalance(tokenIn));
}
function multihopBatchSwapExactOut(
Swap[][] memory swapSequences,
TokenInterface tokenIn,
TokenInterface tokenOut,
uint maxTotalAmountIn,
uint8 flag
)
public payable discountCHI(flag)
returns (uint totalAmountIn)
{
transferFromAll(tokenIn, maxTotalAmountIn);
for (uint i = 0; i < swapSequences.length; i++) {
uint tokenAmountInFirstSwap;
// Specific code for a simple swap and a multihop (2 swaps in sequence)
if (swapSequences[i].length == 1) {
Swap memory swap = swapSequences[i][0];
TokenInterface SwapTokenIn = TokenInterface(swap.tokenIn);
PoolInterface pool = PoolInterface(swap.pool);
if (SwapTokenIn.allowance(address(this), swap.pool) > 0) {
SwapTokenIn.approve(swap.pool, 0);
}
SwapTokenIn.approve(swap.pool, swap.limitReturnAmount);
(tokenAmountInFirstSwap,) = pool.swapExactAmountOut(
swap.tokenIn,
swap.limitReturnAmount,
swap.tokenOut,
swap.swapAmount,
swap.maxPrice
);
} else {
// Consider we are swapping A -> B and B -> C. The goal is to buy a given amount
// of token C. But first we need to buy B with A so we can then buy C with B
// To get the exact amount of C we then first need to calculate how much B we'll need:
uint intermediateTokenAmount; // This would be token B as described above
Swap memory secondSwap = swapSequences[i][1];
PoolInterface poolSecondSwap = PoolInterface(secondSwap.pool);
intermediateTokenAmount = poolSecondSwap.calcInGivenOut(
poolSecondSwap.getBalance(secondSwap.tokenIn),
poolSecondSwap.getDenormalizedWeight(secondSwap.tokenIn),
poolSecondSwap.getBalance(secondSwap.tokenOut),
poolSecondSwap.getDenormalizedWeight(secondSwap.tokenOut),
secondSwap.swapAmount,
poolSecondSwap.swapFee()
);
//// Buy intermediateTokenAmount of token B with A in the first pool
Swap memory firstSwap = swapSequences[i][0];
TokenInterface FirstSwapTokenIn = TokenInterface(firstSwap.tokenIn);
PoolInterface poolFirstSwap = PoolInterface(firstSwap.pool);
if (FirstSwapTokenIn.allowance(address(this), firstSwap.pool) < uint(-1)) {
FirstSwapTokenIn.approve(firstSwap.pool, uint(-1));
}
(tokenAmountInFirstSwap,) = poolFirstSwap.swapExactAmountOut(
firstSwap.tokenIn,
firstSwap.limitReturnAmount,
firstSwap.tokenOut,
intermediateTokenAmount, // This is the amount of token B we need
firstSwap.maxPrice
);
//// Buy the final amount of token C desired
TokenInterface SecondSwapTokenIn = TokenInterface(secondSwap.tokenIn);
if (SecondSwapTokenIn.allowance(address(this), secondSwap.pool) < uint(-1)) {
SecondSwapTokenIn.approve(secondSwap.pool, uint(-1));
}
poolSecondSwap.swapExactAmountOut(
secondSwap.tokenIn,
secondSwap.limitReturnAmount,
secondSwap.tokenOut,
secondSwap.swapAmount,
secondSwap.maxPrice
);
}
totalAmountIn = tokenAmountInFirstSwap.add(totalAmountIn);
}
require(totalAmountIn <= maxTotalAmountIn, "ERR_LIMIT_IN");
transferAll(tokenOut, getBalance(tokenOut));
transferAll(tokenIn, getBalance(tokenIn));
}
function smartSwapExactIn(
TokenInterface tokenIn,
TokenInterface tokenOut,
uint totalAmountIn,
uint minTotalAmountOut,
uint nPools,
uint8 flag
)
public payable discountCHI(flag)
returns (uint totalAmountOut)
{
Swap[] memory swaps;
if (isETH(tokenIn)) {
(swaps,) = viewSplitExactIn(address(weth), address(tokenOut), totalAmountIn, nPools);
} else if (isETH(tokenOut)){
(swaps,) = viewSplitExactIn(address(tokenIn), address(weth), totalAmountIn, nPools);
} else {
(swaps,) = viewSplitExactIn(address(tokenIn), address(tokenOut), totalAmountIn, nPools);
}
totalAmountOut = batchSwapExactIn(swaps, tokenIn, tokenOut, totalAmountIn, minTotalAmountOut, 0x0);
}
function smartSwapExactOut(
TokenInterface tokenIn,
TokenInterface tokenOut,
uint totalAmountOut,
uint maxTotalAmountIn,
uint nPools,
uint8 flag
)
public payable discountCHI(flag)
returns (uint totalAmountIn)
{
Swap[] memory swaps;
if (isETH(tokenIn)) {
(swaps,) = viewSplitExactOut(address(weth), address(tokenOut), totalAmountOut, nPools);
} else if (isETH(tokenOut)){
(swaps,) = viewSplitExactOut(address(tokenIn), address(weth), totalAmountOut, nPools);
} else {
(swaps,) = viewSplitExactOut(address(tokenIn), address(tokenOut), totalAmountOut, nPools);
}
totalAmountIn = batchSwapExactOut(swaps, tokenIn, tokenOut, maxTotalAmountIn, 0x0);
}
function viewSplitExactIn(
address tokenIn,
address tokenOut,
uint swapAmount,
uint nPools
)
public view
returns (Swap[] memory swaps, uint totalOutput)
{
address[] memory poolAddresses = registry.getBestPoolsWithLimit(tokenIn, tokenOut, nPools);
Pool[] memory pools = new Pool[](poolAddresses.length);
uint sumEffectiveLiquidity;
for (uint i = 0; i < poolAddresses.length; i++) {
pools[i] = getPoolData(tokenIn, tokenOut, poolAddresses[i]);
sumEffectiveLiquidity = sumEffectiveLiquidity.add(pools[i].effectiveLiquidity);
}
uint[] memory bestInputAmounts = new uint[](pools.length);
uint totalInputAmount;
for (uint i = 0; i < pools.length; i++) {
bestInputAmounts[i] = swapAmount.mul(pools[i].effectiveLiquidity).div(sumEffectiveLiquidity);
totalInputAmount = totalInputAmount.add(bestInputAmounts[i]);
}
if (totalInputAmount < swapAmount) {
bestInputAmounts[0] = bestInputAmounts[0].add(swapAmount.sub(totalInputAmount));
} else {
bestInputAmounts[0] = bestInputAmounts[0].sub(totalInputAmount.sub(swapAmount));
}
swaps = new Swap[](pools.length);
for (uint i = 0; i < pools.length; i++) {
swaps[i] = Swap({
pool: pools[i].pool,
tokenIn: tokenIn,
tokenOut: tokenOut,
swapAmount: bestInputAmounts[i],
limitReturnAmount: 0,
maxPrice: uint(-1)
});
}
totalOutput = calcTotalOutExactIn(bestInputAmounts, pools);
return (swaps, totalOutput);
}
function viewSplitExactOut(
address tokenIn,
address tokenOut,
uint swapAmount,
uint nPools
)
public view
returns (Swap[] memory swaps, uint totalOutput)
{
address[] memory poolAddresses = registry.getBestPoolsWithLimit(tokenIn, tokenOut, nPools);
Pool[] memory pools = new Pool[](poolAddresses.length);
uint sumEffectiveLiquidity;
for (uint i = 0; i < poolAddresses.length; i++) {
pools[i] = getPoolData(tokenIn, tokenOut, poolAddresses[i]);
sumEffectiveLiquidity = sumEffectiveLiquidity.add(pools[i].effectiveLiquidity);
}
uint[] memory bestInputAmounts = new uint[](pools.length);
uint totalInputAmount;
for (uint i = 0; i < pools.length; i++) {
bestInputAmounts[i] = swapAmount.mul(pools[i].effectiveLiquidity).div(sumEffectiveLiquidity);
totalInputAmount = totalInputAmount.add(bestInputAmounts[i]);
}
if (totalInputAmount < swapAmount) {
bestInputAmounts[0] = bestInputAmounts[0].add(swapAmount.sub(totalInputAmount));
} else {
bestInputAmounts[0] = bestInputAmounts[0].sub(totalInputAmount.sub(swapAmount));
}
swaps = new Swap[](pools.length);
for (uint i = 0; i < pools.length; i++) {
swaps[i] = Swap({
pool: pools[i].pool,
tokenIn: tokenIn,
tokenOut: tokenOut,
swapAmount: bestInputAmounts[i],
limitReturnAmount: uint(-1),
maxPrice: uint(-1)
});
}
totalOutput = calcTotalOutExactOut(bestInputAmounts, pools);
return (swaps, totalOutput);
}
function getPoolData(
address tokenIn,
address tokenOut,
address poolAddress
)
internal view
returns (Pool memory)
{
PoolInterface pool = PoolInterface(poolAddress);
uint tokenBalanceIn = pool.getBalance(tokenIn);
uint tokenBalanceOut = pool.getBalance(tokenOut);
uint tokenWeightIn = pool.getDenormalizedWeight(tokenIn);
uint tokenWeightOut = pool.getDenormalizedWeight(tokenOut);
uint swapFee = pool.swapFee();
uint effectiveLiquidity = calcEffectiveLiquidity(
tokenWeightIn,
tokenBalanceOut,
tokenWeightOut
);
Pool memory returnPool = Pool({
pool: poolAddress,
tokenBalanceIn: tokenBalanceIn,
tokenWeightIn: tokenWeightIn,
tokenBalanceOut: tokenBalanceOut,
tokenWeightOut: tokenWeightOut,
swapFee: swapFee,
effectiveLiquidity: effectiveLiquidity
});
return returnPool;
}
function calcEffectiveLiquidity(
uint tokenWeightIn,
uint tokenBalanceOut,
uint tokenWeightOut
)
internal pure
returns (uint effectiveLiquidity)
{
// Bo * wi/(wi+wo)
effectiveLiquidity =
tokenWeightIn.mul(BONE).div(
tokenWeightOut.add(tokenWeightIn)
).mul(tokenBalanceOut).div(BONE);
return effectiveLiquidity;
}
function calcTotalOutExactIn(
uint[] memory bestInputAmounts,
Pool[] memory bestPools
)
internal pure
returns (uint totalOutput)
{
totalOutput = 0;
for (uint i = 0; i < bestInputAmounts.length; i++) {
uint output = PoolInterface(bestPools[i].pool).calcOutGivenIn(
bestPools[i].tokenBalanceIn,
bestPools[i].tokenWeightIn,
bestPools[i].tokenBalanceOut,
bestPools[i].tokenWeightOut,
bestInputAmounts[i],
bestPools[i].swapFee
);
totalOutput = totalOutput.add(output);
}
return totalOutput;
}
function calcTotalOutExactOut(
uint[] memory bestInputAmounts,
Pool[] memory bestPools
)
internal pure
returns (uint totalOutput)
{
totalOutput = 0;
for (uint i = 0; i < bestInputAmounts.length; i++) {
uint output = PoolInterface(bestPools[i].pool).calcInGivenOut(
bestPools[i].tokenBalanceIn,
bestPools[i].tokenWeightIn,
bestPools[i].tokenBalanceOut,
bestPools[i].tokenWeightOut,
bestInputAmounts[i],
bestPools[i].swapFee
);
totalOutput = totalOutput.add(output);
}
return totalOutput;
}
function transferFromAll(TokenInterface token, uint amount) internal returns(bool) {
if (isETH(token)) {
weth.deposit{value : msg.value}();
} else {
require(token.transferFrom(msg.sender, address(this), amount), "ERR_TRANSFER_FAILED");
}
}
function getBalance(TokenInterface token) internal view returns (uint) {
if (isETH(token)) {
return weth.balanceOf(address(this));
} else {
return token.balanceOf(address(this));
}
}
function transferAll(TokenInterface token, uint amount) internal returns(bool) {
if (amount == 0) {
return true;
}
if (isETH(token)) {
weth.withdraw(amount);
(bool xfer,) = msg.sender.call{value : amount}("");
require(xfer, "ERR_ETH_FAILED");
} else {
require(token.transfer(msg.sender, amount), "ERR_TRANSFER_FAILED");
}
}
function isETH(TokenInterface token) internal pure returns(bool) {
return (address(token) == ETH_ADDRESS);
}
/**
* This function allows governance to take unsupported tokens out of the contract.
* This is in an effort to make someone whole, should they seriously mess up.
* There is no guarantee governance will vote to return these.
* It also allows for removal of airdropped tokens.
*/
function governanceRecoverUnsupported(TokenInterface _token, uint _amount, address _to) external {
require(msg.sender == governance, "!governance");
if (isETH(_token)) {
(bool xfer,) = _to.call{value : _amount}("");
require(xfer, "ERR_ETH_FAILED");
} else {
require(_token.transfer(_to, _amount), "ERR_TRANSFER_FAILED");
}
}
receive() external payable {}
}
{
"compilationTarget": {
"ExchangeProxy.sol": "ExchangeProxy"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_weth","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"swapAmount","type":"uint256"},{"internalType":"uint256","name":"limitReturnAmount","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"}],"internalType":"struct ExchangeProxy.Swap[]","name":"swaps","type":"tuple[]"},{"internalType":"contract TokenInterface","name":"tokenIn","type":"address"},{"internalType":"contract TokenInterface","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"totalAmountIn","type":"uint256"},{"internalType":"uint256","name":"minTotalAmountOut","type":"uint256"},{"internalType":"uint8","name":"flag","type":"uint8"}],"name":"batchSwapExactIn","outputs":[{"internalType":"uint256","name":"totalAmountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"swapAmount","type":"uint256"},{"internalType":"uint256","name":"limitReturnAmount","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"}],"internalType":"struct ExchangeProxy.Swap[]","name":"swaps","type":"tuple[]"},{"internalType":"contract TokenInterface","name":"tokenIn","type":"address"},{"internalType":"contract TokenInterface","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"maxTotalAmountIn","type":"uint256"},{"internalType":"uint8","name":"flag","type":"uint8"}],"name":"batchSwapExactOut","outputs":[{"internalType":"uint256","name":"totalAmountIn","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"chi","outputs":[{"internalType":"contract IFreeFromUpTo","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract TokenInterface","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"governanceRecoverUnsupported","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"swapAmount","type":"uint256"},{"internalType":"uint256","name":"limitReturnAmount","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"}],"internalType":"struct ExchangeProxy.Swap[][]","name":"swapSequences","type":"tuple[][]"},{"internalType":"contract TokenInterface","name":"tokenIn","type":"address"},{"internalType":"contract TokenInterface","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"totalAmountIn","type":"uint256"},{"internalType":"uint256","name":"minTotalAmountOut","type":"uint256"},{"internalType":"uint8","name":"flag","type":"uint8"}],"name":"multihopBatchSwapExactIn","outputs":[{"internalType":"uint256","name":"totalAmountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"swapAmount","type":"uint256"},{"internalType":"uint256","name":"limitReturnAmount","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"}],"internalType":"struct ExchangeProxy.Swap[][]","name":"swapSequences","type":"tuple[][]"},{"internalType":"contract TokenInterface","name":"tokenIn","type":"address"},{"internalType":"contract TokenInterface","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"maxTotalAmountIn","type":"uint256"},{"internalType":"uint8","name":"flag","type":"uint8"}],"name":"multihopBatchSwapExactOut","outputs":[{"internalType":"uint256","name":"totalAmountIn","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_governance","type":"address"}],"name":"setGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_registry","type":"address"}],"name":"setRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract TokenInterface","name":"tokenIn","type":"address"},{"internalType":"contract TokenInterface","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"totalAmountIn","type":"uint256"},{"internalType":"uint256","name":"minTotalAmountOut","type":"uint256"},{"internalType":"uint256","name":"nPools","type":"uint256"},{"internalType":"uint8","name":"flag","type":"uint8"}],"name":"smartSwapExactIn","outputs":[{"internalType":"uint256","name":"totalAmountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract TokenInterface","name":"tokenIn","type":"address"},{"internalType":"contract TokenInterface","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"totalAmountOut","type":"uint256"},{"internalType":"uint256","name":"maxTotalAmountIn","type":"uint256"},{"internalType":"uint256","name":"nPools","type":"uint256"},{"internalType":"uint8","name":"flag","type":"uint8"}],"name":"smartSwapExactOut","outputs":[{"internalType":"uint256","name":"totalAmountIn","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"swapAmount","type":"uint256"},{"internalType":"uint256","name":"nPools","type":"uint256"}],"name":"viewSplitExactIn","outputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"swapAmount","type":"uint256"},{"internalType":"uint256","name":"limitReturnAmount","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"}],"internalType":"struct ExchangeProxy.Swap[]","name":"swaps","type":"tuple[]"},{"internalType":"uint256","name":"totalOutput","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"swapAmount","type":"uint256"},{"internalType":"uint256","name":"nPools","type":"uint256"}],"name":"viewSplitExactOut","outputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"swapAmount","type":"uint256"},{"internalType":"uint256","name":"limitReturnAmount","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"}],"internalType":"struct ExchangeProxy.Swap[]","name":"swaps","type":"tuple[]"},{"internalType":"uint256","name":"totalOutput","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]