Skip to content

Commit 177ae15

Browse files
committed
Added swapAndRepay ParaSwapBuyAndRepay Adapter
1 parent 258a9be commit 177ae15

File tree

4 files changed

+11007
-4
lines changed

4 files changed

+11007
-4
lines changed

contracts/adapters/BaseParaSwapAdapter.sol

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,23 @@ abstract contract BaseParaSwapAdapter is FlashLoanReceiverBase, Ownable {
3737

3838
IPriceOracleGetter public immutable ORACLE;
3939

40-
event Swapped(address indexed fromAsset, address indexed toAsset, uint256 fromAmount, uint256 receivedAmount);
40+
event Swapped(
41+
address indexed fromAsset,
42+
address indexed toAsset,
43+
uint256 fromAmount,
44+
uint256 receivedAmount
45+
);
46+
event Bought(
47+
address indexed fromAsset,
48+
address indexed toAsset,
49+
uint256 amountSold,
50+
uint256 receivedAmount
51+
);
4152

42-
constructor(
43-
ILendingPoolAddressesProvider addressesProvider
44-
) public FlashLoanReceiverBase(addressesProvider) {
53+
constructor(ILendingPoolAddressesProvider addressesProvider)
54+
public
55+
FlashLoanReceiverBase(addressesProvider)
56+
{
4557
ORACLE = IPriceOracleGetter(addressesProvider.getPriceOracle());
4658
}
4759

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

0 commit comments

Comments
 (0)