联合铸币合约
背景知识
目前 DeFi Yeild Framing a.k.a. 流动性挖矿非常火热,不过由于作为一个普通用户,实在感觉现在以太坊的 gas 费和几个月前相比实在是贵太多了。为了帮助农夫们节约手续费我开发了这个联合铸币合约,可以众筹 USDT,印成目标代币并自动进行抵押生息。合约没有经过审计,不过已经跑了一个星期,目前锁仓 3.5m 左右,应该不会出太大的问题。
产品页面:https://y3d.finance/y3d/mint/
因为 UI 比较看起来像是 scam,所以用的人不是很多。。。。
目前我们提供了两种联合铸币产品,USDT 铸 yyCrv,和 USDT 铸 yswUSD,使用方法就是 Deposit USDT 然后等后面有人点 mint() 的时候顺便帮你也 mint() 就行。后者是新出的 Curve 分叉,目前不仅 2.5x 加速,而且头两周还有 bonus,所以年化按照官网给出的数据有 100% - 600% 左右。(但是 DeFi 的高年化普遍都不会持续很久)
这里着重介绍一下 yyCrv 的原理。yCrv 是 Curve 平台针对 YFI 的 yearn 系列稳定币之间的流动性 Token,相比于稳定币本身,yCrv 还可以获取各种被动收入。简单来说 yyCrv 的被动收益包含三个部分:
- yearn 中的理财收益
- Curve 的交易手续费
- Curve 的流动性挖矿
无论之后的行情朝着什么方向发展,看起来她都是目前最为稳定的一个理财组合,这也是为什么 y3d 首发 yyCrv 之间的交易对的原因,而且看起来刚刚上线的 yETH 的理财路径现在最后也是通向这里,也难怪为什么有人说 yCrv 才是真稳定币了。
一般说来,要想获得 yyCrv,首先需要获得 yCrv。最简单的方法是直接从 Uniswap Pool 中购买,但是 yCrv 的溢价很高,非常不划算。另一种方法是从 Curve 合约中进行铸币,但是因为目前以太坊的手续费整体偏高,使得后者的手续费目前通常达到 50 美金甚至 100 美金,不利于向散户推广。
因此,我们设计了联合铸币(United Mint)合约,希望可以解决这一问题。
如何使用
- 存钱
Deposit(uint):将 USDT 存入合约,等待有人来铸币。 - 铸币
Mint():将合约中所有的 USDT 铸成 yyCrv,执行这个方法的人通常我们称之为雷锋。 - 取币
Claim():根据当前价格,计算我的 USDT 余额对应的 yyCrv。如果合约中的 yyCrv 余额足够,则取回这些 yyCrv。 - 还原
Restore(uint):根据当前价格,计算我的 yyCrv 对应的 USDT。如果此时合约中 USDT 的余额足够,则用这笔 yyCrv 替换出 USDT。 - 存钱取币
DepositAndClaim(uint):将 USDT 存入合约,并且如果没有人铸币,则自己主动铸币。
其中 DepositAndClaim(uint) 方法将会被后续出品的 yUSDT 合约频繁调用,因此散户可以经常搭便车(free rider)。
技术细节
数据结构
balance[]: 每个用户的 USDT 数。mintedUSDT: 已参与的铸币的 USDT 数,需要单独存一下。unminted_USDT(): 尚未参与铸币的 USDT 数,就是合约里 USDT 的余额。minted_yyCrv(): 已铸币的 yyCrv 数,就是合约里 yyCrv 的余额。
方法
核心的几个方法:
/**
* @dev Deposit usdt or claim yyCrv directly if balance of yyCrv is sufficient
*/function deposit(uint256 input) external {
require(input != 0, "Empty usdt");
IUSDT(USDT).transferFrom(msg.sender, address(this), input);
if (input > mintedUSDT) {
setBalance(msg.sender, balanceOf(msg.sender).add(input));
emit Deposit(msg.sender, input);
} else {
uint256 output = get_yyCrvFromUsdt(input);
mintedUSDT = mintedUSDT.sub(input);
IERC20(yyCrv).transfer(msg.sender, output);
emit Claim(msg.sender, input, output);
}
}
/**
* @dev Mint all unminted_USDT into yyCrv
*/function mint() public {
require(unminted_USDT() > 0, "Empty usdt");
mintedUSDT = mintedUSDT.add(unminted_USDT());
IyDeposit(yDeposit).add_liquidity([0, 0, unminted_USDT(), 0], 0);
IyyCrv(yyCrv).stake(minted_yCRV());
}
/**
* @dev Claim yyCrv back, if the balance is sufficient, execute mint()
*/function claim() public {
uint256 input = balanceOf(msg.sender);
require(input != 0, "You don't have USDT balance to withdraw");
uint256 r; // requirement yCrvif (mintedUSDT == 0) {
mint();
r = get_yyCrvFromUsdt(input);
} else {
r = get_yyCrvFromUsdt(input);
if (r > minted_yyCRV()) mint();
r = get_yyCrvFromUsdt(input);
}
mintedUSDT = mintedUSDT.sub(input);
IERC20(yyCrv).transfer(msg.sender, r);
setBalance(msg.sender, 0);
emit Claim(msg.sender, input, r);
}
/**
* @dev Try to claim unminted usdt by yyCrv if the balance is sufficient
*/function restore(uint input) external {
require(input != 0, "Empty yyCrv");
require(minted_yyCRV() != 0, "No yyCrv price at this moment");
uint output = get_yyCrvFromUsdt(unminted_USDT());
if (output < input) input = output;
output = get_usdtFromYycrv(input);
mintedUSDT = mintedUSDT.add(output);
IERC20(yyCrv).transferFrom(msg.sender, address(this), input);
IUSDT(USDT).transfer(msg.sender, output);
emit Restore(msg.sender, input, output);
}
/**
* @dev Deposit usdt and claim yyCrv in any case
*/function depositAndClaim(uint256 input) external {
require(input != 0, "Empty usdt");
IUSDT(USDT).transferFrom(msg.sender, address(this), input);
if (input > mintedUSDT) {
mint();
}
uint256 output = get_yyCrvFromUsdt(input);
mintedUSDT = mintedUSDT.sub(input);
IERC20(yyCrv).transfer(msg.sender, output);
emit Claim(msg.sender, input, output);
}
首先 Deposit(), Claim() 看起来都挺直接的。这里先说一下铸币 Mint() 方法,铸币主要是调用 Curve yCrv 的铸币合约 的添加流动性 add_liquidity() 方法,类似 Uniswap 的 add_liquidity(),但是看起来它里面还要跟 yEarn 系列交互,所以里面换来换去,会非常烧 gas。细节可参考这个 铸币过程。
yCrv 交易对合约 中相关的代码如下:
@public
@nonreentrant('lock')
def add_liquidity(uamounts: uint256[N_COINS], min_mint_amount: uint256):
tethered: bool[N_COINS] = TETHERED
amounts: uint256[N_COINS] = ZEROS
for i in range(N_COINS):
uamount: uint256 = uamounts[i]
if uamount > 0:
# Transfer the underlying coin from ownerif tethered[i]:
USDT(self.underlying_coins[i]).transferFrom(
msg.sender, self, uamount)
else:
assert_modifiable(ERC20(self.underlying_coins[i])\
.transferFrom(msg.sender, self, uamount))
# Mint if needed
ERC20(self.underlying_coins[i]).approve(self.coins[i], uamount)
yERC20(self.coins[i]).deposit(uamount)
amounts[i] = yERC20(self.coins[i]).balanceOf(self)
ERC20(self.coins[i]).approve(self.curve, amounts[i])
Curve(self.curve).add_liquidity(amounts, min_mint_amount)
tokens: uint256 = ERC20(self.token).balanceOf(self)
assert_modifiable(ERC20(self.token).transfer(msg.sender, tokens))
然后再重点说一下销毁 restore(uint) 方法,这个方法可以利用其他人想进来铸币的人的余额,用当前合约中的铸币比例,反向兑出自己手里的 yyCrv,这样甚至可以豁免 yyCrv 中的 3% P3D 手续费,并且没有任何人因此蒙受损失,是我觉得比较有趣的一个设计。
最后理论上说两次铸币之间,价格可能是有波动的,我们取平均数,如果这个合约你都能从中套利,那我愿称你为最强。
喜欢我的作品吗?别忘了给予支持与赞赏,让我知道在创作的路上有你陪伴,一起延续这份热忱!

- 来自作者
- 相关推荐