Files
proxmox/packages/economics-toolkit/src/executor.ts
defiQUG dbd517b279 Sync workspace: config, docs, scripts, CI, operator rules, and submodule pointers.
- Update dbis_core, cross-chain-pmm-lps, explorer-monorepo, metamask-integration, pr-workspace/chains
- Omit embedded publish git dirs and empty placeholders from index

Made-with: Cursor
2026-04-12 06:12:20 -07:00

181 lines
4.8 KiB
TypeScript

import { Interface, JsonRpcProvider, Wallet, formatEther } from 'ethers';
import { readFileSync } from 'fs';
const SWAP_ABI = [
'function swapExactIn(address pool, address tokenIn, uint256 amountIn, uint256 minAmountOut) external returns (uint256)',
];
export interface ExecutorAllowlist {
chainId: number;
allowedTo: string[];
/** Max msg.value in wei (string). */
maxValueWei: string;
/** Optional max fee per gas in gwei (omit = no cap). */
maxFeePerGasGwei?: number;
}
export function loadAllowlist(path: string): ExecutorAllowlist {
const raw = readFileSync(path, 'utf8');
const j = JSON.parse(raw) as ExecutorAllowlist;
if (!j.chainId || !Array.isArray(j.allowedTo)) {
throw new Error('Invalid allowlist: need chainId and allowedTo[]');
}
return {
chainId: j.chainId,
allowedTo: j.allowedTo.map((a: string) => a.toLowerCase()),
maxValueWei: j.maxValueWei ?? '0',
maxFeePerGasGwei: j.maxFeePerGasGwei,
};
}
function normalizeAddr(a: string): string {
return a.trim().toLowerCase();
}
/** Calldata for DODOPMMIntegration.swapExactIn; send `to` = integration address. */
export function encodeSwapExactInCalldata(
pool: string,
tokenIn: string,
amountIn: bigint,
minAmountOut: bigint
): string {
const iface = new Interface(SWAP_ABI);
return iface.encodeFunctionData('swapExactIn', [pool, tokenIn, amountIn, minAmountOut]);
}
export interface SimulationResult {
ok: boolean;
error?: string;
gasEstimate?: bigint;
}
export async function simulateCall(params: {
rpcUrl: string;
from: string;
to: string;
data: string;
valueWei?: bigint;
}): Promise<SimulationResult> {
const provider = new JsonRpcProvider(params.rpcUrl);
const tx = {
from: params.from,
to: params.to,
data: params.data,
value: params.valueWei ?? 0n,
};
try {
await provider.call(tx);
} catch (e) {
const msg = e instanceof Error ? e.message : String(e);
return { ok: false, error: msg };
}
try {
const gasEstimate = await provider.estimateGas(tx);
return { ok: true, gasEstimate };
} catch {
return { ok: true, gasEstimate: undefined };
}
}
export async function enforceAllowlistAndSimulate(params: {
rpcUrl: string;
allowlistPath: string;
from: string;
to: string;
data: string;
valueWei?: bigint;
}): Promise<SimulationResult & { allowlistOk: boolean; allowlistError?: string }> {
let list: ExecutorAllowlist;
try {
list = loadAllowlist(params.allowlistPath);
} catch (e) {
return {
ok: false,
allowlistOk: false,
allowlistError: e instanceof Error ? e.message : String(e),
};
}
const provider = new JsonRpcProvider(params.rpcUrl);
const net = await provider.getNetwork();
if (Number(net.chainId) !== list.chainId) {
return {
ok: false,
allowlistOk: false,
allowlistError: `chainId mismatch: rpc=${net.chainId} allowlist=${list.chainId}`,
};
}
if (!list.allowedTo.includes(normalizeAddr(params.to))) {
return {
ok: false,
allowlistOk: false,
allowlistError: `to not in allowlist: ${params.to}`,
};
}
const maxVal = BigInt(list.maxValueWei);
const v = params.valueWei ?? 0n;
if (v > maxVal) {
return {
ok: false,
allowlistOk: false,
allowlistError: `value ${v} exceeds maxValueWei ${maxVal}`,
};
}
const sim = await simulateCall(params);
return { ...sim, allowlistOk: true };
}
export async function broadcastIfApply(params: {
rpcUrl: string;
privateKey: string;
allowlistPath: string;
to: string;
data: string;
valueWei?: bigint;
}): Promise<{ hash?: string; error?: string }> {
const list = loadAllowlist(params.allowlistPath);
const provider = new JsonRpcProvider(params.rpcUrl);
const net = await provider.getNetwork();
if (Number(net.chainId) !== list.chainId) {
return { error: `chainId mismatch: ${net.chainId}` };
}
if (!list.allowedTo.includes(normalizeAddr(params.to))) {
return { error: 'to not allowlisted' };
}
const maxVal = BigInt(list.maxValueWei);
const v = params.valueWei ?? 0n;
if (v > maxVal) {
return { error: 'value exceeds allowlist' };
}
const wallet = new Wallet(params.privateKey, provider);
const tx: Parameters<Wallet['sendTransaction']>[0] = {
to: params.to,
data: params.data,
value: v,
};
if (list.maxFeePerGasGwei !== undefined) {
tx.maxFeePerGas = BigInt(Math.floor(list.maxFeePerGasGwei * 1e9));
}
try {
const sent = await wallet.sendTransaction(tx);
return { hash: sent.hash };
} catch (e) {
return { error: e instanceof Error ? e.message : String(e) };
}
}
/** Format gas cost in ETH given estimate and effective gas price (wei). */
export function formatGasCostEth(gasEstimate: bigint, gasPriceWei: bigint): string {
try {
const wei = gasEstimate * gasPriceWei;
return formatEther(wei);
} catch {
return 'n/a';
}
}