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:
677
src/Managing.Web3Proxy/src/plugins/custom/privy.ts
Normal file
677
src/Managing.Web3Proxy/src/plugins/custom/privy.ts
Normal file
@@ -0,0 +1,677 @@
|
||||
import fp from 'fastify-plugin'
|
||||
import {FastifyReply, FastifyRequest, FastifyInstance} from 'fastify'
|
||||
import {z} from 'zod'
|
||||
import canonicalize from 'canonicalize'
|
||||
import crypto from 'crypto'
|
||||
import {PrivyClient} from '@privy-io/server-auth'
|
||||
import {ethers} from 'ethers'
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
// Load environment variables
|
||||
dotenv.config()
|
||||
|
||||
import Token from '../../generated/gmxsdk/abis/Token.json' with { type: 'json' }
|
||||
import { ARBITRUM } from '../../generated/gmxsdk/configs/chains.js'
|
||||
import { TOKENS } from '../../generated/gmxsdk/configs/tokens.js'
|
||||
import { CONTRACTS } from '../../generated/gmxsdk/configs/contracts.js'
|
||||
import { getClientForAddress, getTokenDataFromTicker } from './gmx.js'
|
||||
import { Ticker } from '../../generated/ManagingApiTypes.js'
|
||||
import { Address } from 'viem'
|
||||
|
||||
/**
|
||||
* Privy Plugin
|
||||
*
|
||||
* This plugin adds functionality for interacting with the Privy API,
|
||||
* including signing messages and token approvals.
|
||||
*
|
||||
* Token Approval Process:
|
||||
* 1. Client sends a request to /approve-token with:
|
||||
* - walletId: The user's wallet ID
|
||||
* - address: The user's wallet address
|
||||
* - ticker: The token ticker or enum value (as string)
|
||||
* - amount: The amount to approve (optional, defaults to max amount)
|
||||
* - chainId: The chain ID where the approval will take place
|
||||
*
|
||||
* 2. The server handles the request by:
|
||||
* - Validating the input parameters
|
||||
* - Getting the token data based on the ticker
|
||||
* - Creating and sending an ERC20 approval transaction using ethers.js and Privy
|
||||
* - Returning the transaction hash to the client
|
||||
*/
|
||||
|
||||
|
||||
declare module 'fastify' {
|
||||
export interface FastifyRequest {
|
||||
signPrivyMessage: typeof signPrivyMessage;
|
||||
approveToken: typeof approveToken;
|
||||
initAddress: typeof initAddress;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an initialized PrivyClient instance with configuration from environment variables
|
||||
* @returns The configured PrivyClient instance
|
||||
*/
|
||||
export const getPrivyClient = (fastify?: FastifyInstance): PrivyClient => {
|
||||
const appId = fastify?.config?.PRIVY_APP_ID || process.env.PRIVY_APP_ID;
|
||||
const appSecret = fastify?.config?.PRIVY_APP_SECRET || process.env.PRIVY_APP_SECRET;
|
||||
const authKey = fastify?.config?.PRIVY_AUTHORIZATION_KEY || process.env.PRIVY_AUTHORIZATION_KEY;
|
||||
|
||||
if (!appId || !appSecret || !authKey) {
|
||||
console.error('Missing Privy environment variables:');
|
||||
console.error('PRIVY_APP_ID:', appId ? 'present' : 'missing');
|
||||
console.error('PRIVY_APP_SECRET:', appSecret ? 'present' : 'missing');
|
||||
console.error('PRIVY_AUTHORIZATION_KEY:', authKey ? 'present' : 'missing');
|
||||
throw new Error('Missing required Privy environment variables');
|
||||
}
|
||||
|
||||
return new PrivyClient(
|
||||
appId,
|
||||
appSecret,
|
||||
{
|
||||
walletApi: {
|
||||
authorizationPrivateKey: authKey
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Authentication function for Privy API calls
|
||||
* @param url The URL for the API endpoint
|
||||
* @param body The request body
|
||||
* @returns The authorization signature
|
||||
*/
|
||||
export function getAuthorizationSignature({url, body}: {url: string; body: object}) {
|
||||
const payload = {
|
||||
version: 1,
|
||||
method: 'POST',
|
||||
url,
|
||||
body,
|
||||
headers: {
|
||||
'privy-app-id': process.env.PRIVY_APP_ID
|
||||
}
|
||||
};
|
||||
|
||||
// JSON-canonicalize the payload and convert it to a buffer
|
||||
// @ts-ignore - canonicalize is a callable function despite TypeScript error
|
||||
const serializedPayload = canonicalize(payload) as string;
|
||||
const serializedPayloadBuffer = Buffer.from(serializedPayload);
|
||||
|
||||
// Get the authorization key from environment variables
|
||||
const privateKeyAsString = (process.env.PRIVY_AUTHORIZATION_KEY ?? "").replace('wallet-auth:', '');
|
||||
|
||||
// Convert private key to PEM format and create a key object
|
||||
const privateKeyAsPem = `-----BEGIN PRIVATE KEY-----\n${privateKeyAsString}\n-----END PRIVATE KEY-----`;
|
||||
const privateKey = crypto.createPrivateKey({
|
||||
key: privateKeyAsPem,
|
||||
format: 'pem',
|
||||
});
|
||||
|
||||
// Sign the payload buffer with the private key
|
||||
const signatureBuffer = crypto.sign('sha256', serializedPayloadBuffer, privateKey);
|
||||
const signature = signatureBuffer.toString('base64');
|
||||
return signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a request to the Privy API with proper authentication and headers.
|
||||
* @param url - The full URL for the API endpoint.
|
||||
* @param body - The request body.
|
||||
* @param requiresAuth - Whether the request requires authentication.
|
||||
* @returns The response data.
|
||||
*/
|
||||
export const makePrivyRequest = async <T>(url: string, body: object, requiresAuth = true): Promise<T> => {
|
||||
try {
|
||||
let headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
'privy-app-id': process.env.PRIVY_APP_ID ?? "",
|
||||
};
|
||||
|
||||
if (requiresAuth) {
|
||||
// Generate authorization signature
|
||||
const authSig = await getAuthorizationSignature({
|
||||
url,
|
||||
body
|
||||
});
|
||||
|
||||
// Create authentication string for basic auth
|
||||
const basicAuthString = `${process.env.PRIVY_APP_ID}:${process.env.PRIVY_APP_SECRET}`;
|
||||
const base64Auth = Buffer.from(basicAuthString).toString('base64');
|
||||
|
||||
headers = {
|
||||
...headers,
|
||||
'privy-authorization-signature': authSig,
|
||||
'Authorization': `Basic ${base64Auth}`
|
||||
};
|
||||
}
|
||||
|
||||
const request = {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify(body)
|
||||
};
|
||||
|
||||
// Make the API request
|
||||
const response = await fetch(url, request).then(res => {
|
||||
return res;
|
||||
}).catch(err => {
|
||||
console.log("error", err);
|
||||
throw err;
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Privy API request failed: ${response.status}`);
|
||||
}
|
||||
|
||||
return await response.json() as T;
|
||||
} catch (error) {
|
||||
console.error('Error making Privy API request:', error);
|
||||
throw new Error('Failed to make Privy API request');
|
||||
}
|
||||
};
|
||||
|
||||
// Schema for sign-message request
|
||||
const signMessageSchema = z.object({
|
||||
walletId: z.string().nonempty(),
|
||||
message: z.string().nonempty(),
|
||||
address: z.string().nonempty()
|
||||
});
|
||||
|
||||
// Schema for token-approval request
|
||||
const tokenApprovalSchema = z.object({
|
||||
walletId: z.string().nonempty(),
|
||||
address: z.string().nonempty(),
|
||||
ticker: z.string().nonempty(),
|
||||
amount: z.bigint().positive().optional(),
|
||||
chainId: z.number().positive().optional()
|
||||
});
|
||||
|
||||
/**
|
||||
* Gets the chain name based on chain ID
|
||||
* @param chainId The chain ID
|
||||
* @returns The CAIP-2 identifier for the chain
|
||||
*/
|
||||
export const getChainName = (chainId: number): string => {
|
||||
switch (chainId) {
|
||||
case 1:
|
||||
return 'eip155:1'; // Ethereum Mainnet
|
||||
case 42161:
|
||||
return 'eip155:42161'; // Arbitrum One
|
||||
case 421613:
|
||||
return 'eip155:421613'; // Arbitrum Goerli
|
||||
case 8453:
|
||||
return 'eip155:8453'; // Base
|
||||
case 84531:
|
||||
return 'eip155:84531'; // Base Goerli
|
||||
default:
|
||||
return `eip155:${chainId}`;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Signs a message using a Privy embedded wallet.
|
||||
* @param walletId - The ID of the wallet to use for signing.
|
||||
* @param message - The message to sign.
|
||||
* @param address - The wallet address to use for signing.
|
||||
* @returns The signature for the message.
|
||||
*/
|
||||
export const signMessage = async (walletId: string, message: string, address: string): Promise<string> => {
|
||||
try {
|
||||
const privy = getPrivyClient();
|
||||
|
||||
const {signature} = await privy.walletApi.ethereum.signMessage({
|
||||
address: address,
|
||||
chainType: 'ethereum',
|
||||
message: message,
|
||||
});
|
||||
|
||||
return signature;
|
||||
} catch (error) {
|
||||
console.error('Error signing message:', error);
|
||||
throw new Error('Failed to sign message with embedded wallet');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Approves a token for spending using Privy wallet (implementation)
|
||||
* @param walletId The wallet ID to use
|
||||
* @param walletAddress The wallet address
|
||||
* @param ticker The token ticker or enum value
|
||||
* @param amount The amount to approve (optional, defaults to max amount)
|
||||
* @param chainId The chain ID
|
||||
* @param spenderAddress The address that will be allowed to spend the tokens
|
||||
* @returns The transaction hash
|
||||
*/
|
||||
export const approveTokenImpl = async (
|
||||
walletAddress: string,
|
||||
ticker: string,
|
||||
chainId?: number,
|
||||
amount?: bigint,
|
||||
): Promise<string> => {
|
||||
try {
|
||||
|
||||
// Get token data from ticker
|
||||
const tokenData = GetToken(ticker);
|
||||
|
||||
// Create contract interface for ERC20 token
|
||||
const contractInterface = new ethers.Interface(Token.abi);
|
||||
|
||||
// Max uint256 value for unlimited approval
|
||||
const approveAmount = amount ?
|
||||
ethers.parseUnits(amount.toString(), tokenData.decimals) :
|
||||
ethers.MaxUint256;
|
||||
|
||||
// Encode the approve function call
|
||||
const data = contractInterface.encodeFunctionData("approve", [walletAddress, approveAmount]);
|
||||
|
||||
chainId = chainId ?? ARBITRUM;
|
||||
|
||||
// Get chain name in CAIP-2 format
|
||||
const networkName = getChainName(chainId);
|
||||
const privy = getPrivyClient();
|
||||
|
||||
// Send the transaction
|
||||
const { hash } = await privy.walletApi.ethereum.sendTransaction({
|
||||
address: walletAddress as Address,
|
||||
chainType: 'ethereum',
|
||||
caip2: networkName as string,
|
||||
transaction: {
|
||||
to: tokenData.address as Address,
|
||||
data: data,
|
||||
chainId: chainId,
|
||||
},
|
||||
} as any);
|
||||
|
||||
return hash;
|
||||
} catch (error) {
|
||||
console.error('Error approving token:', error);
|
||||
throw new Error(`Failed to approve token: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
};
|
||||
|
||||
function GetToken(symbol: string) {
|
||||
return TOKENS[ARBITRUM].find(token => token.symbol === symbol)
|
||||
}
|
||||
|
||||
/**
|
||||
* Approves a token for spending using Privy wallet
|
||||
* @param this The FastifyRequest instance
|
||||
* @param reply The FastifyReply instance
|
||||
* @param address The wallet address
|
||||
* @param ticker The token ticker or enum value
|
||||
* @param chainId The chain ID
|
||||
* @param amount The amount to approve (optional, defaults to max amount)
|
||||
* @returns The response object with success status and transaction hash
|
||||
*/
|
||||
export async function approveToken(
|
||||
this: FastifyRequest,
|
||||
reply: FastifyReply,
|
||||
address: string,
|
||||
ticker: string,
|
||||
chainId: number,
|
||||
amount?: bigint
|
||||
) {
|
||||
try {
|
||||
// Validate the request parameters
|
||||
tokenApprovalSchema.parse({
|
||||
address,
|
||||
ticker,
|
||||
amount,
|
||||
chainId
|
||||
});
|
||||
|
||||
if (!address) {
|
||||
throw new Error('Wallet address is required for token approval');
|
||||
}
|
||||
|
||||
if (!chainId) {
|
||||
throw new Error('Chain ID is required for token approval');
|
||||
}
|
||||
|
||||
// Call the approveTokenImpl function
|
||||
const hash = await approveTokenImpl(address, ticker, chainId, amount);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
hash: hash
|
||||
};
|
||||
} catch (error) {
|
||||
this.log.error(error);
|
||||
|
||||
// Return appropriate error response
|
||||
reply.status(error instanceof z.ZodError ? 400 : 500);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'An unknown error occurred'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs a message using Privy embedded wallet
|
||||
* @param this The FastifyRequest instance
|
||||
* @param reply The FastifyReply instance
|
||||
* @param walletId The wallet ID
|
||||
* @param message The message to sign
|
||||
* @param address The wallet address
|
||||
* @returns The signature of the signed message
|
||||
*/
|
||||
export async function signPrivyMessage(
|
||||
this: FastifyRequest,
|
||||
reply: FastifyReply,
|
||||
walletId: string,
|
||||
message: string,
|
||||
address: string
|
||||
) {
|
||||
try {
|
||||
// Validate the request parameters
|
||||
signMessageSchema.parse({ walletId, message, address });
|
||||
|
||||
// Call the signMessage function
|
||||
const signature = await signMessage(walletId, message, address);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
signature: signature
|
||||
};
|
||||
} catch (error) {
|
||||
this.log.error(error);
|
||||
|
||||
// Return appropriate error response
|
||||
reply.status(error instanceof z.ZodError ? 400 : 500);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'An unknown error occurred'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Approves a specific contract address for spending using Privy wallet
|
||||
* @param walletAddress The wallet address
|
||||
* @param tokenAddress The token address to approve
|
||||
* @param spenderAddress The contract address to approve
|
||||
* @param chainId The chain ID
|
||||
* @param amount The amount to approve (optional, defaults to max amount)
|
||||
* @returns The transaction hash
|
||||
*/
|
||||
export const approveContractImpl = async (
|
||||
walletAddress: string,
|
||||
tokenAddress: string,
|
||||
spenderAddress: string,
|
||||
chainId?: number,
|
||||
amount?: bigint,
|
||||
): Promise<string> => {
|
||||
try {
|
||||
// Create contract interface for ERC20 token
|
||||
const contractInterface = new ethers.Interface(Token.abi);
|
||||
|
||||
// Max uint256 value for unlimited approval
|
||||
const approveAmount = amount ?? ethers.MaxUint256;
|
||||
|
||||
// Encode the approve function call
|
||||
const data = contractInterface.encodeFunctionData("approve", [spenderAddress, approveAmount]);
|
||||
|
||||
chainId = chainId ?? ARBITRUM;
|
||||
|
||||
// Get chain name in CAIP-2 format
|
||||
const networkName = getChainName(chainId);
|
||||
const privy = getPrivyClient();
|
||||
|
||||
// Send the transaction
|
||||
const { hash } = await privy.walletApi.ethereum.sendTransaction({
|
||||
address: walletAddress as Address,
|
||||
chainType: 'ethereum',
|
||||
caip2: networkName as string,
|
||||
transaction: {
|
||||
to: tokenAddress as Address,
|
||||
data: data,
|
||||
chainId: chainId,
|
||||
},
|
||||
} as any);
|
||||
|
||||
return hash;
|
||||
} catch (error) {
|
||||
console.error('Error approving contract:', error);
|
||||
throw new Error(`Failed to approve contract: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the current allowance for a token for the GMX OrderVault contract
|
||||
* @param ownerAddress The address of the token owner
|
||||
* @param tokenAddress The address of the token contract
|
||||
* @returns The current allowance as a BigNumber
|
||||
*/
|
||||
export const getTokenAllowance = async (
|
||||
ownerAddress: string,
|
||||
tokenAddress: string,
|
||||
spenderAddress: string
|
||||
): Promise<bigint> => {
|
||||
try {
|
||||
// use gmx sdk to get allowance
|
||||
const sdk = await getClientForAddress(ownerAddress);
|
||||
const allowance = await sdk.executeMulticall({
|
||||
token: {
|
||||
contractAddress: tokenAddress,
|
||||
abiId: "ERC20",
|
||||
calls: {
|
||||
allowance: {
|
||||
methodName: "allowance",
|
||||
params: [ownerAddress, spenderAddress]
|
||||
}
|
||||
}
|
||||
}
|
||||
}).
|
||||
then(res => {
|
||||
return res.data.token.allowance.returnValues[0];
|
||||
});
|
||||
|
||||
return allowance;
|
||||
} catch (error) {
|
||||
console.error('Error getting token allowance:', error);
|
||||
throw new Error(`Failed to get token allowance: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes a wallet address by approving USDC for GMX trading and GMX OrderVault
|
||||
* @param address The wallet address to initialize
|
||||
* @returns The transaction hashes for both approvals
|
||||
*/
|
||||
export const initAddressImpl = async (
|
||||
address: string,
|
||||
): Promise<{ usdcHash: string, orderVaultHash: string, exchangeRouterHash: string }> => {
|
||||
try {
|
||||
const sdk = await getClientForAddress(address);
|
||||
|
||||
const {tokensData} = await sdk.tokens.getTokensData();
|
||||
const usdcTokenData = getTokenDataFromTicker(Ticker.USDC, tokensData);
|
||||
const wrapperEtherData = getTokenDataFromTicker("WETH", tokensData);
|
||||
let approveAmount = usdcTokenData.prices.maxPrice; // Large enough amount for trading
|
||||
|
||||
// Check approval for USDC
|
||||
const usdcToken = GetToken('USDC');
|
||||
const usdcAllowance = await getTokenAllowance(address, usdcToken.address, address);
|
||||
|
||||
let usdcHash = "";
|
||||
if (usdcAllowance < approveAmount) {
|
||||
// First approve USDC token for GMX trading
|
||||
const usdcToken = GetToken('USDC');
|
||||
usdcHash = await approveTokenImpl(
|
||||
address,
|
||||
usdcToken.symbol,
|
||||
ARBITRUM,
|
||||
usdcTokenData.prices.maxPrice
|
||||
);
|
||||
}else{
|
||||
usdcHash = "Already allowed :" + usdcAllowance;
|
||||
}
|
||||
|
||||
const orderVaultAllowance = await getTokenAllowance(address, usdcToken.address, CONTRACTS[ARBITRUM].OrderVault);
|
||||
|
||||
// Then approve GMX OrderVault with the correct amount
|
||||
let orderVaultHash = "";
|
||||
if (orderVaultAllowance < approveAmount) {
|
||||
orderVaultHash = await approveContractImpl(
|
||||
address,
|
||||
usdcToken.address,
|
||||
CONTRACTS[ARBITRUM].OrderVault,
|
||||
ARBITRUM,
|
||||
approveAmount
|
||||
);
|
||||
}else{
|
||||
orderVaultHash = "Already allowed :" + orderVaultAllowance;
|
||||
}
|
||||
|
||||
const wrapperEtherAllowance = await getTokenAllowance(address, wrapperEtherData.address, CONTRACTS[ARBITRUM].OrderVault);
|
||||
|
||||
let wrapperEtherHash = "";
|
||||
if (wrapperEtherAllowance < approveAmount) {
|
||||
wrapperEtherHash = await approveContractImpl(
|
||||
address,
|
||||
wrapperEtherData.address,
|
||||
CONTRACTS[ARBITRUM].OrderVault,
|
||||
ARBITRUM,
|
||||
approveAmount
|
||||
);
|
||||
}else{
|
||||
wrapperEtherHash = "Already allowed :" + wrapperEtherAllowance;
|
||||
}
|
||||
|
||||
console.log('wrapperEtherAllowance', wrapperEtherAllowance)
|
||||
|
||||
const exchangeRouterAllowance = await getTokenAllowance(address, usdcToken.address, CONTRACTS[ARBITRUM].ExchangeRouter);
|
||||
|
||||
console.log('exchangeRouterAllowance', exchangeRouterAllowance)
|
||||
|
||||
let exchangeRouterHash = "";
|
||||
if (exchangeRouterAllowance < approveAmount) {
|
||||
exchangeRouterHash = await approveContractImpl(
|
||||
address,
|
||||
usdcToken.address,
|
||||
CONTRACTS[ARBITRUM].ExchangeRouter,
|
||||
ARBITRUM,
|
||||
approveAmount
|
||||
);
|
||||
}else{
|
||||
exchangeRouterHash = "Already allowed :" + exchangeRouterAllowance;
|
||||
}
|
||||
|
||||
const wrapperEtherExchangeAllowance = await getTokenAllowance(address, wrapperEtherData.address, CONTRACTS[ARBITRUM].ExchangeRouter);
|
||||
|
||||
let wrapperEtherExchangeHash = "";
|
||||
if (wrapperEtherExchangeAllowance < approveAmount) {
|
||||
wrapperEtherExchangeHash = await approveContractImpl(
|
||||
address,
|
||||
wrapperEtherData.address,
|
||||
CONTRACTS[ARBITRUM].ExchangeRouter,
|
||||
ARBITRUM,
|
||||
approveAmount
|
||||
);
|
||||
}else{
|
||||
wrapperEtherExchangeHash = "Already allowed :" + wrapperEtherExchangeAllowance;
|
||||
}
|
||||
|
||||
console.log('wrapperEtherExchangeAllowance', wrapperEtherExchangeAllowance)
|
||||
|
||||
const usdcSyntheticRouterAllowance = await getTokenAllowance(address, usdcToken.address, CONTRACTS[ARBITRUM].SyntheticsRouter);
|
||||
|
||||
let usdcSyntheticRouterHash = "";
|
||||
if (usdcSyntheticRouterAllowance < approveAmount) {
|
||||
usdcSyntheticRouterHash = await approveContractImpl(
|
||||
address,
|
||||
usdcToken.address,
|
||||
CONTRACTS[ARBITRUM].SyntheticsRouter,
|
||||
ARBITRUM,
|
||||
approveAmount
|
||||
);
|
||||
}else{
|
||||
usdcSyntheticRouterHash = "Already allowed :" + usdcSyntheticRouterAllowance;
|
||||
}
|
||||
|
||||
console.log('usdcSyntheticRouterAllowance', usdcSyntheticRouterAllowance)
|
||||
|
||||
console.log('usdcHash', usdcHash)
|
||||
console.log('orderVaultHash', orderVaultHash)
|
||||
console.log('exchangeRouterHash', exchangeRouterHash)
|
||||
|
||||
return {
|
||||
usdcHash,
|
||||
orderVaultHash,
|
||||
exchangeRouterHash
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error initializing address:', error);
|
||||
throw new Error(`Failed to initialize address: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes a wallet address for GMX trading
|
||||
* @param this The FastifyRequest instance
|
||||
* @param reply The FastifyReply instance
|
||||
* @param address The wallet address to initialize
|
||||
* @returns The response object with success status and transaction hashes
|
||||
*/
|
||||
export async function initAddress(
|
||||
this: FastifyRequest,
|
||||
reply: FastifyReply,
|
||||
address: string
|
||||
) {
|
||||
try {
|
||||
if (!address) {
|
||||
throw new Error('Wallet address is required for initialization');
|
||||
}
|
||||
|
||||
const { usdcHash, orderVaultHash } = await initAddressImpl(address);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
usdcHash,
|
||||
orderVaultHash
|
||||
};
|
||||
} catch (error) {
|
||||
this.log.error(error);
|
||||
|
||||
reply.status(500);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'An unknown error occurred'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The use of fastify-plugin is required to be able
|
||||
* to export the decorators to the outer scope
|
||||
*
|
||||
* @see {@link https://github.com/fastify/fastify-plugin}
|
||||
*/
|
||||
export default fp(async (fastify) => {
|
||||
// Decorate request with methods that use the Fastify instance
|
||||
fastify.decorateRequest('signPrivyMessage', async function(this: FastifyRequest, reply: FastifyReply, walletId: string, message: string, address: string) {
|
||||
return signPrivyMessage.call(this, reply, walletId, message, address);
|
||||
});
|
||||
|
||||
fastify.decorateRequest('approveToken', async function(this: FastifyRequest, reply: FastifyReply, address: string, ticker: string, chainId: number, amount?: bigint) {
|
||||
return approveToken.call(this, reply, address, ticker, chainId, amount);
|
||||
});
|
||||
|
||||
fastify.decorateRequest('initAddress', async function(this: FastifyRequest, reply: FastifyReply, address: string) {
|
||||
return initAddress.call(this, reply, address);
|
||||
});
|
||||
|
||||
// Test the Privy client initialization
|
||||
try {
|
||||
const testClient = getPrivyClient(fastify);
|
||||
fastify.log.info('Privy client initialized successfully');
|
||||
} catch (error) {
|
||||
fastify.log.error('Failed to initialize Privy client:', error);
|
||||
throw error;
|
||||
}
|
||||
}, {
|
||||
name: 'privy-plugin'
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user