Regulated Coin and Deny List
The Sui Coin standard provides a create_regulated_currency
function to create coins. This function is different than create_currency
in that it generates a coin that you can block certain addresses from being able to use those coins in transactions. This ability is a requirement for assets like stablecoins.
Behind the scenes, create_regulated_currency
uses the create_currency
function to create the coin, but also produces a DenyCap
object that allows its bearer to control access to the coin's deny list in a DenyList
object. Consequently, the way to create a coin using create_regulated_currency
is similar to the previous example, with the addition of a transfer of the DenyCap
object to the module publisher.
module examples::regcoin;
use sui::{coin::{Self, DenyCapV2}, deny_list::DenyList};
public struct REGCOIN has drop {}
fun init(witness: REGCOIN, ctx: &mut TxContext) {
let (treasury, deny_cap, metadata) = coin::create_regulated_currency_v2(
witness,
6,
b"REGCOIN",
b"",
b"",
option::none(),
false,
ctx,
);
transfer::public_freeze_object(metadata);
transfer::public_transfer(treasury, ctx.sender());
transfer::public_transfer(deny_cap, ctx.sender())
}
}
When you deploy the previous module using sui client publish
, the console responds with transaction effects, including the creation of the following objects:
...
Object Changes
Created Objects:
ObjectID: <OBJECT-ID>
Sender: <SENDER-ADDR>
Owner: Immutable
ObjectType: 0x2::coin::CoinMetadata<<PACKAGE-ID>::regcoin::REGCOIN>
Version: <VERSION-NUMBER>
Digest: <DIGEST-HASH>
ObjectID: <OBJECT-ID>
Sender: <SENDER-ADDR>
Owner: Account Address ( <PUBLISHER-ADDRESS )
ObjectType: 0x2::package::UpgradeCap
Version: <VERSION-NUMBER>
Digest: <DIGEST-HASH>
ObjectID: <OBJECT-ID>
Sender: <SENDER-ADDR>
Owner: Immutable
ObjectType: 0x2::coin::RegulatedCoinMetadata<<PACKAGE-ID>::regcoin::REGCOIN>
Version: <VERSION-NUMBER>
Digest: <DIGEST-HASH>
ObjectID: <OBJECT-ID>
Sender: <SENDER-ADDR>
Owner: Account Address ( <PUBLISHER-ADDRESS )
ObjectType: 0x2::coin::DenyCap<<PACKAGE-ID>::regcoin::REGCOIN>
Version: <VERSION-NUMBER>
Digest: <DIGEST-HASH>
ObjectID: <OBJECT-ID>
Sender: <SENDER-ADDR>
Owner: Account Address ( <PUBLISHER-ADDRESS )
ObjectType: 0x2::coin::TreasuryCap<PACKAGE-ID>::regcoin::REGCOIN>
Version: <VERSION-NUMBER>
Digest: <DIGEST-HASH>
...
As you might have noticed, the publish action creates a RegulatedCoinMetadata
object along with the standard CoinMetadata
object. You don't need to explicitly call the freeze_object
on the RegulatedCoinMetadata
object, however, because create_regulated_currency
automatically performs this action.
The output also shows the three objects that the publisher now owns: UpgradeCap
for package upgrades, TreasuryCap
for minting or burning coins, and the DenyCap
for adding or removing addresses to or from the deny list for this coin.
DenyList
The Sui framework provides a DenyList
singleton, shared object that the bearer of a DenyCap
can access to specify a list of addresses that are unable to use a Sui core type. The initial use case for DenyList
, however, focuses on limiting access to coins of a specified type. This is useful, for example, when creating a regulated coin on Sui that requires the ability to block certain addresses from using it as inputs to transactions. Regulated coins on Sui satisfy any regulations that require the ability to prevent known bad actors from having access to those coins.
The DenyList
object is a system object that has the address 0x403
. You cannot create it yourself.
Manipulate deny list
For the ability to manipulate the addresses assigned to the deny list for your coin, you must add a few functions to the previous example.
public fun add_addr_from_deny_list(
denylist: &mut DenyList,
denycap: &mut DenyCapV2<REGCOIN>,
denyaddy: address,
ctx: &mut TxContext,
) {
coin::deny_list_v2_add(denylist, denycap, denyaddy, ctx);
}
public fun remove_addr_from_deny_list(
denylist: &mut DenyList,
denycap: &mut DenyCapV2<REGCOIN>,
denyaddy: address,
ctx: &mut TxContext,
) {
coin::deny_list_v2_remove(denylist, denycap, denyaddy, ctx);
}
To use these functions, you pass the DenyList
object (0x403
), your DenyCap
object ID, and the address you want to either add or remove. Using the Sui CLI, you could use sui client call
with the required information:
Beginning with the Sui v1.24.1
release, the --gas-budget
flag is no longer required for CLI commands.
sui client call --function add_addr_from_deny_list --module regcoin --package <PACKAGE-ID> --args <DENY-LIST> <DENY-CAP> <ADDRESS-TO-DENY> --gas-budget <GAS-AMOUNT>
Transaction Digest: <DIGEST-HASH>
The console displays the response from the network, where you can verify the DenyList
object is mutated.
...
MutatedObjects:
ObjectID: 0x0...403
Sender: <SENDER-ADDRESS>
Owner: Shared
ObjectType: 0x2::deny_list::DenyList
Version: <VERSION-NUMBER>
Digest: <DIGEST-HASH>
...
For all Coin
functions available, see the Sui framework coin
module documentation.
Related links
- Closed Loop Token standard: Details for the standard used to create tokens on Sui.
- Source code: The source code in GitHub for this example.
- In-Game Tokens: Example of how to create tokens for use as in-game currency.
- Loyalty Tokens: Example of how to create tokens that reward brand or service loyalty on the Sui network.