From e0c0fe08e2f3816df3510701d25c6e0dbad08b3f Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman Date: Thu, 26 Oct 2023 14:14:26 -0400 Subject: [PATCH 01/24] Updates to setup.mdx --- docs/getting-started/setup.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/getting-started/setup.mdx b/docs/getting-started/setup.mdx index 681eca52..627c1b38 100644 --- a/docs/getting-started/setup.mdx +++ b/docs/getting-started/setup.mdx @@ -35,7 +35,7 @@ If you use macOS, Linux, or another Unix-like OS, the simplest method to install a Rust toolchain is to install `rustup`. Install `rustup` with the following command. -``` +```bash curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ``` @@ -74,7 +74,7 @@ contract will execute on network, however in a local sandbox. Install the Soroban CLI using `cargo install`. ```bash -cargo install --locked --version 20.0.0-rc2 soroban-cli +cargo install --locked --version 20.0.0-rc.4.1 soroban-cli ``` :::info From 3b114c448e889e6467fbef86870c0805cd3e1086 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman Date: Thu, 26 Oct 2023 14:31:01 -0400 Subject: [PATCH 02/24] Add CLI for testnet configuration to Setup --- docs/getting-started/setup.mdx | 47 ++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/docs/getting-started/setup.mdx b/docs/getting-started/setup.mdx index 627c1b38..4ceee50a 100644 --- a/docs/getting-started/setup.mdx +++ b/docs/getting-started/setup.mdx @@ -143,5 +143,52 @@ Users of non-bash shells may need to adapt the above commands to suit their need ::: +### Configuring the CLI for Testnet + +Soroban has a test network called Testnet that you can use to deploy and test your smart contracts. It's a live network, but it's not the same as the Stellar public network. It's a separate network that is used for development and testing, so you can't use it for production apps. But it's a great place to test your contracts before you deploy them to the public network. + +To configure your CLI to interact with Testnet, run the following command: + +```bash +soroban config network add --global testnet \ + --rpc-url https://soroban-testnet.stellar.org:443 \ + --network-passphrase "Test SDF Network ; September 2015" +``` + +Note the `--global`. This creates a file in your home folder's `/.config/soroban/network/testnet.toml` with the settings you specified. This +means that you can use the `--network testnet` flag in any Soroban CLI command to use this network. + +If you want project-specific network configurations, you can omit the `--global` flag, and the networks will be added to your working directory's `.soroban/network` folder instead. + +### Configure an Identity + +When you deploy a smart contract to a network, you need to specify an identity that will be used to sign the transactions. + +Let's configure an identity called `alice`. You can use any name you want, but it might be nice to have some named identities that you can use for testing, such as [`alice`, `bob`, and `carol`](https://en.wikipedia.org/wiki/Alice_and_Bob). + +```bash +soroban config identity generate --global alice +``` + +You can see the public key of `alice` with: + +```bash +soroban config identity address alice +``` + +Like the Network configs, the `--global` means that the identity gets stored in `~/.config/soroban/identity/alice.toml`. You can omit the `--global` flag to store the identity in your project's `.soroban/identity` folder instead. + +All this did so far is generate a public/private keypair on your local machine. No network requests were made, and `alice` has no funds on Testnet. This means that you can't make any transactions with `alice` yet. + +To get `alice` some Testnet tokens, you'll need to use [Friendbot](https://developers.stellar.org/docs/fundamentals-and-concepts/testnet-and-pubnet#friendbot). All Stellar and Soroban test networks have a Friendbot that you can use to get some test tokens. The public Friendbot instance for Testnet lives at `https://friendbot.stellar.org`. Use it: + +```bash +curl "https://friendbot.stellar.org/?addr=$(soroban config identity address alice)" +``` + +:::tip Command Expansion `$(…)` +This uses [command expansion](https://www.gnu.org/software/bash/manual/html_node/Command-Substitution.html), which only works with bash-compatible shells. If you are using Windows or some other shell, you will need to copy the output of `soroban config…` and paste it into the `curl` command, or figure out how command expansion works in your shell. +::: + [Rust]: https://www.rust-lang.org/ [Soroban CLI]: setup#install-the-soroban-cli From 6a8824d7d7e1f38cfbb6022654c6a6c2919b67a0 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman Date: Tue, 24 Oct 2023 14:18:37 -0400 Subject: [PATCH 03/24] Updates to hello-world.mdx Reorder some code snippets to be after the description --- docs/getting-started/hello-world.mdx | 162 ++++++--------------------- 1 file changed, 35 insertions(+), 127 deletions(-) diff --git a/docs/getting-started/hello-world.mdx b/docs/getting-started/hello-world.mdx index 5221e81a..abf8defd 100644 --- a/docs/getting-started/hello-world.mdx +++ b/docs/getting-started/hello-world.mdx @@ -13,7 +13,7 @@ description: Create your first smart contract in Rust. /> Vec { - todo!() + vec![&env, symbol_short!("Hello"), to] } } ``` -The `#[contract]` attribute designates the Contract struct as the type to which contract functions are associated. This implies that the struct will have contract functions implemented for it. Contract functions are defined within an `impl` block for the struct, which is annotated with `#[contractimpl]`. It is important to note that contract functions should have names with a maximum length of 32 characters. Additionally, if a function is intended to be invoked from outside the contract, it should be marked with the `pub` visibility modifier. It is common for the first argument of a contract function to be of type `Env`, allowing access to a copy of the Soroban environment, which is typically necessary for various operations within the contract. - Putting those pieces together a simple contract will look like this. ```rust title="src/lib.rs" @@ -213,6 +213,13 @@ impl Contract { Writing tests for Soroban contracts involves writing Rust code using the test facilities and toolchain that you'd use for testing any Rust code. +You'll need to add an annotation to bottom of `lib.rs` to tell Rust to compile and run the test code. + +```rust +#[cfg(test)] +mod test; +``` + Given a simple contract like the contract demonstrated in the Write a Contract section, a simple test will look like this. @@ -302,6 +309,12 @@ test test::test ... ok Try changing the values in the test to see how it works. +:::note + +The first time you run the tests you may see output in the terminal of cargo compiling all the dependencies before running the tests. Like this: + +::: + [Rust unit test]: https://doc.rust-lang.org/rust-by-example/testing/unit_testing.html [Rust integration test]: https://doc.rust-lang.org/rust-by-example/testing/integration_testing.html @@ -328,119 +341,12 @@ target/wasm32-unknown-unknown/release/hello_soroban.wasm The `.wasm` file contains the logic of the contract, as well as the contract's [specification / interface types](../fundamentals-and-concepts/fully-typed-contracts.mdx), which can be imported into other contracts who wish to call it. This is the only artifact needed to deploy the contract, share the interface with others, or integration test against the contract. :::tip Optimize Builds -To further optimize builds to be as small as possible, see [Optimizing Builds](#optimizing-builds). +To further optimize builds to be as small as possible, see [Optimizing Builds]. ::: -## Run on Sandbox - -If you have [`soroban-cli`] installed, you can run contracts in a local sandbox environment. - -The Soroban sandbox environment is the same Soroban environment that runs on Stellar networks, but it runs without nodes, and without the other features you find on a Stellar network. - -:::info -It's also possible to run a contract on a fully featured local network. See [the RPC Reference page](../reference/rpc.mdx) for more details. -::: - -Using the code we wrote in [Write a Contract] and the resulting `.wasm` file we built in [Build], run the following command to invoke the `hello` function. Here we're setting the `to` argument to `friend`: - -```sh -soroban contract invoke \ - --wasm target/wasm32-unknown-unknown/release/[project_name].wasm \ - --id 1 \ - -- \ - hello \ - --to friend -``` - -The contract invocation should result in the following output. - -```json -["Hello", "friend"] -``` - -:::info The `--` double-dash is required! - -This is a general [CLI pattern](https://unix.stackexchange.com/questions/11376/what-does-double-dash-mean) used by other commands like [cargo run](https://doc.rust-lang.org/cargo/commands/cargo-run.html). Everything after the `--`, sometimes called [slop](https://github.com/clap-rs/clap/issues/971), is passed to a child process. In this case, `soroban contract invoke` builds an _implicit CLI_ on-the-fly for the `hello` method in your contract. It can do this because Soroban SDK embeds your contract's schema / interface types right in the `.wasm` file that gets deployed on-chain. You can also try: - -```bash -soroban contract invoke ... -- --help -``` - -and - -```bash -soroban contract invoke ... -- hello --help -``` - -::: - -### Diagnostic Events - -You will also notice here (and with any other contract run in the sandbox environment) that some diagnostic output will be displayed on the screen. This is because the `soroban-cli` automatically sets the diagnostic level to "Debug" for contracts running in the sandbox. - -Here, we'll see two diagnostic events were emitted by our contract invocation: - -1. First is a `fn_call` diagnostic event. It tells you the _kind_ of event, the contract ID that will be called, the name of the function being called, and a vector of the arguments passed to the function. A (greatly simplified) version of this event's body would look like this: - -``` -{ - event: { - body: { - topics: [ - Symbol(fn_call), - Bytes(0000000000000000000000000000000000000000000000000000000000000001), - Symbol(hello), - ], - data: Symbol(friend), - } - } -} -``` - -2. Next is a `fn_return` diagnostic event. These events are emitted when a contract call completes and contains the _kind_ of event, the name of the function that is returning, and the value returned by the function. A (greatly simplified) version of this event's body would look like this: - -``` -{ - event: { - body: { - topics: [ - Symbol(fn_return), - Symbol(hello), - ], - data: [ - Symbol(Hello), - Symbol(friend), - ] - } - } -} -``` - -You can learn more about diagnostic events in our [Events article](../fundamentals-and-concepts/events#what-are-diagnosticevents). - -## Optimizing Builds - -Use `soroban contract optimize` to further minimize the size of the `.wasm`. First, re-install soroban-cli with the `opt` feature: - - cargo install --locked --version 20.0.0-rc2 soroban-cli --features opt - -Then build an optimized `.wasm` file: - - soroban contract optimize \ - --wasm target/wasm32-unknown-unknown/release/hello_soroban.wasm - -This will optimize and output a new `hello_soroban.optimized.wasm` file in the same location as the input `.wasm`. - -:::tip -Building optimized contracts is only necessary when deploying to a network with fees or when analyzing and profiling a contract to get it as small as possible. If you're just starting out writing a contract, these steps are not necessary. See [Build] for details on how to build for development. -::: - -[Write a Contract]: #write-a-contract -[Build]: #build - ## Commit to version control -Now that everything's working, this is a great time to commit your code to version control. Even if you don't share your project with others, this will make it easier for you to see and understand your own changes throughout the rest of the tutorial. +Before we go on to deploying the contract to Testnet in the next section, this is a great time to commit your code to version control. Even if you don't share your project with others, this will make it easier for you to see and understand your own changes throughout the rest of the tutorial. Go ahead and initialize `hello-soroban` as a git repository: @@ -469,6 +375,8 @@ git commit -m "Initial commit: hello-soroban contract" ## Summary -In this section, we wrote a simple contract that can be deployed to a Soroban network. We also learned how to run the contract locally using the Soroban sandbox environment. +In this section, we wrote a simple contract that can be deployed to a Soroban network. + +Next we'll learn to deploy the hello-soroban contract to Soroban's Testnet network and interact with it over RPC using the CLI. -Next we'll add a new contract to this project, reorganizing the project as a multi-contract project using Cargo Workspaces. The new contract will show off a little bit of Soroban's storage capabilities. +[Optimizing Builds]: deploy-to-testnet#optimizing-builds \ No newline at end of file From 3fd4f7c3697da2ea0a2759e986bcbbb908aab5f6 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman Date: Wed, 25 Oct 2023 09:55:11 -0400 Subject: [PATCH 04/24] Small edits in storing-data.mdx --- docs/getting-started/storing-data.mdx | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/docs/getting-started/storing-data.mdx b/docs/getting-started/storing-data.mdx index 91487743..52daf285 100644 --- a/docs/getting-started/storing-data.mdx +++ b/docs/getting-started/storing-data.mdx @@ -63,11 +63,15 @@ Note that we copied the Cargo.toml file. That's because we're going to need some In the root `Cargo.toml`: - remove the `[package]`, `[lib]`, `[features]`, and `[dev_dependencies]` sections -- keep the `[release.*]` stuff +- keep the `[profile.release*]` stuff - replace the line `[dependencies]` with `[workspace.dependencies]` -- add a `[workspace]` section +- add a `[workspace]` section (see below for specific values) -In the project-specific `Cargo.toml`, keep roughly the opposite sections, and use the dependency versions as specified in the workspace root. It all ends up looking like this: +In the contract-specific `Cargo.toml`: +- remove the `[profile.release*]` stuff +- set the dependency versions to use the workspace versions (see example below) + +It all ends up looking like this: @@ -134,7 +138,7 @@ All tests should pass. ## Code -Rather than initializing this contract with `cargo new`, let's copy the `hello-soroban` project: +Rather than initializing the new contract with `cargo new`, let's copy the `hello-soroban` project: ```bash cp -r contracts/hello-soroban contracts/incrementor @@ -199,12 +203,12 @@ Follow along in your `contracts/incrementor/src/lib.rs` file. ### Contract Data Keys -Contract data is associated with a key. The key is the value that can be used at +Contract data is associated with a key. The key can be used at a later time to lookup the value. `Symbol` is a short (up to 32 characters long) string type with limited character space (only `a-zA-z0-9_` characters are allowed). Identifiers like -contract function names are represented by `Symbol`s. +contract function names and contract data keys are represented by `Symbol`s. The `symbol_short!()` macro is a convenient way to pre-compute short symbols up to 9 characters in length at compile time using `Symbol::short`. It generates a compile-time constant that adheres to the valid character set of letters (a-zA-Z), numbers (0-9), and underscores (\_). If a symbol exceeds the 9-character limit, `Symbol::new` should be utilized for creating symbols at runtime. @@ -231,6 +235,11 @@ If no value is currently stored, the value given to `unwrap_or(...)` is returned Values stored as contract data and retrieved are transmitted from [the environment](../fundamentals-and-concepts/environment-concepts.mdx) and expanded into the type specified. In this case a `u32`. If the value can be expanded, the type returned will be a `u32`. Otherwise, if a developer caused it to be some other type, a panic would occur at the unwrap. The `set()` function stores the new count value against the key, replacing the existing value. +```rust +env.storage() + .instance() + .set(&COUNTER, &count); +``` ### Managing Contract Data Lifetimes with `bump()` From 6e89a0a2634649d77a6638ff977c205451ca809d Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman Date: Wed, 25 Oct 2023 10:27:38 -0400 Subject: [PATCH 05/24] Add high_expiration_watermark argument to bump in incrementor --- docs/getting-started/storing-data.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/storing-data.mdx b/docs/getting-started/storing-data.mdx index 52daf285..e94d78fb 100644 --- a/docs/getting-started/storing-data.mdx +++ b/docs/getting-started/storing-data.mdx @@ -177,7 +177,7 @@ impl IncrementorContract { env.storage().instance().set(&COUNTER, &count); - env.storage().instance().bump(100); + env.storage().instance().bump(100, 100); count } From b5c05d03dcc09d9bf1749ec01e0cfc49cf31550c Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman Date: Thu, 26 Oct 2023 17:15:10 -0400 Subject: [PATCH 06/24] Move deploy-to-testnet after hello-world to allow the user to interact with their contract on testnet --- docs/getting-started/deploy-to-testnet.mdx | 153 +++++---------------- docs/getting-started/storing-data.mdx | 2 +- 2 files changed, 39 insertions(+), 116 deletions(-) diff --git a/docs/getting-started/deploy-to-testnet.mdx b/docs/getting-started/deploy-to-testnet.mdx index da6d384d..8aa2fd27 100644 --- a/docs/getting-started/deploy-to-testnet.mdx +++ b/docs/getting-started/deploy-to-testnet.mdx @@ -1,19 +1,19 @@ --- -sidebar_position: 4 -title: 3. Deploy to Testnet -description: Deploy smart contracts to a live test network. +sidebar_position: 3 +title: 2. Deploy to Testnet +description: Deploy smart contract to a live test network. --- - Deploy smart contracts to a live test network. + Deploy smart contract to a live test network. -You've built two simple contracts and run them locally in Sandbox. Now you can deploy them to a network. +To recap what we've done so far, in [Setup](setup): +- we set up our local environment to write Rust smart contracts +- installed the soroban-cli +- configured the soroban-cli to communicate with the Soroban Testnet via RPC +- and configured an identity to sign transactions -Soroban has a test network called Testnet that you can use to deploy and test your contracts. It's a live network, but it's not the same as the Stellar public network. It's a separate network that is used for development and testing, so you can't use it for production apps. But it's a great place to test your contracts before you deploy them to the public network. -## Configure Testnet in your CLI - -The first step toward deploying to testnet is to configure your CLI to use it. You can do this by running the following command: - - soroban config network add --global testnet \ - --rpc-url https://soroban-testnet.stellar.org:443 \ - --network-passphrase "Test SDF Network ; September 2015" - -Note the `--global`. This creates a file in your home folder's `/.config/soroban/network/testnet.toml` with the settings you specified. This -means that you can use the `--network testnet` flag in any Soroban CLI command to use this network. - -If you want project-specific network configurations, you can omit the `--global` flag, and the networks will be added to your working directory's `.soroban/network` folder instead. - -## Configure an Identity - -So far when using Sandbox, you haven't needed to consider _who_ is sending the transactions. But when you deploy to a network, you need to specify an identity that will be used to sign the transactions. - -Let's configure an identity called `alice`. You can use any name you want, but it might be nice to have some named identities that you can use for testing, such as [`alice`, `bob`, and `carol`](https://en.wikipedia.org/wiki/Alice_and_Bob). - - soroban config identity generate --global alice - -You can see the public key of `alice` with: - - soroban config identity address alice - -Like the Network configs, the `--global` means that the identity gets stored in `~/.config/soroban/identity/alice.toml`. You can omit the `--global` flag to store the identity in your project's `.soroban/identity` folder instead. - -All this did so far is generate a public/private keypair on your local machine. No network requests were made, and `alice` has no funds on Testnet. This means that you can't make any transactions with `alice` yet. - -To get `alice` some Testnet tokens, you'll need to use [Friendbot](https://developers.stellar.org/docs/fundamentals-and-concepts/testnet-and-pubnet#friendbot). All Stellar and Soroban test networks have a Friendbot that you can use to get some test tokens. The public Friendbot instance for Testnet lives at `https://friendbot.stellar.org`. Use it: - - curl "https://friendbot.stellar.org/?addr=$(soroban config identity address alice)" - -:::tip Command Expansion `$(…)` -This uses [command expansion](https://www.gnu.org/software/bash/manual/html_node/Command-Substitution.html), which only works with bash-compatible shells. If you are using Windows or some other shell, you will need to copy the output of `soroban config…` and paste it into the `curl` command, or figure out how command expansion works in your shell. -::: +In [Hello World](hello-world) we wrote a simple contract, and now we are ready to deploy that contract to Testnet, and interact with it. ## Deploy -Now you can deploy your Hello World contract: +To deploy your Hello Soroban contract, run the following command: - soroban contract deploy \ - --wasm target/wasm32-unknown-unknown/release/hello_soroban.wasm \ - --source alice \ - --network testnet +```bash +soroban contract deploy \ + --wasm target/wasm32-unknown-unknown/release/hello_soroban.wasm \ + --source alice \ + --network testnet +``` -This returns the ID of the contract, starting with a `C`. Let's put it somewhere semi-permanent so we can use it later. Copy that value and: +This returns the ID of the contract, starting with a `C`. Let's put it somewhere semi-permanent so we can use it later. Copy that value and put it into a file in the `.soroban` directory called `hello-id`. You may need to create the `.soroban` folder first with `mkdir .soroban`. ```bash -echo "C…" > .soroban/hello-id +echo "C...[your contract id here]" > .soroban/hello-id ``` -You may need to create the `.soroban` folder first with `mkdir .soroban`. - ## Interact -This should look a lot like interacting with the contract in the Sandbox, as you did in [1. Hello World](./hello-world.mdx). Only now the contract lives on the Testnet network, and the CLI is making [RPC](../reference/rpc.mdx) calls in the background. Let's try it: - -```bash -soroban contract invoke \ - --id $(cat .soroban/hello-id) \ - --source alice \ - --network testnet \ - -- \ - --help -``` +Using the code we wrote in [Write a Contract](hello-world#write-a-contract) and the resulting `.wasm` file we built in [Build](hello-world#build), run the following command to invoke the `hello` function. -This again makes use of command expansion `$(…)`; see the note about that above. +:::info +In the background, the CLI is making RPC calls. For information on that checkout out the [RPC](../reference/rpc.mdx) reference page. +::: -This looks a lot like interacting with the contract back in [1. Hello World](./hello-world.mdx), but rather than using a `--wasm` flag to point to a local wasm file, you now specify the `--network` and the `--source`. That's it! You can try calling `hello` as before: +Here we're setting the `to` argument to `RPC`. This again makes use of command expansion `$(…)`; see the note about that in the [Configure an Identity](setup#configure-an-identity) section of Setup. ```bash soroban contract invoke \ @@ -106,68 +67,30 @@ soroban contract invoke \ --to RPC ``` -## Two-step deployment - -It's worth knowing that `deploy` is actually a two-step process. - -1. Upload the contract bytes to the network. Soroban currently refers to this as _installing_ the contract—from the perspective of the blockchain itself, this is a reasonable metaphor. This uploads the bytes of the contract to the network, indexing it by its hash. This contract code can now be referenced by multiple contracts, which means they would have the exact same _behavior_ but separate storage state. - -2. Instantiate the contract. This actually creates what you probably think of as a Smart Contract. It makes a new contract ID, and associates it with the contract bytes that were uploaded in the previous step. - -You can run these two steps separately. Let's try it with the Incrementor contract: - -```bash -soroban contract install \ - --network testnet \ - --source alice \ - --wasm target/wasm32-unknown-unknown/release/incrementor.wasm -``` - -This returns the hash of the Wasm bytes. Now you can use `--wasm-hash` with `deploy` rather than `--wasm`. Let's also automatically pipe the returned contract ID into a file in the `.soroban` directory, so that when you search your command history and reuse the deploy command in the future, you don't forget that step (you might want to go back and do something similar with the Hello World deploy, too): - -```bash -soroban contract deploy \ - --wasm-hash [paste the output from the last command] \ - --source alice \ - --network testnet \ - > .soroban/incrementor-id -``` - -You can check that it saved the contract ID correctly: +The following output should appear. -```bash -cat .soroban/incrementor-id +```json +["Hello", "RPC"] ``` -Now you can interact with it over RPC like you did with the Hello World contract: - -```bash -soroban contract invoke \ - --id $(cat .soroban/incrementor-id) \ - --source alice \ - --network testnet \ - -- \ - increment -``` +:::info The `--` double-dash is required! +This is a general [CLI pattern](https://unix.stackexchange.com/questions/11376/what-does-double-dash-mean) used by other commands like [cargo run](https://doc.rust-lang.org/cargo/commands/cargo-run.html). Everything after the `--`, sometimes called [slop](https://github.com/clap-rs/clap/issues/971), is passed to a child process. In this case, `soroban contract invoke` builds an _implicit CLI_ on-the-fly for the `hello` method in your contract. It can do this because Soroban SDK embeds your contract's schema / interface types right in the `.wasm` file that gets deployed on-chain. You can also try: -## Run your own network/node + soroban contract invoke ... -- --help -Sometimes you'll need to run your own node: +and -- Production apps! It's a bad idea to rely on the public, Stellar-maintained Soroban RPC endpoint. Instead, you should run your own node, and point your app at that. -- When you need a network that differs from the version deployed to Testnet. + soroban contract invoke ... -- hello --help -The Soroban team maintains Docker containers that makes this as straightforward as possible. See the [RPC](../reference/rpc.mdx) reference for details. +::: ## Summary In this lesson, we learned how to: -- configure networks and identities using Soroban CLI -- fund accounts on test networks using Friendbot -- deploy contracts to a test network -- interact with deployed contracts +- deploy a contract to Testnet +- interact with a deployed contract You shouldn't have any changes to commit to git, because we didn't change any code in this lesson! -Up next, we'll use the deployed contracts to build a simple web app. +Next we'll add a new contract to this project, reorganizing the project as a multi-contract project using Cargo Workspaces. The new contract will show off a little bit of Soroban's storage capabilities. \ No newline at end of file diff --git a/docs/getting-started/storing-data.mdx b/docs/getting-started/storing-data.mdx index e94d78fb..66b2b863 100644 --- a/docs/getting-started/storing-data.mdx +++ b/docs/getting-started/storing-data.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 3 +sidebar_position: 4 title: 2. Storing Data description: Write a smart contract that stores and retrieves data. --- From 6bdaef3d9d58ef82a75a9cdb6b4ebc231245bf64 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman Date: Thu, 26 Oct 2023 17:43:47 -0400 Subject: [PATCH 07/24] Add Optimizing Builds to hello-world --- docs/getting-started/hello-world.mdx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/getting-started/hello-world.mdx b/docs/getting-started/hello-world.mdx index abf8defd..4204da43 100644 --- a/docs/getting-started/hello-world.mdx +++ b/docs/getting-started/hello-world.mdx @@ -340,9 +340,20 @@ target/wasm32-unknown-unknown/release/hello_soroban.wasm The `.wasm` file contains the logic of the contract, as well as the contract's [specification / interface types](../fundamentals-and-concepts/fully-typed-contracts.mdx), which can be imported into other contracts who wish to call it. This is the only artifact needed to deploy the contract, share the interface with others, or integration test against the contract. -:::tip Optimize Builds -To further optimize builds to be as small as possible, see [Optimizing Builds]. -::: +## Optimizing Builds + +Use `soroban contract optimize` to further minimize the size of the `.wasm`. First, re-install soroban-cli with the `opt` feature: + + cargo install --locked --version 20.0.0-rc2 soroban-cli --features opt + +Then build an optimized `.wasm` file: + + soroban contract optimize \ + --wasm target/wasm32-unknown-unknown/release/hello_soroban.wasm + +This will optimize and output a new `hello_soroban.optimized.wasm` file in the same location as the input `.wasm`. + +:::tip Building optimized contracts is only necessary when deploying to a network with fees or when analyzing and profiling a contract to get it as small as possible. If you're just starting out writing a contract, these steps are not necessary. See [Build] for details on how to build for development. ::: ## Commit to version control From 535c9bc35410e738738da14fa090ad123f7b2475 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman Date: Thu, 26 Oct 2023 17:44:29 -0400 Subject: [PATCH 08/24] Update storing-data to remove sandbox interation --- docs/getting-started/storing-data.mdx | 43 ++------------------------- 1 file changed, 3 insertions(+), 40 deletions(-) diff --git a/docs/getting-started/storing-data.mdx b/docs/getting-started/storing-data.mdx index 66b2b863..bb92b78f 100644 --- a/docs/getting-started/storing-data.mdx +++ b/docs/getting-started/storing-data.mdx @@ -1,6 +1,6 @@ --- sidebar_position: 4 -title: 2. Storing Data +title: 3. Storing Data description: Write a smart contract that stores and retrieves data. --- @@ -29,7 +29,7 @@ Now that we've built a basic Hello World example to see the rough structure of S This is going to follow along with the [increment example](https://github.com/stellar/soroban-examples/tree/v20.0.0-rc2/increment), which has a single function that increments an internal counter and returns the value. If you want to see a working example, [try it in GitPod](https://gitpod.io/#https://github.com/stellar/soroban-examples/tree/v20.0.0-rc2). -This tutorial assumes that you've already completed the previous steps in Getting Started: [Setup](./setup.mdx) and [Hello World](./hello-world.mdx). +This tutorial assumes that you've already completed the previous steps in Getting Started: [Setup](./setup.mdx), [Hello World](./hello-world.mdx), and [Deploy to Testnet](./deploy-to-testnet.mdx). ## Setting up a multi-contract project @@ -249,43 +249,6 @@ entry's lifetime is not periodically bumped, the entry will eventually reach the For now, it's worth knowing that there are three kinds of storage: `Persistent`, `Temporary`, and `Instance`. This contract only uses `Instance` storage: `env.storage().instance()`. Every time the counter is incremented, this storage gets bumped by 100 [ledgers](https://developers.stellar.org/docs/fundamentals-and-concepts/stellar-data-structures/ledgers), or about 500 seconds. -## Run on Sandbox - -Let's invoke this contract the same way we invoked the Hello World contract. We'll use `--id 2`, since we already used `--id 1` for the Hello World contract. For Sandbox, these values are stored in a `.soroban/ledger.json` file in the current directory; you can always remove this file to reset the ledger. - - soroban contract invoke \ - --wasm target/wasm32-unknown-unknown/release/incrementor.wasm \ - --id 2 \ - -- \ - increment - -You should see the following output: - - 1 - -Rerun the invoke with the `--footprint` option to view the [footprint](../fundamentals-and-concepts/persisting-data.mdx#footprints-and-parallel-contention) of the invocation, which is the ledger entries that the contract will read or write to. - - soroban contract invoke \ - --wasm target/wasm32-unknown-unknown/release/incrementor.wasm \ - --id 2 \ - --footprint \ - -- \ - increment - -You should see: - - Footprint: {"readOnly":[{"contractData":{"contractId":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],"key":{"static":"ledgerKeyContractCode"}}}],"readWrite":[{"contractData":{"contractId":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],"key":{"symbol":[67,79,85,78,84,69,82]}}}]} - -:::info Footprint formats are unstable -Soroban is a pre-release and at this time outputs footprints in an unstable JSON format. -::: - -Run it without the `--footprint` a few more times to watch the count change. - -Use `contract read` to inspect what the full storage looks like after a few runs. - - soroban contract read --id 1 --key COUNTER - ## Tests Open the `contracts/increment/src/test.rs` file and replace the contents with: @@ -348,4 +311,4 @@ git commit -m "add incrementor contract" In this section, we added a new contract to this project, reorganizing the project as a multi-contract project using Cargo Workspaces. The new contract made use of Soroban's storage capabilities to store and retrieve data. We also learned about the different kinds of storage and how to manage their lifetimes. -Next we'll learn to deploy contracts to Soroban's Testnet network and interact with them over RPC using the CLI. +Next we'll learn a bit more about deploying contracts to Soroban's Testnet network and interact with our incrementor contractC using the CLI. From be94c8853728f9b53bdd6498e34afddcbe57f0aa Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman Date: Thu, 26 Oct 2023 17:44:48 -0400 Subject: [PATCH 09/24] Add a deploy-incrementor-to-testnet step --- .../deploy-incrementor-to-testnet.mdx | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 docs/getting-started/deploy-incrementor-to-testnet.mdx diff --git a/docs/getting-started/deploy-incrementor-to-testnet.mdx b/docs/getting-started/deploy-incrementor-to-testnet.mdx new file mode 100644 index 00000000..29c3ab89 --- /dev/null +++ b/docs/getting-started/deploy-incrementor-to-testnet.mdx @@ -0,0 +1,87 @@ +--- +sidebar_position: 5 +title: 4. Deploy Incrementor to Testnet +description: Deploy the Incrementor contract to Testnet. +--- + + + Write a smart contract that stores and retrieves data. + + + + + + +## Two-step deployment + +It's worth knowing that `deploy` is actually a two-step process. + +1. Upload the contract bytes to the network. Soroban currently refers to this as _installing_ the contract—from the perspective of the blockchain itself, this is a reasonable metaphor. This uploads the bytes of the contract to the network, indexing it by its hash. This contract code can now be referenced by multiple contracts, which means they would have the exact same _behavior_ but separate storage state. + +2. Instantiate the contract. This actually creates what you probably think of as a Smart Contract. It makes a new contract ID, and associates it with the contract bytes that were uploaded in the previous step. + +You can run these two steps separately. Let's try it with the Incrementor contract: + +```bash +soroban contract install \ + --network testnet \ + --source alice \ + --wasm target/wasm32-unknown-unknown/release/incrementor.wasm +``` + +This returns the hash of the Wasm bytes. Now you can use `--wasm-hash` with `deploy` rather than `--wasm`. Let's also automatically pipe the returned contract ID into a file in the `.soroban` directory, so that when you search your command history and reuse the deploy command in the future, you don't forget that step (you might want to go back and do something similar with the Hello World deploy, too): + +```bash +soroban contract deploy \ + --wasm-hash [paste the output from the last command] \ + --source alice \ + --network testnet \ + > .soroban/incrementor-id +``` + +You can check that it saved the contract ID correctly: + +```bash +cat .soroban/incrementor-id +``` + +Now you can interact with it over RPC like you did with the Hello World contract: + +```bash +soroban contract invoke \ + --id $(cat .soroban/incrementor-id) \ + --source alice \ + --network testnet \ + -- \ + increment +``` + +You should see the following output: + +```bash +1 +``` + +Run it a few more times to watch the count change. + +## Run your own network/node + +Sometimes you'll need to run your own node: + +- Production apps! It's a bad idea to rely on the public, Stellar-maintained Soroban RPC endpoint. Instead, you should run your own node, and point your app at that. +- When you need a network that differs from the version deployed to Testnet. + +The Soroban team maintains Docker containers that makes this as straightforward as possible. See the [RPC](../reference/rpc.mdx) reference for details. + +:::info It's also possible to run a contract on a fully featured local network. See [the RPC Reference page](../reference/rpc.mdx) for more details. ::: + +Up next, we'll use the deployed contracts to build a simple web app. \ No newline at end of file From a3aaadb4755026dcc6bb499ea4ce3a7472a48e2d Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman Date: Thu, 26 Oct 2023 17:45:17 -0400 Subject: [PATCH 10/24] Update position of Create an App --- docs/getting-started/create-an-app.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/getting-started/create-an-app.mdx b/docs/getting-started/create-an-app.mdx index 6b8fcf4c..b039ee18 100644 --- a/docs/getting-started/create-an-app.mdx +++ b/docs/getting-started/create-an-app.mdx @@ -1,6 +1,6 @@ --- -sidebar_position: 5 -title: 4. Create an App +sidebar_position: 6 +title: 5. Create an App description: Make a frontend web app that interacts with your smart contracts. --- From 0d2cece6645fd911dba74d36b1f2c5ad1bd37332 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Wed, 1 Nov 2023 14:36:56 -0400 Subject: [PATCH 11/24] Apply suggestions from code review Co-authored-by: Chad Ostrowski <221614+chadoh@users.noreply.github.com> --- docs/getting-started/deploy-incrementor-to-testnet.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/deploy-incrementor-to-testnet.mdx b/docs/getting-started/deploy-incrementor-to-testnet.mdx index 29c3ab89..0b2be876 100644 --- a/docs/getting-started/deploy-incrementor-to-testnet.mdx +++ b/docs/getting-started/deploy-incrementor-to-testnet.mdx @@ -5,7 +5,7 @@ description: Deploy the Incrementor contract to Testnet. --- - Write a smart contract that stores and retrieves data. + 4. Deploy Incrementor to Testnet. Date: Wed, 1 Nov 2023 14:41:54 -0400 Subject: [PATCH 12/24] Apply suggestions from code review Co-authored-by: Chad Ostrowski <221614+chadoh@users.noreply.github.com> --- docs/getting-started/deploy-incrementor-to-testnet.mdx | 6 ++---- docs/getting-started/hello-world.mdx | 6 ++++-- docs/getting-started/storing-data.mdx | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/getting-started/deploy-incrementor-to-testnet.mdx b/docs/getting-started/deploy-incrementor-to-testnet.mdx index 0b2be876..db4f05d6 100644 --- a/docs/getting-started/deploy-incrementor-to-testnet.mdx +++ b/docs/getting-started/deploy-incrementor-to-testnet.mdx @@ -17,7 +17,7 @@ description: Deploy the Incrementor contract to Testnet. /> @@ -77,11 +77,9 @@ Run it a few more times to watch the count change. Sometimes you'll need to run your own node: -- Production apps! It's a bad idea to rely on the public, Stellar-maintained Soroban RPC endpoint. Instead, you should run your own node, and point your app at that. +- Production apps! Stellar maintains public test RPC nodes for Testnet and Futurenet, but not for Mainnet. Instead, you will need to run your own node, and point your app at that. If you want to use a software-as-a-service platform for this, [various providers](../reference/rpc-list.mdx) are available. - When you need a network that differs from the version deployed to Testnet. The Soroban team maintains Docker containers that makes this as straightforward as possible. See the [RPC](../reference/rpc.mdx) reference for details. -:::info It's also possible to run a contract on a fully featured local network. See [the RPC Reference page](../reference/rpc.mdx) for more details. ::: - Up next, we'll use the deployed contracts to build a simple web app. \ No newline at end of file diff --git a/docs/getting-started/hello-world.mdx b/docs/getting-started/hello-world.mdx index 4204da43..b34be2e9 100644 --- a/docs/getting-started/hello-world.mdx +++ b/docs/getting-started/hello-world.mdx @@ -311,7 +311,7 @@ Try changing the values in the test to see how it works. :::note -The first time you run the tests you may see output in the terminal of cargo compiling all the dependencies before running the tests. Like this: +The first time you run the tests you may see output in the terminal of cargo compiling all the dependencies before running the tests. ::: @@ -353,7 +353,9 @@ Then build an optimized `.wasm` file: This will optimize and output a new `hello_soroban.optimized.wasm` file in the same location as the input `.wasm`. -:::tip Building optimized contracts is only necessary when deploying to a network with fees or when analyzing and profiling a contract to get it as small as possible. If you're just starting out writing a contract, these steps are not necessary. See [Build] for details on how to build for development. ::: +:::tip +Building optimized contracts is only necessary when deploying to a network with fees or when analyzing and profiling a contract to get it as small as possible. If you're just starting out writing a contract, these steps are not necessary. See [Build] for details on how to build for development. +::: ## Commit to version control diff --git a/docs/getting-started/storing-data.mdx b/docs/getting-started/storing-data.mdx index bb92b78f..d5b07b27 100644 --- a/docs/getting-started/storing-data.mdx +++ b/docs/getting-started/storing-data.mdx @@ -204,7 +204,7 @@ Follow along in your `contracts/incrementor/src/lib.rs` file. ### Contract Data Keys Contract data is associated with a key. The key can be used at -a later time to lookup the value. +a later time to look up the value. `Symbol` is a short (up to 32 characters long) string type with limited character space (only `a-zA-z0-9_` characters are allowed). Identifiers like From eedfa146128c1a348c498b956f23ffc77c1387a9 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman Date: Wed, 1 Nov 2023 14:09:20 -0400 Subject: [PATCH 13/24] Update create-an-app.mdx --- docs/getting-started/create-an-app.mdx | 131 +++++++++---------------- 1 file changed, 44 insertions(+), 87 deletions(-) diff --git a/docs/getting-started/create-an-app.mdx b/docs/getting-started/create-an-app.mdx index b039ee18..b6a9210c 100644 --- a/docs/getting-started/create-an-app.mdx +++ b/docs/getting-started/create-an-app.mdx @@ -16,7 +16,7 @@ Let's get started. You're going to need [Node.js](https://nodejs.org/en/download/package-manager/) v18.14.1 or greater. If you haven't yet, install it now. -Then we want to initialize the current directory, `soroban-tutorial`, as an Astro project, but Astro doesn't like that. It wants to create a new directory. So let's go ahead and do that, then move all the contents of the new directory into their parent directory: +Then we want to initialize the current directory, `soroban-tutorial`, as an Astro project, but Astro doesn't like that. It wants to create a new directory. So let's go ahead and do that, then move all the contents of the new directory into it's parent directory. From the original `soroban-tutorial` directory, run: ```bash npm create astro@4.0.1 soroban-tutorial -- --template basics --install --no-git --typescript strictest @@ -43,74 +43,27 @@ git commit -m "Initialize Astro project" Before we even open the new frontend files, let's generate an NPM package for the Hello World contract. This is our suggested way to interact with contracts from frontends. These generated libraries work with any JavaScript project (not a specific UI like React), and make it easy to work with some of the trickiest bits of Soroban, like encoding [XDR](https://soroban.stellar.org/docs/fundamentals-and-concepts/fully-typed-contracts). -This is going to use the CLI command `soroban contract bindings typescript`. Unfortunately, the version of `bindings typescript` packaged with CLI v0.9.4 has some bugs, and the fix can't be released yet because the `main` branch of the CLI is broken with futurenet. To work around this, we're going to install a pinned version of the CLI from a fixed-and-still-futurenet-compatible fork. Create a directory called `.cargo` (with the dot; it's a hidden folder): - - mkdir .cargo - -Then add one file to it, `config.toml`. Paste these contents into it: - -```toml -[alias] # command aliases -install_soroban = "install --git https://github.com/AhaLabs/soroban-tools --branch smartdeploy --root ./target soroban-cli --debug" -``` - -Now install the pinned version by using the alias you just set up: - - cargo install_soroban - -This will take a couple minutes; it builds a local version from the `smartdeploy` branch into `target/bin/soroban`. Once it's done, check that it worked: - -```bash -ls target/bin/soroban -``` - -And check the version info of this `smartdeploy` install: - -```bash -./target/bin/soroban --version -``` - -If you want, you can add `./target/bin/` [to your PATH](https://unix.stackexchange.com/questions/26047/how-to-correctly-add-a-path-to-path), so that anytime you're in a project with a locally-installed version of `soroban` in its `target/bin` directory, you'll automatically use it (rather than your global version) when you just type `soroban`. For this project, let's also add a `soroban` script in the root of the project that automatically installs the pinned version if it's not there, then uses it. This will make it easier for your collaborators to work with you. Create a file called just `soroban` and paste the following contents: +This is going to use the CLI command `soroban contract bindings typescript`: ```bash -#!/bin/bash -if [ ! -f ./target/bin/soroban ]; then - cargo install_soroban -fi -./target/bin/soroban "$@" # `$@` expands to all arguments passed to this script -``` - -Make sure it's executable: - -```bash -chmod +x soroban -``` - -Ok, now you can finally generate that NPM package: - -```bash -./soroban contract bindings typescript \ +soroban contract bindings typescript \ --network testnet \ --contract-id $(cat .soroban/hello-id) \ - --output-dir hello-soroban-client + --output-dir node_modules/hello-soroban-client ``` We attempt to keep the code in these generated libraries readable, so go ahead and look around. Open up the new `hello-soroban-client` directory in your editor. If you've built or contributed to Node projects, it will all look familiar. You'll see a `package.json` file, a `src` directory, a `tsconfig.json`, and even a README. The README is a great place to start. Go ahead and give it a read. -As it says, when using local libraries, we've had the [most success](https://github.com/stellar/soroban-example-dapp/pull/117#discussion_r1232873560) when generating them directly into the `node_modules` folder, and leaving them out of the `dependencies` section. Yes, this is surprising, but it works the best. Go ahead and move this new library there: - -```bash -mv hello-soroban-client node_modules -``` +As it says, when using local libraries, we've had the [most success](https://github.com/stellar/soroban-example-dapp/pull/117#discussion_r1232873560) when generating them directly into the `node_modules` folder, and leaving them out of the `dependencies` section. Yes, this is surprising, but it works the best. -And then let's update the `package.json` in your `soroban-tutorial` project with a `postinstall` script to make sure the generated library stays up-to-date: +Let's update the `package.json` in your `soroban-tutorial` project with a `postinstall` script to make sure the generated library stays up-to-date: -```diff +```diff title="package.json" "scripts": { ... - "astro": "astro" + "astro": "astro", -+ "postinstall": "./soroban contract bindings typescript --network testnet --contract-id $(cat .soroban/hello-id) --output-dir node_modules/hello-soroban-client" ++ "postinstall": "soroban contract bindings typescript --network testnet --contract-id $(cat .soroban/hello-id) --output-dir node_modules/hello-soroban-client" } ``` @@ -118,7 +71,7 @@ And then let's update the `package.json` in your `soroban-tutorial` project with Now let's open up `src/pages/index.astro` and add some code to call the contract. We'll start by importing the generated library: -```diff +```diff title="src/pages/index.astro" --- import Layout from '../layouts/Layout.astro'; import Card from '../components/Card.astro'; @@ -135,7 +88,7 @@ Now let's open up `src/pages/index.astro` and add some code to call the contract Then find the `

` tag and replace its contents with the greeting: -```diff +```diff title="src/pages/index.astro" -

Welcome to Astro

+

{greeting.join(' ')}

``` @@ -177,10 +130,11 @@ While `hello` is a simple view-only/read method, `increment` changes on-chain st The way signing works in a browser is with a _wallet_. Wallets can be web apps, browser extensions, standalone apps, or even separate hardware devices. -Right now, the wallet that best supports Soroban is -[Freighter](../reference/freighter.mdx). It's a Chrome extension. Go ahead and [install it now](https://freighter.app). +Right now, the wallet that best supports Soroban is [Freighter](../reference/freighter.mdx). It's a Chrome extension. Go ahead and [install it now](https://freighter.app). -Once it's installed, open it up, go to Settings (the gear icon) → Preferences and toggle the switch to Enable Experimental Mode. Then go back to its home screen and select "Future Net" from the top-right dropdown. Finally, if it shows the message that your Stellar address is not funded, go ahead and click the "Fund with Friendbot" button. +Once it's installed, open it up by clicking the extension icon. If this is your first time using Freighter, you will need to create a new wallet. Go through the prompts to create a password and save your recovery passphrase. + +Go to Settings (the gear icon) → Preferences and toggle the switch to Enable Experimental Mode. Then go back to its home screen and select "Test Net" from the top-right dropdown. Finally, if it shows the message that your Stellar address is not funded, go ahead and click the "Fund with Friendbot" button. Now you're all set up to use Freighter as a user, and you can add it to your app. @@ -202,7 +156,7 @@ npm run postinstall Now let's add a new component to the `src/components` directory called `ConnectFreighter.astro` with the following contents: -```html +```html title="src/components/ConnectFreighter.astro"
@@ -270,7 +224,7 @@ Before we add this to our index page, let's make the buttons look better. Open ` -```css +```css title="layouts/Layout.astro" button { border: 1px solid rgb(var(--accent)); background-color: #23262d; @@ -302,7 +256,7 @@ This copies the styles from the `Card` components that Astro included in the tem Now we can import the component in the frontmatter of `pages/index.astro`: -```diff +```diff title="pages/index.astro" --- import Layout from '../layouts/Layout.astro'; import Card from '../components/Card.astro'; @@ -313,7 +267,7 @@ Now we can import the component in the frontmatter of `pages/index.astro`: And add it right below the `

`: -```diff +```diff title="pages/index.astro"

{greeting.join(' ')}

+ ``` @@ -330,11 +284,11 @@ Now you're ready to sign the call to `increment`! ### Call `increment` -We're going to generate a contract client for the incrementor contract with a similar command to the one we used before. Let's move the `hello` bindings generation to its own script, add this one, and call them both from `postinstall` using a double ampersand ([&&](https://stackoverflow.com/a/25669618/249801)): +We're going to generate a contract client for the incrementor contract with a similar command to the one we used before. Let's move the `hello` bindings generation to its own script, add one for incrementor, and call them both from `postinstall` using a double ampersand ([&&](https://stackoverflow.com/a/25669618/249801)): -```json -"bindings:hello": "./soroban contract bindings typescript --network testnet --contract-id $(cat .soroban/hello-id) --output-dir node_modules/hello-soroban-client", -"bindings:incrementor": "./soroban contract bindings typescript --network testnet --contract-id $(cat .soroban/incrementor-id) --output-dir node_modules/incrementor-client", +```json title="package.json" +"bindings:hello": "soroban contract bindings typescript --network testnet --contract-id $(cat .soroban/hello-id) --output-dir node_modules/hello-soroban-client", +"bindings:incrementor": "soroban contract bindings typescript --network testnet --contract-id $(cat .soroban/incrementor-id) --output-dir node_modules/incrementor-client", "postinstall": "npm run bindings:hello && npm run bindings:incrementor" ``` @@ -346,14 +300,19 @@ npm i Now we can import from `incrementor-client` and start using it. We'll again create a new Astro component. Create a new file at `src/components/Counter.astro` with the following contents: -```html +```html title="src/components/Counter.astro" Incrementor
Current value: ???