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 BaseParaSwapBuyAdapter
15 : : * @notice Implements the logic for buying tokens on ParaSwap
16 : : */
17 : : abstract contract BaseParaSwapBuyAdapter is BaseParaSwapAdapter {
18 : : using PercentageMath for uint256;
19 : : using SafeMath for uint256;
20 : : using SafeERC20 for IERC20Detailed;
21 : :
22 : : IParaSwapAugustusRegistry public immutable AUGUSTUS_REGISTRY;
23 : :
24 : : constructor(
25 : : IPoolAddressesProvider addressesProvider,
26 : : IParaSwapAugustusRegistry augustusRegistry
27 : : ) BaseParaSwapAdapter(addressesProvider) {
28 : : // Do something on Augustus registry to check the right contract was passed
29 : 0 : require(!augustusRegistry.isValidAugustus(address(0)), 'Not a valid Augustus address');
30 : 0 : AUGUSTUS_REGISTRY = augustusRegistry;
31 : : }
32 : :
33 : : /**
34 : : * @dev Swaps a token for another using ParaSwap
35 : : * @param toAmountOffset Offset of toAmount in Augustus calldata if it should be overwritten, otherwise 0
36 : : * @param paraswapData Data for Paraswap Adapter
37 : : * @param assetToSwapFrom Address of the asset to be swapped from
38 : : * @param assetToSwapTo Address of the asset to be swapped to
39 : : * @param maxAmountToSwap Max amount to be swapped
40 : : * @param amountToReceive Amount to be received from the swap
41 : : * @return amountSold The amount sold during the swap
42 : : * @return amountBought The amount bought during the swap
43 : : */
44 : : function _buyOnParaSwap(
45 : : uint256 toAmountOffset,
46 : : bytes memory paraswapData,
47 : : IERC20Detailed assetToSwapFrom,
48 : : IERC20Detailed assetToSwapTo,
49 : : uint256 maxAmountToSwap,
50 : : uint256 amountToReceive
51 : : ) internal returns (uint256 amountSold, uint256 amountBought) {
52 : 6 : (bytes memory buyCalldata, IParaSwapAugustus augustus) = abi.decode(
53 : : paraswapData,
54 : : (bytes, IParaSwapAugustus)
55 : : );
56 : :
57 : 6 : require(AUGUSTUS_REGISTRY.isValidAugustus(address(augustus)), 'INVALID_AUGUSTUS');
58 : :
59 : : {
60 : 6 : uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom);
61 : 6 : uint256 toAssetDecimals = _getDecimals(assetToSwapTo);
62 : :
63 : 6 : uint256 fromAssetPrice = _getPrice(address(assetToSwapFrom));
64 : 6 : uint256 toAssetPrice = _getPrice(address(assetToSwapTo));
65 : :
66 : 6 : uint256 expectedMaxAmountToSwap = amountToReceive
67 : : .mul(toAssetPrice.mul(10 ** fromAssetDecimals))
68 : : .div(fromAssetPrice.mul(10 ** toAssetDecimals))
69 : : .percentMul(PercentageMath.PERCENTAGE_FACTOR.add(MAX_SLIPPAGE_PERCENT));
70 : :
71 : 6 : require(maxAmountToSwap <= expectedMaxAmountToSwap, 'maxAmountToSwap exceed max slippage');
72 : : }
73 : :
74 : 6 : uint256 balanceBeforeAssetFrom = assetToSwapFrom.balanceOf(address(this));
75 : 6 : require(balanceBeforeAssetFrom >= maxAmountToSwap, 'INSUFFICIENT_BALANCE_BEFORE_SWAP');
76 : 6 : uint256 balanceBeforeAssetTo = assetToSwapTo.balanceOf(address(this));
77 : :
78 : 6 : address tokenTransferProxy = augustus.getTokenTransferProxy();
79 : 6 : assetToSwapFrom.safeApprove(tokenTransferProxy, maxAmountToSwap);
80 : :
81 : 6 : if (toAmountOffset != 0) {
82 : : // Ensure 256 bit (32 bytes) toAmountOffset value is within bounds of the
83 : : // calldata, not overlapping with the first 4 bytes (function selector).
84 : 1 : require(
85 : : toAmountOffset >= 4 && toAmountOffset <= buyCalldata.length.sub(32),
86 : : 'TO_AMOUNT_OFFSET_OUT_OF_RANGE'
87 : : );
88 : : // Overwrite the toAmount with the correct amount for the buy.
89 : : // In memory, buyCalldata consists of a 256 bit length field, followed by
90 : : // the actual bytes data, that is why 32 is added to the byte offset.
91 : : assembly {
92 : 0 : mstore(add(buyCalldata, add(toAmountOffset, 32)), amountToReceive)
93 : : }
94 : : }
95 : 5 : (bool success, ) = address(augustus).call(buyCalldata);
96 : 5 : if (!success) {
97 : : // Copy revert reason from call
98 : : assembly {
99 : 0 : returndatacopy(0, 0, returndatasize())
100 : 0 : revert(0, returndatasize())
101 : : }
102 : : }
103 : :
104 : : // Reset allowance
105 : 5 : assetToSwapFrom.safeApprove(tokenTransferProxy, 0);
106 : :
107 : 5 : uint256 balanceAfterAssetFrom = assetToSwapFrom.balanceOf(address(this));
108 : 5 : amountSold = balanceBeforeAssetFrom - balanceAfterAssetFrom;
109 : 5 : require(amountSold <= maxAmountToSwap, 'WRONG_BALANCE_AFTER_SWAP');
110 : 5 : amountBought = assetToSwapTo.balanceOf(address(this)).sub(balanceBeforeAssetTo);
111 : 5 : require(amountBought >= amountToReceive, 'INSUFFICIENT_AMOUNT_RECEIVED');
112 : :
113 : 5 : emit Bought(address(assetToSwapFrom), address(assetToSwapTo), amountSold, amountBought);
114 : : }
115 : : }
|