Skip to content

Commit

Permalink
feat: import a wallet (#2)
Browse files Browse the repository at this point in the history
* ci: ignore post install scripts on workflows

* feat: add chakra-ui and load fonts

* feat: add basic boilerplate for onboard app

* feat: add logger utility function

* build(yarn): add jest

* test: add test for tohexstring utility function

* feat: add utility function to convert hex string to bytes

* feat: add private key stroage service

* build(yarn): inject version and environment

* feat: add a backagorund script that starts the onboarding window if it is not intialized

* build(yarn): add react-i18next package

* feat: add redux and move il8n initialization to window load

* chore: squash

* feat: add functionality to save credentials from redux

* feat: add react router and add basic routing with animated transitions

* feat: add password input and implement password strength feature

* feat: add basic event dtos

* chore: squash

* refactor: rename password input

* chore: squash

* feat: add page to enter mnemonic

* chore: squash

* feat: fix issue with encrypting private key

* build(yarn): add uuid package

* feat: successfully save credentials to storage

* test: configure jest for testing browser extension

* chore: squash

* chore: squash

* fix: background script should successfully receive registration complete event

* feat: add main and dashboard page

* test: fix private key test using encyption polyfills

* refactor: convert hex string to bytes before getting iv, salt and data

* feat: add nivgate and toast to store and handle errors gracefully

* build: update lint and prettier to run on tsx files

* chore: squash

* feat: add basic algorand window provider

* ci: release on beta
  • Loading branch information
kieranroneill authored Mar 7, 2023
1 parent ab8a72f commit d2b7549
Show file tree
Hide file tree
Showing 157 changed files with 13,719 additions and 152 deletions.
3 changes: 2 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
coverage/
build/
coverage/
dist/
node_modules/
2 changes: 1 addition & 1 deletion .github/workflows/lint_build_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
with:
node-version: 'lts/*'
- name: "📦 Install"
run: yarn install
run: yarn install --ignore-scripts
- name: "👕 Lint"
run: yarn lint
- name: "🏗️ Build"
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name: Release
on:
push:
branches:
- beta
- main

jobs:
Expand All @@ -17,7 +18,7 @@ jobs:
with:
node-version: 'lts/*'
- name: "📦 Install"
run: yarn install
run: yarn install --ignore-scripts
- name: "🏗️ Build"
run: yarn build
- name: "🔖 Release"
Expand Down
3 changes: 2 additions & 1 deletion .lintstagedrc.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
module.exports = {
'**/*.{js,json,ts}': (filenames) => `prettier --write ${filenames.join(' ')}`,
'**/*.{js,json,ts,tsx}': (filenames) =>
`prettier --write ${filenames.join(' ')}`,
};
3 changes: 2 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
dist/
coverage/
build/
dist/
node_modules/
21 changes: 21 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module.exports = {
collectCoverage: true,
collectCoverageFrom: [
'<rootDir>/src/**/*.{ts,tsx}',
'!<rootDir>/src/**/*.d.ts',
],
coverageDirectory: 'coverage',
moduleFileExtensions: ['js', 'ts', 'tsx'],
rootDir: '.',
setupFilesAfterEnv: ['<rootDir>/test/setup.ts'],
testEnvironment: 'jsdom',
transform: {
'^.+\\.tsx?$': [
'ts-jest',
{
tsconfig: '<rootDir>/test/tsconfig.json',
},
],
},
verbose: true,
};
44 changes: 35 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "@agoralabs-sh/agora-wallet-firefox",
"name": "@agoralabs-sh/agora-wallet-browser-extension",
"version": "1.0.0",
"description": "An Algorand wallet browser extension to sign data, send transactions and rekey with another account.",
"main": "build/popup/index.js",
"main": "build/main.js",
"repository": {
"type": "git",
"url": "https://github.com/agoralabs-sh/agora-wallet-firefox"
"url": "https://github.com/agoralabs-sh/agora-wallet-browser-extension"
},
"author": {
"name": "Kieran O'Neill",
Expand All @@ -23,32 +23,36 @@
],
"private": true,
"engines": {
"node": ">=14.0.0"
"node": ">=15.0.0"
},
"scripts": {
"build": "cross-env TS_NODE_PROJECT=\"webpack/tsconfig.webpack.json\" webpack --config webpack/webpack.config.ts --config-name production",
"lint": "eslint . --ext .ts --ext .js",
"lint": "eslint . --ext .ts --ext .tsx --ext .js",
"package": "web-ext build",
"prepare": "husky install && ./bin/install_firefox.sh",
"prettier": "prettier --config .prettierrc --write \"**/*.{js,json,ts}\"",
"prettier": "prettier --config .prettierrc --write \"**/*.{js,json,ts,tsx}\"",
"start": "cross-env TS_NODE_PROJECT=\"webpack/tsconfig.webpack.json\" webpack --config webpack/webpack.config.ts --config-name development --watch",
"start:firefox": "web-ext run",
"test": "exit 0"
"test": "jest"
},
"devDependencies": {
"@commitlint/cli": "^17.4.4",
"@commitlint/config-conventional": "^17.4.4",
"@faker-js/faker": "^7.6.0",
"@semantic-release/changelog": "^6.0.2",
"@semantic-release/commit-analyzer": "^9.0.2",
"@semantic-release/exec": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@semantic-release/github": "^8.0.7",
"@semantic-release/npm": "^9.0.2",
"@semantic-release/release-notes-generator": "^10.0.3",
"@types/firefox-webext-browser": "^109.0.0",
"@types/jest": "^29.4.0",
"@types/node": "^18.14.2",
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"@types/uuid": "^9.0.1",
"@types/webextension-polyfill": "^0.10.0",
"@types/zxcvbn": "^4.4.1",
"@typescript-eslint/eslint-plugin": "^5.54.0",
"@typescript-eslint/parser": "^5.54.0",
"copy-webpack-plugin": "^11.0.0",
Expand All @@ -58,9 +62,14 @@
"handlebars-loader": "^1.7.3",
"html-webpack-plugin": "^5.5.0",
"husky": "^8.0.3",
"jest": "^29.4.3",
"jest-environment-jsdom": "^29.4.3",
"lint-staged": "^13.1.2",
"mockzilla": "^0.14.0",
"mockzilla-webextension": "^0.15.0",
"prettier": "^2.8.4",
"semantic-release": "^20.1.1",
"ts-jest": "^29.0.5",
"ts-loader": "^9.4.2",
"ts-node": "^10.9.1",
"tsconfig-paths": "^3.14.2",
Expand All @@ -71,7 +80,24 @@
"webpack-merge": "^5.8.0"
},
"dependencies": {
"@chakra-ui/react": "^2.5.1",
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@reduxjs/toolkit": "^1.9.3",
"algosdk": "^2.1.0",
"buffer": "^6.0.3",
"framer-motion": "^10.0.1",
"i18next": "^22.4.10",
"nanoid": "^4.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react-i18next": "^12.2.0",
"react-icons": "^4.7.1",
"react-redux": "^8.0.5",
"react-router-dom": "^6.8.2",
"react-slide-routes": "^3.0.3",
"uuid": "^9.0.0",
"webextension-polyfill": "^0.10.0",
"zxcvbn": "^4.4.2"
}
}
4 changes: 4 additions & 0 deletions src/@types/assets/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare module '*.svg';
declare module '*.ttf';
declare module '*.woff';
declare module '*.woff2';
2 changes: 2 additions & 0 deletions src/@types/global/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declare const __ENV__: 'development' | 'production';
declare const __VERSION__: string;
10 changes: 10 additions & 0 deletions src/agora-wallet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Algorand } from './algorand-provider';

// Types
import { IWindow } from './types';

(() => {
if (!(window as IWindow).algorand) {
(window as IWindow).algorand = new Algorand();
}
})();
98 changes: 98 additions & 0 deletions src/algorand-provider/entities/Algorand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Entities
import BaseProvider from './BaseProvider';

// Errors
import { NoProvidersDetectedError } from '../errors';

// Types
import { IAddProviderOptions } from '../types';

export default class Algorand {
private defaultProviderIndex: number = 0;
private providers: BaseProvider[];

constructor() {
this.providers = [];
}

/**
* Adds a provider, or if the `replace` option is set, will replace any existing providers matching by ID.
* @param {BaseProvider} provider - the provider to add/replace.
* @param {IAddProviderOptions} options - [optional] options that change the behavior when adding a provider.
*/
public addProvider(
provider: BaseProvider,
options?: IAddProviderOptions
): void {
const existingProvider: BaseProvider | null = this.getProvider(provider.id);

// if no provider exists, just add it
if (!existingProvider) {
this.providers.push(provider);
}

if (existingProvider && options?.replace) {
this.providers = this.providers.map((value) =>
value.id === provider.id ? provider : value
);
}

if (options?.makeDefault) {
this.setDefaultProvider(provider.id);
}
}

/**
* Gets the default provider.
* @returns {BaseProvider | null} the default provider or null if no providers exist.
*/
public getDefaultProvider(): BaseProvider | null {
if (this.providers.length <= 0) {
return null;
}

// if the default provider index is out of bounds, reset to 0.
if (this.defaultProviderIndex > this.providers.length - 1) {
this.defaultProviderIndex = 0;
}

return this.providers[this.defaultProviderIndex] || null;
}

/**
* Gets the provider as specified by its ID.
* @param {string} id - the ID of the provider.
* @returns {BaseProvider | null} the provider if it exists, null otherwise.
*/
public getProvider(id: string): BaseProvider | null {
return this.providers.find((value) => value.id === id) || null;
}

/**
* Gets all the providers.
* @returns {BaseProvider[]} gets all teh providers.
*/
public getProviders(): BaseProvider[] {
return this.providers;
}

/**
* Sets the default provider by ID. If the provider does not exist, the default provider is not changed.
* @param {string} id - the ID of the provider to set to default.
*/
public setDefaultProvider(id: string): void {
const index: number = this.providers.findIndex((value) => value.id === id);

this.defaultProviderIndex = index < 0 ? this.defaultProviderIndex : index;
}

public async signData(data: Uint8Array): Promise<Uint8Array> {
const provider: BaseProvider | null = this.getDefaultProvider();

if (!provider) {
throw new NoProvidersDetectedError('no providers detected');
}

return provider.signData(data);
}
}
16 changes: 16 additions & 0 deletions src/algorand-provider/entities/BaseProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Algodv2 } from 'algosdk';

// Types
import { INewBaseProviderOptions } from '../types';

export default abstract class BaseProvider {
protected client: Algodv2;
public id: string;

constructor({ client, id }: INewBaseProviderOptions) {
this.client = client;
this.id = id;
}

public abstract signData(data: Uint8Array): Promise<Uint8Array>;
}
2 changes: 2 additions & 0 deletions src/algorand-provider/entities/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as Algorand } from './Algorand';
export { default as BaseProvider } from './BaseProvider';
9 changes: 9 additions & 0 deletions src/algorand-provider/enums/ErrorCodeEnum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
enum ErrorCodeEnum {
// general errors
UnknownError = 1000,

// provider errors
NoProvidersDetectedError = 2000,
}

export default ErrorCodeEnum;
1 change: 1 addition & 0 deletions src/algorand-provider/enums/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as ErrorCodeEnum } from './ErrorCodeEnum';
12 changes: 12 additions & 0 deletions src/algorand-provider/errors/BaseError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Enums
import { ErrorCodeEnum } from '../enums';

export default abstract class BaseError extends Error {
public readonly code: ErrorCodeEnum;
public message: string;
public readonly name: string;

public constructor(message: string) {
super(message.toLowerCase());
}
}
10 changes: 10 additions & 0 deletions src/algorand-provider/errors/NoProvidersDetectedError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Enums
import { ErrorCodeEnum } from '../enums';

// Errors
import BaseError from './BaseError';

export default class NoProvidersDetectedError extends BaseError {
public readonly code: ErrorCodeEnum = ErrorCodeEnum.NoProvidersDetectedError;
public readonly name: string = 'NoProvidersDetectedError';
}
10 changes: 10 additions & 0 deletions src/algorand-provider/errors/UnknownError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Enums
import { ErrorCodeEnum } from '../enums';

// Errors
import BaseError from './BaseError';

export default class UnknownError extends BaseError {
public readonly code: ErrorCodeEnum = ErrorCodeEnum.UnknownError;
public readonly name: string = 'UnknownError';
}
3 changes: 3 additions & 0 deletions src/algorand-provider/errors/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as BaseError } from './BaseError';
export { default as NoProvidersDetectedError } from './NoProvidersDetectedError';
export { default as UnknownError } from './UnknownError';
2 changes: 2 additions & 0 deletions src/algorand-provider/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './entities';
export * from './types';
11 changes: 11 additions & 0 deletions src/algorand-provider/types/IAddProviderOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* @property {boolean} makeDefault - [optional] sets the added/replaced provider as the default provider. Defaults to false.
* @property {boolean} replace - [optional] determines whether a matching provider, by ID, should be replaced
* with this one. Defaults to false.
*/
interface IAddProviderOptions {
makeDefault?: boolean;
replace?: boolean;
}

export default IAddProviderOptions;
8 changes: 8 additions & 0 deletions src/algorand-provider/types/INewBaseProviderOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Algodv2 } from 'algosdk';

interface INewBaseProviderOptions {
client: Algodv2;
id: string;
}

export default INewBaseProviderOptions;
2 changes: 2 additions & 0 deletions src/algorand-provider/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export type { default as IAddProviderOptions } from './IAddProviderOptions';
export type { default as INewBaseProviderOptions } from './INewBaseProviderOptions';
Loading

0 comments on commit d2b7549

Please sign in to comment.