Skip to content

Commit 3b4e9f8

Browse files
authored
Merge pull request #1294 from lilycom02/develop
feat: Element Protocol optimize
2 parents 3fa4537 + 4e7985a commit 3b4e9f8

File tree

2 files changed

+472
-10
lines changed

2 files changed

+472
-10
lines changed
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
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

Comments
 (0)