SUI学习记录

学习日志

2023-11-10

安装SUI钱包浏览器插件, EDGE浏览器的插件中心里, SUI插件版本存在落后, 需要在Chrome的插件中心安装, 链接为https://chrome.google.com/webstore/detail/sui-wallet/opcgpfmipidbgpenhmajoajpbobppdil/related

可以使用Google账号登录, 获取钱包地址

2023-11-11

1. windows搭建环境

1) 在https://github.com/MystenLabs/sui/releases中下载windows工具包

1.PNG

2) 解压后配置环境变量, 路径按实际路径配置

D:\sui-mainnet-v1.13.0-windows-x86_64\external-crates\move\target\release

D:\sui-mainnet-v1.13.0-windows-x86_64\target\release

2.PNG

3) 简化命令

把exe文件名称中-windows-x86_64删除

4.PNG

4) 测试命令

3.PNG

2. 浏览器插件连接Sui开发链

1) 在Sui浏览器插件中切换到开发链

7.PNG

2) 直接点击按钮即可获取token

12.PNG

3. 命令行工具连接Sui开发链

1) 连接

sui client envs

默认则是连接到开发链, 会创建钱包地址和恢复码

addr: [0x1f0663c112ee1f1a75566039d849557a972293d99f71e45e9b9b72caed9b71a2]
Secret Recovery Phrase: [grant indicate avocado rail state minimum defense balcony pupil very attend sentence]
8.PNG

2) 在浏览器插件里尝试导入

输入前面生成的恢复码


9.PNG

输入密码


10.PNG

可以看到成功导入


11.PNG

3) 请求测试用的tokens

curl --location --request POST 'https://faucet.devnet.sui.io/gas' --header 'Content-Type: application/json' --data-raw '{"FixedAmountRequest": {"recipient": "钱包地址"}}'

请求一次会获取10 SUI

6.PNG

5.PNG

2023-11-18

1. 智能合约

1. 创建move项目

sui move new hello_world

2. 在sources文件夹中创建hello.move

#[lint_allow(self_transfer)]
module hello_world::hello_world {

    use std::string;
    use sui::object::{Self, UID};
    use sui::transfer;
    use sui::tx_context::{Self, TxContext};

    /// 一个包含一个任意的字符串
    struct HelloWorldObject has key, store {
        id: UID,
        text: string::String
    }

    // 这个函数创建了 HelloWoirldObject 定制类型的一个新实例,然后使用 Sui 系统中的转移函数 transfer 将其发送给交易的调用者
    public fun mint(ctx: &mut TxContext) {
        let object = HelloWorldObject {
            id: object::new(ctx),
            text: string::utf8(b"Hello World!")
        };
        transfer::public_transfer(object, tx_context::sender(ctx));
    }

}

3. 构建项目

sui move build

4. 切换到开发链

sui client switch --env devnet

添加测试链

sui client new-env --alias testnet --rpc https://fullnode.testnet.sui.io:443

5. 发布

sui client publish --gas-budget 1000000000
1.png

2023-11-19

创建一个token coin

module zcdc_coin::ZCDCCOIN {
    use std::option;
    use sui::coin::{Self, Coin, TreasuryCap};
    use sui::transfer;
    use sui::tx_context::{Self, TxContext};

    struct ZCDCCOIN has drop {}

    /// Module initializer is called once on module publish. A treasury
    /// cap is sent to the publisher, who then controls minting and burning
    fun init(witness: ZCDCCOIN, ctx: &mut TxContext) {
        let (treasury, metadata) = coin::create_currency(witness, 6, b"ZCDCCoin", b"ZCDCCoin", b"ZCDCCoin is NO.1", option::none(), ctx);
        transfer::public_freeze_object(metadata);
        transfer::public_transfer(treasury, tx_context::sender(ctx));
    }

    public entry fun mint (
        treasury_cap: &mut TreasuryCap<ZCDCCOIN>, amount: u64, recipient: address, ctx: &mut TxContext
    ) {
        coin::mint_and_transfer(treasury_cap, amount, recipient, ctx)
    }

    public entry fun burn (
        treasury_cap: &mut TreasuryCap<ZCDCCOIN>, coin: Coin<ZCDCCOIN>
    ) {
        coin::burn(treasury_cap, coin);
    }
}

获取coin到钱包

sui client call --function mint --module ZCDCCOIN --package 0xe50f9a836d7e97a6ecbab576efda70b1bbb20a54eaaeceac4b191e5d66f929af --args 0x99006836e95823650ee06342033125c8760c7c364ce7f2fde52c473447244cba 10  0x1f0663c112ee1f1a75566039d849557a972293d99f71e45e9b9b72caed9b71a2 --gas-budget 1000000000 

--module 后面是coin的module名

--package 后面是coin的package


2.png

--args 后面第一个参数是coin的id
在自己钱包里找到发布的coin的object id


4.png
3.png

--args 后面第二个参数 是数量

--args 后面第三个参数 是转到的钱包地址

执行结果

5.png
6.png

创建dapp应用

1. 安装依赖

mkdir dapp
cd dapp

pnpm init
pnpm add -D @mysten/sui.js

2. 新建index.js, 写从开发环境获取sui的程序

import { getFullnodeUrl, SuiClient } from '@mysten/sui.js/client';
import { getFaucetHost, requestSuiFromFaucetV0 } from '@mysten/sui.js/faucet';
import { MIST_PER_SUI } from '@mysten/sui.js/utils';
 
// replace <YOUR_SUI_ADDRESS> with your actual address, which is in the form 0x123...
const MY_ADDRESS = '0x1f0663c112ee1f1a75566039d849557a972293d99f71e45e9b9b72caed9b71a2';
 
// create a new SuiClient object pointing to the network you want to use
const suiClient = new SuiClient({ url: getFullnodeUrl('devnet') });
 
// Convert MIST to Sui
const balance = (balance) => {
    return Number.parseInt(balance.totalBalance) / Number(MIST_PER_SUI);
};
 
// store the JSON representation for the SUI the address owns before using faucet
const suiBefore = await suiClient.getBalance({
    owner: MY_ADDRESS,
});
 
await requestSuiFromFaucetV0({
    // use getFaucetHost to make sure you're using correct faucet address
    // you can also just use the address (see Sui TypeScript SDK Quick Start for values)
    // 获取对应环境的水龙头地址(测试环境, 开发环境, 本地环境)
    host: getFaucetHost('devnet'),
    recipient: MY_ADDRESS,
});
 
// store the JSON representation for the SUI the address owns after using faucet
const suiAfter = await suiClient.getBalance({
    owner: MY_ADDRESS,
});
 
// Output result to console.
console.log(
    `Balance before faucet: ${balance(suiBefore)} SUI. Balance after: ${balance(
        suiAfter,
    )} SUI. Hello, SUI!`,
);

3. 新建vue3项目, 图形化查询钱包的coins

1.png

2023-11-22

1. 创建nft合约

module nft::nft_test {
    use sui::url::{Self, Url}
    use std::string;
    use sui::object::{Self, ID, UID};
    use sui::event;
    use sui::tx_context::{Self, TxContext};

    struct NftTest has key, store {
        id: UID,
        name: string::String,
        description: string::String,

    }

    // =======event ==========
    struct NFTMinted has copy, drop {
        object_id: ID,
        creator: address,
        name: string::String,
    }
s
    // 获取名称
    public fun name(nft: &NftTest): &string::String {
        &nft.name
    }

    // 获取描述
    public fun description(nft: &NftTest): &string::String {
        &nft.description
    }

    // 获取链接
    public fun url(nft: &NftTest): &Url {
        &nft.url
    }

    // ========== 入口函数 =======
    // 创建新nft
    public entry fun mint_to_sender (
        name: vector<u8>,
        description: vector<u8>,
        url: vector<u8>,
        ctx: &mut TxContext
    ) {
        let sender = tx_context::sender(ctx);
        let nft = NftTest {
            id: object::new(ctx),
            name: string::uft8(name),
            description: string::uft8(description),
            url: url::new_unsafe_from_bytes(url)
        };
        event::emit(NFTMinted {
            object_id: Object::id(&nft),
            creator: sender,
            name: nft.name
        });

        transfer::public_transfer(nft, sender)
    }

    // 转移nft给新的所有者
    public entry fun transfer(
        nft: NftTest,
        recipient: address,
        _: &mut TxContext
    ) {
        transfer::public_transfer(nft, recipient)
    }

    // 更新nft介绍
    public entry fun update_description(
        nft: &mut NftTest,
        new_description: vector<u8>,
        _: &mut TxContext
    ) {
        nft.description = string::utf8(new_description)
    }

    public entry fun burn(nft: NftTest, _: &mut TxContext) {
        let NftTest {
            id,
            name: _,
            description: _,
            url: _
        } = nft;
        object::delete(id)
    }
}

2. build

sui move build

3. 发布

sui client publish --gas-budget 1000000000
1.PNG

https://suiexplorer.com/object/0x2f24f83514c11ed8c04e72a01106c8698fd232bedc485afa834f1fcc7debda8f?network=devnet

4. 在页面上触发创建事件

2.PNG

20231129

1. 安装sui-move-analysis

1) 下载windows安装包

https://github.com/movebit/move/releases

2) 配置windows环境变量

3) 配置vscode插件

1.PNG

配置sui-move-analysis路径, 注意路径最后去除exe

D:\\sui-move-analyzer\sui-move-analyzer

20231130

1. 命令行里使用zklogin登录谷歌账号

sui keytool zk-login-sign-and-execute-tx --max-epoch 999

--max-epoch设置失效时间

会出现当前所有支持zklogin的平台单点登录地址


2.png

登录后会跳转到sui官方地址, 直接复制粘贴到命令行中即可


1.png

遇到问题: 每次用谷歌的zklogin后 地址都不一样

2. 生成带图片nft

主要需要配置object display

module znft::my_zpet {
    use sui::tx_context::{sender, TxContext};
    use std::string::{utf8, String};
    use sui::transfer;
    use sui::object::{Self, ID, UID};
    use sui::event;
    
    use sui::package::{claim};
    use sui::display;

    struct Zpet has key, store {
        id: UID,
        name: String,
        image_url: String,
        description: String
    }

    struct ZPETMinted has copy, drop {
        object_id: ID,
        creator: address,
        name: String,
        image_url: String,
        description: String
    }

    struct MY_ZPET has drop {}

    public fun name(nft: &Zpet): &String {
        &nft.name
    }

    public fun description(nft: &Zpet): &String {
        &nft.description
    }

    public fun image_url(nft: &Zpet): &String {
        &nft.image_url
    }

    fun init (otw: MY_ZPET, ctx: &mut TxContext) {
        let keys = vector[
            utf8(b"name"),
            utf8(b"link"),
            utf8(b"image_url"),
            utf8(b"description"),
            utf8(b"project_url"),
            utf8(b"creator"),
        ];

        let values = vector[
            utf8(b"{name}"),
            utf8(b""),
            utf8(b"{image_url}"),
            utf8(b"{description}"),
            utf8(b""),
            utf8(b"zcdc")
        ];

        let publisher = claim(otw, ctx);

        let display = display::new_with_fields<Zpet>(
            &publisher, keys, values, ctx
        );

        display::update_version(&mut display);

        transfer::public_transfer(publisher, sender(ctx));
        transfer::public_transfer(display, sender(ctx));
    }

    fun create_nft (
        name: String,
        image_url: String,
        description: String,
        ctx: &mut TxContext
    ): Zpet {
        let sender = sender(ctx);
        let id = object::new(ctx);
        let nft = Zpet { id, name, image_url, description };

        event::emit(ZPETMinted {
            object_id: object::id(&nft),
            creator: sender,
            name: nft.name,
            image_url: nft.image_url,
            description: nft.description
        });
        nft
    }


    public entry fun transfer(
        name: String,
        image_url: String,
        description: String,
        recipient: address,
        ctx: &mut TxContext
    ) {
        transfer::public_transfer(create_nft(name, image_url, description, ctx), recipient)
    }

    public entry fun update_description(
        nft: &mut Zpet,
        new_description: vector<u8>,
        _: &mut TxContext
    ) {
        nft.description = utf8(new_description)
    }

    public entry fun burn(nft: Zpet, _: &mut TxContext) {
        let Zpet {
            id,
            name: _,
            image_url: _,
            description: _
        } = nft;
        object::delete(id)
    }
}

刚开始是在mint的方法里直接transfer, 但是会报warning

3.png

https://zh.blog.sui.io/linter-compile-warnings-update/

解决方法就是, mint的时候不进行transfer, 而是在transfer的方法里调用mint

4.png

生成后的nft


5.png

3. 使用nft storage进行链上存储图片

https://nft.storage/

20231202

1. dapp sdk查询账号的指定nft

getOwnedObjects方法获取当前账号的objects

options中指定获取的内容

filter中写筛选条件, 指定type

解构中 data即为获取的object列表

refetch用来重新获取

  const { data, isPending, error, refetch } = useSuiClientQuery(
    'getOwnedObjects',
    {
      owner: account?.address as string,
      options: {
        showType: true,
        showDisplay: true,
        showContent: true
      },
      filter: {
        MatchAll: [
          {
            StructType: `${PACKAGEID}::my_zpet::Zpet`,
          },
          {
            AddressOwner: account?.address || '',
          },
        ],
      },
    },
    {
      enabled: !!account,
    }
  )

2. 获取当前账号的余额

使用的方法为getBalance

import { useCurrentAccount, useSuiClientQuery } from "@mysten/dapp-kit";
import { Flex, Text } from "@radix-ui/themes";

export function OwnedBalances() {
  const account = useCurrentAccount();
  const { data, isPending, error } = useSuiClientQuery(
    "getBalance",
    {
      owner: account?.address as string,
    },
    {
      enabled: !!account,
    },
  );

  if (!account) {
    return;
  }

  if (error) {
    return <Flex>Error: {error.message}</Flex>;
  }

  if (isPending || !data) {
    return <Flex>加载中...</Flex>;
  }

  return (
    <Text>
       余额: { data && data.totalBalance ? Number(data.totalBalance) / 1000000000 : 0 } SUI
    </Text>
  );
}

3. dapp调用package的event

moveCall调用方法, pure()包裹参数, mutate触发方法, 调用钱包

import muyu from './assets/木鱼.svg'
import muyugun from './assets/木鱼棍.svg'
import soundMp3 from './assets/sound.mp3'
import { Howl } from 'howler'
import { useState } from 'react'
import { TransactionBlock } from "@mysten/sui.js/transactions"
import { PACKAGEID } from './constants'
import { useCurrentAccount, useSignAndExecuteTransactionBlock } from '@mysten/dapp-kit'
import { toast } from 'react-toastify'

interface ImgObj {
  [key: string]: {
    name: string,
    url: string
  }
}

const obj: ImgObj = {
  1: {
    name: '赛博功德记录本',
    url: 'https://bafybeibigwt5csazd3f2fzycxfx2q7i7vna4linuqdlwr5fbaxsjgb45ai.ipfs.nftstorage.link/ipfs/bafybeibigwt5csazd3f2fzycxfx2q7i7vna4linuqdlwr5fbaxsjgb45ai/111.png',
  },
  166: {
    name: '赛博功德:166',
    url: 'https://bafybeickygqtbkdpip7snru6gduxvf4thlrbom6l3qz3gvpkzph2sw6tde.ipfs.nftstorage.link/ipfs/bafybeickygqtbkdpip7snru6gduxvf4thlrbom6l3qz3gvpkzph2sw6tde/166.png',
  },
  288: {
    name: '赛博功德:288',
    url: 'https://bafybeickygqtbkdpip7snru6gduxvf4thlrbom6l3qz3gvpkzph2sw6tde.ipfs.nftstorage.link/ipfs/bafybeickygqtbkdpip7snru6gduxvf4thlrbom6l3qz3gvpkzph2sw6tde/288.png',
  },
  588: {
    name: '赛博功德:588',
    url: 'https://bafybeickygqtbkdpip7snru6gduxvf4thlrbom6l3qz3gvpkzph2sw6tde.ipfs.nftstorage.link/ipfs/bafybeickygqtbkdpip7snru6gduxvf4thlrbom6l3qz3gvpkzph2sw6tde/588.png',
  },
  1888: {
    name: '赛博功德:1888',
    url: 'https://bafybeickygqtbkdpip7snru6gduxvf4thlrbom6l3qz3gvpkzph2sw6tde.ipfs.nftstorage.link/ipfs/bafybeickygqtbkdpip7snru6gduxvf4thlrbom6l3qz3gvpkzph2sw6tde/1888.png',
  },
  2888: {
    name: '赛博功德:2888',
    url: 'https://bafybeickygqtbkdpip7snru6gduxvf4thlrbom6l3qz3gvpkzph2sw6tde.ipfs.nftstorage.link/ipfs/bafybeickygqtbkdpip7snru6gduxvf4thlrbom6l3qz3gvpkzph2sw6tde/2888.png',
  },
  8888: {
    name: '赛博功德:8888',
    url: 'https://bafybeickygqtbkdpip7snru6gduxvf4thlrbom6l3qz3gvpkzph2sw6tde.ipfs.nftstorage.link/ipfs/bafybeickygqtbkdpip7snru6gduxvf4thlrbom6l3qz3gvpkzph2sw6tde/8888.png',
  },
  zzes: {
    name: '赛博功德无量',
    url: 'https://bafybeickygqtbkdpip7snru6gduxvf4thlrbom6l3qz3gvpkzph2sw6tde.ipfs.nftstorage.link/ipfs/bafybeickygqtbkdpip7snru6gduxvf4thlrbom6l3qz3gvpkzph2sw6tde/zzes.png'
  },
}

const sound = new Howl({
  src: [soundMp3],
  html5: true
})
export function GameBlock() {
  const [showGun, setShowGun] = useState(false)
  const [num, setNum] = useState(0)
  const { mutate } = useSignAndExecuteTransactionBlock()
  const account = useCurrentAccount()
  const doIt = () => {
    sound.play()
    console.log(showGun)
    setShowGun(true)
  }
  const cancelIt = () => {
    toast.success('赛博功德加一')
    sound.stop()
    setShowGun(false)
    setNum(num + 1)
    const txb = new TransactionBlock()
    
    let imgObj: {
      name: string,
      url: string
    } | undefined = undefined
    const index = num + 1 + ''
    if (Object.keys(obj).includes(index)) {
      imgObj = obj[index]
    }
    if (index.substring(1) && index.substring(1).split('').length > 5 && index.substring(1).split('').every(str => str === '8')) {
      imgObj = obj['zzes']
    }
    if (account && imgObj) {
      txb.moveCall({
        target: `${PACKAGEID}::my_zpet::transfer`,
        arguments: [
          txb.pure(imgObj.name),
          txb.pure(imgObj.url),
          txb.pure('赛博功德纪念NFT'),
          txb.pure(account.address)
        ]
      })
      mutate(
        {
          transactionBlock: txb
        },
        {
          onError: (err) => {
            toast.error(err.message)
          },
          onSuccess: (result) => {
            toast.success(`赛博功德纪念NFT领取成功: ${result.digest}`)
          },
        }
      )
    }

  }
  return (
    <div className="game-block" onMouseDown={doIt} onMouseUp={cancelIt}>
      <img className="muyu" src={muyu} />
      { showGun && (<img className="gun" src={muyugun} />) }
      <div className="gd">赛博功德:{num}</div>
    </div>
  );
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容