Implement revoke all approvals functionality in TradingController and related services
- Added a new endpoint in TradingController to revoke all token approvals for a specified Privy wallet address, with permission checks for user access. - Implemented the revokeAllApprovals method in TradingService to handle the revocation logic, including error handling and logging. - Updated IWeb3ProxyService and Web3ProxyService to support revocation requests to the Privy service. - Introduced a new PrivyRevokeAllApprovalsResponse type for structured responses from the revocation process. - Enhanced the UI in account tables to allow users to revoke approvals directly from the interface, providing feedback on success or failure. - Updated appsettings.json to change the default model for Gemini integration.
This commit is contained in:
@@ -200,6 +200,49 @@ public class TradingController : BaseController
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Revokes all token approvals for a Privy wallet address.
|
||||
/// Only admins can revoke approvals for any address, regular users can only revoke approvals for their own addresses.
|
||||
/// </summary>
|
||||
/// <param name="publicAddress">The public address of the Privy wallet to revoke approvals for.</param>
|
||||
/// <returns>The revocation response containing success status and transaction hashes.</returns>
|
||||
[HttpPost("RevokeAllApprovals")]
|
||||
public async Task<ActionResult<Managing.Infrastructure.Evm.Models.Privy.PrivyRevokeAllApprovalsResponse>> RevokeAllApprovals([FromBody] string publicAddress)
|
||||
{
|
||||
if (string.IsNullOrEmpty(publicAddress))
|
||||
{
|
||||
return BadRequest("Public address cannot be null or empty.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var user = await GetUser();
|
||||
if (user == null)
|
||||
{
|
||||
return Unauthorized("User not found");
|
||||
}
|
||||
|
||||
// Check if user has permission to revoke approvals for this address
|
||||
if (!await CanUserInitializeAddress(user.Name, publicAddress))
|
||||
{
|
||||
return Forbid(
|
||||
"You don't have permission to revoke approvals for this wallet address. You can only revoke approvals for your own wallet addresses.");
|
||||
}
|
||||
|
||||
var result = await _tradingService.RevokeAllApprovals(publicAddress);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error revoking all approvals for Privy wallet address: {Address}", publicAddress);
|
||||
return StatusCode(500, new Managing.Infrastructure.Evm.Models.Privy.PrivyRevokeAllApprovalsResponse
|
||||
{
|
||||
Success = false,
|
||||
Error = "An error occurred while revoking all approvals for the Privy wallet address."
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the user can initialize the given public address.
|
||||
/// Admins can initialize any address, regular users can only initialize their own addresses.
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
},
|
||||
"Llm": {
|
||||
"Gemini": {
|
||||
"DefaultModel": "gemini-2.0-flash"
|
||||
"DefaultModel": "gemini/gemini-3-flash-preview"
|
||||
},
|
||||
"OpenAI": {
|
||||
"DefaultModel": "gpt-4o"
|
||||
|
||||
@@ -69,6 +69,8 @@ public interface ITradingService
|
||||
DateTime startDate,
|
||||
DateTime endDate,
|
||||
List<IndicatorRequest> indicators);
|
||||
|
||||
Task<Managing.Infrastructure.Evm.Models.Privy.PrivyRevokeAllApprovalsResponse> RevokeAllApprovals(string publicAddress);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -41,5 +41,7 @@ namespace Managing.Application.Abstractions.Services
|
||||
string? ticker = null,
|
||||
DateTime? fromDate = null,
|
||||
DateTime? toDate = null);
|
||||
|
||||
Task<Managing.Infrastructure.Evm.Models.Privy.PrivyRevokeAllApprovalsResponse> RevokeAllApprovalsAsync(string address);
|
||||
}
|
||||
}
|
||||
@@ -549,6 +549,33 @@ public class TradingService : ITradingService
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<Managing.Infrastructure.Evm.Models.Privy.PrivyRevokeAllApprovalsResponse> RevokeAllApprovals(string publicAddress)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(publicAddress))
|
||||
{
|
||||
_logger.LogWarning("Attempted to revoke approvals with null or empty public address");
|
||||
return new Managing.Infrastructure.Evm.Models.Privy.PrivyRevokeAllApprovalsResponse
|
||||
{ Success = false, Error = "Public address cannot be null or empty" };
|
||||
}
|
||||
|
||||
// Call the Web3ProxyService to revoke all approvals
|
||||
var result = await _web3ProxyService.RevokeAllApprovalsAsync(publicAddress);
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error revoking all approvals for address {PublicAddress}", publicAddress);
|
||||
return new Managing.Infrastructure.Evm.Models.Privy.PrivyRevokeAllApprovalsResponse
|
||||
{
|
||||
Success = false,
|
||||
Error = ex.Message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps IndicatorRequest list to a domain Scenario object.
|
||||
/// </summary>
|
||||
|
||||
18
src/Managing.Domain/Evm/PrivyRevokeAllApprovalsResponse.cs
Normal file
18
src/Managing.Domain/Evm/PrivyRevokeAllApprovalsResponse.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace Managing.Infrastructure.Evm.Models.Privy;
|
||||
|
||||
public class PrivyRevokeAllApprovalsResponse
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string? OrderVaultUsdcHash { get; set; }
|
||||
public string? OrderVaultWethHash { get; set; }
|
||||
public string? ExchangeRouterUsdcHash { get; set; }
|
||||
public string? ExchangeRouterWethHash { get; set; }
|
||||
public string? SyntheticsRouterUsdcHash { get; set; }
|
||||
public string? SyntheticsRouterWethHash { get; set; }
|
||||
public string? DataStoreUsdcHash { get; set; }
|
||||
public string? DataStoreWethHash { get; set; }
|
||||
public string? DepositVaultUsdcHash { get; set; }
|
||||
public string? DepositVaultWethHash { get; set; }
|
||||
public string? Error { get; set; }
|
||||
}
|
||||
|
||||
@@ -819,5 +819,19 @@ namespace Managing.Infrastructure.Evm.Services
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
public async Task<Managing.Infrastructure.Evm.Models.Privy.PrivyRevokeAllApprovalsResponse> RevokeAllApprovalsAsync(string address)
|
||||
{
|
||||
var payload = new { address };
|
||||
|
||||
var response = await CallPrivyServiceAsync<Managing.Infrastructure.Evm.Models.Privy.PrivyRevokeAllApprovalsResponse>("/revoke-all-approvals", payload);
|
||||
|
||||
if (response == null)
|
||||
{
|
||||
throw new Web3ProxyException("Revoke all approvals response is null");
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1444,40 +1444,6 @@ export interface JobStatusTypeSummary {
|
||||
count?: number;
|
||||
}
|
||||
|
||||
export interface LlmProgressUpdate {
|
||||
type?: string;
|
||||
message?: string;
|
||||
iteration?: number | null;
|
||||
maxIterations?: number | null;
|
||||
toolName?: string | null;
|
||||
toolArguments?: { [key: string]: any; } | null;
|
||||
content?: string | null;
|
||||
response?: LlmChatResponse | null;
|
||||
error?: string | null;
|
||||
timestamp?: Date;
|
||||
}
|
||||
|
||||
export interface LlmChatResponse {
|
||||
content?: string;
|
||||
provider?: string;
|
||||
model?: string;
|
||||
toolCalls?: LlmToolCall[] | null;
|
||||
usage?: LlmUsage | null;
|
||||
requiresToolExecution?: boolean;
|
||||
}
|
||||
|
||||
export interface LlmToolCall {
|
||||
id?: string;
|
||||
name?: string;
|
||||
arguments?: { [key: string]: any; };
|
||||
}
|
||||
|
||||
export interface LlmUsage {
|
||||
promptTokens?: number;
|
||||
completionTokens?: number;
|
||||
totalTokens?: number;
|
||||
}
|
||||
|
||||
export interface LlmChatRequest {
|
||||
messages?: LlmMessage[];
|
||||
provider?: string | null;
|
||||
@@ -1488,6 +1454,10 @@ export interface LlmChatRequest {
|
||||
tools?: McpToolDefinition[] | null;
|
||||
}
|
||||
|
||||
export interface LlmChatStreamRequest extends LlmChatRequest {
|
||||
connectionId?: string | null;
|
||||
}
|
||||
|
||||
export interface LlmMessage {
|
||||
role?: string;
|
||||
content?: string;
|
||||
@@ -1495,6 +1465,12 @@ export interface LlmMessage {
|
||||
toolCallId?: string | null;
|
||||
}
|
||||
|
||||
export interface LlmToolCall {
|
||||
id?: string;
|
||||
name?: string;
|
||||
arguments?: { [key: string]: any; };
|
||||
}
|
||||
|
||||
export interface McpToolDefinition {
|
||||
name?: string;
|
||||
description?: string;
|
||||
@@ -1508,6 +1484,21 @@ export interface McpParameterDefinition {
|
||||
defaultValue?: any | null;
|
||||
}
|
||||
|
||||
export interface LlmChatResponse {
|
||||
content?: string;
|
||||
provider?: string;
|
||||
model?: string;
|
||||
toolCalls?: LlmToolCall[] | null;
|
||||
usage?: LlmUsage | null;
|
||||
requiresToolExecution?: boolean;
|
||||
}
|
||||
|
||||
export interface LlmUsage {
|
||||
promptTokens?: number;
|
||||
completionTokens?: number;
|
||||
totalTokens?: number;
|
||||
}
|
||||
|
||||
export interface ScenarioViewModel {
|
||||
name: string;
|
||||
indicators: IndicatorViewModel[];
|
||||
@@ -1548,6 +1539,21 @@ export interface PrivyInitAddressResponse {
|
||||
isAlreadyInitialized?: boolean;
|
||||
}
|
||||
|
||||
export interface PrivyRevokeAllApprovalsResponse {
|
||||
success?: boolean;
|
||||
orderVaultUsdcHash?: string | null;
|
||||
orderVaultWethHash?: string | null;
|
||||
exchangeRouterUsdcHash?: string | null;
|
||||
exchangeRouterWethHash?: string | null;
|
||||
syntheticsRouterUsdcHash?: string | null;
|
||||
syntheticsRouterWethHash?: string | null;
|
||||
dataStoreUsdcHash?: string | null;
|
||||
dataStoreWethHash?: string | null;
|
||||
depositVaultUsdcHash?: string | null;
|
||||
depositVaultWethHash?: string | null;
|
||||
error?: string | null;
|
||||
}
|
||||
|
||||
export interface IndicatorRequestDto {
|
||||
indicatorName: string;
|
||||
strategyDescription: string;
|
||||
|
||||
@@ -45,6 +45,7 @@ declare module 'fastify' {
|
||||
signPrivyMessage: typeof signPrivyMessage;
|
||||
approveToken: typeof approveToken;
|
||||
initAddress: typeof initAddress;
|
||||
revokeAllApprovals: typeof revokeAllApprovals;
|
||||
sendToken: typeof sendToken;
|
||||
getWalletBalance: typeof getWalletBalance;
|
||||
getTokenBalanceOnChain: typeof getTokenBalanceOnChain;
|
||||
@@ -975,6 +976,277 @@ export async function initAddress(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Revokes all token approvals for an address by setting approvals to 0 for all GMX contracts
|
||||
* @param address The wallet address to revoke approvals for
|
||||
* @returns Object containing transaction hashes for all revoked approvals
|
||||
*/
|
||||
export const revokeAllApprovalsImpl = async (
|
||||
address: string,
|
||||
fastify?: FastifyInstance
|
||||
): Promise<{
|
||||
orderVaultUsdcHash: string,
|
||||
orderVaultWethHash: string,
|
||||
exchangeRouterUsdcHash: string,
|
||||
exchangeRouterWethHash: string,
|
||||
syntheticsRouterUsdcHash: string,
|
||||
syntheticsRouterWethHash: string,
|
||||
dataStoreUsdcHash: string,
|
||||
dataStoreWethHash: string,
|
||||
depositVaultUsdcHash: string,
|
||||
depositVaultWethHash: string
|
||||
}> => {
|
||||
try {
|
||||
console.log('🚫 Starting revocation of all approvals for address:', address);
|
||||
|
||||
const sdk = await getClientForAddress(address);
|
||||
const {tokensData} = await sdk.tokens.getTokensData();
|
||||
const usdcTokenData = getTokenDataFromTicker(Ticker.USDC, tokensData);
|
||||
const wrapperEtherData = getTokenDataFromTicker("WETH", tokensData);
|
||||
|
||||
// Revoke by setting approval to 0
|
||||
const revokeAmount = BigInt(0);
|
||||
console.log('📊 Revocation amount set to:', revokeAmount.toString());
|
||||
|
||||
// Get USDC and WETH token addresses
|
||||
const usdcToken = GetToken('USDC');
|
||||
console.log('💱 USDC Token Address:', usdcToken.address);
|
||||
console.log('💱 WETH Token Address:', wrapperEtherData.address);
|
||||
|
||||
const results: any = {};
|
||||
|
||||
// ============================================================================
|
||||
// Revoke OrderVault Approvals
|
||||
// ============================================================================
|
||||
console.log('\n🔐 Step 1: Revoking OrderVault approvals...');
|
||||
|
||||
// USDC approval for OrderVault
|
||||
console.log(' Revoking USDC -> OrderVault approval...');
|
||||
results.orderVaultUsdcHash = await approveContractImpl(
|
||||
address,
|
||||
usdcToken.address,
|
||||
CONTRACTS[ARBITRUM].OrderVault,
|
||||
ARBITRUM,
|
||||
revokeAmount,
|
||||
true,
|
||||
fastify
|
||||
);
|
||||
console.log(' ✅ USDC OrderVault revocation hash:', results.orderVaultUsdcHash);
|
||||
|
||||
// WETH approval for OrderVault
|
||||
console.log(' Revoking WETH -> OrderVault approval...');
|
||||
results.orderVaultWethHash = await approveContractImpl(
|
||||
address,
|
||||
wrapperEtherData.address,
|
||||
CONTRACTS[ARBITRUM].OrderVault,
|
||||
ARBITRUM,
|
||||
revokeAmount,
|
||||
true,
|
||||
fastify
|
||||
);
|
||||
console.log(' ✅ WETH OrderVault revocation hash:', results.orderVaultWethHash);
|
||||
|
||||
// ============================================================================
|
||||
// Revoke ExchangeRouter Approvals
|
||||
// ============================================================================
|
||||
console.log('\n🔐 Step 2: Revoking ExchangeRouter approvals...');
|
||||
|
||||
// USDC approval for ExchangeRouter
|
||||
console.log(' Revoking USDC -> ExchangeRouter approval...');
|
||||
results.exchangeRouterUsdcHash = await approveContractImpl(
|
||||
address,
|
||||
usdcToken.address,
|
||||
CONTRACTS[ARBITRUM].ExchangeRouter,
|
||||
ARBITRUM,
|
||||
revokeAmount,
|
||||
true,
|
||||
fastify
|
||||
);
|
||||
console.log(' ✅ USDC ExchangeRouter revocation hash:', results.exchangeRouterUsdcHash);
|
||||
|
||||
// WETH approval for ExchangeRouter
|
||||
console.log(' Revoking WETH -> ExchangeRouter approval...');
|
||||
results.exchangeRouterWethHash = await approveContractImpl(
|
||||
address,
|
||||
wrapperEtherData.address,
|
||||
CONTRACTS[ARBITRUM].ExchangeRouter,
|
||||
ARBITRUM,
|
||||
revokeAmount,
|
||||
true,
|
||||
fastify
|
||||
);
|
||||
console.log(' ✅ WETH ExchangeRouter revocation hash:', results.exchangeRouterWethHash);
|
||||
|
||||
// ============================================================================
|
||||
// Revoke SyntheticsRouter Approvals
|
||||
// ============================================================================
|
||||
console.log('\n🔐 Step 3: Revoking SyntheticsRouter approvals...');
|
||||
|
||||
// USDC approval for SyntheticsRouter
|
||||
console.log(' Revoking USDC -> SyntheticsRouter approval...');
|
||||
results.syntheticsRouterUsdcHash = await approveContractImpl(
|
||||
address,
|
||||
usdcToken.address,
|
||||
CONTRACTS[ARBITRUM].SyntheticsRouter,
|
||||
ARBITRUM,
|
||||
revokeAmount,
|
||||
true,
|
||||
fastify
|
||||
);
|
||||
console.log(' ✅ USDC SyntheticsRouter revocation hash:', results.syntheticsRouterUsdcHash);
|
||||
|
||||
// WETH approval for SyntheticsRouter
|
||||
console.log(' Revoking WETH -> SyntheticsRouter approval...');
|
||||
results.syntheticsRouterWethHash = await approveContractImpl(
|
||||
address,
|
||||
wrapperEtherData.address,
|
||||
CONTRACTS[ARBITRUM].SyntheticsRouter,
|
||||
ARBITRUM,
|
||||
revokeAmount,
|
||||
true,
|
||||
fastify
|
||||
);
|
||||
console.log(' ✅ WETH SyntheticsRouter revocation hash:', results.syntheticsRouterWethHash);
|
||||
|
||||
// ============================================================================
|
||||
// Revoke DataStore Approvals
|
||||
// ============================================================================
|
||||
console.log('\n🔐 Step 4: Revoking DataStore approvals...');
|
||||
|
||||
// USDC approval for DataStore
|
||||
console.log(' Revoking USDC -> DataStore approval...');
|
||||
results.dataStoreUsdcHash = await approveContractImpl(
|
||||
address,
|
||||
usdcToken.address,
|
||||
CONTRACTS[ARBITRUM].DataStore,
|
||||
ARBITRUM,
|
||||
revokeAmount,
|
||||
true,
|
||||
fastify
|
||||
);
|
||||
console.log(' ✅ USDC DataStore revocation hash:', results.dataStoreUsdcHash);
|
||||
|
||||
// WETH approval for DataStore
|
||||
console.log(' Revoking WETH -> DataStore approval...');
|
||||
results.dataStoreWethHash = await approveContractImpl(
|
||||
address,
|
||||
wrapperEtherData.address,
|
||||
CONTRACTS[ARBITRUM].DataStore,
|
||||
ARBITRUM,
|
||||
revokeAmount,
|
||||
true,
|
||||
fastify
|
||||
);
|
||||
console.log(' ✅ WETH DataStore revocation hash:', results.dataStoreWethHash);
|
||||
|
||||
// ============================================================================
|
||||
// Revoke DepositVault Approvals
|
||||
// ============================================================================
|
||||
console.log('\n🔐 Step 5: Revoking DepositVault approvals...');
|
||||
|
||||
// USDC approval for DepositVault
|
||||
console.log(' Revoking USDC -> DepositVault approval...');
|
||||
results.depositVaultUsdcHash = await approveContractImpl(
|
||||
address,
|
||||
usdcToken.address,
|
||||
CONTRACTS[ARBITRUM].DepositVault,
|
||||
ARBITRUM,
|
||||
revokeAmount,
|
||||
true,
|
||||
fastify
|
||||
);
|
||||
console.log(' ✅ USDC DepositVault revocation hash:', results.depositVaultUsdcHash);
|
||||
|
||||
// WETH approval for DepositVault
|
||||
console.log(' Revoking WETH -> DepositVault approval...');
|
||||
results.depositVaultWethHash = await approveContractImpl(
|
||||
address,
|
||||
wrapperEtherData.address,
|
||||
CONTRACTS[ARBITRUM].DepositVault,
|
||||
ARBITRUM,
|
||||
revokeAmount,
|
||||
true,
|
||||
fastify
|
||||
);
|
||||
console.log(' ✅ WETH DepositVault revocation hash:', results.depositVaultWethHash);
|
||||
|
||||
// ============================================================================
|
||||
// SUMMARY: Final revocation status
|
||||
// ============================================================================
|
||||
console.log('\n📋 Final Revocation Summary:');
|
||||
console.log(' OrderVault USDC:', results.orderVaultUsdcHash);
|
||||
console.log(' OrderVault WETH:', results.orderVaultWethHash);
|
||||
console.log(' ExchangeRouter USDC:', results.exchangeRouterUsdcHash);
|
||||
console.log(' ExchangeRouter WETH:', results.exchangeRouterWethHash);
|
||||
console.log(' SyntheticsRouter USDC:', results.syntheticsRouterUsdcHash);
|
||||
console.log(' SyntheticsRouter WETH:', results.syntheticsRouterWethHash);
|
||||
console.log(' DataStore USDC:', results.dataStoreUsdcHash);
|
||||
console.log(' DataStore WETH:', results.dataStoreWethHash);
|
||||
console.log(' DepositVault USDC:', results.depositVaultUsdcHash);
|
||||
console.log(' DepositVault WETH:', results.depositVaultWethHash);
|
||||
|
||||
// Clear approval cache for this address since all approvals have been revoked
|
||||
try {
|
||||
const {clearApprovalCacheForAccount} = await import('../../utils/approvalCache.js');
|
||||
await clearApprovalCacheForAccount(ARBITRUM, address);
|
||||
console.log(' ✅ Approval cache cleared for address');
|
||||
} catch (cacheError) {
|
||||
console.warn(' ⚠️ Failed to clear approval cache:', cacheError);
|
||||
// Don't fail the entire operation if cache clearing fails
|
||||
}
|
||||
|
||||
console.log('\n✅ All approvals revoked successfully!');
|
||||
|
||||
return results;
|
||||
} catch (error) {
|
||||
console.error('❌ Error revoking approvals:', error);
|
||||
throw new Error(`Failed to revoke approvals: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Revokes all token approvals for a wallet address
|
||||
* @param this The FastifyRequest instance
|
||||
* @param reply The FastifyReply instance
|
||||
* @param address The wallet address to revoke approvals for
|
||||
* @returns The response object with success status and transaction hashes
|
||||
*/
|
||||
export async function revokeAllApprovals(
|
||||
this: FastifyRequest,
|
||||
reply: FastifyReply,
|
||||
address: string
|
||||
) {
|
||||
try {
|
||||
if (!address) {
|
||||
throw new Error('Wallet address is required for revocation');
|
||||
}
|
||||
|
||||
const results = await revokeAllApprovalsImpl(address, this.server);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
orderVaultUsdcHash: results.orderVaultUsdcHash,
|
||||
orderVaultWethHash: results.orderVaultWethHash,
|
||||
exchangeRouterUsdcHash: results.exchangeRouterUsdcHash,
|
||||
exchangeRouterWethHash: results.exchangeRouterWethHash,
|
||||
syntheticsRouterUsdcHash: results.syntheticsRouterUsdcHash,
|
||||
syntheticsRouterWethHash: results.syntheticsRouterWethHash,
|
||||
dataStoreUsdcHash: results.dataStoreUsdcHash,
|
||||
dataStoreWethHash: results.dataStoreWethHash,
|
||||
depositVaultUsdcHash: results.depositVaultUsdcHash,
|
||||
depositVaultWethHash: results.depositVaultWethHash
|
||||
};
|
||||
} catch (error) {
|
||||
this.log.error(error);
|
||||
|
||||
reply.status(500);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'An unknown error occurred'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends tokens from one address to another using Privy wallet (implementation)
|
||||
* @param senderAddress The sender's wallet address
|
||||
@@ -1756,6 +2028,10 @@ export default fp(async (fastify) => {
|
||||
return initAddress.call(this, reply, address);
|
||||
});
|
||||
|
||||
fastify.decorateRequest('revokeAllApprovals', async function(this: FastifyRequest, reply: FastifyReply, address: string) {
|
||||
return revokeAllApprovals.call(this, reply, address);
|
||||
});
|
||||
|
||||
fastify.decorateRequest('sendToken', async function(this: FastifyRequest, reply: FastifyReply, senderAddress: string, recipientAddress: string, ticker: string, amount: string, chainId?: number) {
|
||||
return sendToken.call(this, reply, senderAddress, recipientAddress, ticker, amount, chainId);
|
||||
});
|
||||
|
||||
@@ -77,6 +77,50 @@ const plugin: FastifyPluginAsyncTypebox = async (fastify) => {
|
||||
}
|
||||
)
|
||||
|
||||
fastify.post(
|
||||
'/revoke-all-approvals',
|
||||
{
|
||||
schema: {
|
||||
body: Type.Object({
|
||||
address: Type.String()
|
||||
}),
|
||||
response: {
|
||||
200: Type.Object({
|
||||
success: Type.Boolean(),
|
||||
orderVaultUsdcHash: Type.Optional(Type.String()),
|
||||
orderVaultWethHash: Type.Optional(Type.String()),
|
||||
exchangeRouterUsdcHash: Type.Optional(Type.String()),
|
||||
exchangeRouterWethHash: Type.Optional(Type.String()),
|
||||
syntheticsRouterUsdcHash: Type.Optional(Type.String()),
|
||||
syntheticsRouterWethHash: Type.Optional(Type.String()),
|
||||
dataStoreUsdcHash: Type.Optional(Type.String()),
|
||||
dataStoreWethHash: Type.Optional(Type.String()),
|
||||
depositVaultUsdcHash: Type.Optional(Type.String()),
|
||||
depositVaultWethHash: Type.Optional(Type.String()),
|
||||
error: Type.Optional(Type.String())
|
||||
}),
|
||||
400: Type.Object({
|
||||
success: Type.Boolean(),
|
||||
error: Type.String()
|
||||
}),
|
||||
500: Type.Object({
|
||||
success: Type.Boolean(),
|
||||
error: Type.String()
|
||||
})
|
||||
},
|
||||
tags: ['Privy']
|
||||
}
|
||||
},
|
||||
async function (request, reply) {
|
||||
try {
|
||||
const { address } = request.body;
|
||||
return await request.revokeAllApprovals(reply, address);
|
||||
} catch (error) {
|
||||
return handleError(request, reply, error, 'privy/revoke-all-approvals');
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
fastify.post(
|
||||
'/send-token',
|
||||
{
|
||||
|
||||
@@ -2910,7 +2910,7 @@ export class LlmClient extends AuthorizedApiBase {
|
||||
this.baseUrl = baseUrl ?? "http://localhost:5000";
|
||||
}
|
||||
|
||||
llm_ChatStream(request: LlmChatRequest): Promise<LlmProgressUpdate[]> {
|
||||
llm_ChatStream(request: LlmChatStreamRequest): Promise<FileResponse> {
|
||||
let url_ = this.baseUrl + "/Llm/ChatStream";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
@@ -2921,7 +2921,7 @@ export class LlmClient extends AuthorizedApiBase {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json"
|
||||
"Accept": "application/octet-stream"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2932,21 +2932,26 @@ export class LlmClient extends AuthorizedApiBase {
|
||||
});
|
||||
}
|
||||
|
||||
protected processLlm_ChatStream(response: Response): Promise<LlmProgressUpdate[]> {
|
||||
protected processLlm_ChatStream(response: Response): Promise<FileResponse> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 200) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result200: any = null;
|
||||
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as LlmProgressUpdate[];
|
||||
return result200;
|
||||
});
|
||||
if (status === 200 || status === 206) {
|
||||
const contentDisposition = response.headers ? response.headers.get("content-disposition") : undefined;
|
||||
let fileNameMatch = contentDisposition ? /filename\*=(?:(\\?['"])(.*?)\1|(?:[^\s]+'.*?')?([^;\n]*))/g.exec(contentDisposition) : undefined;
|
||||
let fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[3] || fileNameMatch[2] : undefined;
|
||||
if (fileName) {
|
||||
fileName = decodeURIComponent(fileName);
|
||||
} else {
|
||||
fileNameMatch = contentDisposition ? /filename="?([^"]*?)"?(;|$)/g.exec(contentDisposition) : undefined;
|
||||
fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[1] : undefined;
|
||||
}
|
||||
return response.blob().then(blob => { return { fileName: fileName, data: blob, status: status, headers: _headers }; });
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<LlmProgressUpdate[]>(null as any);
|
||||
return Promise.resolve<FileResponse>(null as any);
|
||||
}
|
||||
|
||||
llm_Chat(request: LlmChatRequest): Promise<LlmChatResponse> {
|
||||
@@ -4262,6 +4267,45 @@ export class TradingClient extends AuthorizedApiBase {
|
||||
return Promise.resolve<PrivyInitAddressResponse>(null as any);
|
||||
}
|
||||
|
||||
trading_RevokeAllApprovals(publicAddress: string): Promise<PrivyRevokeAllApprovalsResponse> {
|
||||
let url_ = this.baseUrl + "/Trading/RevokeAllApprovals";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
const content_ = JSON.stringify(publicAddress);
|
||||
|
||||
let options_: RequestInit = {
|
||||
body: content_,
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json"
|
||||
}
|
||||
};
|
||||
|
||||
return this.transformOptions(options_).then(transformedOptions_ => {
|
||||
return this.http.fetch(url_, transformedOptions_);
|
||||
}).then((_response: Response) => {
|
||||
return this.processTrading_RevokeAllApprovals(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processTrading_RevokeAllApprovals(response: Response): Promise<PrivyRevokeAllApprovalsResponse> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 200) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result200: any = null;
|
||||
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as PrivyRevokeAllApprovalsResponse;
|
||||
return result200;
|
||||
});
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<PrivyRevokeAllApprovalsResponse>(null as any);
|
||||
}
|
||||
|
||||
trading_RequestIndicator(request: IndicatorRequestDto): Promise<FileResponse> {
|
||||
let url_ = this.baseUrl + "/Trading/RequestIndicator";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
@@ -6272,40 +6316,6 @@ export interface JobStatusTypeSummary {
|
||||
count?: number;
|
||||
}
|
||||
|
||||
export interface LlmProgressUpdate {
|
||||
type?: string;
|
||||
message?: string;
|
||||
iteration?: number | null;
|
||||
maxIterations?: number | null;
|
||||
toolName?: string | null;
|
||||
toolArguments?: { [key: string]: any; } | null;
|
||||
content?: string | null;
|
||||
response?: LlmChatResponse | null;
|
||||
error?: string | null;
|
||||
timestamp?: Date;
|
||||
}
|
||||
|
||||
export interface LlmChatResponse {
|
||||
content?: string;
|
||||
provider?: string;
|
||||
model?: string;
|
||||
toolCalls?: LlmToolCall[] | null;
|
||||
usage?: LlmUsage | null;
|
||||
requiresToolExecution?: boolean;
|
||||
}
|
||||
|
||||
export interface LlmToolCall {
|
||||
id?: string;
|
||||
name?: string;
|
||||
arguments?: { [key: string]: any; };
|
||||
}
|
||||
|
||||
export interface LlmUsage {
|
||||
promptTokens?: number;
|
||||
completionTokens?: number;
|
||||
totalTokens?: number;
|
||||
}
|
||||
|
||||
export interface LlmChatRequest {
|
||||
messages?: LlmMessage[];
|
||||
provider?: string | null;
|
||||
@@ -6316,6 +6326,10 @@ export interface LlmChatRequest {
|
||||
tools?: McpToolDefinition[] | null;
|
||||
}
|
||||
|
||||
export interface LlmChatStreamRequest extends LlmChatRequest {
|
||||
connectionId?: string | null;
|
||||
}
|
||||
|
||||
export interface LlmMessage {
|
||||
role?: string;
|
||||
content?: string;
|
||||
@@ -6323,6 +6337,12 @@ export interface LlmMessage {
|
||||
toolCallId?: string | null;
|
||||
}
|
||||
|
||||
export interface LlmToolCall {
|
||||
id?: string;
|
||||
name?: string;
|
||||
arguments?: { [key: string]: any; };
|
||||
}
|
||||
|
||||
export interface McpToolDefinition {
|
||||
name?: string;
|
||||
description?: string;
|
||||
@@ -6336,6 +6356,21 @@ export interface McpParameterDefinition {
|
||||
defaultValue?: any | null;
|
||||
}
|
||||
|
||||
export interface LlmChatResponse {
|
||||
content?: string;
|
||||
provider?: string;
|
||||
model?: string;
|
||||
toolCalls?: LlmToolCall[] | null;
|
||||
usage?: LlmUsage | null;
|
||||
requiresToolExecution?: boolean;
|
||||
}
|
||||
|
||||
export interface LlmUsage {
|
||||
promptTokens?: number;
|
||||
completionTokens?: number;
|
||||
totalTokens?: number;
|
||||
}
|
||||
|
||||
export interface ScenarioViewModel {
|
||||
name: string;
|
||||
indicators: IndicatorViewModel[];
|
||||
@@ -6376,6 +6411,21 @@ export interface PrivyInitAddressResponse {
|
||||
isAlreadyInitialized?: boolean;
|
||||
}
|
||||
|
||||
export interface PrivyRevokeAllApprovalsResponse {
|
||||
success?: boolean;
|
||||
orderVaultUsdcHash?: string | null;
|
||||
orderVaultWethHash?: string | null;
|
||||
exchangeRouterUsdcHash?: string | null;
|
||||
exchangeRouterWethHash?: string | null;
|
||||
syntheticsRouterUsdcHash?: string | null;
|
||||
syntheticsRouterWethHash?: string | null;
|
||||
dataStoreUsdcHash?: string | null;
|
||||
dataStoreWethHash?: string | null;
|
||||
depositVaultUsdcHash?: string | null;
|
||||
depositVaultWethHash?: string | null;
|
||||
error?: string | null;
|
||||
}
|
||||
|
||||
export interface IndicatorRequestDto {
|
||||
indicatorName: string;
|
||||
strategyDescription: string;
|
||||
|
||||
@@ -1444,40 +1444,6 @@ export interface JobStatusTypeSummary {
|
||||
count?: number;
|
||||
}
|
||||
|
||||
export interface LlmProgressUpdate {
|
||||
type?: string;
|
||||
message?: string;
|
||||
iteration?: number | null;
|
||||
maxIterations?: number | null;
|
||||
toolName?: string | null;
|
||||
toolArguments?: { [key: string]: any; } | null;
|
||||
content?: string | null;
|
||||
response?: LlmChatResponse | null;
|
||||
error?: string | null;
|
||||
timestamp?: Date;
|
||||
}
|
||||
|
||||
export interface LlmChatResponse {
|
||||
content?: string;
|
||||
provider?: string;
|
||||
model?: string;
|
||||
toolCalls?: LlmToolCall[] | null;
|
||||
usage?: LlmUsage | null;
|
||||
requiresToolExecution?: boolean;
|
||||
}
|
||||
|
||||
export interface LlmToolCall {
|
||||
id?: string;
|
||||
name?: string;
|
||||
arguments?: { [key: string]: any; };
|
||||
}
|
||||
|
||||
export interface LlmUsage {
|
||||
promptTokens?: number;
|
||||
completionTokens?: number;
|
||||
totalTokens?: number;
|
||||
}
|
||||
|
||||
export interface LlmChatRequest {
|
||||
messages?: LlmMessage[];
|
||||
provider?: string | null;
|
||||
@@ -1488,6 +1454,10 @@ export interface LlmChatRequest {
|
||||
tools?: McpToolDefinition[] | null;
|
||||
}
|
||||
|
||||
export interface LlmChatStreamRequest extends LlmChatRequest {
|
||||
connectionId?: string | null;
|
||||
}
|
||||
|
||||
export interface LlmMessage {
|
||||
role?: string;
|
||||
content?: string;
|
||||
@@ -1495,6 +1465,12 @@ export interface LlmMessage {
|
||||
toolCallId?: string | null;
|
||||
}
|
||||
|
||||
export interface LlmToolCall {
|
||||
id?: string;
|
||||
name?: string;
|
||||
arguments?: { [key: string]: any; };
|
||||
}
|
||||
|
||||
export interface McpToolDefinition {
|
||||
name?: string;
|
||||
description?: string;
|
||||
@@ -1508,6 +1484,21 @@ export interface McpParameterDefinition {
|
||||
defaultValue?: any | null;
|
||||
}
|
||||
|
||||
export interface LlmChatResponse {
|
||||
content?: string;
|
||||
provider?: string;
|
||||
model?: string;
|
||||
toolCalls?: LlmToolCall[] | null;
|
||||
usage?: LlmUsage | null;
|
||||
requiresToolExecution?: boolean;
|
||||
}
|
||||
|
||||
export interface LlmUsage {
|
||||
promptTokens?: number;
|
||||
completionTokens?: number;
|
||||
totalTokens?: number;
|
||||
}
|
||||
|
||||
export interface ScenarioViewModel {
|
||||
name: string;
|
||||
indicators: IndicatorViewModel[];
|
||||
@@ -1548,6 +1539,21 @@ export interface PrivyInitAddressResponse {
|
||||
isAlreadyInitialized?: boolean;
|
||||
}
|
||||
|
||||
export interface PrivyRevokeAllApprovalsResponse {
|
||||
success?: boolean;
|
||||
orderVaultUsdcHash?: string | null;
|
||||
orderVaultWethHash?: string | null;
|
||||
exchangeRouterUsdcHash?: string | null;
|
||||
exchangeRouterWethHash?: string | null;
|
||||
syntheticsRouterUsdcHash?: string | null;
|
||||
syntheticsRouterWethHash?: string | null;
|
||||
dataStoreUsdcHash?: string | null;
|
||||
dataStoreWethHash?: string | null;
|
||||
depositVaultUsdcHash?: string | null;
|
||||
depositVaultWethHash?: string | null;
|
||||
error?: string | null;
|
||||
}
|
||||
|
||||
export interface IndicatorRequestDto {
|
||||
indicatorName: string;
|
||||
strategyDescription: string;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {ChevronDownIcon, ChevronRightIcon,} from '@heroicons/react/solid'
|
||||
import React, {useEffect, useMemo, useState} from 'react'
|
||||
import {useNavigate} from 'react-router-dom'
|
||||
import {FiCopy, FiKey, FiPlay, FiTrash2, FiTrendingUp} from 'react-icons/fi'
|
||||
import {FiCopy, FiKey, FiPlay, FiTrash2, FiTrendingUp, FiX} from 'react-icons/fi'
|
||||
|
||||
import useApiUrlStore from '../../../app/store/apiStore'
|
||||
import {SelectColumnFilter, Table, Toast,} from '../../../components/mollecules'
|
||||
@@ -55,6 +55,22 @@ const AccountTable: React.FC<IAccountList> = ({ list, isFetching }) => {
|
||||
}
|
||||
}
|
||||
|
||||
async function revokeAllApprovals(publicAddress: string) {
|
||||
const t = new Toast('Revoking all approvals')
|
||||
const client = new TradingClient({}, apiUrl)
|
||||
|
||||
try {
|
||||
const response = await client.trading_RevokeAllApprovals(publicAddress)
|
||||
if (response.success) {
|
||||
t.update('success', 'All approvals revoked successfully')
|
||||
} else {
|
||||
t.update('error', `Revocation failed: ${response.error || 'Unknown error'}`)
|
||||
}
|
||||
} catch (err) {
|
||||
t.update('error', 'Error: ' + err)
|
||||
}
|
||||
}
|
||||
|
||||
async function copyToClipboard(text: string) {
|
||||
const t = new Toast('Copying to clipboard...')
|
||||
try {
|
||||
@@ -160,6 +176,14 @@ const AccountTable: React.FC<IAccountList> = ({ list, isFetching }) => {
|
||||
<span className="ml-1">Init</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="btn btn-sm btn-warning btn-outline"
|
||||
onClick={() => revokeAllApprovals(account.key)}
|
||||
>
|
||||
<FiX />
|
||||
<span className="ml-1">Revoke</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="btn btn-sm btn-success btn-outline"
|
||||
onClick={() => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {ChevronDownIcon, ChevronRightIcon,} from '@heroicons/react/solid'
|
||||
import React, {useEffect, useMemo, useState} from 'react'
|
||||
import {useNavigate} from 'react-router-dom'
|
||||
import {FiCopy, FiKey, FiPlay, FiTrash2, FiTrendingUp} from 'react-icons/fi'
|
||||
import {FiCopy, FiKey, FiPlay, FiTrash2, FiTrendingUp, FiX} from 'react-icons/fi'
|
||||
|
||||
import useApiUrlStore from '../../../app/store/apiStore'
|
||||
import {SelectColumnFilter, Table, Toast,} from '../../../components/mollecules'
|
||||
@@ -55,6 +55,22 @@ const AccountTable: React.FC<IAccountList> = ({ list, isFetching }) => {
|
||||
}
|
||||
}
|
||||
|
||||
async function revokeAllApprovals(publicAddress: string) {
|
||||
const t = new Toast('Revoking all approvals')
|
||||
const client = new TradingClient({}, apiUrl)
|
||||
|
||||
try {
|
||||
const response = await client.trading_RevokeAllApprovals(publicAddress)
|
||||
if (response.success) {
|
||||
t.update('success', 'All approvals revoked successfully')
|
||||
} else {
|
||||
t.update('error', `Revocation failed: ${response.error || 'Unknown error'}`)
|
||||
}
|
||||
} catch (err) {
|
||||
t.update('error', 'Error: ' + err)
|
||||
}
|
||||
}
|
||||
|
||||
async function copyToClipboard(text: string) {
|
||||
const t = new Toast('Copying to clipboard...')
|
||||
try {
|
||||
@@ -160,6 +176,14 @@ const AccountTable: React.FC<IAccountList> = ({ list, isFetching }) => {
|
||||
<span className="ml-1">Init</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="btn btn-sm btn-warning btn-outline"
|
||||
onClick={() => revokeAllApprovals(account.key)}
|
||||
>
|
||||
<FiX />
|
||||
<span className="ml-1">Revoke</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="btn btn-sm btn-success btn-outline"
|
||||
onClick={() => {
|
||||
|
||||
Reference in New Issue
Block a user