Skip to content

feat(adapter-stellar): Add support for get_existing_roles from Stellar contracts v0.6.0 #298

@pasevin

Description

@pasevin

Summary

OpenZeppelin Stellar Contracts v0.6.0 introduces a new get_existing_roles function that returns all roles that currently have at least one member. This provides a direct on-chain method to enumerate roles without relying on indexer-based event reconstruction.

Current Implementation (Workaround)

The current StellarAccessControlService.getCurrentRoles() implementation uses a two-step workaround:

  1. Role Discovery via Indexer: If knownRoleIds aren't provided during contract registration, the adapter queries the indexer for historical ROLE_GRANTED and ROLE_REVOKED events to discover roles
  2. Member Enumeration: Once role IDs are known, it uses get_role_member_count and get_role_member to enumerate members

This approach has limitations:

  • Requires indexer availability for role discovery
  • Depends on historical event data being indexed
  • Adds latency due to indexer queries
  • May miss roles if indexer data is incomplete

New API from v0.6.0

From storage.rs:

/// Returns a vector containing all existing roles.
/// Defaults to empty vector if no roles exist.
///
/// # Arguments
///
/// * `e` - Access to Soroban environment.
///
/// # Notes
///
/// This function returns all roles that currently have at least one member.
pub fn get_existing_roles(e: &Env) -> Vec<Symbol> {
    let key = AccessControlStorageKey::ExistingRoles;
    if let Some(existing_roles) = e.storage().persistent().get(&key) {
        e.storage().persistent().extend_ttl(&key, ROLE_TTL_THRESHOLD, ROLE_EXTEND_AMOUNT);
        existing_roles
    } else {
        Vec::new(e)
    }
}

Return type: Vec<Symbol> - A vector of role symbols (identifiers)

Proposed Implementation

1. Add on-chain reader function

Add getExistingRoles() function in packages/adapter-stellar/src/access-control/onchain-reader.ts:

export async function getExistingRoles(
  contractAddress: string,
  networkConfig: StellarNetworkConfig
): Promise<string[]>

2. Update feature detection

Add get_existing_roles to feature detection in packages/adapter-stellar/src/access-control/feature-detection.ts:

const hasExistingRolesFunction = !!functions.find(
  (f) => f.name === 'get_existing_roles'
);

3. Update service to prefer new method

Modify StellarAccessControlService.getCurrentRoles() to:

  1. Check if contract supports get_existing_roles (via feature detection)
  2. If supported, call get_existing_roles to get role IDs directly
  3. If not supported, fall back to current indexer-based discovery
  4. Continue using get_role_member_count and get_role_member for member enumeration

4. Fallback Strategy

The current indexer-based role discovery should remain as a fallback for:

  • Contracts deployed before v0.6.0 that don't have get_existing_roles
  • Edge cases where on-chain call fails

Acceptance Criteria

  • Add getExistingRoles on-chain reader function
  • Update feature detection to check for get_existing_roles
  • Modify getCurrentRoles to prefer get_existing_roles when available
  • Keep existing indexer-based discovery as fallback
  • Add unit tests for the new function
  • Update documentation/comments

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions