mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-02 17:31:39 +00:00
211 lines
5.8 KiB
Go
211 lines
5.8 KiB
Go
// 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
|
|
}
|