Open11

【Sui】Derived Objects検証部屋

onigirionigiri

Derived Objects(派生オブジェクト)とは

  • 特徴

    • オブジェクトのIDを 決定的に(予測可能に) 計算できる仕組み。
      parent_id と key を組み合わせて hash(parent_id || key) で生成される。
      生成前でも オフチェーンでIDを計算 できる。
  • 作り方

    • claim を呼んで、親オブジェクトに「この key は使われた」とマークする。
      以降、その Derived Object は独立したオブジェクトとして存在する。
  • 利点

    • ミントを待たずに 事前にアドレスを用意 できる。
  • 例:

    • ゲーム → ガチャの報酬を事前に割り当て、ユーザーに即表示できる。
    • エアドロップ → 先に100万人分のアドレスを計算してトークン送付、ユーザーは後から受け取り可能。
    • マーケット → 存在前のNFTを出品可能。
    • ファイルシステム → 親IDをフォルダ、keyをファイル名のように扱える。

👉 要するに、Derived Objectsは「事前に計算できる住所を持つオブジェクト」で、スケーラブルな設計や新しいUXを可能にする仕組み

onigirionigiri

ここでは実際の挙動を確認するために、以下の流れで検証をしていく

  • NFTとミントを管理するレジストリを例にする
  • スマートコントラクトをデプロイ
  • オフチェーンでミント前のNFTのアドレスを計算
  • ミント前に計算されたアドレスにTokenを送ってみる
  • 実際にNFTをミントし、オフチェーンで計算したアドレスとNFTオブジェクトIDが一致するかを確認する
  • Receiveを呼んでオブジェクトからTokenを受け取れるか検証する
  • おまけ
    • もう一度Claimするとエラーになるか確認する
    • 親レジストリの動的フィールドを確認する
onigirionigiri

NFTとミントを管理するレジストリを例にする

まずはMoveでスマートコントラクトを書く

module derived_objects::derived_objects;

use sui::derived_object;
use sui::transfer::{Receiving};

public struct Registry has key { id: UID }

public struct OnigiriNFT has key, store { id: UID }

fun init(ctx: &mut TxContext) {
    transfer::share_object(Registry { id: object::new(ctx) });
}

public fun mint(registry: &mut Registry, ctx: &mut TxContext) {
    let nft = OnigiriNFT {
        // id: object::new(ctx), // 従来の方法
        id: derived_object::claim(&mut registry.id, ctx.sender()), // Derived Objectsの場合
    };
    transfer::transfer(nft, ctx.sender());
}

public fun receive_from_vault<T: key + store>(
  nft: &mut OnigiriNFT,
  receiving: Receiving<T>,
  ctx: &TxContext
) {
  let obj = transfer::public_receive(&mut nft.id, receiving);
  transfer::public_transfer(obj, ctx.sender());
}

いままでと異なるのは、 UID の生成方法

  • 従来: id: object::new(ctx)
  • Derived Objectsの場合: id: derived_object::claim(&mut registry.id, ctx.sender())
    • 親(Parent)としてRegistryのIDを指定し、キー(Key)としてSenderを指定
onigirionigiri

スマートコントラクトをデプロイ

スマコンをデプロイする

sui client publish
ログ

Transaction Digest: A5AFfKDT6Ep976oUKMdr9vAizdd3HDpyo6x5D9H56f51
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Transaction Data │
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Sender: 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 │
│ Gas Owner: 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 │
│ Gas Budget: 12845200 MIST │
│ Gas Price: 1000 MIST │
│ Gas Payment: │
│ ┌── │
│ │ ID: 0xa6f17cc33669a2a77a47671389af4893306fdd6139e431670298c1bf248525f1 │
│ │ Version: 584426633 │
│ │ Digest: 9yWGq2rjnizrqng5KF4zon1PxMusr3cSjGz7bHP6t8rn │
│ └── │
│ │
│ Transaction Kind: Programmable │
│ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │
│ │ Input Objects │ │
│ ├──────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │
│ │ 0 Pure Arg: Type: address, Value: "0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222" │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
│ ╭─────────────────────────────────────────────────────────────────────────╮ │
│ │ Commands │ │
│ ├─────────────────────────────────────────────────────────────────────────┤ │
│ │ 0 Publish: │ │
│ │ ┌ │ │
│ │ │ Dependencies: │ │
│ │ │ 0x0000000000000000000000000000000000000000000000000000000000000001 │ │
│ │ │ 0x0000000000000000000000000000000000000000000000000000000000000002 │ │
│ │ └ │ │
│ │ │ │
│ │ 1 TransferObjects: │ │
│ │ ┌ │ │
│ │ │ Arguments: │ │
│ │ │ Result 0 │ │
│ │ │ Address: Input 0 │ │
│ │ └ │ │
│ ╰─────────────────────────────────────────────────────────────────────────╯ │
│ │
│ Signatures: │
│ 0/l7UhEmfPkupI7+uGbnfjGV09AjKzw7iTD0OHldn7qUK3cx05vH9SN3aBYP7W4TDjTBZHm6a+ZCfMtdjQdNAw== │
│ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭───────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Transaction Effects │
├───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Digest: A5AFfKDT6Ep976oUKMdr9vAizdd3HDpyo6x5D9H56f51 │
│ Status: Success │
│ Executed Epoch: 870 │
│ │
│ Created Objects: │
│ ┌── │
│ │ ID: 0x268e2cced2ca098f3632432061553305ec806a774b1b6688cfa1db526b2f4ff9 │
│ │ Owner: Account Address ( 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 ) │
│ │ Version: 584426634 │
│ │ Digest: 7bZtddS3f152ErhYZg2TjohJfPxzFchhCmsHpBwvQDm5 │
│ └── │
│ ┌── │
│ │ ID: 0xa998461a61867e7aa50f503c8191755ebe527c4cc42c7288fea630af229b01d2 │
│ │ Owner: Immutable │
│ │ Version: 1 │
│ │ Digest: 6u2jgymRJLaWgTTat2bf9PVKREUYN7JUuEdsBQpK3k29 │
│ └── │
│ ┌── │
│ │ ID: 0xf86b48f75dfd571b79ecf5e6dbb5bec43a798c72d21c40e0984542f448174d04 │
│ │ Owner: Shared( 584426634 ) │
│ │ Version: 584426634 │
│ │ Digest: 43kwc2VLo3poLGzG6G6fgC7WJ2xLzWUG8j6U6rFiUwe7 │
│ └── │
│ Mutated Objects: │
│ ┌── │
│ │ ID: 0xa6f17cc33669a2a77a47671389af4893306fdd6139e431670298c1bf248525f1 │
│ │ Owner: Account Address ( 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 ) │
│ │ Version: 584426634 │
│ │ Digest: 5Efz9ZmGbsz9nQcMHFYJxfaKa6sKdQ7qVciM5rsjLYRD │
│ └── │
│ Gas Object: │
│ ┌── │
│ │ ID: 0xa6f17cc33669a2a77a47671389af4893306fdd6139e431670298c1bf248525f1 │
│ │ Owner: Account Address ( 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 ) │
│ │ Version: 584426634 │
│ │ Digest: 5Efz9ZmGbsz9nQcMHFYJxfaKa6sKdQ7qVciM5rsjLYRD │
│ └── │
│ Gas Cost Summary: │
│ Storage Cost: 10845200 MIST │
│ Computation Cost: 1000000 MIST │
│ Storage Rebate: 978120 MIST │
│ Non-refundable Storage Fee: 9880 MIST │
│ │
│ Transaction Dependencies: │
│ 2dkJtqsoQcyCZJvjZnskNVPQeynwVtwCcA9goAru6tTi │
│ DKsNfdAJdSqvHaWmBTMaAWny7kjj7B9mA7GUdLib7dBT │
│ Dd9pn1zFcSJjinxQewFd2gQdR4XKsHxFioD5MYnwLZQz │
╰───────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─────────────────────────────╮
│ No transaction block events │
╰─────────────────────────────╯

╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Object Changes │
├───────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Created Objects: │
│ ┌── │
│ │ ObjectID: 0x268e2cced2ca098f3632432061553305ec806a774b1b6688cfa1db526b2f4ff9 │
│ │ Sender: 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 │
│ │ Owner: Account Address ( 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 ) │
│ │ ObjectType: 0x2::package::UpgradeCap │
│ │ Version: 584426634 │
│ │ Digest: 7bZtddS3f152ErhYZg2TjohJfPxzFchhCmsHpBwvQDm5 │
│ └── │
│ ┌── │
│ │ ObjectID: 0xf86b48f75dfd571b79ecf5e6dbb5bec43a798c72d21c40e0984542f448174d04 │
│ │ Sender: 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 │
│ │ Owner: Shared( 584426634 ) │
│ │ ObjectType: 0xa998461a61867e7aa50f503c8191755ebe527c4cc42c7288fea630af229b01d2::derived_objects::Registry │
│ │ Version: 584426634 │
│ │ Digest: 43kwc2VLo3poLGzG6G6fgC7WJ2xLzWUG8j6U6rFiUwe7 │
│ └── │
│ Mutated Objects: │
│ ┌── │
│ │ ObjectID: 0xa6f17cc33669a2a77a47671389af4893306fdd6139e431670298c1bf248525f1 │
│ │ Sender: 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 │
│ │ Owner: Account Address ( 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 ) │
│ │ ObjectType: 0x2::coin::Coin<0x2::sui::SUI> │
│ │ Version: 584426634 │
│ │ Digest: 5Efz9ZmGbsz9nQcMHFYJxfaKa6sKdQ7qVciM5rsjLYRD │
│ └── │
│ Published Objects: │
│ ┌── │
│ │ PackageID: 0xa998461a61867e7aa50f503c8191755ebe527c4cc42c7288fea630af229b01d2 │
│ │ Version: 1 │
│ │ Digest: 6u2jgymRJLaWgTTat2bf9PVKREUYN7JUuEdsBQpK3k29 │
│ │ Modules: derived_objects │
│ └── │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭───────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Balance Changes │
├───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ┌── │
│ │ Owner: Account Address ( 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 ) │
│ │ CoinType: 0x2::sui::SUI │
│ │ Amount: -10867080 │
│ └── │
╰───────────────────────────────────────────────────────────────────────────────────────────────────╯

ログからPackageIDとRegistry IDをメモしておく

PackageID: 0xa998461a61867e7aa50f503c8191755ebe527c4cc42c7288fea630af229b01d2
│  │ ObjectID: 0xf86b48f75dfd571b79ecf5e6dbb5bec43a798c72d21c40e0984542f448174d04                               │
│  │ Sender: 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222                                 │
│  │ Owner: Shared( 584426634 )                                                                                 │
│  │ ObjectType: 0xa998461a61867e7aa50f503c8191755ebe527c4cc42c7288fea630af229b01d2::derived_objects::Registry
onigirionigiri

オフチェーンでミント前のNFTのアドレスを計算

いよいよ、オフチェーンでミント前のNFTのオブジェクトIDを計算していくよ!わくわく

ちなみに、@mysten/sui/utils" のSDKを使えば超簡単!
今回計算するのに必要なのはRegistory IDとミントするウォレットアドレスだけ。

import { bcs } from "@mysten/sui/bcs"
import { deriveObjectID } from "@mysten/sui/utils"

const REFISTRY_ID = "0xf86b48f75dfd571b79ecf5e6dbb5bec43a798c72d21c40e0984542f448174d04"
const WALLET_ADDRESS = "0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222"

console.log(deriveObjectID(REFISTRY_ID, "address", bcs.Address.serialize(WALLET_ADDRESS).toBytes()))

実行結果

$ bun derived_objects.ts

0x11f3e872150c419b4e01ec89ff31aab53b5f6b9b1c4ac5acb7c2e876bbba96e7

0x11f3e872150c419b4e01ec89ff31aab53b5f6b9b1c4ac5acb7c2e876bbba96e7 これが、ミント前にオフチェーンで計算したアドレス!

onigirionigiri

ミント前に計算されたアドレスにTokenを送ってみる

すぐにNFTミントしたいけど、

エアドロップ → 先に100万人分のアドレスを計算してトークン送付、ユーザーは後から受け取り可能。

を検証したいので、オフチェーンで計算したNFTアドレスにTokenを送ってみる。今回は単純にSuiトークンを送る

ちなみにNFTなどのオブジェクトにトークンを送ることを、 Transfer to Obejctsって言うよ
https://docs.sui.io/concepts/transfers/transfer-to-object

Prime MachineやRootletsなどのNFTを持っている人にはお馴染みの概念だね!NFTがトークンや他のNFTを所有できたりするよ

ミント前のNFTアドレスにトークンを送る

Validと表示された!送れそう!なんかすごい

では実際に送ってみる!

送金成功!(ちなみに最初Mainnetで送ってしまい無事GOX。死にたい)

SuiVisionでアドレスを入れると、Accountのみ表示された!

実際に 0.1 SUI 着金している👍

onigirionigiri

実際にNFTをミントし、オフチェーンで計算したアドレスとNFTオブジェクトIDが一致するかを確認する

Sui Client PTB CLIで、実際にNFTをミントしてみるよ!

https://docs.sui.io/references/cli/ptb

sui client ptb \
  --move-call \
  0xa998461a61867e7aa50f503c8191755ebe527c4cc42c7288fea630af229b01d2::derived_objects::mint \
  @0xf86b48f75dfd571b79ecf5e6dbb5bec43a798c72d21c40e0984542f448174d04
ログ

Transaction Digest: DAkZC52Z3tNjiuLuWD7vShMYEjUr8emoTNGCQpgJx8gw
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Transaction Data │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Sender: 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 │
│ Gas Owner: 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 │
│ Gas Budget: 6877680 MIST │
│ Gas Price: 1000 MIST │
│ Gas Payment: │
│ ┌── │
│ │ ID: 0xa6f17cc33669a2a77a47671389af4893306fdd6139e431670298c1bf248525f1 │
│ │ Version: 584426635 │
│ │ Digest: 2ABcyh57B9aXWcthfjiPTSE6BEzWBg6EN44FrL25hg9E │
│ └── │
│ │
│ Transaction Kind: Programmable │
│ ╭─────────────────────────────────────────────────────────────────────────────────────────────╮ │
│ │ Input Objects │ │
│ ├─────────────────────────────────────────────────────────────────────────────────────────────┤ │
│ │ 0 Shared Object ID: 0xf86b48f75dfd571b79ecf5e6dbb5bec43a798c72d21c40e0984542f448174d04 │ │
│ ╰─────────────────────────────────────────────────────────────────────────────────────────────╯ │
│ ╭──────────────────────────────────────────────────────────────────────────────────╮ │
│ │ Commands │ │
│ ├──────────────────────────────────────────────────────────────────────────────────┤ │
│ │ 0 MoveCall: │ │
│ │ ┌ │ │
│ │ │ Function: mint │ │
│ │ │ Module: derived_objects │ │
│ │ │ Package: 0xa998461a61867e7aa50f503c8191755ebe527c4cc42c7288fea630af229b01d2 │ │
│ │ │ Arguments: │ │
│ │ │ Input 0 │ │
│ │ └ │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────╯ │
│ │
│ Signatures: │
│ FF1b8EmMvSyjVwAVfmYV3bwPtCvmdY6SBlZZXCmBKaaV02k8CeXdLTzAx0O5x1/lNruI0z+D7rU1rC+2FGZkBg== │
│ │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
╭───────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Transaction Effects │
├───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Digest: DAkZC52Z3tNjiuLuWD7vShMYEjUr8emoTNGCQpgJx8gw │
│ Status: Success │
│ Executed Epoch: 870 │
│ │
│ Created Objects: │
│ ┌── │
│ │ ID: 0x11f3e872150c419b4e01ec89ff31aab53b5f6b9b1c4ac5acb7c2e876bbba96e7 │
│ │ Owner: Account Address ( 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 ) │
│ │ Version: 584426636 │
│ │ Digest: 6sXeLFZf3NFQe8w7dTvegjWEbh5mumgUBostDXoWccSh │
│ └── │
│ ┌── │
│ │ ID: 0xc255ca1b86523c911c7031752245c5c63e8b2033941083b599c9a9c13b3f4052 │
│ │ Owner: Object ID: ( 0xf86b48f75dfd571b79ecf5e6dbb5bec43a798c72d21c40e0984542f448174d04 ) │
│ │ Version: 584426636 │
│ │ Digest: 83xx613dbJypgkaBsoMEcwTFvjJXSoTuzynTskiKZBFJ │
│ └── │
│ Mutated Objects: │
│ ┌── │
│ │ ID: 0xa6f17cc33669a2a77a47671389af4893306fdd6139e431670298c1bf248525f1 │
│ │ Owner: Account Address ( 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 ) │
│ │ Version: 584426636 │
│ │ Digest: 5vCSiUytJYXSVsNWjCik49Hmo2XgAUnidRFLtSRstrjS │
│ └── │
│ ┌── │
│ │ ID: 0xf86b48f75dfd571b79ecf5e6dbb5bec43a798c72d21c40e0984542f448174d04 │
│ │ Owner: Shared( 584426634 ) │
│ │ Version: 584426636 │
│ │ Digest: 7EtbtqLeNGaUgP3y8runDRRZH6r2XsVxNtcMFfdn89KB │
│ └── │
│ Shared Objects: │
│ ┌── │
│ │ ID: 0xf86b48f75dfd571b79ecf5e6dbb5bec43a798c72d21c40e0984542f448174d04 │
│ │ Version: 584426634 │
│ │ Digest: 43kwc2VLo3poLGzG6G6fgC7WJ2xLzWUG8j6U6rFiUwe7 │
│ └── │
│ Gas Object: │
│ ┌── │
│ │ ID: 0xa6f17cc33669a2a77a47671389af4893306fdd6139e431670298c1bf248525f1 │
│ │ Owner: Account Address ( 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 ) │
│ │ Version: 584426636 │
│ │ Digest: 5vCSiUytJYXSVsNWjCik49Hmo2XgAUnidRFLtSRstrjS │
│ └── │
│ Gas Cost Summary: │
│ Storage Cost: 6232000 MIST │
│ Computation Cost: 1000000 MIST │
│ Storage Rebate: 2332440 MIST │
│ Non-refundable Storage Fee: 23560 MIST │
│ │
│ Transaction Dependencies: │
│ 7b3rRPQQv4FZaEJfjsopUfyEzjQLAWyJEfbkZtnzVrBo │
│ A5AFfKDT6Ep976oUKMdr9vAizdd3HDpyo6x5D9H56f51 │
╰───────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─────────────────────────────╮
│ No transaction block events │
╰─────────────────────────────╯

╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Object Changes │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Created Objects: │
│ ┌── │
│ │ ObjectID: 0x11f3e872150c419b4e01ec89ff31aab53b5f6b9b1c4ac5acb7c2e876bbba96e7 │
│ │ Sender: 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 │
│ │ Owner: Account Address ( 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 ) │
│ │ ObjectType: 0xa998461a61867e7aa50f503c8191755ebe527c4cc42c7288fea630af229b01d2::derived_objects::OnigiriNFT │
│ │ Version: 584426636 │
│ │ Digest: 6sXeLFZf3NFQe8w7dTvegjWEbh5mumgUBostDXoWccSh │
│ └── │
│ ┌── │
│ │ ObjectID: 0xc255ca1b86523c911c7031752245c5c63e8b2033941083b599c9a9c13b3f4052 │
│ │ Sender: 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 │
│ │ Owner: Object ID: ( 0xf86b48f75dfd571b79ecf5e6dbb5bec43a798c72d21c40e0984542f448174d04 ) │
│ │ ObjectType: 0x2::dynamic_field::Field<0x2::derived_object::Claimed, 0x2::derived_object::ClaimedStatus> │
│ │ Version: 584426636 │
│ │ Digest: 83xx613dbJypgkaBsoMEcwTFvjJXSoTuzynTskiKZBFJ │
│ └── │
│ Mutated Objects: │
│ ┌── │
│ │ ObjectID: 0xa6f17cc33669a2a77a47671389af4893306fdd6139e431670298c1bf248525f1 │
│ │ Sender: 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 │
│ │ Owner: Account Address ( 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 ) │
│ │ ObjectType: 0x2::coin::Coin<0x2::sui::SUI> │
│ │ Version: 584426636 │
│ │ Digest: 5vCSiUytJYXSVsNWjCik49Hmo2XgAUnidRFLtSRstrjS │
│ └── │
│ ┌── │
│ │ ObjectID: 0xf86b48f75dfd571b79ecf5e6dbb5bec43a798c72d21c40e0984542f448174d04 │
│ │ Sender: 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 │
│ │ Owner: Shared( 584426634 ) │
│ │ ObjectType: 0xa998461a61867e7aa50f503c8191755ebe527c4cc42c7288fea630af229b01d2::derived_objects::Registry │
│ │ Version: 584426636 │
│ │ Digest: 7EtbtqLeNGaUgP3y8runDRRZH6r2XsVxNtcMFfdn89KB │
│ └── │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭───────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Balance Changes │
├───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ┌── │
│ │ Owner: Account Address ( 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 ) │
│ │ CoinType: 0x2::sui::SUI │
│ │ Amount: -4899560 │
│ └── │
╰───────────────────────────────────────────────────────────────────────────────────────────────────╯

│ Created Objects: │
│ ┌── │
│ │ ObjectID: 0x11f3e872150c419b4e01ec89ff31aab53b5f6b9b1c4ac5acb7c2e876bbba96e7 │
│ │ Sender: 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 │
│ │ Owner: Account Address ( 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 ) │
│ │ ObjectType: 0xa998461a61867e7aa50f503c8191755ebe527c4cc42c7288fea630af229b01d2::derived_objects::OnigiriNFT │

キターーーー 0x11f3e872150c419b4e01ec89ff31aab53b5f6b9b1c4ac5acb7c2e876bbba96e7 オフチェーン計算と一致した!

SuiVisionで検索してみると...

さっきはAccoutのみだったのに Object も増えてる!

ちゃんと 0x11f3e872150c419b4e01ec89ff31aab53b5f6b9b1c4ac5acb7c2e876bbba96e7 としてオブジェクトが生成されている。ちょっと感動

onigirionigiri

Receiveを呼んでオブジェクトからトークンを受け取れるか検証する

ミント前に送った(エアドロした) 0.1 SUI を実際に受け取れるか確認する

public fun receive_from_vault<T: key + store>(
  nft: &mut OnigiriNFT,
  receiving: Receiving<T>,
  ctx: &TxContext
) {
  let obj = transfer::public_receive(&mut nft.id, receiving);
  transfer::public_transfer(obj, ctx.sender());
}

スマコンだとここでreceiveを定義している。ミントしたNFTのIDと、受け取りたいトークン(今回はSUI)のIDを指定する

PTBはこんな感じ

sui client ptb \
  --move-call \
  0xa998461a61867e7aa50f503c8191755ebe527c4cc42c7288fea630af229b01d2::derived_objects::receive_from_vault \
  "<0x2::coin::Coin<0x2::sui::SUI>>" \
  @0x11f3e872150c419b4e01ec89ff31aab53b5f6b9b1c4ac5acb7c2e876bbba96e7 @0x34feaeb836b9638877a4fabe994ca7f6749941e2d67a0316940b2c67c408bd32
ログ

Transaction Digest: 2wXTz4Hq24AusdrhfcxfFYWVQtteH5uhdcHDMre8wGaM
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Transaction Data │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Sender: 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 │
│ Gas Owner: 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 │
│ Gas Budget: 3011712 MIST │
│ Gas Price: 1000 MIST │
│ Gas Payment: │
│ ┌── │
│ │ ID: 0xa6f17cc33669a2a77a47671389af4893306fdd6139e431670298c1bf248525f1 │
│ │ Version: 584426637 │
│ │ Digest: AkD6VDNZRqbW7pma99hhi2SvLhZSx7NKJeoea4CmGtXc │
│ └── │
│ │
│ Transaction Kind: Programmable │
│ ╭─────────────────────────────────────────────────────────────────────────────────────────────╮ │
│ │ Input Objects │ │
│ ├─────────────────────────────────────────────────────────────────────────────────────────────┤ │
│ │ 0 Imm/Owned Object ID: 0x11f3e872150c419b4e01ec89ff31aab53b5f6b9b1c4ac5acb7c2e876bbba96e7 │ │
│ │ 1 Receiving Object ID: 0x34feaeb836b9638877a4fabe994ca7f6749941e2d67a0316940b2c67c408bd32 │ │
│ ╰─────────────────────────────────────────────────────────────────────────────────────────────╯ │
│ ╭──────────────────────────────────────────────────────────────────────────────────╮ │
│ │ Commands │ │
│ ├──────────────────────────────────────────────────────────────────────────────────┤ │
│ │ 0 MoveCall: │ │
│ │ ┌ │ │
│ │ │ Function: receive_from_vault │ │
│ │ │ Module: derived_objects │ │
│ │ │ Package: 0xa998461a61867e7aa50f503c8191755ebe527c4cc42c7288fea630af229b01d2 │ │
│ │ │ Type Arguments: │ │
│ │ │ 0x2::coin::Coin<0x2::sui::SUI> │ │
│ │ │ Arguments: │ │
│ │ │ Input 0 │ │
│ │ │ Input 1 │ │
│ │ └ │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────╯ │
│ │
│ Signatures: │
│ ehMqlOFcBvPsGH4KA8XqRrA4TRk3ffavQCexCQ11THQpFiheVOrLtqbaZlPUlPax+FZVcdLdYXYecpv2SgfZDg== │
│ │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
╭───────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Transaction Effects │
├───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Digest: 2wXTz4Hq24AusdrhfcxfFYWVQtteH5uhdcHDMre8wGaM │
│ Status: Success │
│ Executed Epoch: 870 │
│ Mutated Objects: │
│ ┌── │
│ │ ID: 0x11f3e872150c419b4e01ec89ff31aab53b5f6b9b1c4ac5acb7c2e876bbba96e7 │
│ │ Owner: Account Address ( 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 ) │
│ │ Version: 584426638 │
│ │ Digest: 6rGp8oB1fmY6KDDZHNqwuHRuY2xmUi5u6mcvNjZngFCT │
│ └── │
│ ┌── │
│ │ ID: 0x34feaeb836b9638877a4fabe994ca7f6749941e2d67a0316940b2c67c408bd32 │
│ │ Owner: Account Address ( 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 ) │
│ │ Version: 584426638 │
│ │ Digest: 46RTUhgUPWZteDVCC2hGaJsr43nFGkFtmd2yFWD7tgkB │
│ └── │
│ ┌── │
│ │ ID: 0xa6f17cc33669a2a77a47671389af4893306fdd6139e431670298c1bf248525f1 │
│ │ Owner: Account Address ( 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 ) │
│ │ Version: 584426638 │
│ │ Digest: 125H21hQUuUy1Yypus4Gh6sR5nMNxxHTLXr8ZBT1dwm3 │
│ └── │
│ Gas Object: │
│ ┌── │
│ │ ID: 0xa6f17cc33669a2a77a47671389af4893306fdd6139e431670298c1bf248525f1 │
│ │ Owner: Account Address ( 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 ) │
│ │ Version: 584426638 │
│ │ Digest: 125H21hQUuUy1Yypus4Gh6sR5nMNxxHTLXr8ZBT1dwm3 │
│ └── │
│ Gas Cost Summary: │
│ Storage Cost: 3359200 MIST │
│ Computation Cost: 1000000 MIST │
│ Storage Rebate: 3325608 MIST │
│ Non-refundable Storage Fee: 33592 MIST │
│ │
│ Transaction Dependencies: │
│ 2dkJtqsoQcyCZJvjZnskNVPQeynwVtwCcA9goAru6tTi │
│ 7b3rRPQQv4FZaEJfjsopUfyEzjQLAWyJEfbkZtnzVrBo │
│ 9tTvGUstD1wxNGTi9NoAnLv5NMAk9aKP5cyjkcMoJumS │
│ A5AFfKDT6Ep976oUKMdr9vAizdd3HDpyo6x5D9H56f51 │
│ DAkZC52Z3tNjiuLuWD7vShMYEjUr8emoTNGCQpgJx8gw │
╰───────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─────────────────────────────╮
│ No transaction block events │
╰─────────────────────────────╯

╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Object Changes │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Mutated Objects: │
│ ┌── │
│ │ ObjectID: 0x11f3e872150c419b4e01ec89ff31aab53b5f6b9b1c4ac5acb7c2e876bbba96e7 │
│ │ Sender: 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 │
│ │ Owner: Account Address ( 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 ) │
│ │ ObjectType: 0xa998461a61867e7aa50f503c8191755ebe527c4cc42c7288fea630af229b01d2::derived_objects::OnigiriNFT │
│ │ Version: 584426638 │
│ │ Digest: 6rGp8oB1fmY6KDDZHNqwuHRuY2xmUi5u6mcvNjZngFCT │
│ └── │
│ ┌── │
│ │ ObjectID: 0x34feaeb836b9638877a4fabe994ca7f6749941e2d67a0316940b2c67c408bd32 │
│ │ Sender: 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 │
│ │ Owner: Account Address ( 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 ) │
│ │ ObjectType: 0x2::coin::Coin<0x2::sui::SUI> │
│ │ Version: 584426638 │
│ │ Digest: 46RTUhgUPWZteDVCC2hGaJsr43nFGkFtmd2yFWD7tgkB │
│ └── │
│ ┌── │
│ │ ObjectID: 0xa6f17cc33669a2a77a47671389af4893306fdd6139e431670298c1bf248525f1 │
│ │ Sender: 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 │
│ │ Owner: Account Address ( 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 ) │
│ │ ObjectType: 0x2::coin::Coin<0x2::sui::SUI> │
│ │ Version: 584426638 │
│ │ Digest: 125H21hQUuUy1Yypus4Gh6sR5nMNxxHTLXr8ZBT1dwm3 │
│ └── │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭───────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Balance Changes │
├───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ┌── │
│ │ Owner: Account Address ( 0x11f3e872150c419b4e01ec89ff31aab53b5f6b9b1c4ac5acb7c2e876bbba96e7 ) │
│ │ CoinType: 0x2::sui::SUI │
│ │ Amount: -100000000 │
│ └── │
│ ┌── │
│ │ Owner: Account Address ( 0x7babb4543f2d93b99bc231134ebddc57f15416b38874834c7652c85cb01a0222 ) │
│ │ CoinType: 0x2::sui::SUI │
│ │ Amount: 98966408 │
│ └── │
╰───────────────────────────────────────────────────────────────────────────────────────────────────╯

成功したっぽいので、SuiVisionを確認する

NFT側のSUIはなくなっていて、

NFTのオーナー側でSUIを受け取ることができた!

これは例えばPrimeMachinにエアドロされた $BLUB トークンを、 PrimeMachinオーナーのウォレットアドレス宛にClaimした、みたいなのと一緒だよ

onigirionigiri

おまけ1: もう一度Claimするとエラーになるか確認する

Deribed Objectでアドレスを計算して生成することを Claim という。
これは Parent + Key の組み合わせで1回しかClaimできないようになっている

id: derived_object::claim(&mut registry.id, ctx.sender())

つまり、今回書いた mint 関数の場合、特に何か assert はしていないけど、 Derived Objectsによって1アドレス1ミント を強制することができる!

これは想定していなかったちょっとした発見であった

public fun mint(registry: &mut Registry, ctx: &mut TxContext) {
    let nft = OnigiriNFT {
        id: derived_object::claim(&mut registry.id, ctx.sender()),
    };
    transfer::transfer(nft, ctx.sender());
}

試しに同じウォレットからmintを呼ぶと、エラーになる

エラー内容: 'EObjectAlreadyExists' -- 'Derived object is already claimed.'

sui client ptb \
  --move-call \
  0xa998461a61867e7aa50f503c8191755ebe527c4cc42c7288fea630af229b01d2::derived_objects::mint \
  @0xf86b48f75dfd571b79ecf5e6dbb5bec43a798c72d21c40e0984542f448174d04

Error executing transaction 'ZuuMD2XAC7erXMjuNX8MjHgDKv9xsRFThCiUuk7kwf1': 1st command aborted within function '0x0000000000000000000000000000000000000000000000000000000000000002::derived_object::claim' at line 41. Aborted with error code 0 --'EObjectAlreadyExists' -- 'Derived object is already claimed.'
onigirionigiri

おまけ2: 親レジストリの動的フィールドを確認する

0xa998461a61867e7aa50f503c8191755ebe527c4cc42c7288fea630af229b01d2::derived_objects::Registry がどうなっているか確認する

先ほど生成したNFTアドレスが、Registryの動的フィールドとして格納されている。
おまけ1ではここの動的フィールドを確認して、Claim済みかどうかをチェックし、Claim済みであれば 'EObjectAlreadyExists' -- 'Derived object is already claimed.' エラーを投げるようになっている