Skip to content

Add utility functions to improve DX for Advanced Permissions #181

@cursor

Description

@cursor

Overview

While working with the SDK, several developer experience (DX) issues have been identified that could be improved with utility functions. These utilities would make it easier to work with delegations and permission contexts without requiring manual parsing of caveats.

Requested Features

1. Get Expiry Data from Delegation

Problem: Currently, developers need to manually parse the caveats to extract expiry information from a delegation.

Proposed Solution: Add a utility function that extracts and returns expiry data from a delegation's timestamp caveat.

Example API:

// Utility function to get expiry information
function getExpiryData(delegation: Delegation): {
  afterThreshold: number;    // Unix timestamp (seconds) - valid after this time
  beforeThreshold: number;   // Unix timestamp (seconds) - valid before this time (expiry)
} | null;

// Usage
const delegation = createDelegation({
  from: '0x...',
  to: '0x...',
  environment,
  scope: {...},
  caveats: {
    timestamp: {
      afterThreshold: 1704067200,  // Jan 1, 2024
      beforeThreshold: 1735689600,  // Jan 1, 2025
    }
  }
});

const expiryData = getExpiryData(delegation);
if (expiryData) {
  console.log(`Permission expires on: ${new Date(expiryData.beforeThreshold * 1000)}`);
}

Notes:

  • Should return null if no TimestampEnforcer caveat is found
  • Should handle the case where only one threshold is set (e.g., only beforeThreshold for expiry)

2. Check if Permission is Expired

Problem: Developers need a simple way to check whether a permission has expired without manually parsing and comparing timestamps.

Proposed Solution: Add a utility function that checks if a delegation has expired based on the current time.

Example API:

// Utility function to check if delegation is expired
function isPermissionExpired(delegation: Delegation): boolean;

// Usage
const delegation = getStoredDelegation();

if (isPermissionExpired(delegation)) {
  console.log('Permission has expired');
  // Handle expired permission
} else {
  // Permission is still valid
  await redeemDelegation(delegation);
}

Notes:

  • Should return false if no TimestampEnforcer caveat is found (permission doesn't expire)
  • Should check against the current timestamp
  • Should consider both beforeThreshold (expiry) and afterThreshold (not yet valid)

3. Extend CaveatEnforcerClient to Accept PermissionContext

Problem: Currently, CaveatEnforcerClient actions only accept a single Delegation object via CaveatEnforcerParams. However, the SDK has a PermissionContext type that can be either Delegation[] or Hex (encoded delegations). Developers can decode permission contexts to get delegations, but it would be better if the client actions could handle this automatically.

Proposed Solution: Extend CaveatEnforcerClient actions to accept PermissionContext in addition to a single Delegation.

Current Implementation:

export type CaveatEnforcerParams = {
  delegation: Delegation;
};

Proposed API:

export type CaveatEnforcerParams = {
  delegation: Delegation;
} | {
  permissionContext: PermissionContext;
  delegationIndex?: number; // Optional, defaults to 0 (leaf delegation)
};

// Usage Example 1: Current way still works
await caveatClient.getErc20PeriodTransferEnforcerAvailableAmount({
  delegation: myDelegation
});

// Usage Example 2: New way with permission context
const permissionContext = decodeDelegations(encodedPermissions); // or already a Delegation[]

await caveatClient.getErc20PeriodTransferEnforcerAvailableAmount({
  permissionContext: permissionContext,
  delegationIndex: 0 // Check the leaf delegation
});

// Usage Example 3: With encoded permission context
await caveatClient.getErc20PeriodTransferEnforcerAvailableAmount({
  permissionContext: '0x...', // Hex-encoded delegation chain
});

Implementation Notes:

  • The SDK already has decodeDelegations() function to handle PermissionContext
  • For delegation chains, the default should be index 0 (the leaf delegation, which inherits constraints from parents)
  • All existing CaveatEnforcerClient methods should support this:
    • getErc20PeriodTransferEnforcerAvailableAmount
    • getErc20StreamingEnforcerAvailableAmount
    • getMultiTokenPeriodEnforcerAvailableAmount
    • getNativeTokenPeriodTransferEnforcerAvailableAmount
    • getNativeTokenStreamingEnforcerAvailableAmount

Additional Context

  • Related Files:

    • packages/smart-accounts-kit/src/actions/caveatEnforcerClient.ts
    • packages/smart-accounts-kit/src/actions/getCaveatAvailableAmount.ts
    • packages/smart-accounts-kit/src/delegation.ts (contains decodeDelegations)
    • packages/smart-accounts-kit/src/caveatBuilder/timestampBuilder.ts
    • packages/delegation-core/src/caveats/timestamp.ts
  • References:

    • PermissionContext type: Delegation[] | Hex
    • TimestampEnforcer caveat uses 32 bytes: 16 bytes for afterThreshold, 16 bytes for beforeThreshold

Benefits

  1. Improved Developer Experience: Developers won't need to manually parse caveats to extract common information like expiry
  2. Less Error-Prone: Utilities handle edge cases and parsing logic consistently
  3. Better Ergonomics: Working with permission contexts becomes seamless without manual decoding
  4. Cleaner Code: Applications using the SDK will have cleaner, more readable code

Out of Scope

  • Checking for allowance availability (better to use existing getAvailableAmount methods)
  • Error codes like PERMISSION_EXPIRED, ALLOWANCE_EXCEEDED (can be added in a follow-up if needed)

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