From b4817f71f0d60d859c643a376122f5e5c7795cba Mon Sep 17 00:00:00 2001 From: Emmanuel Date: Tue, 1 Mar 2022 13:49:07 -0300 Subject: [PATCH] Filecoin fixes (#1740) * make use of useAllAmount flag * make use of useAllAmount on signing tx process * refactor signOperation process to use extra field better * add deviceTransactionConfig for filecoin * re org fields for confirm tx * change fields order * add extra field parsers * move extra field parsers to the correct file * fix lint issue --- src/families/filecoin/account.ts | 44 +++++++++++ src/families/filecoin/bridge/account.ts | 52 ++++++++----- src/families/filecoin/bridge/utils/utils.ts | 19 ++--- .../filecoin/deviceTransactionConfig.ts | 73 +++++++++++++++++++ src/families/filecoin/transaction.ts | 22 ++++-- src/families/filecoin/utils.ts | 9 +++ src/generated/account.ts | 3 + src/generated/deviceTransactionConfig.ts | 5 ++ 8 files changed, 192 insertions(+), 35 deletions(-) create mode 100644 src/families/filecoin/account.ts create mode 100644 src/families/filecoin/deviceTransactionConfig.ts diff --git a/src/families/filecoin/account.ts b/src/families/filecoin/account.ts new file mode 100644 index 0000000000..4397d7c064 --- /dev/null +++ b/src/families/filecoin/account.ts @@ -0,0 +1,44 @@ +import BigNumber from "bignumber.js"; + +export function fromOperationExtraRaw( + extra: Record | null | undefined +): Record | null | undefined { + if (!extra) return extra; + + const { gasLimit, gasPremium, gasFeeCap } = extra; + + if (gasLimit !== undefined) + extra = { ...extra, gasLimit: new BigNumber(gasLimit) }; + + if (gasPremium !== undefined) + extra = { ...extra, gasPremium: new BigNumber(gasPremium) }; + + if (gasFeeCap !== undefined) + extra = { ...extra, gasFeeCap: new BigNumber(gasFeeCap) }; + + return extra; +} + +export function toOperationExtraRaw( + extra: Record | null | undefined +): Record | null | undefined { + if (!extra) return extra; + + const { gasLimit, gasPremium, gasFeeCap } = extra; + + if (gasLimit !== undefined) + extra = { ...extra, gasLimit: gasLimit.toNumber() }; + + if (gasPremium !== undefined) + extra = { ...extra, gasPremium: gasPremium.toFixed() }; + + if (gasFeeCap !== undefined) + extra = { ...extra, gasFeeCap: gasFeeCap.toFixed() }; + + return extra; +} + +export default { + fromOperationExtraRaw, + toOperationExtraRaw, +}; diff --git a/src/families/filecoin/bridge/account.ts b/src/families/filecoin/bridge/account.ts index 3b4fe5bb0d..f5b53cecd9 100644 --- a/src/families/filecoin/bridge/account.ts +++ b/src/families/filecoin/bridge/account.ts @@ -66,8 +66,10 @@ const getTransactionStatus = async ( const errors: TransactionStatus["errors"] = {}; const warnings: TransactionStatus["warnings"] = {}; + const { balance } = a; const { address } = getAddress(a); - const { recipient, amount, gasPremium, gasFeeCap, gasLimit } = t; + const { recipient, useAllAmount, gasPremium, gasFeeCap, gasLimit } = t; + let { amount } = t; if (!recipient) errors.recipient = new RecipientRequired(); else if (!validateAddress(recipient).isValid) @@ -81,8 +83,8 @@ const getTransactionStatus = async ( // This is the worst case scenario (the tx won't cost more than this value) const estimatedFees = calculateEstimatedFees(gasFeeCap, gasLimit); - // Add the estimated fees to the tx amount - const totalSpent = amount.plus(estimatedFees); + const totalSpent = useAllAmount ? balance : amount.plus(estimatedFees); + amount = useAllAmount ? balance.minus(estimatedFees) : amount; if (amount.lte(0)) errors.amount = new AmountRequired(); if (totalSpent.gt(a.spendableBalance)) errors.amount = new NotEnoughBalance(); @@ -174,11 +176,13 @@ const prepareTransaction = async ( const sync = makeSync(getAccountShape); const broadcast: BroadcastFnSignature = async ({ - signedOperation: { operation }, + signedOperation: { operation, signature }, }) => { // log("debug", "[broadcast] start fn"); - const resp = await broadcastTx(operation.extra.reqToBroadcast); + const tx = getTxToBroadcast(operation, signature); + + const resp = await broadcastTx(tx); const { hash } = resp; const result = patchOperationWithHash(operation, hash); @@ -199,8 +203,18 @@ const signOperation: SignOperationFnSignature = ({ async function main() { // log("debug", "[signOperation] start fn"); - const { recipient, amount, gasFeeCap, gasLimit } = transaction; - const { id: accountId } = account; + const { + recipient, + method, + version, + nonce, + gasFeeCap, + gasLimit, + gasPremium, + useAllAmount, + } = transaction; + let { amount } = transaction; + const { id: accountId, balance } = account; const { address, derivationPath } = getAddress(account); if (!gasFeeCap.gt(0) || !gasLimit.gt(0)) { @@ -218,6 +232,11 @@ const signOperation: SignOperationFnSignature = ({ type: "device-signature-requested", }); + const fee = calculateEstimatedFees(gasFeeCap, gasLimit); + if (useAllAmount) amount = balance.minus(fee); + + transaction = { ...transaction, amount }; + // Serialize tx const serializedTx = toCBOR( getAddressRaw(address), @@ -243,21 +262,12 @@ const signOperation: SignOperationFnSignature = ({ type: "device-signature-granted", }); - const fee = calculateEstimatedFees(gasFeeCap, gasLimit); - const value = amount.plus(fee); - // resolved at broadcast time const txHash = ""; // build signature on the correct format const signature = `${result.signature_compact.toString("base64")}`; - const reqToBroadcast = getTxToBroadcast( - account, - transaction, - signature - ); - const operation: Operation = { id: `${accountId}-${txHash}-OUT`, hash: txHash, @@ -265,13 +275,19 @@ const signOperation: SignOperationFnSignature = ({ senders: [address], recipients: [recipient], accountId, - value, + value: amount, fee, blockHash: null, blockHeight: null, date: new Date(), extra: { - reqToBroadcast, + gasLimit, + gasFeeCap, + gasPremium, + method, + version, + nonce, + signatureType: 1, }, }; diff --git a/src/families/filecoin/bridge/utils/utils.ts b/src/families/filecoin/bridge/utils/utils.ts index c2e626db74..7b4ba9b953 100644 --- a/src/families/filecoin/bridge/utils/utils.ts +++ b/src/families/filecoin/bridge/utils/utils.ts @@ -12,7 +12,6 @@ import { import { fetchBalances, fetchBlockHeight, fetchTxs } from "./api"; import { encodeAccountId } from "../../../../account"; import flatMap from "lodash/flatMap"; -import { Transaction } from "../../types"; type TxsById = { [id: string]: { @@ -103,21 +102,19 @@ export const getAddress = (a: Account): Address => : { address: a.freshAddress, derivationPath: a.freshAddressPath }; export const getTxToBroadcast = ( - account: Account, - transaction: Transaction, + operation: Operation, signature: string ): BroadcastTransactionRequest => { - const { address } = getAddress(account); + const { extra, senders, recipients, value } = operation; const { - recipient, - amount, gasLimit, gasFeeCap, gasPremium, method, version, nonce, - } = transaction; + signatureType, + } = extra; return { message: { @@ -125,15 +122,15 @@ export const getTxToBroadcast = ( method, nonce, params: "", - to: recipient, - from: address, + to: recipients[0], + from: senders[0], gaslimit: gasLimit.toNumber(), gaspremium: gasPremium.toString(), gasfeecap: gasFeeCap.toString(), - value: amount.toFixed(), + value: value.toFixed(), }, signature: { - type: 1, + type: signatureType, data: signature, }, }; diff --git a/src/families/filecoin/deviceTransactionConfig.ts b/src/families/filecoin/deviceTransactionConfig.ts new file mode 100644 index 0000000000..e452d19b64 --- /dev/null +++ b/src/families/filecoin/deviceTransactionConfig.ts @@ -0,0 +1,73 @@ +import type { DeviceTransactionField } from "../../transaction"; +import type { Account, AccountLike, TransactionStatus } from "../../types"; +import type { Transaction } from "./types"; +import { formatCurrencyUnit, getCryptoCurrencyById } from "../../currencies"; +import { methodToString } from "./utils"; + +const currency = getCryptoCurrencyById("filecoin"); + +export type ExtraDeviceTransactionField = + | { + type: "filecoin.gasFeeCap"; + label: string; + value: string; + } + | { + type: "filecoin.gasPremium"; + label: string; + value: string; + } + | { + type: "filecoin.gasLimit"; + label: string; + value: string; + } + | { + type: "filecoin.method"; + label: string; + value: string; + }; + +function getDeviceTransactionConfig(input: { + account: AccountLike; + parentAccount: Account | null | undefined; + transaction: Transaction; + status: TransactionStatus; +}): Array { + const fields: Array = []; + + fields.push({ + type: "amount", + label: "Value", + }); + fields.push({ + type: "filecoin.gasLimit", + label: "Gas Limit", + value: input.transaction.gasLimit.toFixed(), + }); + fields.push({ + type: "filecoin.gasPremium", + label: "Gas Premium", + value: formatCurrencyUnit(currency.units[0], input.transaction.gasPremium, { + showCode: false, + disableRounding: true, + }), + }); + fields.push({ + type: "filecoin.gasFeeCap", + label: "Gas Fee Cap", + value: formatCurrencyUnit(currency.units[0], input.transaction.gasFeeCap, { + showCode: false, + disableRounding: true, + }), + }); + fields.push({ + type: "filecoin.method", + label: "Method", + value: methodToString(input.transaction.method), + }); + + return fields; +} + +export default getDeviceTransactionConfig; diff --git a/src/families/filecoin/transaction.ts b/src/families/filecoin/transaction.ts index 9ddb3dd8ed..e0d81b2b85 100644 --- a/src/families/filecoin/transaction.ts +++ b/src/families/filecoin/transaction.ts @@ -8,12 +8,22 @@ import { getAccountUnit } from "../../account"; import { formatCurrencyUnit } from "../../currencies"; import BigNumber from "bignumber.js"; -export const formatTransaction = (t: Transaction, account: Account): string => ` -SEND ${formatCurrencyUnit(getAccountUnit(account), t.amount, { - showCode: true, - disableRounding: true, -})} -TO ${t.recipient}`; +export const formatTransaction = ( + { recipient, useAllAmount, amount }: Transaction, + account: Account +): string => ` +SEND ${ + useAllAmount + ? "MAX" + : amount.isZero() + ? "" + : " " + + formatCurrencyUnit(getAccountUnit(account), amount, { + showCode: true, + disableRounding: true, + }) +} +TO ${recipient}`; export const fromTransactionRaw = (tr: TransactionRaw): Transaction => { const common = fromTransactionCommonRaw(tr); diff --git a/src/families/filecoin/utils.ts b/src/families/filecoin/utils.ts index 831da319e1..3fe719a3d2 100644 --- a/src/families/filecoin/utils.ts +++ b/src/families/filecoin/utils.ts @@ -19,6 +19,15 @@ export const isError = (r: { return_code: number; error_message: string }) => { throw new Error(`${r.return_code} - ${r.error_message}`); }; +export const methodToString = (method: number): string => { + switch (method) { + case 0: + return "Transfer"; + default: + return "Unknown"; + } +}; + export const getBufferFromString = (message: string): Buffer => isValidHex(message) ? Buffer.from(message, "hex") diff --git a/src/generated/account.ts b/src/generated/account.ts index ece6073d38..e4f6314b1a 100644 --- a/src/generated/account.ts +++ b/src/generated/account.ts @@ -8,6 +8,8 @@ import crypto_org from "../families/crypto_org/account"; import elrond from "../families/elrond/account"; +import filecoin from "../families/filecoin/account"; + import polkadot from "../families/polkadot/account"; @@ -17,5 +19,6 @@ export default { cosmos, crypto_org, elrond, + filecoin, polkadot, }; diff --git a/src/generated/deviceTransactionConfig.ts b/src/generated/deviceTransactionConfig.ts index 014ca10c3f..1e371d2df9 100644 --- a/src/generated/deviceTransactionConfig.ts +++ b/src/generated/deviceTransactionConfig.ts @@ -12,6 +12,8 @@ import elrond from "../families/elrond/deviceTransactionConfig"; import ethereum from "../families/ethereum/deviceTransactionConfig"; +import filecoin from "../families/filecoin/deviceTransactionConfig"; + import polkadot from "../families/polkadot/deviceTransactionConfig"; import ripple from "../families/ripple/deviceTransactionConfig"; @@ -33,6 +35,7 @@ export default { crypto_org, elrond, ethereum, + filecoin, polkadot, ripple, solana, @@ -41,12 +44,14 @@ export default { tron, }; import { ExtraDeviceTransactionField as ExtraDeviceTransactionField_cosmos } from "../families/cosmos/deviceTransactionConfig"; +import { ExtraDeviceTransactionField as ExtraDeviceTransactionField_filecoin } from "../families/filecoin/deviceTransactionConfig"; import { ExtraDeviceTransactionField as ExtraDeviceTransactionField_polkadot } from "../families/polkadot/deviceTransactionConfig"; import { ExtraDeviceTransactionField as ExtraDeviceTransactionField_stellar } from "../families/stellar/deviceTransactionConfig"; import { ExtraDeviceTransactionField as ExtraDeviceTransactionField_tezos } from "../families/tezos/deviceTransactionConfig"; import { ExtraDeviceTransactionField as ExtraDeviceTransactionField_tron } from "../families/tron/deviceTransactionConfig"; export type ExtraDeviceTransactionField = | ExtraDeviceTransactionField_cosmos +| ExtraDeviceTransactionField_filecoin | ExtraDeviceTransactionField_polkadot | ExtraDeviceTransactionField_stellar | ExtraDeviceTransactionField_tezos