Skip to content

Latest commit

 

History

History
31 lines (18 loc) · 1.53 KB

build-wallet.md

File metadata and controls

31 lines (18 loc) · 1.53 KB

How to build a wallet

This guide shows how to build either an WebExtension Wallet or a iFrame-based Wallet.

WebExtension wallet

The full implementation of this example can be found here:

Note:

  • If you want to see a more advanced implementation you can take a look into the repository of the Superhero Wallet

1. Create bridge between extension and page

First you need to create a bridge between your extension and the page. This can be done as follows:

import browser from 'webextension-polyfill';
import {
BrowserRuntimeConnection, BrowserWindowMessageConnection, MESSAGE_DIRECTION, connectionProxy,
} from '@aeternity/aepp-sdk';
(async () => {
console.log('Waiting until document is ready');
await new Promise((resolve) => {
const interval = setInterval(() => {
// TODO: ensure that there is no corresponding event
if (document.readyState !== 'complete') return;
clearInterval(interval);
resolve();
}, 100);
});
console.log('Document is ready');
const port = browser.runtime.connect();
const extConnection = new BrowserRuntimeConnection({ port });
const pageConnection = new BrowserWindowMessageConnection({
target: window,
origin: window.origin,
sendDirection: MESSAGE_DIRECTION.to_aepp,
receiveDirection: MESSAGE_DIRECTION.to_waellet,
});
connectionProxy(pageConnection, extConnection);
})();

2. Initialize AeSdkWallet class

Then you need to initialize AeSdkWallet class in your extension and subscribe for new runtime connections. After the connection is established you can share the wallet details with the application.

import browser from 'webextension-polyfill';
import {
AeSdkWallet, Node, MemoryAccount, generateKeyPair, BrowserRuntimeConnection, WALLET_TYPE,
RpcConnectionDenyError, RpcRejectedByUserError,
} from '@aeternity/aepp-sdk';
(async () => {
const aeppInfo = {};
const aeSdk = new AeSdkWallet({
compilerUrl: 'https://compiler.aepps.com',
nodes: [{
name: 'testnet',
instance: new Node('https://testnet.aeternity.io'),
}],
id: browser.runtime.id,
type: WALLET_TYPE.extension,
name: 'Wallet WebExtension',
// Hook for sdk registration
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(aeppId) {
const { name } = aeppInfo[aeppId];
if (!confirm(`Aepp ${name} with id ${aeppId} wants to subscribe for accounts`)) {
throw new RpcRejectedByUserError();
}
},
onSign(aeppId, params) {
const { name } = aeppInfo[aeppId];
if (!confirm(`Aepp ${name} with id ${aeppId} wants to sign tx ${params.tx}`)) {
throw new RpcRejectedByUserError();
}
},
onAskAccounts(aeppId) {
const { name } = aeppInfo[aeppId];
if (!confirm(`Aepp ${name} with id ${aeppId} wants to get accounts`)) {
throw new RpcRejectedByUserError();
}
},
onMessageSign(aeppId, params) {
const { name } = aeppInfo[aeppId];
if (!confirm(`Aepp ${name} with id ${aeppId} wants to sign msg ${params.message}`)) {
throw new RpcRejectedByUserError();
}
},
});
// The `ExtensionProvider` uses the first account by default.
// You can change active account using `selectAccount(address)` function
await aeSdk.addAccount(new MemoryAccount({
keypair: {
publicKey: 'ak_2dATVcZ9KJU5a8hdsVtTv21pYiGWiPbmVcU1Pz72FFqpk9pSRR',
secretKey: 'bf66e1c256931870908a649572ed0257876bb84e3cdf71efb12f56c7335fad54d5cf08400e988222f26eb4b02c8f89077457467211a6e6d955edb70749c6a33b',
},
}), { select: true });
await aeSdk.addAccount(new MemoryAccount({ keypair: generateKeyPair() }));
browser.runtime.onConnect.addListener((port) => {
// create connection
const connection = new BrowserRuntimeConnection({ port });
// add new aepp to wallet
const clientId = aeSdk.addRpcClient(connection);
// share wallet details
aeSdk.shareWalletInfo(clientId);
setInterval(() => aeSdk.shareWalletInfo(clientId), 3000);
});
console.log('Wallet initialized!');
})();

iFrame-based Wallet

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: