@ledgerhq/coin-filecoin
Version:
Ledger Filecoin Coin integration
115 lines • 4.38 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.estimateFees = estimateFees;
const logs_1 = require("@ledgerhq/logs");
const tokenAccounts_1 = require("../../erc20/tokenAccounts");
const addresses_1 = require("../../network/addresses");
const types_1 = require("../../types");
const api_1 = require("../../network/api");
// Inline method numbers from Filecoin spec (mirrors src/bridge/utils.ts Methods enum).
// logic/ must not import from bridge/, so we inline the relevant constants here.
const METHOD_TRANSFER = 0;
const METHOD_INVOKE_EVM = 3844450837;
// Default fallback fee used when token simulation fails due to zero native balance.
// This is a best-effort estimate; the actual fee will be validated at sign time.
const DEFAULT_FEE_FALLBACK = {
value: 100000000000n,
parameters: {
gasFeeCap: "100000",
gasLimit: "1000000",
gasPremium: "1000",
},
};
// Resolves the ERC-20 recipient to its Ethereum-compatible (0x) form.
function resolveTokenRecipient(recipient) {
if (recipient?.startsWith("0x")) {
return recipient;
}
try {
return (0, addresses_1.convertAddressFilToEth)(recipient ?? "");
}
catch {
throw new Error(`Invalid token recipient: ${recipient}`);
}
}
// Builds the fee-estimation request for an ERC-20 transfer.
// Returns null when token params cannot be encoded (caller falls back to a default estimate).
function buildErc20FeeRequest(intent) {
const asset = intent.asset;
const contractAddr = asset.assetReference.toLowerCase();
const contractValidation = (0, addresses_1.validateAddress)(contractAddr);
if (!contractValidation.isValid) {
throw new Error(`Invalid ERC-20 contract address: ${contractAddr}`);
}
const recipientEth = resolveTokenRecipient(intent.recipient);
try {
const abiEncoded = (0, tokenAccounts_1.abiEncodeTransferParams)(recipientEth, intent.amount.toString());
return {
to: contractValidation.parsedAddress.toString(),
methodNum: METHOD_INVOKE_EVM,
value: "0",
params: (0, tokenAccounts_1.encodeTxnParams)(abiEncoded),
};
}
catch (e) {
(0, logs_1.log)("debug", "[estimateFees] failed to encode token params, using fallback", e);
return null;
}
}
// Builds the fee-estimation request for a native FIL transfer.
function buildNativeFeeRequest(intent) {
const recipientValidation = (0, addresses_1.validateAddress)(intent.recipient ?? "");
return {
to: recipientValidation.isValid ? recipientValidation.parsedAddress.toString() : undefined,
methodNum: METHOD_TRANSFER,
value: intent.amount.toString(),
};
}
async function estimateFees(intent, _customFeesParameters) {
const senderValidation = (0, addresses_1.validateAddress)(intent.sender);
if (!senderValidation.isValid) {
throw new Error(`Invalid sender address: ${intent.sender}`);
}
const from = senderValidation.parsedAddress.toString();
const assetType = intent.asset.type;
let request;
if (assetType === "native") {
request = buildNativeFeeRequest(intent);
}
else if (assetType === "erc20") {
const erc20Request = buildErc20FeeRequest(intent);
if (!erc20Request) {
return DEFAULT_FEE_FALLBACK;
}
request = erc20Request;
}
else {
throw new Error(`Unsupported asset type: ${String(assetType)}`);
}
const { to, methodNum, value, params } = request;
try {
const fees = await (0, api_1.fetchEstimatedFees)({
from,
...(to ? { to } : {}),
methodNum,
blockIncl: types_1.BroadcastBlockIncl,
value,
...(params ? { params } : {}),
});
const gasFeeCap = BigInt(fees.gas_fee_cap);
const gasLimit = BigInt(fees.gas_limit);
return {
value: gasFeeCap * gasLimit,
parameters: {
gasFeeCap: fees.gas_fee_cap,
gasLimit: String(fees.gas_limit),
gasPremium: fees.gas_premium,
},
};
}
catch (e) {
(0, logs_1.log)("debug", "[estimateFees] fee estimation failed, returning fallback", e);
return DEFAULT_FEE_FALLBACK;
}
}
//# sourceMappingURL=estimateFees.js.map