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