Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Created utils package #265

Merged
merged 19 commits into from
May 17, 2022
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
9b8b784
Created basic package, exported isMobile method and replaced is-mobi…
amirsaran3 Apr 18, 2022
eb94625
Fixed parseBigNumber and parseNearAmount
amirsaran3 Apr 20, 2022
56d1f44
Merge branch 'dev' of https://github.com/near-projects/wallet-selecto…
amirsaran3 Apr 25, 2022
83eda63
Merge branch 'dev' of https://github.com/near-projects/wallet-selecto…
amirsaran3 Apr 28, 2022
d35b4f8
Added createAction and createTransaction
amirsaran3 Apr 28, 2022
9c3a5a3
Added createTransaction to near-wallet
amirsaran3 Apr 28, 2022
e05b6d9
Resolved conflicts with dev
amirsaran3 May 9, 2022
eb79fbe
Created wallet-utils package, added signTransactions
amirsaran3 May 11, 2022
e67d9ae
Fixed build errors, removed parse methods
amirsaran3 May 11, 2022
72d9401
Fixed math-wallet and ledger implementations of signTransactions
amirsaran3 May 11, 2022
2f137c0
Removed utils package, split up wallet-utils.ts, removed wallet.ts
amirsaran3 May 12, 2022
b610a4b
Adjusted param type of signTransactions method
amirsaran3 May 16, 2022
980cab1
Resolved conflicts
amirsaran3 May 16, 2022
20952dc
Fixed issue with rpc networkId
amirsaran3 May 16, 2022
4112715
Changed createAction param to be object not array
amirsaran3 May 16, 2022
3dff3d4
Removed accountId param from signTransactions
amirsaran3 May 16, 2022
3d88778
Fixed wallet-utils test
amirsaran3 May 16, 2022
d270d9c
Fixed defaulting for signing transactions
amirsaran3 May 16, 2022
2a3837b
Defaulted to first account when signerId not provided
amirsaran3 May 17, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
"build:near-wallet": "nx run-many --target=build --projects=near-wallet --configuration=production",
"build:sender": "nx run-many --target=build --projects=sender --configuration=production",
"build:wallet-connect": "nx run-many --target=build --projects=wallet-connect --configuration=production",
"build:utils": "nx run-many --target=build --projects=utils --configuration=production",
amirsaran3 marked this conversation as resolved.
Show resolved Hide resolved
"build:wallet-utils": "nx run-many --target=build --projects=wallet-utils --configuration=production",
"lint": "nx workspace-lint && nx run-many --target=lint --all --parallel",
"lint:fix": "nx run-many --target=lint --all --fix",
"serve:react": "nx serve react",
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export { setupWalletSelector } from "./lib/wallet-selector";
export { ModalOptions, Theme } from "./lib/modal/modal.types";

export { Network, NetworkId } from "./lib/options.types";
export { Subscription } from "./lib/services";
export { Subscription, ProviderService } from "./lib/services";
export { Optional } from "./lib/utils.types";

export {
Expand Down Expand Up @@ -51,9 +51,9 @@ export {
AddKeyAction,
DeleteKeyAction,
DeleteAccountAction,
AddKeyPermission,
} from "./lib/wallet";

export { FinalExecutionOutcome } from "near-api-js/lib/providers";

export { transformActions } from "./lib/wallet";
export { waitFor } from "./lib/helpers";
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Provider, QueryParams, ViewAccessKeyParams } from "./provider.service";
import { Provider } from "./provider.service";
import { QueryParams, ViewAccessKeyParams } from "./provider.service.types";
import { mock } from "jest-mock-extended";
import {
FinalExecutionOutcome,
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/lib/wallet/transactions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Action } from "./transactions.types";
import { AddKeyPermission } from "./transactions.types";
import { transactions, utils } from "near-api-js";
import { BN } from "bn.js";

import { Action, AddKeyPermission } from "./transactions.types";
import BN from "bn.js";

const getAccessKey = (permission: AddKeyPermission) => {
if (permission === "FullAccess") {
Expand Down
118 changes: 118 additions & 0 deletions packages/core/src/lib/wallet/wallet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { transactions, providers } from "near-api-js";
amirsaran3 marked this conversation as resolved.
Show resolved Hide resolved

import {
EventEmitterService,
ProviderService,
LoggerService,
StorageService,
} from "../services";
import { Action } from "./transactions.types";
import { Options } from "../options.types";
import { Optional } from "../utils.types";
import { AccountState } from "../store.types";

export interface HardwareWalletConnectParams {
derivationPath: string;
}

export interface SignAndSendTransactionParams {
signerId?: string;
receiverId?: string;
actions: Array<Action>;
}

export interface SignAndSendTransactionsParams {
transactions: Array<Optional<transactions.Transaction, "signerId">>;
}

export type WalletEvents = {
connected: { pending?: boolean; accounts?: Array<AccountState> };
disconnected: null;
accountsChanged: { accounts: Array<AccountState> };
networkChanged: { networkId: string };
};

export interface WalletMetadata<Type extends string = string> {
id: string;
name: string;
description: string | null;
iconUrl: string;
type: Type;
}

interface BaseWallet<
Type extends string,
ExecutionOutcome = providers.FinalExecutionOutcome
> extends WalletMetadata<Type> {
// Determines if the wallet is available for selection.
// TODO: Make this async to support checking if an injected wallet is installed.
isAvailable(): Promise<boolean>;

// Requests sign in for the given wallet.
// Note: Hardware wallets should defer HID connection until user input is required (e.g. public key or signing).
connect(params?: object): Promise<Array<AccountState>>;

// Removes connection to the wallet and triggers a cleanup of subscriptions etc.
disconnect(): Promise<void>;

// Retrieves all active accounts.
getAccounts(): Promise<Array<AccountState>>;

// Signs a list of actions before sending them via an RPC endpoint.
signAndSendTransaction(
params: SignAndSendTransactionParams
): Promise<ExecutionOutcome>;

// Sings a list of transactions before sending them via an RPC endpoint.
signAndSendTransactions(
params: SignAndSendTransactionsParams
): Promise<ExecutionOutcome extends void ? void : Array<ExecutionOutcome>>;
}

export type BrowserWallet = BaseWallet<"browser", void>;

export interface InjectedWallet extends BaseWallet<"injected"> {
getDownloadUrl(): string;
}

export interface HardwareWallet extends BaseWallet<"hardware"> {
connect(params?: HardwareWalletConnectParams): Promise<Array<AccountState>>;
}

export type BridgeWallet = BaseWallet<"bridge", void>;

export type Wallet =
| BrowserWallet
| InjectedWallet
| HardwareWallet
| BridgeWallet;

export type WalletType = Wallet["type"];

export interface WalletOptions<WalletVariation extends Wallet = Wallet> {
options: Options;
metadata: WalletMetadata<WalletVariation["type"]>;
provider: ProviderService;
emitter: EventEmitterService<WalletEvents>;
logger: LoggerService;
storage: StorageService;
}

export type WalletBehaviour<WalletVariation extends Wallet = Wallet> = Omit<
WalletVariation,
keyof WalletMetadata
>;

export type WalletModule<WalletVariation extends Wallet = Wallet> =
WalletMetadata<WalletVariation["type"]> & {
wallet(
options: WalletOptions<WalletVariation>
): WalletBehaviour<WalletVariation>;
};

export type WalletBehaviourFactory<
WalletVariation extends Wallet,
ExtraWalletOptions extends object = object
> = (
options: WalletOptions<WalletVariation> & ExtraWalletOptions
) => WalletBehaviour<WalletVariation>;
114 changes: 49 additions & 65 deletions packages/ledger/src/lib/ledger.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import { transactions as nearTransactions, utils } from "near-api-js";
import { TypedError } from "near-api-js/lib/utils/errors";
import isMobile from "is-mobile";
import { isMobile } from "@near-wallet-selector/utils";
import { signTransactions } from "@near-wallet-selector/wallet-utils";
import {
WalletModuleFactory,
WalletBehaviourFactory,
WalletBehaviourOptions,
AccountState,
HardwareWallet,
transformActions,
Transaction,
Optional,
} from "@near-wallet-selector/core";

import { isLedgerSupported, LedgerClient, Subscription } from "./ledger-client";
import { Signer, utils } from "near-api-js";

interface AuthData {
accountId: string;
Expand Down Expand Up @@ -60,6 +58,34 @@ const Ledger: WalletBehaviourFactory<HardwareWallet> = async ({
}) => {
const _state = setupLedgerState(storage);

const signer: Signer = {
createKey: () => {
throw Error("Not implemented");
},
getPublicKey: async () => {
if (!_state.authData) {
throw new Error(`${metadata.name} not connected`);
}

return utils.PublicKey.from(_state.authData.publicKey);
},
signMessage: async (message: Uint8Array) => {
if (!_state.authData) {
throw new Error(`${metadata.name} not connected`);
}

const signature = await _state.client.sign({
data: message,
derivationPath: _state.authData.derivationPath,
});

return {
signature,
publicKey: utils.PublicKey.from(_state.authData.publicKey),
};
},
};

const getAccounts = (
authData: AuthData | null = _state.authData
): Array<AccountState> => {
Expand Down Expand Up @@ -146,58 +172,6 @@ const Ledger: WalletBehaviourFactory<HardwareWallet> = async ({
return accountIds[0];
};

const signTransactions = async (
transactions: Array<Optional<Transaction, "signerId">>
) => {
if (!_state.authData) {
throw new Error(`${metadata.name} not connected`);
}

const { accountId, derivationPath, publicKey } = _state.authData;

const [block, accessKey] = await Promise.all([
provider.block({ finality: "final" }),
provider.viewAccessKey({ accountId, publicKey }),
]);

const signedTransactions: Array<nearTransactions.SignedTransaction> = [];

for (let i = 0; i < transactions.length; i++) {
const actions = transformActions(transactions[i].actions);

const transaction = nearTransactions.createTransaction(
accountId,
utils.PublicKey.from(publicKey),
transactions[i].receiverId,
accessKey.nonce + i + 1,
actions,
utils.serialize.base_decode(block.header.hash)
);

const serializedTx = utils.serialize.serialize(
nearTransactions.SCHEMA,
transaction
);

const signature = await _state.client.sign({
data: serializedTx,
derivationPath,
});

const signedTx = new nearTransactions.SignedTransaction({
transaction,
signature: new nearTransactions.Signature({
keyType: transaction.publicKey.keyType,
data: signature,
}),
});

signedTransactions.push(signedTx);
}

return signedTransactions;
};

return {
async connect({ derivationPath }) {
const existingAccounts = getAccounts();
Expand Down Expand Up @@ -266,14 +240,19 @@ const Ledger: WalletBehaviourFactory<HardwareWallet> = async ({
// Note: Connection must be triggered by user interaction.
await connectLedgerDevice();

const [signedTx] = await signTransactions([
{
receiverId,
actions,
},
]);
const signedTransactions = await signTransactions(
[
{
receiverId,
actions,
},
],
signer,
provider,
_state.authData.accountId
);

return provider.sendTransaction(signedTx);
return provider.sendTransaction(signedTransactions[0]);
},

async signAndSendTransactions({ transactions }) {
Expand All @@ -286,7 +265,12 @@ const Ledger: WalletBehaviourFactory<HardwareWallet> = async ({
// Note: Connection must be triggered by user interaction.
await connectLedgerDevice();

const signedTransactions = await signTransactions(transactions);
const signedTransactions = await signTransactions(
transactions,
signer,
provider,
_state.authData.accountId
);

return Promise.all(
signedTransactions.map((signedTx) => provider.sendTransaction(signedTx))
Expand Down
Loading