79 lines
3.2 KiB
TypeScript
79 lines
3.2 KiB
TypeScript
/**
|
|
* Floor price helpers for the red-team agent feedback loop.
|
|
*
|
|
* Reads ethPerToken and related floor diagnostics from the Kraiken contract
|
|
* and supporting on-chain state via direct JSON-RPC calls.
|
|
*/
|
|
import { Interface } from 'ethers';
|
|
import { rpcCall } from './rpc.js';
|
|
|
|
// Base WETH address — stable across Anvil forks of Base Sepolia.
|
|
const WETH = '0x4200000000000000000000000000000000000006';
|
|
|
|
const KRK_ABI = [
|
|
'function ethPerToken() external view returns (uint256)',
|
|
'function outstandingSupply() external view returns (uint256)',
|
|
];
|
|
const ERC20_ABI = ['function balanceOf(address account) external view returns (uint256)'];
|
|
|
|
const krkIface = new Interface(KRK_ABI);
|
|
const erc20Iface = new Interface(ERC20_ABI);
|
|
|
|
/**
|
|
* Read the current floor price from the Kraiken contract.
|
|
*
|
|
* @param krkAddress - Kraiken contract address.
|
|
* @param _lmAddress - LiquidityManager address (reserved for future fallback computation).
|
|
* @returns ethPerToken in wei.
|
|
*/
|
|
export async function getEthPerToken(rpcUrl: string, krkAddress: string, _lmAddress: string): Promise<bigint> {
|
|
const calldata = krkIface.encodeFunctionData('ethPerToken', []);
|
|
const result = (await rpcCall(rpcUrl, 'eth_call', [{ to: krkAddress, data: calldata }, 'latest'])) as string;
|
|
const [value] = krkIface.decodeFunctionResult('ethPerToken', result);
|
|
return BigInt(value);
|
|
}
|
|
|
|
/**
|
|
* Read full floor diagnostics for the LiquidityManager / Kraiken pair.
|
|
*
|
|
* All four reads are issued in parallel to minimise round-trip latency.
|
|
*
|
|
* @param lmAddress - LiquidityManager contract address.
|
|
* @param krkAddress - Kraiken contract address.
|
|
* @returns {ethPerToken} floor price in wei
|
|
* @returns {lmEthBalance} native ETH held by the LiquidityManager
|
|
* @returns {lmWethBalance} WETH held by the LiquidityManager
|
|
* @returns {outstandingSupply} total KRK outstanding supply
|
|
*/
|
|
export async function getFloorState(
|
|
rpcUrl: string,
|
|
lmAddress: string,
|
|
krkAddress: string,
|
|
): Promise<{
|
|
ethPerToken: bigint;
|
|
lmEthBalance: bigint;
|
|
lmWethBalance: bigint;
|
|
outstandingSupply: bigint;
|
|
}> {
|
|
const ethPerTokenCalldata = krkIface.encodeFunctionData('ethPerToken', []);
|
|
const outstandingSupplyCalldata = krkIface.encodeFunctionData('outstandingSupply', []);
|
|
const wethBalanceCalldata = erc20Iface.encodeFunctionData('balanceOf', [lmAddress]);
|
|
|
|
const [ethPerTokenHex, lmEthBalanceHex, lmWethBalanceHex, outstandingSupplyHex] = (await Promise.all([
|
|
rpcCall(rpcUrl, 'eth_call', [{ to: krkAddress, data: ethPerTokenCalldata }, 'latest']),
|
|
rpcCall(rpcUrl, 'eth_getBalance', [lmAddress, 'latest']),
|
|
rpcCall(rpcUrl, 'eth_call', [{ to: WETH, data: wethBalanceCalldata }, 'latest']),
|
|
rpcCall(rpcUrl, 'eth_call', [{ to: krkAddress, data: outstandingSupplyCalldata }, 'latest']),
|
|
])) as [string, string, string, string];
|
|
|
|
const [ethPerToken] = krkIface.decodeFunctionResult('ethPerToken', ethPerTokenHex);
|
|
const [lmWethBalance] = erc20Iface.decodeFunctionResult('balanceOf', lmWethBalanceHex);
|
|
const [outstandingSupply] = krkIface.decodeFunctionResult('outstandingSupply', outstandingSupplyHex);
|
|
|
|
return {
|
|
ethPerToken: BigInt(ethPerToken),
|
|
lmEthBalance: BigInt(lmEthBalanceHex),
|
|
lmWethBalance: BigInt(lmWethBalance),
|
|
outstandingSupply: BigInt(outstandingSupply),
|
|
};
|
|
}
|