Skip to content

Commit

Permalink
feat: add example
Browse files Browse the repository at this point in the history
  • Loading branch information
IsaccoSordo committed May 21, 2024
1 parent 273527f commit 9e166b6
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 0 deletions.
1 change: 1 addition & 0 deletions sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module.exports = {
"getting-started/best-practices",
"getting-started/simple-example",
"getting-started/advanced-example",
"getting-started/subscribe-to-active-account",
"getting-started/high-performance",
],
collapsed: false,
Expand Down
177 changes: 177 additions & 0 deletions src/docs/getting-started/subscribe-to-active-account.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# Subscribe to `ACTIVE_ACCOUNT_SET` Advanced Example

:::warning
This page is still under work. Some sections may be incomplete or subject to change.
:::

Beacon provides developers the ability to subscribe to its internal state, as shown on the dedicated page. Since version 4.2.0, subscribing to `ACTIVE_ACCOUNT_SET` has become mandatory. This page provides a custom example with user validation by requesting the wallet to sign a payload.

## Before Starting

Be aware that calling one of the functions listed below will also trigger `ACTIVE_ACCOUNT_SET`. Be careful when calling such functions inside the handler to avoid causing your dApp to enter an endless loop.

**List of functions:**

- `requestPermissions`
- `setActiveAccount`
- `clearActiveAccount`
- `disconnect`
- `destroy`
- `removeAccount`
- `removeAllAccounts`

## Example

After initializing your `dAppClient` instance, you need to subscribe to `ACTIVE_ACCOUNT_SET` as shown below:

```ts
const dAppClient = new DAppClient({
name: "Beacon Docs",
});

dAppClient.subscribeToEvent(BeaconEvent.ACTIVE_ACCOUNT_SET, (account) => {
console.log(`${BeaconEvent.ACTIVE_ACCOUNT_SET} triggered: `, account);
});
```

The handler should be as concise as possible. Ideally, it should just update your active account globally throughout your dApp. However, in some cases, adding extra lines of code can be beneficial as shown below.

## Adding `requestSignPayload`

Sometimes `requestPermissions` may not be enough, and you want to ensure the user who has synced with the wallet is authorized. A common way to accomplish this is by sending a _sign_payload_ request to the wallet.

```ts
dAppClient.subscribeToEvent(BeaconEvent.ACTIVE_ACCOUNT_SET, async (account) => {
console.log(
`${BeaconEvent.ACTIVE_ACCOUNT_SET} triggered: `,
account?.address,
);

if (!account) {
return;
}

try {
await dAppClient.requestSignPayload({
payload:
"05010000004254657a6f73205369676e6564204d6573736167653a207465737455726c20323032332d30322d30385431303a33363a31382e3435345a2048656c6c6f20576f726c64",
});
} catch (err) {
// The request was rejected
// Abort
}
});
```

> **Note:** `ACTIVE_ACCOUNT_SET` gets triggered both when setting a new account and resetting the current one. Make sure not to send a **sign_payload** request without an account.
## Multitab Synchronization

While the example above works for single-page dApps, it may become problematic in a multi-tab setup. Beacon emits an event to keep each tab synced with the internal state. Therefore, if your dApp needs multiple tabs support, the above approach may cause issues. Each tab will send a **sign_payload** request to the wallet, which is not intended and may lead to request rejection if a certain threshold is reached.

To address this, we need to implement multi-tab synchronization. There are multiple ways to achieve this; for simplicity, we use `broadcast-channel`.

### Step 1: Install `broadcast-channel`

Run the following command:

```bash
npm install broadcast-channel
```

### Step 2: Set Up the Channel

The main idea is to elect a tab as the Leader so that only this tab will send a request to the wallet. First, set up a channel.

```ts
const channel = new BroadcastChannel("beacon-channel");
const elector = createLeaderElection(channel);
```

Check if a leader already exists, otherwise request leadership.

```ts
elector.hasLeader().then(async (hasLeader) => {
if (!hasLeader) {
await elector.awaitLeadership();
}
});
```

### Step 3: Update the Handler

Now, inside the handler, check whether the current tab has the leadership. If not, do not send a `sign_payload` request.

```ts
dAppClient.subscribeToEvent(BeaconEvent.ACTIVE_ACCOUNT_SET, async (account) => {
console.log(
`${BeaconEvent.ACTIVE_ACCOUNT_SET} triggered: `,
account?.address,
);

if (!account || !elector.isLeader) {
return;
}

try {
await dAppClient.requestSignPayload({
payload:
"05010000004254657a6f73205369676e6564204d6573736167653a207465737455726c20323032332d30322d30385431303a33363a31382e3435345a2048656c6c6f20576f726c64",
});
} catch (err) {
// The request was rejected
// Abort
}
});
```

## Conclusion

The end result should look like this:

```ts
import { DAppClient, BeaconEvent } from "@airgap/beacon-dapp";
import { NetworkType } from "@airgap/beacon-types";
import { BroadcastChannel, createLeaderElection } from "broadcast-channel";

const channel = new BroadcastChannel("beacon-test");
const elector = createLeaderElection(channel);

elector.hasLeader().then(async (hasLeader) => {
if (!hasLeader) {
await elector.awaitLeadership();
}
});

const dAppClient = new DAppClient({
name: "Beacon Playground",
network: {
type: NetworkType.GHOSTNET,
},
});

dAppClient.subscribeToEvent(BeaconEvent.ACTIVE_ACCOUNT_SET, async (account) => {
console.log(
`${BeaconEvent.ACTIVE_ACCOUNT_SET} triggered: `,
account?.address,
);

if (!account || !elector.isLeader) {
return;
}

try {
await dAppClient.requestSignPayload({
payload:
"05010000004254657a6f73205369676e6564204d6573736167653a207465737455726c20323032332d30322d30385431303a33363a31382e3435345a2048656c6f20576f726c64",
});
} catch (err) {
// The request was rejected
// Abort
}
});
```

## Live Example

For a live example, check [here](https://stackblitz.com/edit/vitejs-vite-71wsul-l2duvv?file=index.ts). We recommend cloning the repository locally for a better development experience.

0 comments on commit 9e166b6

Please sign in to comment.