Stellar Asset Contract
The Stellar Asset Contract (SAC) is an implementation of CAP-46-6 Smart Contract Standardized Asset and SEP-41 Token Interface for Stellar assets.
See examples of how to use the SAC in the Tokens How-To Guides.
Overview
Stellar assets are issued by Stellar accounts. Issue an asset on Stellar by following the Issue an Asset Tutorial.
The Stellar Asset Contract allows users and contracts to make payments with, and interact with, assets. The SAC can interact with assets held by Stellar accounts or contracts.
The SAC is a special built-in contract that has access to functionality of the Stellar network that allows it to use Stellar assets directly.
Each Stellar asset has an instance of the SAC reserved on the network. To use the SAC reserved for an asset, the instance just needs to be deployed.
When the SAC transfers assets between accounts, the same debit and credits occur as they do when a Stellar payment operation is used, because the SAC interacts directly with Stellar account trust lines. When the SAC transfers assets between contracts, it uses Contract Data ledger entries to store the balances for contracts.
Stellar account balances for the native asset are always stored on the account, and Stellar contract balances for the native asset are always stored in a contract data entry.
Stellar account balances for issued assets are always stored in trust lines, and Stellar contract balances for issued assets are always stored in a contract data entry.
For example, when transferring from a Stellar account to a Stellar contract, the Stellar account's trust line entry is debited, and a contract data entry is credited.
And for example, when transferring from a Stellar contract to a Stellar account, a contract data entry is debited, and the account's trust line entry is credited.
In both those examples it is a single asset that is transferring from the account to the contract and back again. No bridging is required and no intermediary tokens are needed. An asset on Stellar and it's Stellar Asset Contract represent the same asset. The SAC for an asset is simply an API for interacting with the asset.
The SAC implements the SEP-41 Token Interface, which is similar to the widely used ERC-20 token standard. Contracts that depend on only the SEP-41 portion of the SAC's interface, are also compatible with any custom token that implements SEP-41.
Some functionality available on the Stellar network in transaction operations, such as the order book, do not have any functions exposed on the Stellar Asset Contract in the current protocol.
Deployment
Every Stellar asset on Stellar has reserved a contract address that the Stellar Asset Contract can be deployed to. Anyone can initiate the deploy and the Stellar asset issuer does not need to be involved.
It can be deployed using the Stellar CLI as shown here.
Or the Stellar SDK can be used as shown here by calling InvokeHostFunctionOp
with HOST_FUNCTION_TYPE_CREATE_CONTRACT
and CONTRACT_ID_FROM_ASSET
. 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.
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.
Interacting with classic Stellar assets
The Stellar Asset Contract is the only way for contracts to interact with Stellar assets, either the native XLM asset, or those issued by Stellar accounts.
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). The following section references some issuer and trustline flags from Stellar classic, which you can learn more about here.
- 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 hasTRUSTLINE_CLAWBACK_ENABLED_FLAG
set. - 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 receive an amount more than the maximum amount that can be represented by a 64-bit signed integer will fail.
- Transfers will only succeed if the corresponding trustline(s) have the
- Using
Address::Contract
- The balance and authorization state will be stored in contract storage, as opposed to a trustline.
- Balances are stored in a 128-bit signed integer.
- A balance can only be clawed back if the issuer account had the
AUTH_CLAWBACK_ENABLED_FLAG
set when the balance was created. A balance is created when either anAddress::Contract
is on the receiving end of a successful transfer, or if the admin sets the authorization state. Read more aboutAUTH_CLAWBACK_ENABLED_FLAG
here.
Balance Authorization Required
In the Address::Contract
case, if the issuer has AUTH_REQUIRED_FLAG
set, then the specified Address::Contract
will need to be explicitly authorized with set_auth
before it can receive a balance. This logic lines up with how trustlines interact with the AUTH_REQUIRED_FLAG
issuer flag, allowing asset issuers to have the same control in Soroban as they do in Stellar classic. Read more about AUTH_REQUIRED_FLAG
here.
Revoking Authorization
The admin can only revoke authorization from an Address
, if the issuer of the asset has AUTH_REVOCABLE_FLAG
set. The deauthorization will fail if the issuer is missing. This requirement is true for both the trustline balances of Address::Account
and contract balances of Address:Contract
. 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.
Authorization semantics
See the authorization overview and auth example for general information about authorization in Soroban.
The token contract contains three kinds of operations that follow the token interface:
- getters, such as
balance
, which do not change the state of the contract - unprivileged mutators, such as
incr_allow
andxfer
, which change the state of the contract but do not require special privileges - privileged mutators, such as
clawback
andset_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 Address
without changing it.
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 token. Similarly, only the administrator can appoint a new administrator.
Contract Interface
This interface can be found in the SDK. It extends the common SEP-41 Token Interface.