Skip to content

Commit

Permalink
refactor(aepp-wallet)!: rewrite to ts all except rpc
Browse files Browse the repository at this point in the history
BREAKING CHANGE: BrowserRuntimeConnection, BrowserWindowMessageConnection are classes
Create instances using new.

BREAKING CHANGE: ContentScriptBridge, WalletDetector rewrited to plain functions
Use `connectionProxy`, `walletDetector` respectively
  • Loading branch information
davidyuk committed May 24, 2022
1 parent a54fdfd commit 19cb42f
Show file tree
Hide file tree
Showing 21 changed files with 386 additions and 609 deletions.
18 changes: 8 additions & 10 deletions docs/guides/build-wallet.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,22 @@ First you need to create a bridge between your extension and the page. This can
```js
import browser from 'webextension-polyfill'
import {
BrowserRuntimeConnection, BrowserWindowMessageConnection, MESSAGE_DIRECTION,
ContentScriptBridge
BrowserRuntimeConnection, BrowserWindowMessageConnection, MESSAGE_DIRECTION, connectionProxy
} from '@aeternity/aepp-sdk'

const readyStateCheckInterval = setInterval(function () {
if (document.readyState === 'complete') {
clearInterval(readyStateCheckInterval)

const port = browser.runtime.connect()
const extConnection = BrowserRuntimeConnection({ port })
const pageConnection = BrowserWindowMessageConnection({
const extConnection = new BrowserRuntimeConnection({ port })
const pageConnection = new BrowserWindowMessageConnection({
target: window,
origin: window.origin,
sendDirection: MESSAGE_DIRECTION.to_aepp,
receiveDirection: MESSAGE_DIRECTION.to_waellet
})

const bridge = ContentScriptBridge({ pageConnection, extConnection })
bridge.run()
connectionProxy(pageConnection, extConnection)
}
}, 10)
```
Expand Down Expand Up @@ -106,7 +104,7 @@ async function init () {
}).then(wallet => {
chrome.runtime.onConnect.addListener(async function (port) {
// create connection
const connection = await BrowserRuntimeConnection({ port })
const connection = new BrowserRuntimeConnection({ port })
// add new aepp to wallet
wallet.addRpcClient(connection)
// share wallet details
Expand Down Expand Up @@ -190,7 +188,7 @@ async function init () {
}).then(wallet => {
chrome.runtime.onConnect.addListener(async function (port) {
// create connection
const connection = await BrowserRuntimeConnection({ port })
const connection = new BrowserRuntimeConnection({ port })
// add new aepp to wallet
wallet.addRpcClient(connection)
// share wallet details
Expand All @@ -206,7 +204,7 @@ init().then(_ => console.log('Wallet initialized!'))
```

## iFrame-based Wallet
The **iFrame-based** approach works similar to the **WebExtension** approach except that the `ContentScriptBridge` in between isn't needed.
The **iFrame-based** approach works similar to the **WebExtension** approach except that the `connectionProxy` in between isn't needed.

You can take a look into the implementation of the following example to see how it works:

Expand Down
11 changes: 4 additions & 7 deletions docs/guides/connect-aepp-to-wallet.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ You can build your own wallet in the next example
## 1. Specify imports and constants and state

```js
import { RpcAepp, WalletDetector, BrowserWindowMessageConnection, Node } from '@aeternity/aepp-sdk'
import { RpcAepp, walletDetector, BrowserWindowMessageConnection, Node } from '@aeternity/aepp-sdk'

const TESTNET_NODE_URL = 'https://testnet.aeternity.io'
const MAINNET_NODE_URL = 'https://mainnet.aeternity.io'
Expand Down Expand Up @@ -62,16 +62,13 @@ methods: {
const handleWallets = async function ({ wallets, newWallet }) {
newWallet = newWallet || Object.values(wallets)[0]
if (confirm(`Do you want to connect to wallet ${newWallet.name}`)) {
detector.stopScan()
stopScan()
// connect to the wallet, see step 4.
await this.connect(newWallet)
}
}

const scannerConnection = BrowserWindowMessageConnection()

const detector = await WalletDetector({ connection: scannerConnection })
detector.scan(handleWallets.bind(this))
const scannerConnection = new BrowserWindowMessageConnection()
const stopScan = walletDetector(scannerConnection, handleWallets.bind(this))
}
}
```
Expand Down
1 change: 0 additions & 1 deletion docs/guides/error-handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ BaseError
└̌───WalletError
│ │ AlreadyConnectedError
│ │ MessageDirectionError
│ │ NoWalletConnectedError
│ │ RpcConnectionError
```
Expand Down
9 changes: 4 additions & 5 deletions examples/browser/aepp/src/Connect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
</template>

<script>
import { WalletDetector, BrowserWindowMessageConnection } from '@aeternity/aepp-sdk'
import { walletDetector, BrowserWindowMessageConnection } from '@aeternity/aepp-sdk'
import Value from './Value'
import { mapGetters } from 'vuex'
Expand All @@ -67,7 +67,7 @@ export default {
newWallet = newWallet || Object.values(wallets)[0]
if (confirm(`Do you want to connect to wallet ${newWallet.name} with id ${newWallet.id}`)) {
console.log('newWallet', newWallet)
detector.stopScan()
stopScan()
await this.aeSdk.connectToWallet(await newWallet.getConnection())
this.walletConnected = true
Expand All @@ -76,9 +76,8 @@ export default {
}
}
const scannerConnection = await BrowserWindowMessageConnection()
const detector = await WalletDetector({ connection: scannerConnection })
detector.scan(handleWallets.bind(this))
const scannerConnection = new BrowserWindowMessageConnection()
const stopScan = walletDetector(scannerConnection, handleWallets.bind(this))
},
async connect () {
if (this.connectMethod === 'reverse-iframe') {
Expand Down
2 changes: 1 addition & 1 deletion examples/browser/wallet-iframe/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export default {
this.address = this.aeSdk.addresses()[0]
const target = this.runningInFrame ? window.parent : this.$refs.aepp.contentWindow
const connection = BrowserWindowMessageConnection({ target })
const connection = new BrowserWindowMessageConnection({ target })
this.aeSdk.addRpcClient(connection)
this.shareWalletInfo(connection.sendMessage.bind(connection))
Expand Down
2 changes: 1 addition & 1 deletion examples/browser/wallet-web-extension/src/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ import {

browser.runtime.onConnect.addListener((port) => {
// create connection
const connection = BrowserRuntimeConnection({ port })
const connection = new BrowserRuntimeConnection({ port })
// add new aepp to wallet
aeSdk.addRpcClient(connection)
// share wallet details
Expand Down
11 changes: 5 additions & 6 deletions examples/browser/wallet-web-extension/src/content-script.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* global browser */

import {
BrowserRuntimeConnection, BrowserWindowMessageConnection, MESSAGE_DIRECTION, ContentScriptBridge
BrowserRuntimeConnection, BrowserWindowMessageConnection, MESSAGE_DIRECTION, connectionProxy
} from '@aeternity/aepp-sdk'

(async () => {
Expand All @@ -17,13 +17,12 @@ import {
console.log('Document is ready')

const port = browser.runtime.connect()
const extConnection = BrowserRuntimeConnection({ port })
const pageConnection = BrowserWindowMessageConnection({
const extConnection = new BrowserRuntimeConnection({ port })
const pageConnection = new BrowserWindowMessageConnection({
target: window,
origin: window.origin,
sendDirection: MESSAGE_DIRECTION.to_aepp,
receiveDirection: MESSAGE_DIRECTION.to_waellet
})

const bridge = ContentScriptBridge({ pageConnection, extConnection })
bridge.run()
connectionProxy(pageConnection, extConnection)
})()
8 changes: 4 additions & 4 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ export { default as Oracle } from './ae/oracle'
export { default as Channel } from './channel'
export { default as Universal } from './ae/universal'

export { default as ContentScriptBridge } from './utils/aepp-wallet-communication/content-script-bridge'
export { default as connectionProxy } from './utils/aepp-wallet-communication/connection-proxy'
export * from './utils/aepp-wallet-communication/schema'
export { default as WalletDetector } from './utils/aepp-wallet-communication/wallet-detector'
export { default as BrowserRuntimeConnection } from './utils/aepp-wallet-communication/connection/browser-runtime'
export { default as BrowserWindowMessageConnection } from './utils/aepp-wallet-communication/connection/browser-window-message'
export { default as walletDetector } from './utils/aepp-wallet-communication/wallet-detector'
export { default as BrowserRuntimeConnection } from './utils/aepp-wallet-communication/connection/BrowserRuntime'
export { default as BrowserWindowMessageConnection } from './utils/aepp-wallet-communication/connection/BrowserWindowMessage'
export * from './utils/errors'
35 changes: 35 additions & 0 deletions src/utils/aepp-wallet-communication/connection-proxy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* ISC License (ISC)
* Copyright (c) 2022 aeternity developers
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/

import BrowserConnection from './connection/Browser'

/**
* Browser connection proxy
* Provide functionality to easily forward messages from one connection to another and back
* @param con1 first connection
* @param con2 second connection
* @returns a function to stop proxying
*/
export default (con1: BrowserConnection, con2: BrowserConnection): () => void => {
con1.connect((msg: any) => con2.sendMessage(msg), () => con2.disconnect())
con2.connect((msg: any) => con1.sendMessage(msg), () => con1.disconnect())

return () => {
con1.disconnect()
con2.disconnect()
}
}
68 changes: 68 additions & 0 deletions src/utils/aepp-wallet-communication/connection/Browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* ISC License (ISC)
* Copyright (c) 2022 aeternity developers
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/

import { AlreadyConnectedError, NoWalletConnectedError } from '../../errors'

/**
* Browser connection base interface
*/
export default abstract class BrowserConnection {
debug: boolean

protected constructor ({ debug = false }: { debug?: boolean }) {
this.debug = debug
}

/**
* Connect
* @param onMessage - Message handler
* @param onDisconnect - trigger when runtime connection in closed
*/
connect (
onMessage: (message: any, origin: string, source: any) => void,
onDisconnect: () => void
): void {
if (this.isConnected()) throw new AlreadyConnectedError('You already connected')
}

/**
* Disconnect
*/
disconnect (): void {
if (!this.isConnected()) throw new NoWalletConnectedError('You dont have connection. Please connect before')
}

/**
* Send message
*/
protected receiveMessage (message: any): void {
if (this.debug) console.log('Receive message:', message)
}

/**
* Send message
*/
sendMessage (message: any): void {
if (this.debug) console.log('Send message:', message)
}

/**
* Check if connected
* @return Is connected
*/
abstract isConnected (): boolean
}
58 changes: 58 additions & 0 deletions src/utils/aepp-wallet-communication/connection/BrowserRuntime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* ISC License (ISC)
* Copyright (c) 2022 aeternity developers
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/

import { Runtime } from 'webextension-polyfill'
import BrowserConnection from './Browser'

/**
* BrowserRuntimeConnection
* Handle browser runtime communication
*/
export default class BrowserRuntimeConnection extends BrowserConnection {
port: Runtime.Port

constructor ({ port, ...options }: { port: Runtime.Port, debug: boolean }) {
super(options)
this.port = port
}

disconnect (): void {
super.disconnect()
this.port.disconnect()
}

connect (
onMessage: (message: any, origin: string, source: Runtime.Port) => void,
onDisconnect: () => void
): void {
super.connect(onMessage, onDisconnect)
this.port.onMessage.addListener((message, port) => {
this.receiveMessage(message)
onMessage(message, port.name, port)
})
this.port.onDisconnect.addListener(onDisconnect)
}

sendMessage (message: any): void {
super.sendMessage(message)
this.port.postMessage(message)
}

isConnected (): boolean {
return this.port.onMessage.hasListeners()
}
}
Loading

0 comments on commit 19cb42f

Please sign in to comment.