Best Practices for Token Launches on Sui with Multisig Setup Guide
This guide provides detailed steps and best practices for launching new tokens on Sui, along with setting up multisig wallets for secure management.
Token launch best practices
- Token publishing and initial setup:
- Use a burner wallet to perform the initial publishing and minting.
- Transfer all minted tokens, remaining
UpgradeCap
s, etc., to the treasury (e.g., a multisig address). - Alternatively, prepare the multi-sig account first and do the publishing directly with it, as it is shown here.
- Execute other transactions through multisig address so that the origin for these transfers is the multisig address, not the burner wallet.
UpgradeCap
andTreasuryCap
management- For tokens with fixed supply:
- Mint the entire supply at the Token Generation Event (TGE).
- Wrap the
TreasuryCap
and make it unusable for certain actions (like minting) to ensure integrity.
- For dynamic supply tokens:
- Evaluate the need for minting based on the specific tokenomics.
- Wrap the
TreasuryCap
to make it usable on certain actions under pre-defined conditions
- Burn the token's
UpgradeCap
to ensure package integrity and transparency - For the tokenomics/staking, since it's advisable to be implemented in a separate package, we can keep the
UpgradeCap
- Multisig solution: Use a multi-signature solution to handle the
TreasuryCap
andUpgradeCap
. Recommended multisig setups to ensure safety:- Multisig (3 of 5) for
UpgradeCap
- 1 AWS KMS/ Cloud KMS
- 2 Ledger
- 1 Yubikey
- 1 ZkLogin address
- Multisig (4 of 6) - treasury / admin cap holder
- 1 GCP KMS
- 1 AWS KMS
- 2 Ledger
- 1 Hot Wallet
- 1 Yubikey
- Multisig (3 of 5) for
- Quorum Approach: Implement a quorum approach with external voters (e.g., auditors, community) controlling upgrades.
- For tokens with fixed supply:
- Token distribution and management
- Minted tokens should be managed securely, through multisig accounts or other multisig wallet solutions like MSafe or StreamFlow.
- You can use treasury management services like MPay for vesting schedules.
- Plan for providing liquidity to Centralized Exchanges (CEXs).
- Coin metadata
- Being careful on the
CoinMetadata
since it needs to be shared or frozen so that the coin can register to different protocols. - If the
CoinMetadata
will get frozen, ensure theimage_url
is served from a domain owned and controlled by the publisher
- Being careful on the
Token listing process
For Sui Wallet:
- By default, tokens are unrecognized. Recognized tokens:
- Appear at the top of your asset list in Sui Wallet (if balance > 0).
- Unrecognized tokens are automatically collapsed.
- Have a USD equivalent balance, provided this information is available via Coingecko and/or Cetus. For explorers:
- SuiVision: Sui Request Form for Listing
- Suiscan: Coin Submit Form for Listing
Multisig setup guide
Overview
Assuming a custom coin named $MYCOIN to be launched on Sui. The primary goals of the launch include deploying the coin on Sui mainnet, managing the treasury through a multisig account, distributing it to defined shareholders, and integrating it with Sui wallet and an explorer.
Pre-requisites
- Sui CLI: Install the Sui Command Line Interface (CLI).
- SUI Coin: Prepare some (~10) SUI coins for gas fees.
- Publish Modules: Follow the setup instructions to publish your modules.
Step 1: Create Sui account
Generate Sui addresses and keys using the command:
$ sui client new-address ed25519
You can also create and use a key using AWS KMS. Follow the steps to create a key using KMS key, select Asymmetric
as the key type and ECC_SECG_P256K1
for the key spec.
https://docs.aws.amazon.com/kms/latest/developerguide/asymm-create-key.html#create-asymmetric-keys-console
Step 2: Get keys from Sui keystore
List the signatures using:
$ sui keytool list --json
The response resembles the following, but displays actual addresses, keys, and peer IDs:
[
{
"alias": "elegant-sphene",
"suiAddress": "<SUI-ADDRESS-1>",
"publicBase64Key": "<PUBLIC-KEY-1>",
"keyScheme": "ed25519",
"flag": 0,
"peerId": "<PEER-ID-1>"
},
{
"alias": "inspiring-topaz",
"suiAddress": "<SUI-ADDRESS-2>",
"publicBase64Key": "<PUBLIC-KEY-2>",
"keyScheme": "ed25519",
"flag": 0,
"peerId": "<PEER-ID-2>"
},
{
"alias": "amazing-emerald",
"suiAddress": "<SUI-ADDRESS-3>",
"publicBase64Key": "<PUBLIC-KEY-3>",
"keyScheme": "ed25519",
"flag": 0,
"peerId": "<PEER-ID-3>"
}
]
Get a public key from AWS KMS. Execute runGetPublicKey
on the following script to get a public key on the format of Sui.
https://gist.github.com/1-4200/3fbd79da289a54d3687326c61823e689#file-kms-sigining-ts-L264.
Do not forget to declare the necessary AWS-related environment variables to run the script.
Step 3: Create and set multisig address
To create a multisig address, input a list of public keys to use for the multisig address and a list of their corresponding weights and the threshold (replacing <VARIABLES>
with actual values).
A setup of two out of three signers is typically considered secure. However, to strengthen security measures, we suggest diversifying the types of signers involved. Including additional types such as a cloud signer, like AWS KMS, can provide enhanced protection, especially for long-term usage. Solely relying on hot wallet keys for multisig may entail security risks.
Generate a multisig address using:
$ sui keytool multi-sig-address --pks <PUBLIC-KEY-ED25519> <PUBLIC-KEY-ED25519> <PUBLIC-KEY-ED25519> --weights 1 1 1 --threshold 2 --json
The response resembles the following:
{
"multisigAddress": "<MULTISIG-ADDRESS>",
"multisig": [
{
"address": "<SUI-ADDRESS-1>",
"publicBase64Key": "<PUBLIC-KEY-1>",
"weight": 1
},
{
"address": "<SUI-ADDRESS-2>",
"publicBase64Key": "<PUBLIC-KEY-2>",
"weight": 1
},
{
"address": "<SUI-ADDRESS-3>",
"publicBase64Key": "<PUBLIC-KEY-3>",
"weight": 1
}
]
}
Set multisig address as active
sui client switch --address <MULTISIG-ADDRESS>
Step 4: Transfer SUI coin to multisig address
Transfer a certain amount of SUI Coins for gas fees from your Sui Wallet, Sui CLI, CEX or any other account to a multisig address. Check if the multisig address has the amount you transferred.
$ sui client gas --json
[
{
"gasCoinId": "0xa8eaedaaa86e3d7bef2d844b22252e24e0bd83289b24970f831c4ec127f888be",
"mistBalance": 4000000000,
"suiBalance": "4.00"
}
]
Step 5: Serialize publish transaction for publishing your modules
Navigate to the repository directory and serialize the publish transaction:
cd <path-to-repo>
sui client publish --serialize-unsigned-transaction
The response resembles the following:
UPDATING GIT DEPENDENCY https://github.com/MystenLabs/sui.git
INCLUDING DEPENDENCY Sui
INCLUDING DEPENDENCY MoveStdlib
BUILDING <your_project_name>
Successfully verified dependencies on-chain against source.
<TX-BYTES>
Step 6: Sign the transaction
Sign the transaction with ≥k keys:
$ sui keytool sign --address <SUI-ADDRESS-1> --data <TX_BYTES> --json
{
"suiAddress": "<SUI-ADDRESS-1>",
"rawTxData": "<TX-BYTES>",
...
"suiSignature": "<SIGNATURE-1>"
}
$ sui keytool sign --address <SUI-ADDRESS-2> --data <TX_BYTES> --json
{
"suiAddress": "<SUI-ADDRESS-2>",
"rawTxData": "<TX-BYTES>",
...
"suiSignature": "<SIGNATURE-2>"
}
Sign the transaction with AWS KMS key. If you wish to include addresses generated by AWS KMS in your multisig, create a signature through the script below on runSignTxBytes. https://gist.github.com/1-4200/3fbd79da289a54d3687326c61823e689#file-kms-sigining-ts-L263 Do not forget to declare the necessary environment variables (AWS-related, TX_BYTES) to run the script.
Step 7: Combine signature
Combine the signatures using:
$ sui keytool multi-sig-combine-partial-sig --pks <PUBLIC-KEY-1> <PUBLIC-KEY-2> <PUBLIC-KEY-3> --weights 1 1 1 --threshold 2 --sigs <SIGNATURE-1> <SIGNATURE-2>
multisig address: <MULTISIG-ADDRESS> # Informational
multisig parsed: <HUMAN-READABLE-STRUCT> # Informational
multisig serialized: <SERIALIZED-MULTISIG>
You need only the signatures of the participating signers whose sum of weights >=k. You must provide all public keys and their weights, and the threshold that defined the multisig address.
Step 8: Execute the transaction
Execute the transaction using the combined signature:
$ sui client execute-signed-tx --tx-bytes <TX_BYTES> --signatures <SERIALIZED-MULTISIG> --json
Example output of the execute-signed-tx
command.
execute-signed-tx
command.{
"digest": "LyCucbaARVLcCL8EPb27Bk5zWAjghdqAc5jVaeEmSSU",
"transaction": {
"data": {
"messageVersion": "v1",
"transaction": {
"kind": "ProgrammableTransaction",
"inputs": [
{
"type": "pure",
"valueType": "address",
"value": "0x51b53d828382b39c820694f4c601d1f413e68b1d963e735aef6637fe270d4d68"
}
],
"transactions": [
{
"Publish": [
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0x0000000000000000000000000000000000000000000000000000000000000002"
]
},
{
"TransferObjects": [
[
{
"Result": 0
}
],
{
"Input": 0
}
]
}
]
},
"sender": "0x51b53d828382b39c820694f4c601d1f413e68b1d963e735aef6637fe270d4d68",
"gasData": {
"payment": [
{
"objectId": "0x0610d6a78501b689300e5d1fc785667f729edd75675ed0b5f9391cee9152aded",
"version": 2,
"digest": "ZZrGvm4qqhs11w5iARTqmxvEwyvg6d12dSC2Ew1CY5y"
}
],
"owner": "0x51b53d828382b39c820694f4c601d1f413e68b1d963e735aef6637fe270d4d68",
"price": "1000",
"budget": "21980400"
}
},
"txSignatures": [
"AIPLM4AVdyMChc5iThoOd37T3LR6u7XLQQuqx2HZcIcwIWvPZaCwDDWxOO87l+xt0e4wxS9cKW+ZGY1gwKm9hwGgoGXnNiF3adQSrq4+y8KWkCWmum6jpYnGZvYQ4FB8Vg=="
]
},
"effects": {
"messageVersion": "v1",
"status": {
"status": "success"
},
"executedEpoch": "1",
"gasUsed": {
"computationCost": "1000000",
"storageCost": "19980400",
"storageRebate": "978120",
"nonRefundableStorageFee": "9880"
},
"modifiedAtVersions": [
{
"objectId": "0x0610d6a78501b689300e5d1fc785667f729edd75675ed0b5f9391cee9152aded",
"sequenceNumber": "2"
}
],
"transactionDigest": "LyCucbaARVLcCL8EPb27Bk5zWAjghdqAc5jVaeEmSSU",
"created": [
{
"owner": "Immutable",
"reference": {
"objectId": "0x00e0ebe33cd95b7084acc08c56e3b93df2f2f847545b83eb5cd00e638eb0b800",
"version": 1,
"digest": "21WwdEYAUapR4UmPF3A8wWaqnsmXBUfLmMxb6ehzu83u"
}
},
{
"owner": "Immutable",
"reference": {
"objectId": "0x2ce11233569304f64f96c3dbf8a1aeb99a0d2d420f0a84092166ac5d50d6087f",
"version": 3,
"digest": "CKFPxn81q8GxQsaQq3T5LNWtE6enQQ1mmwdLRUe2ntrw"
}
},
{
"owner": "Immutable",
"reference": {
"objectId": "0x6b0feae03901037f3db1f18b694b5d18197e74906579823b9958cbdbf6f87278",
"version": 3,
"digest": "2UpNB69ZBqR9GsQJqfz4jyc5yx9FEvTFsLMEiHK4kJFi"
}
},
{
"owner": {
"AddressOwner": "0x51b53d828382b39c820694f4c601d1f413e68b1d963e735aef6637fe270d4d68"
},
"reference": {
"objectId": "0x6fbd871137808ba4ea610fa302fa0943c83f67a8f5a6b0468ff3288303392c57",
"version": 3,
"digest": "fdBKCwEEqxk5MwjsVpZ4SwYMkV6ehTEtXjGHbikKYsF"
}
},
{
"owner": "Immutable",
"reference": {
"objectId": "0x737cad1d42af9a3ce80d737b1eca05faa8f040667ce63c7b8b785beb8eee0cc9",
"version": 3,
"digest": "CMStx1LeqCBRsajPx4v4fp7FSDW4NMYsk1jnx1nuQXEj"
}
},
{
"owner": {
"AddressOwner": "0x51b53d828382b39c820694f4c601d1f413e68b1d963e735aef6637fe270d4d68"
},
"reference": {
"objectId": "0xdc3a30681099a3121adb4d6a6a1ab0901872bccf040b699acb8fa5b3897bc3f6",
"version": 3,
"digest": "GxkGYWT6GvZapMm8phePSTYiAENwfqzcdbUMNjTJbabS"
}
},
{
"owner": {
"AddressOwner": "0x51b53d828382b39c820694f4c601d1f413e68b1d963e735aef6637fe270d4d68"
},
"reference": {
"objectId": "0xf7c9802c55fccd299b68d201f68d3c7165fe0040d30aa6b01f889ce024f132f4",
"version": 3,
"digest": "AN9tG1omyRPaKEtsPh3S9iwnThWFuvMgsXsp9oZsGqXK"
}
}
],
"mutated": [
{
"owner": {
"AddressOwner": "0x51b53d828382b39c820694f4c601d1f413e68b1d963e735aef6637fe270d4d68"
},
"reference": {
"objectId": "0x0610d6a78501b689300e5d1fc785667f729edd75675ed0b5f9391cee9152aded",
"version": 3,
"digest": "AyA5SNDgF9J1YabEzhFsgEcFbUpNReWs6G7GfR7z9kuZ"
}
}
],
"gasObject": {
"owner": {
"AddressOwner": "0x51b53d828382b39c820694f4c601d1f413e68b1d963e735aef6637fe270d4d68"
},
"reference": {
"objectId": "0x0610d6a78501b689300e5d1fc785667f729edd75675ed0b5f9391cee9152aded",
"version": 3,
"digest": "AyA5SNDgF9J1YabEzhFsgEcFbUpNReWs6G7GfR7z9kuZ"
}
},
"dependencies": [
"43xRGMCLDNyNxfCCRwyTd5A9d1JEXMqgjR73rSdxP5pp",
"9YFcEEL2UYQpC8jujNzT2Y4cskrbSpTn9VZftEv5xFa9"
]
},
"events": [],
"objectChanges": [
{
"type": "mutated",
"sender": "0x51b53d828382b39c820694f4c601d1f413e68b1d963e735aef6637fe270d4d68",
"owner": {
"AddressOwner": "0x51b53d828382b39c820694f4c601d1f413e68b1d963e735aef6637fe270d4d68"
},
"objectType": "0x2::coin::Coin<0x2::sui::SUI>",
"objectId": "0x0610d6a78501b689300e5d1fc785667f729edd75675ed0b5f9391cee9152aded",
"version": "3",
"previousVersion": "2",
"digest": "AyA5SNDgF9J1YabEzhFsgEcFbUpNReWs6G7GfR7z9kuZ"
},
{
"type": "published",
"packageId": "0x00e0ebe33cd95b7084acc08c56e3b93df2f2f847545b83eb5cd00e638eb0b800",
"version": "1",
"digest": "21WwdEYAUapR4UmPF3A8wWaqnsmXBUfLmMxb6ehzu83u",
"modules": [
"e4c"
]
},
{
"type": "created",
"sender": "0x51b53d828382b39c820694f4c601d1f413e68b1d963e735aef6637fe270d4d68",
"owner": "Immutable",
"objectType": "0x2::coin::CoinMetadata<0x00e0ebe33cd95b7084acc08c56e3b93df2f2f847545b83eb5cd00e638eb0b800::e4c::E4C>",
"objectId": "0x2ce11233569304f64f96c3dbf8a1aeb99a0d2d420f0a84092166ac5d50d6087f",
"version": "3",
"digest": "CKFPxn81q8GxQsaQq3T5LNWtE6enQQ1mmwdLRUe2ntrw"
},
{
"type": "created",
"sender": "0x51b53d828382b39c820694f4c601d1f413e68b1d963e735aef6637fe270d4d68",
"owner": "Immutable",
"objectType": "0x2::coin::RegulatedCoinMetadata<0x00e0ebe33cd95b7084acc08c56e3b93df2f2f847545b83eb5cd00e638eb0b800::e4c::E4C>",
"objectId": "0x6b0feae03901037f3db1f18b694b5d18197e74906579823b9958cbdbf6f87278",
"version": "3",
"digest": "2UpNB69ZBqR9GsQJqfz4jyc5yx9FEvTFsLMEiHK4kJFi"
},
{
"type": "created",
"sender": "0x51b53d828382b39c820694f4c601d1f413e68b1d963e735aef6637fe270d4d68",
"owner": {
"AddressOwner": "0x51b53d828382b39c820694f4c601d1f413e68b1d963e735aef6637fe270d4d68"
},
"objectType": "0x2::coin::DenyCap<0x00e0ebe33cd95b7084acc08c56e3b93df2f2f847545b83eb5cd00e638eb0b800::e4c::E4C>",
"objectId": "0x6fbd871137808ba4ea610fa302fa0943c83f67a8f5a6b0468ff3288303392c57",
"version": "3",
"digest": "fdBKCwEEqxk5MwjsVpZ4SwYMkV6ehTEtXjGHbikKYsF"
},
{
"type": "created",
"sender": "0x51b53d828382b39c820694f4c601d1f413e68b1d963e735aef6637fe270d4d68",
"owner": "Immutable",
"objectType": "0x00e0ebe33cd95b7084acc08c56e3b93df2f2f847545b83eb5cd00e638eb0b800::e4c::E4CTotalSupply",
"objectId": "0x737cad1d42af9a3ce80d737b1eca05faa8f040667ce63c7b8b785beb8eee0cc9",
"version": "3",
"digest": "CMStx1LeqCBRsajPx4v4fp7FSDW4NMYsk1jnx1nuQXEj"
},
{
"type": "created",
"sender": "0x51b53d828382b39c820694f4c601d1f413e68b1d963e735aef6637fe270d4d68",
"owner": {
"AddressOwner": "0x51b53d828382b39c820694f4c601d1f413e68b1d963e735aef6637fe270d4d68"
},
"objectType": "0x2::coin::Coin<0x00e0ebe33cd95b7084acc08c56e3b93df2f2f847545b83eb5cd00e638eb0b800::e4c::E4C>",
"objectId": "0xdc3a30681099a3121adb4d6a6a1ab0901872bccf040b699acb8fa5b3897bc3f6",
"version": "3",
"digest": "GxkGYWT6GvZapMm8phePSTYiAENwfqzcdbUMNjTJbabS"
},
{
"type": "created",
"sender": "0x51b53d828382b39c820694f4c601d1f413e68b1d963e735aef6637fe270d4d68",
"owner": {
"AddressOwner": "0x51b53d828382b39c820694f4c601d1f413e68b1d963e735aef6637fe270d4d68"
},
"objectType": "0x2::package::UpgradeCap",
"objectId": "0xf7c9802c55fccd299b68d201f68d3c7165fe0040d30aa6b01f889ce024f132f4",
"version": "3",
"digest": "AN9tG1omyRPaKEtsPh3S9iwnThWFuvMgsXsp9oZsGqXK"
}
],
"balanceChanges": [
{
"owner": {
"AddressOwner": "0x51b53d828382b39c820694f4c601d1f413e68b1d963e735aef6637fe270d4d68"
},
"coinType": "0x2::sui::SUI",
"amount": "-20002280"
},
{
"owner": {
"AddressOwner": "0x51b53d828382b39c820694f4c601d1f413e68b1d963e735aef6637fe270d4d68"
},
"coinType": "0x00e0ebe33cd95b7084acc08c56e3b93df2f2f847545b83eb5cd00e638eb0b800::e4c::E4C",
"amount": "100000000000"
}
],
"confirmedLocalExecution": true
}
Coin distribution
Distribute $MYCOIN coins from the multisig address to shareholders and CEXs as needed.
Serialize transactions
Serialize any transaction like the transfer
transaction and follow the same process from Step 6. This section demonstrates how to use an object that belongs to a multisig address and serialize a transfer to be signed. The tx_bytes
value can be any serialized transaction data where the sender is the multisig address. Use the --serialize-unsigned-transaction
flag for supported commands in sui client -h
(publish
, upgrade
, call
, transfer
, transfer-sui
, pay
, pay-all-sui
, pay-sui
, split
, merge-coin
) to output the Base64 encoded transaction bytes.
sui client transfer --to <SUI-ADDRESS> --object-id <OBJECT-ID> --gas-budget <GAS-AMOUNT> --serialize-unsigned-transaction
Raw tx_bytes to execute: <TX_BYTES>
The go-to checklist for coin launch
The go-to checklist for coin launch is a comprehensive and easy-to-follow checklist designed to ensure a smooth and successful launch of a coin on Sui blockchain.
Coin deployment and distribution
Set up a local Sui deployment environment
Determine how to manage accounts that will hold coin-related key objects
Get a certain amount of SUI coins to pay for the gas needed to deploy
Prepare a reliable RPC gateway for transaction execution and querying
Implement the coin distribution logic (fair launch, presale, airdrop, etc.)
Prepare the token distribution plan and whitelists (if applicable)
Set up a token sale platform or distribution mechanism (if applicable)
Integration and listing
Integrate the coin with popular Sui-based wallets to be listed as a recognized
Apply for recognition on popular explorers as an approved coin
Apply for listing on decentralized exchanges (DEXs)
Explore centralized exchanges (CEXs) listings