UNPKG

@ledgerhq/coin-canton

Version:
337 lines 13.4 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getCalTokensCached = exports.getEnabledInstrumentsCached = exports.isTopologyChangeRequiredCached = exports.isPartyAlreadyExists = exports.isPartyNotFound = exports.getNetworkType = exports.getKey = exports.DEFAULT_TAP_REQUEST_AMOUNT = exports.SEPARATOR = void 0; exports.isGatewayEnabled = isGatewayEnabled; exports.getPartyById = getPartyById; exports.getPartyByPubKey = getPartyByPubKey; exports.prepareOnboarding = prepareOnboarding; exports.isTopologyChangeRequired = isTopologyChangeRequired; exports.clearIsTopologyChangeRequiredCache = clearIsTopologyChangeRequiredCache; exports.getEnabledInstruments = getEnabledInstruments; exports.clearEnabledInstrumentsCache = clearEnabledInstrumentsCache; exports.submitOnboarding = submitOnboarding; exports.getBalance = getBalance; exports.getOperations = getOperations; exports.getPendingTransferProposals = getPendingTransferProposals; exports.getTransferPreApproval = getTransferPreApproval; exports.getLedgerEnd = getLedgerEnd; exports.prepare = prepare; exports.prepareTransferRequest = prepareTransferRequest; exports.prepareTransferInstruction = prepareTransferInstruction; exports.preparePreApprovalTransaction = preparePreApprovalTransaction; exports.prepareTapRequest = prepareTapRequest; exports.submit = submit; exports.submitTransferInstruction = submitTransferInstruction; exports.submitPreApprovalTransaction = submitPreApprovalTransaction; exports.submitTapRequest = submitTapRequest; const live_env_1 = require("@ledgerhq/live-env"); const live_network_1 = __importDefault(require("@ledgerhq/live-network")); const cache_1 = require("@ledgerhq/live-network/cache"); const config_1 = __importDefault(require("../config")); const errors_1 = require("../types/errors"); const gateway_1 = require("../types/gateway"); exports.SEPARATOR = "____"; exports.DEFAULT_TAP_REQUEST_AMOUNT = "100000000000000000000000000000000000000"; const getKey = (id, adminId) => `${id}${exports.SEPARATOR}${adminId}`; exports.getKey = getKey; const getGatewayUrl = (currency) => config_1.default.getCoinConfig(currency.id).gatewayUrl; const getNodeId = (currency) => { const overrideNodeId = (0, live_env_1.getEnv)("CANTON_NODE_ID_OVERRIDE"); if (overrideNodeId) { return overrideNodeId; } return config_1.default.getCoinConfig(currency.id).nodeId || "ledger-live-devnet"; }; const getNetworkType = (currency) => config_1.default.getCoinConfig(currency.id).networkType; exports.getNetworkType = getNetworkType; function isGatewayEnabled(currency) { return config_1.default.getCoinConfig(currency.id).useGateway === true; } const isPartyNotFound = (error) => { if (error instanceof Error) { const errorMessage = error.message.toLowerCase().replace(/_/g, " "); return errorMessage.includes("party") && errorMessage.includes("not found"); } return false; }; exports.isPartyNotFound = isPartyNotFound; const isPartyAlreadyExists = (error) => { if (error instanceof Error) { const errorMessage = error.message.toLowerCase().replace(/_/g, " "); return errorMessage.includes("party") && errorMessage.includes("already exists"); } return false; }; exports.isPartyAlreadyExists = isPartyAlreadyExists; const gatewayNetwork = async (req) => { const API_KEY = (0, live_env_1.getEnv)("CANTON_API_KEY"); try { return await (0, live_network_1.default)({ ...req, headers: { ...(req.headers || {}), ...(API_KEY ? { "X-Ledger-Canton-Api-Key": API_KEY } : {}), }, }); } catch (error) { if ((0, exports.isPartyNotFound)(error)) { throw new errors_1.TopologyChangeError("Topology change detected. Re-onboarding required."); } throw error; } }; /* * Parties * @see https://canton-gateway.api.live.ledger.com/docs/openapi/redoc/index.html#tag/Parties */ async function getPartyById(currency, partyId) { return await getParty(currency, partyId, "party-id"); } async function getPartyByPubKey(currency, pubKey) { return await getParty(currency, pubKey, "public-key"); } async function getParty(currency, identifier, by) { const { data } = await gatewayNetwork({ method: "GET", url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/party/${identifier}?by=${by}`, }); return data; } async function prepareOnboarding(currency, pubKey) { const gatewayUrl = getGatewayUrl(currency); const nodeId = getNodeId(currency); const fullUrl = `${gatewayUrl}/v1/node/${nodeId}/onboarding/prepare`; const { data } = await gatewayNetwork({ method: "POST", url: fullUrl, data: { public_key: pubKey, public_key_type: "ed25519", }, }); return data; } async function isTopologyChangeRequired(currency, pubKey) { try { const response = await prepareOnboarding(currency, pubKey); // if response is not undefined (we have a transaction to sign) topology change is required if (response) { return true; } return false; } catch (error) { if ((0, exports.isPartyAlreadyExists)(error)) { return false; } throw error; } } const getIsTopologyChangeRequiredCacheKey = (currency, pubKey) => { const nodeId = getNodeId(currency); return `${pubKey}_${nodeId}`; }; exports.isTopologyChangeRequiredCached = (0, cache_1.makeLRUCache)(isTopologyChangeRequired, getIsTopologyChangeRequiredCacheKey, (0, cache_1.minutes)(10)); function clearIsTopologyChangeRequiredCache(currency, pubKey) { const cacheKey = getIsTopologyChangeRequiredCacheKey(currency, pubKey); exports.isTopologyChangeRequiredCached.clear(cacheKey); } async function getEnabledInstruments(currency) { try { const { data } = await gatewayNetwork({ method: "GET", url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/instruments`, }); return new Set(data.map(({ id, admin }) => (0, exports.getKey)(id, admin))); } catch (error) { // If API fails, return empty array (fail-safe: only native instrument will work) console.error("Failed to fetch enabled instruments:", error); return new Set(); } } const getEnabledInstrumentsCacheKey = (currency) => { const nodeId = getNodeId(currency); return `instruments_${nodeId}`; }; exports.getEnabledInstrumentsCached = (0, cache_1.makeLRUCache)(getEnabledInstruments, getEnabledInstrumentsCacheKey, (0, cache_1.minutes)(15)); function clearEnabledInstrumentsCache(currency) { const cacheKey = getEnabledInstrumentsCacheKey(currency); exports.getEnabledInstrumentsCached.clear(cacheKey); } async function submitOnboarding(currency, publicKey, prepareResponse, { signature, applicationSignature }) { try { const { data } = await gatewayNetwork({ method: "POST", url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/onboarding/submit`, data: { prepare_request: { public_key: publicKey, public_key_type: "ed25519", }, prepare_response: prepareResponse, signature, ...(applicationSignature ? { application_signature: applicationSignature } : {}), }, }); return data; } catch (error) { if ((0, exports.isPartyAlreadyExists)(error)) { // If party already exists, use party_id from prepare response // The network layer strips custom properties from errors, so we can't extract partyId from error return { party: { party_id: prepareResponse.party_id, public_key: publicKey, }, }; } throw error; } } /* * State * @see https://canton-gateway.api.live.ledger.com/docs/openapi/redoc/index.html#tag/State */ async function getBalance(currency, partyId) { const { data } = await gatewayNetwork({ method: "GET", url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/party/${partyId}/balance`, }); return Array.isArray(data) ? data : (data.balances ?? []); } async function getOperations(currency, partyId, options) { const { data } = await gatewayNetwork({ method: "GET", url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/party/${partyId}/operations`, params: options, }); return data; } async function getPendingTransferProposals(currency, partyId) { const { data } = await gatewayNetwork({ method: "GET", url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/party/${partyId}/transfer-proposals?timestamp=${Date.now()}`, }); return data; } async function getTransferPreApproval(currency, partyId) { const { data } = await gatewayNetwork({ method: "GET", url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/party/${partyId}/transfer-preapproval`, }); return data; } async function getLedgerEnd(currency) { const { data } = await gatewayNetwork({ method: "GET", url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/ledger-end`, }); return data; } /* * Transaction * @see https://canton-gateway.api.live.ledger.com/docs/openapi/redoc/index.html#tag/Transaction */ async function prepare(currency, partyId, params) { const { data } = await gatewayNetwork({ method: "POST", url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/party/${partyId}/transaction/prepare`, data: params, }); return data; } async function prepareTransferRequest(currency, partyId, params) { return prepare(currency, partyId, params); } async function prepareTransferInstruction(currency, partyId, params) { return prepare(currency, partyId, params); } async function preparePreApprovalTransaction(currency, partyId) { const { data } = await gatewayNetwork({ method: "POST", url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/party/${partyId}/transaction/prepare`, data: { type: gateway_1.TransactionType.TRANSFER_PRE_APPROVAL_PROPOSAL, receiver: partyId, }, }); return data; } async function prepareTapRequest(currency, { partyId, amount }) { // Default to 1.0 in fixed-point representation (1 * 10^38) const fixedPointAmount = amount ?? exports.DEFAULT_TAP_REQUEST_AMOUNT; const { data } = await gatewayNetwork({ method: "POST", url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/party/${partyId}/transaction/prepare`, data: { amount: fixedPointAmount.toString(), type: gateway_1.TransactionType.TAP_REQUEST, }, }); return data; } async function submit(currency, partyId, serialized, signature) { const { data } = await gatewayNetwork({ method: "POST", url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/party/${partyId}/transaction/submit`, data: { serialized, signature, }, }); return data; } async function submitTransferInstruction(currency, partyId, serialized, signature) { return submit(currency, partyId, serialized, signature); } async function submitPreApprovalTransaction(currency, partyId, { serialized }, signature) { const { data } = await gatewayNetwork({ method: "POST", url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/party/${partyId}/transaction/submit`, data: { serialized, signature, }, }); return { isApproved: true, submissionId: data.submission_id, updateId: data.update_id, }; } async function submitTapRequest(currency, { partyId, serialized, signature }) { const { data } = await gatewayNetwork({ method: "POST", url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/party/${partyId}/transaction/submit`, data: { serialized, signature, }, }); return data; } /** * Fetch Canton tokens from CAL service and create a map of id -> token_identifier */ async function getCalTokens(currency) { const calUrl = (0, live_env_1.getEnv)("CAL_SERVICE_URL"); const { data: calTokens } = await gatewayNetwork({ method: "GET", url: `${calUrl}/v1/tokens?network=${currency.id}&output=id,name,ticker,network,contract_address,token_identifier,units,standard`, }); // Map id -> token_identifier const tokenIdentifierMap = new Map(); for (const token of calTokens) { tokenIdentifierMap.set(token.id, token.token_identifier); } return tokenIdentifierMap; } const getCalTokensCacheKey = (currency) => currency.id; exports.getCalTokensCached = (0, cache_1.makeLRUCache)(getCalTokens, getCalTokensCacheKey, (0, cache_1.minutes)(30)); //# sourceMappingURL=gateway.js.map