From 2071c4555e4efb1177bb71d805e459003d59b8c7 Mon Sep 17 00:00:00 2001 From: Dmytro Kozhevin Date: Fri, 10 Feb 2023 15:47:58 -0500 Subject: [PATCH 1/4] Update token doc to use auth next. --- docs/how-to-guides/tokens.mdx | 165 ++++++++++++++++++++-------------- 1 file changed, 96 insertions(+), 69 deletions(-) diff --git a/docs/how-to-guides/tokens.mdx b/docs/how-to-guides/tokens.mdx index 9fa7e944..b0006585 100644 --- a/docs/how-to-guides/tokens.mdx +++ b/docs/how-to-guides/tokens.mdx @@ -5,150 +5,182 @@ title: Tokens ## Token Example -The [token example] demonstrates how to write a token contract that implements the token interface. +The [token example] demonstrates how to write a token contract that implements +the token interface. [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)][oigp] -[oigp]: https://gitpod.io/#https://github.com/stellar/soroban-examples/tree/v0.4.2 -[token example]: https://github.com/stellar/soroban-examples/tree/v0.4.2/token +[oigp]: https://gitpod.io/#https://github.com/stellar/soroban-examples/tree/v0.5.0 + +[token example]: https://github.com/stellar/soroban-examples/tree/v0.5.0/token ## Token Interface -Token contracts, including the Stellar Asset Contract and example token implementations expose the following common interface. +Token contracts, including the Stellar Asset Contract and example token +implementations expose the following common interface. + +Tokens deployed on Soroban can implement any interface they choose, however, +they should satisfy the following interface to be interoperable with contracts +built to support Soroban's built-in tokens. + +Note, that in the specific cases the interface doesn't have to be fully +implemented. For example, the custom token may not implement the administrative +interface compatible with the Stellar Asset Contract - it won't stop it from +being usable in the contracts that only perform the regular user operations +(transfers, allowances, balances etc.). + +### Compatibility Requirements + +For any given contract function, there are 3 requirements that should be +consistent with the interface described here: -Tokens deployed on Soroban can implement any interface they choose, however, they should satisfy the following interface to be interoperable with contracts built to support Soroban's built-in tokens. +- Function interface (name and arguments) - if not consistent, then the users +simply won't be able to use the function at all. This is the hard requirement. +- Authorization - the users have to authorize the token function calls with all +the arguments of the invocation (see the interface comments). If this is +inconsistent, then the custom token may have issues with getting the correct +signatures from the users and may also confuse the wallet software. +- Events - the token has to emit the events in the specified format. If +inconsistent, then the token may not be handled correctly by the downstream +systems such as block explorers. + +### Code + +The interface below uses the Rust [soroban-sdk](../reference/sdks/rust.mdx). ```rust pub trait Contract { // -------------------------------------------------------------------------------- // Admin interface – privileged functions. // -------------------------------------------------------------------------------- + // + // All the admin functions have to be authorized by the admin with all input + // arguments, i.e. they have to call `admin.require_auth()`. /// If "admin" is the administrator, clawback "amount" from "from". "amount" is burned. - /// Emit event with topics = ["clawback", from: Identifier, to: Identifier], data = [amount: i128] + /// Emit event with topics = ["clawback", admin: Address, to: Address], data = [amount: i128] fn clawback( env: soroban_sdk::Env, - admin: soroban_auth::Signature, - nonce: i128, - from: soroban_auth::Identifier, + admin: Address, + from: Address, amount: i128, ); /// If "admin" is the administrator, mint "amount" to "to". - /// Emit event with topics = ["mint", admin: Identifier, to: Identifier], data = [amount: i128] + /// Emit event with topics = ["mint", admin: Address, to: Address], data = [amount: i128] fn mint( env: soroban_sdk::Env, - admin: soroban_auth::Signature, - nonce: i128, - to: soroban_auth::Identifier, + admin: Address, + to: Address, amount: i128, ); /// If "admin" is the administrator, set the administrator to "id". - /// Emit event with topics = ["set_admin", admin: Identifier], data = [new_admin: Identifier] + /// Emit event with topics = ["set_admin", admin: Address], data = [new_admin: Address] fn set_admin( env: soroban_sdk::Env, - admin: soroban_auth::Signature, - nonce: i128, - new_admin: soroban_auth::Identifier, + admin: Address, + new_admin: Address, ); /// If "admin" is the administrator, set the authorize state of "id" to "authorize". /// If "authorize" is true, "id" should be able to use its balance. - /// Emit event with topics = ["set_auth", admin: Identifier, id: Identifier], data = [authorize: bool] + /// Emit event with topics = ["set_auth", admin: Address, id: Address], data = [authorize: bool] fn set_auth( env: soroban_sdk::Env, - admin: soroban_auth::Signature, - nonce: i128, - id: soroban_auth::Identifier, + admin: Address, + id: Address, authorize: bool, ); // -------------------------------------------------------------------------------- // Token interface // -------------------------------------------------------------------------------- - - /// Get the allowance for "spender" to transfer from "from". - fn allowance( - env: soroban_sdk::Env, - from: soroban_auth::Identifier, - spender: soroban_auth::Identifier, - ) -> i128; + // + // All the functions here have to be authorized by the token spender + // (usually named `from` here) using all the input arguments, i.e. they have + // to call `from.require_auth()`. /// Increase the allowance by "amount" for "spender" to transfer/burn from "from". - /// Emit event with topics = ["incr_allow", from: Identifier, spender: Identifier], data = [amount: i128] + /// Emit event with topics = ["incr_allow", from: Address, spender: Address], data = [amount: i128] fn incr_allow( env: soroban_sdk::Env, - from: soroban_auth::Signature, - nonce: i128, - spender: soroban_auth::Identifier, + from: Address, + spender: Address, amount: i128, ); /// Decrease the allowance by "amount" for "spender" to transfer/burn from "from". /// If "amount" is greater than the current allowance, set the allowance to 0. - /// Emit event with topics = ["decr_allow", from: Identifier, spender: Identifier], data = [amount: i128] + /// Emit event with topics = ["decr_allow", from: Address, spender: Address], data = [amount: i128] fn decr_allow( env: soroban_sdk::Env, - from: soroban_auth::Signature, - nonce: i128, - spender: soroban_auth::Identifier, + from: Address, + spender: Address, amount: i128, ); - /// Get the balance of "id". - fn balance(env: soroban_sdk::Env, id: soroban_auth::Identifier) -> i128; - - /// Get the spendable balance of "id". This will return the same value as balance() - /// unless this is called on the Stellar Asset Contract, in which case this can - /// be less due to reserves/liabilities. - fn spendable(env: soroban_sdk::Env, id: soroban_auth::Identifier) -> i128; - /// Transfer "amount" from "from" to "to. - /// Emit event with topics = ["transfer", from: Identifier, to: Identifier], data = [amount: i128] + /// Emit event with topics = ["transfer", from: Address, to: Address], data = [amount: i128] fn xfer( env: soroban_sdk::Env, - from: soroban_auth::Signature, - nonce: i128, - to: soroban_auth::Identifier, + from: Address, + to: Address, amount: i128, ); /// Transfer "amount" from "from" to "to", consuming the allowance of "spender". - /// Emit event with topics = ["transfer", from: Identifier, to: Identifier], data = [amount: i128] + /// Authorized by spender (`spender.require_auth()`). + /// Emit event with topics = ["transfer", from: Address, to: Address], data = [amount: i128] fn xfer_from( env: soroban_sdk::Env, - spender: soroban_auth::Signature, - nonce: i128, - from: soroban_auth::Identifier, - to: soroban_auth::Identifier, + spender: Address, + from: Address, + to: Address, amount: i128, ); /// Burn "amount" from "from". - /// Emit event with topics = ["burn", from: Identifier], data = [amount: i128] + /// Emit event with topics = ["burn", from: Address], data = [amount: i128] fn burn( env: soroban_sdk::Env, - from: soroban_auth::Signature, - nonce: i128, + from: Address, amount: i128, ); /// Burn "amount" from "from", consuming the allowance of "spender". - /// Emit event with topics = ["burn", from: Identifier], data = [amount: i128] + /// Emit event with topics = ["burn", from: Address], data = [amount: i128] fn burn_from( env: soroban_sdk::Env, - spender: soroban_auth::Signature, - nonce: i128, - from: soroban_auth::Identifier, + spender: Address, + from: Address, amount: i128, ); + // -------------------------------------------------------------------------------- + // Read-only Token interface + // -------------------------------------------------------------------------------- + // + // The functions here don't need any authorization and don't emit any + // events. + + /// Get the balance of "id". + fn balance(env: soroban_sdk::Env, id: Address) -> i128; + + /// Get the spendable balance of "id". This will return the same value as balance() + /// unless this is called on the Stellar Asset Contract, in which case this can + /// be less due to reserves/liabilities. + fn spendable(env: soroban_sdk::Env, id: Address) -> i128; + // Returns true if "id" is authorized to use its balance. - fn authorized(env: soroban_sdk::Env, id: soroban_auth::Identifier) -> bool; + fn authorized(env: soroban_sdk::Env, id: Address) -> bool; - // Returns the current nonce for "id". - fn nonce(env: soroban_sdk::Env, id: soroban_auth::Identifier) -> i128; + /// Get the allowance for "spender" to transfer from "from". + fn allowance( + env: soroban_sdk::Env, + from: Address, + spender: Address, + ) -> i128; // -------------------------------------------------------------------------------- // Descriptive Interface @@ -163,9 +195,4 @@ pub trait Contract { // Get the symbol for this token. fn symbol(env: soroban_sdk::Env) -> soroban_sdk::Bytes; } -``` - -Most types that a token implementation utilizes are provided by the [soroban-sdk] and [soroban-auth] crates. - -[soroban-sdk]: ../reference/sdks/rust -[soroban-auth]: ../reference/sdks/rust-auth +``` \ No newline at end of file From 8811cc623659b7d01cbdeb51c393894726cb1cbb Mon Sep 17 00:00:00 2001 From: Dmytro Kozhevin Date: Fri, 10 Feb 2023 17:39:37 -0500 Subject: [PATCH 2/4] Updated SAC doc --- docs/how-to-guides/stellar-asset-contract.mdx | 184 +++++++++++++----- 1 file changed, 134 insertions(+), 50 deletions(-) diff --git a/docs/how-to-guides/stellar-asset-contract.mdx b/docs/how-to-guides/stellar-asset-contract.mdx index 25bc8414..6a296aa6 100644 --- a/docs/how-to-guides/stellar-asset-contract.mdx +++ b/docs/how-to-guides/stellar-asset-contract.mdx @@ -5,93 +5,177 @@ title: Stellar Asset Contract # Stellar Asset Contract -The Stellar Asset Contract is an implementation of [CAP-46-6 Smart Contract Standardized Asset]. +The Stellar Asset Contract is an implementation of [CAP-46-6 Smart Contract +Standardized Asset]. -[CAP-46-6 Smart Contract Standardized Asset]: https://stellar.org/protocol/cap-46-06 +[CAP-46-6 Smart Contract Standardized Asset]: +https://stellar.org/protocol/cap-46-06 :::caution -The Stellar Asset Contract is in early development, has not been audited, and is intended for use in development and testing only at this stage. Report issues [here](https://github.com/stellar/soroban-token-contract/issues/new/choose). +The Stellar Asset Contract is in early development, has not been audited, and is +intended for use in development and testing only at this stage. Report issues +[here](https://github.com/stellar/rs-soroban-env/issues/new/choose). ::: ## Overview -Stellar has numerous assets on its classic network, and being able to use them in Soroban would give users much more flexibility with how they can use their assets. For this reason, we introduced the Stellar Asset Contract, or SAC for short, which will allow users to use their Stellar account and trustline balances in Soroban. +Stellar has numerous assets on its classic network, and being able to use them +in Soroban would give users much more flexibility with how they can use their +assets. For this reason, we introduced the Stellar Asset Contract, or SAC for +short, which allows users to use their Stellar account and trustline balances in +Soroban. -The SAC implements the [token interface](tokens.mdx), which is similar to the widely used ERC-20 token standard. This should make it easier for existing smart contract developers to get started on Stellar. +The SAC implements the [token interface](tokens.mdx), which is similar to the +widely used ERC-20 token standard. This should make it easier for existing +smart contract developers to get started on Stellar. ## Deployment -For every 'classic' asset exactly one respective Stellar Asset Contract can be deployed. It can be deployed using the `InvokeHostFunctionOp` with `HOST_FUNCTION_TYPE_CREATE_CONTRACT` and `CONTRACT_ID_FROM_ASSET` specified [here](./invoking-contracts-with-transactions). The resulting token will have a deterministic identifier, which will be the sha256 hash of `HashIDPreimage::ENVELOPE_TYPE_CONTRACT_ID_FROM_ASSET` xdr specified [here](https://github.com/stellar/stellar-xdr/blob/026c9cd074bdb28ddde8ee52f2a4502d9e518a09/Stellar-transaction.x#L637). +For every 'classic' asset exactly one respective Stellar Asset Contract can be +deployed. It can be deployed using the `InvokeHostFunctionOp` with +`HOST_FUNCTION_TYPE_CREATE_CONTRACT` and `CONTRACT_ID_FROM_ASSET` specified +[here](./invoking-contracts-with-transactions). The resulting token will have a +deterministic identifier, which will be the sha256 hash of +`HashIDPreimage::ENVELOPE_TYPE_CONTRACT_ID_FROM_ASSET` xdr specified [here][contract_id]. -## Interacting with classic Stellar assets +Anyone can deploy the instances of Stellar Asset Contract, but only the Asset +Issuer will have the administrative permissions initially. -The Stellar Asset Contract is the only way to interact with 'classic' Stellar assets in Soroban. 'Classic' assets include native Stellar token (lumens) and all the existing trustlines. The issuer of the asset will be the administrator of the deployed contract. Because the the Native Stellar token doesn't have an issuer, it will not have an administrator either. It also cannot be burned. +[contract_id]: (https://github.com/stellar/stellar-xdr/blob/dc23adf60e095a6ce626b2b09128e58a5eae0cd0/Stellar-transaction.x#L661) -After the contract has been deployed, users can use their classic account or trustline balance. There are some differences depending on if you are using a classic account identifier vs a non-account identifier like a contract. +## Interacting with classic Stellar assets -- Using `Identifier::Account` - - The balance must exist in a trustline (or an account for the native balance). This means the contract will not store the balance in ContractData. If the trustline or account is missing, any function that tries to interact with that balance will fail. +The Stellar Asset Contract is the only way to interact with 'classic' Stellar +assets in Soroban. 'Classic' assets include native Stellar token (lumens) and +all the existing trustlines. + +The issuer of the asset will be the administrator +of the deployed contract. Because the Native Stellar token doesn't have an +issuer, it will not have an administrator either. It also cannot be burned. + +After the contract has been deployed, users can use their classic account (for +lumens) or trustline (for other assets) balance. There are some differences +depending on if you are using a classic account `Address` vs a contract +`Address` (corresponding either to a regular contract or to a custom account +contract). + +- Using `Address::Account` + - The balance must exist in a trustline (or an account for the native + balance). This means the contract will not store the balance in + ContractData. If the trustline or account is missing, any function that + tries to interact with that balance will fail. - Classic trustline semantics will be followed. - - Transfers will only succeed if the corresponding trustline(s) have the `AUTHORIZED_FLAG` set. - - A trustline balance can only be clawed back using the `clawback` contract function if the trustline has `TRUSTLINE_CLAWBACK_ENABLED_FLAG` set. - - The admin can only deauthorize a trustline if the issuer of the asset has `AUTH_REVOCABLE` set. The deauthorization will fail if the issuer is missing. Note that when a trustline is deauthorized from Soroban, `AUTHORIZED_FLAG` is cleared and `AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG` is set to avoid having to pull offers and redeeming pool shares. - - Transfers to the issuer account will burn the token, while transfers from the issuer account will mint. - - Trustline balances are stored in a 64-bit signed integer even though the interface accepts 128-bit signed integers. Any operation that attempts to send or recieve an amount more than the maximum amount that can be represented by a 64-bit signed integer will fail. -- Using `Identifier::Ed25519` or `Identifier::Contract` - - The balance and authorization state will be stored in ContractData, as opposed to a trustline. - - `AUTH_REVOCABLE` is not required to be set on the issuer to deauthorize a balance. - - These balances are stored in a 128-bit signed integer. + - Transfers will only succeed if the corresponding trustline(s) have the + `AUTHORIZED_FLAG` set. + - A trustline balance can only be clawed back using the `clawback` contract + function if the trustline has `TRUSTLINE_CLAWBACK_ENABLED_FLAG` set. + - The admin can only deauthorize a trustline if the issuer of the asset has + `AUTH_REVOCABLE` set. The deauthorization will fail if the issuer is + missing. Note that when a trustline is deauthorized from Soroban, + `AUTHORIZED_FLAG` is cleared and + `AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG` is set to avoid having to pull + offers and redeeming pool shares. + - Transfers to the issuer account will burn the token, while transfers from + the issuer account will mint. + - Trustline balances are stored in a 64-bit signed integer even though the + interface accepts 128-bit signed integers. Any operation that attempts to + send or recieve an amount more than the maximum amount that can be + represented by a 64-bit signed integer will fail. +- Using `Address::Contract` + - The balance and authorization state will be stored in contract storage, as + opposed to a trustline. + - `AUTH_REVOCABLE` is not required to be set on the issuer to deauthorize a + balance. + - Balances are stored in a 128-bit signed integer. ## Authorization semantics -See the [auth example](./auth.mdx) for an overview of authorization. - -### SAC operations +See the [authorization overview](../learn/authorization.mdx) and +[auth example](./auth.mdx) for general information about authorization in +Soroban. -The token contract contains three kinds of operations +The token contract contains three kinds of operations that follow the token +[interface](tokens.mdx#code): - getters, such as `balance`, which do not change the state of the contract -- unprivileged mutators, such as `incr_allow` and `xfer`, which change the state of the contract but do not require special privileges -- privileged mutators, such as `clawback` and `set_admin`, which change the state of the contract but require special privileges +- unprivileged mutators, such as `incr_allow` and `xfer`, which change the state + of the contract but do not require special privileges +- privileged mutators, such as `clawback` and `set_admin`, which change the + state of the contract but require special privileges -Getters 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. +Getters 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 `Address` 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 `incr_allow` to allow a "spender" to spend the grantor's money up to some limit. So for `incr_allow`, 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. +Unprivileged mutators require authorization from the `Address` that spends or +allows spending their balance. The exceptions are `xfer_from` and `burn_from` +operations where the `Address` that require authorization from the 'spender' +entity that has got an allowance from another `Address` beforehand. -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. +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. -### Replay prevention +## Using Stellar Asset Contract with other contracts -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. +From the contract perspective Stellar Asset Contract is not different from any +other token that implements the Soroban token interface. That's why in order +to generate a client to use it `soroban_token_spec` can be used: -The current nonce for an identity can be retrieved using the `nonce` contract function. +```rust +// Import the token spec to generate the client for the token interface ( +// including SAC). +mod token { + soroban_sdk::contractimport!(file = "../soroban_token_spec.wasm"); +} + +struct MyContract; + +#[contractimpl] +impl MyContract { + fn token_fn(e: Env, token_id: BytesN<32>) { + // Create a client instance for the provided token identifier. If the + // `token_id` value corresponds to an SAC contract, then SAC implementation + // is used. + let client = token::Client::new(&env, &token_id); + // Call token operations. + client.xfer(...); + } +} +``` -### Example: Invoker auth +### Examples -The easiest way to use the built-in token with classic accounts is to just use the [invoker](./auth.mdx#invoker) auth. In this way there won't be a need to sign the contract payload, so the contract call would look like this: +See the full examples that utilize the token contract in various ways for more +details: +- [Timelock](timelock.mdx) and [single offer](single-offer-sale.mdx) move token + via `xfer` to and from the contract +- [Atomic swap](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 -// `with_source_account` is a testing utility, but the contract call arguments -// would be the same for the real contract call too. -token.with_source_account(&token_admin_id).mint( - // No signature needed, just a flag that invoker auth should be used, i.e. - // `token_admin_id` in this case. - &Signature::Invoker, - // Nonce is always 0 for invokers. - &0, - &user_id, - &1000, -); +let admin = Address::random(); +let user = Address::random(); +let token = TokenClient::new(e, &e.register_stellar_asset_contract(admin.clone())); +token.mint(&admin, &user, &1000); ``` -See a more complete example that uses invoker auth in the tests [here](https://github.com/stellar/soroban-examples/blob/51a95262caba3f8ac466fa8bbc07004ad297ae13/timelock/src/test.rs#L60). +See the tests in the [examples](#examples) above for the full test +implementation. ## Contract Interface -This interface can be found in the [SDK](https://github.com/stellar/rs-soroban-sdk/blob/main/soroban-token-spec/src/lib.rs). It extends the common [token interface](tokens.mdx). - -## Interacting with the token contract in tests +This interface can be found in the [SDK]. It extends the common +[token interface](tokens.mdx). -See [interacting with contracts in tests](../learn/interacting-with-contracts#interacting-with-contracts-in-tests) for more general information on this topic. +[SDK]: (https://github.com/stellar/rs-soroban-sdk/blob/main/soroban-token-spec/src/lib.rs) From cc1c461fd9738fcfe82880677271d86621a82f3f Mon Sep 17 00:00:00 2001 From: Dmytro Kozhevin Date: Fri, 10 Feb 2023 17:52:29 -0500 Subject: [PATCH 3/4] !fixup Fix links --- docs/how-to-guides/stellar-asset-contract.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/how-to-guides/stellar-asset-contract.mdx b/docs/how-to-guides/stellar-asset-contract.mdx index 6a296aa6..b84c4e06 100644 --- a/docs/how-to-guides/stellar-asset-contract.mdx +++ b/docs/how-to-guides/stellar-asset-contract.mdx @@ -43,7 +43,7 @@ deterministic identifier, which will be the sha256 hash of Anyone can deploy the instances of Stellar Asset Contract, but only the Asset Issuer will have the administrative permissions initially. -[contract_id]: (https://github.com/stellar/stellar-xdr/blob/dc23adf60e095a6ce626b2b09128e58a5eae0cd0/Stellar-transaction.x#L661) +[contract_id]: https://github.com/stellar/stellar-xdr/blob/dc23adf60e095a6ce626b2b09128e58a5eae0cd0/Stellar-transaction.x#L661 ## Interacting with classic Stellar assets @@ -178,4 +178,4 @@ implementation. This interface can be found in the [SDK]. It extends the common [token interface](tokens.mdx). -[SDK]: (https://github.com/stellar/rs-soroban-sdk/blob/main/soroban-token-spec/src/lib.rs) +[SDK]: https://github.com/stellar/rs-soroban-sdk/blob/main/soroban-token-spec/src/lib.rs From 35cab17b43379eb4b8cc889ed8dee3dd21c68b4e Mon Sep 17 00:00:00 2001 From: Dmytro Kozhevin Date: Fri, 10 Feb 2023 18:28:47 -0500 Subject: [PATCH 4/4] Added a note on SAC initialization. --- docs/how-to-guides/stellar-asset-contract.mdx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/how-to-guides/stellar-asset-contract.mdx b/docs/how-to-guides/stellar-asset-contract.mdx index b84c4e06..2634b96f 100644 --- a/docs/how-to-guides/stellar-asset-contract.mdx +++ b/docs/how-to-guides/stellar-asset-contract.mdx @@ -40,8 +40,10 @@ deployed. It can be deployed using the `InvokeHostFunctionOp` with deterministic identifier, which will be the sha256 hash of `HashIDPreimage::ENVELOPE_TYPE_CONTRACT_ID_FROM_ASSET` xdr specified [here][contract_id]. -Anyone can deploy the instances of Stellar Asset Contract, but only the Asset -Issuer will have the administrative permissions initially. +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