Skip to content

Commit 9707d84

Browse files
committed
Bug fix and test cases for ParaswapRepayAdapter
1 parent e56afde commit 9707d84

File tree

11 files changed

+1518
-36
lines changed

11 files changed

+1518
-36
lines changed

contracts/adapters/ParaSwapRepayAdapter.sol

Lines changed: 22 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@ contract ParaSwapRepayAdapter is BaseParaSwapBuyAdapter, ReentrancyGuard {
3939
* The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset, swap it
4040
* and repay the flash loan.
4141
* Supports only one asset on the flash loan.
42-
* @param assets Address of debt asset
43-
* @param amounts Amount of the debt to be repaid, or maximum amount when repaying entire debt
42+
* @param assets Address of collateral asset(Flash loan asset)
43+
* @param amounts Amount of flash loan taken
4444
* @param premiums Fee of the flash loan
4545
* @param initiator Address of the user
4646
* @param params Additional variadic field to include extra params. Expected parameters:
47-
* IERC20Detailed collateralAsset Address of the reserve to be swapped
48-
* uint256 collateralAmount max Amount of the collateral to be swapped
47+
* IERC20Detailed debtAsset Address of the debt asset
48+
* uint256 debtAmount Amount of debt to be repaid
4949
* uint256 rateMode Rate modes of the debt to be repaid
5050
* uint256 deadline Deadline for the permit signature
5151
* uint256 debtRateMode Rate mode of the debt to be repaid
@@ -68,31 +68,20 @@ contract ParaSwapRepayAdapter is BaseParaSwapBuyAdapter, ReentrancyGuard {
6868
'FLASHLOAN_MULTIPLE_ASSETS_NOT_SUPPORTED'
6969
);
7070

71-
uint256 debtRepayAmount = amounts[0];
71+
uint256 collateralAmount = amounts[0];
7272
uint256 premium = premiums[0];
7373
address initiatorLocal = initiator;
74+
7475

75-
IERC20Detailed debtAsset = IERC20Detailed(assets[0]);
76-
(
77-
IERC20Detailed collateralAsset,
78-
uint256 collateralAmount,
79-
uint256 buyAllBalanceOffset,
80-
uint256 debtRateMode,
81-
bytes memory paraswapData,
82-
PermitSignature memory permitParams
83-
) = abi.decode(params, (IERC20Detailed, uint256, uint256, uint256, bytes, PermitSignature));
76+
IERC20Detailed collateralAsset = IERC20Detailed(assets[0]);
77+
8478

8579
_swapAndRepay(
86-
buyAllBalanceOffset,
87-
paraswapData,
88-
permitParams,
89-
debtRepayAmount,
80+
params,
9081
premium,
9182
initiatorLocal,
9283
collateralAsset,
93-
debtAsset,
94-
collateralAmount,
95-
debtRateMode
84+
collateralAmount
9685
);
9786

9887
return true;
@@ -167,30 +156,29 @@ contract ParaSwapRepayAdapter is BaseParaSwapBuyAdapter, ReentrancyGuard {
167156

168157
/**
169158
* @dev Perform the repay of the debt, pulls the initiator collateral and swaps to repay the flash loan
170-
* @param buyAllBalanceOffset Set to offset of fromAmount in Augustus calldata if wanting to swap all balance, otherwise 0
171-
* @param paraswapData Paraswap data
172-
* @param permitSignature struct containing the permit signature
173-
* @param debtRepayAmount Amount of the debt to be repaid, or maximum amount when repaying entire debt
174159
* @param premium Fee of the flash loan
175160
* @param initiator Address of the user
176161
* @param collateralAsset Address of token to be swapped
177-
* @param debtAsset Address of debt token to be received from the swap
178162
* @param collateralAmount Amount of the reserve to be swapped(flash loan amount)
179-
* @param rateMode Rate mode of the debt to be repaid
180163
*/
181164

182165
function _swapAndRepay(
183-
uint256 buyAllBalanceOffset,
184-
bytes memory paraswapData,
185-
PermitSignature memory permitSignature,
186-
uint256 debtRepayAmount,
166+
bytes calldata params,
187167
uint256 premium,
188168
address initiator,
189169
IERC20Detailed collateralAsset,
190-
IERC20Detailed debtAsset,
191-
uint256 collateralAmount,
192-
uint256 rateMode
170+
uint256 collateralAmount
193171
) private {
172+
173+
(
174+
IERC20Detailed debtAsset,
175+
uint256 debtRepayAmount,
176+
uint256 buyAllBalanceOffset,
177+
uint256 rateMode,
178+
bytes memory paraswapData,
179+
PermitSignature memory permitSignature
180+
) = abi.decode(params, (IERC20Detailed, uint256, uint256, uint256, bytes, PermitSignature));
181+
194182
debtRepayAmount = getDebtRepayAmount(
195183
debtAsset,
196184
rateMode,

contracts/mocks/swap/MockParaSwapAugustus.sol

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,22 @@ contract MockParaSwapAugustus is IParaSwapAugustus {
5656
_expectingSwap = false;
5757
return _receivedAmount;
5858
}
59+
60+
function buy(
61+
address fromToken,
62+
address toToken,
63+
uint256 fromAmount,
64+
uint256 toAmount
65+
) external returns (uint256) {
66+
require(_expectingSwap, 'Not expecting swap');
67+
require(fromToken == _expectedFromToken, 'Unexpected from token');
68+
require(toToken == _expectedToToken, 'Unexpected to token');
69+
require(fromAmount >= _expectedFromAmountMin && fromAmount <= _expectedFromAmountMax, 'From amount out of range');
70+
require(_receivedAmount >= toAmount, 'Received amount of tokens are less than expected');
71+
TOKEN_TRANSFER_PROXY.transferFrom(fromToken, msg.sender, address(this), fromAmount);
72+
MintableERC20(toToken).mint(toAmount);
73+
IERC20(toToken).transfer(msg.sender, toAmount);
74+
_expectingSwap = false;
75+
return fromAmount;
76+
}
5977
}

helpers/contracts-deployments.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import {
5252
WETH9MockedFactory,
5353
WETHGatewayFactory,
5454
FlashLiquidationAdapterFactory,
55+
ParaSwapRepayAdapterFactory,
5556
} from '../types';
5657
import {
5758
withSaveAndVerify,
@@ -670,3 +671,14 @@ export const deployParaSwapLiquiditySwapAdapter = async (
670671
args,
671672
verify
672673
);
674+
675+
export const deployParaSwapRepayAdapter = async (
676+
args: [tEthereumAddress, tEthereumAddress],
677+
verify?: boolean
678+
) =>
679+
withSaveAndVerify(
680+
await new ParaSwapRepayAdapterFactory(await getFirstSigner()).deploy(...args),
681+
eContractid.ParaSwapRepayAdapter,
682+
args,
683+
verify
684+
);

helpers/contracts-getters.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import {
3333
WETH9MockedFactory,
3434
WETHGatewayFactory,
3535
FlashLiquidationAdapterFactory,
36+
ParaSwapRepayAdapterFactory,
3637
} from '../types';
3738
import { IERC20DetailedFactory } from '../types/IERC20DetailedFactory';
3839
import { getEthersSigners, MockTokenMap } from './contracts-helpers';
@@ -391,3 +392,11 @@ export const getParaSwapLiquiditySwapAdapter = async (address?: tEthereumAddress
391392
.address,
392393
await getFirstSigner()
393394
);
395+
396+
export const getParaSwapRepayAdapter = async (address?: tEthereumAddress) =>
397+
await ParaSwapRepayAdapterFactory.connect(
398+
address ||
399+
(await getDb().get(`${eContractid.ParaSwapRepayAdapter}.${DRE.network.name}`).value())
400+
.address,
401+
await getFirstSigner()
402+
);

helpers/contracts-helpers.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,57 @@ export const buildParaSwapLiquiditySwapParams = (
359359
);
360360
};
361361

362+
export const buildParaswapBuyParams = (
363+
buyCalldata: string | Buffer,
364+
augustus: tEthereumAddress,
365+
)=> {
366+
return ethers.utils.defaultAbiCoder.encode(
367+
[
368+
'bytes',
369+
'address'
370+
],
371+
[
372+
buyCalldata,
373+
augustus
374+
]
375+
376+
);
377+
}
378+
export const buildParaSwapRepayParams = (
379+
collateralAsset: tEthereumAddress,
380+
collateralAmount: BigNumberish,
381+
buyAllBalanceOffset: BigNumberish,
382+
debtRateMode: BigNumberish,
383+
buyCalldata: string | Buffer,
384+
augustus: tEthereumAddress,
385+
permitAmount: BigNumberish,
386+
deadline: BigNumberish,
387+
v: BigNumberish,
388+
r: string | Buffer,
389+
s: string | Buffer
390+
391+
) => {
392+
const paraswapData = buildParaswapBuyParams(buyCalldata, augustus)
393+
return ethers.utils.defaultAbiCoder.encode(
394+
[
395+
'address',
396+
'uint256',
397+
'uint256',
398+
'uint256',
399+
'bytes',
400+
'tuple(uint256,uint256,uint8,bytes32,bytes32)',
401+
],
402+
[
403+
collateralAsset,
404+
collateralAmount,
405+
buyAllBalanceOffset,
406+
debtRateMode,
407+
paraswapData,
408+
[permitAmount, deadline, v, r, s],
409+
]
410+
);
411+
};
412+
362413
export const verifyContract = async (
363414
id: string,
364415
instance: Contract,

helpers/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export enum AavePools {
4040
amm = 'amm',
4141
}
4242

43+
4344
export enum eContractid {
4445
Example = 'Example',
4546
LendingPoolAddressesProvider = 'LendingPoolAddressesProvider',
@@ -90,6 +91,7 @@ export enum eContractid {
9091
MockParaSwapAugustus = 'MockParaSwapAugustus',
9192
MockParaSwapAugustusRegistry = 'MockParaSwapAugustusRegistry',
9293
ParaSwapLiquiditySwapAdapter = 'ParaSwapLiquiditySwapAdapter',
94+
ParaSwapRepayAdapter = "ParaSwapRepayAdapter"
9395
}
9496

9597
/*

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { task } from 'hardhat/config';
2+
3+
import { ParaSwapRepayAdapterFactory } from '../../types';
4+
import { verifyContract } from '../../helpers/contracts-helpers';
5+
import { getFirstSigner } from '../../helpers/contracts-getters';
6+
import { eContractid } from '../../helpers/types';
7+
8+
const CONTRACT_NAME = 'ParaSwapRepayAdapter';
9+
10+
task(`deploy-${CONTRACT_NAME}`, `Deploys the ${CONTRACT_NAME} contract`)
11+
.addParam('provider', 'Address of the LendingPoolAddressesProvider')
12+
.addParam('augustusRegistry', 'Address of ParaSwap AugustusRegistry')
13+
.addFlag('verify', `Verify ${CONTRACT_NAME} contract via Etherscan API.`)
14+
.setAction(async ({ provider, augustusRegistry, verify }, localBRE) => {
15+
await localBRE.run('set-DRE');
16+
17+
if (!localBRE.network.config.chainId) {
18+
throw new Error('INVALID_CHAIN_ID');
19+
}
20+
21+
console.log(`\n- ${CONTRACT_NAME} deployment`);
22+
const adapter = await new ParaSwapRepayAdapterFactory(
23+
await getFirstSigner()
24+
).deploy(provider, augustusRegistry);
25+
await adapter.deployTransaction.wait();
26+
console.log(`${CONTRACT_NAME}.address`, adapter.address);
27+
28+
if (verify) {
29+
await verifyContract(eContractid.ParaSwapRepayAdapter, adapter, [
30+
provider,
31+
augustusRegistry,
32+
]);
33+
}
34+
35+
console.log(`\tFinished ${CONTRACT_NAME} deployment`);
36+
});

test-suites/test-aave/__setup.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
deployMockParaSwapAugustus,
3131
deployMockParaSwapAugustusRegistry,
3232
deployParaSwapLiquiditySwapAdapter,
33+
deployParaSwapRepayAdapter,
3334
authorizeWETHGateway,
3435
} from '../../helpers/contracts-deployments';
3536
import { eEthereumNetwork } from '../../helpers/types';
@@ -292,6 +293,8 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => {
292293

293294
await deployParaSwapLiquiditySwapAdapter([addressesProvider.address, augustusRegistry.address]);
294295

296+
await deployParaSwapRepayAdapter([addressesProvider.address, augustusRegistry.address]);
297+
295298
await deployWalletBalancerProvider();
296299

297300
const gateWay = await deployWETHGateway([mockTokens.WETH.address]);

test-suites/test-aave/helpers/make-suite.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
getUniswapRepayAdapter,
1616
getFlashLiquidationAdapter,
1717
getParaSwapLiquiditySwapAdapter,
18+
getParaSwapRepayAdapter,
1819
} from '../../../helpers/contracts-getters';
1920
import { eEthereumNetwork, eNetwork, tEthereumAddress } from '../../../helpers/types';
2021
import { LendingPool } from '../../../types/LendingPool';
@@ -39,7 +40,7 @@ import { WETH9Mocked } from '../../../types/WETH9Mocked';
3940
import { WETHGateway } from '../../../types/WETHGateway';
4041
import { solidity } from 'ethereum-waffle';
4142
import { AaveConfig } from '../../../markets/aave';
42-
import { FlashLiquidationAdapter } from '../../../types';
43+
import { FlashLiquidationAdapter, ParaSwapRepayAdapter } from '../../../types';
4344
import { HardhatRuntimeEnvironment } from 'hardhat/types';
4445
import { usingTenderly } from '../../../helpers/tenderly-utils';
4546

@@ -71,6 +72,8 @@ export interface TestEnv {
7172
wethGateway: WETHGateway;
7273
flashLiquidationAdapter: FlashLiquidationAdapter;
7374
paraswapLiquiditySwapAdapter: ParaSwapLiquiditySwapAdapter;
75+
paraswapRepayAdapter: ParaSwapRepayAdapter;
76+
7477
}
7578

7679
let buidlerevmSnapshotId: string = '0x1';
@@ -96,6 +99,7 @@ const testEnv: TestEnv = {
9699
uniswapRepayAdapter: {} as UniswapRepayAdapter,
97100
flashLiquidationAdapter: {} as FlashLiquidationAdapter,
98101
paraswapLiquiditySwapAdapter: {} as ParaSwapLiquiditySwapAdapter,
102+
paraswapRepayAdapter: {} as ParaSwapRepayAdapter,
99103
registry: {} as LendingPoolAddressesProviderRegistry,
100104
wethGateway: {} as WETHGateway,
101105
} as TestEnv;
@@ -164,6 +168,7 @@ export async function initializeMakeSuite() {
164168
testEnv.flashLiquidationAdapter = await getFlashLiquidationAdapter();
165169

166170
testEnv.paraswapLiquiditySwapAdapter = await getParaSwapLiquiditySwapAdapter();
171+
testEnv.paraswapRepayAdapter = await getParaSwapRepayAdapter();
167172
}
168173

169174
const setSnapshot = async () => {

0 commit comments

Comments
 (0)