133 lines
4.9 KiB
Solidity
133 lines
4.9 KiB
Solidity
// SPDX-License-Identifier: UNLICENCED
|
|
pragma solidity ^0.8.0;
|
|
|
|
import "../interfaces/ISwapExecutor.sol";
|
|
|
|
contract BalancerSwapExecutor is ISwapExecutor {
|
|
address private constant vaultAddress =
|
|
0xBA12222222228d8Ba445958a75a0704d566BF2C8;
|
|
bytes32 private constant swapSelector =
|
|
0x52bbbe2900000000000000000000000000000000000000000000000000000000;
|
|
bytes32 private constant maxUint256 =
|
|
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
|
|
|
|
/**
|
|
* @dev Executes a Balancer swap.
|
|
* @param givenAmount how much of to swap, depending on exactOut either in-
|
|
* or outAmount.
|
|
* @param data the parameters of the swap. This data is roughly the packed
|
|
* encoding of
|
|
* the struct below:
|
|
* ```
|
|
* struct Params {
|
|
* // the token that the caller is selling
|
|
* IERC20 tokenIn;
|
|
*
|
|
* // the token that the caller is receiving in exchange
|
|
* IERC20 tokenOut;
|
|
*
|
|
* // the target pool id
|
|
* bytes32 poolId;
|
|
*
|
|
* // the receiver of `tokenOut`
|
|
* address receiver;
|
|
*
|
|
* // whether we want exactOut semantics
|
|
* bool exactOut;
|
|
*
|
|
* // whether we need to approve the pool to spend `tokenIn`
|
|
* bool tokenApprovalNeeded;
|
|
*
|
|
* }
|
|
* ```
|
|
*/
|
|
function swap(uint256 givenAmount, bytes calldata data)
|
|
external
|
|
payable
|
|
returns (uint256 calculatedAmount)
|
|
{
|
|
IERC20 tokenIn;
|
|
IERC20 tokenOut;
|
|
bytes32 poolId;
|
|
address receiver;
|
|
bool exactOut;
|
|
bool tokenApprovalNeeded;
|
|
assembly {
|
|
tokenIn := shr(96, calldataload(data.offset))
|
|
tokenOut := shr(96, calldataload(add(data.offset, 20)))
|
|
poolId := calldataload(add(data.offset, 40))
|
|
let dataLoad := calldataload(add(data.offset, 72))
|
|
receiver := shr(96, dataLoad)
|
|
exactOut := and(shr(88, dataLoad), 0xff)
|
|
tokenApprovalNeeded := and(shr(80, dataLoad), 0xff)
|
|
|
|
// Check if token approval is needed and perform the approval
|
|
if tokenApprovalNeeded {
|
|
// Prepare approve call
|
|
let approveCalldata := mload(0x40)
|
|
mstore(
|
|
approveCalldata,
|
|
0x095ea7b300000000000000000000000000000000000000000000000000000000
|
|
) // approve selector
|
|
mstore(add(approveCalldata, 4), vaultAddress) // spender
|
|
mstore(add(approveCalldata, 36), maxUint256) // value
|
|
// (maxUint256)
|
|
|
|
let success :=
|
|
call(gas(), tokenIn, 0, approveCalldata, 68, 0, 0)
|
|
if iszero(success) {
|
|
returndatacopy(0, 0, returndatasize())
|
|
revert(0, returndatasize())
|
|
}
|
|
}
|
|
|
|
let ptr := mload(0x40)
|
|
mstore(ptr, swapSelector)
|
|
//limit: as it is always recalculated during the swap, we use the
|
|
// extremums 0 tokenOut or max(uint256) tokenIn.
|
|
let limit := 0
|
|
if exactOut { limit := maxUint256 }
|
|
// as the singleSwap struct contains a bytes, it's considered as
|
|
// dynamic.
|
|
// dynamic values are encoded at the end of the calldata and have a
|
|
// corresponding offset at the beginning
|
|
// we first need to encode the offset of the singleSwap struct
|
|
mstore(add(ptr, 4), 0xe0)
|
|
// fundManagement.sender: is always address(this)
|
|
mstore(add(ptr, 36), address())
|
|
// fundManagement.fromInternalBalance
|
|
mstore(add(ptr, 68), 0)
|
|
// fundManagement.receiver
|
|
mstore(add(ptr, 100), receiver)
|
|
// fundManagement.toInternalBalance
|
|
mstore(add(ptr, 132), 0)
|
|
// limit
|
|
mstore(add(ptr, 164), limit)
|
|
// deadline
|
|
mstore(add(ptr, 196), timestamp())
|
|
// singleSwap.poolId
|
|
mstore(add(ptr, 228), poolId)
|
|
// singleSwap.exactOut
|
|
mstore(add(ptr, 260), exactOut)
|
|
// singleSwap.assetIn
|
|
mstore(add(ptr, 292), tokenIn)
|
|
// singleSwap.assetOut
|
|
mstore(add(ptr, 324), tokenOut)
|
|
// singleSwap.amount
|
|
mstore(add(ptr, 356), givenAmount)
|
|
// singleSwap.userData offset
|
|
mstore(add(ptr, 388), 0xc0)
|
|
// singleSwap.userData lenght
|
|
mstore(add(ptr, 420), 0)
|
|
|
|
let success := call(gas(), vaultAddress, 0, ptr, 452, ptr, 32)
|
|
switch success
|
|
case 0 {
|
|
returndatacopy(0, 0, returndatasize())
|
|
revert(0, returndatasize())
|
|
}
|
|
default { calculatedAmount := mload(ptr) }
|
|
}
|
|
}
|
|
}
|