|
| 1 | +--- |
| 2 | +sidebar_position: 1 |
| 3 | +title: Token Contract |
| 4 | +--- |
| 5 | + |
| 6 | +# Token Contract |
| 7 | + |
| 8 | +The token contract is an implementation of [CAP-54 Smart Contract Standardized Asset]. |
| 9 | + |
| 10 | +[CAP-54 Smart Contract Standardized Asset]: https://stellar.org/protocol/cap-54 |
| 11 | + |
| 12 | +:::caution |
| 13 | +The token contract is in early development, has not been audited, and is not |
| 14 | +intended for use. Report issues |
| 15 | +[here](https://github.com/stellar/soroban-token-contract/issues/new/choose). |
| 16 | +::: |
| 17 | + |
| 18 | +## Overview |
| 19 | + |
| 20 | +Tokens are a vital part of blockchains, and contracts that implement token |
| 21 | +functionality are inevitable on any smart contract platform. A built-in token |
| 22 | +contract has a number of advantages over token contracts written by the |
| 23 | +ecosystem. First, we can special case this contract and run it natively instead |
| 24 | +of running in a WASM VM, reducing the cost of using the contract. Second, we |
| 25 | +can use this built-in contract to allow "classic" Stellar assets to interoperate |
| 26 | +with Soroban. The current iteration of the standard token contract doesn't |
| 27 | +interoperate with "classic" Stellar assets, but this feature will be available |
| 28 | +in the future. Note that this standard token contract does not prevent the |
| 29 | +ecosystem from developing other token contracts if the standard is missing |
| 30 | +functionality they require. |
| 31 | + |
| 32 | +The standard token contract is similar to the widely used ERC-20 token standard, |
| 33 | +which should make it easier for existing smart contract developers to get |
| 34 | +started on Stellar. |
| 35 | + |
| 36 | +## Token contract authorization semantics |
| 37 | + |
| 38 | +See the [authorization example](../examples/authorization) for an overview of |
| 39 | +authorization. |
| 40 | + |
| 41 | +### Token operations |
| 42 | + |
| 43 | +The token contract contains three kinds of operations |
| 44 | + |
| 45 | +- getters, such as `balance`, which do not change the state of the contract |
| 46 | +- unprivileged mutators, such as `approve` and `xfer`, which change the state of |
| 47 | +the contract but do not require special privileges |
| 48 | +- privileged mutators, such as `burn` and `set_admin`, which change the state of |
| 49 | +the contract but require special privileges |
| 50 | + |
| 51 | +Gettors require no authorization because they do not change the state of the |
| 52 | +contract and all contract data is public. For example, `balance` simply returns |
| 53 | +the balance of the specified identity without changing it. |
| 54 | + |
| 55 | +Unprivileged mutators require authorization from some identity. The identity |
| 56 | +which must provide authorization will vary depending on the unprivileged |
| 57 | +mutator. For example, a "grantor" can use `approve` to allow a "spender" to spend |
| 58 | +the grantor's money up to some limit. So for approve, the grantor must provide |
| 59 | +authorization. Similarly, a "sender" can use `xfer` to send money to a |
| 60 | +"recipient". So for `xfer`, the sender must provide authorization. |
| 61 | + |
| 62 | +Priviliged mutators require authorization from a specific privileged identity, |
| 63 | +known as the "administrator". For example, only the administrator can `mint` more |
| 64 | +of the asset. Similarly, only the administrator can appoint a new administrator. |
| 65 | + |
| 66 | +### Replay prevention |
| 67 | + |
| 68 | +The token contract provides replay prevention by using a [nonce](https://en.wikipedia.org/wiki/Cryptographic_nonce). |
| 69 | +The messages that are signed to provide authorization contain a nonce. The |
| 70 | +contract also stores a nonce per identity. When checking signatures, the |
| 71 | +contract loads the nonce for the relevant identity. When an operation succeeds, |
| 72 | +the nonce stored in the contract is incremented. This makes it impossible to |
| 73 | +reuse a signature. |
| 74 | + |
| 75 | +The current nonce for an identity can be retrieved using the `nonce` contract |
| 76 | +function. |
| 77 | + |
| 78 | +### Example: Signing payloads |
| 79 | + |
| 80 | +The [example](https://github.com/stellar/soroban-token-contract/blob/d24fbeaa3cad5406bee8b64e748a71fa38c7c2f3/src/testutils.rs#L48-L70) |
| 81 | +from the test `Token` wrapper class demonstrate how you sign a payload for the |
| 82 | +token contract. |
| 83 | + |
| 84 | +```rust |
| 85 | +pub fn approve(&self, from: &Keypair, spender: &Identifier, amount: &BigInt) { |
| 86 | + let from_id = to_ed25519(&self.env, from); |
| 87 | + let nonce = self.nonce(&from_id); |
| 88 | + |
| 89 | + let msg = SignaturePayload::V0(SignaturePayloadV0 { |
| 90 | + function: symbol!("approve"), |
| 91 | + contract: self.contract_id.clone(), |
| 92 | + network: self.env.ledger().network_passphrase(), |
| 93 | + args: (from_id, &nonce, spender, amount).into_val(&self.env), |
| 94 | + }); |
| 95 | + |
| 96 | + let auth = Signature::Ed25519(Ed25519Signature { |
| 97 | + public_key: from.public.to_bytes().into_val(&self.env), |
| 98 | + signature: from.sign(msg).unwrap().into_val(&self.env), |
| 99 | + }); |
| 100 | + TokenClient::new(&self.env, &self.contract_id).approve(&auth, &nonce, &spender, &amount) |
| 101 | +} |
| 102 | +``` |
| 103 | + |
| 104 | +## Contract Interface |
| 105 | + |
| 106 | +```rust |
| 107 | +// Sets the administrator to "admin". Also sets some metadata |
| 108 | +fn initialize(e: Env, admin: Identifier, decimal: u32, name: Bytes, symbol: Bytes); |
| 109 | + |
| 110 | +// Admin interface -- these functions are privileged |
| 111 | + |
| 112 | +// If "admin" is the administrator, burn "amount" from "from" |
| 113 | +fn burn(e: Env, admin: Signature, nonce: BigInt, from: Identifier, amount: BigInt); |
| 114 | + |
| 115 | +// If "admin" is the administrator, mint "amount" to "to" |
| 116 | +fn mint(e: Env, admin: Signature, nonce: BigInt, to: Identifier, amount: BigInt); |
| 117 | + |
| 118 | +// If "admin" is the administrator, set the administrator to "id" |
| 119 | +fn set_admin(e: Env, admin: Signature, nonce: BigInt, new_admin: Identifier); |
| 120 | + |
| 121 | +// If "admin" is the administrator, freeze "id" |
| 122 | +fn freeze(e: Env, admin: Signature, nonce: BigInt, id: Identifier); |
| 123 | + |
| 124 | +// If "admin" is the administrator, unfreeze "id" |
| 125 | +fn unfreeze(e: Env, admin: Signature, nonce: BigInt, id: Identifier); |
| 126 | + |
| 127 | +// Token Interface |
| 128 | + |
| 129 | +// Get the allowance for "spender" to transfer from "from" |
| 130 | +fn allowance(e: Env, from: Identifier, spender: Identifier) -> BigInt; |
| 131 | + |
| 132 | +// Set the allowance to "amount" for "spender" to transfer from "from" |
| 133 | +fn approve(e: Env, from: Signature, nonce: BigInt, spender: Identifier, amount: BigInt); |
| 134 | + |
| 135 | +// Get the balance of "id" |
| 136 | +fn balance(e: Env, id: Identifier) -> BigInt; |
| 137 | + |
| 138 | +// Transfer "amount" from "from" to "to" |
| 139 | +fn xfer(e: Env, from: Signature, nonce: BigInt, to: Identifier, amount: BigInt); |
| 140 | + |
| 141 | +// Transfer "amount" from "from" to "to", consuming the allowance of "spender" |
| 142 | +fn xfer_from( |
| 143 | + e: Env, |
| 144 | + spender: Signature, |
| 145 | + nonce: BigInt, |
| 146 | + from: Identifier, |
| 147 | + to: Identifier, |
| 148 | + amount: BigInt, |
| 149 | +); |
| 150 | + |
| 151 | +// Returns true if "id" is frozen |
| 152 | +fn is_frozen(e: Env, id: Identifier) -> bool; |
| 153 | + |
| 154 | +// Returns the current nonce for "id" |
| 155 | +fn nonce(e: Env, id: Identifier) -> BigInt; |
| 156 | + |
| 157 | +// Descriptive Interface |
| 158 | + |
| 159 | +// Get the number of decimals used to represent amounts of this token |
| 160 | +fn decimals(e: Env) -> u32; |
| 161 | + |
| 162 | +// Get the name for this token |
| 163 | +fn name(e: Env) -> Bytes; |
| 164 | + |
| 165 | +// Get the symbol for this token |
| 166 | +fn symbol(e: Env) -> Bytes; |
| 167 | +``` |
| 168 | + |
| 169 | +## Interacting with the token contract in tests |
| 170 | + |
| 171 | +See [interacting with contracts in tests](../learn/interacting-with-contracts#interacting-with-contracts-in-tests) |
| 172 | +for more general information on this topic. |
0 commit comments