Skip to content

Commit

Permalink
adding the swapper contract back (#15)
Browse files Browse the repository at this point in the history
* adding the swapper contract back

* changing with new commands
  • Loading branch information
sogipec authored Nov 25, 2022
1 parent 49dcb86 commit fcb7c7e
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 0 deletions.
33 changes: 33 additions & 0 deletions contracts/BaseRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import "./interfaces/external/IWETH9.sol";
import "./interfaces/ICoreBorrow.sol";
import "./interfaces/ILiquidityGauge.sol";
import "./interfaces/ISavingsRateIlliquid.sol";
import "./interfaces/ISwapper.sol";
import "./interfaces/IVaultManager.sol";

// ============================== STRUCTS AND ENUM =============================
Expand All @@ -28,6 +29,7 @@ enum ActionType {
claimRewards,
gaugeDeposit,
borrower,
swapper,
mintSavingsRate,
depositSavingsRate,
redeemSavingsRate,
Expand Down Expand Up @@ -198,6 +200,17 @@ abstract contract BaseRouter is Initializable {
_changeAllowance(IERC20(collateral), address(vaultManager), type(uint256).max);
_angleBorrower(vaultManager, actionsBorrow, dataBorrow, to, who, repayData);
_changeAllowance(IERC20(collateral), address(vaultManager), 0);
} else if (actions[i] == ActionType.swapper) {
(
ISwapper swapperContract,
IERC20 inToken,
IERC20 outToken,
address outTokenRecipient,
uint256 outTokenOwed,
uint256 inTokenObtained,
bytes memory payload
) = abi.decode(data[i], (ISwapper, IERC20, IERC20, address, uint256, uint256, bytes));
_swapper(swapperContract, inToken, outToken, outTokenRecipient, outTokenOwed, inTokenObtained, payload);
} else if (actions[i] == ActionType.mintSavingsRate) {
(IERC20 token, IERC4626 savingsRate, uint256 shares, address to, uint256 maxAmountIn) = abi.decode(
data[i],
Expand Down Expand Up @@ -373,6 +386,26 @@ abstract contract BaseRouter is Initializable {
}
}

/// @notice Uses an external swapper
/// @param swapper Contracts implementing the logic of the swap
/// @param inToken Token used to do the swap
/// @param outToken Token wanted
/// @param outTokenRecipient Address who should have at the end of the swap at least `outTokenOwed`
/// @param outTokenOwed Minimal amount for the `outTokenRecipient`
/// @param inTokenObtained Amount of `inToken` used for the swap
/// @param data Additional info for the specific swapper
function _swapper(
ISwapper swapper,
IERC20 inToken,
IERC20 outToken,
address outTokenRecipient,
uint256 outTokenOwed,
uint256 inTokenObtained,
bytes memory data
) internal {
swapper.swap(inToken, outToken, outTokenRecipient, outTokenOwed, inTokenObtained, data);
}

/// @notice Allows to swap between tokens via UniswapV3 (if there is a path)
/// @param inToken Token used as entrance of the swap
/// @param amount Amount of in token to swap
Expand Down
19 changes: 19 additions & 0 deletions contracts/interfaces/ISwapper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.17;

import "@openzeppelin/contracts/interfaces/IERC20.sol";

/// @title ISwapper
/// @author Angle Core Team
/// @notice Interface for a generic swapper, that supports swaps of higher complexity than aggregators
interface ISwapper {
function swap(
IERC20 inToken,
IERC20 outToken,
address outTokenRecipient,
uint256 outTokenOwed,
uint256 inTokenObtained,
bytes memory data
) external;
}
22 changes: 22 additions & 0 deletions contracts/mock/MockSwapper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.17;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

contract MockSwapper {
using SafeERC20 for IERC20;

function swap(
IERC20 inToken,
IERC20 outToken,
address outTokenRecipient,
uint256 outTokenOwed,
uint256 inTokenObtained,
bytes memory
) external {
inToken.safeTransferFrom(msg.sender, address(this), inTokenObtained);
outToken.safeTransfer(outTokenRecipient, outTokenOwed);
}
}
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
"foundry:setup": "curl -L https://foundry.paradigm.xyz | bash && foundryup && git submodule update --init --recursive",
"deploy": "hardhat deploy --network",
"etherscan": "hardhat etherscan-verify --network",
"foundry:test": "forge test -vvvv",
"hardhat:compile": "hardhat compile",
"hardhat:test": "hardhat test",
"license": "hardhat prepend-spdx-license",
"lint": "yarn lint:sol && yarn lint:js:fix",
"lint:js:fix": "eslint --ignore-path .gitignore --fix --max-warnings 30 'test/**/*.{js,ts}' '*.{js,ts}'",
Expand Down
41 changes: 41 additions & 0 deletions test/hardhat/router/baseRouter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
MockLiquidityGauge__factory,
MockRouterSidechain,
MockRouterSidechain__factory,
MockSwapper,
MockSwapper__factory,
MockTokenPermit,
MockTokenPermit__factory,
MockUniswapV3Router,
Expand All @@ -37,6 +39,7 @@ contract('BaseRouter', () => {
let uniswap: MockUniswapV3Router;
let oneInch: Mock1Inch;
let router: MockRouterSidechain;
let swapper: MockSwapper;
let UNIT_USDC: BigNumber;
let USDCdecimal: BigNumber;
let governor: string;
Expand Down Expand Up @@ -81,6 +84,7 @@ contract('BaseRouter', () => {
await core.toggleGovernor(governor);
await core.toggleGuardian(governor);
await core.toggleGuardian(guardian);
swapper = (await new MockSwapper__factory(deployer).deploy()) as MockSwapper;
await router.initializeRouter(core.address, uniswap.address, oneInch.address);
});
describe('initializeRouter', () => {
Expand Down Expand Up @@ -453,6 +457,43 @@ contract('BaseRouter', () => {
await router.connect(alice).mixer(permits, actions, dataMixer);
});
});
describe('swapper', () => {
it('reverts - when not enough balance', async () => {
const actions = [ActionType.swapper];
const swapperData = ethers.utils.defaultAbiCoder.encode(
['address', 'address', 'address', 'address', 'uint256', 'uint256', 'bytes'],
[swapper.address, USDC.address, agEUR.address, bob.address, parseEther('1'), parseUnits('1', 6), '0x'],
);
const dataMixer = [swapperData];
await expect(router.connect(alice).mixer(permits, actions, dataMixer)).to.be.reverted;
await router
.connect(impersonatedSigners[governor])
.changeAllowance([USDC.address], [swapper.address], [MAX_UINT256]);
await expect(router.connect(alice).mixer(permits, actions, dataMixer)).to.be.reverted;
});
it('success - when enough balance', async () => {
await USDC.mint(alice.address, parseUnits('1', USDCdecimal));
await USDC.connect(alice).approve(router.address, parseUnits('1', USDCdecimal));
await agEUR.mint(swapper.address, parseEther('1'));
await router
.connect(impersonatedSigners[governor])
.changeAllowance([USDC.address], [swapper.address], [MAX_UINT256]);
const transferData = ethers.utils.defaultAbiCoder.encode(
['address', 'address', 'uint256'],
[USDC.address, router.address, parseUnits('1', USDCdecimal)],
);

const swapperData = ethers.utils.defaultAbiCoder.encode(
['address', 'address', 'address', 'address', 'uint256', 'uint256', 'bytes'],
[swapper.address, USDC.address, agEUR.address, bob.address, parseEther('1'), parseUnits('1', 6), '0x'],
);
const actions = [ActionType.transfer, ActionType.swapper];
const dataMixer = [transferData, swapperData];
await router.connect(alice).mixer(permits, actions, dataMixer);
expect(await USDC.balanceOf(swapper.address)).to.be.equal(parseUnits('1', 6));
expect(await agEUR.balanceOf(bob.address)).to.be.equal(parseEther('1'));
});
});
describe('claimRewards', () => {
it('success - claiming rewards from a gauge', async () => {
const gauge = (await new MockLiquidityGauge__factory(deployer).deploy(USDC.address)) as MockLiquidityGauge;
Expand Down
1 change: 1 addition & 0 deletions utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export enum ActionType {
claimRewards,
gaugeDeposit,
borrower,
swapper,
mintSavingsRate,
depositSavingsRate,
redeemSavingsRate,
Expand Down

0 comments on commit fcb7c7e

Please sign in to comment.