add route test framework
This commit is contained in:
36
contracts/SmartRoute/SmartApprove.sol
Normal file
36
contracts/SmartRoute/SmartApprove.sol
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 DODO ZOO.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.6.9;
|
||||
|
||||
import {IERC20} from "../intf/IERC20.sol";
|
||||
import {SafeERC20} from "../lib/SafeERC20.sol";
|
||||
import {Ownable} from "../lib/Ownable.sol";
|
||||
|
||||
|
||||
contract SmartApprove is Ownable {
|
||||
using SafeERC20 for IERC20;
|
||||
address public smartSwap;
|
||||
|
||||
function setSmartSwap(address _smartSwap) external onlyOwner {
|
||||
smartSwap = _smartSwap;
|
||||
}
|
||||
|
||||
|
||||
event Test1(uint256 amount);
|
||||
|
||||
function claimTokens(
|
||||
IERC20 token,
|
||||
address who,
|
||||
address dest,
|
||||
uint256 amount
|
||||
) external {
|
||||
require(msg.sender == smartSwap, "Not SmartSwap Address, Access restricted");
|
||||
emit Test1(amount);
|
||||
// token.safeTransferFrom(who, dest, amount);
|
||||
}
|
||||
}
|
||||
118
contracts/SmartRoute/SmartSwap.sol
Normal file
118
contracts/SmartRoute/SmartSwap.sol
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 DODO ZOO.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.6.9;
|
||||
|
||||
import {Ownable} from "../lib/Ownable.sol";
|
||||
import {ExternalCall} from "../lib/ExternalCall.sol";
|
||||
import {IERC20} from "../intf/IERC20.sol";
|
||||
import {UniversalERC20} from "../lib/UniversalERC20.sol";
|
||||
import {SafeMath} from "../lib/SafeMath.sol";
|
||||
import {DecimalMath} from "../lib/DecimalMath.sol";
|
||||
import {ISmartApprove} from "../intf/ISmartApprove.sol";
|
||||
|
||||
|
||||
contract SmartSwap is Ownable {
|
||||
using SafeMath for uint256;
|
||||
using UniversalERC20 for IERC20;
|
||||
using ExternalCall for address;
|
||||
|
||||
ISmartApprove public smartApprove;
|
||||
|
||||
IERC20 constant ETH_ADDRESS = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
|
||||
|
||||
event Swapped(
|
||||
IERC20 indexed fromToken,
|
||||
IERC20 indexed toToken,
|
||||
address indexed sender,
|
||||
uint256 fromAmount,
|
||||
uint256 toAmount
|
||||
);
|
||||
|
||||
event ExternalRecord(address indexed to, address indexed sender);
|
||||
|
||||
constructor(address _smartApprove) public {
|
||||
smartApprove = ISmartApprove(_smartApprove);
|
||||
}
|
||||
|
||||
|
||||
function dodoSwap(
|
||||
IERC20 fromToken,
|
||||
IERC20 toToken,
|
||||
uint256 fromTokenAmount,
|
||||
uint256 minReturnAmount,
|
||||
address[] memory callPairs,
|
||||
bytes memory callDataConcat,
|
||||
uint256[] memory starts,
|
||||
uint256[] memory gasLimitsAndValues
|
||||
) public payable returns (uint256 returnAmount) {
|
||||
require(minReturnAmount > 0, "haha hihi Min return should be bigger then 0.");
|
||||
require(callPairs.length > 0, "pairs should exists.");
|
||||
|
||||
// if (fromToken != ETH_ADDRESS) {
|
||||
// // smartApprove.claimTokens(fromToken, msg.sender, address(this), fromTokenAmount);
|
||||
// }
|
||||
|
||||
// for (uint256 i = 0; i < callPairs.length; i++) {
|
||||
// require(callPairs[i] != address(smartApprove), "Access denied");
|
||||
// require(
|
||||
// callPairs[i].externalCall(
|
||||
// gasLimitsAndValues[i] & ((1 << 128) - 1),
|
||||
// callDataConcat,
|
||||
// starts[i],
|
||||
// starts[i + 1] - starts[i],
|
||||
// gasLimitsAndValues[i] >> 128
|
||||
// )
|
||||
// );
|
||||
// }
|
||||
|
||||
// // Return back all unswapped
|
||||
// fromToken.universalTransfer(msg.sender, fromToken.universalBalanceOf(address(this)));
|
||||
|
||||
// returnAmount = toToken.universalBalanceOf(address(this));
|
||||
|
||||
// require(returnAmount >= minReturnAmount, "Return amount is not enough");
|
||||
// toToken.universalTransfer(msg.sender, returnAmount);
|
||||
emit Swapped(fromToken, toToken, msg.sender, fromTokenAmount, fromTokenAmount);
|
||||
// emit Swapped(fromToken, toToken, msg.sender, fromTokenAmount, returnAmount);
|
||||
}
|
||||
|
||||
function externalSwap(
|
||||
IERC20 fromToken,
|
||||
IERC20 toToken,
|
||||
address approveTarget,
|
||||
address to,
|
||||
uint256 gasSwap,
|
||||
uint256 fromTokenAmount,
|
||||
uint256 minReturnAmount,
|
||||
bytes memory callDataConcat
|
||||
) public payable returns (uint256 returnAmount) {
|
||||
|
||||
require(minReturnAmount > 0, "Min return should be bigger then 0.");
|
||||
|
||||
if (fromToken != ETH_ADDRESS) {
|
||||
smartApprove.claimTokens(fromToken, msg.sender, address(this), fromTokenAmount);
|
||||
fromToken.approve(approveTarget, fromTokenAmount);
|
||||
}
|
||||
|
||||
(bool success, ) = to.call{value: msg.value, gas: gasSwap}(callDataConcat);
|
||||
|
||||
require(success, "Contract Swap execution Failed");
|
||||
|
||||
// Return back all unswapped
|
||||
fromToken.universalTransfer(msg.sender, fromToken.universalBalanceOf(address(this)));
|
||||
|
||||
returnAmount = toToken.universalBalanceOf(address(this));
|
||||
|
||||
require(returnAmount >= minReturnAmount, "Return amount is not enough");
|
||||
toToken.universalTransfer(msg.sender, returnAmount);
|
||||
|
||||
emit Swapped(fromToken, toToken, msg.sender, fromTokenAmount, returnAmount);
|
||||
|
||||
emit ExternalRecord(to, msg.sender);
|
||||
}
|
||||
}
|
||||
15
contracts/intf/ISmartApprove.sol
Normal file
15
contracts/intf/ISmartApprove.sol
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 DODO ZOO.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.6.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {IERC20} from "./IERC20.sol";
|
||||
|
||||
interface ISmartApprove {
|
||||
function claimTokens(IERC20 token,address who,address dest,uint256 amount) external;
|
||||
}
|
||||
34
contracts/lib/ExternalCall.sol
Normal file
34
contracts/lib/ExternalCall.sol
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 DODO ZOO.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.6.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
library ExternalCall {
|
||||
// Source: https://github.com/gnosis/MultiSigWallet/blob/master/contracts/MultiSigWallet.sol
|
||||
// call has been separated into its own function in order to take advantage
|
||||
// of the Solidity's code generator to produce a loop that copies tx.data into memory.
|
||||
function externalCall(address destination, uint value, bytes memory data, uint dataOffset, uint dataLength, uint gasLimit) internal returns(bool result) {
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
if (gasLimit == 0) {
|
||||
gasLimit = gasleft() - 40000;
|
||||
}
|
||||
assembly {
|
||||
let x := mload(0x40) // "Allocate" memory for output (0x40 is where "free memory" pointer is stored by convention)
|
||||
let d := add(data, 32) // First 32 bytes are the padded length of data, so exclude that
|
||||
result := call(
|
||||
gasLimit,
|
||||
destination,
|
||||
value,
|
||||
add(d, dataOffset),
|
||||
dataLength, // Size of the input (in bytes) - this is what fixes the padding problem
|
||||
x,
|
||||
0 // Output is ignored, therefore the output size is zero
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
75
contracts/lib/UniversalERC20.sol
Normal file
75
contracts/lib/UniversalERC20.sol
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 DODO ZOO.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.6.9;
|
||||
|
||||
import {SafeMath} from "./SafeMath.sol";
|
||||
import {IERC20} from "../intf/IERC20.sol";
|
||||
import {SafeERC20} from "./SafeERC20.sol";
|
||||
|
||||
library UniversalERC20 {
|
||||
|
||||
using SafeMath for uint256;
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
IERC20 private constant ZERO_ADDRESS = IERC20(0x0000000000000000000000000000000000000000);
|
||||
IERC20 private constant ETH_ADDRESS = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
|
||||
|
||||
function universalTransfer(IERC20 token, address to, uint256 amount) internal {
|
||||
universalTransfer(token, to, amount, false);
|
||||
}
|
||||
|
||||
function universalTransfer(IERC20 token, address to, uint256 amount, bool mayFail) internal returns(bool) {
|
||||
if (amount == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (token == ZERO_ADDRESS || token == ETH_ADDRESS) {
|
||||
if (mayFail) {
|
||||
return address(uint160(to)).send(amount);
|
||||
} else {
|
||||
address(uint160(to)).transfer(amount);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
token.safeTransfer(to, amount);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function universalApprove(IERC20 token, address to, uint256 amount) internal {
|
||||
if (token != ZERO_ADDRESS && token != ETH_ADDRESS) {
|
||||
token.safeApprove(to, amount);
|
||||
}
|
||||
}
|
||||
|
||||
function universalTransferFrom(IERC20 token, address from, address to, uint256 amount) internal {
|
||||
if (amount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (token == ZERO_ADDRESS || token == ETH_ADDRESS) {
|
||||
require(from == msg.sender && msg.value >= amount, "msg.value is zero");
|
||||
if (to != address(this)) {
|
||||
address(uint160(to)).transfer(amount);
|
||||
}
|
||||
if (msg.value > amount) {
|
||||
msg.sender.transfer(msg.value.sub(amount));
|
||||
}
|
||||
} else {
|
||||
token.safeTransferFrom(from, to, amount);
|
||||
}
|
||||
}
|
||||
|
||||
function universalBalanceOf(IERC20 token, address who) internal view returns (uint256) {
|
||||
if (token == ZERO_ADDRESS || token == ETH_ADDRESS) {
|
||||
return who.balance;
|
||||
} else {
|
||||
return token.balanceOf(who);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user