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

feat: wallet manager package #12

Merged
merged 52 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
2eac503
chore: setup pipelines, details on package.json file, linter and pret…
wainola Sep 12, 2023
c41a2bc
chore: folder structure for the packages
wainola Sep 14, 2023
3ffdae4
chore: setup for modern yarn and pipelines to check building phase
wainola Sep 15, 2023
4fa5870
chore: remove build step
wainola Sep 15, 2023
0788c62
chore: pipeline for the react component
wainola Sep 15, 2023
c60f73e
chore: scripts and adding some stuff to gitignores
wainola Sep 15, 2023
428f718
chore: eslint
wainola Sep 15, 2023
fe0dc5d
feat: basic implementation for wallet classes
wainola Sep 15, 2023
74c9649
feat: EmvWallet class base implementation
wainola Sep 28, 2023
a8861d0
chore: fix conflicts
wainola Sep 28, 2023
94fd2b0
chore: update package and lock file with vitest
wainola Sep 28, 2023
e6caa8d
feat: substrate wallet class base implementation and some fixes for E…
wainola Sep 28, 2023
43666c3
chore: update tests
wainola Oct 2, 2023
08e7d70
feat: base implementation for reactive controller of wallet manager
wainola Oct 4, 2023
5149995
chore: update Evm base class
wainola Oct 4, 2023
8697e81
chore: basic tests for the reactive controller
wainola Oct 5, 2023
8c9b14b
feat: base implementation for context provider
wainola Oct 11, 2023
e41ebd4
feat: init of providers
wainola Oct 11, 2023
1c3cd31
update package lock
FSM1 Oct 11, 2023
c3e76e3
fix lint and spelling
FSM1 Oct 11, 2023
fa18c10
chore: update on tsconfig and lock file
wainola Oct 11, 2023
c90ab38
feat: public methods to react to change of account and network
wainola Oct 12, 2023
ad4b54a
feat: update yarn lock
wainola Oct 12, 2023
e6c6c0e
update lock
FSM1 Oct 12, 2023
d295070
chore: handling error when connection to provider is rejected and som…
wainola Oct 13, 2023
1386eb8
chore: moving synthetic event creator as util function and removing i…
wainola Oct 18, 2023
b099ce9
chore: addressing comments on pr review
wainola Oct 19, 2023
556c825
chore: removing static methods to instantiate evm wallet class
wainola Oct 19, 2023
797c073
chore: ethers and lit context as peer dependencies
wainola Oct 19, 2023
95b496d
chore: lit context back to dependency definition
wainola Oct 19, 2023
a4cfef5
chore: pr review
wainola Oct 30, 2023
98d0120
chore: ethers as peer dependency and update on lock file
wainola Oct 30, 2023
2c5ad0a
chore: fixating dependencies
wainola Oct 30, 2023
6319a44
chore: versiong yarn
wainola Oct 30, 2023
3f74c0c
chore: going to stable version of yarn
wainola Oct 30, 2023
2a80e1e
chore: update lock file
wainola Oct 30, 2023
eb39261
chore: update yarnrc file
wainola Oct 30, 2023
94d5694
update lock file
FSM1 Oct 30, 2023
2684963
chore: removing checksum behaviour flag
wainola Oct 30, 2023
40592ce
chore: removing js docs comments, adding yarn folder to the gitignore
wainola Nov 2, 2023
fba19a5
chore: changing to removeAllListeners
wainola Nov 2, 2023
f04f2b0
chore: change on gitignore
wainola Nov 2, 2023
909bace
chore: remove reference to yarn release file
wainola Nov 2, 2023
d002ac2
chore: change method names
wainola Nov 2, 2023
244bc78
chore: PR review, changes on constructor implementation
wainola Nov 6, 2023
68ba84d
chore: update gitignore
wainola Nov 7, 2023
ab3e13f
chore: fixing and adding some more basic tests
wainola Nov 7, 2023
c3ab7da
chore: change name of the context provider
wainola Nov 7, 2023
47302b8
chore: init wallet manager on connectedCallback and not the constructor
wainola Nov 23, 2023
7b90cae
chore: default parameter for network value
wainola Nov 23, 2023
3fdf418
chore: pr review last comments
wainola Nov 28, 2023
9405bd5
chore: rename networks property to network
wainola Nov 29, 2023
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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ yarn-error.log*
# yarn NON Zero-install
.pnp.*
.yarn/*
!.yarn/cache
!.yarn/patches
!.yarn/plugins
!.yarn/releases
Expand All @@ -44,4 +45,5 @@ public/sygma-runtime-config*
.idea
.vscode

/dist
/dist
/examples
874 changes: 0 additions & 874 deletions .yarn/releases/yarn-3.6.3.cjs

This file was deleted.

893 changes: 893 additions & 0 deletions .yarn/releases/yarn-4.0.1.cjs

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
nodeLinker: node-modules

yarnPath: .yarn/releases/yarn-3.6.3.cjs

compressionLevel: 0

enableGlobalCache: true

nodeLinker: node-modules

yarnPath: .yarn/releases/yarn-4.0.1.cjs
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"prettier": "^3.0.3",
"typescript": "^5.2.2"
},
"packageManager": "yarn@3.6.3",
"packageManager": "yarn@4.0.1",
"scripts": {
"build:wallet-manager": "yarn workspace @builtwithsygma/sygmaprotocol-wallet-manager clean && yarn workspace @builtwithsygma/sygmaprotocol-wallet-manager build",
"build:sdk-manager": "yarn workspace @builtwithsygma/sygmaprotocol-sdk-manager clean && yarn workspace @builtwithsygma/sygmaprotocol-sdk-manager build",
Expand Down
16 changes: 12 additions & 4 deletions packages/wallet-manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,24 @@
"dev": "tsc --build --clean && tsc --build ./tsconfig.json --watch",
"clean": "rm -rf ./build",
"lint": "eslint 'src/**/*.ts'",
"lint:fix": "yarn run lint --fix"
"lint:fix": "yarn run lint --fix",
"test": "vitest --config ./vite.config.ts"
},
"author": "Sygmaprotocol Product Team",
"devDependencies": {
"@polkadot/types": "10.7.2",
"typescript": "^5.2.2"
"jsdom": "^22.1.0",
"typescript": "^5.2.2",
"vitest": "^0.34.5"
},
"dependencies": {
"@ethersproject/abstract-signer": "5.7.0",
"@lit/context": "1.0.0",
"@polkadot/api": "10.7.2",
"@polkadot/extension-dapp": "^0.46.5",
"ethers": "5.7.2"
"@polkadot/extension-dapp": "0.46.5",
"lit": "3.0.0"
wainola marked this conversation as resolved.
Show resolved Hide resolved
},
"peerDependencies": {
"ethers": "*"
}
}
81 changes: 81 additions & 0 deletions packages/wallet-manager/src/WalletManagerContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { provide, createContext } from '@lit/context';
import { LitElement, html } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import {
type WalletManagerController as TWalletManagerController,
WalletManagerController
} from './WalletManagerController';
import { ethers } from 'ethers';
import { ApiPromise } from '@polkadot/api';
import { Network } from './types';

export const WalletManagerContext = createContext<
TWalletManagerController | undefined
>('wallet-context');

export const AccountContext = createContext<string | undefined>(
'account-context'
);

/**
* @name WalletManagerContextProvider
* @description This component is responsible for providing the WalletManagerController as a context to all its children.
* It also provides a synthetic event creator function that can be used to dispatch custom events.
* @example
* For you to consume the context objects, you need to wrap up your component with the wallet-manager-context on your render method.
* You can either pass a web3Provider, an apiPromise or a wssConnectionUrl to the component.
*
* Passing a web3Provider and an apiPromise
* <wallet-manager-context
* .web3Provider=${web3Provider}
* .apiPromise=${apiPromise}
* >
* <your-component></your-component>
* </wallet-manager-context>
*
* Passing a wssConnectionUrl
* <wallet-manager-context
* .wssConnectionUrl=${wssConnectionUrl}
* >
* <your-component></your-component>
* </wallet-manager-context>
*/
@customElement('wallet-manager-context-provider')
export class WalletManagerContextProvider extends LitElement {
@provide({ context: WalletManagerContext })
@state()
walletManagerController?: WalletManagerController;
wainola marked this conversation as resolved.
Show resolved Hide resolved

@property({ type: Object })
web3Provider?: ethers.providers.Web3Provider;

@property({ type: Object })
apiPromise?: ApiPromise;

@property({ type: String })
wssConnectionUrl?: string;

@property({ type: String })
network: Network;

constructor() {
super();
this.network = Network.EVM;
}

connectedCallback(): void {
this.walletManagerController = new WalletManagerController(
this,
this.network,
{
web3Provider: this.web3Provider,
apiPromise: this.apiPromise,
wssConnectionUrl: this.wssConnectionUrl
}
);
}

render() {
return html` <slot></slot>`;
}
}
128 changes: 128 additions & 0 deletions packages/wallet-manager/src/WalletManagerController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { ReactiveControllerHost } from 'lit';
import { EvmWallet, SubstrateWallet } from '.';
import { Web3Provider } from '@ethersproject/providers';
import { ApiPromise } from '@polkadot/api';
import { AddChain, Network } from './types';
import { customEVMEvents, IWalletManagerController } from './interfaces';
import { Signer } from '@ethersproject/abstract-signer';

export class WalletManagerController implements IWalletManagerController {
private host: ReactiveControllerHost;
evmWallet?: EvmWallet;
substrateWallet?: SubstrateWallet;
account?: string;
substrateAccount?: string;

constructor(
host: ReactiveControllerHost,
network: Network,
initArgument: {
web3Provider?: Web3Provider;
apiPromise?: ApiPromise;
wssConnectionUrl?: string;
}
) {
(this.host = host).addController(this);

if (network === Network.EVM) {
this.initWeb3Provider(initArgument.web3Provider);
} else if (network === Network.Substrate) {
this.initFromApiPromise(initArgument as ApiPromise);
} else if (network === Network.Substrate && initArgument.wssConnectionUrl) {
this.initFromWssProvider(initArgument as string);
}
}

hostDisconnected(): void {
if (this.evmWallet) {
this.evmWallet?.removeAllListeners();
}
}

public addAccountChangedEventListener(
callback: (account: string) => void
): void {
this.evmWallet?.addListener(customEVMEvents.ACCOUNT_CHANGE, (account) => {
this.account = account;
callback(account);
});
}

public addChainChangedEventListener(callback: () => void): void {
this.evmWallet?.addListener(customEVMEvents.CHAIN_CHANGE, () => {
this.account = this.evmWallet?.address;
callback();
});
}

public initWeb3Provider(web3Provider?: Web3Provider): void {
this.evmWallet = new EvmWallet(web3Provider);
}

public initFromApiPromise(apiPromise: ApiPromise): void {
this.substrateWallet = SubstrateWallet.initFromApiPromise(apiPromise);
}

public async initFromWssProvider(wssProvider: string): Promise<void> {
this.substrateWallet =
await SubstrateWallet.initFromWssProvider(wssProvider);
}

public async addChain({
chainId,
chainName,
rpcUrl,
nativeCurrency
}: AddChain): Promise<void> {
try {
await this.evmWallet?.addChain({
chainId,
chainName,
rpcUrl,
nativeCurrency
});
} catch (error) {
throw error;
}
}

public async connectToSubstrate(): Promise<void> {
await this.substrateWallet?.connect();
this.substrateAccount = this.substrateWallet?.substrateAccount;
this.host.requestUpdate();
wainola marked this conversation as resolved.
Show resolved Hide resolved
}

public async connectEvmWallet(): Promise<void> {
await this.evmWallet?.connect();
this.account = this.evmWallet?.address;
this.host.requestUpdate();
}

public getSigner(): Signer {
if (this.evmWallet?.signer) {
return this.evmWallet.signer;
} else {
throw new Error('EvmWallet not initialized');
}
}

get accountData(): string | undefined {
return this.account;
}

get substrateAccountAddress(): string | undefined {
return this.substrateAccount;
}

get provider(): Web3Provider | undefined {
return this.evmWallet?.web3Provider;
}

get apiPromise(): ApiPromise | undefined {
if (this.substrateWallet) {
return this.substrateWallet.apiPromise;
} else {
throw new Error('SubstrateWallet not initialized');
}
}
}
10 changes: 10 additions & 0 deletions packages/wallet-manager/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export { EvmWallet, SubstrateWallet } from './wallets';
export { WalletManagerController } from './WalletManagerController';
export {
WalletManagerContextProvider,
WalletManagerContext,
AccountContext
} from './WalletManagerContext';

export { type SyntheticEventCreator } from './types';
export { syntheticEventCreator } from './utils';
48 changes: 48 additions & 0 deletions packages/wallet-manager/src/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Web3Provider } from '@ethersproject/providers';
import { ApiPromise } from '@polkadot/api';
import { ReactiveController } from 'lit';
import { AddChain } from '../types';
import { Signer } from '@ethersproject/abstract-signer';

export interface SupportedWallet {
id: string;
name: string;
icon: string;
providerName: string;
}

export interface IEvmWallet {
web3Provider: Web3Provider;
address?: string;
signer?: Signer;
connect(): Promise<void>;
addChain({
chainId,
rpcUrl,
chainName
}: Pick<AddChain, 'chainId' | 'rpcUrl' | 'chainName'>): Promise<void>;
}

export interface ISubstrateWallet {
apiPromise: ApiPromise;
substrateAccount?: string;
}

export interface IWalletManagerController extends ReactiveController {
evmWallet?: IEvmWallet;
substrateWallet?: ISubstrateWallet;
account?: string;
substrateAccount?: string;

initWeb3Provider(web3Provider?: Web3Provider): void;
initFromWssProvider(wssProvider: string): Promise<void>;
initFromApiPromise(apiPromise: ApiPromise): void;
addChain(addChainParameters: AddChain): Promise<void>;
connectToSubstrate(): Promise<void>;
connectEvmWallet(): Promise<void>;
}

export const customEVMEvents = {
ACCOUNT_CHANGE: 'account-change',
CHAIN_CHANGE: 'chain-change'
} as const;
27 changes: 27 additions & 0 deletions packages/wallet-manager/src/test/EvmWallet.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ethers } from 'ethers';
import { ExternalProvider } from '@ethersproject/providers';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { EvmWallet } from '../';

describe('EvmWallet', () => {
describe('EvmWallet + provider on window', () => {
beforeEach(() => {
window.ethereum = {
request: () => Promise.resolve(true),
on: vi.fn()
} as ExternalProvider & { on: () => void };
});
it('should be able to create an instance of EvmWallet passing a web3Provider', () => {
const w3Provider = new ethers.providers.Web3Provider(window.ethereum);
const evmWallet = new EvmWallet(w3Provider);

expect(evmWallet).toBeInstanceOf(EvmWallet);
});

it('should be able to create an instance of EvmWallet without passing a web3Provider', () => {
const evmWallet = new EvmWallet();

expect(evmWallet).toBeInstanceOf(EvmWallet);
});
});
});
Loading
Loading