soroban_sdk/token.rs
1//! Token contains types for calling and accessing token contracts, including
2//! the Stellar Asset Contract.
3//!
4//! See [`TokenInterface`] for the interface of token contracts such as the
5//! Stellar Asset Contract.
6//!
7//! Use [`TokenClient`] for calling token contracts such as the Stellar Asset
8//! Contract.
9
10use crate::{contractclient, contractspecfn, Address, Env, String};
11
12// The interface below was copied from
13// https://github.com/stellar/rs-soroban-env/blob/main/soroban-env-host/src/native_contract/token/contract.rs
14// at commit b3c188f48dec51a956c1380fb6fe92201a3f716b.
15//
16// Differences between this interface and the built-in contract
17// 1. The return values here don't return Results.
18// 2. The implementations have been replaced with a panic.
19// 3. &Host type usage are replaced with Env
20
21#[doc(hidden)]
22#[deprecated(note = "use TokenInterface")]
23pub use TokenInterface as Interface;
24
25#[doc(hidden)]
26#[deprecated(note = "use TokenClient")]
27pub use TokenClient as Client;
28
29/// Interface for Token contracts, such as the Stellar Asset Contract.
30///
31/// Defined by [SEP-41].
32///
33/// [SEP-41]: https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0041.md
34///
35/// The token interface provides the following functionality.
36///
37/// If a contract implementing the interface does not support some of the
38/// functionality, it should return an error.
39///
40/// The interface does not define any set of standard errors. Errors can be
41/// defined by the implementing contract.
42///
43/// ## Meta
44///
45/// Tokens implementing the interface expose meta functions about the token:
46/// - [`decimals`][Self::decimals]
47/// - [`name`][Self::name]
48/// - [`symbol`][Self::symbol]
49///
50/// ## Balances
51///
52/// Tokens track a balance for each address that holds the token. Tokens implementing the interface expose
53/// a single function for getting the balance that an address holds:
54/// - [`balance`][Self::balance]
55///
56/// ## Transfers
57///
58/// Tokens allow holders of the token to transfer tokens to other addresses.
59/// Tokens implementing the interface expose a single function for doing so:
60/// - [`transfer`][Self::transfer]
61///
62/// ## Burning
63///
64/// Tokens allow holders of the token to burn, i.e. dispose of, tokens without
65/// transferring them to another holder. Tokens implementing the interface
66/// expose a single function for doing so:
67/// - [`burn`][Self::burn]
68///
69/// ## Allowances
70///
71/// Tokens can allow holders to permit others to transfer amounts from their
72/// balance using the following functions.
73/// - [`allowance`][Self::allowance]
74/// - [`approve`][Self::approve]
75/// - [`transfer_from`][Self::transfer_from]
76/// - [`burn_from`][Self::burn_from]
77///
78/// ## Minting
79///
80/// There are no functions in the token interface for minting tokens. Minting is
81/// an administrative function that can differ significantly from one token to
82/// the next.
83#[contractspecfn(name = "StellarAssetSpec", export = false)]
84#[contractclient(crate_path = "crate", name = "TokenClient")]
85pub trait TokenInterface {
86 /// Returns the allowance for `spender` to transfer from `from`.
87 ///
88 /// The amount returned is the amount that spender is allowed to transfer
89 /// out of from's balance. When the spender transfers amounts, the allowance
90 /// will be reduced by the amount transferred.
91 ///
92 /// # Arguments
93 ///
94 /// * `from` - The address holding the balance of tokens to be drawn from.
95 /// * `spender` - The address spending the tokens held by `from`.
96 fn allowance(env: Env, from: Address, spender: Address) -> i128;
97
98 /// Set the allowance by `amount` for `spender` to transfer/burn from
99 /// `from`.
100 ///
101 /// The amount set is the amount that spender is approved to transfer out of
102 /// from's balance. The spender will be allowed to transfer amounts, and
103 /// when an amount is transferred the allowance will be reduced by the
104 /// amount transferred.
105 ///
106 /// # Arguments
107 ///
108 /// * `from` - The address holding the balance of tokens to be drawn from.
109 /// * `spender` - The address being authorized to spend the tokens held by
110 /// `from`.
111 /// * `amount` - The tokens to be made available to `spender`.
112 /// * `expiration_ledger` - The ledger number where this allowance expires. Cannot
113 /// be less than the current ledger number unless the amount is being set to 0.
114 /// An expired entry (where expiration_ledger < the current ledger number)
115 /// should be treated as a 0 amount allowance.
116 ///
117 /// # Events
118 ///
119 /// Emits an event with topics `["approve", from: Address,
120 /// spender: Address], data = [amount: i128, expiration_ledger: u32]`
121 fn approve(env: Env, from: Address, spender: Address, amount: i128, expiration_ledger: u32);
122
123 /// Returns the balance of `id`.
124 ///
125 /// # Arguments
126 ///
127 /// * `id` - The address for which a balance is being queried. If the
128 /// address has no existing balance, returns 0.
129 fn balance(env: Env, id: Address) -> i128;
130
131 /// Transfer `amount` from `from` to `to`.
132 ///
133 /// # Arguments
134 ///
135 /// * `from` - The address holding the balance of tokens which will be
136 /// withdrawn from.
137 /// * `to` - The address which will receive the transferred tokens.
138 /// * `amount` - The amount of tokens to be transferred.
139 ///
140 /// # Events
141 ///
142 /// Emits an event with topics `["transfer", from: Address, to: Address],
143 /// data = amount: i128`
144 fn transfer(env: Env, from: Address, to: Address, amount: i128);
145
146 /// Transfer `amount` from `from` to `to`, consuming the allowance that
147 /// `spender` has on `from`'s balance. Authorized by spender
148 /// (`spender.require_auth()`).
149 ///
150 /// The spender will be allowed to transfer the amount from from's balance
151 /// if the amount is less than or equal to the allowance that the spender
152 /// has on the from's balance. The spender's allowance on from's balance
153 /// will be reduced by the amount.
154 ///
155 /// # Arguments
156 ///
157 /// * `spender` - The address authorizing the transfer, and having its
158 /// allowance consumed during the transfer.
159 /// * `from` - The address holding the balance of tokens which will be
160 /// withdrawn from.
161 /// * `to` - The address which will receive the transferred tokens.
162 /// * `amount` - The amount of tokens to be transferred.
163 ///
164 /// # Events
165 ///
166 /// Emits an event with topics `["transfer", from: Address, to: Address],
167 /// data = amount: i128`
168 fn transfer_from(env: Env, spender: Address, from: Address, to: Address, amount: i128);
169
170 /// Burn `amount` from `from`.
171 ///
172 /// Reduces from's balance by the amount, without transferring the balance
173 /// to another holder's balance.
174 ///
175 /// # Arguments
176 ///
177 /// * `from` - The address holding the balance of tokens which will be
178 /// burned from.
179 /// * `amount` - The amount of tokens to be burned.
180 ///
181 /// # Events
182 ///
183 /// Emits an event with topics `["burn", from: Address], data = amount:
184 /// i128`
185 fn burn(env: Env, from: Address, amount: i128);
186
187 /// Burn `amount` from `from`, consuming the allowance of `spender`.
188 ///
189 /// Reduces from's balance by the amount, without transferring the balance
190 /// to another holder's balance.
191 ///
192 /// The spender will be allowed to burn the amount from from's balance, if
193 /// the amount is less than or equal to the allowance that the spender has
194 /// on the from's balance. The spender's allowance on from's balance will be
195 /// reduced by the amount.
196 ///
197 /// # Arguments
198 ///
199 /// * `spender` - The address authorizing the burn, and having its allowance
200 /// consumed during the burn.
201 /// * `from` - The address holding the balance of tokens which will be
202 /// burned from.
203 /// * `amount` - The amount of tokens to be burned.
204 ///
205 /// # Events
206 ///
207 /// Emits an event with topics `["burn", from: Address], data = amount:
208 /// i128`
209 fn burn_from(env: Env, spender: Address, from: Address, amount: i128);
210
211 /// Returns the number of decimals used to represent amounts of this token.
212 ///
213 /// # Panics
214 ///
215 /// If the contract has not yet been initialized.
216 fn decimals(env: Env) -> u32;
217
218 /// Returns the name for this token.
219 ///
220 /// # Panics
221 ///
222 /// If the contract has not yet been initialized.
223 fn name(env: Env) -> String;
224
225 /// Returns the symbol for this token.
226 ///
227 /// # Panics
228 ///
229 /// If the contract has not yet been initialized.
230 fn symbol(env: Env) -> String;
231}
232
233/// Interface for admin capabilities for Token contracts, such as the Stellar
234/// Asset Contract.
235#[contractspecfn(name = "StellarAssetSpec", export = false)]
236#[contractclient(crate_path = "crate", name = "StellarAssetClient")]
237pub trait StellarAssetInterface {
238 /// Sets the administrator to the specified address `new_admin`.
239 ///
240 /// # Arguments
241 ///
242 /// * `new_admin` - The address which will henceforth be the administrator
243 /// of this token contract.
244 ///
245 /// # Events
246 ///
247 /// Emits an event with topics `["set_admin", admin: Address], data =
248 /// [new_admin: Address]`
249 fn set_admin(env: Env, new_admin: Address);
250
251 /// Returns the admin of the contract.
252 ///
253 /// # Panics
254 ///
255 /// If the admin is not set.
256 fn admin(env: Env) -> Address;
257
258 /// Sets whether the account is authorized to use its balance. If
259 /// `authorized` is true, `id` should be able to use its balance.
260 ///
261 /// # Arguments
262 ///
263 /// * `id` - The address being (de-)authorized.
264 /// * `authorize` - Whether or not `id` can use its balance.
265 ///
266 /// # Events
267 ///
268 /// Emits an event with topics `["set_authorized", id: Address], data =
269 /// [authorize: bool]`
270 fn set_authorized(env: Env, id: Address, authorize: bool);
271
272 /// Returns true if `id` is authorized to use its balance.
273 ///
274 /// # Arguments
275 ///
276 /// * `id` - The address for which token authorization is being checked.
277 fn authorized(env: Env, id: Address) -> bool;
278
279 /// Mints `amount` to `to`.
280 ///
281 /// # Arguments
282 ///
283 /// * `to` - The address which will receive the minted tokens.
284 /// * `amount` - The amount of tokens to be minted.
285 ///
286 /// # Events
287 ///
288 /// Emits an event with topics `["mint", admin: Address, to: Address], data
289 /// = amount: i128`
290 fn mint(env: Env, to: Address, amount: i128);
291
292 /// Clawback `amount` from `from` account. `amount` is burned in the
293 /// clawback process.
294 ///
295 /// # Arguments
296 ///
297 /// * `from` - The address holding the balance from which the clawback will
298 /// take tokens.
299 /// * `amount` - The amount of tokens to be clawed back.
300 ///
301 /// # Events
302 ///
303 /// Emits an event with topics `["clawback", admin: Address, to: Address],
304 /// data = amount: i128`
305 fn clawback(env: Env, from: Address, amount: i128);
306}
307
308/// Spec contains the contract spec of Token contracts, including the general
309/// interface, as well as the admin interface, such as the Stellar Asset
310/// Contract.
311#[doc(hidden)]
312pub struct StellarAssetSpec;
313
314pub(crate) const SPEC_XDR_INPUT: &[&[u8]] = &[
315 &StellarAssetSpec::spec_xdr_allowance(),
316 &StellarAssetSpec::spec_xdr_authorized(),
317 &StellarAssetSpec::spec_xdr_approve(),
318 &StellarAssetSpec::spec_xdr_balance(),
319 &StellarAssetSpec::spec_xdr_burn(),
320 &StellarAssetSpec::spec_xdr_burn_from(),
321 &StellarAssetSpec::spec_xdr_clawback(),
322 &StellarAssetSpec::spec_xdr_decimals(),
323 &StellarAssetSpec::spec_xdr_mint(),
324 &StellarAssetSpec::spec_xdr_name(),
325 &StellarAssetSpec::spec_xdr_set_admin(),
326 &StellarAssetSpec::spec_xdr_admin(),
327 &StellarAssetSpec::spec_xdr_set_authorized(),
328 &StellarAssetSpec::spec_xdr_symbol(),
329 &StellarAssetSpec::spec_xdr_transfer(),
330 &StellarAssetSpec::spec_xdr_transfer_from(),
331];
332
333pub(crate) const SPEC_XDR_LEN: usize = 6456;
334
335impl StellarAssetSpec {
336 /// Returns the XDR spec for the Token contract.
337 pub const fn spec_xdr() -> [u8; SPEC_XDR_LEN] {
338 let input = SPEC_XDR_INPUT;
339 // Concatenate all XDR for each item that makes up the token spec.
340 let mut output = [0u8; SPEC_XDR_LEN];
341 let mut input_i = 0;
342 let mut output_i = 0;
343 while input_i < input.len() {
344 let subinput = input[input_i];
345 let mut subinput_i = 0;
346 while subinput_i < subinput.len() {
347 output[output_i] = subinput[subinput_i];
348 output_i += 1;
349 subinput_i += 1;
350 }
351 input_i += 1;
352 }
353
354 // Check that the numbers of bytes written is equal to the number of bytes
355 // expected in the output.
356 if output_i != output.len() {
357 panic!("unexpected output length",);
358 }
359
360 output
361 }
362}