Skip to content

Commit

Permalink
feat: wallet connector (FuelLabs#1699)
Browse files Browse the repository at this point in the history
  • Loading branch information
Torres-ssf authored Feb 16, 2024
1 parent 9c321f0 commit f91aa15
Show file tree
Hide file tree
Showing 40 changed files with 2,184 additions and 52 deletions.
5 changes: 5 additions & 0 deletions .changeset/tasty-falcons-sing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fuel-ts/account": patch
---

implement wallet connectors
3 changes: 2 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,15 @@ module.exports = {
],
'@typescript-eslint/no-non-null-assertion': 1,
// Disable error on devDependencies importing since this isn't a TS library
'require-await': 'off',
'@typescript-eslint/require-await': 'error',
'import/no-extraneous-dependencies': ['error', { devDependencies: true }],
'no-await-in-loop': 0,
'prefer-destructuring': 0,
'no-bitwise': 0,
'no-underscore-dangle': 'off',
'class-methods-use-this': 'off',
'no-plusplus': 'off',
'no-param-reassign': ['error', { props: false }],
'@typescript-eslint/no-inferrable-types': 'off',
'@typescript-eslint/lines-between-class-members': [
'error',
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"test:watch": "vitest --watch --config vite.node.config.mts $(scripts/tests-find.sh --node)",
"test:validate": "./scripts/tests-validate.sh",
"test:browser": "vitest --run --coverage --config vite.browser.config.mts $(scripts/tests-find.sh --browser)",
"test:browser:filter": "vitest --run --coverage --config vite.browser.config.mts",
"test:e2e": "vitest --run --config vite.node.config.mts $(scripts/tests-find.sh --e2e)",
"lint": "run-s lint:check prettier:check",
"lint:check": "eslint . --ext .ts --max-warnings 0",
Expand Down
1 change: 1 addition & 0 deletions packages/abi-coder/src/coders/v0/struct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export class StructCoder<TCoders extends Record<string, Coder>> extends Coder<
newOffset += getWordSizePadding(newOffset);
}

// eslint-disable-next-line no-param-reassign
obj[fieldName as keyof DecodedValueOf<TCoders>] = decoded;
return obj;
}, {} as DecodedValueOf<TCoders>);
Expand Down
1 change: 1 addition & 0 deletions packages/abi-coder/src/coders/v1/struct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export class StructCoder<TCoders extends Record<string, Coder>> extends Coder<
let decoded;
[decoded, newOffset] = fieldCoder.decode(data, newOffset);

// eslint-disable-next-line no-param-reassign
obj[fieldName as keyof DecodedValueOf<TCoders>] = decoded;
return obj;
}, {} as DecodedValueOf<TCoders>);
Expand Down
5 changes: 4 additions & 1 deletion packages/account/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,11 @@
"@fuel-ts/hasher": "workspace:*",
"@fuel-ts/interfaces": "workspace:*",
"@fuel-ts/math": "workspace:*",
"@fuel-ts/merkle": "workspace:*",
"@fuel-ts/transactions": "workspace:*",
"@fuel-ts/utils": "workspace:*",
"@fuel-ts/merkle": "workspace:*",
"@fuel-ts/versions": "workspace:*",
"@fuels/assets": "^0.1.4",
"@fuels/vm-asm": "0.42.1",
"graphql": "^16.6.0",
"graphql-request": "5.0.0",
Expand All @@ -68,7 +69,9 @@
"tai64": "^1.0.0",
"events": "^3.3.0",
"@noble/curves": "^1.3.0",
"dexie-observable": "4.0.1-beta.13",
"ethers": "^6.7.1",
"json-rpc-2.0": "^1.7.0",
"portfinder": "^1.0.32",
"tree-kill": "^1.2.2",
"uuid": "^9.0.0"
Expand Down
20 changes: 18 additions & 2 deletions packages/account/src/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { bn } from '@fuel-ts/math';
import { getBytesCopy } from 'ethers';
import type { BytesLike } from 'ethers';

import type { FuelConnector } from './connectors';
import type {
TransactionRequestLike,
CallResult,
Expand All @@ -18,10 +19,10 @@ import type {
Message,
Resource,
ExcludeResourcesOption,
TransactionResponse,
Provider,
ScriptTransactionRequestLike,
ProviderSendTxParams,
TransactionResponse,
} from './providers';
import {
withdrawScript,
Expand Down Expand Up @@ -50,15 +51,18 @@ export class Account extends AbstractAccount {
*/
protected _provider?: Provider;

protected _connector?: FuelConnector;

/**
* Creates a new Account instance.
*
* @param address - The address of the account.
* @param provider - A Provider instance (optional).
*/
constructor(address: string | AbstractAddress, provider?: Provider) {
constructor(address: string | AbstractAddress, provider?: Provider, connector?: FuelConnector) {
super();
this._provider = provider;
this._connector = connector;
this.address = Address.fromDynamicInput(address);
}

Expand Down Expand Up @@ -478,6 +482,13 @@ export class Account extends AbstractAccount {
return this.sendTransaction(request);
}

async signMessage(message: string): Promise<string> {
if (!this._connector) {
throw new FuelError(ErrorCode.MISSING_CONNECTOR, 'A connector is required to sign messages.');
}
return this._connector.signMessage(this.address.toString(), message);
}

/**
* Sends a transaction to the network.
*
Expand All @@ -488,6 +499,11 @@ export class Account extends AbstractAccount {
transactionRequestLike: TransactionRequestLike,
options?: Pick<ProviderSendTxParams, 'awaitExecution'>
): Promise<TransactionResponse> {
if (this._connector) {
return this.provider.getTransactionResponse(
await this._connector.sendTransaction(this.address.toString(), transactionRequestLike)
);
}
const transactionRequest = transactionRequestify(transactionRequestLike);
await this.provider.estimateTxDependencies(transactionRequest);
return this.provider.sendTransaction(transactionRequest, {
Expand Down
261 changes: 261 additions & 0 deletions packages/account/src/connectors/fuel-connector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
/* eslint-disable @typescript-eslint/require-await */
import { EventEmitter } from 'events';

import type { TransactionRequestLike } from '../providers';

import { FuelConnectorEventTypes } from './types';
import type {
FuelConnectorEvents,
ConnectorMetadata,
FuelABI,
Network,
FuelEventArg,
Version,
Asset,
} from './types';

/**
* @name FuelConnector
*
* Wallet Connector is a interface that represents a Wallet Connector and all the methods
* that should be implemented to be compatible with the Fuel SDK.
*/
export abstract class FuelConnector extends EventEmitter {
name: string = '';
metadata: ConnectorMetadata = {} as ConnectorMetadata;
connected: boolean = false;
installed: boolean = false;
events = FuelConnectorEventTypes;

/**
* Should return true if the connector is loaded
* in less then one second.
*
* @returns Always true.
*/
async ping(): Promise<boolean> {
throw new Error('Method not implemented.');
}

/**
* Should return the current version of the connector
* and the network version that is compatible.
*
* @returns boolean - connection status.
*/
async version(): Promise<Version> {
throw new Error('Method not implemented.');
}

/**
* Should return true if the connector is connected
* to any of the accounts available.
*
* @returns The connection status.
*/
async isConnected(): Promise<boolean> {
throw new Error('Method not implemented.');
}

/**
* Should return all the accounts authorized for the
* current connection.
*
* @returns The accounts addresses strings
*/
async accounts(): Promise<Array<string>> {
throw new Error('Method not implemented.');
}

/**
* Should start the connection process and return
* true if the account authorize the connection.
*
* and return false if the user reject the connection.
*
* @emits accounts
* @returns boolean - connection status.
*/
async connect(): Promise<boolean> {
throw new Error('Method not implemented.');
}

/**
* Should disconnect the current connection and
* return false if the disconnection was successful.
*
* @emits assets connection
* @returns The connection status.
*/
async disconnect(): Promise<boolean> {
throw new Error('Method not implemented.');
}

/**
* Should start the sign message process and return
* the signed message.
*
* @param address - The address to sign the message
* @param message - The message to sign all text will be treated as text utf-8
*
* @returns Message signature
*/
async signMessage(_address: string, _message: string): Promise<string> {
throw new Error('Method not implemented.');
}

/**
* Should start the send transaction process and return
* the transaction id submitted to the network.
*
* If the network is not available for the connection
* it should throw an error to avoid the transaction
* to be sent to the wrong network and lost.
*
* @param address - The address to sign the transaction
* @param transaction - The transaction to send
*
* @returns The transaction id
*/
async sendTransaction(_address: string, _transaction: TransactionRequestLike): Promise<string> {
throw new Error('Method not implemented.');
}

/**
* Should return the current account selected inside the connector, if the account
* is authorized for the connection.
*
* If the account is not authorized it should return null.
*
* @returns The current account selected otherwise null.
*/
async currentAccount(): Promise<string | null> {
throw new Error('Method not implemented.');
}

/**
* Should add the the assets metadata to the connector and return true if the asset
* was added successfully.
*
* If the asset already exists it should throw an error.
*
* @emits assets
* @param assets - The assets to add the metadata to the connection.
* @throws Error if the asset already exists
* @returns True if the asset was added successfully
*/
async addAssets(_assets: Array<Asset>): Promise<boolean> {
throw new Error('Method not implemented.');
}

/**
* Should add the the asset metadata to the connector and return true if the asset
* was added successfully.
*
* If the asset already exists it should throw an error.
*
* @emits assets
* @param asset - The asset to add the metadata to the connection.
* @throws Error if the asset already exists
* @returns True if the asset was added successfully
*/
async addAsset(_asset: Asset): Promise<boolean> {
throw new Error('Method not implemented.');
}

/**
* Should return all the assets added to the connector. If a connection is already established.
*
* @returns Array of assets metadata from the connector vinculated to the all accounts from a specific Wallet.
*/
async assets(): Promise<Array<Asset>> {
throw new Error('Method not implemented.');
}

/**
* Should start the add network process and return true if the network was added successfully.
*
* @emits networks
* @throws Error if the network already exists
* @param networkUrl - The URL of the network to be added.
* @returns Return true if the network was added successfully
*/
async addNetwork(_networkUrl: string): Promise<boolean> {
throw new Error('Method not implemented.');
}

/**
* Should start the select network process and return true if the network has change successfully.
*
* @emits networks
* @throws Error if the network already exists
* @param network - The network to be selected.
* @returns Return true if the network was added successfully
*/
async selectNetwork(_network: Network): Promise<boolean> {
throw new Error('Method not implemented.');
}

/**
* Should return all the networks available from the connector. If the connection is already established.
*
* @returns Return all the networks added to the connector.
*/
async networks(): Promise<Array<Network>> {
throw new Error('Method not implemented.');
}

/**
* Should return the current network selected inside the connector. Even if the connection is not established.
*
* @returns Return the current network selected inside the connector.
*/
async currentNetwork(): Promise<Network> {
throw new Error('Method not implemented.');
}

/**
* Should add the ABI to the connector and return true if the ABI was added successfully.
*
* @param contractId - The contract id to add the ABI.
* @param abi - The JSON ABI that represents a contract.
* @returns Return true if the ABI was added successfully.
*/
async addABI(_contractId: string, _abi: FuelABI): Promise<boolean> {
throw new Error('Method not implemented.');
}

/**
* Should return the ABI from the connector vinculated to the all accounts from a specific Wallet.
*
* @param id - The contract id to get the ABI.
* @returns The ABI if it exists, otherwise return null.
*/
async getABI(_id: string): Promise<FuelABI | null> {
throw new Error('Method not implemented.');
}

/**
* Should return true if the abi exists in the connector vinculated to the all accounts from a specific Wallet.
*
* @param id - The contract id to get the abi
* @returns Returns true if the abi exists or false if not.
*/
async hasABI(_id: string): Promise<boolean> {
throw new Error('Method not implemented.');
}

/**
* Event listener for the connector.
*
* @param eventName - The event name to listen
* @param listener - The listener function
*/
on<E extends FuelConnectorEvents['type'], D extends FuelEventArg<E>>(
eventName: E,
listener: (data: D) => void
): this {
super.on(eventName, listener);
return this;
}
}
Loading

0 comments on commit f91aa15

Please sign in to comment.