Developer Cheat Sheet
Quick reference on best practices for Sui Network developers.
Move​
Best practices for writing Move smart contracts on Sui.
General​
- Read the Code Quality Checklist for best practices in Move development.
- Follow the Move conventions for consistent naming and coding style.
- Use
vector-backed collections (vector,VecSet,VecMap,PriorityQueue) with a known maximum size of 1000 items or fewer.- Use dynamic field-backed collections (
Table,Bag,ObjectBag,ObjectTable,LinkedTable) for any collection that allows third-party addition, larger collections, and collections of unknown size. - Move objects have a maximum size of 250KB. Any attempt to create a larger object causes an aborted transaction. Ensure that your objects do not have an ever-growing
vector-backed collection.
- Use dynamic field-backed collections (
- If your function
fneeds a payment in, for example, SUI from the caller, usefun f(payment: Coin<SUI>)notfun f(payment: &mut Coin<SUI>, amount: u64). This is safer for callers; they know exactly how much they are paying, and do not need to trustfto extract the right amount.
Composability​
- Use Sui Object Display to customize how your objects appear in wallets, apps, and explorers.
- Avoid self-transfers. When possible, return the object from the current function so that it can be used in a different command in a programmable transaction block.
Package upgrades​
- Read about package upgrades before publishing your package.
- Packages are immutable, so any published package can be called forever. Use object versioning to prevent older versions from being called.
- If you upgrade a package
P1toP2, other packages and clients that depend onP1will continue usingP1. They do not auto-update toP2. Both dependent packages and client code must be explicitly updated to point atP2. - Packages that expect to be extended by dependent packages can avoid breaking their extensions with each upgrade by providing a standard (unchanging) interface that all versions conform to. See the message sending across a bridge example from Wormhole. Extension packages that produce outbound messages can use
prepare_messagefrom any version of the Wormhole package to produce aMessageTicketwhile client code that sends the message must pass thatMessageTicketintopublish_messagein the latest version of the package.publicfunction signatures cannot be deleted or changed, butpublic(package)functions can. Usepublic(package)or private visibility liberally unless you are exposing library functions that will live forever.- It is not possible to delete
structtypes, change their definition, or add new abilities through an upgrade. Introduce new types carefully because they live forever.
Testing​
- Use the
sui::test_scenariomodule to mimic multi-transaction, multi-sender test scenarios. - Use the
std::unit_testmodule forassert_eq!andassert_ref_eq!macros for better test error messages. - Use the
sui::test_utilsmodule for black-hole functiondestroy. - Use the
std::debugmodule for debug printing throughprint. - Use
sui move test --coverageto compute code coverage information for your tests, andsui move coverage source --module <name>to see uncovered lines highlighted in red. Push coverage all the way to 100% if feasible.
Apps​
- For optimal performance and data consistency, submit writes and reads to the same full node. In the TS SDK, use the wallet's
signTransactionBlockAPI, then submit the transaction through a call toexecute_transactionBlockon your app's full node instead of using the wallet'ssignAndExecuteTransactionBlockAPI. This ensures read-after-write consistency. Reads from your app's full node reflect writes from the transaction right away instead of waiting for a checkpoint. - Implement a local cache for frequently read data rather than over-fetching from the full node.
- Whenever possible, use programmable transaction blocks to compose existing onchain functionality rather than publishing new smart contract code. Programmable transaction blocks allow large-scale batching and heterogeneous composition, driving already-low gas fees down even further.
- Leave gas budget, gas price, and coin selection to the wallet. This gives wallets more flexibility, and the wallet is responsible for dry running a transaction to ensure it does not fail.
Signing​
- Never sign two concurrent transactions that touch the same owned object. Either use independent owned objects, or wait for one transaction to conclude before sending the next one. Violating this rule might lead to client equivocation, which locks the owned objects involved in the two transactions until the end of the current epoch.
- Any
sui clientcommand that crafts a transaction (for example,sui client publishorsui client call) can accept the--serialize-outputflag to output a base64 transaction for signing. - Sui supports several signature schemes for transaction signing, including native multisig.
zkLogin​
- Call the proving service as sparingly as possible. Design your app flows such that you call the proving service only when the user is about to perform a real transaction.
- Beware of how you cache the ephemeral private key. Treat the private key as highly sensitive data, such as a password. If an unexpired ephemeral private key and its corresponding ZK proof are leaked, an attacker can steal the user's assets.