Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update hello-world contract #1239

Closed
wants to merge 9 commits into from
30 changes: 21 additions & 9 deletions docs/build/apps/dapp-frontend.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ 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.

We want to initialize our current project as an Astro project. To do this, we can clone a template. You can find Soroban templates on GitHub by [searching for repositories that start with "soroban-template-"](https://github.com/search?q=%22soroban-template-%22&type=repositories). For this tutorial, we'll use [stellar/soroban-template-astro](https://github.com/stellar/soroban-template-astro). We'll also use a tool called [degit](https://github.com/Rich-Harris/degit) to clone the template without its git history. This will allow us to set it up as our own git project.
We want to create an Astro project with the contracts from the previous lesson. To do this, we can clone a template. You can find Soroban templates on GitHub by [searching for repositories that start with "soroban-template-"](https://github.com/search?q=%22soroban-template-%22&type=repositories). For this tutorial, we'll use [stellar/soroban-template-astro](https://github.com/stellar/soroban-template-astro). We'll also use a tool called [degit](https://github.com/Rich-Harris/degit) to clone the template without its git history. This will allow us to set it up as our own git project.

Since you have `node` and its package manager `npm` installed, you also have `npx`. Make sure you're no longer in your `soroban-hello-world` directory and then run:
Since you have `node` and its package manager `npm` installed, you also have `npx`.

```
We're going to create a new project directory with this template to make things easier in this tutorial, so make sure you're no longer in your `soroban-hello-world` directory and then run:

```sh
npx degit stellar/soroban-template-astro first-soroban-app
cd first-soroban-app
git init
Expand Down Expand Up @@ -56,7 +58,11 @@ This project has the following directory structure, which we'll go over in more
└── tsconfig.json
```

The `contracts` are the same ones you walked through in the previous steps of the tutorial.
The `contracts` are the same ones you walked through in the previous steps of the tutorial. Since we already deployed these contracts with aliases, we can reuse the generated contract ID files by copying them from the `soroban-hello-world/.stellar` directory into this project:

```sh
cp -R ../soroban-hello-world/.stellar/ .stellar
```

## Generate an NPM package for the Hello World contract

Expand All @@ -67,17 +73,23 @@ This is going to use the CLI command `stellar contract bindings typescript`:
```bash
stellar contract bindings typescript \
--network testnet \
--contract-id $(cat .stellar/contract-ids/hello_world.txt) \
--contract-id hello_world \
--output-dir packages/hello_world
```

:::tip

Notice that we were able to use the contract alias, `hello_world`, in place of the contract id!

:::

This project is set up as an NPM Workspace, and so the `hello_world` client library was generated in the `packages` directory at `packages/hello_world`.

We attempt to keep the code in these generated libraries readable, so go ahead and look around. Open up the new `packages/hello_world` 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.

## Generate an NPM package for the Increment contract

Though we can run `soroban contract bindings typescript` for each of our contracts individually, the [soroban-template-astro](https://github.com/stellar/soroban-astro-template) that we used as our template includes a very handy `initialize.js` script that will handle this for all of the contracts in our `contracts` directory.
Though we can run `soroban contract bindings typescript` for each of our contracts individually, the [soroban-template-astro](https://github.com/stellar/soroban-astro-template) project that we used as our template includes a very handy `initialize.js` script that will handle this for all of the contracts in our `contracts` directory.

In addition to generating the NPM packages, `initialize.js` will also:

Expand Down Expand Up @@ -408,7 +420,7 @@ Now you're ready to sign the call to `increment`!

### Call `increment`

Now we can import the `increment` contract client from `soroban_increment_contract` 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:
Now we can import the `increment` contract client from `contracts/increment.ts` 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 title="src/components/Counter.astro"
<strong>Incrementor</strong><br />
Expand All @@ -417,8 +429,8 @@ Current value: <strong id="current-value" aria-live="polite">???</strong><br />
<button data-increment aria-controls="current-value">Increment</button>

<script>
import { getPublicKey, kit } from "../stellar-wallets-kit";
import incrementor from "../contracts/soroban_increment_contract";
import { getPublicKey, signTransaction } from "../stellar-wallets-kit";
import incrementor from "../contracts/increment";
const button = document.querySelector(
"[data-increment]",
) as HTMLButtonElement;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ You can run these two steps separately. Let's try it with the Increment contract
stellar contract install \
--network testnet \
--source alice \
--wasm target/wasm32-unknown-unknown/release/soroban_increment_contract.wasm
--wasm target/wasm32-unknown-unknown/release/increment.wasm
```

</TabItem>
Expand All @@ -57,7 +57,7 @@ stellar contract install `

</Tabs>

This returns the hash of the Wasm bytes, like `6ddb28e0980f643bb97350f7e3bacb0ff1fe74d846c6d4f2c625e766210fbb5b`. Now you can use `--wasm-hash` with `deploy` rather than `--wasm`:
This returns the hash of the Wasm bytes, like `6ddb28e0980f643bb97350f7e3bacb0ff1fe74d846c6d4f2c625e766210fbb5b`. Now you can use `--wasm-hash` with `deploy` rather than `--wasm. Make sure to replace the example wasm hash with your own.

<Tabs groupId="platform" defaultValue={getPlatform()}>

Expand All @@ -67,7 +67,8 @@ This returns the hash of the Wasm bytes, like `6ddb28e0980f643bb97350f7e3bacb0ff
stellar contract deploy \
--wasm-hash 6ddb28e0980f643bb97350f7e3bacb0ff1fe74d846c6d4f2c625e766210fbb5b \
--source alice \
--network testnet
--network testnet \
--alias increment
```

</TabItem>
Expand All @@ -78,7 +79,8 @@ stellar contract deploy \
stellar contract deploy `
--wasm-hash 6ddb28e0980f643bb97350f7e3bacb0ff1fe74d846c6d4f2c625e766210fbb5b `
--source alice `
--network testnet
--network testnet `
--alias increment
```

</TabItem>
Expand Down
12 changes: 10 additions & 2 deletions docs/build/smart-contracts/getting-started/deploy-to-testnet.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ To deploy your HelloWorld contract, run the following command:
stellar contract deploy \
--wasm target/wasm32-unknown-unknown/release/hello_world.wasm \
--source alice \
--network testnet
--network testnet \
--alias hello_world
```

</TabItem>
Expand All @@ -52,7 +53,8 @@ stellar contract deploy \
stellar contract deploy `
--wasm target/wasm32-unknown-unknown/release/hello_world.wasm `
--source alice `
--network testnet
--network testnet `
--alias hello_world
```

</TabItem>
Expand All @@ -61,6 +63,12 @@ stellar contract deploy `

This returns the contract's id, starting with a `C`. In this example, we're going to use `CACDYF3CYMJEJTIVFESQYZTN67GO2R5D5IUABTCUG3HXQSRXCSOROBAN`, so replace it with your actual contract id.

:::tip

We used the `--alias` flag in this deploy command which will create a `.stellar/contract-ids/hello_world.json` file that maps the alias `hello_world` to the contract id and network. This allows us to refer to this contract as its alias instead the contract id.

:::

## Interact

Using the code we wrote in [Write a Contract](./hello-world.mdx#contract-source-code) and the resulting `.wasm` file we built in [Build](hello-world.mdx#build-the-contract), run the following command to invoke the `hello` function.
Expand Down
63 changes: 36 additions & 27 deletions docs/build/smart-contracts/getting-started/hello-world.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@ The `init` command will create a Rust workspace project, using the recommended s
├── Cargo.toml
├── README.md
└── contracts
└── hello_world
├── Cargo.toml
└── src
├── lib.rs
└── test.rs
├── hello_world
│   ├── Cargo.toml
│   ├── Makefile
│   ├── src
│   │   ├── lib.rs
│   │   └── test.rs
```

### Cargo.toml
Expand All @@ -60,7 +61,7 @@ members = [
]

[workspace.dependencies]
soroban-sdk = "20.3.2"
soroban-sdk = "22"
```

:::info
Expand Down Expand Up @@ -150,14 +151,14 @@ All contracts should begin with `#![no_std]` to ensure that the Rust standard li
The contract imports the types and macros that it needs from the `soroban-sdk` crate.

```rust
use soroban_sdk::{contract, contractimpl, symbol_short, vec, Env, Symbol, Vec};
use soroban_sdk::{contract, contractimpl, vec, Env, String, Vec};
```

Many of the types available in typical Rust programs, such as `std::vec::Vec`, are not available, as there is no allocator and no heap memory in Soroban contracts. The `soroban-sdk` provides a variety of types like `Vec`, `Map`, `Bytes`, `BytesN`, `Symbol`, that all utilize the Soroban environment's memory and native capabilities. Primitive values like `u128`, `i128`, `u64`, `i64`, `u32`, `i32`, and `bool` can also be used. Floats and floating point math are not supported.

Contract inputs must not be references.

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.
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.

```rust
#[contract]
Expand All @@ -169,8 +170,8 @@ Contract functions are defined within an `impl` block for the struct, which is a
```rust
#[contractimpl]
impl HelloContract {
pub fn hello(env: Env, to: Symbol) -> Vec<Symbol> {
vec![&env, symbol_short!("Hello"), to]
pub fn hello(env: Env, to: String) -> Vec<String> {
vec![&env, String::from_str(&env, "Hello"), to]
}
}
```
Expand All @@ -179,15 +180,15 @@ Putting those pieces together a simple contract looks like this.

```rust title="contracts/hello_world/src/lib.rs"
#![no_std]
use soroban_sdk::{contract, contractimpl, symbol_short, vec, Env, Symbol, Vec};
use soroban_sdk::{contract, contractimpl, vec, Env, String, Vec};

#[contract]
pub struct HelloContract;

#[contractimpl]
impl HelloContract {
pub fn hello(env: Env, to: Symbol) -> Vec<Symbol> {
vec![&env, symbol_short!("Hello"), to]
pub fn hello(env: Env, to: String) -> Vec<String> {
vec![&env, String::from_str(&env, "Hello"), to]
}
}

Expand All @@ -200,22 +201,22 @@ Note the `mod test` line at the bottom, this will tell Rust to compile and run t

Writing tests for Soroban contracts involves writing Rust code using the test facilities and toolchain that you'd use for testing any Rust code.

Given our HelloContract, a simple test will look like this.
Given our `HelloContract`, a simple test will look like this.

<Tabs>
<TabItem value="lib.rs" label="contracts/hello_world/src/lib.rs">

```rust
#![no_std]
use soroban_sdk::{contract, contractimpl, symbol_short, vec, Env, Symbol, Vec};
use soroban_sdk::{contract, contractimpl, vec, Env, String, Vec};

#[contract]
pub struct HelloContract;

#[contractimpl]
impl HelloContract {
pub fn hello(env: Env, to: Symbol) -> Vec<Symbol> {
vec![&env, symbol_short!("Hello"), to]
pub fn hello(env: Env, to: String) -> Vec<String> {
vec![&env, String::from_str(&env, "Hello"), to]
}
}

Expand All @@ -229,18 +230,22 @@ mod test;
#![cfg(test)]

use super::*;
use soroban_sdk::{symbol_short, vec, Env};
use soroban_sdk::{vec, Env, String};

#[test]
fn test() {
let env = Env::default();
let contract_id = env.register_contract(None, HelloContract);
let contract_id = env.register(HelloContract, ());
let client = HelloContractClient::new(&env, &contract_id);

let words = client.hello(&symbol_short!("Dev"));
let words = client.hello(&String::from_str(&env, "Dev"));
assert_eq!(
words,
vec![&env, symbol_short!("Hello"), symbol_short!("Dev"),]
vec![
&env,
String::from_str(&env, "Hello"),
String::from_str(&env, "Dev"),
]
);
}
```
Expand All @@ -257,23 +262,27 @@ let env = Env::default();
The contract is registered with the environment using the contract type. Contracts can specify a fixed contract ID as the first argument, or provide `None` and one will be generated.

```rust
let contract_id = env.register_contract(None, Contract);
let contract_id = env.register(HelloContract, ());
```

All public functions within an `impl` block that is annotated with the `#[contractimpl]` attribute have a corresponding function generated in a generated client type. The client type will be named the same as the contract type with `Client` appended. For example, in our contract the contract type is `HelloContract`, and the client is named `HelloContractClient`.

```rust
let client = HelloContractClient::new(&env, &contract_id);
let words = client.hello(&symbol_short!("Dev"));
let words = client.hello(&String::from_str(&env, "Dev"));
```

The values returned by functions can be asserted on:

```rust
assert_eq!(
words,
vec![&env, symbol_short!("Hello"), symbol_short!("Dev"),]
);
assert_eq!(
words,
vec![
&env,
String::from_str(&env, "Hello"),
String::from_str(&env, "Dev"),
]
);
```

## Run the Tests
Expand Down
Loading