Share and Withdraw Functions
The tribe_storage.move module implements the core player-facing operations: moving items into and out of the shared tribe pool. There are three functions that handle different scenarios based on whether the caller is the SSU owner or a visiting player.
The Import Block
module tribe_storage_access::tribe_storage;
use tribe_storage_access::config::{Self, AdminCap, XAuth, ExtensionConfig};
use world::access::OwnerCap;
use world::character::Character;
use world::storage_unit::{Self, StorageUnit};The module imports from two sources:
tribe_storage_access::config-- our own config module forExtensionConfig,AdminCap, and theXAuthwitness.world::*-- EVE Frontier's on-chain framework providingOwnerCap,Character, andStorageUnittypes.
Error Constants
#[error(code = 0)]
const ENotTribeMember: vector<u8> = b"Character is not in the configured tribe";
#[error(code = 1)]
const ENoTribeConfig: vector<u8> = b"Tribe configuration not set on ExtensionConfig";SUI Move errors use the #[error] attribute. When an assert! fails with one of these constants, the error message is included in the transaction failure response, making debugging much easier.
share() -- Non-Owner Sharing
The share() function is for non-owner players -- players who have docked at the SSU and have items in their personal per-player slot.
public fun share<T: key>(
extension_config: &ExtensionConfig,
storage_unit: &mut StorageUnit,
character: &Character,
owner_cap: &OwnerCap<T>,
type_id: u64,
quantity: u32,
ctx: &mut TxContext,
) {
verify_tribe(extension_config, character);
let item = storage_unit::withdraw_by_owner(
storage_unit,
character,
owner_cap,
type_id,
quantity,
ctx,
);
storage_unit::deposit_to_open_inventory<XAuth>(
storage_unit,
character,
item,
config::x_auth(),
ctx,
);
}Step-by-step breakdown:
verify_tribe(extension_config, character) -- Checks that the character belongs to the configured tribe. Aborts if not.storage_unit::withdraw_by_owner(...) -- Withdraws items from the player's personal slot. This requires the player's OwnerCap as proof of identity. The function returns an Item value representing the withdrawn goods.storage_unit::deposit_to_open_inventory(...) -- Deposits the item into the SSU's open inventory (the shared tribe pool). This requires the XAuth witness, produced by config::x_auth().The Type Parameter
The share function is generic over T: key. This allows the OwnerCap to be parameterized by any object type (e.g., OwnerCap). In practice, non-owners always pass OwnerCap.
share_from_main() -- Owner Sharing
The SSU owner's items are in the main inventory rather than a per-player slot. This requires different withdrawal logic:
public fun share_from_main(
extension_config: &ExtensionConfig,
storage_unit: &mut StorageUnit,
character: &Character,
type_id: u64,
quantity: u32,
ctx: &mut TxContext,
) {
verify_tribe(extension_config, character);
let item = storage_unit::withdraw_item<XAuth>(
storage_unit,
character,
config::x_auth(),
type_id,
quantity,
ctx,
);
storage_unit::deposit_to_open_inventory<XAuth>(
storage_unit,
character,
item,
config::x_auth(),
ctx,
);
}Key Differences from share()
| Aspect | share() (non-owner) | share_from_main() (owner) |
| Withdrawal function | withdraw_by_owner | withdraw_item |
| Auth for withdrawal | OwnerCap | XAuth witness |
| Source inventory | Per-player slot | Main inventory |
| Needs OwnerCap param | Yes | No |
The owner does not need to pass an OwnerCap because withdraw_item uses the XAuth witness for authorization instead. The SSU framework recognizes the character as the owner and grants access to the main inventory.
withdraw() -- Taking from the Shared Pool
The withdraw() function moves items from the open inventory back to a player's personal slot:
public fun withdraw(
extension_config: &ExtensionConfig,
storage_unit: &mut StorageUnit,
character: &Character,
type_id: u64,
quantity: u32,
to_ssu_owner: bool,
ctx: &mut TxContext,
) {
verify_tribe(extension_config, character);
// Withdraw from the shared tribe pool (open inventory)
let item = storage_unit::withdraw_from_open_inventory<XAuth>(
storage_unit,
character,
config::x_auth(),
type_id,
quantity,
ctx,
);
// Deposit to the caller's personal slot
if (to_ssu_owner) {
storage_unit::deposit_item<XAuth>(
storage_unit, character, item, config::x_auth(), ctx,
);
} else {
storage_unit::deposit_to_owned<XAuth>(
storage_unit, character, item, config::x_auth(), ctx,
);
};
}The to_ssu_owner Flag
The withdraw() function uses a boolean flag to determine the destination:
to_ssu_owner = true: Callsdeposit_item, which puts items into the SSU owner's main inventory. Used when the caller IS the SSU owner.to_ssu_owner = false: Callsdeposit_to_owned, which puts items into the caller's per-player slot. Used when the caller is a visiting player.
This flag is determined client-side by checking whether the connected wallet owns the SSU.
Deposit Function Summary
| Function | Destination | Who uses it |
deposit_to_open_inventory | Open inventory (shared pool) | share() and share_from_main() |
deposit_item | Main inventory (owner's slot) | withdraw() when to_ssu_owner = true |
deposit_to_owned | Per-player slot | withdraw() when to_ssu_owner = false |
Calling from TypeScript
Withdraw Call
const tx = new Transaction();
tx.moveCall({
target: ${TRIBE_STORAGE_PACKAGE_ID}::tribe_storage::withdraw,
arguments: [
tx.object(TRIBE_STORAGE_CONFIG_ID), // ExtensionConfig (shared)
tx.object(ssuId), // StorageUnit (shared)
tx.object(characterObjectId), // Character (owned)
tx.pure.u64(typeId), // item type ID
tx.pure.u32(quantity), // how many
tx.pure.bool(isOwner), // to_ssu_owner flag
],
});Share Call (Non-Owner)
The non-owner share is more complex because it requires borrowing OwnerCap:
const characterType =; // Step 1: Borrow the OwnerCap const [ownerCap, receipt] = tx.moveCall({ target:${EVE_FRONTIER_PACKAGE_ID}::character::Character${EVE_FRONTIER_PACKAGE_ID}::character::borrow_owner_cap, typeArguments: [characterType], arguments: [ tx.object(characterObjectId), tx.object(Inputs.ReceivingRef({ objectId: characterOwnerCapId, version: capRef.version, digest: capRef.digest, })), ], }); // Step 2: Call share with the borrowed cap tx.moveCall({ target:${TRIBE_STORAGE_PACKAGE_ID}::tribe_storage::share, typeArguments: [characterType], arguments: [ tx.object(TRIBE_STORAGE_CONFIG_ID), tx.object(ssuId), tx.object(characterObjectId), ownerCap, tx.pure.u64(typeId), tx.pure.u32(quantity), ], }); // Step 3: Return the borrowed cap tx.moveCall({ target:${EVE_FRONTIER_PACKAGE_ID}::character::return_owner_cap, typeArguments: [characterType], arguments: [tx.object(characterObjectId), ownerCap, receipt], });
Share Call (Owner)
The owner path is simpler -- no cap borrowing needed:
tx.moveCall({
target: ${TRIBE_STORAGE_PACKAGE_ID}::tribe_storage::share_from_main,
arguments: [
tx.object(TRIBE_STORAGE_CONFIG_ID),
tx.object(ssuId),
tx.object(characterObjectId),
tx.pure.u64(typeId),
tx.pure.u32(quantity),
],
});Key Takeaways
- There are three entry functions:
share()for non-owners,share_from_main()for the SSU owner, andwithdraw()for both. - The SSU framework uses different functions for different inventory slots:
withdraw_by_ownervswithdraw_item,deposit_to_ownedvsdeposit_itemvsdeposit_to_open_inventory. - All gated SSU operations require the
XAuthwitness, produced byconfig::x_auth(). - The
to_ssu_ownerboolean inwithdraw()routes items to the correct destination inventory. - Non-owner share operations require borrowing
OwnerCapvia the borrow-use-return pattern in the PTB.
Sign in to track your progress.