This repository has been archived by the owner on Aug 21, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 81
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Updated example docs for Auth Next. (#306)
* Updated example docs for Auth Next.
- Loading branch information
Showing
20 changed files
with
846 additions
and
894 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
Oops, something went wrong.