Skip to content

Commit

Permalink
Integrate a Stellar asset contracts (#754)
Browse files Browse the repository at this point in the history
* Move SAC example section from tokens to guide

* Expand example with balance check

* Remove native token page

* Link to CAP

* Fix links

* Add info about address

* Apply suggestions from code review

Co-authored-by: Elliot Voris <elliot@voris.me>

* Address comments from review

* Revert path-payment change

* move link definitions from tokens page to this guide

* nitpick some headings, add a closing curly bracket in an code block
  • Loading branch information
tupui authored Jul 24, 2024
1 parent 23d3d3e commit 7f5eb8f
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 72 deletions.
105 changes: 105 additions & 0 deletions docs/build/guides/conventions/stellar-asset-contract.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
---
title: Integrate Stellar Assets Contracts
description: Test and use Stellar assets in a Soroban smart contract
---

When interacting with assets in a smart contract, the Stellar Asset Contract is not different from any other token that implements the Stellar [SEP-41 Token Interface].

## Contract Code

The Rust SDK contains a pre-generated client for any contract that implements the token interface:

```rust
use soroban_sdk::{contract, contractimpl}
use soroban_sdk::token;

#[contract]
pub struct MyContract;

#[contractimpl]
impl MyContract {
pub fn token_fn(e: Env, id: Address) {
// Create a client instance for the provided token identifier. If the id
// value corresponds to an SAC contract, then SAC implementation is used.
let client = token::TokenClient::new(&env, &id);
// Call token functions part of the Stellar SEP-41 token interface
client.transfer(...);
}
}
```

The `asset` parameter is not the address of the issuer of an asset, it corresponds to the deployed contract address for this asset.

```bash
stellar contract id asset \
--source G... \
--network testnet \
--asset [asset:issuer]
```

E.g. for USDC, it would be `--asset USDC:G...` For the native asset, XLM, `--asset native`. See the [deploy SAC] guide for more details.

[deploy SAC]: ../cli/deploy-stellar-asset-contract.mdx

:::info[Clients]

A client created by [`token::TokenClient`] implements the functions defined by any contract that implements the [SEP-41 Token Interface]. But with [CAP-46-6 smart contract standardized asset], the Stellar Asset Contract exposes additional functions such as `mint`. To access the additional functions, another client needs to be used: [`token::StellarAssetClient`]. This client only implements the functions from CAP-46-6, which are not part of the SEP-41 interface.

```rust
let client = token::StellarAssetClient::new(&env, &id);
// Call token functions which are not part of the SEP-41 token interface
// but part of the CAP-46-6 Smart Contract Standardized Asset
client.mint(...);
```

:::

## Testing

Soroban Rust SDK provides an easy way to instantiate a Stellar Asset Contract tokens using `register_stellar_asset_contract`. This function can be seen as the deployment of a generic token. In the following example, we are following the best practices outlined in the [Issuing and Distribution Accounts section](../../../tokens/control-asset-access.mdx#issuing-and-distribution-accounts):

```rust
#![cfg(test)]

use soroban_sdk::testutils::Address as _;
use soroban_sdk::{token, Address, Env};
use token::TokenClient;
use token::StellarAssetClient;

#[test]
fn test() {
let e = Env::default();
e.mock_all_auths();

let issuer = Address::random();
let distributer = Address::random();

let token_address = e.register_stellar_asset_contract(issuer.clone());

// client for SEP-41 functions
let token = TokenClient::new(&e, &token_address);
// client for Stellar Asset Contract functions
let token_sac = StellarAssetClient::new(&e, &token_address);

// note that you need to account for the difference between the minimal
// unit and the unit itself when working with amounts.
// E.g. to mint 1 TOKEN, we need to use 1*1e7 in the mint function.
let genesis_amount: i128 = 1_000_000_000 * 10_000_000;

token_sac.mint(&issuer, &distributer, &genesis_amount);

assert_eq!(token.balance(&distributor), genesis_amount);
}
```

## Examples

See the full examples that utilize the token contract in various ways for more details:

- [Timelock](../../smart-contracts/example-contracts/timelock.mdx) and [single offer](../../smart-contracts/example-contracts/single-offer-sale.mdx) move token via `transfer` to and from the contract
- [Atomic swap](../../smart-contracts/example-contracts/atomic-swap.mdx) uses `transfer` to transfer token on behalf of the user

[sep-41 token interface]: ../../../tokens/token-interface.mdx
[cap-46-6 smart contract standardized asset]: https://github.com/stellar/stellar-protocol/blob/master/core/cap-0046-06.md
[`token::tokenclient`]: https://docs.rs/soroban-sdk/latest/soroban_sdk/token/struct.TokenClient.html
[`token::stellarassetclient`]: https://docs.rs/soroban-sdk/latest/soroban_sdk/token/struct.StellarAssetClient.html
11 changes: 0 additions & 11 deletions docs/build/guides/testing/mint-native-token.mdx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ fn move_token(
}
```

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 the `swap` contract invocation.
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 `transfer`. Soroban auth framework makes sure that the `transfer` signatures would have the proper context, and they won't be usable outside the `swap` contract invocation.

### Tests

Expand Down
62 changes: 2 additions & 60 deletions docs/tokens/stellar-asset-contract.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ Or the [Stellar SDK] can be used as shown [here](../learn/encyclopedia/contract-
Anyone can deploy the instances of Stellar Asset Contract. Note, that the initialization of the Stellar Asset Contracts happens automatically during the deployment. Asset Issuer will have the administrative permissions after the contract has been deployed.

[contract_id]: https://github.com/stellar/stellar-xdr/blob/dc23adf60e095a6ce626b2b09128e58a5eae0cd0/Stellar-transaction.x#L661
[stellar-cli]: ../tools/developer-tools/README.mdx#cli
[stellar cli]: ../tools/developer-tools/README.mdx#cli
[stellar sdk]: ../tools/sdks/library.mdx

## Interacting with classic Stellar assets

Expand Down Expand Up @@ -103,62 +104,6 @@ Unprivileged mutators require authorization from the `Address` that spends or al

Priviliged mutators require authorization from a specific privileged identity, known as the "administrator". For example, only the administrator can `mint` more of the token. Similarly, only the administrator can appoint a new administrator.

## Using Stellar Asset Contract with other contracts

From the contract perspective Stellar Asset Contract is not different from any other token that implements the Soroban token interface. The Rust SDK contains a pregenerated client for any contract that implements the token interface:

```rust
use soroban_sdk::token;

struct MyContract;

#[contractimpl]
impl MyContract {
fn token_fn(e: Env, id: Address) {
// Create a client instance for the provided token identifier. If the id
// value corresponds to an SAC contract, then SAC implementation is used.
let client = token::Client::new(&env, &id);
// Call token operations part of the SEP-41 token interface
client.transfer(...);
}
}
```

:::info Clients

A client created by [`token::Client`] implements the functions defined by any contract that implements the [SEP-41 Token Interface]. But the Stellar Asset Contract exposes additional functions such as `mint`. To access the additional functions, another client needs to be used: [`token::StellarAssetClient`]. This client only implements the functions which are not part of the SEP-41.

```rust
let client = token::StellarAssetClient::new(&env, &id);
// Call token operations which are not part of the SEP-41 token interface
// but part of the CAP-46-6 Smart Contract Standardized Asset
client.mint(...);
```

:::

### Examples

See the full examples that utilize the token contract in various ways for more details:

- [Timelock](../build/smart-contracts/example-contracts/timelock.mdx) and [single offer](../build/smart-contracts/example-contracts/single-offer-sale.mdx) move token via `xfer` to and from the contract
- [Atomic swap](../build/smart-contracts/example-contracts/atomic-swap.mdx) uses `incr_allow` to transfer token on behalf of the user

Notice, that these examples don't do anything to support SAC specifically.

### Testing

Soroban Rust SDK provides an easy way to instantiate a Stellar Asset Contract tokens using `register_stellar_asset_contract`. For example:

```rust
let admin = Address::random();
let user = Address::random();
let token = StellarAssetClient::new(e, &e.register_stellar_asset_contract(admin.clone()));
token.mint(&admin, &user, &1000);
```

See the tests in the [examples](#examples) above for the full test implementation.

## Contract Interface

This interface can be found in the [SDK]. It extends the common [SEP-41 Token Interface].
Expand All @@ -167,6 +112,3 @@ This interface can be found in the [SDK]. It extends the common [SEP-41 Token In
[sdk]: https://docs.rs/soroban-sdk/latest/soroban_sdk/token/index.html
[cap-46-6 smart contract standardized asset]: https://github.com/stellar/stellar-protocol/blob/master/core/cap-0046-06.md
[sep-41 token interface]: ./token-interface.mdx
[`soroban_sdk::token`]: https://docs.rs/soroban-sdk/latest/soroban_sdk/token/
[`token::tokenclient`]: https://docs.rs/soroban-sdk/latest/soroban_sdk/token/struct.TokenClient.html
[`token::stellarassetclient`]: https://docs.rs/soroban-sdk/latest/soroban_sdk/token/struct.StellarAssetClient.html

0 comments on commit 7f5eb8f

Please sign in to comment.