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打开

img

将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/

img

然后就可以执行代码了

(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
​

img

点击连接钱包

img

选择需要连接的地址

img

然后就连接上了

img

如果需要切换钱包则需要在钱包处断开连接

img

然后点击Create Counter

img

然后就创建好了

img

点击Increment按钮

img

然后Count的值成功变为了1

img

再点击Reset按钮

img

Count的值重置为0了

img

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值