@ledgerhq/live-common
Version:
Common ground for the Ledger Live apps
135 lines (116 loc) • 3.67 kB
text/typescript
import type { FeeDescriptor } from "../../../bridge/descriptor/types";
import { BigNumber } from "bignumber.js";
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null;
}
function isBigNumber(value: unknown): value is BigNumber {
return BigNumber.isBigNumber(value);
}
type BitcoinFeeItem = {
speed: string;
feePerByte: BigNumber;
};
function getSuggestedFeePerByteRange(
transaction: Record<string, unknown>,
): { min: string; max: string } | null {
const networkInfo = transaction.networkInfo;
if (!isRecord(networkInfo)) return null;
const feeItems = networkInfo.feeItems;
if (!isRecord(feeItems) || !Array.isArray(feeItems.items)) return null;
const items = feeItems.items
.filter((item): item is BitcoinFeeItem => {
if (!isRecord(item)) return false;
return typeof item.speed === "string" && isBigNumber(item.feePerByte);
})
.map(item => item.feePerByte);
if (items.length === 0) return null;
const min = BigNumber.min(...items);
const max = BigNumber.max(...items);
return {
min: min.toFixed(),
max: max.toFixed(),
};
}
export const fees: FeeDescriptor = {
hasPresets: true,
hasCustom: true,
hasCoinControl: true,
presets: {
legend: { type: "feeRate", unit: "sat/vbyte", valueFrom: "presetAmount" },
strategyLabelInAmount: "legend",
getOptions: transaction => {
if (!isRecord(transaction)) {
return [];
}
const networkInfo = transaction.networkInfo;
if (!isRecord(networkInfo)) {
return [];
}
const feeItems = networkInfo.feeItems;
if (!isRecord(feeItems) || !Array.isArray(feeItems.items)) {
return [];
}
const items = feeItems.items
.filter((item): item is BitcoinFeeItem => {
if (!isRecord(item)) return false;
return typeof item.speed === "string" && isBigNumber(item.feePerByte);
})
.map(item => ({ speed: item.speed, feePerByte: item.feePerByte }));
if (items.length === 0) {
return [];
}
return [...items].reverse().map(item => ({
id: item.speed,
amount: item.feePerByte,
}));
},
shouldEstimateWithBridge: () => true,
},
custom: {
inputs: [
{
key: "feePerByte",
type: "number",
unitLabel: "sat/vbyte",
suggestedRange: {
getRange: transaction => {
if (!isRecord(transaction)) return null;
return getSuggestedFeePerByteRange(transaction);
},
},
},
],
getInitialValues: (transaction): Record<string, string> => {
const empty: Record<string, string> = {};
if (!isRecord(transaction)) return empty;
const feePerByte = transaction.feePerByte;
if (isBigNumber(feePerByte) && feePerByte.gt(0)) {
return {
feePerByte: feePerByte.toFixed(),
};
}
const networkInfo = transaction.networkInfo;
if (isRecord(networkInfo) && isRecord(networkInfo.feeItems)) {
const medium = networkInfo.feeItems.medium;
if (isBigNumber(medium) && medium.gt(0)) {
return {
feePerByte: medium.toFixed(),
};
}
}
return {
feePerByte: "",
};
},
buildTransactionPatch: values => {
const feePerByte = new BigNumber(values.feePerByte);
return {
feesStrategy: "custom",
feePerByte:
feePerByte.isNaN() || feePerByte.isNegative()
? new BigNumber(0)
: feePerByte.integerValue(BigNumber.ROUND_DOWN),
};
},
},
};