@ledgerhq/coin-filecoin
Version:
Ledger Filecoin Coin integration
158 lines • 6.95 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateTokenTxnParams = exports.abiEncodeTransferParams = exports.encodeTxnParams = exports.buildTokenAccounts = exports.erc20TxnToOperation = void 0;
const cbor_1 = __importDefault(require("@zondax/cbor"));
const api_1 = require("../api");
const invariant_1 = __importDefault(require("invariant"));
const types_1 = require("../types");
const index_1 = require("@ledgerhq/coin-framework/account/index");
const tokens_1 = require("@ledgerhq/cryptoassets/tokens");
const logs_1 = require("@ledgerhq/logs");
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const operation_1 = require("@ledgerhq/coin-framework/operation");
const network_1 = require("../network");
const ethers_1 = require("ethers");
const ERC20_json_1 = __importDefault(require("./ERC20.json"));
const errors_1 = require("@ledgerhq/errors");
const utils_1 = require("../bridge/utils");
const utils_2 = require("../common-logic/utils");
const erc20TxnToOperation = (tx, address, accountId, unit) => {
try {
const { to, from, timestamp, tx_hash, tx_cid, amount, height, status } = tx;
const value = (0, utils_2.valueFromUnit)(new bignumber_js_1.default(amount), unit);
const isSending = address.toLowerCase() === from.toLowerCase();
const isReceiving = address.toLowerCase() === to.toLowerCase();
const fee = new bignumber_js_1.default(0);
const date = new Date(timestamp * 1000);
const hash = tx_cid ?? tx_hash;
const hasFailed = status !== types_1.TxStatus.Ok;
const ops = [];
if (isSending) {
ops.push({
id: (0, operation_1.encodeOperationId)(accountId, hash, "OUT"),
hash,
type: "OUT",
value: value,
fee,
blockHeight: height,
blockHash: "",
accountId,
senders: [from],
recipients: [to],
date,
hasFailed,
extra: {},
});
}
if (isReceiving) {
ops.push({
id: (0, operation_1.encodeOperationId)(accountId, hash, "IN"),
hash,
type: "IN",
value,
fee,
blockHeight: height,
blockHash: "",
accountId,
senders: [from],
recipients: [to],
date,
hasFailed,
extra: {},
});
}
(0, invariant_1.default)(ops, "filecoin operation is not defined");
return ops;
}
catch (e) {
(0, logs_1.log)("error", "filecoin error converting erc20 transaction to operation", e);
return [];
}
};
exports.erc20TxnToOperation = erc20TxnToOperation;
async function buildTokenAccounts(filAddr, parentAccountId, initialAccount) {
try {
const transfers = await (0, api_1.fetchERC20Transactions)(filAddr);
const transfersUntangled = transfers.reduce((prev, curr) => {
curr.contract_address = curr.contract_address.toLowerCase();
if (prev[curr.contract_address]) {
prev[curr.contract_address] = [...prev[curr.contract_address], curr];
}
else {
prev[curr.contract_address] = [curr];
}
return prev;
}, {});
const subs = [];
for (const [cAddr, txns] of Object.entries(transfersUntangled)) {
const token = (0, tokens_1.findTokenByAddressInCurrency)(cAddr, "filecoin");
if (!token) {
(0, logs_1.log)("error", `filecoin token not found, addr: ${cAddr}`);
continue;
}
const balance = await (0, api_1.fetchERC20TokenBalance)(filAddr, cAddr);
const bnBalance = new bignumber_js_1.default(balance.toString());
const tokenAccountId = (0, index_1.encodeTokenAccountId)(parentAccountId, token);
const operations = txns
.flatMap(txn => (0, exports.erc20TxnToOperation)(txn, filAddr, tokenAccountId, token.units[0]))
.flat()
.sort((a, b) => b.date.getTime() - a.date.getTime());
if (operations.length === 0 && bnBalance.isZero()) {
continue;
}
const maybeExistingSubAccount = initialAccount &&
initialAccount.subAccounts &&
initialAccount.subAccounts.find(a => a.id === tokenAccountId);
const sub = {
type: utils_1.AccountType.TokenAccount,
id: tokenAccountId,
parentId: parentAccountId,
token,
balance: bnBalance,
spendableBalance: bnBalance,
operationsCount: txns.length,
operations,
pendingOperations: maybeExistingSubAccount ? maybeExistingSubAccount.pendingOperations : [],
creationDate: operations.length > 0 ? operations[0].date : new Date(),
swapHistory: maybeExistingSubAccount ? maybeExistingSubAccount.swapHistory : [],
balanceHistoryCache: index_1.emptyHistoryCache, // calculated in the jsHelpers
};
subs.push(sub);
}
return subs;
}
catch (e) {
(0, logs_1.log)("error", "filecoin error building token accounts", e);
return [];
}
}
exports.buildTokenAccounts = buildTokenAccounts;
const encodeTxnParams = (abiEncodedParams) => {
(0, logs_1.log)("debug", `filecoin/abiEncodedParams: ${abiEncodedParams}`);
if (!abiEncodedParams) {
throw new Error("Cannot encode empty abi encoded params");
}
const buffer = Buffer.from(abiEncodedParams.slice(2), "hex"); // buffer/byte array
const dataEncoded = cbor_1.default.encode(buffer);
return dataEncoded.toString("base64");
};
exports.encodeTxnParams = encodeTxnParams;
const abiEncodeTransferParams = (recipient, amount) => {
const contract = new ethers_1.ethers.Interface(ERC20_json_1.default);
const data = contract.encodeFunctionData("transfer", [recipient, amount]);
return data;
};
exports.abiEncodeTransferParams = abiEncodeTransferParams;
const generateTokenTxnParams = (recipient, amount) => {
(0, logs_1.log)("debug", "generateTokenTxnParams", { recipient, amount: amount.toString() });
if (!recipient) {
throw new errors_1.RecipientRequired();
}
recipient = (0, network_1.convertAddressFilToEth)(recipient);
return (0, exports.abiEncodeTransferParams)(recipient, amount.toString());
};
exports.generateTokenTxnParams = generateTokenTxnParams;
//# sourceMappingURL=tokenAccounts.js.map