@ledgerhq/coin-filecoin
Version:
Ledger Filecoin Coin integration
118 lines • 4.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.craftTransaction = craftTransaction;
const message_1 = require("iso-filecoin/message");
const tokenAccounts_1 = require("../../erc20/tokenAccounts");
const addresses_1 = require("../../network/addresses");
const types_1 = require("../../types");
const api_1 = require("../../network/api");
const getNextSequence_1 = require("../account/getNextSequence");
// 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;
// Serialisation format (symmetric with combine.ts):
// Output `transaction` field = JSON.stringify({ cbor, message }) where
// - cbor: base64-encoded CBOR of the Filecoin Message (what the signer signs)
// - message: the message fields in Lotus shape (what combine uses to rebuild
// the BroadcastTransactionRequest without re-deserialising CBOR — iso-filecoin
// v4 exposes Message.serialize() but no static deserialize equivalent).
async function craftTransaction(intent, customFees) {
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 nonce = await (0, getNextSequence_1.getNextSequence)(from);
let to;
let value;
let method;
let params;
const assetType = intent.asset.type;
if (assetType === "native") {
const recipientValidation = (0, addresses_1.validateAddress)(intent.recipient ?? "");
if (!recipientValidation.isValid) {
throw new Error(`Invalid recipient address: ${intent.recipient}`);
}
to = recipientValidation.parsedAddress.toString();
value = intent.amount.toString();
method = METHOD_TRANSFER;
}
else if (assetType === "erc20") {
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}`);
}
to = contractValidation.parsedAddress.toString();
value = "0";
method = METHOD_INVOKE_EVM;
const recipientEth = intent.recipient?.startsWith("0x")
? intent.recipient
: (() => {
try {
return (0, addresses_1.convertAddressFilToEth)(intent.recipient ?? "");
}
catch {
throw new Error(`Invalid token recipient: ${intent.recipient}`);
}
})();
const abiEncoded = (0, tokenAccounts_1.abiEncodeTransferParams)(recipientEth, intent.amount.toString());
params = (0, tokenAccounts_1.encodeTxnParams)(abiEncoded);
}
else {
throw new Error(`Unsupported asset type: ${String(assetType)}`);
}
// Fetch gas estimates (or use custom fees if provided)
let gasFeeCap;
let gasLimit;
let gasPremium;
if (customFees?.parameters) {
gasFeeCap = String(customFees.parameters["gasFeeCap"] ?? "0");
gasLimit = Number(customFees.parameters["gasLimit"] ?? 0);
gasPremium = String(customFees.parameters["gasPremium"] ?? "0");
}
else {
const fees = await (0, api_1.fetchEstimatedFees)({
from,
to,
methodNum: method,
blockIncl: types_1.BroadcastBlockIncl,
value,
...(params ? { params } : {}),
});
gasFeeCap = fees.gas_fee_cap;
gasLimit = fees.gas_limit;
gasPremium = fees.gas_premium;
}
const message = new message_1.Message({
to,
from,
nonce: Number(nonce),
value,
gasFeeCap,
gasLimit,
gasPremium,
method,
params,
version: 0,
});
const cbor = Buffer.from(message.serialize()).toString("base64");
const messageJson = {
version: message.version ?? 0,
to: message.to,
from: message.from,
nonce: message.nonce,
value: message.value,
gasLimit: message.gasLimit,
gasFeeCap: message.gasFeeCap,
gasPremium: message.gasPremium,
method: message.method,
params: message.params ?? "",
};
return {
transaction: JSON.stringify({ cbor, message: messageJson }),
};
}
//# sourceMappingURL=craftTransaction.js.map