想快速换钱实现兑换。只能够在去中心化交易所(DEX)进行。
在这里鄙视一下GATEIO,入账最少半个小时以上,黄花菜都凉了~
现在排名前几的DEX有uni,sushi,curve。我们先选uni和curve。
说真的,curve官网文档配色真的是反人类。
https://curve.readthedocs.io/exchange-pools.html#exchange-pools-plain
还有uniswap。
https://docs.uniswap.org/protocol/guides/flash-integrations/inheritance-constructors
开始看他们的调用示例来决定我们的合约代码应该需要怎么写,扩展参数应该有哪些!
找到他们的池子合约地址。
curve的有两个:
0xd51a44d3fae010294c616388b506acda1bfaae46 0x80466c64868e1ab14a1ddf27a676c3fcbe638fe5

点看查看源码,发现又臭又长。决定放弃阅读。结合文档,拿到exchange的相关信息,然后定义外部接口
//Curve合约接口
interface ICurveSwap {
/**
fromIndex,要发送代币的索引值
toIndex,要接收代币的索引值
fromAmount,需要兑换的代币数量
dyAmount,获得的最小代币数量
**/
function exchange(uint256 fromIndex, uint256 toIndex, uint256 fromAmount, uint256 dyAmount) external payable;
/**
计算获得的最小代币数量
fromIndex,要发送代币的索引值
toIndex,要接收代币的索引值
fromAmount,需要兑换的代币数量
**/
function get_dy(uint256 fromIndex, uint256 toIndex, uint256 fromAmount) external view returns (uint256);
}
接下来是uniswap,同样找到相关合约地址:
0x4e68ccd3e89f51c3074ca5072bbac773960dfa36

https://docs.uniswap.org/protocol/reference/core/UniswapV3Pool
关于文档,uniswap真的比curve好太多太多了。
interface IUniSwap {
/** recipient 接收地址
zeroForOne 交换的方向,token0->token1为 true,token1->token0为 false
amountSpecified 交换的数量,数值可正可负
sqrtPriceLimitX96 价格平方根的Q64.96,如果是token0/token1方向的兑换,价格不能低于该参数。 如果是token1/token0方向,则不能大于该参数。
data 回调函数参数
**/
function swap(
address recipient,
bool zeroForOne,
int256 amountSpecified,
uint160 sqrtPriceLimitX96,
bytes data
) external returns (int256 amount0, int256 amount1);
}
说实话,看官方解释,看得我一脸懵逼。sqrtPriceLimitX96这个参数太难理解了。其实就是xian价的意思。设置为0,则忽略。
返回值暂时不用管。
function curveSwap(uint256 tokenIn, address tokenInAddress, uint256 tokenOut, uint256 amount) public {
approve(CURVE_POOL, tokenInAddress, amount);
ICurveSwap(CURVE_POOL).exchange(tokenIn, tokenOut, amount, 0);
}
// 请求授权
function approve(address requester, address token, uint256 amount) internal {
uint256 alowance = IERC20(token).allowance(address(this), requester);
if (alowance < amount) {
IERC20(token).safeApprove(requester, MAX_INT);
}
}
因为curve要授权,所以在调用之前,确保已经进行授权。
比较疑惑的是tokenIn和tokenOut。这两个是池子定义的数组。在coins变量里面。内容如下
coins: constant(address[N_COINS]) = [
0xdAC17F958D2ee523a2206206994597C13D831ec7, //usdt index:0
0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599, //wbtc
0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, //weth index:2
]
我们取的是索引值0和2的。WBTC不用管了。
function uniSwap(address tokenIn, address tokenOut, uint256 amount) public {
//地址address类型本质上是一个160位的数字,可以进行加减
bool zeroForOne = tokenIn < tokenOut;
//这行代码是抄的,回头再仔细想想是什么意思
uint160 sqrtPriceLimitX96 = (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1);
IUniSwap(UNISWAP_POOL).swap(address(this), zeroForOne, int256(amount), sqrtPriceLimitX96, abi.encode(amount));
}
//uni主动回调的函数
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata _data) public {
uint256 amount = abi.decode(_data, (uint256));
IERC20(WETH).safeTransfer(UNISWAP_POOL, amount);
}
因为uniswap餐用的是闪兑功能。需要我们提供一个回调函数uniswapV3SwapCallback。
我们在回调函数里面把钱给他转过去。
好了。写完之后便可以开始测试。
接下来命令一条龙服务:
instance = await MoneyBot.deployed();
//发送10以太币
instance.send(web3.utils.toWei('10', 'ether'))
//把eth转为WETH
instance.ETHtoWETH(web3.utils.toWei('8', 'ether'));
//查看合约里面的WETH余额
contractWethBalance = await instance.getThisWethBalance();
contractWethBalance.toString();
//查看合约里面的USDT余额
contractUSDTBalance = await instance.getThisUSDTBalance();
contractUSDTBalance.toString();
//定义两个合约地址变量
WETHADDR="0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
USDTADDR="0xdAC17F958D2ee523a2206206994597C13D831ec7";
//测试uni闪兑功能。
instance.uniSwap(WETHADDR,USDTADDR,web3.utils.toWei('1'));
//再次查看合约里面的USDT余额,看看是否有改变。
contractUSDTBalance = await instance.getThisUSDTBalance();
contractUSDTBalance.toString();
//测试curve的exchange,从WETH兑换到USDT
instance.curveSwap(2,WETHADDR,0,web3.utils.toWei('2'));
//再次查看合约里面的USDT余额,看看是否有改变。
contractUSDTBalance = await instance.getThisUSDTBalance();
contractUSDTBalance.toString();
OK,如果余额发生改变,证明兑换已经成功。
测试通过后,便可以再次进行优化代码。比如说定义一个统一接口swap,根据flag进行识别具体是要使用哪一个模块来兑换。我知道java程序员喜欢用字符串或者字典。但是solidity用字符串实在是牺牲太大。所以用一个uint8就可以!
下一节,准备开始把闪电贷和swap功能结合起来。
附上主类的代码。
pragma solidity ^0.8.17;
import "./Libs.sol";
import "./AddressList.sol";
contract MoneyBot {
using SafeMath for uint256;
using SafeERC20 for IERC20;
//合约的部署者
address owner;
uint256 MAX_INT = 2 ** 256 - 1;
constructor(){
owner = address(tx.origin);
}
// 返回合约的所有者
function getOwner() public view returns (address) {
return owner;
}
// 返回某个账户的某个token的余额
function getTokenBalance(address token, address account) public view returns (uint256) {
return IERC20(token).balanceOf(account);
}
// 返回合约部署者账户中的ETH余额
function getMyWethBalance() public view returns (uint256) {
return IERC20(WETH).balanceOf(owner);
}
// 返回合约部署者账户中的USDT余额
function getMyUSDTBalance() public view returns (uint256) {
return IERC20(USDT).balanceOf(owner);
}
// 返回合约账户中的WETH余额
function getThisWethBalance() public view returns (uint256) {
return IERC20(WETH).balanceOf(address(this));
}
// 返回合约账户中的USDT余额
function getThisUSDTBalance() public view returns (uint256) {
return IERC20(USDT).balanceOf(address(this));
}
//把合约里面的ETH转到钱包
function turnOutETH(uint256 amount) public onlyOwner {
payable(owner).transfer(amount);
}
//把合约里面的token转到钱包
function turnOutToken(address token, uint256 amount) public onlyOwner {
IERC20(token).transfer(owner, amount);
}
// WETH转换为ETH
function WETHToETH(uint256 amount) public onlyOwner {
IWETH(WETH).withdraw(amount);
}
// ETH转换为WETH
function ETHtoWETH(uint256 amount) public onlyOwner {
IWETH(WETH).deposit{value : amount}();
}
function curveSwap(uint256 tokenIn, address tokenInAddress, uint256 tokenOut, uint256 amount) public {
approve(CURVE_POOL, tokenInAddress, amount);
ICurveSwap(CURVE_POOL).exchange(tokenIn, tokenOut, amount, 0);
}
// 请求授权
function approve(address requester, address token, uint256 amount) internal {
uint256 alowance = IERC20(token).allowance(address(this), requester);
if (alowance < amount) {
IERC20(token).safeApprove(requester, MAX_INT);
}
}
function uniSwap(address tokenIn, address tokenOut, uint256 amount) public {
//地址address类型本质上是一个160位的数字,可以进行加减
bool zeroForOne = tokenIn < tokenOut;
//这行代码是抄的,回头再仔细想想是什么意思
uint160 sqrtPriceLimitX96 = (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1);
IUniSwap(UNISWAP_POOL).swap(address(this), zeroForOne, int256(amount), sqrtPriceLimitX96, abi.encode(amount));
}
//uni主动回调的函数
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata _data) public {
uint256 amount = abi.decode(_data, (uint256));
IERC20(WETH).safeTransfer(UNISWAP_POOL, amount);
}
//发起一个eth的闪电贷
function doFlashEth(uint256 amount) public onlyOwner {
ILiquidity(LIQUIDITY_POOL).borrow(WETH, amount,
abi.encodeWithSelector(this.receiveBuyBigHouse.selector, abi.encode(amount)));
}
// callback ,收到钱后可以拿来买一个大房子。
function receiveBuyBigHouse(bytes memory data) public {
require(msg.sender == BORROWER_PROXY, "bu shi BorrorwerProxy,paochu yichang.");
//购买大房子
uint256 amount = abi.decode(data, (uint256));
IERC20(WETH).transfer(LIQUIDITY_POOL, amount);
}
//fallback
receive() external payable {}
// modifier
modifier onlyOwner(){
require(address(msg.sender) == owner, "No authority");
_;
}
}

最后,大家记得帮忙众筹6颗核桃啊~。脑子不够用了。
点击转账三百块!
