Skip to content

Commit afc3e33

Browse files
committed
feat: add example
1 parent 50f95f1 commit afc3e33

File tree

2 files changed

+264
-33
lines changed

2 files changed

+264
-33
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+
};

defi/Element/readme.md

Lines changed: 29 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -50,53 +50,49 @@ Element Protocol 的核心架构包括以下几个部分:
5050

5151
### 交互示例
5252

53-
以下是与 Element Protocol 交互的基本流程:
53+
以下是与 Element Protocol 交互的基本流程,完整的JavaScript实现可以在[element-interaction.js](./element-interaction.js)文件中找到
5454

5555
1. **存入资产获取 PT 和 YT**
56-
```solidity
56+
```javascript
57+
// 从element-interaction.js中导入函数
58+
const { depositToElement } = require('./element-interaction.js');
59+
5760
// 假设我们要存入 1000 DAI 到 Element
58-
function depositToElement(uint256 amount) external {
59-
// 批准 Element Tranche 合约使用 DAI
60-
dai.approve(address(tranche), amount);
61-
62-
// 存入 DAI 并获取 PT 和 YT
63-
tranche.deposit(amount, msg.sender);
64-
65-
// 现在用户持有等量的 PT 和 YT
61+
async function deposit() {
62+
const depositAmount = ethers.utils.parseEther('1000');
63+
await depositToElement(depositAmount);
64+
// 现在用户持有等量的 PT 和 YT
6665
}
6766
```
6867

6968
2. **在 AMM 中交易 PT 获取固定收益**
70-
```solidity
71-
function tradePTForBaseAsset(uint256 ptAmount) external {
72-
// 批准 AMM 使用 PT
73-
principalToken.approve(address(amm), ptAmount);
74-
75-
// 计算预期获得的基础资产数量
76-
uint256 baseAssetAmount = amm.getAmountOut(ptAmount, address(principalToken), address(baseAsset));
77-
78-
// 执行交易
79-
amm.swap(
80-
address(principalToken),
81-
address(baseAsset),
82-
ptAmount,
83-
baseAssetAmount * 0.99, // 设置滑点容忍度
84-
msg.sender
85-
);
69+
```javascript
70+
// 从element-interaction.js中导入函数
71+
const { tradePTForBaseAsset } = require('./element-interaction.js');
72+
73+
// 交易PT获取固定收益
74+
async function trade() {
75+
const ptAmount = ethers.utils.parseEther('1000'); // 假设我们有1000个PT
76+
const result = await tradePTForBaseAsset(ptAmount, 0.01); // 1%滑点容忍度
77+
console.log(`固定收益率: ${result.fixedRate.toFixed(2)}%`);
8678
}
8779
```
8880

8981
3. **到期后赎回 PT**
90-
```solidity
91-
function redeemPT(uint256 ptAmount) external {
92-
// 确保已经到期
93-
require(block.timestamp >= tranche.unlockTimestamp(), "Not yet matured");
94-
95-
// 赎回基础资产
96-
tranche.redeemPrincipal(ptAmount, msg.sender);
82+
```javascript
83+
// 从element-interaction.js中导入函数
84+
const { redeemPT } = require('./element-interaction.js');
85+
86+
// 到期后赎回PT
87+
async function redeem() {
88+
const ptAmount = ethers.utils.parseEther('1000');
89+
await redeemPT(ptAmount);
90+
// 成功赎回基础资产
9791
}
9892
```
9993

94+
更多详细实现和使用方法请参考[element-interaction.js](./element-interaction.js)文件。
95+
10096
## 零息债券专用AMM
10197
零息债券的价格可由利率大小和到期时间来決定,他是以面额折价计算,也就是考量到未來現金流复利,折现到现在的价值为多少,公式如下:
10298
债券价格 (PV) = 面额 (FV) ÷ (1 + r)^n

0 commit comments

Comments
 (0)