mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-04 18:31:41 +00:00
@@ -0,0 +1,210 @@
|
||||
// Package keeper implements UCAN integration for the DEX module
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/sonr-io/sonr/x/dex/types"
|
||||
)
|
||||
|
||||
// ValidateUCANForDEXOperation validates UCAN token for a DEX operation
|
||||
func (k Keeper) ValidateUCANForDEXOperation(
|
||||
ctx sdk.Context,
|
||||
ucanToken string,
|
||||
did string,
|
||||
operation string,
|
||||
params map[string]any,
|
||||
) error {
|
||||
if ucanToken == "" {
|
||||
// No UCAN provided - check if operation requires it
|
||||
if k.requiresUCAN(operation) {
|
||||
return fmt.Errorf("UCAN token required for operation %s", operation)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate UCAN token structure and signature
|
||||
capability, err := k.parseUCANToken(ucanToken)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid UCAN token: %w", err)
|
||||
}
|
||||
|
||||
// Check expiration
|
||||
if ctx.BlockTime().After(capability.Expiration) {
|
||||
return fmt.Errorf("UCAN token expired")
|
||||
}
|
||||
|
||||
// Verify resource matches operation
|
||||
expectedResource := k.getResourceForOperation(operation)
|
||||
if !k.resourceMatches(capability.Resource, expectedResource) {
|
||||
return fmt.Errorf(
|
||||
"UCAN resource %s does not match operation %s",
|
||||
capability.Resource,
|
||||
operation,
|
||||
)
|
||||
}
|
||||
|
||||
// Verify ability
|
||||
if !k.hasAbility(capability.Ability, operation) {
|
||||
return fmt.Errorf(
|
||||
"UCAN ability %s insufficient for operation %s",
|
||||
capability.Ability,
|
||||
operation,
|
||||
)
|
||||
}
|
||||
|
||||
// Validate constraints
|
||||
if err := k.validateConstraints(capability.Constraints, params); err != nil {
|
||||
return fmt.Errorf("UCAN constraints not satisfied: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// requiresUCAN checks if an operation requires UCAN authorization
|
||||
func (k Keeper) requiresUCAN(operation string) bool {
|
||||
// Critical operations that always require UCAN
|
||||
criticalOps := []string{
|
||||
"large_swap", // Swaps above threshold
|
||||
"remove_liquidity", // Removing liquidity
|
||||
"cancel_all_orders", // Canceling all orders
|
||||
}
|
||||
|
||||
return slices.Contains(criticalOps, operation)
|
||||
}
|
||||
|
||||
// parseUCANToken parses and validates a UCAN token
|
||||
func (k Keeper) parseUCANToken(token string) (*types.UCANCapability, error) {
|
||||
// This is a simplified implementation
|
||||
// Real implementation would validate JWT signature and parse claims
|
||||
|
||||
// For now, parse as JSON for simplicity
|
||||
var capability types.UCANCapability
|
||||
if err := json.Unmarshal([]byte(token), &capability); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse UCAN token: %w", err)
|
||||
}
|
||||
|
||||
return &capability, nil
|
||||
}
|
||||
|
||||
// getResourceForOperation maps operations to UCAN resources
|
||||
func (k Keeper) getResourceForOperation(operation string) string {
|
||||
resourceMap := map[string]string{
|
||||
"swap": "dex:swap",
|
||||
"execute_swap": "dex:swap",
|
||||
"provide_liquidity": "dex:liquidity:provide",
|
||||
"remove_liquidity": "dex:liquidity:remove",
|
||||
"create_order": "dex:order:create",
|
||||
"cancel_order": "dex:order:cancel",
|
||||
"register_account": "dex:account:register",
|
||||
}
|
||||
|
||||
if resource, ok := resourceMap[operation]; ok {
|
||||
return resource
|
||||
}
|
||||
|
||||
return fmt.Sprintf("dex:%s", operation)
|
||||
}
|
||||
|
||||
// resourceMatches checks if UCAN resource matches required resource
|
||||
func (k Keeper) resourceMatches(ucanResource, requiredResource string) bool {
|
||||
// Exact match
|
||||
if ucanResource == requiredResource {
|
||||
return true
|
||||
}
|
||||
|
||||
// Wildcard match (e.g., "dex:*" matches any DEX operation)
|
||||
if strings.HasSuffix(ucanResource, ":*") {
|
||||
prefix := strings.TrimSuffix(ucanResource, "*")
|
||||
return strings.HasPrefix(requiredResource, prefix)
|
||||
}
|
||||
|
||||
// Hierarchical match (e.g., "dex:swap" matches "dex:swap:osmosis")
|
||||
return strings.HasPrefix(requiredResource, ucanResource+":")
|
||||
}
|
||||
|
||||
// hasAbility checks if UCAN ability is sufficient for operation
|
||||
func (k Keeper) hasAbility(ucanAbility, operation string) bool {
|
||||
// Map operations to required abilities
|
||||
requiredAbilities := map[string][]string{
|
||||
"swap": {"execute", "trade"},
|
||||
"provide_liquidity": {"execute", "provide"},
|
||||
"remove_liquidity": {"execute", "remove"},
|
||||
"create_order": {"execute", "create"},
|
||||
"cancel_order": {"execute", "cancel"},
|
||||
"read": {"read", "view"},
|
||||
}
|
||||
|
||||
required, ok := requiredAbilities[operation]
|
||||
if !ok {
|
||||
// Default to requiring "execute" ability
|
||||
required = []string{"execute"}
|
||||
}
|
||||
|
||||
// Check if UCAN ability matches any required ability
|
||||
for _, req := range required {
|
||||
if ucanAbility == req || ucanAbility == "*" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// validateConstraints validates UCAN constraints against operation parameters
|
||||
func (k Keeper) validateConstraints(constraints, params map[string]any) error {
|
||||
// Check amount constraints
|
||||
if maxAmount, ok := constraints["max_amount"]; ok {
|
||||
if amount, ok := params["amount"]; ok {
|
||||
if !k.isAmountWithinLimit(amount, maxAmount) {
|
||||
return fmt.Errorf("amount exceeds UCAN limit")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check pool constraints
|
||||
if allowedPools, ok := constraints["allowed_pools"]; ok {
|
||||
if poolID, ok := params["pool_id"]; ok {
|
||||
if !k.isPoolAllowed(poolID, allowedPools) {
|
||||
return fmt.Errorf("pool not allowed by UCAN")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check chain constraints
|
||||
if allowedChains, ok := constraints["allowed_chains"]; ok {
|
||||
if connectionID, ok := params["connection_id"]; ok {
|
||||
if !k.isChainAllowed(connectionID, allowedChains) {
|
||||
return fmt.Errorf("chain not allowed by UCAN")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// isAmountWithinLimit checks if amount is within UCAN limit
|
||||
func (k Keeper) isAmountWithinLimit(amount, maxAmount any) bool {
|
||||
// Convert and compare amounts
|
||||
// Simplified implementation - real one would handle different types
|
||||
return true
|
||||
}
|
||||
|
||||
// isPoolAllowed checks if pool is in allowed list
|
||||
func (k Keeper) isPoolAllowed(poolID, allowedPools any) bool {
|
||||
// Check if pool is in allowed list
|
||||
// Simplified implementation
|
||||
return true
|
||||
}
|
||||
|
||||
// isChainAllowed checks if chain is in allowed list
|
||||
func (k Keeper) isChainAllowed(connectionID, allowedChains any) bool {
|
||||
// Check if chain connection is allowed
|
||||
// Simplified implementation
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user