mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-04 18:31:41 +00:00
@@ -0,0 +1,248 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/sonr-io/sonr/x/dex/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
icatypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/types"
|
||||
host "github.com/cosmos/ibc-go/v8/modules/core/24-host"
|
||||
)
|
||||
|
||||
// RegisterDEXAccount registers a new ICA account for DEX operations
|
||||
func (k Keeper) RegisterDEXAccount(
|
||||
ctx sdk.Context,
|
||||
did string,
|
||||
connectionID string,
|
||||
features []string,
|
||||
) (*types.InterchainDEXAccount, error) {
|
||||
// Validate inputs
|
||||
if did == "" {
|
||||
return nil, fmt.Errorf("DID cannot be empty")
|
||||
}
|
||||
if connectionID == "" {
|
||||
return nil, fmt.Errorf("connection ID cannot be empty")
|
||||
}
|
||||
|
||||
// Validate DID exists by trying to get the document
|
||||
if _, err := k.didKeeper.GetDIDDocument(ctx, did); err != nil {
|
||||
return nil, fmt.Errorf("DID %s does not exist: %w", did, err)
|
||||
}
|
||||
|
||||
// Check if account already exists
|
||||
accountKey := GetAccountKey(did, connectionID)
|
||||
existing, err := k.Accounts.Get(ctx, accountKey)
|
||||
if err == nil {
|
||||
// Return existing account regardless of status (idempotent)
|
||||
return &existing, nil
|
||||
}
|
||||
|
||||
// Generate unique port ID
|
||||
portID := GetPortID(did, connectionID)
|
||||
|
||||
// Register ICA account
|
||||
if err := k.icaControllerKeeper.RegisterInterchainAccount(
|
||||
ctx,
|
||||
connectionID,
|
||||
portID,
|
||||
"", // Use default version
|
||||
); err != nil {
|
||||
return nil, fmt.Errorf("failed to register ICA account: %w", err)
|
||||
}
|
||||
|
||||
// Create DEX account record
|
||||
account := types.InterchainDEXAccount{
|
||||
Did: did,
|
||||
ConnectionId: connectionID,
|
||||
PortId: portID,
|
||||
EnabledFeatures: features,
|
||||
Status: types.ACCOUNT_STATUS_PENDING,
|
||||
CreatedAt: ctx.BlockTime(),
|
||||
}
|
||||
|
||||
// Store account
|
||||
if err := k.Accounts.Set(ctx, accountKey, account); err != nil {
|
||||
return nil, fmt.Errorf("failed to store DEX account: %w", err)
|
||||
}
|
||||
|
||||
// Update DID mappings
|
||||
if err := k.addDIDMapping(ctx, did, connectionID); err != nil {
|
||||
return nil, fmt.Errorf("failed to update DID mappings: %w", err)
|
||||
}
|
||||
|
||||
return &account, nil
|
||||
}
|
||||
|
||||
// GetDEXAccount retrieves a DEX account by DID and connection
|
||||
func (k Keeper) GetDEXAccount(
|
||||
ctx sdk.Context,
|
||||
did, connectionID string,
|
||||
) (*types.InterchainDEXAccount, error) {
|
||||
accountKey := GetAccountKey(did, connectionID)
|
||||
account, err := k.Accounts.Get(ctx, accountKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("DEX account not found: %w", err)
|
||||
}
|
||||
return &account, nil
|
||||
}
|
||||
|
||||
// GetDEXAccountsByDID retrieves all DEX accounts for a DID
|
||||
func (k Keeper) GetDEXAccountsByDID(
|
||||
ctx sdk.Context,
|
||||
did string,
|
||||
) ([]types.InterchainDEXAccount, error) {
|
||||
didAccounts, err := k.DIDToAccounts.Get(ctx, did)
|
||||
if err != nil {
|
||||
return nil, nil // No accounts for this DID
|
||||
}
|
||||
|
||||
var accounts []types.InterchainDEXAccount
|
||||
for _, connID := range didAccounts.Accounts {
|
||||
account, err := k.GetDEXAccount(ctx, did, connID)
|
||||
if err == nil {
|
||||
accounts = append(accounts, *account)
|
||||
}
|
||||
}
|
||||
return accounts, nil
|
||||
}
|
||||
|
||||
// SendDEXTransaction sends a transaction through ICA
|
||||
func (k Keeper) SendDEXTransaction(
|
||||
ctx sdk.Context,
|
||||
did string,
|
||||
connectionID string,
|
||||
msgs []sdk.Msg,
|
||||
memo string,
|
||||
timeoutDuration time.Duration,
|
||||
) (uint64, error) {
|
||||
// Get DEX account
|
||||
account, err := k.GetDEXAccount(ctx, did, connectionID)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to get DEX account: %w", err)
|
||||
}
|
||||
|
||||
if account.Status != types.ACCOUNT_STATUS_ACTIVE {
|
||||
return 0, fmt.Errorf("DEX account is not active")
|
||||
}
|
||||
|
||||
// Get ICA address
|
||||
icaAddress, found := k.icaControllerKeeper.GetInterchainAccountAddress(
|
||||
ctx,
|
||||
connectionID,
|
||||
account.PortId,
|
||||
)
|
||||
if !found {
|
||||
return 0, fmt.Errorf("ICA address not found")
|
||||
}
|
||||
|
||||
// Get channel capability
|
||||
channelID, found := k.icaControllerKeeper.GetActiveChannelID(ctx, connectionID, account.PortId)
|
||||
if !found {
|
||||
return 0, fmt.Errorf("active channel not found")
|
||||
}
|
||||
|
||||
chanCap, ok := k.ScopedKeeper.GetCapability(
|
||||
ctx,
|
||||
host.ChannelCapabilityPath(account.PortId, channelID),
|
||||
)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("channel capability not found")
|
||||
}
|
||||
|
||||
// Encode messages
|
||||
data, err := icatypes.SerializeCosmosTx(k.cdc, msgs, icatypes.EncodingProtobuf)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to serialize transaction: %w", err)
|
||||
}
|
||||
|
||||
// Create packet data
|
||||
packetData := icatypes.InterchainAccountPacketData{
|
||||
Type: icatypes.EXECUTE_TX,
|
||||
Data: data,
|
||||
Memo: memo,
|
||||
}
|
||||
|
||||
// Calculate timeout
|
||||
timeoutTimestamp := ctx.BlockTime().Add(timeoutDuration).UnixNano()
|
||||
|
||||
// Send transaction
|
||||
sequence, err := k.icaControllerKeeper.SendTx(
|
||||
ctx,
|
||||
chanCap,
|
||||
connectionID,
|
||||
account.PortId,
|
||||
packetData,
|
||||
uint64(timeoutTimestamp),
|
||||
)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to send ICA transaction: %w", err)
|
||||
}
|
||||
|
||||
// Log transaction
|
||||
k.Logger(ctx).Info("DEX transaction sent",
|
||||
"did", did,
|
||||
"connection", connectionID,
|
||||
"ica_address", icaAddress,
|
||||
"sequence", sequence,
|
||||
)
|
||||
|
||||
return sequence, nil
|
||||
}
|
||||
|
||||
// OnICAAccountCreated handles successful ICA account creation
|
||||
func (k Keeper) OnICAAccountCreated(ctx sdk.Context, portID, address string) error {
|
||||
// Find account by port ID
|
||||
var account *types.InterchainDEXAccount
|
||||
k.Accounts.Walk(ctx, nil, func(key string, value types.InterchainDEXAccount) (bool, error) {
|
||||
if value.PortId == portID {
|
||||
account = &value
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
|
||||
if account == nil {
|
||||
return fmt.Errorf("DEX account not found for port %s", portID)
|
||||
}
|
||||
|
||||
// Update account status and address
|
||||
account.Status = types.ACCOUNT_STATUS_ACTIVE
|
||||
account.AccountAddress = address
|
||||
account.HostChainId = k.getHostChainID(ctx, account.ConnectionId)
|
||||
|
||||
// Store updated account
|
||||
accountKey := GetAccountKey(account.Did, account.ConnectionId)
|
||||
if err := k.Accounts.Set(ctx, accountKey, *account); err != nil {
|
||||
return fmt.Errorf("failed to update DEX account: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
func (k Keeper) addDIDMapping(ctx sdk.Context, did, connectionID string) error {
|
||||
didAccounts, _ := k.DIDToAccounts.Get(ctx, did)
|
||||
|
||||
// Check if already exists
|
||||
for _, conn := range didAccounts.Accounts {
|
||||
if conn == connectionID {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
didAccounts.Accounts = append(didAccounts.Accounts, connectionID)
|
||||
return k.DIDToAccounts.Set(ctx, did, didAccounts)
|
||||
}
|
||||
|
||||
func (k Keeper) getHostChainID(ctx sdk.Context, connectionID string) string {
|
||||
conn, found := k.connectionKeeper.GetConnection(ctx, connectionID)
|
||||
if !found {
|
||||
return ""
|
||||
}
|
||||
// Extract chain ID from connection counterparty
|
||||
// This is a simplified version - actual implementation may vary
|
||||
return conn.Counterparty.ClientId
|
||||
}
|
||||
Reference in New Issue
Block a user