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.
What Personal Join Does
- Calls
market::mint_joined_in_personal_kioskwith 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
Soulobject is created withprovenance_kind = 2(personal-join) andorigin_refset to a string encoding the source type and object ID. - A
SoulStateshared 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
- Go to /wrap-link and choose Personal Join. You must be logged in with your connected Sui wallet.
- 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.
- 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. - Sign the transaction. The client calls
buildPersonalJoinSoulTxwhich assembles the full PTB: borrow the kiosk cap, place the source NFT, callmarket::mint_joined_in_personal_kiosk, return the kiosk cap. - Post-TX sync. After the TX succeeds, the app calls the publish API which mirrors the
SoulAsset,SoulState,SoulMemory, and optionallySoulSkillsrows 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
currentKioskIdis null, a new personal kiosk is created in the same PTB. - The source NFT is placed via
kiosk::placeusing a borrowedKioskOwnerCapextracted withpersonal_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.