Skip to content
This repository has been archived by the owner on Jul 15, 2022. It is now read-only.

Commit

Permalink
Merge pull request #275 from LedgerHQ/erc20OptimisticUpdate
Browse files Browse the repository at this point in the history
Erc20 optimistic update
  • Loading branch information
gre authored Jun 13, 2019
2 parents 4ee8202 + 57ff0ee commit 414cbf3
Show file tree
Hide file tree
Showing 14 changed files with 137 additions and 41 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "git",
"url": "git://github.com/LedgerHQ/ledger-live-common"
},
"version": "6.5.0",
"version": "6.6.0-beta.4",
"main": "lib/index.js",
"license": "MIT",
"scripts": {
Expand Down Expand Up @@ -62,7 +62,7 @@
"ripple-binary-codec": "^0.2.0",
"ripple-bs58check": "^2.0.2",
"ripple-hashes": "^0.3.1",
"ripple-lib": "^1.2.4",
"ripple-lib": "1.1.2",
"rxjs": "^6.5.2",
"rxjs-compat": "^6.5.2"
},
Expand Down
10 changes: 4 additions & 6 deletions src/account/groupOperations.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,10 @@ export function groupAccountsOperationsByDay(
bestOpInfo = { accountI: i, fromPending: false };
}
// look in pending operations
if (account.type === "Account") {
const opP = account.pendingOperations[indexesPending[i]];
if (opP && (!bestOp || opP.date > bestOp.date)) {
bestOp = opP;
bestOpInfo = { accountI: i, fromPending: true };
}
const opP = account.pendingOperations[indexesPending[i]];
if (opP && (!bestOp || opP.date > bestOp.date)) {
bestOp = opP;
bestOpInfo = { accountI: i, fromPending: true };
}
}
if (bestOp) {
Expand Down
37 changes: 35 additions & 2 deletions src/account/helpers.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @flow
import invariant from "invariant";
import type { Account, TokenAccount } from "../types";
import type { Account, TokenAccount, Operation } from "../types";

// by convention, a main account is the top level account
// in case of an Account is the account itself
Expand Down Expand Up @@ -29,7 +29,8 @@ export function clearAccount<T: Account | TokenAccount>(account: T): T {
if (account.type === "TokenAccount") {
return {
...account,
operations: []
operations: [],
pendingOperations: []
};
}

Expand Down Expand Up @@ -59,3 +60,35 @@ export function flattenAccounts(
}
return accounts;
}

const appendPendingOp = (ops: Operation[], op: Operation) => {
const filtered: Operation[] = ops.filter(
o => o.transactionSequenceNumber === op.transactionSequenceNumber
);
filtered.push(op);
return filtered;
};

export const addPendingOperation = (account: Account, operation: Operation) => {
const accountCopy = { ...account };
const { subOperations } = operation;
const { tokenAccounts } = account;
if (subOperations && tokenAccounts) {
const taCopy: TokenAccount[] = tokenAccounts.slice(0);
subOperations.forEach(op => {
const acc = taCopy.find(ta => ta.id === op.accountId);
if (acc) {
taCopy[taCopy.indexOf(acc)] = {
...acc,
pendingOperations: appendPendingOp(acc.pendingOperations, op)
};
}
});
accountCopy.tokenAccounts = taCopy;
}
accountCopy.pendingOperations = appendPendingOp(
accountCopy.pendingOperations,
operation
);
return accountCopy;
};
6 changes: 4 additions & 2 deletions src/account/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
getAccountUnit,
isAccountEmpty,
clearAccount,
flattenAccounts
flattenAccounts,
addPendingOperation
} from "./helpers";
import {
shouldShowNewAccount,
Expand Down Expand Up @@ -82,5 +83,6 @@ export {
reorderAccountByCountervalues,
reorderTokenAccountsByCountervalues,
groupAccountsOperationsByDay,
groupAccountOperationsByDay
groupAccountOperationsByDay,
addPendingOperation
};
47 changes: 28 additions & 19 deletions src/account/serialization.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,21 @@ import type {
} from "../types";
import { getCryptoCurrencyById, getTokenById } from "../currencies";

export const toOperationRaw = ({
date,
value,
fee,
subOperations, // eslint-disable-line
...op
}: Operation): OperationRaw => ({
...op,
date: date.toISOString(),
value: value.toString(),
fee: fee.toString()
});
export const toOperationRaw = (
{ date, value, fee, subOperations, ...op }: Operation,
preserveSubOperation?: boolean
): OperationRaw => {
const copy: $Exact<OperationRaw> = {
...op,
date: date.toISOString(),
value: value.toString(),
fee: fee.toString()
};
if (subOperations && preserveSubOperation) {
copy.subOperations = subOperations.map(o => toOperationRaw(o));
}
return copy;
};

export const inferSubOperations = (
txHash: string,
Expand All @@ -41,7 +44,7 @@ export const inferSubOperations = (
};

export const fromOperationRaw = (
{ date, value, fee, extra, ...op }: OperationRaw,
{ date, value, fee, extra, subOperations, ...op }: OperationRaw,
accountId: string,
tokenAccounts?: ?(TokenAccount[])
): Operation => {
Expand All @@ -56,13 +59,17 @@ export const fromOperationRaw = (

if (tokenAccounts) {
res.subOperations = inferSubOperations(op.hash, tokenAccounts);
} else if (subOperations) {
res.subOperations = subOperations.map(o =>
fromOperationRaw(o, o.accountId)
);
}

return res;
};

export function fromTokenAccountRaw(raw: TokenAccountRaw): TokenAccount {
const { id, parentId, tokenId, operations, balance } = raw;
const { id, parentId, tokenId, operations, pendingOperations, balance } = raw;
const token = getTokenById(tokenId);
const convertOperation = op => fromOperationRaw(op, id);
return {
Expand All @@ -71,18 +78,20 @@ export function fromTokenAccountRaw(raw: TokenAccountRaw): TokenAccount {
parentId,
token,
balance: BigNumber(balance),
operations: operations.map(convertOperation)
operations: (operations || []).map(convertOperation),
pendingOperations: (pendingOperations || []).map(convertOperation)
};
}

export function toTokenAccountRaw(raw: TokenAccount): TokenAccountRaw {
const { id, parentId, token, operations, balance } = raw;
const { id, parentId, token, operations, pendingOperations, balance } = raw;
return {
id,
parentId,
tokenId: token.id,
balance: balance.toString(),
operations: operations.map(toOperationRaw)
operations: operations.map(o => toOperationRaw(o)),
pendingOperations: pendingOperations.map(o => toOperationRaw(o))
};
}

Expand Down Expand Up @@ -179,8 +188,8 @@ export function toAccountRaw({
freshAddress,
freshAddressPath,
blockHeight,
operations: operations.map(toOperationRaw),
pendingOperations: pendingOperations.map(toOperationRaw),
operations: operations.map(o => toOperationRaw(o)),
pendingOperations: pendingOperations.map(o => toOperationRaw(o)),
currencyId: currency.id,
unitMagnitude: unit.magnitude,
lastSyncDate: lastSyncDate.toISOString(),
Expand Down
2 changes: 1 addition & 1 deletion src/bridge/LibcoreEthereumAccountBridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ const getTotalSpent = (a, t) => {
const tAccount = getTransactionAccount(a, t);

if (t.useAllAmount) {
return tAccount.balance;
return Promise.resolve(tAccount.balance);
}

const amount = BigNumber(t.amount || "0");
Expand Down
3 changes: 3 additions & 0 deletions src/bridge/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,13 @@ export interface AccountBridge<Transaction> {
// Implement an optimistic response for signAndBroadcast.
// it should add the operation in account.pendingOperations.
// if you do implement this, make sure to properly handle cleanup of pendingOperations (likely to do during sync, for instance make sure to clean "zombies" transaction and transaction that actually appear in .operations)
// DEPRECATED: use the one exported in lib/account
/*
addPendingOperation?: (
account: Account,
optimisticOperation: Operation
) => Account;
*/

// some coins will have a way to configure the API it hits.
// it is stored in account.endpointConfig
Expand Down
1 change: 1 addition & 0 deletions src/families/ethereum/libcore-buildTokenAccounts.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ async function buildERC20TokenAccount({
parentId: parentAccountId,
token,
operations,
pendingOperations: [],
balance
};

Expand Down
24 changes: 23 additions & 1 deletion src/families/ethereum/libcore-signAndBroadcast.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ async function ethereum({
id: `${accountId}-${txHash}-OUT`,
hash: txHash,
type: "OUT",
value: BigNumber(transaction.amount).plus(fee),
value: transaction.tokenAccountId
? fee
: BigNumber(transaction.amount).plus(fee),
fee,
blockHash: null,
blockHeight: null,
Expand All @@ -39,6 +41,26 @@ async function ethereum({
extra: {}
};

const { tokenAccountId } = transaction;
if (tokenAccountId) {
op.subOperations = [
{
id: `${tokenAccountId}-${txHash}-OUT`,
hash: txHash,
type: "OUT",
value: BigNumber(transaction.amount),
fee,
blockHash: null,
blockHeight: null,
senders,
recipients: [transaction.recipient],
accountId: tokenAccountId,
date: new Date(),
extra: {}
}
];
}

return op;
}

Expand Down
1 change: 1 addition & 0 deletions src/mock/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ function genTokenAccount(
parentId: account.id,
token,
operations: [],
pendingOperations: [],
balance: BigNumber(0)
};

Expand Down
11 changes: 11 additions & 0 deletions src/reconciliation.js
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,12 @@ export function patchTokenAccount(
updatedRaw.id
);

const pendingOperations = patchOperations(
account.pendingOperations,
updatedRaw.pendingOperations,
updatedRaw.id
);

const next: $Exact<TokenAccount> = {
...account
};
Expand All @@ -269,6 +275,11 @@ export function patchTokenAccount(
changed = true;
}

if (account.pendingOperations !== pendingOperations) {
next.pendingOperations = pendingOperations;
changed = true;
}

if (updatedRaw.balance !== account.balance.toString()) {
next.balance = BigNumber(updatedRaw.balance);
changed = true;
Expand Down
2 changes: 2 additions & 0 deletions src/types/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type TokenAccount = {
parentId: string,
token: TokenCurrency,
operations: Operation[],
pendingOperations: Operation[],
balance: BigNumber
};

Expand Down Expand Up @@ -99,6 +100,7 @@ export type TokenAccountRaw = {
parentId: string,
tokenId: string,
operations: OperationRaw[],
pendingOperations: OperationRaw[],
balance: string
};

Expand Down
3 changes: 2 additions & 1 deletion src/types/operation.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,6 @@ export type OperationRaw = {
hasFailed?: boolean,
// --------------------------------------------- specific operation raw fields
date: string,
extra: Object // would be a serializable version of the extra
extra: Object, // would be a serializable version of the extra
subOperations?: OperationRaw[]
};
27 changes: 20 additions & 7 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6456,6 +6456,19 @@ ripple-address-codec@^2.0.1:
hash.js "^1.0.3"
x-address-codec "^0.7.0"

[email protected]:
version "0.2.0"
resolved "https://registry.yarnpkg.com/ripple-binary-codec/-/ripple-binary-codec-0.2.0.tgz#cef049f671f398de255e5c190b9f6545c7c7c36f"
integrity sha512-qCf3syhtwPFq70JIh/7VSegynj5gWXVNI5T5I7dobqiNxY3fZQvOePRnchnN1OzC0jMh8x0b2ASmkvIlf259zQ==
dependencies:
babel-runtime "^6.6.1"
bn.js "^4.11.3"
create-hash "^1.1.2"
decimal.js "^5.0.8"
inherits "^2.0.1"
lodash "^4.12.0"
ripple-address-codec "^2.0.1"

[email protected], ripple-binary-codec@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/ripple-binary-codec/-/ripple-binary-codec-0.2.1.tgz#f343da758a64f3aceb4164b8a98fbc153b088c14"
Expand Down Expand Up @@ -6484,7 +6497,7 @@ ripple-bs58check@^2.0.2:
create-hash "^1.1.0"
ripple-bs58 "^4.0.0"

ripple-hashes@0.3.2, ripple-hashes@^0.3.1:
ripple-hashes@^0.3.1:
version "0.3.2"
resolved "https://registry.yarnpkg.com/ripple-hashes/-/ripple-hashes-0.3.2.tgz#f3ac3b1832cec6d0bac07e82acc10a0a6a1cc84e"
integrity sha512-1Emm/raLNChNR5nVoQPJ7NWQpHr8jJjIZDoug4ukw4RZ3+eehO5nyx86a+MaQKAeAc1nJRFwVjBvh9SeYpelYw==
Expand Down Expand Up @@ -6514,10 +6527,10 @@ [email protected]:
bignumber.js "^4.1.0"
lodash "^4.17.4"

ripple-lib@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/ripple-lib/-/ripple-lib-1.2.4.tgz#834c318aae1a255beb9a57bc2b36a08cf9226019"
integrity sha512-hmlj+AIisg4XuSS4ee4uhB9Glc/kM02ZNfl7KF336NDPu81vhqJuR8pikSGwJ/Ay7wIVrP9ny1zR/6FI6lZTZg==
ripple-lib@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/ripple-lib/-/ripple-lib-1.1.2.tgz#e9cf21e7ac61c70c90dd0b105bacc561e5046f12"
integrity sha512-LPWsOi0trS1gcBKJpQPA9vDCn3IUfW0RYaWLLlELoNQY0MJUOG1e0J3qQlDrL+/gRW/MywlPtux5D+I4GiEVHw==
dependencies:
"@types/lodash" "^4.14.85"
"@types/ws" "^3.2.0"
Expand All @@ -6526,8 +6539,8 @@ ripple-lib@^1.2.4:
jsonschema "1.2.2"
lodash "^4.17.4"
ripple-address-codec "^2.0.1"
ripple-binary-codec "0.2.1"
ripple-hashes "0.3.2"
ripple-binary-codec "0.2.0"
ripple-hashes "^0.3.1"
ripple-keypairs "^0.10.1"
ripple-lib-transactionparser "0.7.1"
ws "^3.3.1"
Expand Down

0 comments on commit 414cbf3

Please sign in to comment.