Branch data Line data Source code
1 : : // SPDX-License-Identifier: BUSL-1.1
2 : : pragma solidity ^0.8.10;
3 : :
4 : : import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
5 : : import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
6 : : import {PercentageMath} from '../../protocol/libraries/math/PercentageMath.sol';
7 : : import {IPoolAddressesProvider} from '../../interfaces/IPoolAddressesProvider.sol';
8 : : import {IERC20Detailed} from '../../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
9 : : import {IParaSwapAugustus} from './interfaces/IParaSwapAugustus.sol';
10 : : import {IParaSwapAugustusRegistry} from './interfaces/IParaSwapAugustusRegistry.sol';
11 : : import {BaseParaSwapAdapter} from './BaseParaSwapAdapter.sol';
12 : :
13 : : /**
14 : : * @title BaseParaSwapSellAdapter
15 : : * @notice Implements the logic for selling tokens on ParaSwap
16 : : * @author Jason Raymond Bell
17 : : */
18 : : abstract contract BaseParaSwapSellAdapter is BaseParaSwapAdapter {
19 : : using PercentageMath for uint256;
20 : : using SafeMath for uint256;
21 : : using SafeERC20 for IERC20Detailed;
22 : :
23 : : IParaSwapAugustusRegistry public immutable AUGUSTUS_REGISTRY;
24 : :
25 : : constructor(
26 : : IPoolAddressesProvider addressesProvider,
27 : : IParaSwapAugustusRegistry augustusRegistry
28 : : ) BaseParaSwapAdapter(addressesProvider) {
29 : : // Do something on Augustus registry to check the right contract was passed
30 : 0 : require(!augustusRegistry.isValidAugustus(address(0)));
31 : 0 : AUGUSTUS_REGISTRY = augustusRegistry;
32 : : }
33 : :
34 : : /**
35 : : * @dev Swaps a token for another using ParaSwap
36 : : * @param fromAmountOffset Offset of fromAmount in Augustus calldata if it should be overwritten, otherwise 0
37 : : * @param swapCalldata Calldata for ParaSwap's AugustusSwapper contract
38 : : * @param augustus Address of ParaSwap's AugustusSwapper contract
39 : : * @param assetToSwapFrom Address of the asset to be swapped from
40 : : * @param assetToSwapTo Address of the asset to be swapped to
41 : : * @param amountToSwap Amount to be swapped
42 : : * @param minAmountToReceive Minimum amount to be received from the swap
43 : : * @return amountReceived The amount received from the swap
44 : : */
45 : : function _sellOnParaSwap(
46 : : uint256 fromAmountOffset,
47 : : bytes memory swapCalldata,
48 : : IParaSwapAugustus augustus,
49 : : IERC20Detailed assetToSwapFrom,
50 : : IERC20Detailed assetToSwapTo,
51 : : uint256 amountToSwap,
52 : : uint256 minAmountToReceive
53 : : ) internal returns (uint256 amountReceived) {
54 : 9 : require(AUGUSTUS_REGISTRY.isValidAugustus(address(augustus)), 'INVALID_AUGUSTUS');
55 : :
56 : : {
57 : 9 : uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom);
58 : 9 : uint256 toAssetDecimals = _getDecimals(assetToSwapTo);
59 : :
60 : 9 : uint256 fromAssetPrice = _getPrice(address(assetToSwapFrom));
61 : 9 : uint256 toAssetPrice = _getPrice(address(assetToSwapTo));
62 : :
63 : 9 : uint256 expectedMinAmountOut = amountToSwap
64 : : .mul(fromAssetPrice.mul(10 ** toAssetDecimals))
65 : : .div(toAssetPrice.mul(10 ** fromAssetDecimals))
66 : : .percentMul(PercentageMath.PERCENTAGE_FACTOR - MAX_SLIPPAGE_PERCENT);
67 : :
68 : 9 : require(expectedMinAmountOut <= minAmountToReceive, 'MIN_AMOUNT_EXCEEDS_MAX_SLIPPAGE');
69 : : }
70 : :
71 : 9 : uint256 balanceBeforeAssetFrom = assetToSwapFrom.balanceOf(address(this));
72 : 9 : require(balanceBeforeAssetFrom >= amountToSwap, 'INSUFFICIENT_BALANCE_BEFORE_SWAP');
73 : 9 : uint256 balanceBeforeAssetTo = assetToSwapTo.balanceOf(address(this));
74 : :
75 : 9 : address tokenTransferProxy = augustus.getTokenTransferProxy();
76 : 9 : assetToSwapFrom.safeApprove(tokenTransferProxy, 0);
77 : 9 : assetToSwapFrom.safeApprove(tokenTransferProxy, amountToSwap);
78 : :
79 : 9 : if (fromAmountOffset != 0) {
80 : : // Ensure 256 bit (32 bytes) fromAmount value is within bounds of the
81 : : // calldata, not overlapping with the first 4 bytes (function selector).
82 : 3 : require(
83 : : fromAmountOffset >= 4 && fromAmountOffset <= swapCalldata.length.sub(32),
84 : : 'FROM_AMOUNT_OFFSET_OUT_OF_RANGE'
85 : : );
86 : : // Overwrite the fromAmount with the correct amount for the swap.
87 : : // In memory, swapCalldata consists of a 256 bit length field, followed by
88 : : // the actual bytes data, that is why 32 is added to the byte offset.
89 : : assembly {
90 : 0 : mstore(add(swapCalldata, add(fromAmountOffset, 32)), amountToSwap)
91 : : }
92 : : }
93 : 6 : (bool success, ) = address(augustus).call(swapCalldata);
94 : 6 : if (!success) {
95 : : // Copy revert reason from call
96 : : assembly {
97 : 0 : returndatacopy(0, 0, returndatasize())
98 : 0 : revert(0, returndatasize())
99 : : }
100 : : }
101 : :
102 : 6 : require(
103 : : assetToSwapFrom.balanceOf(address(this)) == balanceBeforeAssetFrom - amountToSwap,
104 : : 'WRONG_BALANCE_AFTER_SWAP'
105 : : );
106 : 6 : amountReceived = assetToSwapTo.balanceOf(address(this)).sub(balanceBeforeAssetTo);
107 : 6 : require(amountReceived >= minAmountToReceive, 'INSUFFICIENT_AMOUNT_RECEIVED');
108 : :
109 : 6 : emit Swapped(address(assetToSwapFrom), address(assetToSwapTo), amountToSwap, amountReceived);
110 : : }
111 : : }
|