Soulidity

Resources

Wrap + Link Guide

Personal Join lets you add a Soul layer on top of any existing Sui NFT. Your original NFT stays unchanged — you gain Soul identity, encrypted content, memory, and skills without a new token contract.

📊 Protocol Stats

What Personal Join Does

  • Calls market::mint_joined_in_personal_kiosk with your source NFT's object ID and type.
  • The source NFT is placed into your personal kiosk (using kiosk::place) before the Soul is minted — the contract requires co-location to prove current ownership.
  • A fresh Soul object is created with provenance_kind = 2 (personal-join) and origin_ref set to a string encoding the source type and object ID.
  • A SoulState shared object is created and linked to the new Soul.
  • Optionally creates a founding memory entry and an initial skills bundle in the same transaction.

Step-by-Step: Personal Join Flow

  1. Go to /wrap-link and choose Personal Join. You must be logged in with your connected Sui wallet.
  2. Select your NFT. The page reads your personal kiosk on-chain and lists all NFTs with Sui Display metadata. Only Sui-native NFTs are supported in the current release.
  3. Configure the Soul. Fill in name, description, image URL, and upload your Soul content (soul.md), optional founding memory (memory.md), and optional skills bundle (skill.zip). These are encrypted and uploaded to Walrus before the TX is built.
  4. Sign the transaction. The client calls buildPersonalJoinSoulTx which assembles the full PTB: borrow the kiosk cap, place the source NFT, call market::mint_joined_in_personal_kiosk, return the kiosk cap.
  5. Post-TX sync. After the TX succeeds, the app calls the publish API which mirrors the SoulAsset, SoulState, SoulMemory, and optionally SoulSkills rows into the DB.

Transaction Builder

The TX is built in web/lib/soulidity/tx/personal-join.ts via buildPersonalJoinSoulTx:

buildPersonalJoinSoulTx({
  currentKioskId?,             // reuse existing personal kiosk if present
  currentKioskCapOnChainId?,
  sourceObjectId,              // the NFT to wrap
  sourceObjectType,            // full Move type string e.g. "0xabc::nft::MyNFT"
  name, description, imageUrl,
  protectedBlobObjectId,       // Walrus Blob object already registered on-chain
  foundingMemoryBlobObjectId?, // optional founding memory Blob
  skillsBlobObjectId?,         // optional initial skills Blob
  initialSkillName?,           // defaults to "default"
  skillsVisibility?,           // "public" | "private", defaults to "private"
  initialSprite?,              // optional active sprite binding + sprite config JSON
  initialVoice?,               // optional active voice binding + voice config JSON
  originRef,                   // provenance string — typically "type::objectId"
  creatorRoyaltyBps,           // 0–10000
})

The function validates that originRef and sourceObjectType are non-empty before building. Soulidity deployment IDs are loaded from lib/soulidity/deployment-manifest.json.

Kiosk Mechanics

Soulidity uses the Sui personal kiosk extension. The transaction handles kiosk creation if you do not already have one:

  • If currentKioskId is null, a new personal kiosk is created in the same PTB.
  • The source NFT is placed via kiosk::place using a borrowed KioskOwnerCap extracted with personal_kiosk::borrow_val / return_val.
  • The market contract requires the source NFT to be inside the same kiosk where the Soul is minted. It reads the NFT's object ID from the kiosk directly on-chain.
  • After mint the source NFT remains in your kiosk alongside the new Soul.

Provenance on-chain

The origin_ref field on the Soul object permanently records the wrapped NFT's identity. It is a human-readable string set by the UI at wrap time. The SoulCreated event carries provenance_kind = 2 so indexers can distinguish personal-join Souls from native mints.

The source NFT is never locked, burned, or modified. You can still trade the source NFT independently. The Soul layer is purely additive — revoking it would require the Soul owner to manually burn or abandon the Soul object.