Introduce registry of valid Augustus addresses

Set in constructor of BaseParaSwapSellAdapter and validates before swap.
Created mock registry that only validates one address.
Changed the test fixtures to accomodate registry and added two new tests.
Updated deployment script.
Registry address left as a placeholder in package.json since not known yet.

Fixes MixBytes Warning 1.
This commit is contained in:
Jason Raymond Bell 2021-05-20 23:25:51 +01:00
parent 9d1cb50d76
commit 4fe36c8fa4
12 changed files with 138 additions and 17 deletions

View File

@ -5,6 +5,7 @@ pragma experimental ABIEncoderV2;
import {BaseParaSwapAdapter} from './BaseParaSwapAdapter.sol';
import {PercentageMath} from '../protocol/libraries/math/PercentageMath.sol';
import {IParaSwapAugustus} from '../interfaces/IParaSwapAugustus.sol';
import {IParaSwapAugustusRegistry} from '../interfaces/IParaSwapAugustusRegistry.sol';
import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
@ -16,10 +17,15 @@ import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detai
abstract contract BaseParaSwapSellAdapter is BaseParaSwapAdapter {
using PercentageMath for uint256;
IParaSwapAugustusRegistry public immutable AUGUSTUS_REGISTRY;
constructor(
ILendingPoolAddressesProvider addressesProvider
ILendingPoolAddressesProvider addressesProvider,
IParaSwapAugustusRegistry augustusRegistry
) public BaseParaSwapAdapter(addressesProvider) {
// This is only required to initialize BaseParaSwapAdapter
// Do something on Augustus registry to check the right contract was passed
require(!augustusRegistry.isValidAugustus(address(0)));
AUGUSTUS_REGISTRY = augustusRegistry;
}
/**
@ -42,6 +48,8 @@ abstract contract BaseParaSwapSellAdapter is BaseParaSwapAdapter {
uint256 amountToSwap,
uint256 minAmountToReceive
) internal returns (uint256 amountReceived) {
require(AUGUSTUS_REGISTRY.isValidAugustus(address(augustus)), 'INVALID_AUGUSTUS');
{
uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom);
uint256 toAssetDecimals = _getDecimals(assetToSwapTo);

View File

@ -4,6 +4,7 @@ pragma experimental ABIEncoderV2;
import {BaseParaSwapSellAdapter} from './BaseParaSwapSellAdapter.sol';
import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
import {IParaSwapAugustusRegistry} from '../interfaces/IParaSwapAugustusRegistry.sol';
import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
import {IERC20WithPermit} from '../interfaces/IERC20WithPermit.sol';
import {IParaSwapAugustus} from '../interfaces/IParaSwapAugustus.sol';
@ -16,8 +17,9 @@ import {ReentrancyGuard} from '../dependencies/openzeppelin/contracts/Reentrancy
*/
contract ParaSwapLiquiditySwapAdapter is BaseParaSwapSellAdapter, ReentrancyGuard {
constructor(
ILendingPoolAddressesProvider addressesProvider
) public BaseParaSwapSellAdapter(addressesProvider) {
ILendingPoolAddressesProvider addressesProvider,
IParaSwapAugustusRegistry augustusRegistry
) public BaseParaSwapSellAdapter(addressesProvider, augustusRegistry) {
// This is only required to initialize BaseParaSwapSellAdapter
}

View File

@ -0,0 +1,7 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
interface IParaSwapAugustusRegistry {
function isValidAugustus(address augustus) external view returns (bool);
}

View File

@ -8,7 +8,7 @@ import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
import {MintableERC20} from '../tokens/MintableERC20.sol';
contract MockParaSwapAugustus is IParaSwapAugustus {
MockParaSwapTokenTransferProxy _tokenTransferProxy;
MockParaSwapTokenTransferProxy immutable TOKEN_TRANSFER_PROXY;
bool _expectingSwap;
address _expectedFromToken;
address _expectedToToken;
@ -17,11 +17,11 @@ contract MockParaSwapAugustus is IParaSwapAugustus {
uint256 _receivedAmount;
constructor() public {
_tokenTransferProxy = new MockParaSwapTokenTransferProxy();
TOKEN_TRANSFER_PROXY = new MockParaSwapTokenTransferProxy();
}
function getTokenTransferProxy() external view override returns (address) {
return address(_tokenTransferProxy);
return address(TOKEN_TRANSFER_PROXY);
}
function expectSwap(
@ -50,7 +50,7 @@ contract MockParaSwapAugustus is IParaSwapAugustus {
require(toToken == _expectedToToken, 'Unexpected to token');
require(fromAmount >= _expectedFromAmountMin && fromAmount <= _expectedFromAmountMax, 'From amount out of range');
require(_receivedAmount >= toAmount, 'Received amount of tokens are less than expected');
_tokenTransferProxy.transferFrom(fromToken, msg.sender, address(this), fromAmount);
TOKEN_TRANSFER_PROXY.transferFrom(fromToken, msg.sender, address(this), fromAmount);
MintableERC20(toToken).mint(_receivedAmount);
IERC20(toToken).transfer(msg.sender, _receivedAmount);
_expectingSwap = false;

View File

@ -0,0 +1,17 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import {IParaSwapAugustusRegistry} from '../../interfaces/IParaSwapAugustusRegistry.sol';
contract MockParaSwapAugustusRegistry is IParaSwapAugustusRegistry {
address immutable AUGUSTUS;
constructor(address augustus) public {
AUGUSTUS = augustus;
}
function isValidAugustus(address augustus) external view override returns (bool) {
return augustus == AUGUSTUS;
}
}

View File

@ -36,6 +36,7 @@ import {
MockATokenFactory,
MockFlashLoanReceiverFactory,
MockParaSwapAugustusFactory,
MockParaSwapAugustusRegistryFactory,
MockStableDebtTokenFactory,
MockVariableDebtTokenFactory,
MockUniswapV2Router02Factory,
@ -620,9 +621,20 @@ export const deployMockParaSwapAugustus = async (verify?: boolean) =>
verify
);
export const deployParaSwapLiquiditySwapAdapter = async (
export const deployMockParaSwapAugustusRegistry = async (
args: [tEthereumAddress],
verify?: boolean
) =>
withSaveAndVerify(
await new MockParaSwapAugustusRegistryFactory(await getFirstSigner()).deploy(...args),
eContractid.MockParaSwapAugustusRegistry,
args,
verify
);
export const deployParaSwapLiquiditySwapAdapter = async (
args: [tEthereumAddress, tEthereumAddress],
verify?: boolean
) =>
withSaveAndVerify(
await new ParaSwapLiquiditySwapAdapterFactory(await getFirstSigner()).deploy(...args),

View File

@ -19,6 +19,7 @@ import {
MockVariableDebtTokenFactory,
MockUniswapV2Router02Factory,
MockParaSwapAugustusFactory,
MockParaSwapAugustusRegistryFactory,
ParaSwapLiquiditySwapAdapterFactory,
PriceOracleFactory,
ReserveLogicFactory,
@ -374,6 +375,14 @@ export const getMockParaSwapAugustus = async (address?: tEthereumAddress) =>
await getFirstSigner()
);
export const getMockParaSwapAugustusRegistry = async (address?: tEthereumAddress) =>
await MockParaSwapAugustusRegistryFactory.connect(
address ||
(await getDb().get(`${eContractid.MockParaSwapAugustusRegistry}.${DRE.network.name}`).value())
.address,
await getFirstSigner()
);
export const getParaSwapLiquiditySwapAdapter = async (address?: tEthereumAddress) =>
await ParaSwapLiquiditySwapAdapterFactory.connect(
address ||

View File

@ -88,6 +88,7 @@ export enum eContractid {
UniswapRepayAdapter = 'UniswapRepayAdapter',
FlashLiquidationAdapter = 'FlashLiquidationAdapter',
MockParaSwapAugustus = 'MockParaSwapAugustus',
MockParaSwapAugustusRegistry = 'MockParaSwapAugustusRegistry',
ParaSwapLiquiditySwapAdapter = 'ParaSwapLiquiditySwapAdapter',
}

View File

@ -67,7 +67,7 @@
"dev:UniswapLiquiditySwapAdapter": "hardhat --network kovan deploy-UniswapLiquiditySwapAdapter --provider 0x88757f2f99175387aB4C6a4b3067c77A695b0349 --router 0xfcd87315f0e4067070ade8682fcdbc3006631441 --weth 0xd0a1e359811322d97991e03f863a0c30c2cf029c",
"main:deployUniswapRepayAdapter": "hardhat --network main deploy-UniswapRepayAdapter --provider 0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5 --router 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D --weth 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"main:UniswapLiquiditySwapAdapter": "hardhat --network main deploy-UniswapLiquiditySwapAdapter --provider 0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5 --router 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D --weth 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"main:ParaSwapLiquiditySwapAdapter": "hardhat --network main deploy-ParaSwapLiquiditySwapAdapter --provider 0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5",
"main:ParaSwapLiquiditySwapAdapter": "hardhat --network main deploy-ParaSwapLiquiditySwapAdapter --provider 0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5 --augustusRegistry 0x0000000000000000000000000000000000000000",
"kovan:verify": "npm run hardhat:kovan verify:general -- --all --pool Aave",
"ropsten:verify": "npm run hardhat:ropsten verify:general -- --all --pool Aave",
"mainnet:verify": "npm run hardhat:main verify:general -- --all --pool Aave",

View File

@ -8,8 +8,9 @@ const CONTRACT_NAME = 'ParaSwapLiquiditySwapAdapter';
task(`deploy-${CONTRACT_NAME}`, `Deploys the ${CONTRACT_NAME} contract`)
.addParam('provider', 'Address of the LendingPoolAddressesProvider')
.addParam('augustusRegistry', 'Address of ParaSwap AugustusRegistry')
.addFlag('verify', `Verify ${CONTRACT_NAME} contract via Etherscan API.`)
.setAction(async ({ provider, verify }, localBRE) => {
.setAction(async ({ provider, augustusRegistry, verify }, localBRE) => {
await localBRE.run('set-DRE');
if (!localBRE.network.config.chainId) {
@ -19,10 +20,10 @@ task(`deploy-${CONTRACT_NAME}`, `Deploys the ${CONTRACT_NAME} contract`)
console.log(`\n- ${CONTRACT_NAME} deployment`);
const adapter = await new ParaSwapLiquiditySwapAdapterFactory(
await getFirstSigner()
).deploy(provider);
).deploy(provider, augustusRegistry);
await adapter.deployTransaction.wait();
console.log(`${CONTRACT_NAME}.address`, adapter.address);
await verifyContract(adapter.address, [provider]);
await verifyContract(adapter.address, [provider, augustusRegistry]);
console.log(`\tFinished ${CONTRACT_NAME} deployment`);
});

View File

@ -27,6 +27,7 @@ import {
deployUniswapRepayAdapter,
deployFlashLiquidationAdapter,
deployMockParaSwapAugustus,
deployMockParaSwapAugustusRegistry,
deployParaSwapLiquiditySwapAdapter,
authorizeWETHGateway,
} from '../../helpers/contracts-deployments';
@ -286,9 +287,11 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => {
await deployUniswapRepayAdapter(adapterParams);
await deployFlashLiquidationAdapter(adapterParams);
await deployMockParaSwapAugustus();
const augustus = await deployMockParaSwapAugustus();
await deployParaSwapLiquiditySwapAdapter([addressesProvider.address]);
const augustusRegistry = await deployMockParaSwapAugustusRegistry([augustus.address]);
await deployParaSwapLiquiditySwapAdapter([addressesProvider.address, augustusRegistry.address]);
await deployWalletBalancerProvider();

View File

@ -6,9 +6,13 @@ import {
getSignatureFromTypedData,
buildParaSwapLiquiditySwapParams,
} from '../../helpers/contracts-helpers';
import { getMockParaSwapAugustus } from '../../helpers/contracts-getters';
import {
getMockParaSwapAugustus,
getMockParaSwapAugustusRegistry,
} from '../../helpers/contracts-getters';
import { deployParaSwapLiquiditySwapAdapter } from '../../helpers/contracts-deployments';
import { MockParaSwapAugustus } from '../../types/MockParaSwapAugustus';
import { MockParaSwapAugustusRegistry } from '../../types/MockParaSwapAugustusRegistry';
import { Zero } from '@ethersproject/constants';
import BigNumber from 'bignumber.js';
import { DRE, evmRevert, evmSnapshot } from '../../helpers/misc-utils';
@ -23,10 +27,12 @@ const { expect } = require('chai');
makeSuite('ParaSwap adapters', (testEnv: TestEnv) => {
let mockAugustus: MockParaSwapAugustus;
let mockAugustusRegistry: MockParaSwapAugustusRegistry;
let evmSnapshotId: string;
before(async () => {
mockAugustus = await getMockParaSwapAugustus();
mockAugustusRegistry = await getMockParaSwapAugustusRegistry();
});
beforeEach(async () => {
@ -43,13 +49,25 @@ makeSuite('ParaSwap adapters', (testEnv: TestEnv) => {
const { addressesProvider } = testEnv;
await deployParaSwapLiquiditySwapAdapter([
addressesProvider.address,
mockAugustusRegistry.address,
]);
});
it('should revert if not valid addresses provider', async () => {
await expect(
deployParaSwapLiquiditySwapAdapter([
mockAugustus.address,
mockAugustus.address, // any invalid contract can be used here
mockAugustusRegistry.address,
])
).to.be.reverted;
});
it('should revert if not valid augustus registry', async () => {
const { addressesProvider } = testEnv;
await expect(
deployParaSwapLiquiditySwapAdapter([
addressesProvider.address,
mockAugustus.address, // any invalid contract can be used here
])
).to.be.reverted;
});
@ -1637,6 +1655,49 @@ makeSuite('ParaSwap adapters', (testEnv: TestEnv) => {
).to.be.revertedWith('MIN_AMOUNT_EXCEEDS_MAX_SLIPPAGE');
});
it('should revert if wrong address used for Augustus', async () => {
const { users, weth, oracle, dai, aWETH, paraswapLiquiditySwapAdapter } = testEnv;
const user = users[0].signer;
const userAddress = users[0].address;
const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10');
const daiPrice = await oracle.getAssetPrice(dai.address);
const expectedDaiAmount = await convertToCurrencyDecimals(
dai.address,
new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0)
);
await mockAugustus.expectSwap(weth.address, dai.address, amountWETHtoSwap, amountWETHtoSwap, expectedDaiAmount);
// User will swap liquidity aEth to aDai
await aWETH.connect(user).approve(paraswapLiquiditySwapAdapter.address, amountWETHtoSwap);
const mockAugustusCalldata = mockAugustus.interface.encodeFunctionData(
'swap',
[weth.address, dai.address, amountWETHtoSwap, expectedDaiAmount]
);
await expect(
paraswapLiquiditySwapAdapter.connect(user).swapAndDeposit(
weth.address,
dai.address,
amountWETHtoSwap,
expectedDaiAmount,
0,
mockAugustusCalldata,
oracle.address, // using arbitrary contract instead of mock Augustus
{
amount: 0,
deadline: 0,
v: 0,
r: '0x0000000000000000000000000000000000000000000000000000000000000000',
s: '0x0000000000000000000000000000000000000000000000000000000000000000',
}
)
).to.be.revertedWith('INVALID_AUGUSTUS');
});
it('should bubble up errors from Augustus', async () => {
const { users, weth, oracle, dai, aWETH, paraswapLiquiditySwapAdapter } = testEnv;
const user = users[0].signer;