Skip to content

Commit

Permalink
customizable selectors on uni v2 (#36)
Browse files Browse the repository at this point in the history
* customizable selectors on uni v2

* new uniSwap on bsc
  • Loading branch information
Jarvis authored Sep 16, 2021
1 parent b595e86 commit 590905b
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 41 deletions.
4 changes: 2 additions & 2 deletions contracts.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"fetchTokenBalances": "0xB8C6Ed80688a2674623D89A0AaBD3a87507B1868",
"fetchAaveDataWrapper": "0xE7314051BCFD832398e232f2c274278c44121a3f",
"swapContracts": {
"uniSwap": "0x813718C50df497BC136d5d6dfc0E0aDA8AB0C93e",
"uniSwap": "0x17303CD8334D752d6b8D53b81Eae34019C34C657",
"kyberDmm": "0xb455810945407Ad3146B74Aa3773df1cDaD61b11"
},
"lendingContracts": {
Expand All @@ -34,7 +34,7 @@
"fetchTokenBalances": "0xEB029e27F86D4FA920e214Cbb933924C6df91e83",
"fetchAaveDataWrapper": "0x9e81428F5672EDCea83F5bf58b6B2cD968df8EE6",
"swapContracts": {
"uniSwap": "0xf5a40993852e6C47B4981bEAC1659BF09B83C521",
"uniSwap": "0xf2A5A8EA6794aaEaBb1d86Dfd6664c0075bf1a17",
"kyberDmm": "0x029B41cD39Ccb4131807AaB24C2ae5c948Da56f9"
},
"lendingContracts": {
Expand Down
105 changes: 74 additions & 31 deletions contracts/swap/UniSwap.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,21 @@ contract UniSwap is BaseSwap {
using BytesLib for bytes;

EnumerableSet.AddressSet private uniRouters;
address public wEth;
mapping(address => bytes4) public customSwapFromEth;
mapping(address => bytes4) public customSwapToEth;

event UpdatedUniRouters(IUniswapV2Router02[] routers, bool isSupported);

constructor(address _admin, IUniswapV2Router02[] memory routers) BaseSwap(_admin) {
constructor(
address _admin,
IUniswapV2Router02[] memory routers,
address _weth
) BaseSwap(_admin) {
for (uint256 i = 0; i < routers.length; i++) {
uniRouters.add(address(routers[i]));
}
wEth = _weth;
}

function getAllUniRouters() external view returns (address[] memory addresses) {
Expand All @@ -36,6 +44,15 @@ contract UniSwap is BaseSwap {
}
}

function updateCustomSwapSelector(
address _router,
bytes4 _swapFromEth,
bytes4 _swapToEth
) external onlyAdmin {
customSwapFromEth[_router] = _swapFromEth;
customSwapToEth[_router] = _swapToEth;
}

function updateUniRouters(IUniswapV2Router02[] calldata routers, bool isSupported)
external
onlyAdmin
Expand All @@ -58,8 +75,11 @@ contract UniSwap is BaseSwap {
onlyProxyContract
returns (uint256 destAmount)
{
IUniswapV2Router02 router = parseExtraArgs(params.extraArgs);
uint256[] memory amounts = router.getAmountsOut(params.srcAmount, params.tradePath);
address router = parseExtraArgs(params.extraArgs);
uint256[] memory amounts = IUniswapV2Router02(router).getAmountsOut(
params.srcAmount,
params.tradePath
);
destAmount = amounts[params.tradePath.length - 1];
}

Expand All @@ -70,8 +90,11 @@ contract UniSwap is BaseSwap {
onlyProxyContract
returns (uint256 srcAmount)
{
IUniswapV2Router02 router = parseExtraArgs(params.extraArgs);
uint256[] memory amounts = router.getAmountsIn(params.destAmount, params.tradePath);
address router = parseExtraArgs(params.extraArgs);
uint256[] memory amounts = IUniswapV2Router02(router).getAmountsIn(
params.destAmount,
params.tradePath
);
srcAmount = amounts[0];
}

Expand All @@ -88,9 +111,9 @@ contract UniSwap is BaseSwap {
{
require(params.tradePath.length >= 2, "invalid tradePath");

IUniswapV2Router02 router = parseExtraArgs(params.extraArgs);
address router = parseExtraArgs(params.extraArgs);

safeApproveAllowance(address(router), IERC20Ext(params.tradePath[0]));
safeApproveAllowance(router, IERC20Ext(params.tradePath[0]));

uint256 tradeLen = params.tradePath.length;
IERC20Ext actualSrc = IERC20Ext(params.tradePath[0]);
Expand All @@ -99,35 +122,59 @@ contract UniSwap is BaseSwap {
// convert eth/bnb -> weth/wbnb address to trade on Uni
address[] memory convertedTradePath = params.tradePath;
if (convertedTradePath[0] == address(ETH_TOKEN_ADDRESS)) {
convertedTradePath[0] = router.WETH();
convertedTradePath[0] = wEth;
}
if (convertedTradePath[tradeLen - 1] == address(ETH_TOKEN_ADDRESS)) {
convertedTradePath[tradeLen - 1] = router.WETH();
convertedTradePath[tradeLen - 1] = wEth;
}

uint256 destBalanceBefore = getBalance(actualDest, params.recipient);

if (actualSrc == ETH_TOKEN_ADDRESS) {
// swap eth/bnb -> token
router.swapExactETHForTokensSupportingFeeOnTransferTokens{value: params.srcAmount}(
params.minDestAmount,
convertedTradePath,
params.recipient,
MAX_AMOUNT
);
if (customSwapFromEth[address(router)] != "") {
(bool success, ) = router.call{value: params.srcAmount}(
abi.encodeWithSelector(
customSwapFromEth[address(router)],
params.minDestAmount,
convertedTradePath,
params.recipient,
MAX_AMOUNT
)
);
require(success, "swapFromEth: failed");
} else {
IUniswapV2Router02(router).swapExactETHForTokensSupportingFeeOnTransferTokens{
value: params.srcAmount
}(params.minDestAmount, convertedTradePath, params.recipient, MAX_AMOUNT);
}
} else {
if (actualDest == ETH_TOKEN_ADDRESS) {
// swap token -> eth/bnb
router.swapExactTokensForETHSupportingFeeOnTransferTokens(
params.srcAmount,
params.minDestAmount,
convertedTradePath,
params.recipient,
MAX_AMOUNT
);
if (customSwapToEth[address(router)] != "") {
(bool success, ) = router.call(
abi.encodeWithSelector(
customSwapToEth[address(router)],
params.srcAmount,
params.minDestAmount,
convertedTradePath,
params.recipient,
MAX_AMOUNT
)
);
require(success, "swapToEth: failed");
} else {
IUniswapV2Router02(router).swapExactTokensForETHSupportingFeeOnTransferTokens(
params.srcAmount,
params.minDestAmount,
convertedTradePath,
params.recipient,
MAX_AMOUNT
);
}
} else {
// swap token -> token
router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
IUniswapV2Router02(router).swapExactTokensForTokensSupportingFeeOnTransferTokens(
params.srcAmount,
params.minDestAmount,
convertedTradePath,
Expand All @@ -141,14 +188,10 @@ contract UniSwap is BaseSwap {
}

/// @param extraArgs expecting <[20B] address router>
function parseExtraArgs(bytes calldata extraArgs)
internal
view
returns (IUniswapV2Router02 router)
{
function parseExtraArgs(bytes calldata extraArgs) internal view returns (address router) {
require(extraArgs.length == 20, "invalid args");
router = IUniswapV2Router02(extraArgs.toAddress(0));
require(router != IUniswapV2Router02(0), "invalid address");
require(uniRouters.contains(address(router)), "unsupported router");
router = extraArgs.toAddress(0);
require(router != address(0), "invalid address");
require(uniRouters.contains(router), "unsupported router");
}
}
12 changes: 11 additions & 1 deletion scripts/config_bsc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,21 @@ export const BscConfig: Record<string, IConfig> = {
// Pancake swap
uniswap: {
routers: [
'0x10ed43c718714eb63d5aa57b78b54704e256024e', // panceke v2
'0x10ed43c718714eb63d5aa57b78b54704e256024e', // pancake v2
'0x05ff2b0db69458a0750badebc4f9e13add608c7f', // pancake v1
'0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506', // sushiswap
'0xcF0feBd3f17CEf5b47b0cD257aCf6025c5BFf3b7', // apeswap
'0x7DAe51BD3E3376B8c7c4900E9107f12Be3AF1bA8', // mdex
'0x325E343f1dE602396E256B67eFd1F61C3A6B38Bd', // babyswap
'0xCDe540d7eAFE93aC5fE6233Bee57E1270D3E330F', // bakeryswap
],
customSelectors: {
// bakeryswap
'0xCDe540d7eAFE93aC5fE6233Bee57E1270D3E330F': {
swapFromEth: 'swapExactBNBForTokensSupportingFeeOnTransferTokens(uint256,address[],address,uint256)',
swapToEth: 'swapExactTokensForBNBSupportingFeeOnTransferTokens(uint256,uint256,address[],address,uint256)',
},
},
},

kyberDmm: {
Expand Down
7 changes: 7 additions & 0 deletions scripts/config_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ export interface IConfig {
// Uniswap or clones
uniswap?: {
routers: string[];
customSelectors?: Record<
string,
{
swapFromEth: string;
swapToEth: string;
}
>;
};

uniswapV3?: {
Expand Down
22 changes: 20 additions & 2 deletions scripts/deployLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
} from '../typechain';
import {Contract} from '@ethersproject/contracts';
import {IAaveV2Config} from './config_utils';
import {sleep, zeroAddress} from '../test/helper';
import {equalHex, sleep, zeroAddress} from '../test/helper';
import {PopulatedTransaction} from 'ethers';
import {TransactionRequest} from '@ethersproject/abstract-provider';
import {multisig} from '../hardhat.config';
Expand Down Expand Up @@ -173,7 +173,8 @@ async function deployContracts(
existingContract?.['swapContracts']?.['uniSwap'],
undefined,
contractAdmin,
networkConfig.uniswap.routers
networkConfig.uniswap.routers,
networkConfig.wNative
)) as UniSwap),
uniSwapV3: !networkConfig.uniswapV3
? undefined
Expand Down Expand Up @@ -503,6 +504,23 @@ async function updateUniSwap(uniSwap: UniSwap | undefined, extraArgs: {from?: st
let toBeRemoved = existing.filter((add) => !configRouters.includes(add));
let toBeAdded = configRouters.filter((add) => !existing.includes(add));
await updateAddressSet(uniSwap.populateTransaction.updateUniRouters, toBeRemoved, toBeAdded, extraArgs);

log(1, 'update custom selectors');
for (const [router, {swapFromEth, swapToEth}] of Object.entries(networkConfig.uniswap.customSelectors ?? {})) {
let swapFromEthSelector = ethers.utils.solidityKeccak256(['string'], [swapFromEth]).slice(0, 10);
let swapToEthSelector = ethers.utils.solidityKeccak256(['string'], [swapToEth]).slice(0, 10);

let selector1 = await uniSwap.customSwapFromEth(router);
let selector2 = await uniSwap.customSwapToEth(router);

if (!equalHex(selector1, swapFromEth) || !equalHex(selector2, swapToEth)) {
const tx = await executeTxnOnBehalfOf(
await uniSwap.populateTransaction.updateCustomSwapSelector(router, swapFromEthSelector, swapToEthSelector)
);
log(2, '> Updating selectors:', router, swapFromEthSelector, swapToEthSelector);
await printInfo(tx);
}
}
}

async function updateUniSwapV3(uniSwapV3: UniSwapV3 | undefined, extraArgs: {from?: string}) {
Expand Down
4 changes: 4 additions & 0 deletions test/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,7 @@ export const sleep = (timeout: number) => {
}, timeout);
});
};

export const equalHex = (a: string, b: string) => {
return a.toLowerCase() === b.toLowerCase();
};
2 changes: 1 addition & 1 deletion test/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const setupContracts = async (accounts: SignerWithAddress[]) => {

let proxyInstance = (await ethers.getContractAt(
'SmartWalletImplementation',
krystalContracts.smartWalletProxy.address
krystalContracts.smartWalletProxy!.address
)) as SmartWalletImplementation;

// Fund wallet
Expand Down
8 changes: 4 additions & 4 deletions test/swap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ describe('swap test', async () => {
executeSwapTest(
'univ2/clones',
async () => {
return setup.krystalContracts.swapContracts.uniSwap!.address;
return setup.krystalContracts.swapContracts!.uniSwap!.address;
},
router,
async () => hexlify(arrayify(router)),
Expand All @@ -278,7 +278,7 @@ describe('swap test', async () => {
executeSwapTest(
'uniV3',
async () => {
return setup.krystalContracts.swapContracts.uniSwapV3!.address;
return setup.krystalContracts.swapContracts!.uniSwapV3!.address;
},
router,
async (tradePath: string[]) => {
Expand Down Expand Up @@ -309,7 +309,7 @@ describe('swap test', async () => {
executeSwapTest(
'kyberProxy',
async () => {
return setup.krystalContracts.swapContracts.kyberProxy!.address;
return setup.krystalContracts.swapContracts!.kyberProxy!.address;
},
networkSetting.kyberProxy.proxy,
async () => '0x', // empty hint
Expand Down Expand Up @@ -346,7 +346,7 @@ describe('swap test', async () => {
executeSwapTest(
'kyberDmm',
async () => {
return setup.krystalContracts.swapContracts.kyberDmm!.address;
return setup.krystalContracts.swapContracts!.kyberDmm!.address;
},
networkSetting.kyberDmm.router,
async (tradePath: string[]) => {
Expand Down

0 comments on commit 590905b

Please sign in to comment.