UNPKG

@ledgerhq/coin-cardano

Version:
288 lines 10.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.isProtocolParamsValid = exports.isValidNumString = exports.getBech32PoolId = exports.decodeTokenName = exports.isHexString = exports.getMemoFromTx = exports.getAccountChange = exports.isTestnet = exports.getOperationType = exports.getAccountStakeCredential = exports.getTokenDiff = exports.mergeTokens = exports.getEpoch = exports.getTTL = exports.getAbsoluteSlot = exports.isValidAddress = exports.getBaseAddress = exports.getCredentialKey = exports.getExtendedPublicKeyFromHex = exports.getBipPathString = exports.getBipPath = exports.isAlreadyStaking = exports.canStake = exports.getBipPathFromString = void 0; const constants_1 = require("./constants"); const typhonjs_1 = require("@stricahq/typhonjs"); const types_1 = require("./types"); const bip32ed25519_1 = require("@stricahq/bip32ed25519"); const bignumber_js_1 = __importDefault(require("bignumber.js")); const networks_1 = require("./networks"); const groupBy_1 = __importDefault(require("lodash/groupBy")); const cryptoassets_1 = require("@ledgerhq/cryptoassets"); const ShelleyTypeAddress_1 = __importDefault(require("@stricahq/typhonjs/dist/address/ShelleyTypeAddress")); const bech32_1 = __importDefault(require("bech32")); /** * returns BipPath object with account, chain and index field for cardano * * @param {string} path */ function getBipPathFromString(path) { const regEx = new RegExp(`^${constants_1.CARDANO_PURPOSE}'/${constants_1.CARDANO_COIN_TYPE}'/(\\d*)'/([012])/(\\d*)`); const result = path.match(regEx); if (result === null) { throw new Error("Invalid derivation path"); } return getBipPath({ account: parseInt(result[1]), chain: parseInt(result[2]), index: parseInt(result[3]), }); } exports.getBipPathFromString = getBipPathFromString; /** * * @returns true if the account can stake, false otherwise */ function canStake(account) { return account.balance.gt(0); } exports.canStake = canStake; /** * * @returns true if account is staked, false otherwise */ function isAlreadyStaking(account) { return !!account.cardanoResources?.delegation?.poolId; } exports.isAlreadyStaking = isAlreadyStaking; /** * returns complete bipPath with purpose, coin, account, chain and index for cardano */ function getBipPath({ account, chain, index, }) { return { purpose: constants_1.CARDANO_PURPOSE, coin: constants_1.CARDANO_COIN_TYPE, account, chain, index, }; } exports.getBipPath = getBipPath; /** * returns bipPathString from account, chain and index for cardano */ function getBipPathString({ account, chain, index, }) { return `${constants_1.CARDANO_PURPOSE}'/${constants_1.CARDANO_COIN_TYPE}'/${account}'/${chain}/${index}`; } exports.getBipPathString = getBipPathString; function getExtendedPublicKeyFromHex(keyHex) { return bip32ed25519_1.Bip32PublicKey.fromBytes(Buffer.from(keyHex, "hex")); } exports.getExtendedPublicKeyFromHex = getExtendedPublicKeyFromHex; function getCredentialKey(accountKey, path) { const keyBytes = accountKey.derive(path.chain).derive(path.index).toPublicKey().hash(); const pubKeyHex = keyBytes.toString("hex"); return { key: pubKeyHex, path, }; } exports.getCredentialKey = getCredentialKey; /** * returns cardano base address by paymentKey and stakeKey */ function getBaseAddress({ networkId, paymentCred, stakeCred, }) { const paymentCredential = { hash: Buffer.from(paymentCred.key, "hex"), type: typhonjs_1.types.HashType.ADDRESS, bipPath: paymentCred.path, }; const stakeCredential = { hash: Buffer.from(stakeCred.key, "hex"), type: typhonjs_1.types.HashType.ADDRESS, bipPath: stakeCred.path, }; return new typhonjs_1.address.BaseAddress(networkId, paymentCredential, stakeCredential); } exports.getBaseAddress = getBaseAddress; /** * Returns true if address is a valid * * @param {string} address */ const isValidAddress = (address, networkId) => { if (!address) return false; try { const cardanoAddress = typhonjs_1.utils.getAddressFromString(address); if (cardanoAddress instanceof ShelleyTypeAddress_1.default) { const addressNetworkId = Number(cardanoAddress.getHex().toLowerCase().charAt(1)); if (addressNetworkId !== networkId) { return false; } } } catch (error) { return false; } return true; }; exports.isValidAddress = isValidAddress; const getAbsoluteSlot = function (networkName, time) { const networkParams = (0, networks_1.getNetworkParameters)(networkName); const byronChainEndSlots = networkParams.shelleyStartEpoch * networkParams.byronSlotsPerEpoch; const byronChainEndTime = byronChainEndSlots * networkParams.byronSlotDuration; const shelleyChainTime = time.getTime() - networkParams.chainStartTime - byronChainEndTime; const shelleyChainSlots = Math.floor(shelleyChainTime / networkParams.shelleySlotDuration); return byronChainEndSlots + shelleyChainSlots; }; exports.getAbsoluteSlot = getAbsoluteSlot; /** * Returns the time to live for transaction * * @returns {number} */ function getTTL(networkName) { return (0, exports.getAbsoluteSlot)(networkName, new Date()) + constants_1.TTL_GAP; } exports.getTTL = getTTL; function getEpoch(networkName, time) { const networkParams = (0, networks_1.getNetworkParameters)(networkName); const chainTime = time.getTime() - networkParams.chainStartTime; const epoch = Math.floor(chainTime / (networkParams.shelleySlotsPerEpoch * networkParams.shelleySlotDuration)); return epoch; } exports.getEpoch = getEpoch; function mergeTokens(tokens) { return Object.values((0, groupBy_1.default)(tokens, t => `${t.policyId}${t.assetName}`)).map(similarTokens => ({ policyId: similarTokens[0].policyId, assetName: similarTokens[0].assetName, amount: similarTokens.reduce((total, token) => total.plus(token.amount), new bignumber_js_1.default(0)), })); } exports.mergeTokens = mergeTokens; /** * @param { Array<TyphonTypes.Token> } b * @param { Array<TyphonTypes.Token> } a * @returns a - b */ function getTokenDiff(a, b) { return mergeTokens(a.concat(b.map(t => ({ ...t, amount: t.amount.negated() })))).filter(t => !t.amount.eq(0)); } exports.getTokenDiff = getTokenDiff; function getAccountStakeCredential(xpub, index) { const accountXPubKey = getExtendedPublicKeyFromHex(xpub); const keyPath = getCredentialKey(accountXPubKey, getBipPath({ account: index, chain: types_1.StakeChain.stake, index: constants_1.STAKING_ADDRESS_INDEX, })); return { key: keyPath.key, path: keyPath.path, }; } exports.getAccountStakeCredential = getAccountStakeCredential; function getOperationType({ valueChange, fees, }) { return valueChange.isNegative() ? valueChange.absoluteValue().eq(fees) ? "FEES" : "OUT" : valueChange.isPositive() ? "IN" : "NONE"; } exports.getOperationType = getOperationType; function isTestnet(currency) { return (0, cryptoassets_1.getCryptoCurrencyById)(currency.id).isTestnetFor ? true : false; } exports.isTestnet = isTestnet; function getAccountChange(t, accountCredentialsMap) { let accountInputAda = new bignumber_js_1.default(0); const accountInputTokens = []; t.inputs.forEach(i => { if (accountCredentialsMap[i.paymentKey]) { accountInputAda = accountInputAda.plus(i.value); accountInputTokens.push(...i.tokens.map(t => ({ assetName: t.assetName, policyId: t.policyId, amount: new bignumber_js_1.default(t.value), }))); } }); let accountOutputAda = new bignumber_js_1.default(0); const accountOutputTokens = []; t.outputs.forEach(o => { if (accountCredentialsMap[o.paymentKey]) { accountOutputAda = accountOutputAda.plus(o.value); accountOutputTokens.push(...o.tokens.map(t => ({ assetName: t.assetName, policyId: t.policyId, amount: new bignumber_js_1.default(t.value), }))); } }); return { ada: accountOutputAda.minus(accountInputAda), tokens: getTokenDiff(accountOutputTokens, accountInputTokens), }; } exports.getAccountChange = getAccountChange; function getMemoFromTx(tx) { let memo; const metadataValue = tx.metadata?.data.find(m => m.label === constants_1.MEMO_LABEL.toString()); if (metadataValue) { try { const parsedValue = JSON.parse(metadataValue.value); if (parsedValue.msg && Array.isArray(parsedValue.msg) && parsedValue.msg.length) { memo = parsedValue.msg.join(", "); } // eslint-disable-next-line no-empty } catch (e) { } } return memo; } exports.getMemoFromTx = getMemoFromTx; function isHexString(value) { const regExp = /^[0-9a-fA-F]+$/; return regExp.test(value); } exports.isHexString = isHexString; function decodeTokenName(assetName) { if (assetName.length > 0) { const bytes = [...Buffer.from(assetName, "hex")]; if (bytes.filter(byte => byte <= 32 || byte >= 127).length === 0) { return String.fromCharCode(...bytes); } } return assetName; } exports.decodeTokenName = decodeTokenName; function getBech32PoolId(poolId, networkName) { const networkParams = (0, networks_1.getNetworkParameters)(networkName); const words = bech32_1.default.toWords(Buffer.from(poolId, "hex")); const encoded = bech32_1.default.encode(networkParams.poolIdPrefix, words, 1000); return encoded; } exports.getBech32PoolId = getBech32PoolId; function isValidNumString(value) { if (typeof value !== "string" && typeof value !== "number") return false; if (isNaN(Number(value))) return false; if (new bignumber_js_1.default(value).isNaN()) return false; return true; } exports.isValidNumString = isValidNumString; function isProtocolParamsValid(pp) { const paramsRequiredCheck = [ pp.minFeeA, pp.minFeeB, pp.stakeKeyDeposit, pp.lovelacePerUtxoWord, pp.collateralPercent, pp.priceSteps, pp.priceMem, pp.maxTxSize, pp.maxValueSize, pp.utxoCostPerByte, ]; return paramsRequiredCheck.every(isValidNumString); } exports.isProtocolParamsValid = isProtocolParamsValid; //# sourceMappingURL=logic.js.map