Web3 开发实战:Sui 区块链从零到上手
Table of Contents
Web3 开发实战:Sui 区块链从零到上手
Web3 浪潮席卷而来,Sui 作为新一代高性能区块链,以其独特的 Move 语言和创新设计脱颖而出。你是否想快速入门 Web3 开发,却不知从何下手?本文将带你从零开始,通过实战操作掌握 Sui 区块链的核心技能。从安装 Sui CLI 到编写智能合约,再到打造一个前端 DApp,全程手把手教学。不管你是区块链小白还是想探索新平台的开发者,这篇指南都将是你开启 Sui Web3 之旅的完美起点!
本文通过详细的实战步骤,展示了如何在 Sui 区块链上从零开始完成开发全流程。内容涵盖 Sui CLI 的安装与配置、钱包地址和代币余额管理、Move 语言编写的计数器智能合约开发与部署,以及基于 React 和 @mysten /dapp-kit 的前端 DApp 搭建。每个环节配有代码示例、运行结果和浏览器交互演示,让你轻松理解 Sui 的核心功能和操作逻辑。无论你是想快速上手 Web3 开发,还是探索 Sui 生态的潜力,这篇教程都能帮你打下坚实基础。
实操
安装
brew install sui
更新
brew upgrade sui
==> Auto-updating Homebrew...
Adjust how often this is run with HOMEBREW_AUTO_UPDATE_SECS or disable with
HOMEBREW_NO_AUTO_UPDATE. Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
==> Auto-updated Homebrew!
Updated 3 taps (pulumi/tap, homebrew/core and homebrew/cask).
==> New Formulae
geesefs harsh lld@19 llvm@19
==> New Casks
bambu-connect earnapp font-playpen-sans-deva inmusic-software-center restapia
captainplugins font-lxgw-wenkai-gb-lite hamrs-pro moment slidepad
You have 60 outdated formulae installed.
Warning: sui 1.45.2 already installed
查看 Sui 版本号
sui --version
sui 1.45.2-homebrew
查看当前活动的默认钱包地址
sui client active-address
[warning] Client/Server api version mismatch, client api version : 1.45.2, server api version : 1.45.1
0x35370841d2e69b495b1e2f944a3087e4242f314e503691a00b054e0ee2a45a73
列出当前 Sui 客户端配置中所有可用的钱包地址
sui client addresses
[warning] Client/Server api version mismatch, client api version : 1.45.2, server api version : 1.45.1
╭──────────────────────┬────────────────────────────────────────────────────────────────────┬────────────────╮
│ alias │ address │ active address │
├──────────────────────┼────────────────────────────────────────────────────────────────────┼────────────────┤
│ dazzling-chrysoprase │ 0x35370841d2e69b495b1e2f944a3087e4242f314e503691a00b054e0ee2a45a73 │ * │
╰──────────────────────┴────────────────────────────────────────────────────────────────────┴────────────────╯
查询指定钱包地址的 SUI 代币余额
sui client balance
[warning] Client/Server api version mismatch, client api version : 1.45.2, server api version : 1.45.1
╭────────────────────────────────────────╮
│ Balance of coins owned by this address │
├────────────────────────────────────────┤
│ ╭──────────────────────────────────╮ │
│ │ coin balance (raw) balance │ │
│ ├──────────────────────────────────┤ │
│ │ Sui 14829596064 14.82 SUI │ │
│ │ WAL 0 0.00 WAL │ │
│ ╰──────────────────────────────────╯ │
╰────────────────────────────────────────╯
sui client balance 0x35370841d2e69b495b1e2f944a3087e4242f314e503691a00b054e0ee2a45a73
[warning] Client/Server api version mismatch, client api version : 1.45.2, server api version : 1.45.1
╭────────────────────────────────────────╮
│ Balance of coins owned by this address │
├────────────────────────────────────────┤
│ ╭──────────────────────────────────╮ │
│ │ coin balance (raw) balance │ │
│ ├──────────────────────────────────┤ │
│ │ Sui 14829596064 14.82 SUI │ │
│ │ WAL 0 0.00 WAL │ │
│ ╰──────────────────────────────────╯ │
╰────────────────────────────────────────╯
sui client balance 0x6518cfc4854eb8b175c406e25e16e5042cf84a6c91c6eea9485eebeb18df4df0
[warning] Client/Server api version mismatch, client api version : 1.45.2, server api version : 1.45.1
╭────────────────────────────────────────╮
│ Balance of coins owned by this address │
├────────────────────────────────────────┤
│ ╭─────────────────────────────────╮ │
│ │ coin balance (raw) balance │ │
│ ├─────────────────────────────────┤ │
│ │ Sui 6987944472 6.98 SUI │ │
│ │ WAL 0 0.00 WAL │ │
│ ╰─────────────────────────────────╯ │
╰────────────────────────────────────────╯
列出当前活跃地址拥有的所有 Gas 对象(Gas Objects)
sui client gas
[warning] Client/Server api version mismatch, client api version : 1.45.2, server api version : 1.45.1
╭────────────────────────────────────────────────────────────────────┬────────────────────┬──────────────────╮
│ gasCoinId │ mistBalance (MIST) │ suiBalance (SUI) │
├────────────────────────────────────────────────────────────────────┼────────────────────┼──────────────────┤
│ 0x09ae107f8b03e0297bd8419d9aba9cc3358dd638ca3a8d7cf7c60de3c0eb51ed │ 14829596064 │ 14.82 │
╰────────────────────────────────────────────────────────────────────┴────────────────────┴──────────────────╯
列出所有已配置的 Sui 网络环境(Environments)
sui client envs
[warning] Client/Server api version mismatch, client api version : 1.45.2, server api version : 1.45.1
╭─────────┬─────────────────────────────────────┬────────╮
│ alias │ url │ active │
├─────────┼─────────────────────────────────────┼────────┤
│ devnet │ https://fullnode.devnet.sui.io:443 │ │
│ mainnet │ https://fullnode.mainnet.sui.io:443 │ │
│ testnet │ https://fullnode.testnet.sui.io:443 │ * │
╰─────────┴─────────────────────────────────────┴────────╯
显示当前 Sui CLI 正在使用的网络环境(environment)
sui client active-env
[warning] Client/Server api version mismatch, client api version : 1.45.2, server api version : 1.45.1
testnet
将当前 CLI 会话切换到 Sui 主网环境
sui client switch --env mainnet
[warning] Client/Server api version mismatch, client api version : 1.45.2, server api version : 1.45.1
Active environment switched to [mainnet]
sui client active-env
[warning] Client/Server api version mismatch, client api version : 1.45.2, server api version : 1.45.3
mainnet
sui client envs
[warning] Client/Server api version mismatch, client api version : 1.45.2, server api version : 1.45.3
╭─────────┬─────────────────────────────────────┬────────╮
│ alias │ url │ active │
├─────────┼─────────────────────────────────────┼────────┤
│ devnet │ https://fullnode.devnet.sui.io:443 │ │
│ mainnet │ https://fullnode.mainnet.sui.io:443 │ * │
│ testnet │ https://fullnode.testnet.sui.io:443 │ │
╰─────────┴─────────────────────────────────────┴────────╯
sui client switch --env testnet
[warning] Client/Server api version mismatch, client api version : 1.45.2, server api version : 1.45.3
Active environment switched to [testnet]
sui client active-env
[warning] Client/Server api version mismatch, client api version : 1.45.2, server api version : 1.45.1
testnet
创建项目
创建一个新的 Sui Move Dapp项目
https://sdk.mystenlabs.com/dapp-kit/create-dapp
pnpm create @mysten/dapp
✔ Which starter template would you like to use? · react-e2e-counter
✔ What is the name of your dApp? (this will be used as the directory name) · sui-dapp
切换到项目目录并用RustRover
打开项目
cd sui-dapp/
open -a RustRover .
查看项目目录结构
hello_sui_move/sui-dapp on main [?] via ⬢ v22.1.0 via 🅒 base
➜ tree . -L 6 -I 'target|cache|lib|build|node_modules'
.
├── README.md
├── index.html
├── move
│ └── counter
│ ├── Move.lock
│ ├── Move.toml
│ └── sources
│ └── counter.move
├── package.json
├── pnpm-lock.yaml
├── prettier.config.cjs
├── src
│ ├── App.tsx
│ ├── Counter.tsx
│ ├── CreateCounter.tsx
│ ├── constants.ts
│ ├── main.tsx
│ ├── networkConfig.ts
│ └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.mts
5 directories, 18 files
合约代码counter.move
module counter::counter {
public struct Counter has key {
id: UID,
owner: address,
value: u64
}
public fun create(ctx: &mut TxContext) {
transfer::share_object(Counter {
id: object::new(ctx),
owner: ctx.sender(),
value: 0
})
}
public fun increment(counter: &mut Counter) {
counter.value = counter.value + 1;
}
public fun set_value(counter: &mut Counter, value: u64, ctx: &TxContext) {
assert!(counter.owner == ctx.sender(), 0);
counter.value = value;
}
}
部署合约
hello_sui_move/sui-dapp on main [?] via ⬢ v22.1.0 via 🅒 base
➜ cd move/counter
hello_sui_move/sui-dapp/move/counter on main [?] via ⬢ v22.1.0 via 🅒 base
➜ ls
Move.toml sources
hello_sui_move/sui-dapp/move/counter on main [?] via ⬢ v22.1.0 via 🅒 base
➜ sui client publish --skip-dependency-verification
[warn] Client/Server api version mismatch, client api version : 1.32.0, server api version : 1.46.1
UPDATING GIT DEPENDENCY https://github.com/MystenLabs/sui.git
INCLUDING DEPENDENCY Sui
INCLUDING DEPENDENCY MoveStdlib
BUILDING counter
Skipping dependency verification
Transaction Digest: GjioTVaWBDXem8fXVL2PmYs3wjVNiyXBKBFUV4MRs4sc
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Transaction Data │
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Sender: 0x35370841d2e69b495b1e2f944a3087e4242f314e503691a00b054e0ee2a45a73 │
│ Gas Owner: 0x35370841d2e69b495b1e2f944a3087e4242f314e503691a00b054e0ee2a45a73 │
│ Gas Budget: 10033200 MIST │
│ Gas Price: 1000 MIST │
│ Gas Payment: │
│ ┌── │
│ │ ID: 0x09ae107f8b03e0297bd8419d9aba9cc3358dd638ca3a8d7cf7c60de3c0eb51ed │
│ │ Version: 370791513 │
│ │ Digest: 7aSdnD3XRN49WWApxK3LPhiFLzQrQ9gPdmhf2TfB7eYo │
│ └── │
│ │
│ Transaction Kind: Programmable │
│ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │
│ │ Input Objects │ │
│ ├──────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │
│ │ 0 Pure Arg: Type: address, Value: "0x35370841d2e69b495b1e2f944a3087e4242f314e503691a00b054e0ee2a45a73" │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
│ ╭─────────────────────────────────────────────────────────────────────────╮ │
│ │ Commands │ │
│ ├─────────────────────────────────────────────────────────────────────────┤ │
│ │ 0 Publish: │ │
│ │ ┌ │ │
│ │ │ Dependencies: │ │
│ │ │ 0x0000000000000000000000000000000000000000000000000000000000000001 │ │
│ │ │ 0x0000000000000000000000000000000000000000000000000000000000000002 │ │
│ │ └ │ │
│ │ │ │
│ │ 1 TransferObjects: │ │
│ │ ┌ │ │
│ │ │ Arguments: │ │
│ │ │ Result 0 │ │
│ │ │ Address: Input 0 │ │
│ │ └ │ │
│ ╰─────────────────────────────────────────────────────────────────────────╯ │
│ │
│ Signatures: │
│ bM5PpjP6DBvq3YtuRb+W9vI5QnjWHPCWMU7N5lKQtdYkKPd7KRopfy+FnLg8fBT62OGDqRTVZTuz048l70qbDA== │
│ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭───────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Transaction Effects │
├───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Digest: GjioTVaWBDXem8fXVL2PmYs3wjVNiyXBKBFUV4MRs4sc │
│ Status: Success │
│ Executed Epoch: 695 │
│ │
│ Created Objects: │
│ ┌── │
│ │ ID: 0x00012e566ad8b36293836f4901f72d32f181782299bf54724f7dbb6b718c9683 │
│ │ Owner: Immutable │
│ │ Version: 1 │
│ │ Digest: BwqnKimSHf4iD4Erf3Jwj4JntQRxMxYN5YZ4dGEB5Dx4 │
│ └── │
│ ┌── │
│ │ ID: 0x9d049515152a144171b4037eb8b5b24c2f811474321c121872e56c6273c99a20 │
│ │ Owner: Account Address ( 0x35370841d2e69b495b1e2f944a3087e4242f314e503691a00b054e0ee2a45a73 ) │
│ │ Version: 370791514 │
│ │ Digest: HZL8SN7iwmzy4Q2KjTLUxeWEv6Gb4oFXU2WwuiGvRpuQ │
│ └── │
│ Mutated Objects: │
│ ┌── │
│ │ ID: 0x09ae107f8b03e0297bd8419d9aba9cc3358dd638ca3a8d7cf7c60de3c0eb51ed │
│ │ Owner: Account Address ( 0x35370841d2e69b495b1e2f944a3087e4242f314e503691a00b054e0ee2a45a73 ) │
│ │ Version: 370791514 │
│ │ Digest: 4FrFJ4bYDx8yBAvnQJeBjJ768TcQ5EHLje33kJmReQRb │
│ └── │
│ Gas Object: │
│ ┌── │
│ │ ID: 0x09ae107f8b03e0297bd8419d9aba9cc3358dd638ca3a8d7cf7c60de3c0eb51ed │
│ │ Owner: Account Address ( 0x35370841d2e69b495b1e2f944a3087e4242f314e503691a00b054e0ee2a45a73 ) │
│ │ Version: 370791514 │
│ │ Digest: 4FrFJ4bYDx8yBAvnQJeBjJ768TcQ5EHLje33kJmReQRb │
│ └── │
│ Gas Cost Summary: │
│ Storage Cost: 8033200 MIST │
│ Computation Cost: 1000000 MIST │
│ Storage Rebate: 978120 MIST │
│ Non-refundable Storage Fee: 9880 MIST │
│ │
│ Transaction Dependencies: │
│ VRqNMSsv1sWT8v2yzYkUXozp3aupQVKM4HBM1T547Lf │
│ 2KKFDYfXCwBWaS1e3i4gLnjW1DsQoWqYQMb4SVBZFQR2 │
╰───────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─────────────────────────────╮
│ No transaction block events │
╰─────────────────────────────╯
╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Object Changes │
├──────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Created Objects: │
│ ┌── │
│ │ ObjectID: 0x9d049515152a144171b4037eb8b5b24c2f811474321c121872e56c6273c99a20 │
│ │ Sender: 0x35370841d2e69b495b1e2f944a3087e4242f314e503691a00b054e0ee2a45a73 │
│ │ Owner: Account Address ( 0x35370841d2e69b495b1e2f944a3087e4242f314e503691a00b054e0ee2a45a73 ) │
│ │ ObjectType: 0x2::package::UpgradeCap │
│ │ Version: 370791514 │
│ │ Digest: HZL8SN7iwmzy4Q2KjTLUxeWEv6Gb4oFXU2WwuiGvRpuQ │
│ └── │
│ Mutated Objects: │
│ ┌── │
│ │ ObjectID: 0x09ae107f8b03e0297bd8419d9aba9cc3358dd638ca3a8d7cf7c60de3c0eb51ed │
│ │ Sender: 0x35370841d2e69b495b1e2f944a3087e4242f314e503691a00b054e0ee2a45a73 │
│ │ Owner: Account Address ( 0x35370841d2e69b495b1e2f944a3087e4242f314e503691a00b054e0ee2a45a73 ) │
│ │ ObjectType: 0x2::coin::Coin<0x2::sui::SUI> │
│ │ Version: 370791514 │
│ │ Digest: 4FrFJ4bYDx8yBAvnQJeBjJ768TcQ5EHLje33kJmReQRb │
│ └── │
│ Published Objects: │
│ ┌── │
│ │ PackageID: 0x00012e566ad8b36293836f4901f72d32f181782299bf54724f7dbb6b718c9683 │
│ │ Version: 1 │
│ │ Digest: BwqnKimSHf4iD4Erf3Jwj4JntQRxMxYN5YZ4dGEB5Dx4 │
│ │ Modules: counter │
│ └── │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
╭───────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Balance Changes │
├───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ┌── │
│ │ Owner: Account Address ( 0x35370841d2e69b495b1e2f944a3087e4242f314e503691a00b054e0ee2a45a73 ) │
│ │ CoinType: 0x2::sui::SUI │
│ │ Amount: -8055080 │
│ └── │
╰───────────────────────────────────────────────────────────────────────────────────────────────────╯
查看交易详情
https://testnet.suivision.xyz/txblock/GjioTVaWBDXem8fXVL2PmYs3wjVNiyXBKBFUV4MRs4sc
https://testnet.suivision.xyz/txblock/GjioTVaWBDXem8fXVL2PmYs3wjVNiyXBKBFUV4MRs4sc?tab=Changes
在浏览器查看合约
main.tsx
文件
import React from "react";
import ReactDOM from "react-dom/client";
import "@mysten/dapp-kit/dist/index.css";
import "@radix-ui/themes/styles.css";
import { SuiClientProvider, WalletProvider } from "@mysten/dapp-kit";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Theme } from "@radix-ui/themes";
import App from "./App.tsx";
import { networkConfig } from "./networkConfig.ts";
const queryClient = new QueryClient();
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<Theme appearance="dark">
<QueryClientProvider client={queryClient}>
<SuiClientProvider networks={networkConfig} defaultNetwork="testnet">
<WalletProvider autoConnect>
<App />
</WalletProvider>
</SuiClientProvider>
</QueryClientProvider>
</Theme>
</React.StrictMode>,
);
App.tsx
文件
import { ConnectButton, useCurrentAccount } from "@mysten/dapp-kit";
import { isValidSuiObjectId } from "@mysten/sui/utils";
import { Box, Container, Flex, Heading } from "@radix-ui/themes";
import { useState } from "react";
import { Counter } from "./Counter";
import { CreateCounter } from "./CreateCounter";
function App() {
const currentAccount = useCurrentAccount();
const [counterId, setCounter] = useState(() => {
const hash = window.location.hash.slice(1);
return isValidSuiObjectId(hash) ? hash : null;
});
return (
<>
<Flex
position="sticky"
px="4"
py="2"
justify="between"
style={{
borderBottom: "1px solid var(--gray-a2)",
}}
>
<Box>
<Heading>dApp Starter Template</Heading>
</Box>
<Box>
<ConnectButton />
</Box>
</Flex>
<Container>
<Container
mt="5"
pt="2"
px="4"
style={{ background: "var(--gray-a2)", minHeight: 500 }}
>
{currentAccount ? (
counterId ? (
<Counter id={counterId} />
) : (
<CreateCounter
onCreated={(id) => {
window.location.hash = id;
setCounter(id);
}}
/>
)
) : (
<Heading>Please connect your wallet</Heading>
)}
</Container>
</Container>
</>
);
}
export default App;
Counter.tsx
文件
import {
useCurrentAccount,
useSignAndExecuteTransaction,
useSuiClient,
useSuiClientQuery,
} from "@mysten/dapp-kit";
import type { SuiObjectData } from "@mysten/sui/client";
import { Transaction } from "@mysten/sui/transactions";
import { Button, Flex, Heading, Text } from "@radix-ui/themes";
import { useNetworkVariable } from "./networkConfig";
import { useState } from "react";
import ClipLoader from "react-spinners/ClipLoader";
export function Counter({ id }: { id: string }) {
const counterPackageId = useNetworkVariable("counterPackageId");
const suiClient = useSuiClient();
const currentAccount = useCurrentAccount();
const { mutate: signAndExecute } = useSignAndExecuteTransaction();
const { data, isPending, error, refetch } = useSuiClientQuery("getObject", {
id,
options: {
showContent: true,
showOwner: true,
},
});
const [waitingForTxn, setWaitingForTxn] = useState("");
const executeMoveCall = (method: "increment" | "reset") => {
setWaitingForTxn(method);
const tx = new Transaction();
if (method === "reset") {
tx.moveCall({
arguments: [tx.object(id), tx.pure.u64(0)],
target: `${counterPackageId}::counter::set_value`,
});
} else {
tx.moveCall({
arguments: [tx.object(id)],
target: `${counterPackageId}::counter::increment`,
});
}
signAndExecute(
{
transaction: tx,
},
{
onSuccess: (tx) => {
suiClient.waitForTransaction({ digest: tx.digest }).then(async () => {
await refetch();
setWaitingForTxn("");
});
},
},
);
};
if (isPending) return <Text>Loading...</Text>;
if (error) return <Text>Error: {error.message}</Text>;
if (!data.data) return <Text>Not found</Text>;
const ownedByCurrentAccount =
getCounterFields(data.data)?.owner === currentAccount?.address;
return (
<>
<Heading size="3">Counter {id}</Heading>
<Flex direction="column" gap="2">
<Text>Count: {getCounterFields(data.data)?.value}</Text>
<Flex direction="row" gap="2">
<Button
onClick={() => executeMoveCall("increment")}
disabled={waitingForTxn !== ""}
>
{waitingForTxn === "increment" ? (
<ClipLoader size={20} />
) : (
"Increment"
)}
</Button>
{ownedByCurrentAccount ? (
<Button
onClick={() => executeMoveCall("reset")}
disabled={waitingForTxn !== ""}
>
{waitingForTxn === "reset" ? <ClipLoader size={20} /> : "Reset"}
</Button>
) : null}
</Flex>
</Flex>
</>
);
}
function getCounterFields(data: SuiObjectData) {
if (data.content?.dataType !== "moveObject") {
return null;
}
return data.content.fields as { value: number; owner: string };
}
CreateCounter.tsx
文件
import { Transaction } from "@mysten/sui/transactions";
import { Button, Container } from "@radix-ui/themes";
import { useSignAndExecuteTransaction, useSuiClient } from "@mysten/dapp-kit";
import { useNetworkVariable } from "./networkConfig";
import ClipLoader from "react-spinners/ClipLoader";
export function CreateCounter({
onCreated,
}: {
onCreated: (id: string) => void;
}) {
const counterPackageId = useNetworkVariable("counterPackageId");
const suiClient = useSuiClient();
const {
mutate: signAndExecute,
isSuccess,
isPending,
} = useSignAndExecuteTransaction();
function create() {
const tx = new Transaction();
tx.moveCall({
arguments: [],
target: `${counterPackageId}::counter::create`,
});
signAndExecute(
{
transaction: tx,
},
{
onSuccess: async ({ digest }) => {
const { effects } = await suiClient.waitForTransaction({
digest: digest,
options: {
showEffects: true,
},
});
onCreated(effects?.created?.[0]?.reference?.objectId!);
},
},
);
}
return (
<Container>
<Button
size="3"
onClick={() => {
create();
}}
disabled={isSuccess || isPending}
>
{isSuccess || isPending ? <ClipLoader size={20} /> : "Create Counter"}
</Button>
</Container>
);
}
networkConfig.ts
文件
import { getFullnodeUrl } from "@mysten/sui/client";
import {
DEVNET_COUNTER_PACKAGE_ID,
TESTNET_COUNTER_PACKAGE_ID,
MAINNET_COUNTER_PACKAGE_ID,
} from "./constants.ts";
import { createNetworkConfig } from "@mysten/dapp-kit";
const { networkConfig, useNetworkVariable, useNetworkVariables } =
createNetworkConfig({
devnet: {
url: getFullnodeUrl("devnet"),
variables: {
counterPackageId: DEVNET_COUNTER_PACKAGE_ID,
},
},
testnet: {
url: getFullnodeUrl("testnet"),
variables: {
counterPackageId: TESTNET_COUNTER_PACKAGE_ID,
},
},
mainnet: {
url: getFullnodeUrl("mainnet"),
variables: {
counterPackageId: MAINNET_COUNTER_PACKAGE_ID,
},
},
});
export { useNetworkVariable, useNetworkVariables, networkConfig };
PACKAGE_ID
配置
在constants.ts
文件中配置PACKAGE_ID
export const DEVNET_COUNTER_PACKAGE_ID = "0xTODO";
export const TESTNET_COUNTER_PACKAGE_ID = "0x00012e566ad8b36293836f4901f72d32f181782299bf54724f7dbb6b718c9683";
export const MAINNET_COUNTER_PACKAGE_ID = "0xTODO";
安装依赖
hello_sui_move/sui-dapp on main [?] via ⬢ v22.1.0 via 🅒 base
➜ pnpm install
运行前端项目
hello_sui_move/sui-dapp on main [?] via ⬢ v22.1.0 via 🅒 base took 18.3s
➜ pnpm dev
> sui-dapp@0.0.0 dev /Users/qiaopengjun/Code/sui/hello_sui_move/sui-dapp
> vite
VITE v6.2.5 ready in 168 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h + enter to show help
在浏览器打开并连接钱包
调用 Create Counter
成功调用
调用 increment
成功调用
总结
通过这篇实战指南,我们从安装 Sui CLI 开始,一步步完成了钱包管理、智能合约开发和前端交互的完整流程,最终在 Sui 区块链上成功运行了一个计数器 DApp。这个过程不仅展示了 Sui 在 Web3 开发中的高效与简洁,也为新手提供了一个清晰的学习路径。无论你是想迈出 Web3 开发的第一步,还是希望深入了解 Sui 的技术优势,现在都可以信心满满地动手实践。下一站,不妨挑战更复杂的合约或应用,解锁 Sui 的更多可能!