Skip to main content

UserMapper

Struct UserMapper 

Source
pub struct UserMapper<SA, A = CurrentStorage>
where SA: StorageMapperApi, A: StorageAddress<SA>,
{ /* private fields */ }
Expand description

A specialized bidirectional mapper for managing smart contract users with auto-incrementing IDs.

§Storage Layout

The UserMapper maintains three storage patterns for efficient user management:

  1. Address to ID mapping:

    • base_key + "_address_to_id" + encoded_address → user ID (usize)
  2. ID to address mapping:

    • base_key + "_id_to_address" + id → user address
  3. User counter:

    • base_key + "_count" → total number of registered users

§User ID Assignment

  • IDs start from 1 and increment sequentially (never 0)
  • ID 0 represents “no user” or “user not found”
  • Once assigned, user IDs are permanent and never reused
  • No removal functionality - users cannot be deleted once registered

§Main Operations

  • Auto-register: get_or_create_user(address) - Gets ID or assigns new one. O(1).
  • Batch register: get_or_create_users(addresses, callback) - Registers multiple users efficiently.
  • Lookup ID: get_user_id(address) - Returns user ID (0 if not found). O(1).
  • Lookup Address: get_user_address(id) - Returns address for ID. O(1).
  • Count: get_user_count() - Returns total number of registered users. O(1).
  • Bulk Access: get_all_addresses() - Returns all user addresses (expensive). O(n).

§Trade-offs

  • Pros: Sequential IDs enable efficient iteration; permanent user registry; bidirectional lookup; auto-incrementing simplifies user management; built-in user counting.
  • Cons: No user removal; IDs never reused; get_all_addresses() can be expensive for large user bases; slightly higher storage overhead than simple address lists.

§Comparison with AddressToIdMapper

  • UserMapper: Specialized for users; includes user count; batch operations; no removal
  • AddressToIdMapper: Generic address mapping; supports removal; no built-in counting

§Use Cases

  • User registration and management systems
  • Participant tracking in contracts (staking, voting, etc.)
  • Whitelist management with sequential user numbering
  • Any scenario requiring both address-based lookup and iteration by user ID
  • Loyalty programs or membership systems

§Example

// Register users (auto-assign IDs)
let id1 = mapper.get_or_create_user(&user1);  // Returns 1
let id2 = mapper.get_or_create_user(&user2);  // Returns 2
let id1_again = mapper.get_or_create_user(&user1);  // Returns 1 (existing)

assert_eq!(id1, 1);
assert_eq!(id2, 2);
assert_eq!(id1_again, 1);
assert_eq!(mapper.get_user_count(), 2);

// Lookup by address
assert_eq!(mapper.get_user_id(&user1), 1);
assert_eq!(mapper.get_user_id(&user3), 0);  // Not registered

// Lookup by ID
assert_eq!(mapper.get_user_address(1), Some(user1.clone()));
assert_eq!(mapper.get_user_address(999), None);  // Invalid ID

// Safe address lookup (returns zero address if invalid)
let addr = mapper.get_user_address_or_zero(1);
assert_eq!(addr, user1);

let zero_addr = mapper.get_user_address_or_zero(999);
assert!(zero_addr.is_zero());

// Batch registration with callback
let addresses = vec![user2.clone(), user3.clone()];
mapper.get_or_create_users(addresses.into_iter(), |id, is_new| {
    if is_new {
        // Handle new user registration
    } else {
        // Handle existing user
    }
});

assert_eq!(mapper.get_user_count(), 3);

// Get all users (expensive for large lists)
let all_users = mapper.get_all_addresses();
assert_eq!(all_users.len(), 3);

// Note: No removal functionality - users are permanent

Implementations§

Source§

impl<SA, A> UserMapper<SA, A>
where SA: StorageMapperApi, A: StorageAddress<SA>,

Source

pub fn get_user_id(&self, address: &ManagedAddress<SA>) -> usize

Yields the user id for a given address. Will return 0 if the address is not known to the contract.

Source

pub fn get_user_address(&self, id: usize) -> Option<ManagedAddress<SA>>

Yields the user address for a given id, if the id is valid.

Source

pub fn get_user_address_unchecked(&self, id: usize) -> ManagedAddress<SA>

Yields the user address for a given id. Will cause a deserialization error if the id is invalid.

Source

pub fn get_user_address_or_zero(&self, id: usize) -> ManagedAddress<SA>

Yields the user address for a given id, if the id is valid. Otherwise returns the zero address (0x000…)

Source

pub fn get_user_count(&self) -> usize

Number of users.

Source

pub fn get_all_addresses(&self) -> ManagedVec<SA, ManagedAddress<SA>>

Loads all addresses from storage and places them in a ManagedVec. Can easily consume a lot of gas.

Source§

impl<SA> UserMapper<SA, CurrentStorage>
where SA: StorageMapperApi,

Source

pub fn get_or_create_users<AddressIter, F>( &self, address_iter: AddressIter, user_id_lambda: F, )
where AddressIter: Iterator<Item = ManagedAddress<SA>>, F: FnMut(usize, bool),

Tries to insert a number of addresses. Calls a lambda function for each, with the new user id and whether of nor the user was already present.

Source

pub fn get_or_create_user(&self, address: &ManagedAddress<SA>) -> usize

Yields the user id for a given address, or creates a new user id if there isn’t one. Will safely keep the user count in sync.

Trait Implementations§

Source§

impl<SA> StorageMapper<SA> for UserMapper<SA>
where SA: StorageMapperApi,

Source§

fn new(base_key: StorageKey<SA>) -> Self

Will be called automatically by the #[storage_mapper] annotation generated code.
Source§

impl<SA> StorageMapperFromAddress<SA> for UserMapper<SA, ManagedAddress<SA>>
where SA: StorageMapperApi,

Source§

fn new_from_address( address: ManagedAddress<SA>, base_key: StorageKey<SA>, ) -> Self

Will be called automatically by the #[storage_mapper_from_address] annotation generated code.
Source§

impl<SA> TopEncodeMulti for UserMapper<SA, CurrentStorage>
where SA: StorageMapperApi,

Behaves like a MultiResultVec

when an endpoint result, and lists all users addresses.

Source§

fn multi_encode_or_handle_err<O, H>( &self, output: &mut O, h: H, ) -> Result<(), H::HandledErr>

Version of top_encode that can handle errors as soon as they occur. For instance in can exit immediately and make sure that if it returns, it is a success. By not deferring error handling, this can lead to somewhat smaller bytecode.
Source§

fn multi_encode<O>(&self, output: &mut O) -> Result<(), EncodeError>

Attempt to serialize the value to output.
Source§

impl<SA> TypeAbi for UserMapper<SA, CurrentStorage>
where SA: StorageMapperApi,

Behaves like a MultiResultVec when an endpoint result.

Source§

type Unmanaged = UserMapper<SA>

Source§

fn type_name() -> TypeName

Source§

fn type_name_rust() -> TypeName

Source§

fn type_names() -> TypeNames

Source§

fn provide_type_descriptions<TDC: TypeDescriptionContainer>( accumulator: &mut TDC, )

A type can provide more than its own name. For instance, a struct can also provide the descriptions of the type of its fields. TypeAbi doesn’t care for the exact accumulator type, which is abstracted by the TypeDescriptionContainer trait.
Source§

impl<SA> TypeAbiFrom<UserMapper<SA>> for MultiValueEncoded<SA, ManagedAddress<SA>>
where SA: StorageMapperApi,

Source§

impl<SA> TypeAbiFrom<UserMapper<SA>> for UserMapper<SA, CurrentStorage>
where SA: StorageMapperApi,

Auto Trait Implementations§

§

impl<SA, A> Freeze for UserMapper<SA, A>

§

impl<SA, A> RefUnwindSafe for UserMapper<SA, A>

§

impl<SA, A> Send for UserMapper<SA, A>
where A: Send, SA: Send, <SA as HandleTypeInfo>::ManagedBufferHandle: Send,

§

impl<SA, A> Sync for UserMapper<SA, A>
where A: Sync, SA: Sync, <SA as HandleTypeInfo>::ManagedBufferHandle: Sync,

§

impl<SA, A> Unpin for UserMapper<SA, A>

§

impl<SA, A> UnsafeUnpin for UserMapper<SA, A>

§

impl<SA, A> UnwindSafe for UserMapper<SA, A>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<O, T> ProxyArg<O> for T
where O: TypeAbiFrom<T>, T: TopEncodeMulti,