Skip to main content

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.
  • If your function f needs a payment in, for example, SUI from the caller, use fun f(payment: Coin<SUI>) not fun 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 trust f to 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 P1 to P2, other packages and clients that depend on P1 will continue using P1. They do not auto-update to P2. Both dependent packages and client code must be explicitly updated to point at P2.
  • 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_message from any version of the Wormhole package to produce a MessageTicket while client code that sends the message must pass that MessageTicket into publish_message in the latest version of the package.
    • public function signatures cannot be deleted or changed, but public(package) functions can. Use public(package) or private visibility liberally unless you are exposing library functions that will live forever.
    • It is not possible to delete struct types, change their definition, or add new abilities through an upgrade. Introduce new types carefully because they live forever.

Testing​

  • Use the sui::test_scenario module to mimic multi-transaction, multi-sender test scenarios.
  • Use the std::unit_test module for assert_eq! and assert_ref_eq! macros for better test error messages.
  • Use the sui::test_utils module for black-hole function destroy.
  • Use the std::debug module for debug printing through print.
  • Use sui move test --coverage to compute code coverage information for your tests, and sui 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 signTransactionBlock API, then submit the transaction through a call to execute_transactionBlock on your app's full node instead of using the wallet's signAndExecuteTransactionBlock API. 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 client command that crafts a transaction (for example, sui client publish or sui client call) can accept the --serialize-output flag 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.