Skip to content

Commit

Permalink
Merge pull request #1 from klever-io/cardano-connect-test
Browse files Browse the repository at this point in the history
feat: ✨ Cardano base provider
  • Loading branch information
wilmacedo authored Feb 16, 2024
2 parents 8d40a05 + 502bc1b commit d69dc1f
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 14 deletions.
9 changes: 9 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## Task title

> Description
✨ Features:
- [ ]

🔨 Improvements:
- [ ]
8 changes: 8 additions & 0 deletions src/ada/available-wallets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// TODO: Add klever mobile app source name
export const availableWallets = ['nami'] as const

export type AvailableWallet = typeof availableWallets[number]

export enum CardanoWallet {
NAMI = 'nami',
}
30 changes: 30 additions & 0 deletions src/ada/connect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { NotInjectedError } from '@/errors';
import { NoAvailableAccountsError } from '@/errors/no-accounts-available-error';
import { NoProviderAvailableError } from '@/errors/no-provider-available-error';
import { web3Window } from '@/types';
import { availableWallets } from './available-wallets';
import type { CardanoUsedAddress } from './types';

export async function connect(wallet?: string): Promise<CardanoUsedAddress[]> {
if (!web3Window.cardano)
throw new NotInjectedError()

let injectedWallet = wallet
if (typeof injectedWallet === 'undefined') {
for (const availableWallet of availableWallets) {
if (web3Window.cardano[availableWallet])
injectedWallet = availableWallet
}
}

if (typeof injectedWallet === 'undefined')
throw new NoProviderAvailableError()

await web3Window.cardano[injectedWallet].enable()

const usedAddresses: CardanoUsedAddress[] = await web3Window.cardano.getUsedAddresses()
if (usedAddresses.length === 0)
throw new NoAvailableAccountsError()

return usedAddresses
}
35 changes: 35 additions & 0 deletions src/ada/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { ProviderEntity } from '@/entities/provider-entity';
import type { Account, Address, Balance } from '@/types';
import { CardanoWallet } from './available-wallets';
import { connect } from './connect';
import type { CardanoProviderProps } from './types';

export class CardanoProvider implements ProviderEntity {
wallet?: CardanoWallet

constructor({ wallet }: CardanoProviderProps) {
if (wallet)
this.wallet = CardanoWallet[wallet.toUpperCase() as keyof typeof CardanoWallet]
}

async connect(): Promise<Account[]> {
const accounts = await connect(this.wallet)

return accounts.map((account, index) => ({
name: `Account #${index}`,
address: account,
}))
}

async getBalance(address: Address): Promise<Balance> {
throw new Error('Not yet implemented.')
}

async signMessage(address: Address, message: string): Promise<string> {
throw new Error('Not yet implemented.')
}

signatureVerify(message: string, signature: string, address: string): boolean {
throw new Error('Not yet implemented.')
}
}
7 changes: 7 additions & 0 deletions src/ada/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { AvailableWallet } from './available-wallets';

export interface CardanoProviderProps {
wallet?: AvailableWallet
}

export type CardanoUsedAddress = string
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './ada/index'
export * from './networks'
export * from './substrate/dot/index'
export * from './substrate/ksm/index'
Expand Down
6 changes: 6 additions & 0 deletions src/networks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@ export const Networks = {
name: 'Kusama',
decimals: 12,
},
ada: {
id: 3,
name: 'Cardano',
decimals: 15, // TODO: Check this value
},
}

export enum Network {
POLKADOT = 'dot',
KUSAMA = 'ksm',
CARDANO = 'ada',
}

export type NetworkKey = keyof typeof Networks
Expand Down
5 changes: 3 additions & 2 deletions src/substrate/connect.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { NotInjectedError } from '@/errors';
import { NoAvailableAccountsError } from '@/errors/no-accounts-available-error';
import { NoProviderAvailableError } from '@/errors/no-provider-available-error';
import { web3Window } from '@/types';
import * as extension from '@polkadot/extension-dapp';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { connect } from './connect';
Expand All @@ -17,11 +18,11 @@ describe('Connect wallet use case', () => {
vi.resetAllMocks()

appName = 'Web3 Hub';
(window as any).injectedWeb3 = {}
web3Window.injectedWeb3 = {}
})

it('should be able to throw error when window dont have web3 object', async () => {
delete (window as any).injectedWeb3
delete web3Window.injectedWeb3

await expect(connect(appName)).rejects.toThrow(NotInjectedError)
})
Expand Down
3 changes: 2 additions & 1 deletion src/substrate/connect.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { NotInjectedError } from '@/errors';
import { NoAvailableAccountsError } from '@/errors/no-accounts-available-error';
import { NoProviderAvailableError } from '@/errors/no-provider-available-error';
import { web3Window } from '@/types';
import { web3Accounts, web3Enable } from '@polkadot/extension-dapp';
import type { SubstrateAccountWithMeta } from './types';

export async function connect(appName: string): Promise<SubstrateAccountWithMeta[]> {
if (!(window as any).injectedWeb3)
if (!web3Window.injectedWeb3)
throw new NotInjectedError()

const isInjected = await web3Enable(appName)
Expand Down
11 changes: 10 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { CardanoProviderProps } from './ada/types'
import type { NetworkKey } from './networks'
import type { SubstrateProviderProps } from './substrate/types'

export type Address = string
Expand All @@ -12,4 +14,11 @@ export interface Balance {
frozen: number | string
}

export type ProviderBuilderProps = SubstrateProviderProps
export type ProviderBuilderProps<T extends NetworkKey> = T extends 'dot' | 'ksm' ? SubstrateProviderProps : T extends 'ada' ? CardanoProviderProps : never;

export type Web3Window = {
injectedWeb3: any
cardano: any
} & Window & typeof globalThis

export const web3Window = (window as Web3Window)
24 changes: 14 additions & 10 deletions src/web3-provider.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { CardanoProvider } from './ada';
import type { CardanoProviderProps } from './ada/types';
import type { ProviderEntity } from './entities/provider-entity';
import { InvalidNetworkError } from './errors/invalid-network-error';
import type { NetworkData, NetworkKey } from './networks';
import { getNetworkKeyById, isValidNetwork } from './networks';
import { PolkadotProvider } from './substrate/dot';
import { KusamaProvider } from './substrate/ksm';
import type { SubstrateProviderProps } from './substrate/types';
import type { ProviderBuilderProps } from './types';

export class Web3Provider {
Expand All @@ -21,16 +24,17 @@ export class Web3Provider {
this.network = network
}

private getAvailableProviders() {
return {
dot: PolkadotProvider,
ksm: KusamaProvider,
// TODO: Improve props type to avoid generic for vanilla javascript users
build<T extends NetworkKey>(props: ProviderBuilderProps<T>): ProviderEntity {
switch (this.network) {
case 'dot':
return new PolkadotProvider(props as SubstrateProviderProps)
case 'ksm':
return new KusamaProvider(props as SubstrateProviderProps)
case 'ada':
return new CardanoProvider(props as CardanoProviderProps)
default:
throw new InvalidNetworkError()
}
}

build(props: ProviderBuilderProps): ProviderEntity {
const Provider = this.getAvailableProviders()[this.network]

return new Provider(props)
}
}

0 comments on commit d69dc1f

Please sign in to comment.