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

Custom wallet #106

Merged
merged 7 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ dist

# Stores VSCode versions used for testing VSCode extensions
.vscode-test
.vscode
#.vscode

# yarn v2
.yarn/cache
Expand Down
2 changes: 1 addition & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ dist

# Stores VSCode versions used for testing VSCode extensions
.vscode-test
.vscode
#.vscode

# yarn v2
.yarn/cache
Expand Down
7 changes: 7 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"description": "React hooks for using Algorand compatible wallets in dApps.",
"scripts": {
"dev": "yarn storybook",
"build": "rm -rf dist && rollup -c",
"build": "rimraf dist && rollup -c",
"test": "jest",
"lint": "eslint '**/*.{js,ts,tsx}'",
"format": "prettier --check '**/*.{js,ts,tsx}'",
Expand Down Expand Up @@ -60,6 +60,7 @@
"algosdk": "^2.1.0",
"babel-jest": "^29.1.2",
"babel-loader": "^9.0.0",
"buffer": "^6.0.3",
"commitizen": "4.3.0",
"css-loader": "^6.5.1",
"cz-conventional-changelog": "3.3.0",
Expand All @@ -80,6 +81,7 @@
"react-dom": "^18.2.0",
"release-it": "^16.1.0",
"require-from-string": "^2.0.2",
"rimraf": "^5.0.1",
"rollup": "^3.3.0",
"rollup-plugin-analyzer": "^4.0.0",
"rollup-plugin-dts": "^5.0.0",
Expand Down
104 changes: 104 additions & 0 deletions src/clients/custom/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import Algod, { getAlgodClient } from '../../algod'
import BaseClient from '../base'
import { DEFAULT_NETWORK, PROVIDER_ID } from '../../constants'
import { debugLog } from '../../utils/debugLog'
import { ICON } from './constants'
import type _algosdk from 'algosdk'
import type { Network } from '../../types/node'
import type { InitParams } from '../../types/providers'
import type { Metadata, Wallet } from '../../types/wallet'
import type { CustomProvider, CustomWalletClientConstructor } from './types'

class CustomWalletClient extends BaseClient {
network: Network
providerProxy: CustomProvider

static metadata: Metadata = {
id: PROVIDER_ID.CUSTOM,
icon: ICON,
isWalletConnect: false,
name: 'Custom'
}

constructor({
providerProxy,
metadata,
algosdk,
algodClient,
network
}: CustomWalletClientConstructor) {
super(metadata, algosdk, algodClient)

this.providerProxy = providerProxy
this.network = network
}

static async init({
clientOptions,
algodOptions,
algosdkStatic,
network = DEFAULT_NETWORK
}: InitParams<PROVIDER_ID.CUSTOM>): Promise<BaseClient | null> {
try {
debugLog(`${PROVIDER_ID.CUSTOM.toUpperCase()} initializing...`)

if (!clientOptions) {
throw new Error(`Attempt to create custom wallet with no provider specified.`)
}

const algosdk = algosdkStatic || (await Algod.init(algodOptions)).algosdk
const algodClient = getAlgodClient(algosdk, algodOptions)

try {
return new CustomWalletClient({
providerProxy: clientOptions.getProvider({
algod: algodClient,
algosdkStatic: algosdk,
network
}),
metadata: {
...CustomWalletClient.metadata,
name: clientOptions.name,
icon: clientOptions.icon ?? CustomWalletClient.metadata.icon
},
algodClient,
algosdk,
network
})
} finally {
debugLog(`${PROVIDER_ID.CUSTOM.toUpperCase()} initialized`, '✅')
}
} catch (e) {
console.error('Error initializing...', e)
return null
}
}

async connect(): Promise<Wallet> {
return await this.providerProxy.connect(this.metadata)
}

async disconnect() {
await this.providerProxy.disconnect()
}

async reconnect(): Promise<Wallet | null> {
return await this.providerProxy.reconnect(this.metadata)
}

async signTransactions(
connectedAccounts: string[],
txnGroups: Uint8Array[] | Uint8Array[][],
indexesToSign?: number[],
returnGroup = true
) {
return await this.providerProxy.signTransactions(
connectedAccounts,
txnGroups,
indexesToSign,
returnGroup
)
}
}

export default CustomWalletClient
2 changes: 2 additions & 0 deletions src/clients/custom/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const ICON =
"data:image/svg+xml,%3Csvg fill='%23000000' width='800px' height='800px' viewBox='0 0 24 24' id='wallet' data-name='Flat Line' xmlns='http://www.w3.org/2000/svg' class='icon flat-line'%3E%3Cpath id='secondary' d='M16,12h5V8H5A2,2,0,0,1,3,6V19a1,1,0,0,0,1,1H20a1,1,0,0,0,1-1V16H16a1,1,0,0,1-1-1V13A1,1,0,0,1,16,12Z' style='fill: rgb(44, 169, 188); stroke-width: 2;'%3E%3C/path%3E%3Cpath id='primary' d='M19,4H5A2,2,0,0,0,3,6H3A2,2,0,0,0,5,8H21' style='fill: none; stroke: rgb(0, 0, 0); stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;'%3E%3C/path%3E%3Cpath id='primary-2' data-name='primary' d='M21,8V19a1,1,0,0,1-1,1H4a1,1,0,0,1-1-1V6A2,2,0,0,0,5,8Zm0,4H16a1,1,0,0,0-1,1v2a1,1,0,0,0,1,1h5Z' style='fill: none; stroke: rgb(0, 0, 0); stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;'%3E%3C/path%3E%3C/svg%3E"
3 changes: 3 additions & 0 deletions src/clients/custom/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import custom from './client'

export default custom
33 changes: 33 additions & 0 deletions src/clients/custom/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type algosdk from 'algosdk'
import type { Network } from '../../types/node'
import type { Metadata, Wallet } from '../../types/wallet'

export type CustomOptions = {
name: string
icon?: string
getProvider: (params: {
network?: Network
algod?: algosdk.Algodv2
algosdkStatic?: typeof algosdk
}) => CustomProvider
}

export type CustomProvider = {
connect(metadata: Metadata): Promise<Wallet>
disconnect(): Promise<void>
reconnect(metadata: Metadata): Promise<Wallet | null>
signTransactions(
connectedAccounts: string[],
txnGroups: Uint8Array[] | Uint8Array[][],
indexesToSign?: number[],
returnGroup?: boolean
): Promise<Uint8Array[]>
}

export type CustomWalletClientConstructor = {
providerProxy: CustomProvider
metadata: Metadata
algosdk: typeof algosdk
algodClient: algosdk.Algodv2
network: Network
}
18 changes: 16 additions & 2 deletions src/clients/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,21 @@ import algosigner from './algosigner'
import walletconnect from './walletconnect2'
import kmd from './kmd'
import mnemonic from './mnemonic'
import { CustomProvider } from './custom/types'
import custom from './custom'

export { pera, myalgo, defly, exodus, algosigner, walletconnect, kmd, mnemonic }
export {
pera,
myalgo,
defly,
exodus,
algosigner,
walletconnect,
kmd,
mnemonic,
custom,
CustomProvider
}

export default {
[pera.metadata.id]: pera,
Expand All @@ -19,5 +32,6 @@ export default {
[algosigner.metadata.id]: algosigner,
[walletconnect.metadata.id]: walletconnect,
[kmd.metadata.id]: kmd,
[mnemonic.metadata.id]: mnemonic
[mnemonic.metadata.id]: mnemonic,
[custom.metadata.id]: custom
}
19 changes: 17 additions & 2 deletions src/components/Example/Example.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React from 'react'
import { DeflyWalletConnect } from '@blockshake/defly-connect'
import { DaffiWalletConnect } from '@daffiwallet/connect'
import { WalletProvider, PROVIDER_ID, useInitializeProviders } from '../../index'
import { WalletProvider, PROVIDER_ID, useInitializeProviders, Network } from '../../index'
import Account from './Account'
import Connect from './Connect'
import Transact from './Transact'
import algosdk from 'algosdk'
import { ManualGoalSigningAlertPromptProvider } from './TestManualProvider'

const getDynamicPeraWalletConnect = async () => {
const PeraWalletConnect = (await import('@perawallet/connect')).PeraWalletConnect
Expand All @@ -17,7 +19,20 @@ export default function ConnectWallet() {
{ id: PROVIDER_ID.DEFLY, clientStatic: DeflyWalletConnect },
{ id: PROVIDER_ID.PERA, getDynamicClient: getDynamicPeraWalletConnect },
{ id: PROVIDER_ID.DAFFI, clientStatic: DaffiWalletConnect },
{ id: PROVIDER_ID.EXODUS }
{ id: PROVIDER_ID.EXODUS },
{
id: PROVIDER_ID.CUSTOM,
clientOptions: {
name: 'Manual',
getProvider: (params: {
network?: Network
algod?: algosdk.Algodv2
algosdkStatic?: typeof algosdk
}) => {
return new ManualGoalSigningAlertPromptProvider(params.algosdkStatic ?? algosdk)
}
}
}
]
})

Expand Down
Loading