Skip to content

Commit

Permalink
solution: cancel and speed up transactions new ui (#1347)
Browse files Browse the repository at this point in the history
  • Loading branch information
BOOMER74 authored Dec 8, 2023
1 parent 6e591b7 commit d23785c
Show file tree
Hide file tree
Showing 70 changed files with 709 additions and 2,382 deletions.
8 changes: 7 additions & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ import * as utils from './utils';

export * as PersistentState from './persistentState';
export { BackendApi } from './BackendApi';
export { BitcoinRawTransaction, BitcoinRawTransactionInput, BitcoinRawTransactionOutput } from './transaction/bitcoin';
export {
BitcoinRawTransaction,
BitcoinRawTransactionInput,
BitcoinRawTransactionOutput,
isBitcoinRawTransaction,
} from './transaction/bitcoin';
export {
BlockchainCode,
Blockchains,
Expand Down Expand Up @@ -56,6 +61,7 @@ export {
EthereumReceipt,
EthereumTransaction,
EthereumTransactionType,
isEthereumRawTransaction,
isEthereumTransaction,
} from './transaction/ethereum';
export { default as DefaultLogger } from './logging/DefaultLogger';
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/transaction/bitcoin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@ export interface BitcoinRawTransaction {
vin: BitcoinRawTransactionInput[];
vout: BitcoinRawTransactionOutput[];
}

export function isBitcoinRawTransaction(tx: unknown): tx is BitcoinRawTransaction {
return tx != null && typeof tx === 'object' && 'vin' in tx && Array.isArray(tx.vin);
}
4 changes: 4 additions & 0 deletions packages/core/src/transaction/ethereum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ export interface EthereumReceipt {
transactionIndex: number;
}

export function isEthereumRawTransaction(tx: unknown): tx is EthereumRawTransaction {
return tx != null && typeof tx === 'object' && 'nonce' in tx && typeof tx.nonce === 'string';
}

export function isEthereumTransaction(tx: unknown): tx is EthereumTransaction {
return (
typeof tx === 'object' &&
Expand Down
32 changes: 16 additions & 16 deletions packages/core/src/transaction/workflow/TxBuilder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { BitcoinEntry, EthereumEntry } from '@emeraldpay/emerald-vault-core';
import { InputUtxo, TokenData, TokenRegistry, amountFactory, blockchainIdToCode } from '../../blockchains';
import { DEFAULT_GAS_LIMIT, EthereumTransactionType } from '../ethereum';
import { DEFAULT_VKB_FEE } from './create-tx/CreateBitcoinTx';
import { isAnyBitcoinCreateTx, isErc20CreateTx, isEthereumCreateTx } from './create-tx/types';
import { isAnyBitcoinCreateTx, isErc20CreateTx, isEtherCreateTx } from './create-tx/types';
import { TxBuilder } from './TxBuilder';
import { FeeRange, TxTarget } from './types';

Expand Down Expand Up @@ -216,7 +216,7 @@ describe('TxBuilder', () => {
tokenRegistry,
);

const isCorrectCreateTx = isEthereumCreateTx(createTx);
const isCorrectCreateTx = isEtherCreateTx(createTx);

expect(isCorrectCreateTx).toBeTruthy();

Expand Down Expand Up @@ -287,7 +287,7 @@ describe('TxBuilder', () => {
tokenRegistry,
);

const isCorrectCreateTx = isEthereumCreateTx(createTx);
const isCorrectCreateTx = isEtherCreateTx(createTx);

expect(isCorrectCreateTx).toBeTruthy();

Expand Down Expand Up @@ -360,7 +360,7 @@ describe('TxBuilder', () => {
tokenRegistry,
);

const isCorrectCreateTx = isEthereumCreateTx(ethCreateTx);
const isCorrectCreateTx = isEtherCreateTx(ethCreateTx);

expect(isCorrectCreateTx).toBeTruthy();

Expand Down Expand Up @@ -393,7 +393,7 @@ describe('TxBuilder', () => {

const token = tokenRegistry.byAddress(blockchain, tokenData.address);

expect(erc20CreateTx.amount.equals(token.getAmount(0))).toBeTruthy();
expect(erc20CreateTx.amount.equals(token.getAmount(1))).toBeTruthy();
expect(erc20CreateTx.from).toEqual(ethEntry1.address?.value);
expect(erc20CreateTx.to).toEqual(ethToAddress);
expect(erc20CreateTx.target).toEqual(TxTarget.MANUAL);
Expand Down Expand Up @@ -422,7 +422,7 @@ describe('TxBuilder', () => {
tokenRegistry,
);

const isCorrectCreateTx = isEthereumCreateTx(ethCreateTx);
const isCorrectCreateTx = isEtherCreateTx(ethCreateTx);

expect(isCorrectCreateTx).toBeTruthy();

Expand Down Expand Up @@ -510,7 +510,7 @@ describe('TxBuilder', () => {
tokenRegistry,
);

const isConvertedCreateTx = isEthereumCreateTx(ethCreateTx);
const isConvertedCreateTx = isEtherCreateTx(ethCreateTx);

expect(isConvertedCreateTx).toBeTruthy();

Expand Down Expand Up @@ -571,7 +571,7 @@ describe('TxBuilder', () => {
tokenRegistry,
);

const isConvertedCreateTx = isEthereumCreateTx(ethCreateTx);
const isConvertedCreateTx = isEtherCreateTx(ethCreateTx);

expect(isConvertedCreateTx).toBeTruthy();

Expand Down Expand Up @@ -606,7 +606,7 @@ describe('TxBuilder', () => {
tokenRegistry,
);

const isCorrectCreateTx = isEthereumCreateTx(ethCreateTx);
const isCorrectCreateTx = isEtherCreateTx(ethCreateTx);

expect(isCorrectCreateTx).toBeTruthy();

Expand Down Expand Up @@ -659,7 +659,7 @@ describe('TxBuilder', () => {
tokenRegistry,
);

const isCorrectCreateTx = isEthereumCreateTx(ethCreateTx);
const isCorrectCreateTx = isEtherCreateTx(ethCreateTx);

expect(isCorrectCreateTx).toBeTruthy();

Expand All @@ -681,14 +681,14 @@ describe('TxBuilder', () => {
tokenRegistry,
);

const isConvertedCreateTx = isEthereumCreateTx(etcCreateTx);
const isConvertedCreateTx = isEtherCreateTx(etcCreateTx);

expect(isConvertedCreateTx).toBeTruthy();

if (isConvertedCreateTx) {
const factory = amountFactory(blockchainIdToCode(etcEntry.blockchain));

expect(etcCreateTx.amount.equals(factory(0))).toBeTruthy();
expect(etcCreateTx.amount.equals(factory(1))).toBeTruthy();
expect(etcCreateTx.from).toEqual(etcEntry.address?.value);
expect(etcCreateTx.to).toEqual(ethToAddress);
expect(etcCreateTx.target).toEqual(TxTarget.MANUAL);
Expand Down Expand Up @@ -716,7 +716,7 @@ describe('TxBuilder', () => {
tokenRegistry,
);

const isCorrectCreateTx = isEthereumCreateTx(eth1CreateTx);
const isCorrectCreateTx = isEtherCreateTx(eth1CreateTx);

expect(isCorrectCreateTx).toBeTruthy();

Expand All @@ -738,7 +738,7 @@ describe('TxBuilder', () => {
tokenRegistry,
);

const isConvertedCreateTx = isEthereumCreateTx(eth2CreateTx);
const isConvertedCreateTx = isEtherCreateTx(eth2CreateTx);

expect(isConvertedCreateTx).toBeTruthy();

Expand Down Expand Up @@ -773,7 +773,7 @@ describe('TxBuilder', () => {
tokenRegistry,
);

const isCorrectCreateTx = isEthereumCreateTx(ethCreateTx);
const isCorrectCreateTx = isEtherCreateTx(ethCreateTx);

expect(isCorrectCreateTx).toBeTruthy();

Expand All @@ -797,7 +797,7 @@ describe('TxBuilder', () => {
tokenRegistry,
);

const isConvertedCreateTx = isEthereumCreateTx(restoredEthCreateTx);
const isConvertedCreateTx = isEtherCreateTx(restoredEthCreateTx);

expect(isConvertedCreateTx).toBeTruthy();

Expand Down
31 changes: 15 additions & 16 deletions packages/core/src/transaction/workflow/TxBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,16 @@ import {
isEthereum,
} from '../../blockchains';
import { EthereumTransactionType } from '../ethereum';
import { CreateBitcoinTx, CreateErc20Tx, CreateEthereumTx } from './create-tx';
import { CreateBitcoinTx, CreateErc20Tx, CreateEtherTx } from './create-tx';
import {
AnyCreateTx,
AnyErc20CreateTx,
AnyEthereumCreateTx,
AnyEtherCreateTx,
fromBitcoinPlainTx,
fromErc20PlainTx,
fromEthereumPlainTx,
isAnyErc20CreateTx,
isErc20CreateTx,
isEthereumCreateTx,
isEtherCreateTx,
} from './create-tx/types';
import {
BitcoinPlainTx,
Expand All @@ -53,7 +52,7 @@ interface DataProvider {
getUtxo(entry: BitcoinEntry): InputUtxo[];
}

type EthereumBasicCreateTx = CreateEthereumTx | CreateErc20Tx;
type EthereumBasicCreateTx = CreateEtherTx | CreateErc20Tx;

export class TxBuilder implements BuilderOrigin {
readonly asset: string;
Expand Down Expand Up @@ -126,13 +125,9 @@ export class TxBuilder implements BuilderOrigin {
throw new Error('Bitcoin entry provided for Ethereum transaction');
}

if (tokenRegistry.hasAddress(blockchain, transaction.asset)) {
createTx = fromErc20PlainTx(transaction, tokenRegistry);
} else {
createTx = fromEthereumPlainTx(transaction);
}
createTx = fromEthereumPlainTx(transaction, tokenRegistry);

if (isEthereumCreateTx(createTx) || isErc20CreateTx(createTx)) {
if (isEtherCreateTx(createTx) || isErc20CreateTx(createTx)) {
if (asset !== createTx.getAsset() || blockchain !== createTx.blockchain) {
return this.convertEthereumTx(createTx);
}
Expand Down Expand Up @@ -168,11 +163,11 @@ export class TxBuilder implements BuilderOrigin {
return createTx;
}

private initEthereumTx(entry: EthereumEntry): CreateEthereumTx {
private initEthereumTx(entry: EthereumEntry): CreateEtherTx {
const { asset } = this;
const { getBalance } = this.dataProvider;

const createTx = new CreateEthereumTx(null, blockchainIdToCode(entry.blockchain));
const createTx = new CreateEtherTx(null, blockchainIdToCode(entry.blockchain));

createTx.totalBalance = getBalance(entry, asset) as WeiAny;

Expand Down Expand Up @@ -216,7 +211,7 @@ export class TxBuilder implements BuilderOrigin {
? oldCreateTx.transferFrom ?? ownerAddress
: ownerAddress;
} else {
newCreateTx = new CreateEthereumTx(null, blockchain, type);
newCreateTx = new CreateEtherTx(null, blockchain, type);
newCreateTx.totalBalance = getBalance(entry, asset) as WeiAny;
}

Expand All @@ -233,7 +228,11 @@ export class TxBuilder implements BuilderOrigin {
newCreateTx.priorityGasPrice = feeRange.stdPriorityGasPrice;
}

if (oldCreateTx.target === TxTarget.SEND_ALL) {
if (oldCreateTx.target === TxTarget.MANUAL) {
newCreateTx.amount = isAnyErc20CreateTx(newCreateTx)
? tokenRegistry.byAddress(blockchain, newCreateTx.getAsset()).getAmount(oldCreateTx.amount.number)
: amountFactory(blockchain)(oldCreateTx.amount.number);
} else {
newCreateTx.target = TxTarget.SEND_ALL;

if (!newCreateTx.rebalance()) {
Expand All @@ -248,7 +247,7 @@ export class TxBuilder implements BuilderOrigin {
return newCreateTx;
}

private populateEthereumTx(createTx: AnyEthereumCreateTx | AnyErc20CreateTx, transaction: EthereumPlainTx): void {
private populateEthereumTx(createTx: AnyEtherCreateTx | AnyErc20CreateTx, transaction: EthereumPlainTx): void {
const { asset, entry, feeRange, ownerAddress, tokenRegistry } = this;
const { getBalance } = this.dataProvider;

Expand Down
69 changes: 37 additions & 32 deletions packages/core/src/transaction/workflow/TxSigner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
WalletEntry,
isBitcoinEntry,
isBitcoinTx,
isEthereumTx,
} from '@emeraldpay/emerald-vault-core';
import { Transaction as BitcoinTx } from 'bitcoinjs-lib';
import { BlockchainCode, Blockchains } from '../../blockchains';
Expand Down Expand Up @@ -54,8 +53,36 @@ export class TxSigner implements SignerOrigin {
this.handler = handler;
}

static convertEthereumTx(transaction: EthereumTransaction): UnsignedEthereumTx {
const { from, gas, gasPrice, maxGasPrice, priorityGasPrice, data, nonce, to, type, value } = transaction;
async sign(): Promise<SignedTx> {
const { createTx, entry, password } = this;
const { signTx } = this.handler;

let unsigned: UnsignedTx;

if (isAnyBitcoinCreateTx(createTx)) {
unsigned = createTx.build();
} else {
unsigned = await this.convertEthereumTx(createTx.blockchain, createTx.build());
}

const signedTx = await signTx(unsigned, entry.id, password);

this.verifySigned(signedTx.raw);

if (isBitcoinTx(unsigned)) {
this.updateXPubIndex(unsigned);
}

return signedTx;
}

private async convertEthereumTx(
blockchain: BlockchainCode,
transaction: EthereumTransaction,
): Promise<UnsignedEthereumTx> {
const { from, gas, gasPrice, maxGasPrice, priorityGasPrice, data, to, type, value } = transaction;

const { getNonce } = this.dataProvider;

let gasPrices:
| Pick<UnsignedBasicEthereumTx, 'gasPrice'>
Expand All @@ -72,45 +99,23 @@ export class TxSigner implements SignerOrigin {
};
}

let { nonce } = transaction;

if (nonce == null) {
nonce = await getNonce(blockchain, from);
}

return {
...gasPrices,
data,
from,
gas,
nonce,
to,
nonce: nonce ?? 0,
value: value.number.toString(),
};
}

async sign(): Promise<SignedTx> {
const { createTx, entry, password } = this;
const { getNonce } = this.dataProvider;
const { signTx } = this.handler;

let unsigned: UnsignedTx;

if (isAnyBitcoinCreateTx(createTx)) {
unsigned = createTx.build();
} else {
unsigned = TxSigner.convertEthereumTx(createTx.build());
}

if (isEthereumTx(unsigned)) {
unsigned.nonce = await getNonce(createTx.blockchain, unsigned.from);
}

const signedTx = await signTx(unsigned, entry.id, password);

this.verifySigned(signedTx.raw);

if (isBitcoinTx(unsigned)) {
this.updateXPubIndex(unsigned);
}

return signedTx;
}

private verifySigned(raw: string): void {
const { createTx } = this;

Expand Down
Loading

0 comments on commit d23785c

Please sign in to comment.