Skip to content

Latest commit

 

History

History
143 lines (113 loc) · 6.55 KB

File metadata and controls

143 lines (113 loc) · 6.55 KB

02 - Mental Model Shift: EVM -> Sui

Path: Learning Path > 02 Mental Model Shift

This repo assumes you already think in Solidity. The goal here is not to re-teach smart contracts, but to rewire a few instincts so Sui feels natural.

1. Learning goals

  1. Understand what changes (objects, ownership, capabilities, packages).
  2. Know how this repo is laid out for a linear learning path.
  3. Run a quick environment sanity check before touching code.

2. EVM -> Sui translation

  1. Contract storage -> objects + tables: your state lives in owned/shared objects and typed table entries, not in a single contract storage map. See packages/dapp/contracts/oracle-market/sources/shop.move (Shop, Discount, Shop.listings, Shop.accepted_currencies).
  2. onlyOwner -> capability: authority is proved by holding a capability object. See ShopOwnerCap in packages/dapp/contracts/oracle-market/sources/shop.move.
  3. Deployment -> publish + instantiate: publishing creates a package object; stateful instances are created later as shared objects. See publish flow in packages/dapp/src/scripts/contracts/publish.ts and shop creation in packages/dapp/src/scripts/owner/shop-create.ts.
  4. Inheritance -> modules + generics: Move has no inheritance or dynamic dispatch; reuse is done through modules, functions, and type parameters. This repo uses ShopItem<phantom T> and Coin<C> to keep types safe without polymorphism.
  5. Composability -> PTBs: you compose calls at runtime in a programmable transaction block (PTB), rather than writing a single on-chain "router" contract for every workflow.
  6. Upgrades -> new package + UpgradeCap: upgrades publish a new package ID gated by an UpgradeCap. Callers opt into new package IDs explicitly.

3. Concept deep dive: abilities and resources

  • Abilities (key, store, copy, drop): abilities declare how values can be stored and moved. key turns a struct into an object with identity. store lets it live on-chain. copy and drop opt into value semantics. In this repo, objects like Shop and ShopOwnerCap are has key, store, while events are has copy, drop. Code: packages/dapp/contracts/oracle-market/sources/shop.move (struct definitions)
  • Resources and ownership: Move resources must be moved, not copied. Owned objects (like ShopOwnerCap) are authority tokens. Passing a resource by value is a one-time action, which is why capability and coin flows remain explicit. Code: packages/dapp/contracts/oracle-market/sources/shop.move (ShopOwnerCap)
  • Object ownership types: Sui supports address-owned, shared, immutable, and object-owned objects. This repo uses address-owned capabilities/receipts, shared objects for Shop/discounts, and object-owned dynamic-field children under tables. Code: packages/dapp/contracts/oracle-market/sources/shop.move (ShopOwnerCap, Shop)
  • Strings (String): Sui Move’s String type is designed for user-facing, human-readable text and is always UTF-8 encoded. This is different from Solidity, where string is a dynamic byte array and encoding is not enforced. Using String ensures your data is valid UTF-8, which is important for interoperability and user interfaces. Many Sui and Move standard library functions expect or return String, making it the idiomatic choice for names, descriptions, and other text fields. Use vector only when you need to store arbitrary bytes (e.g., binary data, hashes, or non-UTF-8 content). Code: packages/dapp/contracts/oracle-market/sources/shop.move (Shop.name, ItemListing.name)
  • Options instead of sentinels: optional values use Option instead of magic constants. This is used for optional listing links, discount expiry, and max-redemption caps. Code: packages/dapp/contracts/oracle-market/sources/shop.move (Option fields)

4. Code references

  1. packages/dapp/contracts/oracle-market/sources/shop.move (Shop, ShopOwnerCap, entry functions)
  2. packages/dapp/src/scripts/contracts/publish.ts (publish flow + artifacts)
  3. packages/dapp/src/scripts/owner/shop-create.ts (shop instantiation)

Code spotlight: object-first state + capability auth packages/dapp/contracts/oracle-market/sources/shop.move

public struct ShopOwnerCap has key, store {
  id: UID,
  shop_id: ID,
}

public struct Shop has key, store {
  id: UID,
  owner: address,
  name: String,
  disabled: bool,
  accepted_currencies: Table<TypeName, AcceptedCurrency>,
  listings: Table<ID, ItemListing>,
}

Code spotlight: publish flow resolves package + artifacts packages/dapp/src/scripts/contracts/publish.ts

const fullPackagePath = resolveFullPackagePath(
  path.resolve(tooling.suiConfig.paths.move),
  cliArguments.packagePath
)

const deploymentArtifacts = await loadDeploymentArtifacts(
  tooling.suiConfig.network.networkName
)

if (
  await shouldSkipPublish(
    tooling,
    cliArguments.rePublish,
    deploymentArtifacts,
    fullPackagePath
  )
) {
  logSkippedPublish(tooling.suiConfig.network.networkName, fullPackagePath)
  return
}

await publishPackageToNetwork(
  tooling,
  fullPackagePath,
  derivePublishOptions(tooling.suiConfig.network.networkName, cliArguments)
)

Code spotlight: instantiate and share a Shop after publish packages/dapp/contracts/oracle-market/sources/shop.move

public fun create_shop_and_share(name: String, ctx: &mut TxContext): (ID, ShopOwnerCap) {
  let (shop, owner_cap) = create_shop(name, ctx);
  let shop_id = shop.id();

  transfer::public_share_object(shop);
  (shop_id, owner_cap)
}

7. Exercises

  1. Open packages/dapp/contracts/oracle-market/sources/shop.move and find ShopOwnerCap. Expected outcome: you can explain why it replaces onlyOwner.
  2. Skim packages/dapp/src/scripts/contracts/publish.ts and list the artifacts it writes. Expected outcome: you can point to packages/dapp/deployments/deployment.<network>.json.

8. Diagram: object-centric state

EVM: contract storage
  Contract
    mapping(listingId => Listing)

Sui: shared objects + typed tables
  Shop (shared)
    table listings: listing_id (ID) -> ItemListing
    table accepted_currencies: coin_type (TypeName) -> AcceptedCurrency
  Discount (shared)

9. Further reading (Sui docs)

10. Navigation

  1. Previous: 01 Repo Layout + How to Navigate
  2. Next: 03 EVM → Sui Cheatsheet
  3. Back to map: Learning Path Map