forked from airgap-it/airgap-wallet
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
17 changed files
with
2,845 additions
and
625 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,40 +1,63 @@ | ||
import { Component } from '@angular/core' | ||
import { assertNever } from '@airgap/angular-core' | ||
import { Component, OnInit } from '@angular/core' | ||
import { ModalController } from '@ionic/angular' | ||
import WalletConnect from '@walletconnect/client' | ||
import BigNumber from 'bignumber.js' | ||
import { ErrorCategory, handleErrorSentry } from 'src/app/services/sentry-error-handler/sentry-error-handler' | ||
import { WalletconnectV1Handler, WalletconnectV1HandlerContext } from './handler/walletconnect-v1.handler' | ||
import { WalletconnectV2Handler, WalletconnectV2HandlerContext } from './handler/walletconnect-v2.handler' | ||
import { WalletconnectHandler } from './handler/walletconnect.handler' | ||
|
||
export interface WalletconnectV1Context extends WalletconnectV1HandlerContext { | ||
version: 1 | ||
} | ||
|
||
export interface WalletconnectV2Context extends WalletconnectV2HandlerContext { | ||
version: 2 | ||
} | ||
|
||
export type WalletconnectContext = WalletconnectV1Context | WalletconnectV2Context | ||
|
||
@Component({ | ||
selector: 'app-dapp-confirm', | ||
templateUrl: './dapp-confirm.page.html', | ||
styleUrls: ['./dapp-confirm.page.scss'] | ||
}) | ||
export class DappConfirmPage { | ||
private readonly connector: WalletConnect | undefined | ||
public id: string | undefined | ||
public result: string | undefined | ||
export class DappConfirmPage implements OnInit { | ||
public context: WalletconnectContext | ||
public result: string | ||
|
||
private readonly handlers = { | ||
v1: new WalletconnectV1Handler(), | ||
v2: new WalletconnectV2Handler() | ||
} | ||
|
||
public constructor(private readonly modalController: ModalController) {} | ||
|
||
public async ngOnInit(): Promise<void> { | ||
this.result = await this.getHandler().readResult(this.context) | ||
} | ||
|
||
public async approveRequest() { | ||
this.connector.approveRequest({ | ||
id: new BigNumber(this.id).toNumber(), | ||
result: this.result | ||
}) | ||
await this.getHandler().approveRequest(this.context) | ||
this.dismissModal() | ||
} | ||
|
||
public async rejectRequest() { | ||
this.connector.rejectRequest({ | ||
id: new BigNumber(this.id).toNumber(), | ||
error: { | ||
message: 'USER_REJECTION' // optional | ||
} | ||
}) | ||
await this.getHandler().rejectRequest(this.context) | ||
this.dismissModal() | ||
} | ||
|
||
public async dismissModal(): Promise<void> { | ||
this.modalController.dismiss().catch(handleErrorSentry(ErrorCategory.NAVIGATION)) | ||
} | ||
|
||
private getHandler(): WalletconnectHandler<any> { | ||
switch (this.context.version) { | ||
case 1: | ||
return this.handlers['v1'] | ||
case 2: | ||
return this.handlers['v2'] | ||
default: | ||
assertNever('context', this.context) | ||
} | ||
} | ||
} |
31 changes: 31 additions & 0 deletions
31
src/app/pages/dapp-confirm/handler/walletconnect-v1.handler.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import WalletConnect from '@walletconnect/client' | ||
|
||
import { WalletconnectHandler } from './walletconnect.handler' | ||
|
||
export interface WalletconnectV1HandlerContext { | ||
id: number | ||
result: string | ||
connector: WalletConnect | ||
} | ||
|
||
export class WalletconnectV1Handler implements WalletconnectHandler<WalletconnectV1HandlerContext> { | ||
public async readResult(context: WalletconnectV1HandlerContext): Promise<string> { | ||
return context.result | ||
} | ||
|
||
public async approveRequest(context: WalletconnectV1HandlerContext): Promise<void> { | ||
context.connector.approveRequest({ | ||
id: context.id, | ||
result: context.result | ||
}) | ||
} | ||
|
||
public async rejectRequest(context: WalletconnectV1HandlerContext): Promise<void> { | ||
context.connector.rejectRequest({ | ||
id: context.id, | ||
error: { | ||
message: 'USER_REJECTION' | ||
} | ||
}) | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
src/app/pages/dapp-confirm/handler/walletconnect-v2.handler.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import V2Client from '@walletconnect/web3wallet' | ||
import { getSdkError } from '@walletconnect/utils' | ||
|
||
import { WalletconnectHandler } from './walletconnect.handler' | ||
|
||
export interface WalletconnectV2HandlerContext { | ||
id: number | ||
topic: string | ||
result: string | ||
client: V2Client | ||
} | ||
|
||
export class WalletconnectV2Handler implements WalletconnectHandler<WalletconnectV2HandlerContext> { | ||
public async readResult(context: WalletconnectV2HandlerContext): Promise<string> { | ||
return context.result | ||
} | ||
|
||
public async approveRequest(context: WalletconnectV2HandlerContext): Promise<void> { | ||
context.client.respondSessionRequest({ | ||
topic: context.topic, | ||
response: { | ||
id: context.id, | ||
jsonrpc: '2.0', | ||
result: context.result | ||
} | ||
}) | ||
} | ||
|
||
public async rejectRequest(context: WalletconnectV2HandlerContext): Promise<void> { | ||
context.client.respondSessionRequest({ | ||
topic: context.topic, | ||
response: { | ||
id: context.id, | ||
jsonrpc: '2.0', | ||
error: getSdkError('USER_REJECTED') | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export interface WalletconnectHandler<C> { | ||
readResult(context: C): Promise<string> | ||
approveRequest(context: C): Promise<void> | ||
rejectRequest(context: C): Promise<void> | ||
} |
150 changes: 150 additions & 0 deletions
150
src/app/pages/walletconnect/handler/walletconnect-v1.handler.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
import WalletConnect from '@walletconnect/client' | ||
import { saveWalletConnectV1Session } from 'src/app/services/walletconnect/helpers' | ||
import { | ||
JSONRPC as _JSONRPC, | ||
EthMethods, | ||
WalletconnectMessage, | ||
Namespace, | ||
SessionRequest, | ||
EthTx, | ||
SwitchEthereumChain | ||
} from '../walletconnect.types' | ||
|
||
import { WalletconnectHandler } from './walletconnect.handler' | ||
|
||
enum Methods { | ||
SESSION_REQUEST = 'session_request' | ||
} | ||
|
||
type JSONRPC<T = unknown> = _JSONRPC<T, EthMethods | Methods> | ||
|
||
export interface WalletconnectV1HandlerContext { | ||
request: JSONRPC | ||
connector: WalletConnect | ||
} | ||
|
||
function rejectRequest(connector: WalletConnect, request: JSONRPC) { | ||
connector.rejectRequest({ | ||
id: request.id, | ||
error: { | ||
message: 'USER_REJECTION' // optional | ||
} | ||
}) | ||
} | ||
|
||
export class WalletconnectV1Handler implements WalletconnectHandler<WalletconnectV1HandlerContext> { | ||
public async readMessage(context: WalletconnectV1HandlerContext): Promise<WalletconnectMessage> { | ||
switch (context.request.method) { | ||
case Methods.SESSION_REQUEST: | ||
return this.readSessionRequest(context.request as JSONRPC<SessionRequest>, context.connector) | ||
case EthMethods.ETH_SENDTRANSACTION: | ||
return this.readEthSendTransaction(context.request as JSONRPC<EthTx>, context.connector) | ||
case EthMethods.PERSONAL_SIGN_REQUEST: | ||
return this.readPersonalSign(context.request as JSONRPC<string>, context.connector) | ||
case EthMethods.WALLET_SWITCH_ETHEREUM_CHAIN: | ||
return this.readWalletSwitchEthereumChain(context.request as JSONRPC<SwitchEthereumChain>, context.connector) | ||
case EthMethods.ETH_SIGN_TYPED_DATA: | ||
return { type: 'unsupported', namespace: Namespace.ETH, method: EthMethods.ETH_SIGN_TYPED_DATA } | ||
default: | ||
return { type: 'unsupported', namespace: Namespace.ETH } | ||
} | ||
} | ||
|
||
private readSessionRequest(request: JSONRPC<SessionRequest>, connector: WalletConnect): WalletconnectMessage { | ||
const chains: string[] | undefined = | ||
request.params[0].chainId !== undefined ? [`${Namespace.ETH}:${request.params[0].chainId}`] : undefined | ||
|
||
return { | ||
type: 'permissionRequest', | ||
chains, | ||
dAppMetadata: { | ||
name: request.params[0].peerMeta.name, | ||
description: request.params[0].peerMeta.description, | ||
url: request.params[0].peerMeta.url, | ||
icon: request.params[0].peerMeta.icons ? request.params[0].peerMeta.icons[0] : undefined | ||
}, | ||
canOverrideChain: true, | ||
approve: async (accounts: string[]): Promise<void> => { | ||
const account = accounts[0] | ||
if (account === undefined || !(account.startsWith(Namespace.ETH) || account.startsWith('::'))) { | ||
return | ||
} | ||
|
||
const [_namespace, chainId, address] = account.split(':') | ||
const ethChainId = parseInt(chainId, 10) | ||
|
||
connector.approveSession({ | ||
chainId: !isNaN(ethChainId) ? ethChainId : 1, | ||
accounts: [address] | ||
}) | ||
await saveWalletConnectV1Session(connector.peerId, connector.session) | ||
}, | ||
reject: async (): Promise<void> => { | ||
rejectRequest(connector, request) | ||
} | ||
} | ||
} | ||
|
||
private readEthSendTransaction(request: JSONRPC<EthTx>, connector: WalletConnect): WalletconnectMessage { | ||
return { | ||
type: 'signRequest', | ||
version: 1, | ||
namespace: Namespace.ETH, | ||
chain: `${Namespace.ETH}:${connector.chainId}`, | ||
request: { | ||
id: request.id.toString(), | ||
method: EthMethods.ETH_SENDTRANSACTION, | ||
params: request.params | ||
}, | ||
cancel: async (): Promise<void> => { | ||
rejectRequest(connector, request) | ||
} | ||
} | ||
} | ||
|
||
private readPersonalSign(request: JSONRPC<string>, connector: WalletConnect): WalletconnectMessage { | ||
return { | ||
type: 'signRequest', | ||
version: 1, | ||
namespace: Namespace.ETH, | ||
chain: `${Namespace.ETH}:${connector.chainId}`, | ||
request: { | ||
id: request.id.toString(), | ||
method: EthMethods.PERSONAL_SIGN_REQUEST, | ||
params: request.params | ||
}, | ||
cancel: async (): Promise<void> => { | ||
rejectRequest(connector, request) | ||
} | ||
} | ||
} | ||
|
||
private readWalletSwitchEthereumChain(request: JSONRPC<SwitchEthereumChain>, connector: WalletConnect): WalletconnectMessage { | ||
return { | ||
type: 'switchAccountRequest', | ||
namespace: Namespace.ETH, | ||
account: connector.accounts[0], | ||
dAppMetadata: { | ||
name: connector.peerMeta.name, | ||
description: connector.peerMeta.description, | ||
url: connector.peerMeta.url, | ||
icon: connector.peerMeta.icons ? connector.peerMeta.icons[0] : undefined | ||
}, | ||
request: { | ||
id: request.id.toString(), | ||
method: EthMethods.WALLET_SWITCH_ETHEREUM_CHAIN, | ||
params: request.params | ||
}, | ||
respond: async (chainId: number): Promise<void> => { | ||
connector.updateSession({ | ||
chainId, | ||
accounts: connector.accounts | ||
}) | ||
await saveWalletConnectV1Session(connector.peerId, connector.session) | ||
}, | ||
cancel: async (): Promise<void> => { | ||
rejectRequest(connector, request) | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.