Resources
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.
market::mint_joined_in_personal_kiosk with your source NFT's object ID and type.kiosk::place) before the Soul is minted — the contract requires co-location to prove current ownership.Soul object is created with provenance_kind = 2 (personal-join) and origin_ref set to a string encoding the source type and object ID.SoulState shared object is created and linked to the new Soul.soul.md), optional founding memory (memory.md), and optional skills bundle (skills.zip). These are encrypted and uploaded to Walrus before the TX is built.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.SoulAsset, SoulState, and the unified SoulContent slots (soul.md, founding memory, initial skills, optional sprite) into the DB. See Content Format.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
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.
Soulidity uses the Sui personal kiosk extension. The transaction handles kiosk creation if you do not already have one:
currentKioskId is null, a new personal kiosk is created in the same PTB.kiosk::place using a borrowed KioskOwnerCap extracted with personal_kiosk::borrow_val / return_val.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 and is not verified on-chain after mint — surfaces should label personal-join provenance accordingly. 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.
origin_ref. The contract does not deduplicate — wrap discipline is a UI / off-chain concern.market::mint_native_in_personal_kiosk) is simpler.