@stricahq/typhonjs
Version:
Pure JS Cardano Wallet library
283 lines (282 loc) • 13.4 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createPlutusDataCbor = exports.createAuxiliaryDataCbor = exports.getMaximumTokenSets = exports.getAddressFromString = exports.decodeBech32 = exports.getAddressFromHex = exports.calculateMinUtxoAmountBabbage = exports.calculateMinUtxoAmount = exports.getOutputValueSize = void 0;
const buffer_1 = require("buffer");
const cbors = __importStar(require("@stricahq/cbors"));
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const _ = require("lodash");
const bs58_1 = __importDefault(require("bs58"));
const bech32_1 = require("bech32");
const constants_1 = require("../constants");
const BaseAddress_1 = __importDefault(require("../address/BaseAddress"));
const ByronAddress_1 = __importDefault(require("../address/ByronAddress"));
const EnterpriseAddress_1 = __importDefault(require("../address/EnterpriseAddress"));
const PointerAddress_1 = __importDefault(require("../address/PointerAddress"));
const helpers_1 = require("./helpers");
const types_1 = require("../types");
const RewardAddress_1 = __importDefault(require("../address/RewardAddress"));
const encoder_1 = require("./encoder");
const getOutputValueSize = (adaAmount, tokens) => {
const encodedAmount = tokens.length > 0 ? [adaAmount, (0, encoder_1.encodeOutputTokens)(tokens)] : adaAmount;
return cbors.Encoder.encode(encodedAmount).byteLength;
};
exports.getOutputValueSize = getOutputValueSize;
const calculateMinUtxoAmount = (tokens, lovelacePerUtxoWord, hasPlutusDataHash) => {
const uniqueTokens = (0, helpers_1.getUniqueTokens)(tokens);
const roundupBytesToWords = (x) => Math.floor((x + 7) / 8);
const coinSize = 2;
let utxoEntrySizeWithoutVal = 27;
if (hasPlutusDataHash) {
utxoEntrySizeWithoutVal += 10;
}
const adaOnlyUtxoSize = utxoEntrySizeWithoutVal + coinSize;
const tokenBundle = _(uniqueTokens)
.groupBy(({ policyId }) => policyId)
.value();
const policyCount = _.reduce(tokenBundle, (result) => {
result += 1;
return result;
}, 0);
// sumAssetNameLengths must be computed per-policy: the same asset name stored under
// two different policies occupies space in two separate CBOR maps, so it counts twice.
// ref: https://cardano-ledger.readthedocs.io/en/latest/explanations/min-utxo-mary.html
const assetNameSize = _.reduce(tokenBundle, (totalSum, policyTokens) => {
// Deduplicate asset names within this policy, then sum their byte lengths.
// Empty asset names (0-char) contribute 0 bytes per spec (sumAssetNameLengths).
const uniqueNamesInPolicy = new Set(policyTokens.map((t) => t.assetName));
const policyAssetNameSize = [...uniqueNamesInPolicy].reduce((sum, assetName) => sum + buffer_1.Buffer.from(assetName, "hex").length, 0);
return totalSum + policyAssetNameSize;
}, 0);
const policyIdSize = 28;
const size = 6 + roundupBytesToWords(uniqueTokens.length * 12 + assetNameSize + policyCount * policyIdSize);
const minUtxo = lovelacePerUtxoWord.toNumber() * adaOnlyUtxoSize;
if (uniqueTokens.length === 0) {
return new bignumber_js_1.default(minUtxo);
}
const minUtxoWithTokens = lovelacePerUtxoWord.toNumber() * (utxoEntrySizeWithoutVal + size);
return bignumber_js_1.default.max(minUtxo, minUtxoWithTokens);
};
exports.calculateMinUtxoAmount = calculateMinUtxoAmount;
const calculateMinUtxoAmountBabbage = (output, utxoCostPerByte) => {
const sizeOutput = Object.assign(Object.assign({}, output), { amount: new bignumber_js_1.default(constants_1.maxAdaAmount) });
const minADA = new bignumber_js_1.default(160 + cbors.Encoder.encode((0, encoder_1.encodeOutput)(sizeOutput)).length).multipliedBy(utxoCostPerByte);
return minADA;
};
exports.calculateMinUtxoAmountBabbage = calculateMinUtxoAmountBabbage;
const getAddressFromHex = (hexAddress) => {
const hexAddressString = hexAddress.toString("hex");
const typeHex = hexAddressString.toLowerCase().charAt(0);
const networkId = Number(hexAddressString.toLowerCase().charAt(1));
let stakeCredential;
let paymentCredential;
switch (typeHex) {
case "e":
stakeCredential = {
hash: buffer_1.Buffer.from(hexAddressString.slice(2).slice(0, 56), "hex"),
type: types_1.HashType.ADDRESS,
};
return new RewardAddress_1.default(networkId, stakeCredential);
case "f":
stakeCredential = {
hash: buffer_1.Buffer.from(hexAddressString.slice(2).slice(0, 56), "hex"),
type: types_1.HashType.SCRIPT,
};
return new RewardAddress_1.default(networkId, stakeCredential);
case "7": {
paymentCredential = {
hash: buffer_1.Buffer.from(hexAddressString.slice(2), "hex"),
type: types_1.HashType.SCRIPT,
};
return new EnterpriseAddress_1.default(networkId, paymentCredential);
}
case "6": {
paymentCredential = {
hash: buffer_1.Buffer.from(hexAddressString.slice(2), "hex"),
type: types_1.HashType.ADDRESS,
};
return new EnterpriseAddress_1.default(networkId, paymentCredential);
}
case "5": {
paymentCredential = {
hash: buffer_1.Buffer.from(hexAddressString.slice(2).slice(0, 56), "hex"),
type: types_1.HashType.SCRIPT,
};
const vlq = hexAddressString.slice(2).slice(56);
return new PointerAddress_1.default(networkId, paymentCredential, vlq);
}
case "4": {
paymentCredential = {
hash: buffer_1.Buffer.from(hexAddressString.slice(2).slice(0, 56), "hex"),
type: types_1.HashType.ADDRESS,
};
const vlq = hexAddressString.slice(2).slice(56);
return new PointerAddress_1.default(networkId, paymentCredential, vlq);
}
case "3":
paymentCredential = {
hash: buffer_1.Buffer.from(hexAddressString.slice(2).slice(0, 56), "hex"),
type: types_1.HashType.SCRIPT,
};
stakeCredential = {
hash: buffer_1.Buffer.from(hexAddressString.slice(2).slice(56), "hex"),
type: types_1.HashType.SCRIPT,
};
return new BaseAddress_1.default(networkId, paymentCredential, stakeCredential);
case "2":
paymentCredential = {
hash: buffer_1.Buffer.from(hexAddressString.slice(2).slice(0, 56), "hex"),
type: types_1.HashType.ADDRESS,
};
stakeCredential = {
hash: buffer_1.Buffer.from(hexAddressString.slice(2).slice(56), "hex"),
type: types_1.HashType.SCRIPT,
};
return new BaseAddress_1.default(networkId, paymentCredential, stakeCredential);
case "1":
paymentCredential = {
hash: buffer_1.Buffer.from(hexAddressString.slice(2).slice(0, 56), "hex"),
type: types_1.HashType.SCRIPT,
};
stakeCredential = {
hash: buffer_1.Buffer.from(hexAddressString.slice(2).slice(56), "hex"),
type: types_1.HashType.ADDRESS,
};
return new BaseAddress_1.default(networkId, paymentCredential, stakeCredential);
case "0":
paymentCredential = {
hash: buffer_1.Buffer.from(hexAddressString.slice(2).slice(0, 56), "hex"),
type: types_1.HashType.ADDRESS,
};
stakeCredential = {
hash: buffer_1.Buffer.from(hexAddressString.slice(2).slice(56), "hex"),
type: types_1.HashType.ADDRESS,
};
return new BaseAddress_1.default(networkId, paymentCredential, stakeCredential);
case "8":
return new ByronAddress_1.default(hexAddress);
default:
throw new Error("Unsupported address type");
}
};
exports.getAddressFromHex = getAddressFromHex;
const decodeBech32 = (bech32Address) => {
const decoded = bech32_1.bech32.decode(bech32Address, 114);
const decodedBech = bech32_1.bech32.fromWords(decoded.words);
const decodedAddress = buffer_1.Buffer.from(decodedBech);
return {
prefix: decoded.prefix,
value: decodedAddress,
};
};
exports.decodeBech32 = decodeBech32;
const getAddressFromString = (address) => {
try {
const byronAddress = buffer_1.Buffer.from(bs58_1.default.decode(address));
try {
cbors.Decoder.decode(byronAddress);
}
catch (e) {
throw new Error("Invalid Byron Address");
}
return new ByronAddress_1.default(byronAddress);
}
catch (error) {
try {
const decodeAddr = (0, exports.decodeBech32)(address);
if (decodeAddr.prefix === "addr" ||
decodeAddr.prefix === "addr_test" ||
decodeAddr.prefix === "stake" ||
decodeAddr.prefix === "stake_test") {
return (0, exports.getAddressFromHex)(decodeAddr.value);
}
throw new Error("Invalid Address");
}
catch (err) {
throw new Error("Invalid Address");
}
}
};
exports.getAddressFromString = getAddressFromString;
const getMaximumTokenSets = (oTokens, maxValueSizePP) => {
const tokens = _.cloneDeep(oTokens);
const result = [];
while (tokens.length > 0) {
const tokenArray = [];
const tokenLengthFixed = tokens.length;
for (let i = 0; i < tokenLengthFixed; i += 1) {
const token = tokens.shift();
if (token) {
// set default value as full token
let newToken = token;
// if the token amount is more than the max amount (int), only use max amount token
// add remaining amount of tokens into another output set
if (token.amount.gte(constants_1.maxTokenAmount)) {
newToken = {
assetName: token.assetName,
policyId: token.policyId,
amount: new bignumber_js_1.default(constants_1.maxTokenAmount),
};
}
// calculate the current token set size
const tokenArrayOutputSize = (0, exports.getOutputValueSize)(new bignumber_js_1.default(constants_1.maxAdaAmount), [
...tokenArray,
newToken,
]);
// only add the token to the current set if its under maxValueSize limit
if (tokenArrayOutputSize < maxValueSizePP) {
tokenArray.push(newToken);
// if the above token used in this set had max value
// add the remaining token amount for the next set
if (token.amount.gte(constants_1.maxTokenAmount)) {
token.amount = token.amount.minus(constants_1.maxTokenAmount);
tokens.push(token);
}
}
else {
// add the popped token back to main list, since it didn't make it into the current set
tokens.push(token);
}
// while modifying this func, make sure to handle the case above, no logic must follow this line
}
}
result.push(tokenArray);
}
return result;
};
exports.getMaximumTokenSets = getMaximumTokenSets;
const createAuxiliaryDataCbor = (auxiliaryData) => {
const encodedAuxData = (0, encoder_1.encodeAuxiliaryData)(auxiliaryData);
return cbors.Encoder.encode(encodedAuxData);
};
exports.createAuxiliaryDataCbor = createAuxiliaryDataCbor;
const createPlutusDataCbor = (plutusData) => {
const encodedPlutusData = (0, encoder_1.encodePlutusData)(plutusData);
return cbors.Encoder.encode(encodedPlutusData);
};
exports.createPlutusDataCbor = createPlutusDataCbor;