How is building on Sui different from Ethereum?
October 29, 2026
Building on Sui means writing Move instead of Solidity and modelling state as objects instead of contract storage. On Ethereum, a contract owns its data, often in mappings keyed by address. On Sui, each piece of state is an object with an owner, and transactions pass objects into functions explicitly. Transactions are different too: one programmable transaction block (PTB) can chain up to 1,024 calls atomically, so many things that need a router contract on Ethereum are composed client-side on Sui.
State: mappings vs objects
A typical ERC-20 keeps mapping(address => uint256) balances. On Sui, a balance is a Coin<T> object owned by the holder. An NFT is just an object in your wallet, not an entry in a registry. Data that many users have to write to lives in a shared object. Data that belongs to one user lives in an owned object. That one choice decides whether a transaction has to wait for consensus ordering against other users. See shared vs owned objects.
module example::profile;
use std::string::String;
public struct Profile has key, store {
id: UID,
name: String,
}
// Instead of profiles[msg.sender] = ..., mint an object and give it to the sender
public fun create(name: String, ctx: &mut TxContext) {
let p = Profile { id: object::new(ctx), name };
transfer::transfer(p, ctx.sender());
}Access control
Instead of Ownable and require(msg.sender == owner), Sui code usually uses capability objects. A function takes &AdminCap, and only the address holding that object can call it. Capabilities can be transferred, wrapped or destroyed like any other object.
Transactions
| Ethereum | Sui |
|---|---|
| One contract call per transaction | A PTB with up to 1,024 commands, atomic |
| Nonce per account | No nonces; object versions prevent replays |
approve + transferFrom |
Pass the coin object directly into the call |
| Mempool, then block inclusion | Full node → validator → consensus; typically 400–700 ms to finality |
| Gas paid in ETH (wei) | Gas paid in SUI (MIST, 10^-9 SUI), plus a refundable storage fee |
import { Transaction } from '@mysten/sui/transactions';
const tx = new Transaction();
const [payment] = tx.splitCoins(tx.gas, [1_000_000_000]); // 1 SUI
tx.moveCall({
target: `${PACKAGE_ID}::shop::buy`,
arguments: [tx.object(SHOP_ID), payment],
});No approval step is needed: the coin goes straight into the function. See gas budgets for how fees work.
Upgrades
Ethereum upgradeability usually means proxies. On Sui, package upgrades are native and controlled by an UpgradeCap object. An upgrade publishes a new package version at a new ID, and the old version stays on-chain. Shared objects normally carry a version field so that new code can block calls from old package versions.
Storage fees
On Sui you pay a storage fee when you create or grow an object, and you get most of it back as a rebate when you delete it. Ethereum doesn't refund storage. That gives Sui contracts a reason to clean up objects they no longer need.
Tooling
| Ethereum | Sui | |
|---|---|---|
| Language | Solidity, Vyper | Move (2024 edition) |
| Framework / CLI | Foundry, Hardhat | Sui CLI (sui move build, sui move test, sui client publish) |
| Client SDKs | ethers, viem | @mysten/sui, Rust SDK |
| Reading chain data | JSON-RPC | gRPC and GraphQL (JSON-RPC on public fullnodes is deprecated) |
| Wallet login | Wallet signatures | Wallets plus zkLogin and passkeys |
What stays familiar
Events exist (sui::event::emit), addresses are hex strings, and secp256k1 keys are supported. Sui is not EVM compatible, though, so Solidity code has to be rewritten. To try it out, get testnet SUI and send a PTB in the playground.
Sources
- Ethereum to Sui, Sui documentation
- Sui Move concepts, Sui documentation
- Transaction life cycle, Sui documentation