Skip to content

Commit

Permalink
[DOC] Update dapp-frontend.mdx so it includes how to use the StellarW…
Browse files Browse the repository at this point in the history
…alletsKit to connect the wallets (#913)

* [DOC] Update dapp-frontend.mdx so it includes how to use the StellarWalletsKit to connect the wallets

* Update docs/build/apps/dapp-frontend.mdx

Co-authored-by: Elliot Voris <[email protected]>

* Apply suggestions from code review

Co-authored-by: Elliot Voris <[email protected]>

---------

Co-authored-by: Elliot Voris <[email protected]>
  • Loading branch information
earrietadev and ElliotFriend authored Aug 16, 2024
1 parent 289427d commit e2a08cb
Showing 1 changed file with 95 additions and 40 deletions.
135 changes: 95 additions & 40 deletions docs/build/apps/dapp-frontend.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -211,22 +211,57 @@ Go to Settings (the gear icon) → Preferences and toggle the switch to Enable E

Now you're all set up to use Freighter as a user, and you can add it to your app.

### Add Freighter
### Add the StellarWalletsKit and set it up

We're going to add a "Connect" button to the page that opens Freighter and prompts the user to give your web page permission to use Freighter. Once they grant this permission, the "Connect" button will be replaced with a message saying, "Signed in as [their public key]".
Even though we're using Freighter to test our app, there are more wallets that support signing smart contract transactions. To make their integration easier, we are using the `StellarWalletsKit` library which allows us support all Stellar Wallets with a single library.

First, add [@stellar/freighter-api](https://www.npmjs.com/package/@stellar/freighter-api) as a dependency:
To install this kit we are going to include the next package:

```bash
npm install @stellar/freighter-api
```shell
npm install @creit.tech/stellar-wallets-kit
```

With the package installed, we are going to create a new simple file where our instantiated kit and simple state will be located. Create the file `src/stellar-wallets-kit.ts` and paste this:

```ts title="src/stellar-wallets-kit.ts"
import {
allowAllModules,
FREIGHTER_ID,
StellarWalletsKit,
WalletNetwork,
} from "@creit.tech/stellar-wallets-kit";

const kit: StellarWalletsKit = new StellarWalletsKit({
modules: allowAllModules(),
network: WalletNetwork.TESTNET,
selectedWalletId: FREIGHTER_ID,
});

const connectionState: { publicKey: string | undefined } = {
publicKey: undefined,
};

function loadedPublicKey(): string | undefined {
return connectionState.publicKey;
}

function setPublicKey(data: string): void {
connectionState.publicKey = data;
}

export { kit, loadedPublicKey, setPublicKey };
```

Now let's add a new component to the `src/components` directory called `ConnectFreighter.astro` with the following contents:
In the code above, we created an instance of the kit and two simple functions that will take care of "setting" and "loading" the public key of the user. This lets us use the user's public key elsewhere in our code. The kit is started with Freighter as the default wallet, and the Testnet network as the default network. You can learn more about how the kit works in [the StellarWalletsKit documentation](https://stellarwalletskit.dev/)

```html title="src/components/ConnectFreighter.astro"
<div id="freighter-wrap" class="wrap" aria-live="polite">
Now we're going to add a "Connect" button to the page which will open the kit's built in modal, and prompt the user to use their preferred wallet. Once the user picks their preferred wallet and grants permission to accept requests from the website, we will fetch the public key and the "Connect" button will be replaced with a message saying, "Signed in as [their public key]".

Now let's add a new component to the `src/components` directory called `ConnectWallet.astro` with the following content:

```html title="src/components/ConnectWallet.astro"
<div id="connect-wrap" class="wrap" aria-live="polite">
<div class="ellipsis">
<button data-connect aria-controls="freighter-wrap">Connect</button>
<button data-connect aria-controls="connect-wrap">Connect</button>
</div>
</div>

Expand All @@ -247,34 +282,38 @@ Now let's add a new component to the `src/components` directory called `ConnectF
</style>

<script>
import { isAllowed, setAllowed, getUserInfo } from '@stellar/freighter-api';
import { kit, setPublicKey } from '../stellar-wallets-kit';
const wrap = document.querySelector('#freighter-wrap');
const ellipsis = document.querySelector('#freighter-wrap .ellipsis');
const button = document.querySelector('[data-connect]');
async function getPk() {
const { publicKey } = await getUserInfo();
return publicKey;
}
async function setLoggedIn(publicKey: string) {
ellipsis.innerHTML = `Signed in as ${publicKey}`;
ellipsis.title = publicKey;
}
if (await isAllowed()) {
const publicKey = await getPk();
if (publicKey) setLoggedIn(publicKey);
else wrap.innerHTML = 'Freighter is locked.<br>Sign in & refresh the page.';
} else {
button.addEventListener('click', async () => {
button.disabled = true;
await setAllowed();
const publicKey = await getPk();
await setLoggedIn(publicKey);
});
}
button.addEventListener('click', async () => {
button.disabled = true;
try {
await kit.openModal({
onWalletSelected: async option => {
try {
kit.setWallet(option.id);
const { address } = await kit.getAddress();
setPublicKey(address);
await setLoggedIn(address);
} catch (e) {
console.error(e);
}
},
});
} catch (e) {
console.error(e);
}
button.disabled = false;
});
</script>
```

Expand All @@ -286,7 +325,7 @@ And all the `script` declarations get bundled together and included intelligentl

You can read more about this in [Astro's page about client-side scripts](https://docs.astro.build/en/guides/client-side-scripts/).

The code itself here is pretty self-explanatory. We import a few methods from `@stellar/freighter-api` to check if the user is logged in. If they already are, then `isAllowed` returns `true`. If it's been more than a day since they've used the Freighter extension, then the `publicKey` will be blank, so we tell them to unlock Freighter and refresh the page. If `isAllowed` and the `publicKey` both look good, we replace the contents of the `div` with the signed-in message, replacing the button. Otherwise, we add a click handler to the button to prompt the user to connect Freighter with `setAllowed`. Once they do, we again replace the contents of the `div` with the signed-in message. The [`aria` stuff](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions) ensures that screen readers will read the new contents when they're updated.
The code itself here is pretty self-explanatory. We import the wallets kit from the file we created before. Then, when the user clicks on the button, we launch the built-in modal do display to the user connection options. Once the user picks their preferred wallet, we set it as the wallets kit's default wallet before requesting and saving the address.

Now we can import the component in the frontmatter of `pages/index.astro`:

Expand All @@ -295,15 +334,15 @@ Now we can import the component in the frontmatter of `pages/index.astro`:
import Layout from '../layouts/Layout.astro';
import Card from '../components/Card.astro';
import helloWorld from "../contracts/hello_world";
+import ConnectFreighter from '../components/ConnectFreighter.astro';
+import ConnectWallet from '../components/ConnectWallet.astro'
...
```

And add it right below the `<h1>`:

```diff title="pages/index.astro"
<h1>{greeting}</h1>
+<ConnectFreighter />
+<ConnectWallet />
```

If you're no longer running your dev server, go ahead and restart it:
Expand All @@ -327,27 +366,43 @@ Current value: <strong id="current-value" aria-live="polite">???</strong><br />
<button data-increment aria-controls="current-value">Increment</button>

<script>
import { kit, loadedPublicKey } from "../stellar-wallets-kit";
import incrementor from "../contracts/soroban_increment_contract";
import { isAllowed, getPublicKey, signTransaction } from '@stellar/freighter-api';
const button = document.querySelector("[data-increment]");
const currentValue = document.querySelector("#current-value");
if (await isAllowed()) {
const publicKey = await getPublicKey();
if (publicKey) incrementor.options.publicKey = publicKey;
}
button.addEventListener("click", async () => {
const publicKey = loadedPublicKey();
if (!publicKey) {
alert("Please connect your wallet first");
return;
} else {
incrementor.options.publicKey = publicKey;
}
button.disabled = true;
button.classList.add("loading");
currentValue.innerHTML =
currentValue.innerHTML +
'<span class="visually-hidden"> – updating…</span>';
const tx = await incrementor.increment();
const { result } = await tx.signAndSend({signTransaction});
// Only use `innerHTML` with contract values you trust!
// Blindly using values from an untrusted contract opens your users to script injection attacks!
currentValue.innerHTML = result.toString();
try {
const { result } = await tx.signAndSend({
signTransaction: async (xdr) => {
const { signedTxXdr } = await kit.signTransaction(xdr);
return signedTxXdr;
},
});
// Only use `innerHTML` with contract values you trust!
// Blindly using values from an untrusted contract opens your users to script injection attacks!
currentValue.innerHTML = result.toString();
} catch (e) {
console.error(e);
}
button.disabled = false;
button.classList.remove("loading");
Expand Down

0 comments on commit e2a08cb

Please sign in to comment.