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

Commit

Permalink
Merge pull request #85 from stellar/dev
Browse files Browse the repository at this point in the history
  • Loading branch information
leighmcculloch authored Sep 2, 2022
2 parents 4238d07 + c1a57c5 commit 98ffc86
Show file tree
Hide file tree
Showing 22 changed files with 1,083 additions and 611 deletions.
40 changes: 40 additions & 0 deletions docs/SDKs/rust-auth.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
sidebar_position: 2
title: Soroban Rust Auth SDK
---

The `soroban-auth` Rust crate contains the Soroban Rust Auth SDK. It provides
utilities for verifying signatures on invocations of smart contracts. It is
intended for use alongside the [`soroban-sdk`].

[`soroban-sdk`]: rust

:::caution
The `soroban-auth` crate is in early development. Report issues
[here](https://github.com/stellar/rs-soroban-sdk/issues/new/choose).
:::

## SDK Documentation

Auth documentation is available at:
https://docs.rs/soroban-auth

## Subscribe to Releases

Subscribe to releases on the GitHub repository:
https://github.com/stellar/rs-soroban-sdk

## Add `soroban-auth` as a Dependency

Add the following sections to the `Cargo.toml` to import `soroban-auth`.

```toml
[features]
testutils = ["soroban-auth/testutils"]

[dependencies]
soroban-auth = "0.0.4"

[dev_dependencies]
soroban-auth = { version = "0.0.4", features = ["soroban-auth/testutils"] }
```
5 changes: 4 additions & 1 deletion docs/SDKs/rust.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,8 @@ Add the following sections to the `Cargo.toml` to import the `soroban-sdk`.
testutils = ["soroban-sdk/testutils"]

[dependencies]
soroban-sdk = "0.0.3"
soroban-sdk = "0.0.4"

[dev_dependencies]
soroban-sdk = { version = "0.0.4", features = ["soroban-sdk/testutils"] }
```
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"position": 3,
"label": "Standard Contracts",
"position": 5,
"label": "Built-in Contracts",
"link": {
"type": "generated-index"
}
Expand Down
172 changes: 172 additions & 0 deletions docs/built-in-contracts/token-contract.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
---
sidebar_position: 1
title: Token Contract
---

# Token Contract

The token contract is an implementation of [CAP-54 Smart Contract Standardized Asset].

[CAP-54 Smart Contract Standardized Asset]: https://stellar.org/protocol/cap-54

:::caution
The token contract is in early development, has not been audited, and is not
intended for use. Report issues
[here](https://github.com/stellar/soroban-token-contract/issues/new/choose).
:::

## Overview

Tokens are a vital part of blockchains, and contracts that implement token
functionality are inevitable on any smart contract platform. A built-in token
contract has a number of advantages over token contracts written by the
ecosystem. First, we can special case this contract and run it natively instead
of running in a WASM VM, reducing the cost of using the contract. Second, we
can use this built-in contract to allow "classic" Stellar assets to interoperate
with Soroban. The current iteration of the standard token contract doesn't
interoperate with "classic" Stellar assets, but this feature will be available
in the future. Note that this standard token contract does not prevent the
ecosystem from developing other token contracts if the standard is missing
functionality they require.

The standard token contract is similar to the widely used ERC-20 token standard,
which should make it easier for existing smart contract developers to get
started on Stellar.

## Token contract authorization semantics

See the [authorization example](../examples/authorization) for an overview of
authorization.

### Token operations

The token contract contains three kinds of operations

- getters, such as `balance`, which do not change the state of the contract
- unprivileged mutators, such as `approve` and `xfer`, which change the state of
the contract but do not require special privileges
- privileged mutators, such as `burn` and `set_admin`, which change the state of
the contract but require special privileges

Gettors require no authorization because they do not change the state of the
contract and all contract data is public. For example, `balance` simply returns
the balance of the specified identity without changing it.

Unprivileged mutators require authorization from some identity. The identity
which must provide authorization will vary depending on the unprivileged
mutator. For example, a "grantor" can use `approve` to allow a "spender" to spend
the grantor's money up to some limit. So for approve, the grantor must provide
authorization. Similarly, a "sender" can use `xfer` to send money to a
"recipient". So for `xfer`, the sender must provide authorization.

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

### Replay prevention

The token contract provides replay prevention by using a [nonce](https://en.wikipedia.org/wiki/Cryptographic_nonce).
The messages that are signed to provide authorization contain a nonce. The
contract also stores a nonce per identity. When checking signatures, the
contract loads the nonce for the relevant identity. When an operation succeeds,
the nonce stored in the contract is incremented. This makes it impossible to
reuse a signature.

The current nonce for an identity can be retrieved using the `nonce` contract
function.

### Example: Signing payloads

The [example](https://github.com/stellar/soroban-token-contract/blob/d24fbeaa3cad5406bee8b64e748a71fa38c7c2f3/src/testutils.rs#L48-L70)
from the test `Token` wrapper class demonstrate how you sign a payload for the
token contract.

```rust
pub fn approve(&self, from: &Keypair, spender: &Identifier, amount: &BigInt) {
let from_id = to_ed25519(&self.env, from);
let nonce = self.nonce(&from_id);

let msg = SignaturePayload::V0(SignaturePayloadV0 {
function: symbol!("approve"),
contract: self.contract_id.clone(),
network: self.env.ledger().network_passphrase(),
args: (from_id, &nonce, spender, amount).into_val(&self.env),
});

let auth = Signature::Ed25519(Ed25519Signature {
public_key: from.public.to_bytes().into_val(&self.env),
signature: from.sign(msg).unwrap().into_val(&self.env),
});
TokenClient::new(&self.env, &self.contract_id).approve(&auth, &nonce, &spender, &amount)
}
```

## Contract Interface

```rust
// Sets the administrator to "admin". Also sets some metadata
fn initialize(e: Env, admin: Identifier, decimal: u32, name: Bytes, symbol: Bytes);

// Admin interface -- these functions are privileged

// If "admin" is the administrator, burn "amount" from "from"
fn burn(e: Env, admin: Signature, nonce: BigInt, from: Identifier, amount: BigInt);

// If "admin" is the administrator, mint "amount" to "to"
fn mint(e: Env, admin: Signature, nonce: BigInt, to: Identifier, amount: BigInt);

// If "admin" is the administrator, set the administrator to "id"
fn set_admin(e: Env, admin: Signature, nonce: BigInt, new_admin: Identifier);

// If "admin" is the administrator, freeze "id"
fn freeze(e: Env, admin: Signature, nonce: BigInt, id: Identifier);

// If "admin" is the administrator, unfreeze "id"
fn unfreeze(e: Env, admin: Signature, nonce: BigInt, id: Identifier);

// Token Interface

// Get the allowance for "spender" to transfer from "from"
fn allowance(e: Env, from: Identifier, spender: Identifier) -> BigInt;

// Set the allowance to "amount" for "spender" to transfer from "from"
fn approve(e: Env, from: Signature, nonce: BigInt, spender: Identifier, amount: BigInt);

// Get the balance of "id"
fn balance(e: Env, id: Identifier) -> BigInt;

// Transfer "amount" from "from" to "to"
fn xfer(e: Env, from: Signature, nonce: BigInt, to: Identifier, amount: BigInt);

// Transfer "amount" from "from" to "to", consuming the allowance of "spender"
fn xfer_from(
e: Env,
spender: Signature,
nonce: BigInt,
from: Identifier,
to: Identifier,
amount: BigInt,
);

// Returns true if "id" is frozen
fn is_frozen(e: Env, id: Identifier) -> bool;

// Returns the current nonce for "id"
fn nonce(e: Env, id: Identifier) -> BigInt;

// Descriptive Interface

// Get the number of decimals used to represent amounts of this token
fn decimals(e: Env) -> u32;

// Get the name for this token
fn name(e: Env) -> Bytes;

// Get the symbol for this token
fn symbol(e: Env) -> Bytes;
```

## Interacting with the token contract in tests

See [interacting with contracts in tests](../learn/interacting-with-contracts#interacting-with-contracts-in-tests)
for more general information on this topic.
Loading

0 comments on commit 98ffc86

Please sign in to comment.