Skip to content

Commit

Permalink
Merge pull request #155 from nautls/improve-injection-time
Browse files Browse the repository at this point in the history
Improve overall performance
  • Loading branch information
arobsn authored Jul 7, 2024
2 parents ab217de + b88f712 commit 3b8229d
Show file tree
Hide file tree
Showing 53 changed files with 1,850 additions and 631 deletions.
5 changes: 2 additions & 3 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"parser": "vue-eslint-parser",
"parserOptions": { "parser": "@typescript-eslint/parser" },
"env": { "es6": true },
"ignorePatterns": ["node_modules", "dist", "coverage", "jest.config.ts", "vue.config.js"],
"ignorePatterns": ["node_modules", "dist", "coverage"],
"extends": [
"eslint:recommended",
"plugin:vue/vue3-recommended",
Expand All @@ -17,8 +17,7 @@
"console": true,
"WebAssembly": true,
"chrome": true,
"process": true,
"Buffer": true
"process": true
},
"settings": {
"import/resolver": {
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nautilus-wallet",
"version": "0.11.0",
"version": "0.12.0",
"private": true,
"type": "module",
"engines": {
Expand Down Expand Up @@ -57,7 +57,6 @@
"yaml": "^2.4.5"
},
"devDependencies": {
"@crxjs/vite-plugin": "^2.0.0-beta.23",
"@ergo-graphql/types": "^0.5.1",
"@nautilus-js/eip12-types": "^0.1.11",
"@tsconfig/node20": "^20.1.4",
Expand All @@ -67,6 +66,7 @@
"@types/json-bigint": "^1.0.4",
"@types/lodash-es": "^4.17.12",
"@types/qrcode": "^1.5.5",
"@types/webextension-polyfill": "^0.10.7",
"@typescript-eslint/parser": "^7.13.0",
"@vitejs/plugin-vue": "^5.0.5",
"@vue/eslint-config-prettier": "^9.0.0",
Expand All @@ -84,6 +84,7 @@
"vite-plugin-node-polyfills": "^0.22.0",
"vite-plugin-top-level-await": "^1.4.1",
"vite-plugin-wasm": "^3.3.0",
"vite-plugin-web-extension": "^4.1.4",
"vite-plugin-windicss": "^1.9.3",
"vite-svg-loader": "^5.1.0",
"vitest": "^1.6.0",
Expand Down
1,550 changes: 1,401 additions & 149 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/chains/ergo/babelFees.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { BigNumber } from "bignumber.js";
import { first, isEmpty, orderBy, sortBy } from "lodash-es";
import { orderBy, sortBy } from "lodash-es";
import { first, isEmpty } from "@fleet-sdk/common";
import { Constant } from "ergo-lib-wasm-browser";
import { addressFromErgoTree } from "./addresses";
import { graphQLService } from "@/chains/ergo/services/graphQlService";
Expand Down
5 changes: 3 additions & 2 deletions src/chains/ergo/boxFetcher.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { difference, find, isEmpty, sortBy, unionBy } from "lodash-es";
import { difference, sortBy, unionBy } from "lodash-es";
import { isEmpty } from "@fleet-sdk/common";
import { addressesDbService } from "@/database/addressesDbService";
import { assetsDbService } from "@/database/assetsDbService";
import { utxosDbService } from "@/database/utxosDbService";
Expand Down Expand Up @@ -27,7 +28,7 @@ export async function fetchBoxes(
if (
options.useAllAddressesAsFallback &&
isEmpty(boxes) &&
!find(pendingBoxes, (b) => !b.locked && b.content)
!pendingBoxes.find((b) => !b.locked && b.content)
) {
boxes = await fetchBoxesFromExplorer(difference(await getAllAddresses(walletId), addresses));
}
Expand Down
63 changes: 0 additions & 63 deletions src/chains/ergo/dataSigning.ts

This file was deleted.

19 changes: 12 additions & 7 deletions src/chains/ergo/services/graphQlService.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { chunk, first, isEmpty, min } from "lodash-es";
import { Address, Box, Header, Info, SignedTransaction, State, Token } from "@ergo-graphql/types";
import { Client, createClient, fetchExchange, gql, TypedDocumentNode } from "@urql/core";
import { retryExchange } from "@urql/exchange-retry";
import { hex, utf8 } from "@fleet-sdk/crypto";
import { SColl, SConstant, SPair } from "@fleet-sdk/serializer";
import { chunk, first, isEmpty, min } from "@fleet-sdk/common";
import { browser, hasBrowserContext } from "@/common/browser";
import { sigmaDecode } from "@/chains/ergo/serialization";
import { ErgoBox, Registers } from "@/types/connector";
Expand Down Expand Up @@ -274,7 +274,7 @@ class GraphQLService {
}

private async getUsedAddressesFromChunk(addresses: string[]): Promise<string[]> {
const query = gql<{ addresses: Address[] }>`
const query = gql`
query Addresses($addresses: [String!]!) {
addresses(addresses: $addresses) {
address
Expand All @@ -283,21 +283,26 @@ class GraphQLService {
}
`;

const response = await this.#queryClient.query(query, { addresses }).toPromise();
const response = await this.#queryClient
.query<{ addresses: Address[] }>(query, { addresses })
.toPromise();

return response.data?.addresses.filter((x) => x.used).map((x) => x.address) || [];
}

async getCurrentHeight(): Promise<number | undefined> {
const query = gql<{ blockHeaders: Header[] }>`
const query = gql`
query query {
blockHeaders(take: 1) {
height
}
}
`;

const response = await this.#queryClient.query(query, {}).toPromise();
return first(response.data?.blockHeaders)?.height;
const response = await this.#queryClient
.query<{ blockHeaders: Header[] }>(query, {})
.toPromise();
return response.data?.blockHeaders[0]?.height;
}

public async getUnspentBoxes(addresses: string[]): Promise<ErgoBox[]> {
Expand Down Expand Up @@ -359,7 +364,7 @@ class GraphQLService {
heights = heights.concat(chunk);
}

return { oldest: min(heights), count: heights.length };
return { oldest: min(...heights), count: heights.length };
}

public async getMempoolBoxes(address: string): Promise<ErgoBox[]> {
Expand Down
138 changes: 138 additions & 0 deletions src/chains/ergo/signing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { hex, randomBytes, utf8 } from "@fleet-sdk/crypto";
import { ErgoMessage } from "@fleet-sdk/core";
import {
EIP12UnsignedTransaction,
first,
SignedInput,
SignedTransaction,
some
} from "@fleet-sdk/common";
import { getPrivateDeriver, Prover } from "./transaction/prover";
import { getChangeAddress } from "./addresses";
import { extractAddressesFromInputs } from "./extraction";
import HdKey from "./hdKey";
import { graphQLService } from "./services/graphQlService";
import { hdKeyPool } from "@/common/objectPool";
import { SigningState, WalletType } from "@/types/internal";
import { addressesDbService } from "@/database/addressesDbService";
import { walletsDbService } from "@/database/walletsDbService";
import { IDbAddress } from "@/types/database";
import store from "@/store";

export type UnsignedAuthMessage = {
message: string;
origin: string;
};

export type AuthSignedMessage = {
signedMessage: string;
proof: string;
};

/**
* Creates a EIP-28 signing message
* @param message
* @param origin
* @returns the signing message formatted as "signingMessage;origin;timestamp;randomBytes"
*/
export function buildAuthMessage(message: string, origin: string): string {
const rand = hex.encode(randomBytes(32));
return `${message};${origin};${Math.floor(Date.now() / 1000)};${rand}`;
}

export async function signMessage(
message: ErgoMessage,
addresses: string[],
walletId: number,
password: string
): Promise<string> {
return hex.encode(
new Prover(await getPrivateDeriver(walletId, password))
.from(await addressesDbService.getByWalletIdAndScripts(walletId, addresses, "strict"))
.signMessage(message.serialize().toBytes())
);
}

export async function signAuthMessage(
unsigned: UnsignedAuthMessage,
addresses: string[],
walletId: number,
password: string
): Promise<AuthSignedMessage> {
const signedMessage = buildAuthMessage(unsigned.message, unsigned.origin);
const proof = new Prover(await getPrivateDeriver(walletId, password))
.from(await addressesDbService.getByWalletIdAndScripts(walletId, addresses, "strict"))
.signMessage(utf8.decode(signedMessage));

return { signedMessage, proof: hex.encode(proof) };
}

type SignStateReportCallback = (newState: Partial<SigningState>) => void;

export async function signTransaction(
transaction: EIP12UnsignedTransaction,
walletId: number,
password: string,
callback: SignStateReportCallback
): Promise<SignedTransaction>;
export async function signTransaction(
transaction: EIP12UnsignedTransaction,
walletId: number,
password: string,
callback: SignStateReportCallback,
inputsToSign?: number[]
): Promise<SignedInput[]>;
export async function signTransaction(
transaction: EIP12UnsignedTransaction,
walletId: number,
password: string,
callback: SignStateReportCallback,
inputsToSign?: number[]
) {
const inputAddresses = extractAddressesFromInputs(transaction.inputs);
const ownAddresses = await addressesDbService.getByWalletId(walletId);
const addresses = ownAddresses
.filter((a) => inputAddresses.includes(a.script))
.map((a) => dbAddressMapper(a));

if (addresses.length === 0) {
const changeIndex = store.state.currentWallet.settings.defaultChangeIndex;
const change = (a: IDbAddress): boolean => a.index === changeIndex;
const firstIndex = (a: IDbAddress): boolean => a.index === 0;

addresses.push(
dbAddressMapper(
ownAddresses.find(change) ?? ownAddresses.find(firstIndex) ?? first(ownAddresses)
)
);
}

if (callback) callback({ statusText: "Loading context data..." });

const isLedger = store.state.currentWallet.type === WalletType.Ledger;
const deriver = isLedger
? hdKeyPool.get(store.state.currentWallet.publicKey)
: await HdKey.fromMnemonic(await walletsDbService.getMnemonic(walletId, password));

const encodedAddresses = ownAddresses.map((a) => a.script);
const changeAddress = getChangeAddress(transaction.outputs, encodedAddresses);

const blockHeaders = isLedger ? [] : await graphQLService.getBlockHeaders({ take: 10 });
const changeIndex = ownAddresses.find((a) => a.script === changeAddress)?.index ?? 0;
const prover = new Prover(deriver)
.from(addresses)
.useLedger(isLedger)
.changeIndex(changeIndex)
.setHeaders(blockHeaders)
.setCallback(callback);

if (inputsToSign && some(inputsToSign)) {
return await prover.signInputs(transaction, inputsToSign);
} else {
return await prover.signTx(transaction);
}
}

function dbAddressMapper(a: IDbAddress) {
return { ...a, balance: undefined };
}
9 changes: 4 additions & 5 deletions src/chains/ergo/transaction/interpreter/outputInterpreter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { BigNumber } from "bignumber.js";
import { find, findIndex, first, isEmpty } from "lodash-es";
import { AddressType, ErgoAddress, Network } from "@fleet-sdk/core";
import { EIP12UnsignedInput } from "@fleet-sdk/common";
import { EIP12UnsignedInput, first, isEmpty } from "@fleet-sdk/common";
import { utf8 } from "@fleet-sdk/crypto";
import { isBabelContract } from "../../babelFees";
import { ERG_DECIMALS, ERG_TOKEN_ID, MAINNET } from "@/constants/ergo";
Expand Down Expand Up @@ -51,7 +50,7 @@ export class OutputInterpreter {
}

public get isMinting(): boolean {
return find(this._assets, (a) => a.minting) !== undefined;
return this._assets.find((a) => a.minting) !== undefined;
}

public get isBabelBoxSwap(): boolean {
Expand Down Expand Up @@ -129,7 +128,7 @@ export class OutputInterpreter {

const minting = this.getMintingToken();
if (minting) {
const index = findIndex(tokens, (t) => t.tokenId === minting.tokenId);
const index = tokens.findIndex((t) => t.tokenId === minting.tokenId);
if (index > -1) {
tokens[index] = minting;
}
Expand All @@ -144,7 +143,7 @@ export class OutputInterpreter {
return undefined;
}

const token = find(this._box.assets, (b) => b.tokenId === firstInputId);
const token = this._box.assets.find((b) => b.tokenId === firstInputId);
if (!token) {
return undefined;
}
Expand Down
5 changes: 3 additions & 2 deletions src/chains/ergo/transaction/interpreter/txInterpreter.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { BigNumber } from "bignumber.js";
import { difference, find, groupBy, isEmpty } from "lodash-es";
import { difference, groupBy } from "lodash-es";
import {
EIP12UnsignedInput,
EIP12UnsignedTransaction,
isEmpty,
some,
utxoDiff,
utxoSum
Expand Down Expand Up @@ -43,7 +44,7 @@ export class TxInterpreter {
this._tx = tx;
this._addresses = ownAddresses;
this._assetInfo = assetInfo;
this._feeBox = find(tx.outputs, (b) => isMinerFeeContract(b.ergoTree));
this._feeBox = tx.outputs.find((b) => isMinerFeeContract(b.ergoTree));

const isOwnErgoTree = (tree: string) => this._addresses.includes(addressFromErgoTree(tree));
const isSendingOutput = (output: ErgoBoxCandidate) =>
Expand Down
Loading

0 comments on commit 3b8229d

Please sign in to comment.