|
| 1 | +const { ethers } = require('ethers'); |
| 2 | + |
| 3 | +const config = { |
| 4 | + // 提供者配置 - 连接到以太坊网络 |
| 5 | + provider: new ethers.providers.JsonRpcProvider('https://mainnet.infura.io/v3/YOUR_INFURA_KEY'), |
| 6 | + // 钱包配置 - 使用私钥创建钱包实例 |
| 7 | + wallet: null, // 需要设置: new ethers.Wallet('YOUR_PRIVATE_KEY', provider), |
| 8 | + // 合约地址 |
| 9 | + addresses: { |
| 10 | + dai: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI 代币地址 |
| 11 | + tranche: '', // Element Tranche 合约地址 |
| 12 | + principalToken: '', // PT 代币地址 |
| 13 | + amm: '', // Element AMM 地址 |
| 14 | + baseAsset: '' // 基础资产地址 |
| 15 | + }, |
| 16 | + // ABI 配置 |
| 17 | + abis: { |
| 18 | + erc20: [ |
| 19 | + 'function approve(address spender, uint256 amount) external returns (bool)', |
| 20 | + 'function balanceOf(address account) external view returns (uint256)' |
| 21 | + ], |
| 22 | + tranche: [ |
| 23 | + 'function deposit(uint256 amount, address recipient) external', |
| 24 | + 'function unlockTimestamp() external view returns (uint256)', |
| 25 | + 'function redeemPrincipal(uint256 amount, address recipient) external' |
| 26 | + ], |
| 27 | + amm: [ |
| 28 | + 'function getAmountOut(uint256 amountIn, address tokenIn, address tokenOut) external view returns (uint256)', |
| 29 | + 'function swap(address tokenIn, address tokenOut, uint256 amountIn, uint256 minAmountOut, address recipient) external returns (uint256)' |
| 30 | + ] |
| 31 | + } |
| 32 | +}; |
| 33 | + |
| 34 | +// 合约实例 |
| 35 | +let contracts = {}; |
| 36 | + |
| 37 | +/** |
| 38 | + * 初始化合约实例 |
| 39 | + */ |
| 40 | +async function initContracts() { |
| 41 | + // 确保钱包已配置 |
| 42 | + if (!config.wallet) { |
| 43 | + throw new Error('请先配置钱包'); |
| 44 | + } |
| 45 | + |
| 46 | + // 初始化合约实例 |
| 47 | + contracts.dai = new ethers.Contract(config.addresses.dai, config.abis.erc20, config.wallet); |
| 48 | + contracts.tranche = new ethers.Contract(config.addresses.tranche, config.abis.tranche, config.wallet); |
| 49 | + contracts.principalToken = new ethers.Contract(config.addresses.principalToken, config.abis.erc20, config.wallet); |
| 50 | + contracts.amm = new ethers.Contract(config.addresses.amm, config.abis.amm, config.wallet); |
| 51 | + contracts.baseAsset = new ethers.Contract(config.addresses.baseAsset, config.abis.erc20, config.wallet); |
| 52 | + |
| 53 | + console.log('合约初始化完成'); |
| 54 | +} |
| 55 | + |
| 56 | +/** |
| 57 | + * 1. 存入资产获取 PT 和 YT |
| 58 | + * @param {string} amount - 存入的资产数量(以wei为单位) |
| 59 | + * @returns {Promise<object>} 交易收据 |
| 60 | + */ |
| 61 | +async function depositToElement(amount) { |
| 62 | + console.log(`准备存入 ${ethers.utils.formatEther(amount)} DAI 到 Element...`); |
| 63 | + |
| 64 | + try { |
| 65 | + // 批准 Element Tranche 合约使用 DAI |
| 66 | + console.log('批准 Tranche 合约使用 DAI...'); |
| 67 | + const approveTx = await contracts.dai.approve(config.addresses.tranche, amount); |
| 68 | + await approveTx.wait(); |
| 69 | + console.log('批准成功,交易哈希:', approveTx.hash); |
| 70 | + |
| 71 | + // 存入 DAI 并获取 PT 和 YT |
| 72 | + console.log('存入 DAI 并获取 PT 和 YT...'); |
| 73 | + const depositTx = await contracts.tranche.deposit(amount, config.wallet.address); |
| 74 | + const receipt = await depositTx.wait(); |
| 75 | + console.log('存入成功,交易哈希:', depositTx.hash); |
| 76 | + |
| 77 | + // 获取 PT 和 YT 余额 |
| 78 | + const ptBalance = await contracts.principalToken.balanceOf(config.wallet.address); |
| 79 | + console.log(`现在持有 ${ethers.utils.formatEther(ptBalance)} PT`); |
| 80 | + |
| 81 | + return receipt; |
| 82 | + } catch (error) { |
| 83 | + console.error('存入资产失败:', error); |
| 84 | + throw error; |
| 85 | + } |
| 86 | +} |
| 87 | + |
| 88 | +/** |
| 89 | + * 2. 在 AMM 中交易 PT 获取固定收益 |
| 90 | + * @param {string} ptAmount - PT 代币数量(以wei为单位) |
| 91 | + * @param {number} slippageTolerance - 滑点容忍度(0-1之间的小数,默认0.01即1%) |
| 92 | + * @returns {Promise<object>} 交易收据和固定收益率 |
| 93 | + */ |
| 94 | +async function tradePTForBaseAsset(ptAmount, slippageTolerance = 0.01) { |
| 95 | + console.log(`准备交易 ${ethers.utils.formatEther(ptAmount)} PT 获取固定收益...`); |
| 96 | + |
| 97 | + try { |
| 98 | + // 批准 AMM 使用 PT |
| 99 | + console.log('批准 AMM 使用 PT...'); |
| 100 | + const approveTx = await contracts.principalToken.approve(config.addresses.amm, ptAmount); |
| 101 | + await approveTx.wait(); |
| 102 | + console.log('批准成功,交易哈希:', approveTx.hash); |
| 103 | + |
| 104 | + // 计算预期获得的基础资产数量 |
| 105 | + const baseAssetAmount = await contracts.amm.getAmountOut( |
| 106 | + ptAmount, |
| 107 | + config.addresses.principalToken, |
| 108 | + config.addresses.baseAsset |
| 109 | + ); |
| 110 | + console.log(`预期获得 ${ethers.utils.formatEther(baseAssetAmount)} 基础资产`); |
| 111 | + |
| 112 | + // 计算最小获得的基础资产数量(考虑滑点) |
| 113 | + const minAmountOut = baseAssetAmount.mul( |
| 114 | + ethers.BigNumber.from(Math.floor((1 - slippageTolerance) * 10000)) |
| 115 | + ).div(ethers.BigNumber.from(10000)); |
| 116 | + |
| 117 | + // 执行交易 |
| 118 | + console.log('执行交易...'); |
| 119 | + const swapTx = await contracts.amm.swap( |
| 120 | + config.addresses.principalToken, |
| 121 | + config.addresses.baseAsset, |
| 122 | + ptAmount, |
| 123 | + minAmountOut, |
| 124 | + config.wallet.address |
| 125 | + ); |
| 126 | + const receipt = await swapTx.wait(); |
| 127 | + console.log('交易成功,交易哈希:', swapTx.hash); |
| 128 | + |
| 129 | + // 计算固定收益率 |
| 130 | + const fixedRate = calculateFixedRate(ptAmount, baseAssetAmount); |
| 131 | + console.log(`固定收益率: ${fixedRate.toFixed(2)}%`); |
| 132 | + |
| 133 | + return { receipt, fixedRate }; |
| 134 | + } catch (error) { |
| 135 | + console.error('交易 PT 失败:', error); |
| 136 | + throw error; |
| 137 | + } |
| 138 | +} |
| 139 | + |
| 140 | +/** |
| 141 | + * 3. 到期后赎回 PT |
| 142 | + * @param {string} ptAmount - PT 代币数量(以wei为单位) |
| 143 | + * @returns {Promise<object>} 交易收据 |
| 144 | + */ |
| 145 | +async function redeemPT(ptAmount) { |
| 146 | + console.log(`准备赎回 ${ethers.utils.formatEther(ptAmount)} PT...`); |
| 147 | + |
| 148 | + try { |
| 149 | + // 检查是否已到期 |
| 150 | + const unlockTimestamp = await contracts.tranche.unlockTimestamp(); |
| 151 | + const currentTimestamp = Math.floor(Date.now() / 1000); |
| 152 | + |
| 153 | + if (currentTimestamp < unlockTimestamp) { |
| 154 | + const remainingTime = unlockTimestamp - currentTimestamp; |
| 155 | + const days = Math.floor(remainingTime / 86400); |
| 156 | + throw new Error(`尚未到期,还需等待 ${days} 天`); |
| 157 | + } |
| 158 | + |
| 159 | + // 赎回基础资产 |
| 160 | + console.log('赎回基础资产...'); |
| 161 | + const redeemTx = await contracts.tranche.redeemPrincipal(ptAmount, config.wallet.address); |
| 162 | + const receipt = await redeemTx.wait(); |
| 163 | + console.log('赎回成功,交易哈希:', redeemTx.hash); |
| 164 | + |
| 165 | + return receipt; |
| 166 | + } catch (error) { |
| 167 | + console.error('赎回 PT 失败:', error); |
| 168 | + throw error; |
| 169 | + } |
| 170 | +} |
| 171 | + |
| 172 | +/** |
| 173 | + * 计算固定收益率 |
| 174 | + * @param {ethers.BigNumber} ptAmount - PT 代币数量 |
| 175 | + * @param {ethers.BigNumber} baseAssetAmount - 获得的基础资产数量 |
| 176 | + * @returns {number} 年化固定收益率(百分比) |
| 177 | + */ |
| 178 | +function calculateFixedRate(ptAmount, baseAssetAmount) { |
| 179 | + // 获取当前时间和到期时间 |
| 180 | + const currentTimestamp = Math.floor(Date.now() / 1000); |
| 181 | + const unlockTimestamp = contracts.tranche.unlockTimestamp(); |
| 182 | + |
| 183 | + // 计算剩余时间(年) |
| 184 | + const timeRemainingInYears = (unlockTimestamp - currentTimestamp) / (365 * 24 * 60 * 60); |
| 185 | + |
| 186 | + // 计算收益率 |
| 187 | + // 假设 PT 和基础资产的面值相同,那么: |
| 188 | + // 收益率 = ((面值 / 当前价格) - 1) / 剩余时间 |
| 189 | + const faceValue = ethers.utils.parseEther('1'); // 假设面值为1 |
| 190 | + const currentPrice = baseAssetAmount.mul(faceValue).div(ptAmount); |
| 191 | + |
| 192 | + const yieldRate = (faceValue.mul(ethers.BigNumber.from(10000)).div(currentPrice).toNumber() / 10000 - 1) / timeRemainingInYears; |
| 193 | + |
| 194 | + // 转换为百分比 |
| 195 | + return yieldRate * 100; |
| 196 | +} |
| 197 | + |
| 198 | +/** |
| 199 | + * 使用示例 |
| 200 | + */ |
| 201 | +async function main() { |
| 202 | + try { |
| 203 | + // 初始化合约 |
| 204 | + await initContracts(); |
| 205 | + |
| 206 | + // 示例1:存入1000 DAI |
| 207 | + const depositAmount = ethers.utils.parseEther('1000'); |
| 208 | + await depositToElement(depositAmount); |
| 209 | + |
| 210 | + // 示例2:交易PT获取固定收益 |
| 211 | + const ptAmount = ethers.utils.parseEther('1000'); // 假设我们有1000个PT |
| 212 | + await tradePTForBaseAsset(ptAmount, 0.01); // 1%滑点容忍度 |
| 213 | + |
| 214 | + // 示例3:到期后赎回PT(注意:只有在到期后才能执行) |
| 215 | + // await redeemPT(ptAmount); |
| 216 | + |
| 217 | + console.log('示例执行完成'); |
| 218 | + } catch (error) { |
| 219 | + console.error('执行示例时出错:', error); |
| 220 | + } |
| 221 | +} |
| 222 | + |
| 223 | +// 如果直接运行此脚本,则执行main函数 |
| 224 | +if (require.main === module) { |
| 225 | + // 在运行前,请先在配置中设置正确的合约地址和私钥 |
| 226 | + // main().catch(console.error); |
| 227 | +} |
| 228 | + |
| 229 | +module.exports = { |
| 230 | + initContracts, |
| 231 | + depositToElement, |
| 232 | + tradePTForBaseAsset, |
| 233 | + redeemPT, |
| 234 | + calculateFixedRate |
| 235 | +}; |
0 commit comments