Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
8b3328b
Modernize smart-wallets references + rewrite guestbook tutorial
AshFrancis Apr 16, 2026
1072e68
docs: address Copilot review feedback on guestbook tutorial
AshFrancis Apr 16, 2026
e495299
docs: show PRIVATE_RELAYER_BASE_URL in overview .env snippet
AshFrancis Apr 17, 2026
23d723f
docs: follow the moved SDK repos and fix the funder secret command
kaankacar Sep 8, 2026
603d171
docs: carry over guestbook contract and bindings fixes from #1945
ElliotFriend Sep 8, 2026
f38866f
docs: re-aim the guestbook tutorial at the current app
ElliotFriend Sep 8, 2026
439083e
docs: use the workspace protocol for the guestbook bindings package
ElliotFriend Sep 8, 2026
bb66f8e
docs: match the automated bindings list to what initialize.js does
ElliotFriend Sep 8, 2026
3b00ec7
docs: trim the guestbook frontend walkthrough to the parts that teach
ElliotFriend Sep 8, 2026
8571ac9
docs: keep the guestbook section description short enough to be usable
ElliotFriend Sep 8, 2026
46913c7
docs: tighten the guestbook relayer proxy origin check
ElliotFriend Sep 9, 2026
5a61a95
Merge branch 'main' of github.com:stellar/stellar-docs into chore/fin…
ElliotFriend Sep 14, 2026
e86279c
docs: drop migration framing from guestbook passkey prerequisites
ElliotFriend Sep 14, 2026
9e030f1
fix old-style admonition title
ElliotFriend Sep 14, 2026
578fb01
docs: describe the guestbook tooling without the before-and-after
ElliotFriend Sep 14, 2026
f52a766
docs: correct what the guestbook relayer proxy passes through
ElliotFriend Sep 15, 2026
5657017
docs: cover the kit's connection-verification failures
ElliotFriend Sep 15, 2026
69ed349
docs: compile the guestbook bindings explicitly, not via prepare
ElliotFriend Sep 15, 2026
3855b52
docs: scope the guestbook testing caveat to the frontend
ElliotFriend Sep 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/build/apps/guestbook/README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ sidebar_position: 57

import DocCardList from "@theme/DocCardList";

This section walks you through designing and building a decentralized application (dapp) that interacts with a smart contract guestbook, allowing users to read and write public messages. This tutorial also implements a passkey-powered smart wallet for user authentication.
This section walks you through designing and building a decentralized application (dapp) that interacts with a smart contract guestbook, allowing users to read and write public messages. The tutorial also implements a passkey-powered smart wallet for user authentication.

That smart wallet is built with [Smart Account Kit](https://github.com/stellar/smart-account-kit) and the [OpenZeppelin Smart Account](https://docs.openzeppelin.com/stellar-contracts/accounts/smart-account) contracts, and its transactions are submitted by the [OpenZeppelin Relayer](https://docs.openzeppelin.com/relayer/guides/stellar-channels-guide) running the Stellar Channels plugin.

<DocCardList />
117 changes: 80 additions & 37 deletions docs/build/apps/guestbook/bindings.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,96 +19,139 @@ We'll be generating our contract bindings, and keeping them in the same reposito
- Your deploy process might include a step that builds/deploys/binds a contract package at deploy-time.
- You could even generate and publish a bindings package all by itself. Then `pnpm install <bindings_package_name>` can be done in any dapp that you (or somebody else) might need to interact with that contract.

### The manual method
## The manual method

Before you skip ahead! Take a look at this (brief) section. It's _really_ useful to have a full understanding of what steps we're going through in the automated section. This will help you adapt and/or troubleshoot this tutorial for your specific purposes.

#### Install the compiled contract
### Install the compiled contract

The smart contract code needs to be installed to the network first. This uploads the compiled, binary Wasm file to the blockchain to be instantiated into a contract later on. From inside your project directory:

```shell
```sh
stellar contract upload \
--source-account <identity or secret key> \
--network testnet \
--wasm ./target/wasm32v1-none/release/ye_olde_guestbook.wasm
```

#### Deploy a contract instance
### Deploy a contract instance

This will return a hexadecimal hash corresponding to the uploaded Wasm executable. This hash can then be used in the deploy command to create a new contract instance:
This will return a hexadecimal hash corresponding to the uploaded Wasm executable (it's just the Sha256 hash of the executable file, fyi). This hash can then be used in the deploy command to create a new contract instance. Our `__constructor` function takes an `admin` address, and the `title` and `text` of the first guestbook message, so we supply those arguments after the `--` separator:

```shell
```sh
stellar contract deploy \
--source-account <identity or secret key> \
--network testnet \
--wasm-hash <wasm_hash_from_install_step>
--wasm-hash <wasm_hash_from_install_step> \
-- \
--admin <admin address> \
--title "Welcome!" \
--text "Thanks for visiting. Please sign my guestbook!"
```

#### Generate bindings for the deployed contract
### Generate bindings for the deployed contract

Now we can (again) use the Stellar CLI to generate bindings from the contract we've just deployed. You can also generate these bindings from your local Wasm file using the `--wasm-hash` parameter. The `--overwrite` parameter is used to tell the CLI that it should output the generated bindings package, even if it finds the directory is not empty (i.e., we're re-binding a contract because we've modified the code and redeployed it).

```shell
```sh
stellar contract bindings typescript \
--network testnet \
--id <contract_address_from_deploy_step> \
--output-dir ./packages/ye_olde_guestbook \
--overwrite
```

We'll need to build the bindings package, since (in its initial state) the package is mostly TypeScript types and stubs for the various contract functions.
The guestbook keeps its bindings packages in a [pnpm workspace](https://pnpm.io/workspaces), so `packages/*` is already claimed by the workspace glob and our freshly generated package is picked up automatically:

```shell
cd packages/ye_olde_guestbook
pnpm install
pnpm run build
cd ../..
```yaml title="pnpm-workspace.yaml"
packages:
- "packages/*"
```

#### Import the bindings package as a project dependency
That leaves two bits of housekeeping. The CLI writes a _standalone_ package, so it ships its own `pnpm-lock.yaml`, which you don't want inside a workspace (the root lockfile is the only one that matters). And the generated `package.json` only defines a `build` script, so we'll add a `prepare` script, which gets the bindings compiled on a clean `pnpm install`. That last one is nicer than it sounds: it means the built `dist/` directory never has to be committed.

With our bindings generated, we can add it to our frontend project. Run this from the root of your project:
```sh
rm -f packages/ye_olde_guestbook/pnpm-lock.yaml
pnpm --filter ye_olde_guestbook pkg set scripts.prepare=tsc
```

:::tip[Customize your bindings]

You could take this opportunity to customize your generated bindings _before_ you build them. By default, generated bindings will re-export the entirety of `@stellar/stellar-sdk` for your frontend application. If this behavior isn't desired, you can remove it. These packages are your own to modify as you see fit.

:::

### Import the bindings package as a project dependency

With our bindings generated, we can add it to our frontend project. Because it's a workspace package, we don't point at a file path. We let pnpm resolve it from the workspace instead, running this from the root of the project:

```sh
pnpm add -D ye_olde_guestbook --workspace
```

```shell
pnpm add file:./packages/ye_olde_guestbook
That records `"ye_olde_guestbook": "workspace:*"` in your root `package.json`, and pnpm _links_ the package out of `packages/` rather than copying it. So when you re-generate the bindings after changing your contract, your frontend picks up the new version with no re-install.

This is also the moment the bindings actually get compiled. Adding the dependency runs an install, the install runs the `prepare` script we just added, and `prepare` runs `tsc`. You'll see pnpm report it as it goes.

:::caution

Don't lean on `prepare` any harder than that, because pnpm only runs lifecycle scripts when it has installing to do. Against an already up-to-date workspace it prints "Already up to date" and skips them entirely, so a `dist/` that was never built (or that you cleaned) stays missing. Locally that's a puzzling import error. On a deploy it's worse: a warm build cache means the install has nothing to do, `prepare` never fires, and the build fails to resolve `ye_olde_guestbook` on a commit that worked perfectly well yesterday.

The fix is to compile the workspace packages explicitly, as a step the other scripts depend on rather than a side effect you hope for:

```json title="package.json"
{
"scripts": {
"bindings": "pnpm --filter \"./packages/*\" run build",
"dev": "pnpm run bindings && vite dev",
"build": "pnpm run bindings && vite build",
"check": "pnpm run bindings && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json"
}
}
```

#### Import the bindings client into the SvelteKit project
Now `pnpm dev` and `pnpm build` always have bindings to import, whatever pnpm decided to do about the install.

:::

### Import the bindings client into the SvelteKit project

:::info

We're straying just a _bit_ into the Svelte-ish side of things here. The main goal of this step is to get the contract client (which is the "bindings package" we've just generated) into our frontend in a way that makes it usable anywhere we need it. In SvelteKit, we put it into `src/lib/contracts` because that means we can easily access the client by importing from `$lib/contracts/ye_olde_guestbook` whenever and wherever we need it.
We're straying just a _bit_ into the Svelte-ish side of things here. The main goal of this step is to get the contract client (which is the "bindings package" we've just generated) into our frontend in a way that makes it usable anywhere we need it. In SvelteKit, we're putting it into `src/lib/contracts` because that means we can easily access the client by importing from `$lib/contracts/ye_olde_guestbook` whenever and wherever we need it.

:::

Now, we'll define the contract client in a way we can easily access it through the rest of our app.

```js title="src/lib/contracts/ye_olde_guestbook.ts"
import * as Client from "ye_olde_guestbook"; // import the package we just added as a dependency
import { PUBLIC_STELLAR_RPC_URL } from "$env/static/public"; // import the RPC url from the .env file
```ts title="src/lib/contracts/ye_olde_guestbook.ts"
import { Client, networks } from "ye_olde_guestbook";
import { PUBLIC_STELLAR_RPC_URL } from "$env/static/public";

// instantiate and export the Client class from the bindings package
export default new Client.Client({
...Client.networks.testnet, // this includes the contract address and network passphrase
rpcUrl: PUBLIC_STELLAR_RPC_URL, // this is required to invoke the contract through RPC calls
// `networks.testnet` contains the contract address and network passphrase
// baked in at bindings-generation time.
export default new Client({
...networks.testnet,
rpcUrl: PUBLIC_STELLAR_RPC_URL,
});
```

### The automated way
## The automated way

That was a lot of steps and a lot of work wasn't it!?

That was a lot of steps and a lot of work wasn't it!? The good news is that our starter template (remember that?) comes with an `initialize.js` script that will perform all of those actions for you! This script will go through all the following steps for you:
The good news is that our starter template (remember [that](./overview.mdx#start-from-the-stellar-template-repository)?) comes with an `initialize.js` script that will perform all of those actions for you! This script will go through all the following steps for you:

- Create and fund a keypair in the CLI
- Install and deploy **all contracts** in the `/contracts` directory
- Generate bindings from the deployed contracts
- Compile, install, and deploy **all contracts** in the `/contracts` directory
- Generate bindings from the deployed contracts, and settle each package into the workspace: add the `prepare` script, gitignore the compiled `dist/` directory, and delete the standalone lockfile the CLI writes
- Create a `$lib/contracts/<contract_alias>.ts` file for easy import into your frontend code

You can always customize this script to suit your needs. Check out the [source code here](https://github.com/ElliotFriend/soroban-template-sveltekit-passkeys/blob/main/initialize.js) (which has been documented with comments). Or, you can see the [officially maintained script](https://github.com/stellar/soroban-template-astro/blob/main/initialize.js) in the [`soroban-template-astro` repository](https://github.com/stellar/soroban-template-astro), as well.
You can always customize this script to suit your needs. Check out the [source code here](https://github.com/ElliotFriend/stellar-template-sveltekit-passkeys/blob/main/initialize.js) (which has been documented with comments). Or, you can see the [officially maintained script](https://github.com/stellar/soroban-template-astro/blob/main/initialize.js) in the [`soroban-template-astro` repository](https://github.com/stellar/soroban-template-astro), as well.

Run the initialization script like so:

```shell
```sh
node initialize.js
```

Expand All @@ -120,10 +163,10 @@ For a more comprehensive overview of the process of creating, customizing, and u

We've also added a command to the `package.json` scripts, so you can run this initialize script simply by running (from your project's root directory):

```shell
```sh
pnpm run setup
```

Right, so we've now created a starter project, written a guestbook smart contract, and generated an NPM package that will help us interact with that contract on the network. Amazing!
Right, so we've now cloned the starter project, written a guestbook smart contract, and generated an NPM package that will help us interact with that contract on the network. Amazing!

Next up, let's take a look at how our users will connect with and interact with our dapp. It's time for passkeys! (insert air horn noises)📢
Next up, let's set up the one prerequisite our passkey-powered smart wallets need: an OpenZeppelin Relayer API key.
Loading
Loading