UNPKG

@ledgerhq/coin-multiversx

Version:
264 lines 10.7 kB
import { encodeOperationId } from "@ledgerhq/coin-framework/operation"; import { inferSubOperations } from "@ledgerhq/coin-framework/serialization/index"; import { getAbandonSeedAddress } from "@ledgerhq/cryptoassets"; import { getEnv } from "@ledgerhq/live-env"; import { Address, ApiNetworkProvider, Transaction as MultiversXSdkTransaction, TransactionPayload, } from "@multiversx/sdk-core"; import { BigNumber } from "bignumber.js"; import { CHAIN_ID, GAS_PER_DATA_BYTE, GAS_PRICE, GAS_PRICE_MODIFIER, MIN_GAS_LIMIT, MULTIVERSX_STAKING_POOL, } from "../constants"; import { MultiversXTransferOptions, } from "../types"; import { BinaryUtils } from "../utils/binary.utils"; import MultiversXApi from "./apiCalls"; import { MultiversXAccount } from "./dtos/multiversx-account"; const api = new MultiversXApi(getEnv("MULTIVERSX_API_ENDPOINT"), getEnv("MULTIVERSX_DELEGATION_API_ENDPOINT")); const networkConfig = { clientName: "ledger-live" }; const proxy = new ApiNetworkProvider(getEnv("MULTIVERSX_API_ENDPOINT"), networkConfig); /** * Get account balances and nonce */ export const getAccount = async (addr) => { const { balance, nonce, isGuarded } = await api.getAccountDetails(addr); const blockHeight = await api.getBlockchainBlockHeight(); const account = new MultiversXAccount(new BigNumber(balance), nonce, isGuarded, blockHeight); return account; }; export const getProviders = async () => { const providers = await api.getProviders(); return providers; }; export const getNetworkConfig = async () => { return await proxy.getNetworkConfig(); }; export const getAccountNonce = async (addr) => { const address = new Address(addr); const account = await proxy.getAccount(address); return account.nonce; }; /** * 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 === 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(0); } let token1, token2; switch (transaction.action.name) { case "transfer": return new 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(token1.value); } else { return new BigNumber(token2.value); } default: return new 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 == MULTIVERSX_STAKING_POOL)); let dataDecoded; switch (transaction.mode) { case "send": return new BigNumber(0); case "delegate": return new BigNumber(transaction.value ?? 0); case "unDelegate": dataDecoded = BinaryUtils.base64Decode(transaction.data ?? ""); return new BigNumber(`0x${dataDecoded.split("@")[1]}`); case "reDelegateRewards": case "claimRewards": case "withdraw": default: return new BigNumber(operation?.value ?? new BigNumber(0)); } } /** * Map transaction to a correct Operation Value (affecting account balance) */ function getEGLDOperationValue(transaction, address) { if (transaction.mode === "send") { if (transaction.transfer === MultiversXTransferOptions.esdt) { // Only fees paid in EGLD for token transactions return isSender(transaction, address) && transaction.fee ? new BigNumber(transaction.fee) : new BigNumber(0); } else { return isSender(transaction, address) ? isSelfSend(transaction) ? new BigNumber(transaction.fee ?? 0) // Self-send, only fees are paid : new BigNumber(transaction.value ?? 0).plus(transaction.fee ?? 0) // The sender pays the amount and the fees : new 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(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(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(0) : transaction.mode === "claimRewards" ? delegationAmount.minus(fee) : getEGLDOperationValue(transaction, addr); const operation = { id: 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, hasFailed, }; const subOperations = subAccounts ? inferSubOperations(transaction.txHash ?? "", subAccounts) : undefined; if (subOperations) { operation.subOperations = subOperations; } let contract = undefined; if (transaction.receiver) { const isReceiverSmartContract = 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(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: encodeOperationId(tokenAccountId, hash, type), accountId: tokenAccountId, hash, date, type, value, fee, senders, recipients, blockHeight, blockHash: transaction.miniBlockHash, extra: {}, }; }; /** * Fetch operation list */ export 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)); }; export const getAccountESDTTokens = async (address) => { return await api.getESDTTokensForAddress(address); }; export const getAccountDelegations = async (address) => { return await api.getAccountDelegations(address); }; export const hasESDTTokens = async (address) => { const tokensCount = await api.getESDTTokensCountForAddress(address); return tokensCount > 0; }; export const getESDTOperations = async (tokenAccountId, address, tokenIdentifier, startAt) => { const accountESDTTransactions = await api.getESDTTransactionsForAddress(address, tokenIdentifier, startAt); return accountESDTTransactions.map(transaction => transactionToESDTOperation(tokenAccountId, address, transaction, tokenIdentifier)); }; /** * Obtain fees from blockchain */ export const getFees = async (t) => { const networkConfig = { MinGasLimit: MIN_GAS_LIMIT, GasPerDataByte: GAS_PER_DATA_BYTE, GasPriceModifier: GAS_PRICE_MODIFIER, ChainID: CHAIN_ID, }; const transaction = new MultiversXSdkTransaction({ data: TransactionPayload.fromEncoded(t.data?.trim()), receiver: new Address(getAbandonSeedAddress("elrond")), chainID: CHAIN_ID, gasPrice: GAS_PRICE, gasLimit: t.gasLimit ?? networkConfig.MinGasLimit, sender: Address.empty(), }); const feesStr = transaction.computeFee(networkConfig).toFixed(); return new BigNumber(feesStr); }; /** * Broadcast blob to blockchain */ export const broadcastTransaction = async (signedOperation) => { return await api.submit(signedOperation); }; //# sourceMappingURL=sdk.js.map