UNPKG

@atomiqlabs/chain-starknet

Version:

Starknet specific base implementation

133 lines (107 loc) 4.45 kB
import {getLogger, toBigInt, toHex} from "../../../utils/Utils"; import {Provider} from "starknet"; import {StarknetTokens} from "./StarknetTokens"; const MAX_FEE_AGE = 5000; export class StarknetFees { private readonly logger = getLogger("StarknetFees: "); private readonly feeDA: "L1" | "L2"; private readonly nonceDA: "L1" | "L2"; private readonly provider: Provider; private readonly gasToken: "ETH" | "STRK"; private readonly maxFeeRate: bigint; private readonly feeMultiplierPPM: bigint; private blockFeeCache: { timestamp: number, feeRate: Promise<bigint> } = null; constructor( provider: Provider, gasToken: "ETH" | "STRK" = "STRK", maxFeeRate: number = gasToken==="ETH" ? 100_000_000_000 /*100 GWei*/ : 1_000_000_000_000_000 /*100 * 10000 GWei*/, feeMultiplier: number = 1.25, da?: {fee?: "L1" | "L2", nonce?: "L1" | "L2"} ) { this.provider = provider; this.gasToken = gasToken; this.maxFeeRate = BigInt(maxFeeRate); this.feeDA = da?.fee ?? "L1"; this.nonceDA = da?.nonce ?? "L1"; this.feeMultiplierPPM = BigInt(Math.floor(feeMultiplier*1000000)); } /** * Gets starknet fee rate * * @private * @returns {Promise<BN>} L1 gas price denominated in Wei */ private async _getFeeRate(): Promise<bigint> { const block = await this.provider.getBlockWithTxHashes("latest"); let l1GasCost = toBigInt(this.gasToken==="ETH" ? block.l1_gas_price.price_in_wei : block.l1_gas_price.price_in_fri); l1GasCost = l1GasCost * this.feeMultiplierPPM / 1000000n; this.logger.debug("_getFeeRate(): L1 fee rate: "+l1GasCost.toString(10)); return l1GasCost; } /** * Gets the gas price with caching, format: <gas price in Wei>;<transaction version: v1/v3> * * @private */ public async getFeeRate(): Promise<string> { if(this.blockFeeCache==null || Date.now() - this.blockFeeCache.timestamp > MAX_FEE_AGE) { let obj = { timestamp: Date.now(), feeRate: null }; obj.feeRate = this._getFeeRate().catch(e => { if(this.blockFeeCache===obj) this.blockFeeCache=null; throw e; }); this.blockFeeCache = obj; } let feeRate = await this.blockFeeCache.feeRate; if(feeRate>this.maxFeeRate) feeRate = this.maxFeeRate; const fee = feeRate.toString(10)+";"+(this.gasToken === "ETH" ? "v1" : "v3"); this.logger.debug("getFeeRate(): calculated fee: "+fee); return fee; } public getDefaultGasToken(): string { return this.gasToken==="ETH" ? StarknetTokens.ERC20_ETH : StarknetTokens.ERC20_STRK; } /** * Calculates the total gas fee fee paid for a given gas limit at a given fee rate * * @param gas * @param feeRate */ public static getGasFee(gas: number, feeRate: string): bigint { if(feeRate==null) return 0n; const arr = feeRate.split(";"); const gasPrice = BigInt(arr[0]); return gasPrice * BigInt(gas); } public static getGasToken(feeRate: string): string { if(feeRate==null) return null; const arr = feeRate.split(";"); const txVersion = arr[1] as "v1" | 'v3'; return txVersion==="v1" ? StarknetTokens.ERC20_ETH : StarknetTokens.ERC20_STRK; } getFeeDetails(L1GasLimit: number, L2GasLimit: number, feeRate: string) { if(feeRate==null) return null; const arr = feeRate.split(";"); const gasPrice = BigInt(arr[0]); const version = arr[1] as "v1" | "v3"; const maxFee = toHex(BigInt(L1GasLimit) * gasPrice, 16); return { maxFee: maxFee, version: version==="v1" ? "0x1" : "0x3" as "0x1" | "0x3", resourceBounds: { l1_gas: {max_amount: toHex(L1GasLimit, 16), max_price_per_unit: toHex(gasPrice, 16)}, l2_gas: {max_amount: "0x0", max_price_per_unit: "0x0"} }, tip: "0x0", paymasterData: [], nonceDataAvailabilityMode: this.nonceDA, feeDataAvailabilityMode: this.feeDA } } }