本文转自本人微信公众号的文章,所以带有wx水印,希望大家能理解一下。后续新文会自己留存不带水印。
Sui Dapp counter计数器实现
1.1使用template自动创建示例
#自动创建counter
pnpm create @mysten/dapp --template react-e2e-counter
#当前目录下会有counter目录
(base) root@DESKTOP-8UK78GU:~/sui_dappkit# ls
counter hello_sui
(base) root@DESKTOP-8UK78GU:~/sui_dappkit# cd counter/
#安装依赖
pnpm add @mysten/sui.js @mysten/dapp-kit @tanstack/react-query
然后通过vscode打开
将move.toml修改一下
[package]
name = "counter"
version = "0.0.1"
[dependencies]
Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/mainnet" }
[addresses]
counter = "0x0
然后部署合约
sui client publish --gas-budget 100000000 --skip-dependency-verification
#package部分信息
│ Published Objects: │
│ ┌── │
│ │ PackageID: 0xef7e9f18db243cd1391eb735c84e7293257682db11888b722510627a7ef42b93 │
│ │ Version: 1 │
│ │ Digest: GEBwAzQqMgTTtMsL2Zbt6QwUXjuq2Dtf9QCr6qxjqKmf │
│ │ Modules: counter │
│ └── │
#获取到PackageID
0xef7e9f18db243cd1391eb735c84e7293257682db11888b722510627a7ef42b93
1.2 修改模板中的前端代码
修改src/中的constant.ts文件,将packageid写入
export const DEVNET_COUNTER_PACKAGE_ID = "0xTODO";
export const MAINNET_COUNTER_PACKAGE_ID = "0xTODO";
export const TESTNET_COUNTER_PACKAGE_ID = "0xef7e9f18db243cd1391eb735c84e7293257682db11888b722510627a7ef42b93"
然后修改关于调用networkConfig.ts的其他文件内容(因为在前端交互的时候连接不到packageid,然后自己做了代码调整)。
CreateCounter.tsx,删除了networkConfig.ts的引用,直接用
TESTNET_COUNTER_PACKAGE_ID 作为代码中的packageid参数。
import { TransactionBlock } from "@mysten/sui.js/transactions";
import { Button, Container } from "@radix-ui/themes";
import {
useSignAndExecuteTransactionBlock,
useSuiClient,
} from "@mysten/dapp-kit";
import { TESTNET_COUNTER_PACKAGE_ID } from "./constants";
export function CreateCounter({
onCreated,
}: {
onCreated: (id: string) => void;
}) {
const client = useSuiClient();
const { mutate: signAndExecute } = useSignAndExecuteTransactionBlock();
return (
<Container>
<Button
size="3"
onClick={() => {
create();
}}
>
Create Counter
</Button>
</Container>
);
function create() {
const txb = new TransactionBlock();
txb.moveCall({
arguments: [],
target: `${TESTNET_COUNTER_PACKAGE_ID}::counter::create`,
});
signAndExecute(
{
transactionBlock: txb,
options: {
showEffects: true,
showObjectChanges: true,
},
},
{
onSuccess: (tx) => {
client
.waitForTransactionBlock({
digest: tx.digest,
})
.then(() => {
const objectId = tx.effects?.created?.[0]?.reference?.objectId;
if (objectId) {
onCreated(objectId);
}
});
},
},
);
}
}
Counter.tsx如上,删除了networkConfig.ts的引用。
import {
useCurrentAccount,
useSignAndExecuteTransactionBlock,
useSuiClient,
useSuiClientQuery,
} from "@mysten/dapp-kit";
import type { SuiObjectData } from "@mysten/sui.js/client";
import { TransactionBlock } from "@mysten/sui.js/transactions";
import { Button, Flex, Heading, Text } from "@radix-ui/themes";
import { TESTNET_COUNTER_PACKAGE_ID } from "./constants";
export function Counter({ id }: { id: string }) {
const client = useSuiClient();
const currentAccount = useCurrentAccount();
const { mutate: signAndExecute } = useSignAndExecuteTransactionBlock();
const { data, isPending, error, refetch } = useSuiClientQuery("getObject", {
id,
options: {
showContent: true,
showOwner: true,
},
});
const executeMoveCall = (method: "increment" | "reset") => {
const txb = new TransactionBlock();
if (method === "reset") {
txb.moveCall({
arguments: [txb.object(id), txb.pure.u64(0)],
target: `${TESTNET_COUNTER_PACKAGE_ID}::counter::set_value`,
});
} else {
txb.moveCall({
arguments: [txb.object(id)],
target: `${TESTNET_COUNTER_PACKAGE_ID}::counter::increment`,
});
}
signAndExecute(
{
transactionBlock: txb,
options: {
showEffects: true,
showObjectChanges: true,
},
},
{
onSuccess: (tx) => {
client.waitForTransactionBlock({ digest: tx.digest }).then(() => {
refetch();
});
},
},
);
};
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")}>
Increment
</Button>
{ownedByCurrentAccount ? (
<Button onClick={() => executeMoveCall("reset")}>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 };
}
然后修改main.tsx修改网络配置,使用getFullnodeUrl
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, createNetworkConfig } from "@mysten/dapp-kit";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Theme } from "@radix-ui/themes";
import App from "./App.tsx";
import { getFullnodeUrl } from "@mysten/sui.js/client";
const queryClient = new QueryClient();
const { networkConfig } = createNetworkConfig({
localnet: { url: getFullnodeUrl("localnet") },
devnet: { url: getFullnodeUrl("devnet") },
testnet: { url: getFullnodeUrl("testnet") },
mainnet: { url: getFullnodeUrl("mainnet") },
});
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<Theme appearance="light">
<QueryClientProvider client={queryClient}>
<SuiClientProvider networks={networkConfig} defaultNetwork="testnet">
<WalletProvider autoConnect>
<App />
</WalletProvider>
</SuiClientProvider>
</QueryClientProvider>
</Theme>
</React.StrictMode>,
);
CTRL+鼠标左键选中getFullnodeUrl,修改一下getFullnodeUrl中的testnet的rpc端点
https://wallet-rpc.testnet.sui.io/
然后就可以执行代码了
(base) root@DESKTOP-8UK78GU:~/sui_dappkit/counter# npm run dev
> counter@0.0.0 dev
> vite
VITE v4.5.2 ready in 362 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h to show help
点击连接钱包
选择需要连接的地址
然后就连接上了
如果需要切换钱包则需要在钱包处断开连接
然后点击Create Counter
然后就创建好了
点击Increment按钮
然后Count的值成功变为了1
再点击Reset按钮
Count的值重置为0了
1.3 合约解读
在合约的代码中调用create函数是value默认为0
public fun create(ctx: &mut TxContext) {
transfer::share_object(Counter {
id: object::new(ctx),
owner: tx_context::sender(ctx),
value: 0
})
}
通过点击Increment按钮,调用increment时实现自增+1
/// Increment a counter by 1.
public fun increment(counter: &mut Counter) {
counter.value = counter.value + 1;
}
通过点击Reset按钮,调用set_value时实现数值修改,在下面的代码中reset的按钮默认往set_value中传入objectid和u64类型的整数0。
if (method === "reset") {
txb.moveCall({
arguments: [txb.object(id), txb.pure.u64(0)],
target: `${TESTNET_COUNTER_PACKAGE_ID}::counter::set_value`,
});
} else {
txb.moveCall({
arguments: [txb.object(id)],
target: `${TESTNET_COUNTER_PACKAGE_ID}::counter::increment`,
});
}
当参数传入set_value,则将objectid中的value值等于传入的value值0,实现重置为0。
/// Set value (only runnable by the Counter owner)
public fun set_value(counter: &mut Counter, value: u64, ctx: &TxContext) {
assert!(counter.owner == tx_context::sender(ctx), 0);
counter.value = value;
}
本文源码已上传github:
https://github.com/baicaiyihao/counter
Sui move_cn社交账号
telegram: https://t.me/move_cn
X(twitter): https://twitter.com/move_cn
QQ群: 79489587
微信公众号:Move中文
Sui中文开发群: https://t.me/sui_dev_cn
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!