From c62f6c7f37daedc8c8666cd4bcb492beeacfea41 Mon Sep 17 00:00:00 2001 From: Hugo Morosini Date: Mon, 17 May 2021 11:54:20 +0200 Subject: [PATCH] added LL platform types and logic (#1166) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * added ledgerLiveAPI hook * added serializers, converters and platform types * fixed tx serializer * fixed types * added bitcoin tx * feat(Platform): added SignedTransaction types * added paraswap -> eth dep Co-authored-by: RĂ©mi Jarasson (Ext) --- package.json | 1 + src/apps/polyfill.js | 1 + src/platform/JSONRPCServer.js | 42 ++++++++++ src/platform/converters.js | 35 ++++++++ src/platform/rawTypes.js | 39 +++++++++ src/platform/serializers.js | 151 ++++++++++++++++++++++++++++++++++ src/platform/types.js | 56 +++++++++++++ yarn.lock | 5 ++ 8 files changed, 330 insertions(+) create mode 100644 src/platform/JSONRPCServer.js create mode 100644 src/platform/converters.js create mode 100644 src/platform/rawTypes.js create mode 100644 src/platform/serializers.js create mode 100644 src/platform/types.js diff --git a/package.json b/package.json index cbcacf3d23..8afd792761 100644 --- a/package.json +++ b/package.json @@ -108,6 +108,7 @@ "expect": "^26.6.2", "invariant": "^2.2.2", "isomorphic-ws": "^4.0.1", + "json-rpc-2.0": "^0.2.16", "lodash": "^4.17.21", "lru-cache": "5.1.1", "numeral": "^2.0.6", diff --git a/src/apps/polyfill.js b/src/apps/polyfill.js index 38453c77ec..e55072d76d 100644 --- a/src/apps/polyfill.js +++ b/src/apps/polyfill.js @@ -38,6 +38,7 @@ listCryptoCurrencies(true, true).forEach((a) => { ["ThunderCore", "Ethereum"], ["Volta", "Ethereum"], ["ZenCash", "Bitcoin"], + ["Paraswap", "Ethereum"], ].forEach(([name, dep]) => declareDep(name, dep)); export const getDependencies = (appName: string): string[] => diff --git a/src/platform/JSONRPCServer.js b/src/platform/JSONRPCServer.js new file mode 100644 index 0000000000..fa21714e04 --- /dev/null +++ b/src/platform/JSONRPCServer.js @@ -0,0 +1,42 @@ +// @flow +import { useEffect, useRef, useCallback } from "react"; +import { + JSONRPCServer, + JSONRPCServerAndClient, + JSONRPCClient, +} from "json-rpc-2.0"; + +type LedgerLiveAPIHandlers = {}; + +type SendFunc = (request: any) => void; + +export const useJSONRPCServer = ( + handlers: LedgerLiveAPIHandlers, + send: SendFunc +) => { + const serverRef: { current: null | typeof JSONRPCServerAndClient } = useRef( + null + ); + + useEffect(() => { + const server = new JSONRPCServerAndClient( + new JSONRPCServer(), + new JSONRPCClient(send) + ); + + const methodIds = Object.keys(handlers); + methodIds.forEach((methodId: string) => { + server.addMethod(methodId, handlers[methodId]); + }); + + serverRef.current = server; + }, [send, handlers]); + + const receive = useCallback(async (request: any) => { + if (serverRef.current) { + await serverRef.current.receiveAndSend(request); + } + }, []); + + return [receive]; +}; diff --git a/src/platform/converters.js b/src/platform/converters.js new file mode 100644 index 0000000000..2f6ce0548d --- /dev/null +++ b/src/platform/converters.js @@ -0,0 +1,35 @@ +// @flow + +import type { Account, CryptoCurrency } from "../types"; +import type { PlatformAccount, PlatformCurrency } from "./types"; + +export function accountToPlatformAccount(account: Account): PlatformAccount { + return { + id: account.id, + name: account.name, + address: account.freshAddress, + currency: account.currency.id, + balance: account.balance, + spendableBalance: account.spendableBalance, + blockHeight: account.blockHeight, + lastSyncDate: account.lastSyncDate, + }; +} + +export function currencyToPlatformCurrency( + currency: CryptoCurrency +): PlatformCurrency { + return { + type: currency.type, + id: currency.id, + ticker: currency.ticker, + name: currency.name, + family: currency.family, + color: currency.color, + units: currency.units.map((unit) => ({ + name: unit.name, + code: unit.code, + magnitude: unit.magnitude, + })), + }; +} diff --git a/src/platform/rawTypes.js b/src/platform/rawTypes.js new file mode 100644 index 0000000000..fdf9d10f7c --- /dev/null +++ b/src/platform/rawTypes.js @@ -0,0 +1,39 @@ +// @flow +import type { SignedOperationRaw } from "../types"; + +export type RawPlatformAccount = { + id: string, + name: string, + address: string, + currency: string, + balance: string, + spendableBalance: string, + blockHeight: number, + lastSyncDate: string, +}; + +export interface RawPlatformTransactionCommon { + amount: string; + recipient: string; +} + +export interface RawPlatformEthereumTransaction + extends RawPlatformTransactionCommon { + family: "ethereum"; + nonce: ?number; + data: ?string; + gasPrice: ?string; + gasLimit: ?string; +} + +export interface RawPlatformBitcoinTransaction + extends RawPlatformTransactionCommon { + family: "bitcoin"; + feePerByte: ?string; +} + +export type RawPlatformTransaction = + | RawPlatformEthereumTransaction + | RawPlatformBitcoinTransaction; + +export type RawPlatformSignedTransaction = SignedOperationRaw; diff --git a/src/platform/serializers.js b/src/platform/serializers.js new file mode 100644 index 0000000000..0a6891ac50 --- /dev/null +++ b/src/platform/serializers.js @@ -0,0 +1,151 @@ +// @flow + +import { fromSignedOperationRaw, toSignedOperationRaw } from "../transaction"; + +import type { + RawPlatformAccount, + RawPlatformTransaction, + RawPlatformEthereumTransaction, + RawPlatformBitcoinTransaction, + RawPlatformSignedTransaction, +} from "./rawTypes"; +import type { + PlatformAccount, + PlatformTransaction, + PlatformEthereumTransaction, + PlatformBitcoinTransaction, + PlatformSignedTransaction, +} from "./types"; + +import { BigNumber } from "bignumber.js"; + +export function serializePlatformAccount( + account: PlatformAccount +): RawPlatformAccount { + return { + id: account.id, + name: account.name, + address: account.address, + currency: account.currency, + balance: account.balance.toString(), + spendableBalance: account.spendableBalance.toString(), + blockHeight: account.blockHeight, + lastSyncDate: account.lastSyncDate.toString(), + }; +} + +export function deserializePlatformAccount( + rawAccount: RawPlatformAccount +): PlatformAccount { + return { + id: rawAccount.id, + name: rawAccount.name, + address: rawAccount.address, + currency: rawAccount.currency, + balance: new BigNumber(rawAccount.balance), + spendableBalance: new BigNumber(rawAccount.spendableBalance), + blockHeight: rawAccount.blockHeight, + lastSyncDate: new Date(rawAccount.lastSyncDate), + }; +} + +export function serializePlatformEthereumTransaction( + transaction: PlatformEthereumTransaction +): RawPlatformEthereumTransaction { + return { + family: transaction.family, + amount: transaction.amount.toString(), + recipient: transaction.recipient, + nonce: transaction.nonce, + data: transaction.data ? transaction.data.toString() : undefined, + gasPrice: transaction.gasPrice + ? transaction.gasPrice.toString() + : undefined, + gasLimit: transaction.gasLimit + ? transaction.gasLimit.toString() + : undefined, + }; +} + +export function deserializePlatformEthereumTransaction( + rawTransaction: RawPlatformEthereumTransaction +): PlatformEthereumTransaction { + return { + family: rawTransaction.family, + amount: new BigNumber(rawTransaction.amount), + recipient: rawTransaction.recipient, + nonce: rawTransaction.nonce, + data: rawTransaction.data ? Buffer.from(rawTransaction.data) : undefined, + gasPrice: rawTransaction.gasPrice + ? new BigNumber(rawTransaction.gasPrice) + : undefined, + gasLimit: rawTransaction.gasLimit + ? new BigNumber(rawTransaction.gasLimit) + : undefined, + }; +} + +export function serializePlatformBitcoinTransaction( + transaction: PlatformBitcoinTransaction +): RawPlatformBitcoinTransaction { + return { + family: transaction.family, + amount: transaction.amount.toString(), + recipient: transaction.recipient, + feePerByte: transaction.feePerByte + ? transaction.feePerByte.toString() + : undefined, + }; +} + +export function deserializePlatformBitcoinTransaction( + rawTransaction: RawPlatformBitcoinTransaction +): PlatformBitcoinTransaction { + return { + family: rawTransaction.family, + amount: new BigNumber(rawTransaction.amount), + recipient: rawTransaction.recipient, + feePerByte: rawTransaction.feePerByte + ? new BigNumber(rawTransaction.feePerByte) + : undefined, + }; +} + +export function serializePlatformTransaction( + transaction: PlatformTransaction +): RawPlatformTransaction { + switch (transaction.family) { + case "ethereum": + return serializePlatformEthereumTransaction(transaction); + case "bitcoin": + return serializePlatformBitcoinTransaction(transaction); + default: + throw new Error(`Can't serialize ${transaction.family} transactions`); + } +} + +export function deserializePlatformTransaction( + rawTransaction: RawPlatformTransaction +): PlatformTransaction { + switch (rawTransaction.family) { + case "ethereum": + return deserializePlatformEthereumTransaction(rawTransaction); + case "bitcoin": + return deserializePlatformBitcoinTransaction(rawTransaction); + default: + throw new Error(`Can't deserialize transaction: family not supported`); + } +} + +export function serializePlatformSignedTransaction( + signedTransaction: PlatformSignedTransaction +): RawPlatformSignedTransaction { + return toSignedOperationRaw(signedTransaction, true); +} + +export function deserializePlatformSignedTransaction( + rawSignedTransaction: RawPlatformSignedTransaction, + accountId: string +): PlatformSignedTransaction { + return fromSignedOperationRaw(rawSignedTransaction, accountId); +} diff --git a/src/platform/types.js b/src/platform/types.js new file mode 100644 index 0000000000..6e5ba62f14 --- /dev/null +++ b/src/platform/types.js @@ -0,0 +1,56 @@ +// @flow + +import type { BigNumber } from "bignumber.js"; + +import type { SignedOperation } from "../types"; + +export type PlatformAccount = { + id: string, + name: string, + address: string, + currency: string, + balance: BigNumber, + spendableBalance: BigNumber, + blockHeight: number, + lastSyncDate: Date, +}; + +export type PlatformUnit = { + name: string, + code: string, + magnitude: number, +}; + +export type PlatformCurrency = { + type: string, + color: string, + ticker: string, + id: string, + name: string, + family: string, + units: PlatformUnit[], +}; + +export interface PlatformTransactionCommon { + amount: BigNumber; + recipient: string; +} + +export interface PlatformEthereumTransaction extends PlatformTransactionCommon { + family: "ethereum"; + nonce: ?number; + data: ?Buffer; + gasPrice: ?BigNumber; + gasLimit: ?BigNumber; +} + +export interface PlatformBitcoinTransaction extends PlatformTransactionCommon { + family: "bitcoin"; + feePerByte: ?BigNumber; +} + +export type PlatformTransaction = + | PlatformEthereumTransaction + | PlatformBitcoinTransaction; + +export type PlatformSignedTransaction = SignedOperation; diff --git a/yarn.lock b/yarn.lock index 4a9e17156c..3889e25e71 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5392,6 +5392,11 @@ json-parse-even-better-errors@^2.3.0: resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== +json-rpc-2.0@^0.2.16: + version "0.2.16" + resolved "https://registry.yarnpkg.com/json-rpc-2.0/-/json-rpc-2.0-0.2.16.tgz#8ee70443f09abc26a9d339f382c930f3d84cf635" + integrity sha512-nXKBcNZxkoeyKpotT/T3tciv+e7EQb13nuDRLD2tff8Vs39VpPTOvL0BOXM3mN5QE10BlVEq5LDSV344m4zpeg== + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"