Filter everything with users (#16)

* Filter everything with users

* Fix backtests and user management

* Add cursor rules

* Fix backtest and bots

* Update configs names

* Sign until unauth

* Setup delegate

* Setup delegate and sign

* refact

* Enhance Privy signature generation with improved cryptographic methods

* Add Fastify backend

* Add Fastify backend routes for privy

* fix privy signing

* fix privy client

* Fix tests

* add gmx core

* fix merging sdk

* Fix tests

* add gmx core

* add gmx core

* add privy to boilerplate

* clean

* fix

* add fastify

* Remove Managing.Fastify submodule

* Add Managing.Fastify as regular directory instead of submodule

* Update .gitignore to exclude Managing.Fastify dist and node_modules directories

* Add token approval functionality to Privy plugin

- Introduced a new endpoint `/approve-token` for approving ERC20 tokens.
- Added `approveToken` method to the Privy plugin for handling token approvals.
- Updated `signPrivyMessage` to differentiate between message signing and token approval requests.
- Enhanced the plugin with additional schemas for input validation.
- Included new utility functions for token data retrieval and message construction.
- Updated tests to verify the new functionality and ensure proper request decoration.

* Add PrivyApproveTokenResponse model for token approval response

- Created a new class `PrivyApproveTokenResponse` to encapsulate the response structure for token approval requests.
- The class includes properties for `Success` status and a transaction `Hash`.

* Refactor trading commands and enhance API routes

- Updated `OpenPositionCommandHandler` to use asynchronous methods for opening trades and canceling orders.
- Introduced new Fastify routes for opening positions and canceling orders with appropriate request validation.
- Modified `EvmManager` to handle both Privy and non-Privy wallet operations, utilizing the Fastify API for Privy wallets.
- Adjusted test configurations to reflect changes in account types and added helper methods for testing Web3 proxy services.

* Enhance GMX trading functionality and update dependencies

- Updated `dev:start` script in `package.json` to include the `-d` flag for Fastify.
- Upgraded `fastify-cli` dependency to version 7.3.0.
- Added `sourceMap` option to `tsconfig.json`.
- Refactored GMX plugin to improve position opening logic, including enhanced error handling and validation.
- Introduced a new method `getMarketInfoFromTicker` for better market data retrieval.
- Updated account type in `PrivateKeys.cs` to use `Privy`.
- Adjusted `EvmManager` to utilize the `direction` enum directly for trade direction handling.

* Refactor GMX plugin for improved trading logic and market data retrieval

- Enhanced the `openGmxPositionImpl` function to utilize the `TradeDirection` enum for trade direction handling.
- Introduced `getTokenDataFromTicker` and `getMarketByIndexToken` functions for better market and token data retrieval.
- Updated collateral calculation and logging for clarity.
- Adjusted `EvmManager` to ensure proper handling of price values in trade requests.

* Refactor GMX plugin and enhance testing for position opening

- Updated `test:single` script in `package.json` to include TypeScript compilation before running tests.
- Removed `this` context from `getClientForAddress` function and replaced logging with `console.error`.
- Improved collateral calculation in `openGmxPositionImpl` for better precision.
- Adjusted type casting for `direction` in the API route to utilize `TradeDirection` enum.
- Added a new test for opening a long position in GMX, ensuring functionality and correctness.

* Update sdk

* Update

* update fastify

* Refactor start script in package.json to simplify command execution

- Removed the build step from the start script, allowing for a more direct launch of the Fastify server.

* Update package.json for Web3Proxy

- Changed the name from "Web3Proxy" to "web3-proxy".
- Updated version from "0.0.0" to "1.0.0".
- Modified the description to "The official Managing Web3 Proxy".

* Update Dockerfile for Web3Proxy

- Upgraded Node.js base image from 18-alpine to 22.14.0-alpine.
- Added NODE_ENV environment variable set to production.

* Refactor Dockerfile and package.json for Web3Proxy

- Removed the build step from the Dockerfile to streamline the image creation process.
- Updated the start script in package.json to include the build step, ensuring the application is built before starting the server.

* Add fastify-tsconfig as a development dependency in Dockerfile-web3proxy

* Remove fastify-tsconfig extension from tsconfig.json for Web3Proxy

* Add PrivyInitAddressResponse model for handling initialization responses

- Introduced a new class `PrivyInitAddressResponse` to encapsulate the response structure for Privy initialization, including properties for success status, USDC hash, order vault hash, and error message.

* Update

* Update

* Remove fastify-tsconfig installation from Dockerfile-web3proxy

* Add build step to Dockerfile-web3proxy

- Included `npm run build` in the Dockerfile to ensure the application is built during the image creation process.

* Update

* approvals

* Open position from front embedded wallet

* Open position from front embedded wallet

* Open position from front embedded wallet

* Fix call contracts

* Fix limit price

* Close position

* Fix close position

* Fix close position

* add pinky

* Refactor position handling logic

* Update Dockerfile-pinky to copy package.json and source code from the correct directory

* Implement password protection modal and enhance UI with new styles; remove unused audio elements and update package dependencies.

* add cancel orders

* Update callContract function to explicitly cast account address as Address type

* Update callContract function to cast transaction parameters as any type for compatibility

* Cast transaction parameters as any type in approveTokenImpl for compatibility

* Cast wallet address and transaction parameters as Address type in approveTokenImpl for type safety

* Add .env configuration file for production setup including database and server settings

* Refactor home route to update welcome message and remove unused SDK configuration code

* add referral code

* fix referral

* Add sltp

* Fix typo

* Fix typo

* setup sltp on backtend

* get orders

* get positions with slp

* fixes

* fixes close position

* fixes

* Remove MongoDB project references from Dockerfiles for managing and worker APIs

* Comment out BotManagerWorker service registration and remove MongoDB project reference from Dockerfile

* fixes
This commit is contained in:
Oda
2025-04-20 22:18:27 +07:00
committed by GitHub
parent 0ae96a3278
commit 528c62a0a1
400 changed files with 94446 additions and 1635 deletions

View File

@@ -11,6 +11,8 @@ using Managing.Domain.Trades;
using Managing.Infrastructure.Evm.Abstractions;
using Managing.Infrastructure.Evm.Models;
using Managing.Infrastructure.Evm.Models.Gmx.v2;
using Managing.Infrastructure.Evm.Models.Privy;
using Managing.Infrastructure.Evm.Models.Proxy;
using Managing.Infrastructure.Evm.Referentials;
using Managing.Infrastructure.Evm.Services;
using Managing.Infrastructure.Evm.Services.Gmx;
@@ -26,6 +28,7 @@ using BalanceOfFunction = Nethereum.Contracts.Standards.ERC20.ContractDefinition
using BalanceOfOutputDTO = Nethereum.Contracts.Standards.ERC20.ContractDefinition.BalanceOfOutputDTO;
using Chain = Managing.Domain.Evm.Chain;
using TransferEventDTO = Nethereum.Contracts.Standards.ERC721.ContractDefinition.TransferEventDTO;
using Microsoft.Extensions.Logging;
namespace Managing.Infrastructure.Evm;
@@ -37,7 +40,7 @@ public class EvmManager : IEvmManager
private readonly IEnumerable<ISubgraphPrices> _subgraphs;
private Dictionary<string, Dictionary<string, decimal>> _geckoPrices;
private readonly GmxV2Service _gmxV2Service;
private readonly IPrivyService _privyService;
private readonly IWeb3ProxyService _web3ProxyService;
private readonly List<Ticker> _eligibleTickers = new List<Ticker>()
{
@@ -45,13 +48,14 @@ public class EvmManager : IEvmManager
Ticker.PEPE, Ticker.DOGE, Ticker.UNI
};
public EvmManager(IEnumerable<ISubgraphPrices> subgraphs, IPrivyService privyService)
public EvmManager(IEnumerable<ISubgraphPrices> subgraphs,
IWeb3ProxyService web3ProxyService)
{
var defaultChain = ChainService.GetEthereum();
_web3 = new Web3(defaultChain.RpcUrl);
_httpClient = new HttpClient();
_subgraphs = subgraphs;
_privyService = privyService;
_web3ProxyService = web3ProxyService;
_geckoPrices = _geckoPrices != null && _geckoPrices.Any()
? _geckoPrices
: new Dictionary<string, Dictionary<string, decimal>>();
@@ -426,19 +430,20 @@ public class EvmManager : IEvmManager
return lastPrices.Last();
}
public async Task<bool> InitAddress(string chainName, string publicAddress, string privateKey)
public async Task<bool> InitAddress(string publicAddress)
{
try
{
var chain = ChainService.GetChain(chainName);
var account = new Wallet(privateKey, _password).GetAccount(publicAddress);
var web3 = new Web3(account, chain.RpcUrl);
var tickers = await GetAvailableTicker();
await GmxService.InitAccountForTrading(web3, publicAddress, tickers);
return true;
var response = await _web3ProxyService.CallPrivyServiceAsync<PrivyInitAddressResponse>(
"/init-address",
new { address = publicAddress });
return response.Success;
}
catch (Exception ex)
{
// Log the error
Console.Error.WriteLine($"Error initializing address: {ex.Message}");
return false;
}
}
@@ -512,12 +517,34 @@ public class EvmManager : IEvmManager
public async Task<bool> CancelOrders(Account account, Ticker ticker)
{
var wallet = new Wallet(account.Secret, _password).GetAccount(account.Key);
var chain = ChainService.GetChain(Constants.Chains.Arbitrum);
var web3 = new Web3(wallet, chain.RpcUrl);
// return await GmxService.CancelOrders(web3, account.Key, ticker);
var service = new GmxV2Service();
return await service.CancelOrders(web3, account.Key, ticker);
if (account.IsPrivyWallet)
{
try
{
var response = await _web3ProxyService.CallGmxServiceAsync<dynamic>("/cancel-orders",
new
{
account = account.Key,
walletId = account.Secret,
ticker = ticker.ToString()
});
return response.success ?? false;
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error canceling orders via Fastify API: {ex.Message}");
return false;
}
}
else
{
var wallet = new Wallet(account.Secret, _password).GetAccount(account.Key);
var chain = ChainService.GetChain(Constants.Chains.Arbitrum);
var web3 = new Web3(wallet, chain.RpcUrl);
var service = new GmxV2Service();
return await service.CancelOrders(web3, account.Key, ticker);
}
}
public async Task<Trade> IncreasePosition(
@@ -526,17 +553,67 @@ public class EvmManager : IEvmManager
TradeDirection direction,
decimal price,
decimal quantity,
decimal? leverage)
decimal? leverage,
decimal? stopLossPrice = null,
decimal? takeProfitPrice = null)
{
var wallet = new Wallet(account.Secret, _password).GetAccount(account.Key);
var chain = ChainService.GetChain(Constants.Chains.Arbitrum);
var web3 = new Web3(wallet, chain.RpcUrl);
Trade trade = null;
try
{
trade = await _gmxV2Service.IncreasePosition(web3, account.Key, ticker, direction, price, quantity,
leverage);
// If this is a Privy wallet, call the GMX service through Fastify API
if (account.IsPrivyWallet)
{
try
{
var response = await _web3ProxyService.CallGmxServiceAsync<object>("/open-position",
new
{
account = account.Key,
walletId = account.Secret,
tradeType = price > 0 ? "limit" : "market",
ticker = ticker.ToString(),
direction = direction.ToString(),
price = price,
quantity,
leverage = leverage ?? 1.0m,
stopLossPrice = stopLossPrice,
takeProfitPrice = takeProfitPrice
});
// Create a trade object using the returned hash
var tradeType = price > 0 ? TradeType.Limit : TradeType.Market;
var tradeStatus = TradeStatus.Requested; // Use a valid enum value that exists in TradeStatus
trade = new Trade(
DateTime.UtcNow,
direction,
tradeStatus,
tradeType,
ticker,
quantity,
price,
leverage ?? 1.0m,
account.Key,
""
);
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
else
{
// Continue with the existing direct service call for non-Privy wallets
var wallet = new Wallet(account.Secret, _password).GetAccount(account.Key);
var chain = ChainService.GetChain(Constants.Chains.Arbitrum);
var web3 = new Web3(wallet, chain.RpcUrl);
trade = await _gmxV2Service.IncreasePosition(web3, account.Key, ticker, direction, price, quantity,
leverage);
}
}
catch (Exception ex)
{
@@ -554,11 +631,45 @@ public class EvmManager : IEvmManager
decimal quantity,
decimal? leverage)
{
Trade trade = null;
if (account.IsPrivyWallet)
{
try
{
var response = await _web3ProxyService.CallGmxServiceAsync<ClosePositionResponse>("/close-position",
new
{
account = account.Key,
ticker = ticker.ToString(),
direction = direction.ToString(),
});
trade = new Trade(
DateTime.UtcNow,
direction,
TradeStatus.Requested,
TradeType.Market,
ticker,
quantity,
price,
leverage ?? 1,
response.Hash,
""
);
return trade;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
var wallet = new Wallet(account.Secret, _password).GetAccount(account.Key);
var chain = ChainService.GetChain(Constants.Chains.Arbitrum);
var web3 = new Web3(wallet, chain.RpcUrl);
Trade trade = null;
try
{
trade = await _gmxV2Service.DecreasePosition(web3, account.Key, ticker, direction, price, quantity,
@@ -573,7 +684,9 @@ public class EvmManager : IEvmManager
}
public async Task<Trade> DecreaseOrder(Account account, TradeType tradeType, Ticker ticker,
TradeDirection direction, decimal price, decimal quantity, decimal? leverage)
TradeDirection direction, decimal price, decimal quantity, decimal? leverage,
decimal? stopLossPrice = null,
decimal? takeProfitPrice = null)
{
var wallet = new Wallet(account.Secret, _password).GetAccount(account.Key);
var chain = ChainService.GetChain(Constants.Chains.Arbitrum);
@@ -582,8 +695,11 @@ public class EvmManager : IEvmManager
Trade trade;
try
{
trade = await GmxService.DecreaseOrder(web3, tradeType, account.Key, ticker, direction, price, quantity,
leverage);
// TODO: This method in GmxV2Service might not exist or needs different handling for Privy wallets.
// Commenting out for now as IncreasePosition is the priority.
// trade = await _gmxV2Service.DecreaseOrder(web3, account.Key, tradeType, ticker, direction, price,
// quantity, leverage);
trade = null; // Placeholder return
}
catch (Exception ex)
{
@@ -595,6 +711,34 @@ public class EvmManager : IEvmManager
public async Task<Trade> GetTrade(Account account, string chainName, Ticker ticker)
{
if (account.IsPrivyWallet)
{
var result = await _web3ProxyService.GetGmxServiceAsync<GetGmxPositionsResponse>(
"/positions",
new { account = account.Key, ticker = ticker.ToString() });
var position = result.Positions.FirstOrDefault(p => p.Ticker == ticker.ToString());
if (position == null)
return null;
// TODO: Map the position object to a Trade object
var trade = new Trade(
position.Date,
MiscExtensions.ParseEnum<TradeDirection>(position.Direction),
MiscExtensions.ParseEnum<TradeStatus>(position.Status),
MiscExtensions.ParseEnum<TradeType>(position.TradeType),
MiscExtensions.ParseEnum<Ticker>(position.Ticker),
(decimal)position.Quantity,
(decimal)position.Price,
(decimal?)position.Leverage,
account.Key,
position.ExchangeOrderId
);
return trade;
}
return await GetTrade(account.Key, chainName, ticker);
}
@@ -608,12 +752,7 @@ public class EvmManager : IEvmManager
public async Task<List<FundingRate>> GetFundingRates()
{
var chain = ChainService.GetChain(Constants.Chains.Arbitrum);
var web3 = new Web3(chain.RpcUrl);
var service = new GmxV2Service();
var fundingRates = await service.GetFundingRates(web3);
return fundingRates;
return await _web3ProxyService.CallGmxServiceAsync<List<FundingRate>>("/gmx/funding-rates", new { });
}
public async Task<decimal> QuantityInPosition(string chainName, string publicAddress, Ticker ticker)
@@ -635,17 +774,39 @@ public class EvmManager : IEvmManager
public async Task<List<Trade>> GetOrders(Account account, Ticker ticker)
{
var wallet = new Wallet(account.Secret, _password).GetAccount(account.Key);
var chain = ChainService.GetChain(Constants.Chains.Arbitrum);
var web3 = new Web3(wallet, chain.RpcUrl);
// var orders = await GmxService.GetOrders(web3, account.Key, ticker);
var orders = await _gmxV2Service.GetOrders(web3, account.Key, ticker);
if (account.IsPrivyWallet)
{
var orders = await _web3ProxyService.CallGmxServiceAsync<List<Trade>>("/get-orders",
new { address = account.Key, walletId = account.Secret, ticker = ticker.ToString() });
return GmxV2Mappers.Map(orders);
return orders;
}
else
{
var chain = ChainService.GetChain(Constants.Chains.Arbitrum);
var web3 = new Web3(chain.RpcUrl);
// var orders = await GmxService.GetOrders(web3, account.Key, ticker);
var orders = await _gmxV2Service.GetOrders(web3, account.Key, ticker);
return GmxV2Mappers.Map(orders);
}
return new List<Trade>();
}
public async Task<bool> SetAllowance(Account account, Ticker ticker, BigInteger amount)
{
if (account.IsPrivyWallet)
{
var allowance = await _web3ProxyService.CallPrivyServiceAsync<PrivyApproveTokenResponse>("/approve-token",
new
{
address = account.Key, walletId = account.Secret, ticker = ticker.ToString(),
amount = amount.Equals(0) ? null : amount.ToString()
});
return false;
}
var web3 = BuildWeb3ForAccount(account);
var contractAddress = TokenService.GetContractAddress(ticker);
var approval = await EvmBase.ApproveToken(web3, account.Key, contractAddress,
@@ -663,7 +824,29 @@ public class EvmManager : IEvmManager
public async Task<(string Id, string Address)> CreatePrivyWallet()
{
var privyWallet = await _privyService.CreateWalletAsync();
var privyWallet = await _web3ProxyService.CallPrivyServiceAsync<PrivyWallet>("/privy/create-wallet", new { });
return (privyWallet.Id, privyWallet.Address);
}
/// <summary>
/// Signs a message using the embedded wallet
/// </summary>
/// <param name="embeddedWalletId">The wallet id of the embedded wallet</param>
/// <param name="address">The address of the embedded wallet</param>
/// <param name="message">The message to sign</param>
/// <returns>The signature response</returns>
public async Task<string> SignMessageAsync(string embeddedWalletId, string address, string message)
{
// Construct the request body using the exact format from Privy documentati
var requestBody = new
{
address = address,
walletId = embeddedWalletId,
message = message,
};
var response =
await _web3ProxyService.CallPrivyServiceAsync<PrivySigningResponse>("sign-message", requestBody);
return response.Signature;
}
}