import { ethers } from "ethers";
const portalAbi = [
"function getToken(address token) view returns ((bool,uint16,uint16,uint16,uint16,uint16,uint256,address),(address,address,uint256,uint8,uint256,uint256,uint256,uint256),uint8)",
"function quoteBuy(address token,uint256 quoteIn) view returns (uint256,uint256,uint256,uint256)",
"function quoteSell(address token,uint256 tokenIn) view returns (uint256,uint256,uint256,uint256)",
"function buy(address token,uint256 quoteIn,uint256 minTokenOut) payable",
"function sell(address token,uint256 tokenIn,uint256 minQuoteOut)",
];
const erc20Abi = [
"function decimals() view returns (uint8)",
"function approve(address spender,uint256 amount) returns (bool)",
];
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
const signer = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
const portal = new ethers.Contract(process.env.PORTAL_ADDRESS!, portalAbi, signer);
const STATUS_TRADING_INTERNAL = 1n;
function applySlippageBps(amount: bigint, bps: bigint) {
// 例如 bps=100 代表 1%
return (amount * (10000n - bps)) / 10000n;
}
export async function buyWithNative(token: string, quoteInEth: string, slippageBps = 100n) {
const quoteIn = ethers.parseEther(quoteInEth);
const [, st] = await portal.getToken(token);
if (BigInt(st.status) !== STATUS_TRADING_INTERNAL) {
throw new Error("Token not in TradingInternal");
}
const [tokenOut] = await portal.quoteBuy(token, quoteIn);
const minTokenOut = applySlippageBps(tokenOut, slippageBps);
const tx = await portal.buy(token, quoteIn, minTokenOut, { value: quoteIn });
return tx.wait();
}
export async function buyWithErc20(token: string, quoteToken: string, quoteInRaw: bigint, slippageBps = 100n) {
const quote = new ethers.Contract(quoteToken, erc20Abi, signer);
const [, st] = await portal.getToken(token);
if (BigInt(st.status) !== STATUS_TRADING_INTERNAL) {
throw new Error("Token not in TradingInternal");
}
const [tokenOut] = await portal.quoteBuy(token, quoteInRaw);
const minTokenOut = applySlippageBps(tokenOut, slippageBps);
await (await quote.approve(await portal.getAddress(), quoteInRaw)).wait();
const tx = await portal.buy(token, quoteInRaw, minTokenOut); // msg.value = 0
return tx.wait();
}
export async function sellToken(token: string, tokenInWad: bigint, slippageBps = 100n) {
const launchToken = new ethers.Contract(token, erc20Abi, signer);
const [, st] = await portal.getToken(token);
if (BigInt(st.status) !== STATUS_TRADING_INTERNAL) {
throw new Error("Token not in TradingInternal");
}
const [quoteOut] = await portal.quoteSell(token, tokenInWad);
const minQuoteOut = applySlippageBps(quoteOut, slippageBps);
await (await launchToken.approve(await portal.getAddress(), tokenInWad)).wait();
const tx = await portal.sell(token, tokenInWad, minQuoteOut);
return tx.wait();
}