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

refactor(wallet-rpc)!: rewrite to TypeScript #1548

Merged
merged 15 commits into from
Jun 7, 2022
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
95 changes: 44 additions & 51 deletions docs/guides/build-wallet.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const accounts = [
MemoryAccount({ keypair: generateKeyPair() }), // generate keypair for account1
MemoryAccount({ keypair: generateKeyPair() }) // generate keypair for account2
]
const aeppInfo = {}

async function init () {
// Init extension stamp from sdk
Expand All @@ -63,42 +64,37 @@ async function init () {
// The `ExtensionProvider` uses the first account by default. You can change active account using `selectAccount(address)` function
accounts,
// Hook for sdk registration
onConnection (aepp, action) {
if (confirm(`Aepp ${aepp.info.name} with id ${aepp.id} wants to connect`)) {
action.accept()
} else {
action.deny()
onConnection (aeppId, params) {
if (!confirm(`Aepp ${params.name} with id ${aeppId} wants to connect`)) {
throw new RpcConnectionDenyError()
}
aeppInfo[aeppId] = params
},
onDisconnect (msg, client) {
console.log('Disconnect client: ', client)
},
onSubscription (aepp, action) {
if (confirm(`Aepp ${aepp.info.name} with id ${aepp.id} wants to subscribe for accounts`)) {
action.accept()
} else {
action.deny()
onSubscription (aeppId) {
const { name } = aeppInfo[aeppId]
if (!confirm(`Aepp ${name} with id ${aeppId} wants to subscribe for accounts`)) {
throw new RpcRejectedByUserError()
}
},
onSign (aepp, action) {
if (confirm(`Aepp ${aepp.info.name} with id ${aepp.id} wants to sign tx ${action.params.tx}`)) {
action.accept()
} else {
action.deny()
onSign (aeppId, params) {
const { name } = aeppInfo[aeppId]
if (!confirm(`Aepp ${name} with id ${aeppId} wants to sign tx ${params.tx}`)) {
throw new RpcRejectedByUserError()
}
},
onAskAccounts (aepp, action) {
if (confirm(`Aepp ${aepp.info.name} with id ${aepp.id} wants to get accounts`)) {
action.accept()
} else {
action.deny()
onAskAccounts (aeppId) {
const { name } = aeppInfo[aeppId]
if (!confirm(`Aepp ${name} with id ${aeppId} wants to get accounts`)) {
throw new RpcRejectedByUserError()
}
},
onMessageSign (aepp, action) {
if (confirm(`Aepp ${aepp.info.name} with id ${aepp.id} wants to sign msg ${action.params.message}`)) {
action.accept()
} else {
action.deny()
onMessageSign (aeppId, params) {
const { name } = aeppInfo[aeppId]
if (!confirm(`Aepp ${name} with id ${aeppId} wants to sign msg ${params.message}`)) {
throw new RpcRejectedByUserError()
}
}
}).then(wallet => {
Expand Down Expand Up @@ -142,46 +138,43 @@ async function init () {
// The `ExtensionProvider` uses the first account by default. You can change active account using `selectAccount(address)` function
accounts,
// Hook for sdk registration
onConnection (aepp, action) {
if (confirm(`Aepp ${aepp.info.name} with id ${aepp.id} wants to connect`)) {
onConnection (aeppId, params, origin) {
if (confirm(`Aepp ${params.name} with id ${aeppId} wants to connect`)) {
// Whitelist aepp domains for node connection
const aepps = ['https://test', 'https://aepp.aeternity.com']
if (aepp.info.connectNode && aepps.includes(aepp.info.origin)) action.accept()
else action.deny()
action.accept() // Accept wallet connection without sharing node URLs
if (params.connectNode && aepps.includes(origin)) {}
else throw new RpcConnectionDenyError()
// Connect to aepp without sharing node URLs
} else {
action.deny()
throw new RpcConnectionDenyError()
}
aeppInfo[aeppId] = params
},
onDisconnect (msg, client) {
console.log('Disconnect client: ', client)
},
onSubscription (aepp, action) {
if (confirm(`Aepp ${aepp.info.name} with id ${aepp.id} wants to subscribe for accounts`)) {
action.accept()
} else {
action.deny()
onSubscription (aeppId) {
const { name } = aeppInfo[aeppId]
if (!confirm(`Aepp ${name} with id ${aeppId} wants to subscribe for accounts`)) {
throw new RpcRejectedByUserError()
}
},
onSign (aepp, action) {
if (confirm(`Aepp ${aepp.info.name} with id ${aepp.id} wants to sign tx ${action.params.tx}`)) {
action.accept()
} else {
action.deny()
onSign (aeppId, params) {
const { name } = aeppInfo[aeppId]
if (!confirm(`Aepp ${name} with id ${aeppId} wants to sign tx ${params.tx}`)) {
throw new RpcRejectedByUserError()
}
},
onAskAccounts (aepp, action) {
if (confirm(`Aepp ${aepp.info.name} with id ${aepp.id} wants to get accounts`)) {
action.accept()
} else {
action.deny()
onAskAccounts (aeppId) {
const { name } = aeppInfo[aeppId]
if (!confirm(`Aepp ${name} with id ${aeppId} wants to get accounts`)) {
throw new RpcRejectedByUserError()
}
},
onMessageSign (aepp, action) {
if (confirm(`Aepp ${aepp.info.name} with id ${aepp.id} wants to sign msg ${action.params.message}`)) {
action.accept()
} else {
action.deny()
onMessageSign (aeppId, params) {
const { name } = aeppInfo[aeppId]
if (!confirm(`Aepp ${name} with id ${aeppId} wants to sign msg ${params.message}`)) {
throw new RpcRejectedByUserError()
}
}
}).then(wallet => {
Expand Down
18 changes: 13 additions & 5 deletions examples/browser/wallet-iframe/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
<script>
import {
MemoryAccount, RpcWallet, Node, generateKeyPair,
BrowserWindowMessageConnection, METHODS, WALLET_TYPE
BrowserWindowMessageConnection, METHODS, WALLET_TYPE,
RpcConnectionDenyError, RpcRejectedByUserError
} from '@aeternity/aepp-sdk'
import Value from './Value'

Expand Down Expand Up @@ -71,9 +72,11 @@ export default {
}
},
async mounted () {
const genConfirmCallback = getActionName => (aepp, { accept, deny, params }) => {
if (confirm(`Client ${aepp.info.name} with id ${aepp.id} want to ${getActionName(params)}`)) accept()
else deny()
const aeppInfo = {}
const genConfirmCallback = (getActionName) => (aeppId, params) => {
if (!confirm(`Client ${aeppInfo[aeppId].name} with id ${aeppId} want to ${getActionName(params)}`)) {
throw new RpcRejectedByUserError()
}
}
this.aeSdk = await RpcWallet({
id: window.origin,
Expand All @@ -93,7 +96,12 @@ export default {
MemoryAccount({ keypair: generateKeyPair() })
],
name: 'Wallet Iframe',
onConnection: genConfirmCallback(() => 'connect'),
onConnection: (aeppId, params) => {
if (!confirm(`Client ${params.name} with id ${aeppId} want to connect`)) {
throw new RpcConnectionDenyError()
}
aeppInfo[aeppId] = params
},
onSubscription: genConfirmCallback(() => 'subscription'),
onSign: genConfirmCallback(({ returnSigned, tx }) => `${returnSigned ? 'sign' : 'sign and broadcast'} ${JSON.stringify(tx)}`),
onMessageSign: genConfirmCallback(() => 'message sign'),
Expand Down
50 changes: 24 additions & 26 deletions examples/browser/wallet-web-extension/src/background.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import browser from 'webextension-polyfill'
import {
RpcWallet, Node, MemoryAccount, generateKeyPair, BrowserRuntimeConnection, WALLET_TYPE
RpcWallet, Node, MemoryAccount, generateKeyPair, BrowserRuntimeConnection, WALLET_TYPE,
RpcConnectionDenyError, RpcRejectedByUserError
} from '@aeternity/aepp-sdk'

(async () => {
const aeppInfo = {}

const aeSdk = await RpcWallet({
compilerUrl: 'https://compiler.aepps.com',
nodes: [{
Expand All @@ -25,42 +28,37 @@ import {
MemoryAccount({ keypair: generateKeyPair() })
],
// Hook for sdk registration
onConnection (aepp, action) {
if (confirm(`Aepp ${aepp.info.name} with id ${aepp.id} wants to connect`)) {
action.accept()
} else {
action.deny()
onConnection (aeppId, params) {
if (!confirm(`Aepp ${params.name} with id ${aeppId} wants to connect`)) {
throw new RpcConnectionDenyError()
}
aeppInfo[aeppId] = params
},
onDisconnect (msg, client) {
console.log('Client disconnected:', client)
},
onSubscription (aepp, action) {
if (confirm(`Aepp ${aepp.info.name} with id ${aepp.id} wants to subscribe for accounts`)) {
action.accept()
} else {
action.deny()
onSubscription (aeppId) {
const { name } = aeppInfo[aeppId]
if (!confirm(`Aepp ${name} with id ${aeppId} wants to subscribe for accounts`)) {
throw new RpcRejectedByUserError()
}
},
onSign (aepp, action) {
if (confirm(`Aepp ${aepp.info.name} with id ${aepp.id} wants to sign tx ${action.params.tx}`)) {
action.accept()
} else {
action.deny()
onSign (aeppId, params) {
const { name } = aeppInfo[aeppId]
if (!confirm(`Aepp ${name} with id ${aeppId} wants to sign tx ${params.tx}`)) {
throw new RpcRejectedByUserError()
}
},
onAskAccounts (aepp, action) {
if (confirm(`Aepp ${aepp.info.name} with id ${aepp.id} wants to get accounts`)) {
action.accept()
} else {
action.deny()
onAskAccounts (aeppId) {
const { name } = aeppInfo[aeppId]
if (!confirm(`Aepp ${name} with id ${aeppId} wants to get accounts`)) {
throw new RpcRejectedByUserError()
}
},
onMessageSign (aepp, action) {
if (confirm(`Aepp ${aepp.info.name} with id ${aepp.id} wants to sign msg ${action.params.message}`)) {
action.accept()
} else {
action.deny()
onMessageSign (aeppId, params) {
const { name } = aeppInfo[aeppId]
if (!confirm(`Aepp ${name} with id ${aeppId} wants to sign msg ${params.message}`)) {
throw new RpcRejectedByUserError()
}
}
})
Expand Down
8 changes: 4 additions & 4 deletions src/account/multiple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ import AccountResolver, { _AccountResolver, Account } from './resolver'
import { UnavailableAccountError } from '../utils/errors'
import type stampit from '@stamp/it' // eslint-disable-line @typescript-eslint/no-unused-vars

class _AccountMultiple extends _AccountResolver {
export class _AccountMultiple extends _AccountResolver {
accounts: { [key: EncodedData<'ak'>]: _AccountBase }
selectedAddress?: string
selectedAddress?: EncodedData<'ak'>

async init (
{ accounts = [], address }: { accounts: _AccountBase[], address: EncodedData<'ak'> } & Parameters<_AccountBase['init']>[0]
Expand Down Expand Up @@ -60,8 +60,8 @@ class _AccountMultiple extends _AccountResolver {
* @return {String[]}
* @example addresses()
*/
addresses (): string[] {
return Object.keys(this.accounts)
addresses (): Array<EncodedData<'ak'>> {
return Object.keys(this.accounts) as Array<EncodedData<'ak'>>
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/node-pool/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ interface NodePool {
readonly api: Node['api']
}

class _NodePool implements NodePool {
export class _NodePool implements NodePool {
readonly api: Node['api']
pool: Map<string, Node>
selectedNode?: Node
Expand Down Expand Up @@ -159,7 +159,7 @@ export default stampit<NodePool>({
selectNode: _NodePool.prototype.selectNode,
getNodeInfo: _NodePool.prototype.getNodeInfo,
isNodeConnected: _NodePool.prototype.isNodeConnected,
getNetworkId: _NodePool.prototype.getNetworkId,
getNetworkId,
getNodesInPool: _NodePool.prototype.getNodesInPool
}
})
11 changes: 7 additions & 4 deletions src/utils/aepp-wallet-communication/rpc/RpcClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ interface JsonRpcResponse {
}
}

type RpcApiHandler = (p?: any, origin?: string) => any | undefined
type RpcApi<Keys> = { [k in keyof Keys]: RpcApiHandler }
type RpcApiHandler = (p?: any) => any | undefined
type RpcApi<Api> = { [k in keyof Api]: RpcApiHandler }
type WithOrigin<Api extends RpcApi<Api>> = {
[k in keyof Api]: (p: Parameters<Api[k]>[0], origin: string) => ReturnType<Api[k]>
}

/**
* Contain functionality for using RPC conection
Expand All @@ -36,12 +39,12 @@ export default class RpcClient <
connection: BrowserConnection
#callbacks = new Map<number, { resolve: (v: any) => void, reject: (e: Error) => void }>()
#messageId = 0
#methods: LocalApi
#methods: WithOrigin<LocalApi>

constructor (
connection: BrowserConnection,
onDisconnect: () => void,
methods: LocalApi
methods: WithOrigin<LocalApi>
) {
this.connection = connection
this.#methods = methods
Expand Down
14 changes: 7 additions & 7 deletions src/utils/aepp-wallet-communication/rpc/aepp-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import AccountRpc from '../../../account/rpc'
import { decode, EncodedData } from '../../encoder'
import { Accounts, WalletInfo, Network, WalletApi, AeppApi } from './types'
import RpcClient from './RpcClient'
import { METHODS, VERSION } from '../schema'
import { METHODS, VERSION, SUBSCRIPTION_TYPES } from '../schema'
import {
AlreadyConnectedError,
NoWalletConnectedError,
Expand Down Expand Up @@ -108,16 +108,16 @@ abstract class _AeppRpc extends _AccountResolver {
delete this._accounts
this.onDisconnect(disconnectParams)
}, {
[METHODS.updateAddress]: (params: Accounts) => {
[METHODS.updateAddress]: (params) => {
this._accounts = params
this.onAddressChange(params)
},
[METHODS.updateNetwork]: async (params: Network) => {
[METHODS.updateNetwork]: async (params) => {
const { node } = params
if (node != null) this.addNode(node.name, await Node(node), true)
this.onNetworkChange(params)
},
[METHODS.closeConnection]: (params: any) => {
[METHODS.closeConnection]: (params) => {
disconnectParams = params
client.connection.disconnect()
},
Expand Down Expand Up @@ -155,12 +155,12 @@ abstract class _AeppRpc extends _AccountResolver {

/**
* Subscribe for addresses from wallet
* @param type Should be one of 'current' (the selected account), 'connected' (all)
* @param value Subscription action
* @param type Subscription type
* @param value Should be one of 'current' (the selected account), 'connected' (all)
* @return Accounts from wallet
*/
async subscribeAddress (
type: 'current' | 'connected', value: 'subscribe' | 'unsubscribe'
type: SUBSCRIPTION_TYPES, value: 'current' | 'connected'
): Promise<ReturnType<WalletApi[METHODS.subscribeAddress]>> {
this._ensureConnected()
const result = await this.rpcClient.request(METHODS.subscribeAddress, { type, value })
Expand Down
Loading