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 {BaseParaSwapAdapter} from './BaseParaSwapAdapter.sol';
import {PercentageMath} from '../protocol/libraries/math/PercentageMath.sol'; import {PercentageMath} from '../protocol/libraries/math/PercentageMath.sol';
import {IParaSwapAugustus} from '../interfaces/IParaSwapAugustus.sol'; import {IParaSwapAugustus} from '../interfaces/IParaSwapAugustus.sol';
import {IParaSwapAugustusRegistry} from '../interfaces/IParaSwapAugustusRegistry.sol';
import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.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 { abstract contract BaseParaSwapSellAdapter is BaseParaSwapAdapter {
using PercentageMath for uint256; using PercentageMath for uint256;
IParaSwapAugustusRegistry public immutable AUGUSTUS_REGISTRY;
constructor( constructor(
ILendingPoolAddressesProvider addressesProvider ILendingPoolAddressesProvider addressesProvider,
IParaSwapAugustusRegistry augustusRegistry
) public BaseParaSwapAdapter(addressesProvider) { ) 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 amountToSwap,
uint256 minAmountToReceive uint256 minAmountToReceive
) internal returns (uint256 amountReceived) { ) internal returns (uint256 amountReceived) {
require(AUGUSTUS_REGISTRY.isValidAugustus(address(augustus)), 'INVALID_AUGUSTUS');
{ {
uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom); uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom);
uint256 toAssetDecimals = _getDecimals(assetToSwapTo); uint256 toAssetDecimals = _getDecimals(assetToSwapTo);

View File

@ -4,6 +4,7 @@ pragma experimental ABIEncoderV2;
import {BaseParaSwapSellAdapter} from './BaseParaSwapSellAdapter.sol'; import {BaseParaSwapSellAdapter} from './BaseParaSwapSellAdapter.sol';
import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
import {IParaSwapAugustusRegistry} from '../interfaces/IParaSwapAugustusRegistry.sol';
import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol'; import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
import {IERC20WithPermit} from '../interfaces/IERC20WithPermit.sol'; import {IERC20WithPermit} from '../interfaces/IERC20WithPermit.sol';
import {IParaSwapAugustus} from '../interfaces/IParaSwapAugustus.sol'; import {IParaSwapAugustus} from '../interfaces/IParaSwapAugustus.sol';
@ -16,8 +17,9 @@ import {ReentrancyGuard} from '../dependencies/openzeppelin/contracts/Reentrancy
*/ */
contract ParaSwapLiquiditySwapAdapter is BaseParaSwapSellAdapter, ReentrancyGuard { contract ParaSwapLiquiditySwapAdapter is BaseParaSwapSellAdapter, ReentrancyGuard {
constructor( constructor(
ILendingPoolAddressesProvider addressesProvider ILendingPoolAddressesProvider addressesProvider,
) public BaseParaSwapSellAdapter(addressesProvider) { IParaSwapAugustusRegistry augustusRegistry
) public BaseParaSwapSellAdapter(addressesProvider, augustusRegistry) {
// This is only required to initialize BaseParaSwapSellAdapter // 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'; import {MintableERC20} from '../tokens/MintableERC20.sol';
contract MockParaSwapAugustus is IParaSwapAugustus { contract MockParaSwapAugustus is IParaSwapAugustus {
MockParaSwapTokenTransferProxy _tokenTransferProxy; MockParaSwapTokenTransferProxy immutable TOKEN_TRANSFER_PROXY;
bool _expectingSwap; bool _expectingSwap;
address _expectedFromToken; address _expectedFromToken;
address _expectedToToken; address _expectedToToken;
@ -17,11 +17,11 @@ contract MockParaSwapAugustus is IParaSwapAugustus {
uint256 _receivedAmount; uint256 _receivedAmount;
constructor() public { constructor() public {
_tokenTransferProxy = new MockParaSwapTokenTransferProxy(); TOKEN_TRANSFER_PROXY = new MockParaSwapTokenTransferProxy();
} }
function getTokenTransferProxy() external view override returns (address) { function getTokenTransferProxy() external view override returns (address) {
return address(_tokenTransferProxy); return address(TOKEN_TRANSFER_PROXY);
} }
function expectSwap( function expectSwap(
@ -50,7 +50,7 @@ contract MockParaSwapAugustus is IParaSwapAugustus {
require(toToken == _expectedToToken, 'Unexpected to token'); require(toToken == _expectedToToken, 'Unexpected to token');
require(fromAmount >= _expectedFromAmountMin && fromAmount <= _expectedFromAmountMax, 'From amount out of range'); require(fromAmount >= _expectedFromAmountMin && fromAmount <= _expectedFromAmountMax, 'From amount out of range');
require(_receivedAmount >= toAmount, 'Received amount of tokens are less than expected'); 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); MintableERC20(toToken).mint(_receivedAmount);
IERC20(toToken).transfer(msg.sender, _receivedAmount); IERC20(toToken).transfer(msg.sender, _receivedAmount);
_expectingSwap = false; _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, MockATokenFactory,
MockFlashLoanReceiverFactory, MockFlashLoanReceiverFactory,
MockParaSwapAugustusFactory, MockParaSwapAugustusFactory,
MockParaSwapAugustusRegistryFactory,
MockStableDebtTokenFactory, MockStableDebtTokenFactory,
MockVariableDebtTokenFactory, MockVariableDebtTokenFactory,
MockUniswapV2Router02Factory, MockUniswapV2Router02Factory,
@ -620,9 +621,20 @@ export const deployMockParaSwapAugustus = async (verify?: boolean) =>
verify verify
); );
export const deployParaSwapLiquiditySwapAdapter = async ( export const deployMockParaSwapAugustusRegistry = async (
args: [tEthereumAddress], args: [tEthereumAddress],
verify?: boolean verify?: boolean
) =>
withSaveAndVerify(
await new MockParaSwapAugustusRegistryFactory(await getFirstSigner()).deploy(...args),
eContractid.MockParaSwapAugustusRegistry,
args,
verify
);
export const deployParaSwapLiquiditySwapAdapter = async (
args: [tEthereumAddress, tEthereumAddress],
verify?: boolean
) => ) =>
withSaveAndVerify( withSaveAndVerify(
await new ParaSwapLiquiditySwapAdapterFactory(await getFirstSigner()).deploy(...args), await new ParaSwapLiquiditySwapAdapterFactory(await getFirstSigner()).deploy(...args),

View File

@ -19,6 +19,7 @@ import {
MockVariableDebtTokenFactory, MockVariableDebtTokenFactory,
MockUniswapV2Router02Factory, MockUniswapV2Router02Factory,
MockParaSwapAugustusFactory, MockParaSwapAugustusFactory,
MockParaSwapAugustusRegistryFactory,
ParaSwapLiquiditySwapAdapterFactory, ParaSwapLiquiditySwapAdapterFactory,
PriceOracleFactory, PriceOracleFactory,
ReserveLogicFactory, ReserveLogicFactory,
@ -374,6 +375,14 @@ export const getMockParaSwapAugustus = async (address?: tEthereumAddress) =>
await getFirstSigner() 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) => export const getParaSwapLiquiditySwapAdapter = async (address?: tEthereumAddress) =>
await ParaSwapLiquiditySwapAdapterFactory.connect( await ParaSwapLiquiditySwapAdapterFactory.connect(
address || address ||

View File

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

View File

@ -67,7 +67,7 @@
"dev:UniswapLiquiditySwapAdapter": "hardhat --network kovan deploy-UniswapLiquiditySwapAdapter --provider 0x88757f2f99175387aB4C6a4b3067c77A695b0349 --router 0xfcd87315f0e4067070ade8682fcdbc3006631441 --weth 0xd0a1e359811322d97991e03f863a0c30c2cf029c", "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: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: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", "kovan:verify": "npm run hardhat:kovan verify:general -- --all --pool Aave",
"ropsten:verify": "npm run hardhat:ropsten 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", "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`) task(`deploy-${CONTRACT_NAME}`, `Deploys the ${CONTRACT_NAME} contract`)
.addParam('provider', 'Address of the LendingPoolAddressesProvider') .addParam('provider', 'Address of the LendingPoolAddressesProvider')
.addParam('augustusRegistry', 'Address of ParaSwap AugustusRegistry')
.addFlag('verify', `Verify ${CONTRACT_NAME} contract via Etherscan API.`) .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'); await localBRE.run('set-DRE');
if (!localBRE.network.config.chainId) { if (!localBRE.network.config.chainId) {
@ -19,10 +20,10 @@ task(`deploy-${CONTRACT_NAME}`, `Deploys the ${CONTRACT_NAME} contract`)
console.log(`\n- ${CONTRACT_NAME} deployment`); console.log(`\n- ${CONTRACT_NAME} deployment`);
const adapter = await new ParaSwapLiquiditySwapAdapterFactory( const adapter = await new ParaSwapLiquiditySwapAdapterFactory(
await getFirstSigner() await getFirstSigner()
).deploy(provider); ).deploy(provider, augustusRegistry);
await adapter.deployTransaction.wait(); await adapter.deployTransaction.wait();
console.log(`${CONTRACT_NAME}.address`, adapter.address); console.log(`${CONTRACT_NAME}.address`, adapter.address);
await verifyContract(adapter.address, [provider]); await verifyContract(adapter.address, [provider, augustusRegistry]);
console.log(`\tFinished ${CONTRACT_NAME} deployment`); console.log(`\tFinished ${CONTRACT_NAME} deployment`);
}); });

View File

@ -27,6 +27,7 @@ import {
deployUniswapRepayAdapter, deployUniswapRepayAdapter,
deployFlashLiquidationAdapter, deployFlashLiquidationAdapter,
deployMockParaSwapAugustus, deployMockParaSwapAugustus,
deployMockParaSwapAugustusRegistry,
deployParaSwapLiquiditySwapAdapter, deployParaSwapLiquiditySwapAdapter,
authorizeWETHGateway, authorizeWETHGateway,
} from '../../helpers/contracts-deployments'; } from '../../helpers/contracts-deployments';
@ -286,9 +287,11 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => {
await deployUniswapRepayAdapter(adapterParams); await deployUniswapRepayAdapter(adapterParams);
await deployFlashLiquidationAdapter(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(); await deployWalletBalancerProvider();

View File

@ -6,9 +6,13 @@ import {
getSignatureFromTypedData, getSignatureFromTypedData,
buildParaSwapLiquiditySwapParams, buildParaSwapLiquiditySwapParams,
} from '../../helpers/contracts-helpers'; } from '../../helpers/contracts-helpers';
import { getMockParaSwapAugustus } from '../../helpers/contracts-getters'; import {
getMockParaSwapAugustus,
getMockParaSwapAugustusRegistry,
} from '../../helpers/contracts-getters';
import { deployParaSwapLiquiditySwapAdapter } from '../../helpers/contracts-deployments'; import { deployParaSwapLiquiditySwapAdapter } from '../../helpers/contracts-deployments';
import { MockParaSwapAugustus } from '../../types/MockParaSwapAugustus'; import { MockParaSwapAugustus } from '../../types/MockParaSwapAugustus';
import { MockParaSwapAugustusRegistry } from '../../types/MockParaSwapAugustusRegistry';
import { Zero } from '@ethersproject/constants'; import { Zero } from '@ethersproject/constants';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { DRE, evmRevert, evmSnapshot } from '../../helpers/misc-utils'; import { DRE, evmRevert, evmSnapshot } from '../../helpers/misc-utils';
@ -23,10 +27,12 @@ const { expect } = require('chai');
makeSuite('ParaSwap adapters', (testEnv: TestEnv) => { makeSuite('ParaSwap adapters', (testEnv: TestEnv) => {
let mockAugustus: MockParaSwapAugustus; let mockAugustus: MockParaSwapAugustus;
let mockAugustusRegistry: MockParaSwapAugustusRegistry;
let evmSnapshotId: string; let evmSnapshotId: string;
before(async () => { before(async () => {
mockAugustus = await getMockParaSwapAugustus(); mockAugustus = await getMockParaSwapAugustus();
mockAugustusRegistry = await getMockParaSwapAugustusRegistry();
}); });
beforeEach(async () => { beforeEach(async () => {
@ -43,13 +49,25 @@ makeSuite('ParaSwap adapters', (testEnv: TestEnv) => {
const { addressesProvider } = testEnv; const { addressesProvider } = testEnv;
await deployParaSwapLiquiditySwapAdapter([ await deployParaSwapLiquiditySwapAdapter([
addressesProvider.address, addressesProvider.address,
mockAugustusRegistry.address,
]); ]);
}); });
it('should revert if not valid addresses provider', async () => { it('should revert if not valid addresses provider', async () => {
await expect( await expect(
deployParaSwapLiquiditySwapAdapter([ 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; ).to.be.reverted;
}); });
@ -1637,6 +1655,49 @@ makeSuite('ParaSwap adapters', (testEnv: TestEnv) => {
).to.be.revertedWith('MIN_AMOUNT_EXCEEDS_MAX_SLIPPAGE'); ).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 () => { it('should bubble up errors from Augustus', async () => {
const { users, weth, oracle, dai, aWETH, paraswapLiquiditySwapAdapter } = testEnv; const { users, weth, oracle, dai, aWETH, paraswapLiquiditySwapAdapter } = testEnv;
const user = users[0].signer; const user = users[0].signer;