@ledgerhq/coin-multiversx
Version:
Ledger MultiversX Coin integration
283 lines • 12.2 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.broadcastTransaction = exports.getFees = exports.getESDTOperations = exports.hasESDTTokens = exports.getAccountDelegations = exports.getAccountESDTTokens = exports.getEGLDOperations = exports.getAccountNonce = exports.getNetworkConfig = exports.getProviders = exports.getAccount = void 0;
const operation_1 = require("@ledgerhq/coin-framework/operation");
const index_1 = require("@ledgerhq/coin-framework/serialization/index");
const cryptoassets_1 = require("@ledgerhq/cryptoassets");
const live_env_1 = require("@ledgerhq/live-env");
const sdk_core_1 = require("@multiversx/sdk-core");
const bignumber_js_1 = require("bignumber.js");
const constants_1 = require("../constants");
const types_1 = require("../types");
const binary_utils_1 = require("../utils/binary.utils");
const apiCalls_1 = __importDefault(require("./apiCalls"));
const multiversx_account_1 = require("./dtos/multiversx-account");
const api = new apiCalls_1.default((0, live_env_1.getEnv)("MULTIVERSX_API_ENDPOINT"), (0, live_env_1.getEnv)("MULTIVERSX_DELEGATION_API_ENDPOINT"));
const networkConfig = { clientName: "ledger-live" };
const proxy = new sdk_core_1.ApiNetworkProvider((0, live_env_1.getEnv)("MULTIVERSX_API_ENDPOINT"), networkConfig);
/**
* Get account balances and nonce
*/
const getAccount = async (addr) => {
const { balance, nonce, isGuarded } = await api.getAccountDetails(addr);
const blockHeight = await api.getBlockchainBlockHeight();
const account = new multiversx_account_1.MultiversXAccount(new bignumber_js_1.BigNumber(balance), nonce, isGuarded, blockHeight);
return account;
};
exports.getAccount = getAccount;
const getProviders = async () => {
const providers = await api.getProviders();
return providers;
};
exports.getProviders = getProviders;
const getNetworkConfig = async () => {
return await proxy.getNetworkConfig();
};
exports.getNetworkConfig = getNetworkConfig;
const getAccountNonce = async (addr) => {
const address = new sdk_core_1.Address(addr);
const account = await proxy.getAccount(address);
return account.nonce;
};
exports.getAccountNonce = getAccountNonce;
/**
* Returns true if account is the signer
*/
function isSender(transaction, addr) {
return transaction.sender === addr;
}
function isSelfSend(transaction) {
return (!!transaction.sender && !!transaction.receiver && transaction.sender === transaction.receiver);
}
/**
* Map transaction to an Operation Type
*/
function getEGLDOperationType(transaction, addr) {
if (transaction.action && transaction.action.category === "stake") {
const stakeAction = transaction.action.name;
switch (stakeAction) {
case "delegate":
return "DELEGATE";
case "unDelegate":
return "UNDELEGATE";
case "withdraw":
return "WITHDRAW_UNBONDED";
case "claimRewards":
return "REWARD";
case "reDelegateRewards":
return "DELEGATE";
}
}
return isSender(transaction, addr)
? transaction.transfer === types_1.MultiversXTransferOptions.esdt
? "FEES"
: "OUT"
: "IN";
}
function getESDTOperationValue(transaction, tokenIdentifier) {
const hasFailed = !transaction.status || transaction.status === "fail" || transaction.status === "invalid";
if (!transaction.action || hasFailed) {
return new bignumber_js_1.BigNumber(0);
}
let token1, token2;
switch (transaction.action.name) {
case "transfer":
return new bignumber_js_1.BigNumber(transaction.action.arguments.transfers[0].value ?? 0);
case "swap":
token1 = transaction.action.arguments.transfers[0];
token2 = transaction.action.arguments.transfers[1];
if (token1.token === tokenIdentifier) {
return new bignumber_js_1.BigNumber(token1.value);
}
else {
return new bignumber_js_1.BigNumber(token2.value);
}
default:
return new bignumber_js_1.BigNumber(transaction.tokenValue ?? 0);
}
}
function getStakingAmount(transaction, address) {
const operation = transaction.operations?.find(({ sender, receiver, action, type }) => action === "transfer" &&
type === "egld" &&
sender === transaction.receiver &&
(receiver === address || receiver === constants_1.MULTIVERSX_STAKING_POOL));
let dataDecoded;
switch (transaction.mode) {
case "send":
return new bignumber_js_1.BigNumber(0);
case "delegate":
return new bignumber_js_1.BigNumber(transaction.value ?? 0);
case "unDelegate":
dataDecoded = binary_utils_1.BinaryUtils.base64Decode(transaction.data ?? "");
return new bignumber_js_1.BigNumber(`0x${dataDecoded.split("@")[1]}`);
case "reDelegateRewards":
case "claimRewards":
case "withdraw":
default:
return new bignumber_js_1.BigNumber(operation?.value ?? new bignumber_js_1.BigNumber(0));
}
}
/**
* Map transaction to a correct Operation Value (affecting account balance)
*/
function getEGLDOperationValue(transaction, address) {
if (transaction.mode === "send") {
if (transaction.transfer === types_1.MultiversXTransferOptions.esdt) {
// Only fees paid in EGLD for token transactions
return isSender(transaction, address) && transaction.fee
? new bignumber_js_1.BigNumber(transaction.fee)
: new bignumber_js_1.BigNumber(0);
}
else {
return isSender(transaction, address)
? isSelfSend(transaction)
? new bignumber_js_1.BigNumber(transaction.fee ?? 0) // Self-send, only fees are paid
: new bignumber_js_1.BigNumber(transaction.value ?? 0).plus(transaction.fee ?? 0) // The sender pays the amount and the fees
: new bignumber_js_1.BigNumber(transaction.value ?? 0); // The recipient gets the amount
}
}
else {
// Operation value for staking transactions are just the fees, plus possible rewards
// Other amounts are put in extra.amount
return new bignumber_js_1.BigNumber(transaction.fee ?? 0);
}
}
/**
* Map the MultiversX history transaction to a Ledger Live Operation
*/
function transactionToEGLDOperation(accountId, addr, transaction, subAccounts) {
const type = getEGLDOperationType(transaction, addr);
const fee = new bignumber_js_1.BigNumber(transaction.fee ?? 0);
const hasFailed = !transaction.status || transaction.status === "fail" || transaction.status === "invalid";
const delegationAmount = getStakingAmount(transaction, addr);
const value = hasFailed
? isSender(transaction, addr)
? fee
: new bignumber_js_1.BigNumber(0)
: transaction.mode === "claimRewards"
? delegationAmount.minus(fee)
: getEGLDOperationValue(transaction, addr);
const operation = {
id: (0, operation_1.encodeOperationId)(accountId, transaction.txHash ?? "", type),
accountId,
fee,
value,
type,
hash: transaction.txHash ?? "",
blockHash: transaction.miniBlockHash,
blockHeight: transaction.round,
date: new Date(transaction.timestamp ? transaction.timestamp * 1000 : 0),
extra: {
amount: delegationAmount,
},
senders: (type === "OUT" || type === "IN") && transaction.sender ? [transaction.sender] : [],
recipients: (type === "OUT" || type === "IN") && transaction.receiver ? [transaction.receiver] : [],
transactionSequenceNumber: isSender(transaction, addr) && transaction.nonce !== undefined
? new bignumber_js_1.BigNumber(transaction.nonce.toString())
: undefined,
hasFailed,
};
const subOperations = subAccounts
? (0, index_1.inferSubOperations)(transaction.txHash ?? "", subAccounts)
: undefined;
if (subOperations) {
operation.subOperations = subOperations;
}
let contract = undefined;
if (transaction.receiver) {
const isReceiverSmartContract = sdk_core_1.Address.newFromBech32(transaction.receiver).isSmartContract();
contract = isReceiverSmartContract ? transaction.receiver : undefined;
}
if (contract) {
operation.contract = contract;
}
return operation;
}
const getESDTOperationType = (transaction, address) => {
return isSender(transaction, address) ? "OUT" : "IN";
};
const transactionToESDTOperation = (tokenAccountId, addr, transaction, tokenIdentifier) => {
const type = getESDTOperationType(transaction, addr);
const value = getESDTOperationValue(transaction, tokenIdentifier);
const fee = new bignumber_js_1.BigNumber(transaction.fee ?? 0);
const senders = transaction.sender ? [transaction.sender] : [];
const recipients = transaction.receiver ? [transaction.receiver] : [];
const hash = transaction.txHash ?? "";
const blockHeight = transaction.round;
const date = new Date(transaction.timestamp ? transaction.timestamp * 1000 : 0);
return {
id: (0, operation_1.encodeOperationId)(tokenAccountId, hash, type),
accountId: tokenAccountId,
hash,
date,
type,
value,
fee,
senders,
recipients,
blockHeight,
blockHash: transaction.miniBlockHash,
extra: {},
};
};
/**
* Fetch operation list
*/
const getEGLDOperations = async (accountId, addr, startAt, subAccounts) => {
const rawTransactions = await api.getHistory(addr, startAt);
if (!rawTransactions)
return rawTransactions;
return rawTransactions.map(transaction => transactionToEGLDOperation(accountId, addr, transaction, subAccounts));
};
exports.getEGLDOperations = getEGLDOperations;
const getAccountESDTTokens = async (address) => {
return await api.getESDTTokensForAddress(address);
};
exports.getAccountESDTTokens = getAccountESDTTokens;
const getAccountDelegations = async (address) => {
return await api.getAccountDelegations(address);
};
exports.getAccountDelegations = getAccountDelegations;
const hasESDTTokens = async (address) => {
const tokensCount = await api.getESDTTokensCountForAddress(address);
return tokensCount > 0;
};
exports.hasESDTTokens = hasESDTTokens;
const getESDTOperations = async (tokenAccountId, address, tokenIdentifier, startAt) => {
const accountESDTTransactions = await api.getESDTTransactionsForAddress(address, tokenIdentifier, startAt);
return accountESDTTransactions.map(transaction => transactionToESDTOperation(tokenAccountId, address, transaction, tokenIdentifier));
};
exports.getESDTOperations = getESDTOperations;
/**
* Obtain fees from blockchain
*/
const getFees = async (t) => {
const networkConfig = {
MinGasLimit: constants_1.MIN_GAS_LIMIT,
GasPerDataByte: constants_1.GAS_PER_DATA_BYTE,
GasPriceModifier: constants_1.GAS_PRICE_MODIFIER,
ChainID: constants_1.CHAIN_ID,
};
const transaction = new sdk_core_1.Transaction({
data: sdk_core_1.TransactionPayload.fromEncoded(t.data?.trim()),
receiver: new sdk_core_1.Address((0, cryptoassets_1.getAbandonSeedAddress)("elrond")),
chainID: constants_1.CHAIN_ID,
gasPrice: constants_1.GAS_PRICE,
gasLimit: t.gasLimit ?? networkConfig.MinGasLimit,
sender: sdk_core_1.Address.empty(),
});
const feesStr = transaction.computeFee(networkConfig).toFixed();
return new bignumber_js_1.BigNumber(feesStr);
};
exports.getFees = getFees;
/**
* Broadcast blob to blockchain
*/
const broadcastTransaction = async (signedOperation) => {
return await api.submit(signedOperation);
};
exports.broadcastTransaction = broadcastTransaction;
//# sourceMappingURL=sdk.js.map