Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refine and clarify some points in the custom SAC admin guide #1245

Merged
merged 2 commits into from
Feb 3, 2025
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 27 additions & 7 deletions docs/build/guides/tokens/custom-sac-admin.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,28 @@ hide_table_of_contents: true
description: Set a custom administrator account on a deployed SAC
---

The [Stellar Asset Contract (SAC)](../../../tokens/stellar-asset-contract.mdx) includes functionality to set a custom administration account on the contract itself. This can be seen as a security precaution, similar to locking an asset's issuer account after creating the total supply of tokens. It should also be noted that an asset's issuer account can have a distinct set of signatures and weights, and these are unrelated to the SAC admin address.
The [Stellar Asset Contract (SAC)](../../../tokens/stellar-asset-contract.mdx) includes functionality to set a custom administration account on the contract itself. This can allow for flexible arrangements of asset administration, which can be configured to suit many distinct use-cases.

For example, you can set a custom SAC admin, and then lock the issuer account, allowing for token administration to be _exclusively_ performed through the SAC contract. Or, you could set a custom SAC admin and still keep the issuer account unlocked, allowing for a hybrid approach to token administration.

:::info

It should be understood that an asset's issuer account can have a distinct set of signatures and weights, and these are unrelated to the SAC admin address.

:::

## Considerations

To effectively utilize the SAC admin functionality, you should be aware of a few things first:

- When a SAC is initially enabled for an issued asset, the `Admin` address on the contract defaults to the issuer account.
- A SAC `Admin` address can be either a regular Stellar account (`G...`), or it can be a smart contract (`C...`) address. This opens up the possibility of programmatically minting and taking other administrative actions for an asset by way of a smart contract invocation.
- Changing a SAC `Admin` address (initially) requires authorization from the asset's issuer account. After the `Admin` address has been set the first time, subsequent changes will require authorization from the _current_ `Admin` address.
- If an asset's issuer account is locked while the SAC `Admin` address has not been changed, it will _never_ be possible to change the SAC `Admin` address.
- If an asset's issuer account is locked after the SAC `Admin` address has been changed, tokens will still be mint-able from the SAC as long as it's authorized by the _current_ `Admin` address.
- A SAC `Admin` address can be either a regular Stellar account (`G...`), or it can be a smart contract (`C...`) address. The use-cases requiring a smart contract to act as a SAC `Admin` tend to be more common. This opens up the possibility of programmatically minting and taking other administrative actions for an asset by way of a smart contract invocation.
- Changing a SAC `Admin` address requires authorization from the asset's _current_ `Admin` address, which will be the issuer account the first time the action is performed.

Depending on the use-case, it can be beneficial for asset issuers to mint an entire supply of tokens up front, and then lock down the issuer account. This would keep the total token supply capped forever. You can learn more about this practice on the [asset design considerations](../../../tokens/control-asset-access.mdx#limiting-the-supply-of-an-asset) page. However, since the issuer account and a SAC `Admin` address act independently, there are some interesting points to make that might seem counterintuitive at first glance.

- If an asset's issuer account is locked while the SAC `Admin` address has not been changed, it will _never_ be possible to change the SAC `Admin` address. This is because the issuer account can no longer authorize the first `set_admin` invocation.
- If an asset's issuer account is locked after the SAC `Admin` address has been changed, tokens will still be mint-able and/or clawback-able (if enabled) from the SAC as long as it's authorized by the _current_ `Admin` address.

## Example

Expand All @@ -35,7 +46,7 @@ This gives us the SAC address of `CBVYF2KJ72BRPLVPCUL3PGWDO5RK2XP4AJDHKX7GDDBJW4

:::note

We're using the asset's issuer account here to enable the SAC on the Testnet network, but this could be done using _any_ account.
We're using the asset's issuer account here to enable the SAC on the Testnet network, but this action can be performed using _any_ account.

:::

Expand Down Expand Up @@ -91,7 +102,15 @@ If you examine the asset's `GCS5...` issuer account, you won't see anything diff

### Set the `Admin` address to some contract `C...` address

For this part of the example, we'll use a very simple "minter" contract as the `Admin` address. It has a very simple implementation, and only one main function, `start_mint`:
For this part of the example, we'll use a very simple "minter" contract as the `Admin` address. It has a very simple implementation, and only one main function, `start_mint`.

:::warning

The following "minter" contract calls the `require_auth()` function for itself. This means you would need to include some `__check_auth` logic in the contract, which is demonstrated in the [custom account example contract](../../smart-contracts/example-contracts/custom-account.mdx). We'll omit those details here, for the sake of simplicity and brevity.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is way too nuanced and really not related to the topic. This makes the example incomplete without check_auth, and it doesn't really demonstrate something close to reality (I don't expect token contracts to also be custom accounts).

I can think of the following simple example that's not completely permissionless, but also demonstrates something interesting: consider a claim_airdrop function (or something along these lines), that mints some small fixed amount to a given address. It would take the receiver address, call require_auth for it in order to make sure it's a real address, then make sure the address hasn't claimed before (using storage), then mint some amount of token to it. This shouldn't add more than a couple lines of code to the example (there is no need to even use some UDTs for the keys, just add the addresses directly to the storage for brevity).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is perfect! i'll use that instead. thanks!


While this call to authenticate the minter contract isn't _technically necessary_, permissionless operations can be dangerous and should be avoided when writing a smart contract.

:::

```rust
#[contractimpl]
Expand All @@ -101,6 +120,7 @@ impl Contract {
}

pub fn star_mint(env: Env, to: Address, amount: i128) {
env.current_contract_address().require_auth();
let sac_address: Address = env.storage().instance().get(&symbol_short!("SAC_ADDR")).unwrap();
let token_client = token::StellarAssetClient::new(&env, &sac_address);
token_client.mint(&to, &amount);
Expand Down