Files
managing-apps/src/Managing.Application/Accounts/AccountService.cs
2025-07-06 14:39:01 +07:00

313 lines
10 KiB
C#

using Managing.Application.Abstractions.Repositories;
using Managing.Application.Abstractions.Services;
using Managing.Domain.Accounts;
using Managing.Domain.Users;
using Microsoft.Extensions.Logging;
using static Managing.Common.Enums;
namespace Managing.Application.Accounts;
public class AccountService : IAccountService
{
private readonly IAccountRepository _accountRepository;
private readonly IExchangeService _exchangeService;
private readonly IEvmManager _evmManager;
private readonly ICacheService _cacheService;
private readonly IWeb3ProxyService _web3ProxyService;
private readonly IUserRepository _userRepository;
private readonly ILogger<AccountService> _logger;
public AccountService(
IAccountRepository accountRepository,
ILogger<AccountService> logger,
IExchangeService exchangeService,
IEvmManager evmManager,
ICacheService cacheService,
IWeb3ProxyService web3ProxyService,
IUserRepository userRepository)
{
_accountRepository = accountRepository;
_logger = logger;
_exchangeService = exchangeService;
_evmManager = evmManager;
_cacheService = cacheService;
_web3ProxyService = web3ProxyService;
_userRepository = userRepository;
}
public async Task<Account> CreateAccount(User user, Account request)
{
var account = await _accountRepository.GetAccountByNameAsync(request.Name);
if (account != null)
{
throw new Exception($"Account {request.Name} alreary exist");
}
else
{
request.User = user;
if (request.Exchange == TradingExchanges.Evm
&& request.Type == AccountType.Trader)
{
var keys = _evmManager.GenerateAddress();
request.Key = keys.Key;
request.Secret = keys.Secret;
}
else if (request.Exchange == TradingExchanges.Evm
&& request.Type == AccountType.Privy)
{
if (string.IsNullOrEmpty(request.Key))
{
// No key provided, create new privy embedded wallet.
// TODO : Fix it to create privy wallet
var privyClient = await _evmManager.CreatePrivyWallet();
request.Key = privyClient.Address;
request.Secret = privyClient.Id;
}
else
{
request.Key = request.Key; // Address
request.Secret = request.Secret; // Privy wallet id
}
}
else
{
request.Key = request.Key;
request.Secret = request.Secret;
}
await _accountRepository.InsertAccountAsync(request);
}
return request;
}
public bool DeleteAccount(User user, string name)
{
try
{
_accountRepository.DeleteAccountByName(name);
return true;
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
return false;
}
}
public async Task<Account> GetAccount(string name, bool hideSecrets, bool getBalance)
{
var account = await _accountRepository.GetAccountByNameAsync(name);
ManageProperties(hideSecrets, getBalance, account);
account.User = await _userRepository.GetUserByNameAsync(account.User.Name);
return account;
}
public async Task<Account> GetAccountByKey(string key, bool hideSecrets, bool getBalance)
{
var account = await _accountRepository.GetAccountByKeyAsync(key);
ManageProperties(hideSecrets, getBalance, account);
return account;
}
public async Task<Account> GetAccountByUser(User user, string name, bool hideSecrets, bool getBalance)
{
var account = await _accountRepository.GetAccountByNameAsync(name);
ManageProperties(hideSecrets, getBalance, account);
return account;
}
public IEnumerable<Account> GetAccounts(bool hideSecrets, bool getBalance)
{
var result = _accountRepository.GetAccounts();
var accounts = new List<Account>();
foreach (var account in result)
{
ManageProperties(hideSecrets, getBalance, account);
accounts.Add(account);
}
return accounts;
}
public IEnumerable<Account> GetAccountsByUser(User user, bool hideSecrets = true)
{
var cacheKey = $"user-account-{user.Name}";
return _cacheService.GetOrSave(cacheKey, () => { return GetAccounts(user, hideSecrets, false); },
TimeSpan.FromMinutes(5));
}
private IEnumerable<Account> GetAccounts(User user, bool hideSecrets, bool getBalance)
{
var result = _accountRepository.GetAccounts();
var accounts = new List<Account>();
foreach (var account in result.Where(a => a.User.Name == user.Name))
{
ManageProperties(hideSecrets, getBalance, account);
accounts.Add(account);
}
return accounts;
}
public IEnumerable<Account> GetAccountsBalancesByUser(User user, bool hideSecrets)
{
var cacheKey = $"user-account-balance-{user.Name}";
var accounts = _cacheService.GetOrSave(cacheKey, () => { return GetAccounts(user, true, true); },
TimeSpan.FromHours(3));
return accounts;
}
public async Task<GmxClaimableSummary> GetGmxClaimableSummaryAsync(User user, string accountName)
{
// Get the account for the user
var account = await GetAccountByUser(user, accountName, true, false);
if (account == null)
{
throw new ArgumentException($"Account '{accountName}' not found for user '{user.Name}'");
}
// Ensure the account has a valid address/key
if (string.IsNullOrEmpty(account.Key))
{
throw new ArgumentException($"Account '{accountName}' does not have a valid address");
}
try
{
// Call the Web3ProxyService to get GMX claimable summary
var infrastructureResponse = await _web3ProxyService.GetGmxClaimableSummaryAsync(account.Key);
// Map from infrastructure model to domain model
return infrastructureResponse;
}
catch (Exception ex) when (!(ex is ArgumentException || ex is InvalidOperationException))
{
_logger.LogError(ex, "Error getting GMX claimable summary for account {AccountName} and user {UserName}",
accountName, user.Name);
throw new InvalidOperationException($"Failed to get GMX claimable summary: {ex.Message}", ex);
}
}
public async Task<SwapInfos> SwapGmxTokensAsync(User user, string accountName, Ticker fromTicker, Ticker toTicker, double amount, string orderType = "market", double? triggerRatio = null, double allowedSlippage = 0.5)
{
// Get the account for the user
var account = await GetAccountByUser(user, accountName, true, false);
if (account == null)
{
throw new ArgumentException($"Account '{accountName}' not found for user '{user.Name}'");
}
// Ensure the account has a valid address/key
if (string.IsNullOrEmpty(account.Key))
{
throw new ArgumentException($"Account '{accountName}' does not have a valid address");
}
try
{
// Call the Web3ProxyService to swap GMX tokens
var swapInfos = await _web3ProxyService.SwapGmxTokensAsync(
account.Key,
fromTicker,
toTicker,
amount,
orderType,
triggerRatio,
allowedSlippage
);
return swapInfos;
}
catch (Exception ex) when (!(ex is ArgumentException || ex is InvalidOperationException))
{
_logger.LogError(ex, "Error swapping GMX tokens for account {AccountName} and user {UserName}",
accountName, user.Name);
throw new InvalidOperationException($"Failed to swap GMX tokens: {ex.Message}", ex);
}
}
public async Task<SwapInfos> SendTokenAsync(User user, string accountName, string recipientAddress, Ticker ticker, decimal amount, int? chainId = null)
{
// Get the account for the user
var account = await GetAccountByUser(user, accountName, true, false);
if (account == null)
{
throw new ArgumentException($"Account '{accountName}' not found for user '{user.Name}'");
}
// Ensure the account has a valid address/key
if (string.IsNullOrEmpty(account.Key))
{
throw new ArgumentException($"Account '{accountName}' does not have a valid address");
}
// Validate recipient address
if (string.IsNullOrEmpty(recipientAddress))
{
throw new ArgumentException("Recipient address is required");
}
// Validate amount
if (amount <= 0)
{
throw new ArgumentException("Amount must be greater than zero");
}
try
{
// Call the Web3ProxyService to send tokens
var swapInfos = await _web3ProxyService.SendTokenAsync(
account.Key,
recipientAddress,
ticker,
amount,
chainId
);
return swapInfos;
}
catch (Exception ex) when (!(ex is ArgumentException || ex is InvalidOperationException))
{
_logger.LogError(ex, "Error sending tokens for account {AccountName} and user {UserName}",
accountName, user.Name);
throw new InvalidOperationException($"Failed to send tokens: {ex.Message}", ex);
}
}
private void ManageProperties(bool hideSecrets, bool getBalance, Account account)
{
if (account != null)
{
if (getBalance)
{
try
{
account.Balances = _exchangeService.GetBalances(account).Result;
}
catch (Exception ex)
{
_logger.LogError(ex, $"Cannot get account {account.Name} balance");
}
}
if (hideSecrets)
{
account.Secret = "";
}
}
}
}