这篇文章给大家介绍如何在DeFi应用中集成Curve协议,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。
Curve.Fi是一组规模庞大的DEFi协议,这个教程的目的是帮助你关注智能合约开发的现代方法,以及如何在自己的Defi应用中集成Curve.Fi这种大型协议的关键点。
是的,我已经说过我们将深入研究代码。尽管我们不是在谈论AMM模型的数学运算,也不是在Curve.Fi合约中使用的其他一些特定技巧,但是我们仍然需要了解一些内容。当然,这不是在谈论成功的DeFi开发-我没有给出提示。这是我经验的一部分,在这里我将分享一些有关Curve智能合约的基本架构以及我们如何与这些架构集成的知识。实际上,下图中包含了本教程的全部内容:
我们研究的是Y-Pool,它以一种简单明了的方式组织。存款(Deposit)合约将你的资金打包成Y-Token,然后将其发送到兑换(Swap)合约,兑换合约为用户生成LP代币。如果你要提取流通性份额以及所赚取的收益,则合约会执行相同的操作,但是方式相反。这一切都与金融合约有关(当然,如果我们对池平衡和股数计算背后的复杂逻辑选择视而不见)。
此外,我们还有Gauge和Minter合同,它们是Curve.Fi DAO的一部分。这些合约使你可以获得CRV代币奖励并将其抵押,以便获得协议治理的投票权。
在我们充分了解这些需要交互的合约后,就可以开始我们的开发了。
你可能还记得,我们正在处理一些使用Vyper开发的外部合约。为了与这些合约交互,我们需要创建一些接口。这并不是最困难的工作:只需要仔细查看原始合约并挑出将要调用的方法即可。
让我们将从存款合约开始。我们需要资金进出的方法,以及一些数据查看界面(例如,查看已注册的代币及其Y对应代币)。
contract ICurveFi_DepositY { function add_liquidity(uint256[4] calldata uamounts, uint256 min_mint_amount) external; function remove_liquidity(uint256 _amount, uint256[4] calldata min_uamounts) external; function remove_liquidity_imbalance(uint256[4] calldata uamounts, uint256 max_burn_amount) external; function coins(int128 i) external view returns (address); function underlying_coins(int128 i) external view returns (address); function underlying_coins() external view returns (address[4] memory); function curve() external view returns (address); function token() external view returns (address); }
然后用相同的方法处理兑换合约(你可能还记得,该合约为用户铸造LP代币)。
contract ICurveFi_Gauge { function lp_token() external view returns(address); function crv_token() external view returns(address); function balanceOf(address addr) external view returns (uint256); function deposit(uint256 _value) external; function withdraw(uint256 _value) external; function claimable_tokens(address addr) external returns (uint256); function minter() external view returns(address); //use minter().mint(gauge_addr) to claim CRV function integrate_fraction(address _for) external view returns(uint256); function user_checkpoint(address _for) external returns(bool); }
现在该处理DAO的主要合约-流动性计量器合约。它执行复杂的功能,但我们主要需要用于创建检查点的方法以及查看Minter和CRV代币地址的界面。
contract ICurveFi_Gauge { function lp_token() external view returns(address); function crv_token() external view returns(address); function balanceOf(address addr) external view returns (uint256); function deposit(uint256 _value) external; function withdraw(uint256 _value) external; function claimable_tokens(address addr) external returns (uint256); function minter() external view returns(address); //use minter().mint(gauge_addr) to claim CRV function integrate_fraction(address _for) external view returns(uint256); function user_checkpoint(address _for) external returns(bool); }
还有从用户界面的角度来看其实是最简单的合约— Minter。实际上,我们只需要minter方法本身,以及一个可以查看有效奖励的方法。
contract ICurveFi_Minter { function mint(address gauge_addr) external; function minted(address _for, address gauge_addr) external view returns(uint256); function toggle_approve_mint(address minting_user) external; function token() external view returns(address); }
最后一个是YToken接口,不过它并非最不重要,因为我们要交互的就是Curve的Y池。YToken是一个简单的ERC20接口,几乎没有其他额外的方法。
contract IYERC20 { //ERC20 functions // // //Y-token functions function deposit(uint256 amount) external; function withdraw(uint256 shares) external; function getPricePerFullShare() external view returns (uint256); function token() external returns(address); }
现在我们准备创建一些代码来与此协议交互。
我们的实验合约的主要目标是展示如何将Curve协议作为资金流终点来组织资金的流动。因此,我们将有两种主要方法:一种用于完整的存款流程(具有从Curve.Fi可获得的所有好处),另一种用于提款流程。
让我们从存款开始。
function multiStepDeposit(uint256[4] memory _amounts) public { address[4] memory stablecoins = ICurveFi_DepositY(curveFi_Deposit).underlying_coins(); for (uint256 i = 0; i < stablecoins.length; i++) { IERC20(stablecoins[i]).safeTransferFrom(_msgSender(), address(this), _amounts[i]); IERC20(stablecoins[i]).safeApprove(curveFi_Deposit, _amounts[i]); } //Step 1 - deposit stablecoins and get Curve.Fi LP tokens ICurveFi_DepositY(curveFi_Deposit).add_liquidity(_amounts, 0); //0 to mint all Curve has to //Step 2 - stake Curve LP tokens into Gauge and get CRV rewards uint256 curveLPBalance = IERC20(curveFi_LPToken).balanceOf(address(this)); IERC20(curveFi_LPToken).safeApprove(curveFi_LPGauge, curveLPBalance); ICurveFi_Gauge(curveFi_LPGauge).deposit(curveLPBalance); //Step 3 - get all the rewards (and make whatever you need with them) crvTokenClaim(); uint256 crvAmount = IERC20(curveFi_CRVToken).balanceOf(address(this)); IERC20(curveFi_CRVToken).safeTransfer(_msgSender(), crvAmount); }
正像你看见的,我们在multiStepDeposit()
方法中执行了几个动作。首先,将我们的资金(在本例中为选定的稳定币)存入Deposit合约,以便接收Curve LP代币。第二步是将LP代币存入Gauge合约,以便获得CRV代币奖励。第三步取决于你自己 —— 可以使用CRV奖励做任何想做的事情。
实际上,相同的方案也适用于multiStepWithdraw()方法,但顺序相反。
function multiStepWithdraw(uint256[4] memory _amounts) public { address[4] memory stablecoins = ICurveFi_DepositY(curveFi_Deposit).underlying_coins(); //Step 1 - Calculate amount of Curve LP-tokens to unstake uint256 nWithdraw; uint256 i; for (i = 0; i < stablecoins.length; i++) { nWithdraw = nWithdraw.add(normalize(stablecoins[i], _amounts[i])); } uint256 withdrawShares = calculateShares(nWithdraw); //Check if you can re-use unstaked LP tokens uint256 notStaked = curveLPTokenUnstaked(); if (notStaked > 0) { withdrawShares = withdrawShares.sub(notStaked); } //Step 2 - Unstake Curve LP tokens from Gauge ICurveFi_Gauge(curveFi_LPGauge).withdraw(withdrawShares); //Step 3 - Withdraw stablecoins from CurveDeposit IERC20(curveFi_LPToken).safeApprove(curveFi_Deposit, withdrawShares); ICurveFi_DepositY(curveFi_Deposit).remove_liquidity_imbalance(_amounts, withdrawShares); //Step 4 - Send stablecoins to the requestor // }
是的,解决方案非常简单,某种程度上看起来很笨拙,并且存在几个明显的漏洞 —— 虽然这些代码仅用于演示目的。你可以添加所需的所有检查,将阶段拆分为不同的方法,为方法设置不同的权限,并执行使你的DeFi项目成功所需的所有其他工作。
教程的最后一步是添加几个单元测试,以验证解决方案是否有效。虽然在这里我们面临几个问题。第一个(也是主要的)问题是Curve.Fi协议环境。为了测试解决方案,我们需要在本地环境(在我们的例子中为Ganache)上模拟完整的Curve协议。由于这是一组复杂的合约,而且是在Vyper上编写的,因此我们不能仅将它们编译并部署到我们的Ganache节点中。
我已经完成了一些工作,并准备了Curve协议的测试存根,可以将其与你的项目一起编译并部署你的测试网上。除了出于测试目的的几种简化,这些测试存根与原始的Curve协议完全相同。你可以在这里下载。
因此,我们可以跳过将Vyper代码转换为可测试的Solidity合约的无聊部分,直接查看单元测试的输出:
关于如何在DeFi应用中集成Curve协议就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。