联合铸币合约
背景知识
目前 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 手续费,并且没有任何人因此蒙受损失,是我觉得比较有趣的一个设计。
最后理论上说两次铸币之间,价格可能是有波动的,我们取平均数,如果这个合约你都能从中套利,那我愿称你为最强。
喜欢我的作品吗?别忘了给予支持与赞赏,让我知道在创作的路上有你陪伴,一起延续这份热忱!
- 来自作者
- 相关推荐