## IPFS介紹 #### 我們這次要使用IPFS(Pinata)來存放我們的Coin圖示。 有關IPFS以及Pinata介紹,請看這篇文章 [IPFS介紹](https://hackmd.io/7vj9f0HiQXGfo_j1_u0KfA?view) ## 上傳圖片至Pinata 我們使用Pinata當作我們儲存圖片的IPFS系統。 [Pinata上傳圖片頁面](https://app.pinata.cloud/pinmanager) ![](https://hackmd.io/_uploads/rkvIzlxyp.png) 首先我們點選右上角的 "Add Files" 按鈕,然後點選 "File",會有上傳視窗讓你選擇圖片。 ![](https://hackmd.io/_uploads/ByjgQlxJT.png) 這邊可以隨意上傳隨便一張圖片,GIF檔也可以。 這邊我用Co-Sui讀書會的icon當作Coin的圖示。 ![](https://hackmd.io/_uploads/B197CONxp.png) 上傳好後,我們可以看到有出現我們剛剛上傳的檔案,待會會用到。 ![](https://hackmd.io/_uploads/H1LJ7RSg6.png) ## 智能合約程式範例 TODO部分可以自定義 ```rust module workshop::mycoin { use std::option; use sui::coin::{Self, Coin, TreasuryCap}; use sui::transfer; use sui::tx_context::{Self, TxContext}; use sui::url::{Self}; // One Time Witness struct MYCOIN has drop {} fun init(witness: MYCOIN, ctx: &mut TxContext) { // TODO let decimals = 9; let symbol = b"RC"; let name = b"Ryan Coin"; let description = b"Ryan Coin Demo Test"; let icon_url = url::new_unsafe_from_bytes(b"https://pin.ski/3PKHM4G"); let (treasury, metadata) = coin::create_currency(witness, decimals, symbol, name, description, option::some(icon_url), ctx); transfer::public_freeze_object(metadata); // transfer::public_share_object(metadata); transfer::public_transfer(treasury, tx_context::sender(ctx)); } public entry fun mint( treasury_cap: &mut TreasuryCap<MYCOIN>, amount: u64, recipient: address, ctx: &mut TxContext ) { coin::mint_and_transfer(treasury_cap, amount, recipient, ctx) } public entry fun burn(treasury_cap: &mut TreasuryCap<MYCOIN>, coin: Coin<MYCOIN>) { coin::burn(treasury_cap, coin); } } ``` ### 編譯程式(查看有沒有Error) ```rust PS D:\vscode\workspace\workshop> sui move build UPDATING GIT DEPENDENCY https://github.com/MystenLabs/sui.git INCLUDING DEPENDENCY Sui INCLUDING DEPENDENCY MoveStdlib BUILDING workshop ``` ### 部署程式 ```javascript PS D:\vscode\workspace\workshop> sui client publish --gas-budget 100000000 UPDATING GIT DEPENDENCY https://github.com/MystenLabs/sui.git INCLUDING DEPENDENCY Sui INCLUDING DEPENDENCY MoveStdlib BUILDING workshop Successfully verified dependencies on-chain against source. ----- Transaction Digest ---- EjJdQhCvzP5gaWGuTCM1BAyJdn1nYRE22L86DDWFmTnr ----- Transaction Data ---- Transaction Signature: [Signature(Ed25519SuiSignature(Ed25519SuiSignature([0, 253, 145, 79, 231, 142, 0, 169, 64, 119, 206, 209, 206, 44, 47, 30, 185, 82, 13, 76, 233, 184, 193, 68, 128, 69, 178, 57, 216, 174, 120, 201, 157, 74, 78, 33, 141, 32, 147, 190, 228, 43, 250, 60, 64, 35, 69, 34, 155, 180, 50, 202, 136, 150, 13, 100, 219, 15, 180, 145, 238, 20, 75, 179, 5, 247, 100, 111, 221, 105, 109, 168, 119, 191, 214, 73, 60, 87, 207, 170, 217, 25, 125, 35, 32, 56, 114, 200, 3, 129, 122, 80, 229, 159, 146, 202, 222])))] Transaction Kind : Programmable Inputs: [Pure(SuiPureValue { value_type: Some(Address), value: "0xd1c4d5d9115d8aa9de808eb59c0f2cf614a67ca26f9759b1d07b56fe9fc612c7" })] Commands: [ Publish(<modules>,0x0000000000000000000000000000000000000000000000000000000000000001,0x0000000000000000000000000000000000000000000000000000000000000002), TransferObjects([Result(0)],Input(0)), ] Sender: 0xd1c4d5d9115d8aa9de808eb59c0f2cf614a67ca26f9759b1d07b56fe9fc612c7 Gas Payment: Object ID: 0x8a371ba703f4f23b2b72059a8bdcd5b051797e4bba1d46e080b35e89c2cfe298, version: 0x10d4e7, digest: 2kjVgy9FxuV8yfbv17hnLZixrWGqshXQtuWSJHqhZLXB Gas Owner: 0xd1c4d5d9115d8aa9de808eb59c0f2cf614a67ca26f9759b1d07b56fe9fc612c7 Gas Price: 1000 Gas Budget: 100000000 ----- Transaction Effects ---- Status : Success Created Objects: - ID: 0x5c9ee5742dc91d70ca2eb770d7b4163f2c15f5110ecbdeed80169d0048214a8e , Owner: Account Address ( 0xd1c4d5d9115d8aa9de808eb59c0f2cf614a67ca26f9759b1d07b56fe9fc612c7 ) - ID: 0xa555e59ec6b991743704ddf9c76dd4f010b1e128a1e248c433ffea93071adcc1 , Owner: Account Address ( 0xd1c4d5d9115d8aa9de808eb59c0f2cf614a67ca26f9759b1d07b56fe9fc612c7 ) - ID: 0xda67a492f9a8132e85a985b7853f70b008877f04439a62a55b75da27b2879a2e , Owner: Immutable - ID: 0xee7e173dc96e999bccb34c11ac445d2bae82605ee14c1d93abadbefc706fcfa0 , Owner: Immutable Mutated Objects: - ID: 0x8a371ba703f4f23b2b72059a8bdcd5b051797e4bba1d46e080b35e89c2cfe298 , Owner: Account Address ( 0xd1c4d5d9115d8aa9de808eb59c0f2cf614a67ca26f9759b1d07b56fe9fc612c7 ) ----- Events ---- Array [] ----- Object changes ---- Array [ Object { "type": String("mutated"), "sender": String("0xd1c4d5d9115d8aa9de808eb59c0f2cf614a67ca26f9759b1d07b56fe9fc612c7"), "owner": Object { "AddressOwner": String("0xd1c4d5d9115d8aa9de808eb59c0f2cf614a67ca26f9759b1d07b56fe9fc612c7"), }, "objectType": String("0x2::coin::Coin<0x2::sui::SUI>"), "objectId": String("0x8a371ba703f4f23b2b72059a8bdcd5b051797e4bba1d46e080b35e89c2cfe298"), "version": String("1103080"), "previousVersion": String("1103079"), "digest": String("5E5D7ued9SMTLZGPedufV3sDVCXUAi6b8tGCrPbxcTCX"), }, Object { "type": String("created"), "sender": String("0xd1c4d5d9115d8aa9de808eb59c0f2cf614a67ca26f9759b1d07b56fe9fc612c7"), "owner": Object { "AddressOwner": String("0xd1c4d5d9115d8aa9de808eb59c0f2cf614a67ca26f9759b1d07b56fe9fc612c7"), }, "objectType": String("0x2::coin::TreasuryCap<0xee7e173dc96e999bccb34c11ac445d2bae82605ee14c1d93abadbefc706fcfa0::mycoin::MYCOIN>"), "objectId": String("0x5c9ee5742dc91d70ca2eb770d7b4163f2c15f5110ecbdeed80169d0048214a8e"), "version": String("1103080"), "digest": String("7DTUyYZ7KTfVpZRVLw8RGQTYWnAfgTCzGZTZ8J6DVomd"), }, Object { "type": String("created"), "sender": String("0xd1c4d5d9115d8aa9de808eb59c0f2cf614a67ca26f9759b1d07b56fe9fc612c7"), "owner": Object { "AddressOwner": String("0xd1c4d5d9115d8aa9de808eb59c0f2cf614a67ca26f9759b1d07b56fe9fc612c7"), }, "objectType": String("0x2::package::UpgradeCap"), "objectId": String("0xa555e59ec6b991743704ddf9c76dd4f010b1e128a1e248c433ffea93071adcc1"), "version": String("1103080"), "digest": String("CNehnr5PwZXnZ9bsoTmaBo9Fjn2EovX52h6w6siYPbqX"), }, Object { "type": String("created"), "sender": String("0xd1c4d5d9115d8aa9de808eb59c0f2cf614a67ca26f9759b1d07b56fe9fc612c7"), "owner": String("Immutable"), "objectType": String("0x2::coin::CoinMetadata<0xee7e173dc96e999bccb34c11ac445d2bae82605ee14c1d93abadbefc706fcfa0::mycoin::MYCOIN>"), "objectId": String("0xda67a492f9a8132e85a985b7853f70b008877f04439a62a55b75da27b2879a2e"), "version": String("1103080"), "digest": String("ChNW1XZpgaU6F6PYaWGjXiu2ivK8FkQuN86kyLc6F9Ms"), }, Object { "type": String("published"), "packageId": String("0xee7e173dc96e999bccb34c11ac445d2bae82605ee14c1d93abadbefc706fcfa0"), "version": String("1"), "digest": String("KZ7sctkfzAC44d26oYfQcJRLPpUfEF551XEZ4WzEab4"), "modules": Array [ String("mycoin"), ], }, ] ----- Balance changes ---- Array [ Object { "owner": Object { "AddressOwner": String("0xd1c4d5d9115d8aa9de808eb59c0f2cf614a67ca26f9759b1d07b56fe9fc612c7"), }, "coinType": String("0x2::sui::SUI"), "amount": String("-14644280"), }, ] ``` 使用 [Sui 瀏覽器](https://suiexplorer.com/?network=testnet) 查看我們剛剛所建立的CoinMetadata ```rust Object { "type": String("created"), "sender": String("0xd1c4d5d9115d8aa9de808eb59c0f2cf614a67ca26f9759b1d07b56fe9fc612c7"), "owner": String("Immutable"), "objectType": String("0x2::coin::CoinMetadata<0xee944f1737dd012eddb5d4860e1363bed3b70c5b8faeb9daddbcb25511e222f9::mycoin::MYCOIN>"), "objectId": String("0x45ee1c055e175876a994f2db6cdf4e252f2f117c91c8a192feb5cd9b9a5423cc"), "version": String("1103081"), "digest": String("7MfpJNWRNka6UHwXWCysSDF4M2NwRQ29NevWRfrVBREF"), }, ``` 找到objectType為 0x2::coin::CoinMetadata ... 使用objectId查該物件的詳細資料 ![](https://hackmd.io/_uploads/HyI46uLlT.png) 可以發現我們剛剛建立的同質化代幣的詳細資料,該Object是被公開且不可更改的。 接下來我們去查剛剛部署的合約,Mint Token給自己。 ```rust Object { "type": String("published"), "packageId": String("0xee944f1737dd012eddb5d4860e1363bed3b70c5b8faeb9daddbcb25511e222f9"), "version": String("1"), "digest": String("D2h9P8rr7v5Rv7KWbBFDr1dfubWrttenZWy8zZNdHcWB"), "modules": Array [ String("mycoin"), ], }, ``` 找到type為 String("published") 使用packageId查該合約的詳細資料 ![](https://hackmd.io/_uploads/rkViCdIx6.png) 接下來使用mint這個方法 ![](https://hackmd.io/_uploads/SkRT0_Uxp.png) Arg0帶入TreasuryCap的objectId ```rust Object { "type": String("created"), "sender": String("0xd1c4d5d9115d8aa9de808eb59c0f2cf614a67ca26f9759b1d07b56fe9fc612c7"), "owner": Object { "AddressOwner": String("0xd1c4d5d9115d8aa9de808eb59c0f2cf614a67ca26f9759b1d07b56fe9fc612c7"), }, "objectType": String("0x2::coin::TreasuryCap<0xee944f1737dd012eddb5d4860e1363bed3b70c5b8faeb9daddbcb25511e222f9::mycoin::MYCOIN>"), "objectId": String("0x52c28b8067f800286b4c9fb50a871212694203c26bf06861fb2309d40d08edf6"), "version": String("1103081"), "digest": String("BnGax56XVN8GkC6biqxWzqY5TrZ3SaveTpjTFLhZR1o7"), }, ``` Arg1帶入要mint的金額,因為上面設定1個Token為1x10的9次方,所以假如要mint 20個Token給自己,要輸入20然後後面9個0,也就是20000000000。 Arg2帶入要Mint給哪個錢包地址。 ![](https://hackmd.io/_uploads/r1VKeFIeT.png) 可以看到Ryan Coin(name) +20 RC(symbol),以及icon圖示 ![](https://hackmd.io/_uploads/Byz2lFLg6.png) 錢包也可以看到Ryan Coin目前有20個RC ![](https://hackmd.io/_uploads/HyuMWYUgT.png) #### 銷毀Token ![](https://hackmd.io/_uploads/S1YLzKIlp.png) Arg0帶入TreasuryCap的objectId ```rust Object { "type": String("created"), "sender": String("0xd1c4d5d9115d8aa9de808eb59c0f2cf614a67ca26f9759b1d07b56fe9fc612c7"), "owner": Object { "AddressOwner": String("0xd1c4d5d9115d8aa9de808eb59c0f2cf614a67ca26f9759b1d07b56fe9fc612c7"), }, "objectType": String("0x2::coin::TreasuryCap<0xee944f1737dd012eddb5d4860e1363bed3b70c5b8faeb9daddbcb25511e222f9::mycoin::MYCOIN>"), "objectId": String("0x52c28b8067f800286b4c9fb50a871212694203c26bf06861fb2309d40d08edf6"), "version": String("1103081"), "digest": String("BnGax56XVN8GkC6biqxWzqY5TrZ3SaveTpjTFLhZR1o7"), }, ``` Arg1要帶入要銷毀Coin的objectId ![](https://hackmd.io/_uploads/SkKpGtLxp.png) ![](https://hackmd.io/_uploads/SyBW7FIlT.png) 會發現我們錢包已經把那20個Ryan Coin銷毀了 ![](https://hackmd.io/_uploads/HJoQ7FUlT.png)