diff --git a/src/Managing.Application/Bots/Grains/AgentGrain.cs b/src/Managing.Application/Bots/Grains/AgentGrain.cs index 5b1b9c71..038e8657 100644 --- a/src/Managing.Application/Bots/Grains/AgentGrain.cs +++ b/src/Managing.Application/Bots/Grains/AgentGrain.cs @@ -58,7 +58,7 @@ public class AgentGrain : Grain, IAgentGrain { _state.State.AgentName = agentName; await _state.WriteStateAsync(); - + // Create an empty AgentSummary for the new agent var emptySummary = new AgentSummary { @@ -75,7 +75,7 @@ public class AgentGrain : Grain, IAgentGrain TotalVolume = 0, TotalBalance = 0 }; - + await _agentService.SaveOrUpdateAgentSummary(emptySummary); _logger.LogInformation("Agent {UserId} initialized with name {AgentName} and empty summary", userId, agentName); } @@ -93,7 +93,7 @@ public class AgentGrain : Grain, IAgentGrain { _logger.LogInformation("Received agent summary update event for user {UserId}, event type: {EventType}", this.GetPrimaryKeyLong(), updateEvent.EventType); - + // Only update summary if the event is for this agent's bots if (_state.State.BotIds.Contains(updateEvent.BotId)) { @@ -217,7 +217,7 @@ public class AgentGrain : Grain, IAgentGrain { await _state.WriteStateAsync(); _logger.LogInformation("Bot {BotId} registered to Agent {UserId}", botId, this.GetPrimaryKeyLong()); - + // Update summary after registering bot await UpdateSummary(); } @@ -229,7 +229,7 @@ public class AgentGrain : Grain, IAgentGrain { await _state.WriteStateAsync(); _logger.LogInformation("Bot {BotId} unregistered from Agent {UserId}", botId, this.GetPrimaryKeyLong()); - + // Update summary after unregistering bot await UpdateSummary(); } @@ -242,7 +242,7 @@ public class AgentGrain : Grain, IAgentGrain // Check if a swap is already in progress if (_state.State.IsSwapInProgress) { - _logger.LogInformation("Swap already in progress for agent {UserId}, bot {RequestingBotId} will wait", + _logger.LogInformation("Swap already in progress for agent {UserId}, bot {RequestingBotId} will wait", this.GetPrimaryKeyLong(), requestingBotId); return new BalanceCheckResult { @@ -254,10 +254,11 @@ public class AgentGrain : Grain, IAgentGrain } // Check cooldown period (5 minutes between swaps) - if (_state.State.LastSwapTime.HasValue && + if (_state.State.LastSwapTime.HasValue && DateTime.UtcNow - _state.State.LastSwapTime.Value < TimeSpan.FromMinutes(5)) { - _logger.LogInformation("Swap cooldown period active for agent {UserId}, bot {RequestingBotId} will wait", + _logger.LogInformation( + "Swap cooldown period active for agent {UserId}, bot {RequestingBotId} will wait", this.GetPrimaryKeyLong(), requestingBotId); return new BalanceCheckResult { @@ -272,7 +273,7 @@ public class AgentGrain : Grain, IAgentGrain var balanceData = await GetOrRefreshBalanceDataAsync(accountName); if (balanceData == null) { - _logger.LogError("Failed to get balance data for account {AccountName}, user {UserId}", + _logger.LogError("Failed to get balance data for account {AccountName}, user {UserId}", accountName, this.GetPrimaryKeyLong()); return new BalanceCheckResult { @@ -283,20 +284,23 @@ public class AgentGrain : Grain, IAgentGrain }; } - _logger.LogInformation("Agent {UserId} balance check - ETH: {EthValue:F2} USD, USDC: {UsdcValue:F2} USD (cached: {IsCached})", - this.GetPrimaryKeyLong(), balanceData.EthValueInUsd, balanceData.UsdcValue, + _logger.LogInformation( + "Agent {UserId} balance check - ETH: {EthValue:F2} USD, USDC: {UsdcValue:F2} USD (cached: {IsCached})", + this.GetPrimaryKeyLong(), balanceData.EthValueInUsd, balanceData.UsdcValue, _state.State.CachedBalanceData?.IsValid == true); // Check USDC minimum balance first (this will stop the bot if insufficient) if (balanceData.UsdcValue < Constants.GMX.Config.MinimumPositionAmount) { - _logger.LogWarning("USDC balance is below minimum required amount - ETH: {EthValue:F2} USD, USDC: {UsdcValue:F2} USD (minimum: {Minimum})", + _logger.LogWarning( + "USDC balance is below minimum required amount - ETH: {EthValue:F2} USD, USDC: {UsdcValue:F2} USD (minimum: {Minimum})", balanceData.EthValueInUsd, balanceData.UsdcValue, Constants.GMX.Config.MinimumPositionAmount); return new BalanceCheckResult { IsSuccessful = false, FailureReason = BalanceCheckFailureReason.InsufficientUsdcBelowMinimum, - Message = $"USDC balance below minimum required amount ({Constants.GMX.Config.MinimumPositionAmount} USD)", + Message = + $"USDC balance below minimum required amount ({Constants.GMX.Config.MinimumPositionAmount} USD)", ShouldStopBot = true }; } @@ -314,9 +318,11 @@ public class AgentGrain : Grain, IAgentGrain } // Check if we have enough USDC for swap (need at least 5 USD for swap) - if (balanceData.UsdcValue < (Constants.GMX.Config.MinimumPositionAmount + Constants.GMX.Config.AutoSwapAmount) ) + if (balanceData.UsdcValue < + (Constants.GMX.Config.MinimumPositionAmount + (decimal)Constants.GMX.Config.AutoSwapAmount)) { - _logger.LogWarning("Insufficient USDC balance for swap - ETH: {EthValue:F2} USD, USDC: {UsdcValue:F2} USD (need {AutoSwapAmount} USD for swap)", + _logger.LogWarning( + "Insufficient USDC balance for swap - ETH: {EthValue:F2} USD, USDC: {UsdcValue:F2} USD (need {AutoSwapAmount} USD for swap)", balanceData.EthValueInUsd, balanceData.UsdcValue, Constants.GMX.Config.AutoSwapAmount); return new BalanceCheckResult { @@ -333,7 +339,8 @@ public class AgentGrain : Grain, IAgentGrain try { - _logger.LogInformation("Initiating USDC to ETH swap for agent {UserId} - swapping 5 USDC", this.GetPrimaryKeyLong()); + _logger.LogInformation("Initiating USDC to ETH swap for agent {UserId} - swapping 5 USDC", + this.GetPrimaryKeyLong()); // Get user for the swap var userId = (int)this.GetPrimaryKeyLong(); @@ -351,14 +358,15 @@ public class AgentGrain : Grain, IAgentGrain } // Perform the swap - var swapInfo = await _accountService.SwapGmxTokensAsync(user, accountName, - Ticker.USDC, Ticker.ETH, 5); + var swapInfo = await _accountService.SwapGmxTokensAsync(user, accountName, + Ticker.USDC, Ticker.ETH, Constants.GMX.Config.AutoSwapAmount); if (swapInfo.Success) { - _logger.LogInformation("Successfully swapped 5 USDC to ETH for agent {UserId}, transaction hash: {Hash}", + _logger.LogInformation( + "Successfully swapped 5 USDC to ETH for agent {UserId}, transaction hash: {Hash}", userId, swapInfo.Hash); - + // Update last swap time and invalidate cache _state.State.LastSwapTime = DateTime.UtcNow; _state.State.CachedBalanceData = null; // Invalidate cache after successful swap @@ -372,7 +380,7 @@ public class AgentGrain : Grain, IAgentGrain } else { - _logger.LogError("Failed to swap USDC to ETH for agent {UserId}: {Error}", + _logger.LogError("Failed to swap USDC to ETH for agent {UserId}: {Error}", userId, swapInfo.Error ?? swapInfo.Message); return new BalanceCheckResult { @@ -392,13 +400,13 @@ public class AgentGrain : Grain, IAgentGrain } catch (Exception ex) { - _logger.LogError(ex, "Error checking/ensuring ETH balance for agent {UserId}, bot {RequestingBotId}", + _logger.LogError(ex, "Error checking/ensuring ETH balance for agent {UserId}, bot {RequestingBotId}", this.GetPrimaryKeyLong(), requestingBotId); - + // Clear swap in progress flag on error _state.State.IsSwapInProgress = false; await _state.WriteStateAsync(); - + return new BalanceCheckResult { IsSuccessful = false, @@ -417,7 +425,7 @@ public class AgentGrain : Grain, IAgentGrain try { // Check if we have valid cached data for the same account - if (_state.State.CachedBalanceData?.IsValid == true && + if (_state.State.CachedBalanceData?.IsValid == true && _state.State.CachedBalanceData.AccountName == accountName) { _logger.LogDebug("Using cached balance data for account {AccountName}", accountName); @@ -426,7 +434,7 @@ public class AgentGrain : Grain, IAgentGrain // Fetch fresh balance data _logger.LogInformation("Fetching fresh balance data for account {AccountName}", accountName); - + var userId = (int)this.GetPrimaryKeyLong(); var user = await _userService.GetUserByIdAsync(userId); if (user == null) @@ -467,7 +475,7 @@ public class AgentGrain : Grain, IAgentGrain } catch (Exception ex) { - _logger.LogError(ex, "Error fetching balance data for account {AccountName}, user {UserId}", + _logger.LogError(ex, "Error fetching balance data for account {AccountName}, user {UserId}", accountName, this.GetPrimaryKeyLong()); return null; } diff --git a/src/Managing.Common/Constants.cs b/src/Managing.Common/Constants.cs index c98f8d62..889074e6 100644 --- a/src/Managing.Common/Constants.cs +++ b/src/Managing.Common/Constants.cs @@ -82,25 +82,25 @@ namespace Managing.Common Ticker.WIF, }; - public static readonly Ticker[] SupportedTickers = - { - Ticker.BTC, - Ticker.ETH, - Ticker.BNB, - Ticker.DOGE, - Ticker.ADA, - Ticker.SOL, - Ticker.XRP, - Ticker.LINK, - Ticker.RENDER, - Ticker.SUI, - Ticker.GMX, - Ticker.ARB, - Ticker.PEPE, - Ticker.PENDLE, - Ticker.AAVE, - Ticker.HYPE - }; + public static readonly Ticker[] SupportedTickers = + { + Ticker.BTC, + Ticker.ETH, + Ticker.BNB, + Ticker.DOGE, + Ticker.ADA, + Ticker.SOL, + Ticker.XRP, + Ticker.LINK, + Ticker.RENDER, + Ticker.SUI, + Ticker.GMX, + Ticker.ARB, + Ticker.PEPE, + Ticker.PENDLE, + Ticker.AAVE, + Ticker.HYPE + }; public static class Decimals { @@ -109,7 +109,7 @@ namespace Managing.Common public const decimal MinimumPositionAmount = 5m; public const decimal MinimumEthBalance = 2m; - public const decimal AutoSwapAmount = 3m; + public const double AutoSwapAmount = 3; } public class TokenAddress diff --git a/src/Managing.Web3Proxy/src/server.ts b/src/Managing.Web3Proxy/src/server.ts index 69d2513e..51e5cc50 100644 --- a/src/Managing.Web3Proxy/src/server.ts +++ b/src/Managing.Web3Proxy/src/server.ts @@ -180,14 +180,20 @@ async function init () { } } - // Store the request ID for later use - request.idempotencyKey = requestId as string + // Store the request ID for later use using a symbol to avoid TypeScript property errors + const IDEMPOTENCY_KEY_SYMBOL = Symbol.for('idempotency-key') + // Attach the symbol to the request object + ;(request as any)[IDEMPOTENCY_KEY_SYMBOL] = requestId as string + }) // Add post-handler hook to store successful responses app.addHook('onSend', async (request, reply, payload) => { - if (request.idempotencyKey && request.method === 'POST') { - const requestId = request.idempotencyKey + // Retrieve the idempotency key from the symbol + const IDEMPOTENCY_KEY_SYMBOL = Symbol.for('idempotency-key') + const idempotencyKey = (request as any)[IDEMPOTENCY_KEY_SYMBOL] + if (idempotencyKey && request.method === 'POST') { + const requestId = idempotencyKey // Only store successful responses (2xx status codes) if (reply.statusCode >= 200 && reply.statusCode < 300) { diff --git a/src/Managing.Web3Proxy/src/types/fastify.d.ts b/src/Managing.Web3Proxy/src/types/fastify.d.ts deleted file mode 100644 index 5da9b42a..00000000 --- a/src/Managing.Web3Proxy/src/types/fastify.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare module 'fastify' { - interface FastifyRequest { - idempotencyKey?: string - } -} diff --git a/src/Managing.Web3Proxy/test/plugins/swap-tokens.test.ts b/src/Managing.Web3Proxy/test/plugins/swap-tokens.test.ts index ec545b71..65a2cc2b 100644 --- a/src/Managing.Web3Proxy/test/plugins/swap-tokens.test.ts +++ b/src/Managing.Web3Proxy/test/plugins/swap-tokens.test.ts @@ -7,15 +7,15 @@ describe('swap tokens implementation', () => { it('should swap SOL to USDC successfully', async () => { try { - const testAccount = '0xbBA4eaA534cbD0EcAed5E2fD6036Aec2E7eE309f' + const testAccount = '0x932167388dD9aad41149b3cA23eBD489E2E2DD78' const sdk = await getClientForAddress(testAccount) console.log('Account', sdk.account) const result = await swapGmxTokensImpl( sdk, - Ticker.PENDLE, + Ticker.ETH, Ticker.USDC, - 13.339559522 + 0.0042 ) assert.strictEqual(typeof result, 'string')