Written by: Sui Network

Sui is a L1 public chain redesigned and built from first principles, designed to provide creators and developers with a development platform capable of supporting the next billion users in Web3. Applications on Sui are based on the Move smart contract language and are horizontally scalable, allowing developers to support a wide range of application development quickly and at low cost. The Sui mainnet was officially launched on May 3, 2023.

This article will serve as a quick reference for best practices for developers on Sui Network.

Move general knowledge

Read details about package upgrades and write upgrade-friendly code.

Packages are immutable, and vulnerable package code can be called forever. The solution is to add protection at the object level. If a package is upgraded from P to P', other packages and clients that depend on P will continue to use P instead of automatically updating to P'. Therefore, both package-dependent and client-dependent code must be updated to explicitly point to P'. Packages that expect to be extended by dependent packages can avoid breaking their previous extensions every time they are upgraded by providing (unchanged) interfaces that meet all version standards. Taking the Wormhole cross-chain bridge as an example, to send messages through Wormhole as a bridge, to generate an extension package that sends messages, you can use the prepare_message instruction in any version of the Wormhole package to generate a MessageTicket, and the client code that sends messages must pass the MessageTicket to publish_message in the latest version of the package. Public functions cannot be deleted or changed, but public(friend) functions can. You are free to use public(friend) or self-visible functions unless you want to make the current library functions public forever. You cannot delete struct types, add new fields (although you can add dynamic fields), or upgrade new functions. Please think carefully when adding new types, because once added they will exist forever!

Use vector-backed collections (e.g. vector, VecSet, VecMap, PriorityQueue) with a maximum of 1000 items.

Use dynamic field-backed collections (e.g. Table, Bag, ObjectBag, ObjectTable, LinkedTable) for any collections that allow third-party additions, larger collections, and collections of unknown size. Sui Move objects are limited to 250KB in size - any attempt to create a larger object will cause the transaction to abort, make sure your objects do not continue to grow larger than the collection backed by the vector.

If your function f requires a payment from the caller, for example using SUI, use fun f(payment: Coin) instead of fun f(payment: &mut Coin, amount: u64) . This is safer for the caller because they know exactly what the payment amount is and don't need to rely on function f to extract the correct amount.

There is no need to optimize gas consumption by small amounts. When calculating the cost on Sui, the cost is rounded to the nearest bucket, so only very drastic fluctuations will cause gas to change. In particular, if your transaction is already in the lowest cost range, it cannot be cheaper. See the figure below for details.

Follow the Move coding conventions for a consistent style.

Composability Customize how your objects appear in wallets, applications, and browsers using the display standard. Avoid using "self-transfer" functions - whenever possible, return obj from the current function instead of writing transfer::transfer(obj, tx_context::sender(ctx)), which allows the caller or a programmable transaction block to use obj. Testing Use sui::test_scenario` to simulate test scenarios with multiple transactions and multiple senders. Use sui::test_utilsmodule to get better error messages with assert_eq tests, debug printing with print, and test-only destruction with destroy. Use sui::test_utilsmodule to calculate code coverage information for tests, and sui move coverage source --module to see uncovered lines highlighted in red. It is recommended to set coverage to 100% when feasible. Applications For best performance and data consistency, applications should submit write and read requests on the same full node. In the TS SDK, this means that applications should use the wallet's signTransactionBlock API and then submit transactions by calling execute_transactionBlock on the application's full node, rather than using the wallet's signAndExecuteTransactionBlock API. This ensures write-before-read consistency - reading from the application's full node will immediately reflect the transaction's writes, rather than waiting for a checkpoint. To reduce latency, if your application needs to know that a transaction has been confirmed, but does not need to immediately see the transaction's effects or read the objects/events written by the transaction, use executeTransactionBlock with "showEffects": false and "showEvents": false. Applications should cache frequently read data locally rather than frequently fetching from the full node. Whenever possible, use programmable transaction blocks to compose existing on-chain functionality rather than releasing new smart contract code. Programmable transaction blocks allow for large-scale batching and heterogeneous composition, further reducing already low gas fees. Applications should leave the choice of gas budget, gas price, and coin to wallets, which will provide wallets with greater flexibility, and it is the wallet's responsibility to test-run transactions to ensure that transactions do not fail.Signatures Never sign two concurrent transactions that touch the same exclusive object, either use the exclusive object separately or wait for one transaction to end before sending the next. Violating this rule may cause clients to be ambiguous, thereby locking the exclusive objects involved in two transactions at the same time until the end of the current epoch. Any sui client command that initiates a transaction (such as sui client publish, sui client call) can accept the --serialize-output flag to output the base64 transaction to be signed. Sui supports multiple signature schemes for transaction signing, including native multi-signature.