@ledgerhq/coin-hedera
Version:
Ledger Hedera Coin integration
192 lines • 9.51 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createApi = createApi;
const rejectBalanceOptions_1 = require("@ledgerhq/coin-module-framework/api/getBalance/rejectBalanceOptions");
const craftTransactionData_1 = require("@ledgerhq/coin-module-framework/logic/craftTransactionData");
const currencies_1 = require("@ledgerhq/cryptoassets/currencies");
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const invariant_1 = __importDefault(require("invariant"));
const validateAddress_1 = require("../bridge/validateAddress");
const config_1 = __importDefault(require("../config"));
const constants_1 = require("../constants");
const logic_1 = require("../logic");
const utils_1 = require("../logic/utils");
const api_1 = require("../network/api");
const utils_2 = require("../network/utils");
function createApi(config, currencyId) {
config_1.default.setCoinConfig(() => ({ ...config, status: { type: "active" } }));
const currency = (0, currencies_1.getCryptoCurrencyById)(currencyId);
return {
broadcast: async (tx) => {
const response = await (0, logic_1.broadcast)(tx);
return Buffer.from(response.transactionHash).toString("base64");
},
combine: logic_1.combine,
craftTransaction: async (txIntent, customFees) => {
(0, invariant_1.default)(!txIntent.useAllAmount, "useAllAmount is not supported");
const { serializedTx } = await (0, logic_1.craftTransaction)({
txIntent,
...(customFees && { customFees }),
config,
});
return {
transaction: serializedTx,
};
},
craftRawTransaction: (_transaction, _sender, _publicKey, _sequence) => {
throw new Error("craftRawTransaction is not supported");
},
estimateFees: async (txIntent) => {
let estimateFeesParams;
const operationType = (0, utils_1.mapIntentToSDKOperation)(txIntent);
if (operationType === constants_1.HEDERA_OPERATION_TYPES.ContractCall) {
estimateFeesParams = { operationType, txIntent };
}
else {
estimateFeesParams = { currency, operationType };
}
const estimatedFee = await (0, logic_1.estimateFees)(estimateFeesParams);
return {
value: BigInt(estimatedFee.tinybars.toString()),
};
},
getBalance: (address, options) => (0, rejectBalanceOptions_1.rejectBalanceOptions)(() => (0, logic_1.getBalance)(currency, address), options),
getBlock: height => {
if (config.useHgraphForErc20) {
return (0, logic_1.getBlockV2)(height);
}
return (0, logic_1.getBlock)(height);
},
getBlockInfo: height => (0, logic_1.getBlockInfo)(height),
lastBlock: () => {
if (config.useHgraphForErc20) {
return (0, logic_1.lastBlockV2)();
}
return (0, logic_1.lastBlock)();
},
listOperations: async (address, { cursor, limit, order, minHeight }) => {
(0, invariant_1.default)(minHeight === 0, "minHeight is not supported");
let latestAccountOperations;
if (config.useHgraphForErc20) {
const evmAddress = await (0, utils_1.toEVMAddress)(address);
(0, invariant_1.default)(evmAddress, `hedera: evm address is missing for ${address}`);
const [mirrorTokens, erc20TokenBalances] = await Promise.all([
api_1.apiClient.getAccountTokens(address),
(0, utils_2.getERC20BalancesForAccountV2)(address),
]);
latestAccountOperations = await (0, logic_1.listOperationsV2)({
currency,
address,
evmAddress,
mirrorTokens,
...(typeof cursor === "string" && { cursor }),
...(typeof limit === "number" && { limit }),
...(typeof order === "string" && { order }),
erc20Tokens: erc20TokenBalances,
fetchAllPages: false,
skipFeesForTokenOperations: true,
useEncodedHash: false,
useSyntheticBlocks: true,
});
}
else {
const mirrorTokens = await api_1.apiClient.getAccountTokens(address);
latestAccountOperations = await (0, logic_1.listOperations)({
currency,
address,
cursor,
limit,
order,
mirrorTokens,
fetchAllPages: false,
skipFeesForTokenOperations: true,
useEncodedHash: false,
useSyntheticBlocks: true,
});
}
const liveOperations = [
...latestAccountOperations.coinOperations,
...latestAccountOperations.tokenOperations,
];
const sortedLiveOperations = [...liveOperations].sort((a, b) => {
const aConsensusTime = a.extra.consensusTimestamp;
const bConsensusTime = b.extra.consensusTimestamp;
const aTime = a.date.getTime();
const bTime = b.date.getTime();
const dateDiff = order === "desc" ? bTime - aTime : aTime - bTime;
if (aConsensusTime && bConsensusTime) {
const aTime = new bignumber_js_1.default(aConsensusTime);
const bTime = new bignumber_js_1.default(bConsensusTime);
const timeDiff = order === "desc" ? bTime.minus(aTime) : aTime.minus(bTime);
// REWARD operations have the same consensus time as operation that triggered them
return timeDiff.isZero() ? dateDiff : timeDiff.toNumber();
}
return dateDiff;
});
const coinFrameworkOperations = sortedLiveOperations.map(liveOp => {
const asset = liveOp.contract
? {
type: liveOp.standard ?? "token",
assetReference: liveOp.contract,
assetOwner: address,
}
: { type: "native" };
// Prefer inferred payer from operation extra, fallback to transaction_id parsing for legacy ops.
let feesPayer = liveOp.extra?.feesPayer;
if (!feesPayer && liveOp.extra?.transactionId)
feesPayer = (0, utils_1.extractInitiator)(liveOp.extra.transactionId);
// REWARD operations append a suffix to the tx.hash to ensure uniqueness
const hash = liveOp.type === "REWARD"
? liveOp.hash.replace(constants_1.STAKING_REWARD_HASH_SUFFIX, "")
: liveOp.hash;
return {
id: liveOp.id,
type: liveOp.type,
senders: liveOp.senders,
recipients: liveOp.recipients,
value: (0, utils_1.getOperationValue)({ asset, operation: liveOp }),
asset,
details: {
...liveOp.extra,
ledgerOpType: liveOp.type,
...(asset.type !== "native" && { assetAmount: liveOp.value.toFixed(0) }),
...(liveOp.extra.stakedAmount && {
stakedAmount: BigInt(liveOp.extra.stakedAmount.toFixed(0)),
}),
},
tx: {
hash,
fees: BigInt(liveOp.fee.toFixed(0)),
...(feesPayer && { feesPayer }),
date: liveOp.date,
block: {
height: liveOp.blockHeight ?? constants_1.HARDCODED_BLOCK_HEIGHT,
hash: liveOp.blockHash ?? (0, utils_1.getBlockHash)(liveOp.blockHeight ?? constants_1.HARDCODED_BLOCK_HEIGHT),
time: liveOp.date,
},
failed: liveOp.hasFailed ?? false,
},
};
});
return {
items: coinFrameworkOperations,
next: latestAccountOperations.nextCursor || undefined,
};
},
getValidators: cursor => (0, logic_1.getValidators)(cursor),
getStakes: async (address) => (0, logic_1.getStakes)(address),
getRewards: async (address, cursor) => (0, logic_1.getRewards)(address, cursor),
validateIntent: async (_transactionIntent, _balances, _customFees) => {
throw new Error("validateIntent is not supported");
},
getNextSequence: async (_address) => {
throw new Error("getNextSequence is not supported");
},
validateAddress: validateAddress_1.validateAddress,
craftTransactionData: craftTransactionData_1.craftTransactionData,
};
}
//# sourceMappingURL=index.js.map