yamaswap-sdk
Version:
ETF SDK for Solana and Evm
231 lines (205 loc) • 7.81 kB
text/typescript
import ETFTOKEN from "../abis/CryptoETFToken.json";
import CETO from "../abis/CryptoETFOracle.json";
import ROUTER from "../abis/CryptoETFRouter.json";
import ETFFACTORY from "../abis/CryptoETFTokenFactory.json";
import { Contract, ethers } from "ethers";
import type { ETFCreateParams, ETFBurnParams, MintETFTokenParams, ETFCreateResult, MintETFResult } from '../types/params';
export class DexClientEVM {
public readonly factory: Contract;
public readonly router: Contract;
public readonly oracle: Contract;
public readonly signer: ethers.JsonRpcSigner;
// 常量定义
private readonly WETH: string;
constructor(
signer: ethers.JsonRpcSigner,
addresses: {
TWAP: string;
CETO: string;
ETFROUTER: string;
ETFFACTORY: string;
WETH: string;
}
// , network: "localhost" | "arbitrumSepolia" | "sepolia"
) {
this.factory = new ethers.Contract(addresses.ETFFACTORY, ETFFACTORY.abi, signer);
this.router = new ethers.Contract(addresses.ETFROUTER, ROUTER.abi, signer);
this.oracle = new ethers.Contract(addresses.CETO, CETO.abi, signer);
this.signer = signer;
this.WETH = addresses.WETH;
}
public async createETF(params: ETFCreateParams): Promise<ETFCreateResult> {
try {
const { name, symbol, description: tokenUri, assets } = params;
// 检查是否已存在
const existingETF = await this.factory.etfListM(symbol);
if (existingETF !== ethers.ZeroAddress) {
return {
success: false,
error: 'ETF already exists',
data: {
etfAddress: existingETF,
symbol
}
};
}
// 创建 ETF
const tx = await this.factory.createETF(
name,
symbol,
tokenUri,
assets.map(asset => ({
tokenAddress: asset.token,
distribution: asset.weight // 5000
}))
);
await tx.wait();
// 获取创建的 ETF 地址
const etfAddress = await this.factory.etfListM(symbol);
console.log(`Created ETF at address: ${etfAddress}`);
return {
success: true,
txid: tx.hash,
data: {
etfAddress,
symbol,
name
}
};
} catch (error) {
console.error("Error in createETF:", error);
return {
success: false,
txid: '',
error: error instanceof Error ? error.message : 'Failed to create ETF'
};
}
}
public async purchaseETF(params: MintETFTokenParams): Promise<MintETFResult> {
try {
const { etfAddress, lamports } = params;
// 检查用户余额
const userAddress = await this.signer.getAddress();
const balance = await this.signer.provider?.getBalance(userAddress);
console.log(
"USER ETH=>",
ethers.formatEther(balance)
);
// 获取最新 NAV
const nav = await this.oracle.nav(etfAddress, this.WETH, 10);
console.log('Current ETF NAV:', ethers.formatEther(nav), 'ETH');
// 设置截止时间
const deadline = Math.round(Date.now() / 1000) + 100;
// 购买 ETF
const tx = await this.router.purchaseWithExactEth(
etfAddress,
this.signer.address,
0,
deadline,
{ value: ethers.parseUnits(lamports.toString()) }
);
const receipt = await tx.wait();
// 获取 ETF 合约实例
const etfBalance = await this.getETFBalance(etfAddress);
return {
success: true,
txid: receipt.transactionHash,
data: {
balance: etfBalance.toString(),
etfAddress
}
};
} catch (error) {
console.error("Error in purchaseETF:", error);
return {
success: false,
txid: '',
error: error instanceof Error ? error.message : 'Failed to purchase ETF'
};
}
}
public async burnETF(params: ETFBurnParams): Promise<MintETFResult> {
try {
const { etfAddress, lamports } = params;
const deadline = Math.round(Date.now() / 1000) + 100;
// 赎回 ETF
const tx = await this.router.redeemWithExactEth(
etfAddress,
lamports,
this.signer.address,
0,
deadline
);
const receipt = await tx.wait();
const etfBalance = await this.getETFBalance(etfAddress);
return {
success: true,
txid: receipt.transactionHash,
data: {
balance: etfBalance.toString(),
etfAddress
}
};
} catch (error) {
console.error("Error in burnETF:", error);
return {
success: false,
txid: '',
error: error instanceof Error ? error.message : 'Failed to burn ETF'
};
}
}
// 查询方法
public async getETFInfo(etfAddress: string) {
const etfContract = new ethers.Contract(etfAddress, ETFTOKEN.abi, this.signer);
const totalSupply = await etfContract.totalSupply();
// 获取组成部分信息
const constituentTokens = await etfContract.getConstituentTokens();
const reserves = await Promise.all(
constituentTokens.map(async (token: string) => ({
token,
reserve: await etfContract.constitunentsReserves(token)
}))
);
return {
totalSupply: totalSupply.toString(),
reserves
};
}
public async getETFBalance(etfAddress: string) {
const etfContract = new ethers.Contract(etfAddress, ETFTOKEN.abi, this.signer);
return await etfContract.balanceOf(this.signer.address);
}
public async getNav(etfAddress: string) {
return await this.oracle.nav(etfAddress, this.WETH, 10);
}
// async function checkResult(etf, ceto) {
// console.log("======BEGIN TO CHECK STATE=====");
// const [signer1] = await ethers.getSigners();
// const etfC = await ethers.getContractAt(ETFTOKENABI, etf);
// console.log("MINT TOTAL ETF=>", await etfC.totalSupply());
// //检查etf reverse
// for (const _consti of constitunents) {
// let _token = await ethers.getContractAt(ERC20ABI, _consti.tokenAddress);
// console.log(
// "RESERVE %s IN ETF=>%s",
// _consti.tokenAddress,
// await etfC.constitunentsReserves(_consti.tokenAddress)
// );
// console.log(
// "ERC20 %s BALANCE=>",
// _consti.tokenAddress,
// await _token.balanceOf(etf)
// );
// }
// console.log(
// "AFTER EFT NAV=>",
// ethers.formatEther(await ceto.nav(etf, WETH, 10)),
// "ETH"
// );
// console.log(
// "USER ETH=>",
// ethers.formatEther(await ethers.provider.getBalance(signer1.address))
// );
// }
}