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

add Lute as Provider #129

Merged
merged 6 commits into from
Jan 16, 2024
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
35 changes: 31 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ npm install @blockshake/defly-connect @perawallet/connect @daffiwallet/connect

In the root of your app, initialize the `WalletProvider` with the `useInitializeProviders` hook.

This example initializes Defly, Pera, Daffi and Exodus wallet providers. The default node configuration (mainnet via [AlgoNode](https://algonode.io/api/)) is used. See [Provider Configuration](#provider-configuration) for more options.
This example initializes Defly, Pera, Daffi, Exodus, and Lute wallet providers. The default node configuration (mainnet via [AlgoNode](https://algonode.io/api/)) is used. See [Provider Configuration](#provider-configuration) for more options.

You can initialize your providers in two ways:

Expand All @@ -117,14 +117,20 @@ import { WalletProvider, useInitializeProviders, PROVIDER_ID } from '@txnlab/use
import { DeflyWalletConnect } from '@blockshake/defly-connect'
import { PeraWalletConnect } from '@perawallet/connect'
import { DaffiWalletConnect } from '@daffiwallet/connect'
import LuteConnect from 'lute-connect'

export default function App() {
const providers = useInitializeProviders({
providers: [
{ id: PROVIDER_ID.DEFLY, clientStatic: DeflyWalletConnect },
{ id: PROVIDER_ID.PERA, clientStatic: PeraWalletConnect },
{ id: PROVIDER_ID.DAFFI, clientStatic: DaffiWalletConnect },
{ id: PROVIDER_ID.EXODUS }
{ id: PROVIDER_ID.EXODUS },
{
id: PROVIDER_ID.LUTE,
clientStatic: LuteConnect,
clientOptions: { siteName: 'YourSiteName' }
}
]
})

Expand Down Expand Up @@ -157,13 +163,23 @@ const getDynamicDaffiWalletConnect = async () => {
return DaffiWalletConnect
}

const getDynamicLuteConnect = async () => {
const LuteConnect = (await import('lute-connect')).default
return LuteConnect
}

export default function App() {
const providers = useInitializeProviders({
providers: [
{ id: PROVIDER_ID.DEFLY, getDynamicClient: getDynamicDeflyWalletConnect },
{ id: PROVIDER_ID.PERA, getDynamicClient: getDynamicPeraWalletConnect },
{ id: PROVIDER_ID.DAFFI, getDynamicClient: getDynamicDaffiWalletConnect },
{ id: PROVIDER_ID.EXODUS }
{ id: PROVIDER_ID.EXODUS },
{
id: PROVIDER_ID.LUTE,
getDynamicClient: getDynamicLuteConnect,
clientOptions: { siteName: 'YourSiteName' }
}
]
})

Expand Down Expand Up @@ -477,6 +493,11 @@ useEffect(() => {
- Website - https://www.exodus.com/
- Download - https://www.exodus.com/download/

### Lute Wallet

- Website - https://lute.app/
- Install dependency - `npm install lute-connect`

### KMD (Algorand Key Management Daemon)

- Documentation - https://developer.algorand.org/docs/rest-apis/kmd
Expand Down Expand Up @@ -617,6 +638,7 @@ import { DeflyWalletConnect } from '@blockshake/defly-connect'
import { PeraWalletConnect } from '@perawallet/connect'
import { DaffiWalletConnect } from '@daffiwallet/connect'
import { WalletConnectModalSign } from '@walletconnect/modal-sign-html'
import LuteConnect from 'lute-connect'

export default function App() {
const providers = useInitializeProviders({
Expand All @@ -640,7 +662,12 @@ export default function App() {
}
}
},
{ id: PROVIDER_ID.EXODUS }
{ id: PROVIDER_ID.EXODUS },
{
id: PROVIDER_ID.LUTE,
clientStatic: LuteConnect,
clientOptions: { siteName: 'YourSiteName' }
}
],
nodeConfig: {
network: 'mainnet',
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"jest": "^29.1.2",
"jest-canvas-mock": "^2.5.0",
"jest-environment-jsdom": "^29.3.1",
"lute-connect": "^1.0.7",
"postcss": "^8.4.17",
"prettier": "2.8.8",
"react": "^18.2.0",
Expand Down
3 changes: 3 additions & 0 deletions src/clients/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import myalgo from './myalgo'
import defly from './defly'
import exodus from './exodus'
import algosigner from './algosigner'
import lute from './lute'
import walletconnect from './walletconnect2'
import kmd from './kmd'
import mnemonic from './mnemonic'
Expand All @@ -16,6 +17,7 @@ export {
defly,
exodus,
algosigner,
lute,
walletconnect,
kmd,
mnemonic,
Expand All @@ -30,6 +32,7 @@ export default {
[defly.metadata.id]: defly,
[exodus.metadata.id]: exodus,
[algosigner.metadata.id]: algosigner,
[lute.metadata.id]: lute,
[walletconnect.metadata.id]: walletconnect,
[kmd.metadata.id]: kmd,
[mnemonic.metadata.id]: mnemonic,
Expand Down
186 changes: 186 additions & 0 deletions src/clients/lute/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import { WalletTransaction } from 'lute-connect'
import Algod, { getAlgodClient } from '../../algod'
import { DEFAULT_NETWORK, PROVIDER_ID } from '../../constants'
import { DecodedSignedTransaction, DecodedTransaction, Network } from '../../types/node'
import type { InitParams } from '../../types/providers'
import { debugLog } from '../../utils/debugLog'
import BaseClient from '../base'
import { ICON } from './constants'
import type { LuteClientConstructor, LuteConnectOptions } from './types'
import type LuteConnect from 'lute-connect'

class LuteClient extends BaseClient {
#client: LuteConnect
clientOptions?: LuteConnectOptions
network: Network

constructor({
metadata,
client,
clientOptions,
algosdk,
algodClient,
network
}: LuteClientConstructor) {
super(metadata, algosdk, algodClient)
this.#client = client
this.clientOptions = clientOptions
this.network = network
this.metadata = LuteClient.metadata
}

static metadata = {
id: PROVIDER_ID.LUTE,
name: 'Lute',
icon: ICON,
isWalletConnect: false
}

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

let LuteConnect
if (clientStatic) {
LuteConnect = clientStatic
} else if (getDynamicClient) {
LuteConnect = await getDynamicClient()
} else {
throw new Error('Lute provider missing required property: clientStatic or getDynamicClient')
}

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

if (!clientOptions) {
throw new Error('Lute provider missing required property: clientOptions')
}
const lute = new LuteConnect(clientOptions.siteName)
const provider = new LuteClient({
metadata: LuteClient.metadata,
client: lute,
clientOptions,
algosdk: algosdk,
algodClient: algodClient,
network
})

debugLog(`${PROVIDER_ID.LUTE.toUpperCase()} initialized`, '✅')

return provider
} catch (e) {
console.error('Error initializing...', e)
return null
}
}

async connect() {
const genesis = (await this.algodClient.genesis().do()) as { network: string; id: string }
const genesisID = `${genesis.network}-${genesis.id}`
const addresses = await this.#client.connect(genesisID)

if (addresses.length === 0) {
throw new Error(`No accounts found for ${LuteClient.metadata.id}`)
}

const mappedAccounts = addresses.map((address: string, index: number) => ({
name: `Lute Wallet ${index + 1}`,
address,
providerId: LuteClient.metadata.id
}))

return {
...LuteClient.metadata,
accounts: mappedAccounts
}
}

// eslint-disable-next-line @typescript-eslint/require-await
async reconnect() {
return null
}

// eslint-disable-next-line @typescript-eslint/require-await
async disconnect() {
return
}

shouldSignTxnObject(
txn: DecodedTransaction | DecodedSignedTransaction,
addresses: string[],
indexesToSign: number[] | undefined,
idx: number
): boolean {
const isIndexMatch = !indexesToSign || indexesToSign.includes(idx)
const isSigned = 'txn' in txn
const canSign = !isSigned && addresses.includes(this.algosdk.encodeAddress(txn.snd))
const shouldSign = isIndexMatch && canSign

return shouldSign
}
acfunk marked this conversation as resolved.
Show resolved Hide resolved

async signTransactions(
connectedAccounts: string[],
transactions: Uint8Array[],
indexesToSign?: number[],
returnGroup = true
) {
// Decode the transactions to access their properties.
const decodedTxns = transactions.map((txn) => {
return this.algosdk.decodeObj(txn)
}) as Array<DecodedTransaction | DecodedSignedTransaction>

const signedIndexes: number[] = []

// Marshal the transactions,
// and add the signers property if they shouldn't be signed.
const txnsToSign = decodedTxns.reduce<WalletTransaction[]>((acc, txn, idx) => {
const isSigned = 'txn' in txn
const shouldSign = this.shouldSignTxnObject(txn, connectedAccounts, indexesToSign, idx)

if (shouldSign) {
signedIndexes.push(idx)
acc.push({
txn: Buffer.from(transactions[idx]).toString('base64')
})
} else {
acc.push({
txn: isSigned
? Buffer.from(
this.algosdk.decodeSignedTransaction(transactions[idx]).txn.toByte()
).toString('base64')
: Buffer.from(transactions[idx]).toString('base64'),
stxn: isSigned ? Buffer.from(transactions[idx]).toString('base64') : undefined,
signers: []
})
}

return acc
}, [])

// Sign them with the client.
const result = (await this.#client.signTxns(txnsToSign)) as (Uint8Array | null)[]

const signedTxns = transactions.reduce<Uint8Array[]>((acc, txn, i) => {
if (signedIndexes.includes(i)) {
const signedByUser = result.shift()
signedByUser && acc.push(signedByUser)
} else if (returnGroup) {
acc.push(txn)
}

return acc
}, [])

return signedTxns
}
}

export default LuteClient
3 changes: 3 additions & 0 deletions src/clients/lute/constants.ts

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions src/clients/lute/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import lute from './client'

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

export type LuteConnectOptions = {
siteName: string
}

export type LuteClientConstructor = {
metadata: Metadata
client: LuteConnect
clientOptions?: LuteConnectOptions
algosdk: typeof algosdk
algodClient: algosdk.Algodv2
network: Network
}
3 changes: 2 additions & 1 deletion src/components/Example/Example.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ jest.mock('../../index', () => ({
DEFLY: 'mock_defly_id',
PERA: 'mock_pera_id',
DAFFI: 'mock_daffi_id',
EXODUS: 'mock_exodus_id'
EXODUS: 'mock_exodus_id',
LUTE: 'mock_lute_id'
},
useInitializeProviders: jest.fn()
}))
Expand Down
2 changes: 2 additions & 0 deletions src/components/Example/Example.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react'
import { DeflyWalletConnect } from '@blockshake/defly-connect'
import { DaffiWalletConnect } from '@daffiwallet/connect'
import LuteConnect from 'lute-connect'
import { WalletProvider, PROVIDER_ID, useInitializeProviders, Network } from '../../index'
import Account from './Account'
import Connect from './Connect'
Expand All @@ -20,6 +21,7 @@ export default function ConnectWallet() {
{ id: PROVIDER_ID.PERA, getDynamicClient: getDynamicPeraWalletConnect },
{ id: PROVIDER_ID.DAFFI, clientStatic: DaffiWalletConnect },
{ id: PROVIDER_ID.EXODUS },
{ id: PROVIDER_ID.LUTE, clientStatic: LuteConnect, clientOptions: { siteName: 'Storybook' } },
{
id: PROVIDER_ID.CUSTOM,
clientOptions: {
Expand Down
1 change: 1 addition & 0 deletions src/constants/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export enum PROVIDER_ID {
CUSTOM = 'custom',
PERA = 'pera',
DAFFI = 'daffi',
LUTE = 'lute',
MYALGO = 'myalgo',
ALGOSIGNER = 'algosigner',
DEFLY = 'defly',
Expand Down
Loading