Skip to content

Commit 113f28f

Browse files
authored
Merge pull request #1866 from aeternity/feature/refactor-rpc
Extract WalletConnectorFrame, deprecate AeSdkAepp
2 parents 908930d + 33a081a commit 113f28f

File tree

14 files changed

+375
-24
lines changed

14 files changed

+375
-24
lines changed

examples/browser/aepp/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"dependencies": {
1010
"@aeternity/aepp-calldata": "^1.5.1",
1111
"@aeternity/aepp-sdk": "file:../../..",
12+
"@ledgerhq/hw-transport-webusb": "^6.27.17",
1213
"buffer": "^6.0.3",
1314
"core-js": "^3.32.1",
1415
"tailwindcss": "^2.2.19",
@@ -21,6 +22,9 @@
2122
"sass": "^1.66.1",
2223
"sass-loader": "^13.3.2"
2324
},
25+
"peerDependencies": {
26+
"webpack": "^5.0.0"
27+
},
2428
"browserslist": [
2529
"> 1%",
2630
"last 2 versions",

examples/browser/aepp/src/Connect.vue

Lines changed: 144 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,67 @@
3434
>
3535
Cancel detection
3636
</button>
37+
38+
<template v-if="walletConnected">
39+
<br>
40+
<button @click="getAccounts">
41+
Get accounts
42+
</button>
43+
<button @click="subscribeAccounts('subscribe', 'current')">
44+
Subscribe current
45+
</button>
46+
<button @click="subscribeAccounts('unsubscribe', 'current')">
47+
Unsubscribe current
48+
</button>
49+
<button @click="subscribeAccounts('subscribe', 'connected')">
50+
Subscribe connected
51+
</button>
52+
<button @click="subscribeAccounts('unsubscribe', 'connected')">
53+
Unsubscribe connected
54+
</button>
55+
56+
<div>
57+
<div>RPC Accounts</div>
58+
<div>{{ rpcAccounts.map((account) => account.address.slice(0, 8)).join(', ') }}</div>
59+
</div>
60+
</template>
61+
</div>
62+
63+
<h2>Ledger Hardware Wallet</h2>
64+
<div class="group">
65+
<template v-if="ledgerStatus">
66+
<div>
67+
<div>Connection status</div>
68+
<div>{{ ledgerStatus }}</div>
69+
</div>
70+
</template>
71+
<button
72+
v-else-if="!ledgerAccountFactory"
73+
@click="connectLedger"
74+
>
75+
Connect
76+
</button>
77+
<template v-else>
78+
<button @click="disconnectLedger">
79+
Disconnect
80+
</button>
81+
<button @click="addLedgerAccount">
82+
Add Account
83+
</button>
84+
<button
85+
v-if="ledgerAccounts.length > 1"
86+
@click="switchLedgerAccount"
87+
>
88+
Switch Account
89+
</button>
90+
<button @click="switchNode">
91+
Switch Node
92+
</button>
93+
<div v-if="ledgerAccounts.length">
94+
<div>Ledger Accounts</div>
95+
<div>{{ ledgerAccounts.map((account) => account.address.slice(0, 8)).join(', ') }}</div>
96+
</div>
97+
</template>
3798
</div>
3899

39100
<div class="group">
@@ -58,8 +119,10 @@
58119
<script>
59120
import {
60121
walletDetector, BrowserWindowMessageConnection, RpcConnectionDenyError, RpcRejectedByUserError,
122+
WalletConnectorFrame, AccountLedgerFactory,
61123
} from '@aeternity/aepp-sdk';
62124
import { mapState } from 'vuex';
125+
import TransportWebUSB from '@ledgerhq/hw-transport-webusb';
63126
64127
export default {
65128
data: () => ({
@@ -70,6 +133,10 @@ export default {
70133
reverseIframeWalletUrl: process.env.VUE_APP_WALLET_URL ?? `http://${location.hostname}:9000`,
71134
walletInfo: null,
72135
cancelWalletDetection: null,
136+
rpcAccounts: [],
137+
ledgerStatus: '',
138+
ledgerAccountFactory: null,
139+
ledgerAccounts: [],
73140
}),
74141
computed: {
75142
...mapState(['aeSdk']),
@@ -79,6 +146,54 @@ export default {
79146
},
80147
},
81148
methods: {
149+
async connectLedger() {
150+
try {
151+
this.ledgerStatus = 'Waiting for Ledger response';
152+
const transport = await TransportWebUSB.create();
153+
this.ledgerAccountFactory = new AccountLedgerFactory(transport);
154+
} catch (error) {
155+
if (error.name === 'TransportOpenUserCancelled') return;
156+
throw error;
157+
} finally {
158+
this.ledgerStatus = '';
159+
}
160+
},
161+
async disconnectLedger() {
162+
this.ledgerAccountFactory = null;
163+
this.ledgerAccounts = [];
164+
this.$store.commit('setAddress', undefined);
165+
if (Object.keys(this.aeSdk.accounts).length) this.aeSdk.removeAccount(this.aeSdk.address);
166+
},
167+
async addLedgerAccount() {
168+
try {
169+
this.ledgerStatus = 'Waiting for Ledger response';
170+
const idx = this.ledgerAccounts.length;
171+
const account = await this.ledgerAccountFactory.initialize(idx);
172+
this.ledgerStatus = `Ensure that ${account.address} is displayed on Ledger HW screen`;
173+
await this.ledgerAccountFactory.getAddress(idx, true);
174+
this.ledgerAccounts.push(account);
175+
this.setAccount(this.ledgerAccounts[0]);
176+
} catch (error) {
177+
if (error.statusCode === 0x6985) return;
178+
throw error;
179+
} finally {
180+
this.ledgerStatus = '';
181+
}
182+
},
183+
switchLedgerAccount() {
184+
this.ledgerAccounts.push(this.ledgerAccounts.shift());
185+
this.setAccount(this.ledgerAccounts[0]);
186+
},
187+
async switchNode() {
188+
await this.setNode(this.$store.state.networkId === 'ae_mainnet' ? 'ae_uat' : 'ae_mainnet');
189+
},
190+
async getAccounts() {
191+
this.rpcAccounts = await this.walletConnector.getAccounts();
192+
if (this.rpcAccounts.length) this.setAccount(this.rpcAccounts[0]);
193+
},
194+
async subscribeAccounts(type, value) {
195+
await this.walletConnector.subscribeAccounts(type, value);
196+
},
82197
async detectWallets() {
83198
if (this.connectMethod === 'reverse-iframe') {
84199
this.reverseIframe = document.createElement('iframe');
@@ -93,6 +208,7 @@ export default {
93208
stopDetection();
94209
resolve(newWallet.getConnection());
95210
this.cancelWalletDetection = null;
211+
this.walletInfo = newWallet.info;
96212
}
97213
});
98214
this.cancelWalletDetection = () => {
@@ -103,25 +219,43 @@ export default {
103219
};
104220
});
105221
},
222+
async setNode(networkId) {
223+
const [{ name }] = (await this.aeSdk.getNodesInPool())
224+
.filter((node) => node.nodeNetworkId === networkId);
225+
this.aeSdk.selectNode(name);
226+
this.$store.commit('setNetworkId', networkId);
227+
},
228+
setAccount(account) {
229+
if (Object.keys(this.aeSdk.accounts).length) this.aeSdk.removeAccount(this.aeSdk.address);
230+
this.aeSdk.addAccount(account, { select: true });
231+
this.$store.commit('setAddress', account.address);
232+
},
106233
async connect() {
107234
this.walletConnecting = true;
108-
this.aeSdk.onDisconnect = () => {
109-
this.walletConnected = false;
110-
this.walletInfo = null;
111-
this.$store.commit('setAddress', undefined);
112-
if (this.reverseIframe) this.reverseIframe.remove();
113-
};
114235
try {
115236
const connection = await this.detectWallets();
116237
try {
117-
this.walletInfo = await this.aeSdk.connectToWallet(connection);
238+
this.walletConnector = await WalletConnectorFrame.connect('Simple æpp', connection);
118239
} catch (error) {
119240
if (error instanceof RpcConnectionDenyError) connection.disconnect();
120241
throw error;
121242
}
243+
this.walletConnector.on('disconnect', () => {
244+
this.walletConnected = false;
245+
this.walletInfo = null;
246+
this.rpcAccounts = [];
247+
this.$store.commit('setAddress', undefined);
248+
if (this.reverseIframe) this.reverseIframe.remove();
249+
});
122250
this.walletConnected = true;
123-
const { address: { current } } = await this.aeSdk.subscribeAddress('subscribe', 'connected');
124-
this.$store.commit('setAddress', Object.keys(current)[0]);
251+
252+
this.setNode(this.walletConnector.networkId);
253+
this.walletConnector.on('networkIdChange', (networkId) => this.setNode(networkId));
254+
255+
this.walletConnector.on('accountsChange', (accounts) => {
256+
this.rpcAccounts = accounts;
257+
if (accounts.length) this.setAccount(accounts[0]);
258+
});
125259
} catch (error) {
126260
if (
127261
error.message === 'Wallet detection cancelled'
@@ -134,7 +268,7 @@ export default {
134268
}
135269
},
136270
disconnect() {
137-
this.aeSdk.disconnectWallet();
271+
this.walletConnector.disconnect();
138272
},
139273
},
140274
};

examples/browser/aepp/src/store.js

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,18 @@
11
import { shallowRef } from 'vue';
22
import { createStore } from 'vuex';
3-
import { AeSdkAepp, Node, CompilerHttp } from '@aeternity/aepp-sdk';
3+
import { AeSdk, Node, CompilerHttp } from '@aeternity/aepp-sdk';
44

55
const store = createStore({
66
state: {
77
address: undefined,
88
networkId: undefined,
9-
// AeSdkAepp instance can't be in deep reactive https://github.com/aeternity/aepp-sdk-js/blob/develop/docs/README.md#vue3
10-
aeSdk: shallowRef(new AeSdkAepp({
11-
name: 'Simple æpp',
9+
// AeSdk instance can't be in deep reactive https://github.com/aeternity/aepp-sdk-js/blob/develop/docs/README.md#vue3
10+
aeSdk: shallowRef(new AeSdk({
1211
nodes: [
1312
{ name: 'testnet', instance: new Node('https://testnet.aeternity.io') },
1413
{ name: 'mainnet', instance: new Node('https://mainnet.aeternity.io') },
1514
],
1615
onCompiler: new CompilerHttp('https://v8.compiler.aepps.com'),
17-
async onNetworkChange({ networkId }) {
18-
const [{ name }] = (await this.getNodesInPool())
19-
.filter((node) => node.nodeNetworkId === networkId);
20-
this.selectNode(name);
21-
store.commit('setNetworkId', networkId);
22-
},
23-
onAddressChange: ({ current }) => store.commit('setAddress', Object.keys(current)[0]),
2416
})),
2517
},
2618
mutations: {

examples/browser/aepp/src/styles.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ body {
1919
}
2020

2121
button {
22-
@extend .w-40, .p-2, .m-2, .rounded-full, .bg-purple-500, .text-white, .text-xs;
22+
@extend .w-44, .p-2, .m-2, .rounded-full, .bg-purple-500, .text-white, .text-xs;
2323

2424
&:disabled {
2525
@extend .bg-purple-300, .cursor-not-allowed;

examples/browser/aepp/vue.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
const { defineConfig } = require('@vue/cli-service');
2+
const webpack = require('webpack');
23

34
module.exports = defineConfig({
45
publicPath: process.env.PUBLIC_PATH ?? '/',
56
devServer: {
67
port: 9001,
78
},
9+
configureWebpack: {
10+
plugins: [
11+
new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'] }),
12+
],
13+
},
814
});

examples/browser/wallet-iframe/src/styles.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ body {
1515
}
1616

1717
button {
18-
@extend .w-40, .p-2, .m-2, .rounded-full, .bg-purple-500, .text-white, .text-xs;
18+
@extend .w-44, .p-2, .m-2, .rounded-full, .bg-purple-500, .text-white, .text-xs;
1919

2020
&:disabled {
2121
@extend .bg-purple-300, .cursor-not-allowed;

examples/browser/wallet-web-extension/src/styles.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ body {
77
}
88

99
button {
10-
@extend .w-40, .p-2, .m-2, .rounded-full, .bg-purple-500, .text-white, .text-xs;
10+
@extend .w-44, .p-2, .m-2, .rounded-full, .bg-purple-500, .text-white, .text-xs;
1111

1212
&:disabled {
1313
@extend .bg-purple-300, .cursor-not-allowed;

package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
"bs58": "^6.0.0",
7171
"buffer": "^6.0.3",
7272
"canonicalize": "^2.0.0",
73+
"eventemitter3": "^5.0.1",
7374
"events": "^3.3.0",
7475
"isomorphic-ws": "^5.0.0",
7576
"json-bigint": "^1.0.0",

src/AeSdkAepp.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import BrowserConnection from './aepp-wallet-communication/connection/Browser';
2121
/**
2222
* RPC handler for AEPP side
2323
* Contain functionality for wallet interaction and connect it to sdk
24+
* @deprecated use WalletConnectorFrame instead
2425
* @category aepp wallet communication
2526
*/
2627
export default class AeSdkAepp extends AeSdkBase {

0 commit comments

Comments
 (0)