Skip to content
This repository has been archived by the owner on Aug 21, 2024. It is now read-only.

Commit

Permalink
Updated example docs for Auth Next. (#306)
Browse files Browse the repository at this point in the history
* Updated example docs for Auth Next.
  • Loading branch information
dmkozh authored Feb 10, 2023
1 parent bdb3030 commit 603c4b8
Show file tree
Hide file tree
Showing 20 changed files with 846 additions and 894 deletions.
10 changes: 5 additions & 5 deletions docs/getting-started/storing-data.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ title: 2. Storing Data
The [increment example] demonstrates how to write a simple contract that stores data, with a single function that increments an internal counter and returns the value.

[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)][oigp]
[oigp]: https://gitpod.io/#https://github.com/stellar/soroban-examples/tree/v0.4.2
[oigp]: https://gitpod.io/#https://github.com/stellar/soroban-examples/tree/v0.5.0

[increment example]: https://github.com/stellar/soroban-examples/tree/v0.4.2/increment
[increment example]: https://github.com/stellar/soroban-examples/tree/v0.5.0/increment

## Run the Example

First go through the [Setup] process to get your development environment configured, then clone the `v0.4.2` tag of `soroban-examples` repository:
First go through the [Setup] process to get your development environment configured, then clone the `v0.5.0` tag of `soroban-examples` repository:

[Setup]: setup.mdx

```
git clone -b v0.4.2 https://github.com/stellar/soroban-examples
git clone -b v0.5.0 https://github.com/stellar/soroban-examples
```

Or, skip the development environment setup and open this example in [Gitpod][oigp].
Expand Down Expand Up @@ -62,7 +62,7 @@ impl IncrementContract {
}
```

Ref: https://github.com/stellar/soroban-examples/tree/v0.4.2/increment
Ref: https://github.com/stellar/soroban-examples/tree/v0.5.0/increment

## How it Works

Expand Down
12 changes: 6 additions & 6 deletions docs/how-to-guides/alloc.mdx
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
---
sidebar_position: 15
sidebar_position: 10
title: Allocator
---

The [allocator example] demonstrates how to utilize the allocator feature when writing a contract.

[allocator example]: https://github.com/stellar/soroban-examples/tree/v0.4.2/alloc
[allocator example]: https://github.com/stellar/soroban-examples/tree/v0.5.0/alloc

[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)][oigp]
[oigp]: https://gitpod.io/#https://github.com/stellar/soroban-examples/tree/v0.4.2
[oigp]: https://gitpod.io/#https://github.com/stellar/soroban-examples/tree/v0.5.0

The `soroban-sdk` crate provides a lightweight bump-pointer allocator which can be used to emulate heap memory allocation in a WASM smart contract.

## Run the Example

First go through the [Setup] process to get your development environment configured, then clone the `v0.4.2` tag of `soroban-examples` repository:
First go through the [Setup] process to get your development environment configured, then clone the `v0.5.0` tag of `soroban-examples` repository:

[setup]: ../getting-started/setup.mdx

```
git clone -b v0.4.2 https://github.com/stellar/soroban-examples
git clone -b v0.5.0 https://github.com/stellar/soroban-examples
```

Or, skip the development environment setup and open this example in [Gitpod][oigp].
Expand Down Expand Up @@ -77,7 +77,7 @@ impl AllocContract {
}
```

Ref: https://github.com/stellar/soroban-examples/tree/v0.4.2/alloc
Ref: https://github.com/stellar/soroban-examples/tree/v0.5.0/alloc

## How it Works

Expand Down
20 changes: 20 additions & 0 deletions docs/how-to-guides/atomic-multi-swap.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
sidebar_position: 12
title: Batched Atomic Swaps
---

The [atomic swap batching example] swaps a pair of tokens between the two groups
of users that authorized the `swap` operation from the [Atomic Swap] example.

This contract basically batches the multiple swaps while following some simple
rules to match the swap participants.

Follow the comments in the code for more information.

[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)][oigp]

[oigp]: https://gitpod.io/#https://github.com/stellar/soroban-examples/tree/v0.5.0

[Atomic Swap]: atomic-swap.mdx

[atomic swap batching example]: https://github.com/stellar/soroban-examples/tree/v0.5.0/atomic_multiswap
233 changes: 233 additions & 0 deletions docs/how-to-guides/atomic-swap.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
---
sidebar_position: 11
title: Atomic Swap
---

The [atomic swap example] swaps two tokens between two authorized parties
atomically while following the limits they set.

This is example demonstrates advanced usage of Soroban auth framework and
assumes the reader is familiar with the [auth example](auth.mdx) and with
Soroban token usage.

[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)][oigp]

[oigp]: https://gitpod.io/#https://github.com/stellar/soroban-examples/tree/v0.5.0

[atomic swap example]: https://github.com/stellar/soroban-examples/tree/v0.5.0/atomic_swap

## Run the Example

First go through the [Setup] process to get your development environment
configured, then clone the `v0.5.0` tag of `soroban-examples` repository:

[Setup]: ../getting-started/setup.mdx

```
git clone -b v0.5.0 https://github.com/stellar/soroban-examples
```

Or, skip the development environment setup and open this example in [Gitpod][oigp].

To run the tests for the example use `cargo test`.

```
cargo test -p soroban-atomic-swap
```

You should see the output:

```
running 1 test
test test::test_atomic_swap ... ok
```

## Code

```rust title="atomic_swap/src/lib.rs"
mod token {
soroban_sdk::contractimport!(file = "../soroban_token_spec.wasm");
}

pub struct AtomicSwapContract;

#[contractimpl]
impl AtomicSwapContract {
// Swap token A for token B atomically. Settle for the minimum requested price
// for each party (this is an arbitrary choice to demonstrate the usage of
// allowance; full amounts could be swapped as well).
pub fn swap(
env: Env,
a: Address,
b: Address,
token_a: BytesN<32>,
token_b: BytesN<32>,
amount_a: i128,
min_b_for_a: i128,
amount_b: i128,
min_a_for_b: i128,
) {
// Verify preconditions on the minimum price for both parties.
if amount_b < min_b_for_a {
panic!("not enough token B for token A");
}
if amount_a < min_a_for_b {
panic!("not enough token A for token B");
}
// Require authorization for a subset of arguments specific to a party.
// Notice, that arguments are symmetric - there is no difference between
// `a` and `b` in the call and hence their signatures can be used
// either for `a` or for `b` role.
a.require_auth_for_args(
(token_a.clone(), token_b.clone(), amount_a, min_b_for_a).into_val(&env),
);
b.require_auth_for_args(
(token_b.clone(), token_a.clone(), amount_b, min_a_for_b).into_val(&env),
);

// Perform the swap via two token transfers.
move_token(&env, token_a, &a, &b, amount_a, min_a_for_b);
move_token(&env, token_b, &b, &a, amount_b, min_b_for_a);
}
}

fn move_token(
env: &Env,
token: BytesN<32>,
from: &Address,
to: &Address,
approve_amount: i128,
xfer_amount: i128,
) {
let token = token::Client::new(&env, &token);
let contract_address = env.current_contract_address();
// This call needs to be authorized by `from` address. Since it increases
// the allowance on behalf of the contract, `from` doesn't need to know `to`
// at the signature time.
token.incr_allow(&from, &contract_address, &approve_amount);
token.xfer_from(&contract_address, &from, to, &xfer_amount);
}
```

Ref: https://github.com/stellar/soroban-examples/tree/v0.5.0/atomic_swap

## How it Works

The example contract requires two `Address`-es to authorize their parts of the
swap operation: one `Address` wants to sell a given amount of token A for
token B at a given price and another `Address` wants to sell token B for token
A at a given price. The contract swaps the tokens atomically, but only if the
requested minimum price is respected for both parties.

Open the `atomic_swap/src/lib.rs` file or see the code above to follow along.

### Swap authorization

```rust
...
a.require_auth_for_args(
(token_a.clone(), token_b.clone(), amount_a, min_b_for_a).into_val(&env),
);
b.require_auth_for_args(
(token_b.clone(), token_a.clone(), amount_b, min_a_for_b).into_val(&env),
);
...
```

Authorization of `swap` function leverages `require_auth_for_args` Soroban host
function. Both `a` and `b` need to authorize symmetric arguments: token they
sell, token they buy, amount of token they sell, minimum amount of token they
want to recieve. This means that `a` and `b` can be freely exchanged in the
invocation arguments (as long as the respective arguments are changed too).

### Moving the tokens

```rust
...
// Perform the swap via two token transfers.
move_token(&env, token_a, &a, &b, amount_a, min_a_for_b);
move_token(&env, token_b, &b, &a, amount_b, min_b_for_a);
...
fn move_token(
env: &Env,
token: BytesN<32>,
from: &Address,
to: &Address,
approve_amount: i128,
xfer_amount: i128,
) {
let token = token::Client::new(&env, &token);
let contract_address = env.current_contract_address();
// This call needs to be authorized by `from` address. Since it increases
// the allowance on behalf of the contract, `from` doesn't need to know `to`
// at the signature time.
token.incr_allow(&from, &contract_address, &approve_amount);
token.xfer_from(&contract_address, &from, to, &xfer_amount);
}
```

The swap itself is implemented via two token moves: from `a` to `b` and from `b`
to `a`. The token move is implemented via allowance: the users don't need to
know each other in order to perform the swap and instead they authorize the swap
contract to spend the necessary amount of token on their behalf via
`incr_allow`. Soroban auth framework makes sure that the `incr_allow` signatures
would have the proper context and they won't be usable outside of the `swap`
contract invocation.

### Tests

Open the [`atomic_swap/src/test.rs`] file to follow along.

[`atomic_swap/src/test.rs`]: https://github.com/stellar/soroban-examples/tree/v0.5.0/atomic_swap/src/test.rs

Refer to another examples for the general information on the test setup.

The interesting part for this example is verification of `swap` authorization:

```rust
contract.swap(
&a,
&b,
&token_a.contract_id,
&token_b.contract_id,
&1000,
&4500,
&5000,
&950,
);

assert_eq!(
env.recorded_top_authorizations(),
std::vec![
(
a.clone(),
contract.contract_id.clone(),
symbol!("swap"),
(
token_a.contract_id.clone(),
token_b.contract_id.clone(),
1000_i128,
4500_i128
)
.into_val(&env),
),
(
b.clone(),
contract.contract_id.clone(),
symbol!("swap"),
(
token_b.contract_id.clone(),
token_a.contract_id.clone(),
5000_i128,
950_i128
)
.into_val(&env),
),
]
);
```

`env.recorded_top_authorizations()` returns all the top-level authorizations.
Hence in the case of `swap` two authorizations are expected. Note, that the
element order here matches the order of `require_auth_for_args` calls in the
contract.
Loading

0 comments on commit 603c4b8

Please sign in to comment.