@ledgerhq/coin-stellar
Version:
Ledger Stellar Coin integration
137 lines • 5.64 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.envelopeFromAnyXDR = exports.createApi = void 0;
const config_1 = __importDefault(require("../config"));
const logic_1 = require("../logic");
const errors_1 = require("@ledgerhq/errors");
const logs_1 = require("@ledgerhq/logs");
const stellar_sdk_1 = require("@stellar/stellar-sdk");
function createApi(config) {
config_1.default.setCoinConfig(() => ({ ...config, status: { type: "active" } }));
return {
broadcast: logic_1.broadcast,
combine: compose,
craftTransaction: craft,
estimateFees: estimate,
getBalance: logic_1.getBalance,
lastBlock: logic_1.lastBlock,
listOperations: operations,
getBlock(_height) {
throw new Error("getBlock is not supported");
},
getBlockInfo(_height) {
throw new Error("getBlockInfo is not supported");
},
};
}
exports.createApi = createApi;
async function craft(transactionIntent, customFees) {
const fees = customFees !== undefined ? customFees : await (0, logic_1.estimateFees)();
// NOTE: check how many memos, throw if more than one?
// if (transactionIntent.memos && transactionIntent.memos.length > 1) {
// throw new Error("Stellar only supports one memo per transaction.");
// }
const memo = "memo" in transactionIntent ? transactionIntent.memo : undefined;
const hasMemoValue = memo && memo.type !== "NO_MEMO";
const tx = await (0, logic_1.craftTransaction)({ address: transactionIntent.sender }, {
type: transactionIntent.type,
recipient: transactionIntent.recipient,
amount: transactionIntent.amount,
fee: fees,
...(transactionIntent.asset.type === "token"
? {
assetCode: transactionIntent.asset.assetCode,
assetIssuer: transactionIntent.asset.assetIssuer,
}
: {}),
memoType: memo?.type,
...(hasMemoValue ? { memoValue: memo.value } : {}),
});
// Note: the API returns the signature base, not the full XDR, see BACK-8727 for more context
return tx.signatureBase;
}
function compose(tx, signature, pubkey) {
if (!pubkey) {
throw new Error("Missing pubkey");
}
// note: accept here `TransactionEnvelope` or `TransactionSignaturePayload`, see BACK-8727 for more context
return (0, logic_1.combine)(envelopeFromAnyXDR(tx, "base64"), signature, pubkey);
}
async function estimate() {
const value = await (0, logic_1.estimateFees)();
return { value };
}
async function operations(address, { minHeight }) {
return operationsFromHeight(address, minHeight);
}
async function operationsFromHeight(address, minHeight) {
const state = {
pageSize: 200,
heightLimit: minHeight,
continueIterations: true,
accumulator: [],
};
// unfortunately, the stellar API does not support an option to filter by min height
// so the only strategy to get ALL operations is to iterate over all of them in descending order
// until we reach the desired minHeight
while (state.continueIterations) {
const options = { limit: state.pageSize, order: "desc", minHeight };
if (state.apiNextCursor) {
options.cursor = state.apiNextCursor;
}
try {
const [operations, nextCursor] = await (0, logic_1.listOperations)(address, options);
state.accumulator.push(...operations);
state.apiNextCursor = nextCursor;
state.continueIterations = nextCursor !== "";
}
catch (e) {
if (e instanceof errors_1.LedgerAPI4xx && e.status === 429) {
(0, logs_1.log)("coin:stellar", "(api/operations): TooManyRequests, retrying in 4s");
await new Promise(resolve => setTimeout(resolve, 4000));
}
else {
throw e;
}
}
}
return [state.accumulator, state.apiNextCursor ? state.apiNextCursor : ""];
}
/**
* Deserialize a transaction envelope, also accepting transaction signature payload form.
*
* @param input serialized `TransactionEnvelope` or `TransactionSignaturePayload`
* @param format serialization encoding
*/
function envelopeFromAnyXDR(input, format) {
try {
return stellar_sdk_1.xdr.TransactionEnvelope.fromXDR(input, format);
}
catch (envelopeError) {
try {
return signatureBaseToEnvelope(stellar_sdk_1.xdr.TransactionSignaturePayload.fromXDR(input, format));
}
catch (signatureBaseError) {
throw new Error(`Failed decoding transaction as an envelope (${envelopeError}) or as a signature base: (${signatureBaseError})`);
}
}
}
exports.envelopeFromAnyXDR = envelopeFromAnyXDR;
/**
* Convert a `TransactionSignaturePayload` into a `TransactionEnvelope`.
*
* @param signatureBase deserialized `TransactionSignaturePayload`
*/
function signatureBaseToEnvelope(signatureBase) {
const tx = signatureBase.taggedTransaction().value();
if (tx instanceof stellar_sdk_1.xdr.Transaction) {
return stellar_sdk_1.xdr.TransactionEnvelope.envelopeTypeTx(new stellar_sdk_1.xdr.TransactionV1Envelope({ tx, signatures: [] }));
}
else {
return stellar_sdk_1.xdr.TransactionEnvelope.envelopeTypeTxFeeBump(new stellar_sdk_1.xdr.FeeBumpTransactionEnvelope({ tx, signatures: [] }));
}
}
//# sourceMappingURL=index.js.map