UNPKG

@ledgerhq/coin-cardano

Version:
188 lines (172 loc) 6.43 kB
import BigNumber from "bignumber.js"; import { OperationType } from "@ledgerhq/types-live"; import { HashType } from "@stricahq/typhonjs/dist/types"; import { encodeOperationId } from "@ledgerhq/coin-framework/operation"; import { formatCurrencyUnit } from "@ledgerhq/coin-framework/currencies"; import ShelleyTypeAddress from "@stricahq/typhonjs/dist/address/ShelleyTypeAddress"; import { types as TyphonTypes, Transaction as TyphonTransaction } from "@stricahq/typhonjs"; import { getAccountStakeCredential, getOperationType } from "./logic"; import { MEMO_LABEL } from "./constants"; import { CardanoAccount, CardanoOperation, CardanoOperationExtra, CardanoResources, Transaction, } from "./types"; export const buildOptimisticOperation = ( account: CardanoAccount, unsignedTransaction: TyphonTransaction, transaction: Transaction, ): CardanoOperation => { const cardanoResources = account.cardanoResources as CardanoResources; const accountCreds = new Set( [...cardanoResources.externalCredentials, ...cardanoResources.internalCredentials].map( cred => cred.key, ), ); const stakeCredential = getAccountStakeCredential(account.xpub as string, account.index); const protocolParams = account.cardanoResources.protocolParams; const accountInput = unsignedTransaction .getInputs() .reduce( (total, i) => accountCreds.has(i.address.paymentCredential.hash.toString("hex")) ? total.plus(i.amount) : total.plus(0), new BigNumber(0), ); const accountOutput = unsignedTransaction .getOutputs() .reduce( (total, o) => o.address instanceof ShelleyTypeAddress && accountCreds.has(o.address.paymentCredential.hash.toString("hex")) ? total.plus(o.amount) : total.plus(0), new BigNumber(0), ); const txCertificates = unsignedTransaction.getCertificates(); const stakeRegistrationCertificates: Array<TyphonTypes.StakeRegistrationCertificate> = []; const stakeDeRegistrationCertificates: Array<TyphonTypes.StakeDeRegistrationCertificate> = []; txCertificates.forEach(c => { if (c.type === TyphonTypes.CertificateType.STAKE_REGISTRATION) { stakeRegistrationCertificates.push(c); } else if (c.type === TyphonTypes.CertificateType.STAKE_DE_REGISTRATION) { stakeDeRegistrationCertificates.push(c); } }); const txWithdrawals = unsignedTransaction.getWithdrawals(); const transactionHash = unsignedTransaction.getTransactionHash().toString("hex"); const auxiliaryData = unsignedTransaction.getAuxiliaryData(); const extra: CardanoOperationExtra = {}; if (auxiliaryData) { const memoMetadata = auxiliaryData.metadata.find(m => m.label === MEMO_LABEL); if (memoMetadata && memoMetadata.data instanceof Map) { const msg = memoMetadata.data.get("msg"); if (Array.isArray(msg) && msg.length) { extra.memo = msg.join(", "); } } } let operationValue = accountOutput.minus(accountInput); if (stakeRegistrationCertificates.length) { const walletRegistration = stakeRegistrationCertificates.find( c => c.cert.stakeCredential.type === HashType.ADDRESS && c.cert.stakeCredential.hash.toString("hex") === stakeCredential.key, ); if (walletRegistration) { extra.deposit = formatCurrencyUnit( account.currency.units[0], new BigNumber(protocolParams.stakeKeyDeposit), { showCode: true, disableRounding: true, }, ); } } if (stakeDeRegistrationCertificates.length) { const walletDeRegistration = stakeDeRegistrationCertificates.find( c => c.cert.stakeCredential.type === HashType.ADDRESS && c.cert.stakeCredential.hash.toString("hex") === stakeCredential.key, ); if (walletDeRegistration) { operationValue = operationValue.minus(protocolParams.stakeKeyDeposit); extra.refund = formatCurrencyUnit( account.currency.units[0], new BigNumber(protocolParams.stakeKeyDeposit), { showCode: true, disableRounding: true, }, ); } } if (txWithdrawals && txWithdrawals.length) { const walletWithdraw = txWithdrawals.find( w => w.rewardAccount.stakeCredential.type === HashType.ADDRESS && w.rewardAccount.stakeCredential.hash.toString("hex") === stakeCredential.key, ); if (walletWithdraw) { operationValue = operationValue.minus(walletWithdraw.amount); extra.rewards = formatCurrencyUnit( account.currency.units[0], new BigNumber(walletWithdraw.amount), { showCode: true, disableRounding: true, }, ); } } const opType: OperationType = txCertificates.find( c => c.type === TyphonTypes.CertificateType.STAKE_DELEGATION, ) ? "DELEGATE" : txCertificates.find(c => c.type === TyphonTypes.CertificateType.STAKE_KEY_DE_REGISTRATION) ? "UNDELEGATE" : getOperationType({ valueChange: operationValue, fees: unsignedTransaction.getFee(), }); const op: CardanoOperation = { id: encodeOperationId(account.id, transactionHash, opType), hash: transactionHash, type: opType, value: operationValue.absoluteValue(), fee: unsignedTransaction.getFee(), blockHash: undefined, blockHeight: null, senders: unsignedTransaction.getInputs().map(i => i.address.getBech32()), recipients: unsignedTransaction.getOutputs().map(o => o.address.getBech32()), accountId: account.id, date: new Date(), extra, }; const tokenAccount = transaction.subAccountId ? account.subAccounts?.find(ta => ta.id === transaction.subAccountId) : null; if (tokenAccount && opType === "OUT") { op.subOperations = [ { id: encodeOperationId(tokenAccount.id, transactionHash, opType), hash: transactionHash, type: opType, value: transaction.useAllAmount ? tokenAccount.balance : transaction.amount, fee: transaction.fees as BigNumber, blockHash: undefined, blockHeight: null, senders: unsignedTransaction.getInputs().map(i => i.address.getBech32()), recipients: unsignedTransaction.getOutputs().map(o => o.address.getBech32()), accountId: tokenAccount.id, date: new Date(), extra: {}, }, ]; } return op; };