Add new endpoint to retrieve balance
This commit is contained in:
@@ -11,7 +11,7 @@ import {ARBITRUM} from '../../generated/gmxsdk/configs/chains.js'
|
|||||||
import {TOKENS} from '../../generated/gmxsdk/configs/tokens.js'
|
import {TOKENS} from '../../generated/gmxsdk/configs/tokens.js'
|
||||||
import {CONTRACTS} from '../../generated/gmxsdk/configs/contracts.js'
|
import {CONTRACTS} from '../../generated/gmxsdk/configs/contracts.js'
|
||||||
import {getClientForAddress, getTokenDataFromTicker} from './gmx.js'
|
import {getClientForAddress, getTokenDataFromTicker} from './gmx.js'
|
||||||
import {Ticker} from '../../generated/ManagingApiTypes.js'
|
import {Balance, Chain, Ticker} from '../../generated/ManagingApiTypes.js'
|
||||||
import {Address} from 'viem'
|
import {Address} from 'viem'
|
||||||
|
|
||||||
// Load environment variables
|
// Load environment variables
|
||||||
@@ -45,6 +45,7 @@ declare module 'fastify' {
|
|||||||
approveToken: typeof approveToken;
|
approveToken: typeof approveToken;
|
||||||
initAddress: typeof initAddress;
|
initAddress: typeof initAddress;
|
||||||
sendToken: typeof sendToken;
|
sendToken: typeof sendToken;
|
||||||
|
getWalletBalance: typeof getWalletBalance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,6 +66,10 @@ export const getPrivyClient = (fastify?: FastifyInstance): PrivyClient => {
|
|||||||
throw new Error('Missing required Privy environment variables');
|
throw new Error('Missing required Privy environment variables');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('appId', appId)
|
||||||
|
console.log('appSecret', appSecret)
|
||||||
|
console.log('authKey', authKey)
|
||||||
|
|
||||||
return new PrivyClient(
|
return new PrivyClient(
|
||||||
appId,
|
appId,
|
||||||
appSecret,
|
appSecret,
|
||||||
@@ -117,11 +122,17 @@ export function getAuthorizationSignature({url, body}: {url: string; body: objec
|
|||||||
/**
|
/**
|
||||||
* Makes a request to the Privy API with proper authentication and headers.
|
* Makes a request to the Privy API with proper authentication and headers.
|
||||||
* @param url - The full URL for the API endpoint.
|
* @param url - The full URL for the API endpoint.
|
||||||
* @param body - The request body.
|
* @param body - The request body (optional for GET requests).
|
||||||
* @param requiresAuth - Whether the request requires authentication.
|
* @param requiresAuth - Whether the request requires authentication.
|
||||||
|
* @param method - The HTTP method (defaults to POST).
|
||||||
* @returns The response data.
|
* @returns The response data.
|
||||||
*/
|
*/
|
||||||
export const makePrivyRequest = async <T>(url: string, body: object, requiresAuth = true): Promise<T> => {
|
export const makePrivyRequest = async <T>(
|
||||||
|
url: string,
|
||||||
|
body: object = {},
|
||||||
|
requiresAuth = true,
|
||||||
|
method: 'GET' | 'POST' = 'POST'
|
||||||
|
): Promise<T> => {
|
||||||
try {
|
try {
|
||||||
let headers: Record<string, string> = {
|
let headers: Record<string, string> = {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -129,31 +140,31 @@ export const makePrivyRequest = async <T>(url: string, body: object, requiresAut
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (requiresAuth) {
|
if (requiresAuth) {
|
||||||
// Generate authorization signature
|
// Create Basic Authentication header
|
||||||
const authSig = await getAuthorizationSignature({
|
const appId = process.env.PRIVY_APP_ID ?? "";
|
||||||
url,
|
const appSecret = process.env.PRIVY_APP_SECRET ?? "";
|
||||||
body
|
const basicAuthString = `${appId}:${appSecret}`;
|
||||||
});
|
|
||||||
|
|
||||||
// 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');
|
const base64Auth = Buffer.from(basicAuthString).toString('base64');
|
||||||
|
|
||||||
headers = {
|
headers.Authorization = `Basic ${base64Auth}`;
|
||||||
...headers,
|
|
||||||
'privy-authorization-signature': authSig,
|
|
||||||
'Authorization': `Basic ${base64Auth}`
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const request = {
|
const requestInit: RequestInit = {
|
||||||
method: 'POST',
|
method: method,
|
||||||
headers,
|
headers,
|
||||||
body: JSON.stringify(body)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Only add body for non-GET requests
|
||||||
|
if (method !== 'GET') {
|
||||||
|
requestInit.body = JSON.stringify(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('url', url)
|
||||||
|
|
||||||
|
console.log('requestInit', requestInit)
|
||||||
|
|
||||||
// Make the API request
|
// Make the API request
|
||||||
const response = await fetch(url, request).then(res => {
|
const response = await fetch(url, requestInit).then(res => {
|
||||||
return res;
|
return res;
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.log("error", err);
|
console.log("error", err);
|
||||||
@@ -196,6 +207,13 @@ const tokenSendSchema = z.object({
|
|||||||
chainId: z.number().positive().optional()
|
chainId: z.number().positive().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Schema for wallet balance request
|
||||||
|
const walletBalanceSchema = z.object({
|
||||||
|
address: z.string().nonempty(),
|
||||||
|
assets: z.array(z.nativeEnum(Ticker)).min(1), // Required: array of valid Ticker enum values
|
||||||
|
chains: z.array(z.string().nonempty()).min(1) // Required: array of chain names
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the chain name based on chain ID
|
* Gets the chain name based on chain ID
|
||||||
* @param chainId The chain ID
|
* @param chainId The chain ID
|
||||||
@@ -732,6 +750,228 @@ export const sendTokenImpl = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the wallet balance for a specific address using Privy API
|
||||||
|
* @param address The wallet address to get balance for
|
||||||
|
* @param assets Array of assets to filter by using Ticker enum (e.g. [Ticker.USDC, Ticker.ETH])
|
||||||
|
* @param chains Array of chain names to filter by (e.g. ['arbitrum', 'ethereum'])
|
||||||
|
* @returns Array of Balance objects
|
||||||
|
*/
|
||||||
|
export const getWalletBalanceImpl = async (
|
||||||
|
address: string,
|
||||||
|
assets: Ticker[],
|
||||||
|
chains: string[]
|
||||||
|
): Promise<Balance[]> => {
|
||||||
|
try {
|
||||||
|
// First, get the user by wallet address using Privy Client
|
||||||
|
const privy = getPrivyClient();
|
||||||
|
|
||||||
|
// Get user by wallet address
|
||||||
|
const user = await privy.getUserByWalletAddress(address);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new Error(`User not found for wallet address: ${address}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the embedded wallet for this address
|
||||||
|
const embeddedWallet = user.linkedAccounts.find(
|
||||||
|
(account: any) =>
|
||||||
|
account.type === 'wallet' &&
|
||||||
|
account.chainType === 'ethereum' &&
|
||||||
|
account.address?.toLowerCase() === address.toLowerCase()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!embeddedWallet) {
|
||||||
|
throw new Error(`Embedded wallet not found for address: ${address}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract wallet ID from the embedded wallet
|
||||||
|
const walletId = (embeddedWallet as any).walletId || (embeddedWallet as any).id;
|
||||||
|
|
||||||
|
if (!walletId) {
|
||||||
|
throw new Error(`Wallet ID not found for embedded wallet`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make authenticated request to get wallet balance
|
||||||
|
let balanceUrl = `https://auth.privy.io/api/v1/wallets/${walletId}/balance`;
|
||||||
|
|
||||||
|
// Add required query parameters for multiple assets and chains
|
||||||
|
const queryParams = new URLSearchParams();
|
||||||
|
|
||||||
|
// Add multiple asset parameters
|
||||||
|
assets.forEach(asset => {
|
||||||
|
queryParams.append('asset', asset.toLowerCase()); // Convert Ticker enum to lowercase
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add multiple chain parameters
|
||||||
|
chains.forEach(chain => {
|
||||||
|
queryParams.append('chain', chain);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add include_currency parameter for USD pricing
|
||||||
|
queryParams.append('include_currency', 'usd');
|
||||||
|
|
||||||
|
balanceUrl += `?${queryParams.toString()}`;
|
||||||
|
const balanceResponse = await makePrivyRequest<{
|
||||||
|
balances: Array<{
|
||||||
|
chain: string;
|
||||||
|
asset: string;
|
||||||
|
raw_value: string;
|
||||||
|
raw_value_decimals: number;
|
||||||
|
display_values: {
|
||||||
|
[key: string]: string;
|
||||||
|
usd: string;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
}>(balanceUrl, {}, true, 'GET');
|
||||||
|
|
||||||
|
console.log('balanceResponse', balanceResponse)
|
||||||
|
|
||||||
|
// Convert to Balance interface format (matching ManagingApiTypes.ts)
|
||||||
|
const balances: Balance[] = balanceResponse.balances
|
||||||
|
.map(balance => {
|
||||||
|
// Parse the raw value using decimals
|
||||||
|
const amount = parseFloat(balance.raw_value) / Math.pow(10, balance.raw_value_decimals);
|
||||||
|
|
||||||
|
// Get USD price from display_values
|
||||||
|
const usdValue = parseFloat(balance.display_values.usd);
|
||||||
|
|
||||||
|
// Calculate price per token (if amount > 0)
|
||||||
|
const price = amount > 0 ? usdValue / amount : 0;
|
||||||
|
|
||||||
|
// Get chain ID from chain name
|
||||||
|
const chainId = getChainIdFromName(balance.chain);
|
||||||
|
const caip2 = getChainName(chainId);
|
||||||
|
|
||||||
|
const chain: Chain = {
|
||||||
|
id: caip2,
|
||||||
|
rpcUrl: undefined,
|
||||||
|
name: getChainNameFromCaip2(caip2),
|
||||||
|
chainId: chainId
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
tokenImage: '', // Not provided in the response
|
||||||
|
tokenName: balance.asset.toUpperCase(), // Use asset name (usdc -> USDC)
|
||||||
|
amount: amount,
|
||||||
|
price: price,
|
||||||
|
value: usdValue,
|
||||||
|
tokenAdress: '', // Not provided in this response format
|
||||||
|
chain: chain
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('balances', balances)
|
||||||
|
|
||||||
|
return balances;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting wallet balance:', error);
|
||||||
|
throw new Error(`Failed to get wallet balance: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to get chain name from CAIP-2 identifier
|
||||||
|
* @param caip2 The CAIP-2 identifier
|
||||||
|
* @returns Human readable chain name
|
||||||
|
*/
|
||||||
|
const getChainNameFromCaip2 = (caip2: string): string => {
|
||||||
|
switch (caip2) {
|
||||||
|
case 'eip155:1':
|
||||||
|
return 'Ethereum Mainnet';
|
||||||
|
case 'eip155:42161':
|
||||||
|
return 'Arbitrum One';
|
||||||
|
case 'eip155:421613':
|
||||||
|
return 'Arbitrum Goerli';
|
||||||
|
case 'eip155:8453':
|
||||||
|
return 'Base';
|
||||||
|
case 'eip155:84531':
|
||||||
|
return 'Base Goerli';
|
||||||
|
default:
|
||||||
|
return `Chain ${caip2}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to get chain ID from chain name
|
||||||
|
* @param chainName The chain name (e.g., 'arbitrum', 'ethereum', 'base')
|
||||||
|
* @returns The corresponding chain ID
|
||||||
|
*/
|
||||||
|
const getChainIdFromName = (chainName: string): number => {
|
||||||
|
switch (chainName.toLowerCase()) {
|
||||||
|
case 'ethereum':
|
||||||
|
case 'mainnet':
|
||||||
|
return 1;
|
||||||
|
case 'arbitrum':
|
||||||
|
case 'arbitrum-one':
|
||||||
|
return 42161;
|
||||||
|
case 'arbitrum-goerli':
|
||||||
|
return 421613;
|
||||||
|
case 'base':
|
||||||
|
return 8453;
|
||||||
|
case 'base-goerli':
|
||||||
|
return 84531;
|
||||||
|
default:
|
||||||
|
// Default to Arbitrum if unknown chain
|
||||||
|
return 42161;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets wallet balance using Privy embedded wallet
|
||||||
|
* @param this The FastifyRequest instance
|
||||||
|
* @param reply The FastifyReply instance
|
||||||
|
* @param address The wallet address to get balance for
|
||||||
|
* @param assets Array of assets to filter by using Ticker enum (e.g. [Ticker.USDC, Ticker.ETH])
|
||||||
|
* @param chains Array of chain names to filter by (e.g. ['arbitrum', 'ethereum'])
|
||||||
|
* @returns The response object with success status and balances array
|
||||||
|
*/
|
||||||
|
export async function getWalletBalance(
|
||||||
|
this: FastifyRequest,
|
||||||
|
reply: FastifyReply,
|
||||||
|
address: string,
|
||||||
|
assets: Ticker[],
|
||||||
|
chains: string[]
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
// Validate the request parameters
|
||||||
|
walletBalanceSchema.parse({
|
||||||
|
address,
|
||||||
|
assets,
|
||||||
|
chains
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!address) {
|
||||||
|
throw new Error('Wallet address is required for balance retrieval');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!assets || assets.length === 0) {
|
||||||
|
throw new Error('At least one asset is required for balance retrieval');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chains || chains.length === 0) {
|
||||||
|
throw new Error('At least one chain is required for balance retrieval');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the getWalletBalanceImpl function
|
||||||
|
const balances = await getWalletBalanceImpl(address, assets, chains);
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
balances: balances
|
||||||
|
};
|
||||||
|
} 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'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends tokens from one address to another using Privy wallet
|
* Sends tokens from one address to another using Privy wallet
|
||||||
* @param this The FastifyRequest instance
|
* @param this The FastifyRequest instance
|
||||||
@@ -821,6 +1061,10 @@ export default fp(async (fastify) => {
|
|||||||
return sendToken.call(this, reply, senderAddress, recipientAddress, ticker, amount, chainId);
|
return sendToken.call(this, reply, senderAddress, recipientAddress, ticker, amount, chainId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
fastify.decorateRequest('getWalletBalance', async function(this: FastifyRequest, reply: FastifyReply, address: string, assets: Ticker[], chains: string[]) {
|
||||||
|
return getWalletBalance.call(this, reply, address, assets, chains);
|
||||||
|
});
|
||||||
|
|
||||||
// Test the Privy client initialization
|
// Test the Privy client initialization
|
||||||
try {
|
try {
|
||||||
const testClient = getPrivyClient(fastify);
|
const testClient = getPrivyClient(fastify);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {FastifyPluginAsyncTypebox, Type} from '@fastify/type-provider-typebox'
|
|||||||
import {handleError} from '../../../utils/errorHandler.js'
|
import {handleError} from '../../../utils/errorHandler.js'
|
||||||
import {TOKENS} from '../../../generated/gmxsdk/configs/tokens.js'
|
import {TOKENS} from '../../../generated/gmxsdk/configs/tokens.js'
|
||||||
import {ARBITRUM} from '../../../generated/gmxsdk/configs/chains.js'
|
import {ARBITRUM} from '../../../generated/gmxsdk/configs/chains.js'
|
||||||
|
import {Ticker} from '../../../generated/ManagingApiTypes.js'
|
||||||
|
|
||||||
const plugin: FastifyPluginAsyncTypebox = async (fastify) => {
|
const plugin: FastifyPluginAsyncTypebox = async (fastify) => {
|
||||||
fastify.post(
|
fastify.post(
|
||||||
@@ -147,6 +148,64 @@ const plugin: FastifyPluginAsyncTypebox = async (fastify) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fastify.get(
|
||||||
|
'/wallet-balance',
|
||||||
|
{
|
||||||
|
schema: {
|
||||||
|
querystring: Type.Object({
|
||||||
|
address: Type.String(),
|
||||||
|
asset: Type.Union([Type.String(), Type.Array(Type.String())]), // Can be single or array
|
||||||
|
chain: Type.Union([Type.String(), Type.Array(Type.String())]) // Can be single or array
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: Type.Object({
|
||||||
|
success: Type.Boolean(),
|
||||||
|
balances: Type.Optional(Type.Array(Type.Object({
|
||||||
|
tokenImage: Type.Optional(Type.String()),
|
||||||
|
tokenName: Type.Optional(Type.String()),
|
||||||
|
amount: Type.Optional(Type.Number()),
|
||||||
|
price: Type.Optional(Type.Number()),
|
||||||
|
value: Type.Optional(Type.Number()),
|
||||||
|
tokenAdress: Type.Optional(Type.String()),
|
||||||
|
chain: Type.Optional(Type.Object({
|
||||||
|
id: Type.Optional(Type.String()),
|
||||||
|
rpcUrl: Type.Optional(Type.String()),
|
||||||
|
name: Type.Optional(Type.String()),
|
||||||
|
chainId: Type.Optional(Type.Number())
|
||||||
|
}))
|
||||||
|
}))),
|
||||||
|
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, asset, chain } = request.query;
|
||||||
|
|
||||||
|
// Convert single values to arrays if needed
|
||||||
|
const assetsArray = Array.isArray(asset) ? asset : [asset];
|
||||||
|
const chainsArray = Array.isArray(chain) ? chain : [chain];
|
||||||
|
|
||||||
|
// Convert asset strings to Ticker enums
|
||||||
|
const assetEnums = assetsArray.map(a => a as Ticker);
|
||||||
|
|
||||||
|
return await request.getWalletBalance(reply, address, assetEnums, chainsArray);
|
||||||
|
} catch (error) {
|
||||||
|
return handleError(request, reply, error, 'privy/wallet-balance');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default plugin
|
export default plugin
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import {test} from 'node:test'
|
||||||
|
import assert from 'node:assert'
|
||||||
|
import {getWalletBalanceImpl} from '../../src/plugins/custom/privy.js'
|
||||||
|
import {Ticker} from '../../src/generated/ManagingApiTypes.js'
|
||||||
|
|
||||||
|
test('getWalletBalanceImpl should fetch wallet balance for valid address', async () => {
|
||||||
|
const testWalletId = 'cm7vxs99f0007blcl8cmzv74t'
|
||||||
|
// Note: Replace with actual wallet address associated with the wallet ID from Privy dashboard
|
||||||
|
const testAddress = '0x932167388dD9aad41149b3cA23eBD489E2E2DD78'
|
||||||
|
const assets = [Ticker.USDC, Ticker.ETH] // Required: array of assets using Ticker enum
|
||||||
|
const chains = ['arbitrum', 'ethereum'] // Required: array of chains
|
||||||
|
|
||||||
|
try {
|
||||||
|
const balances = await getWalletBalanceImpl(testAddress, assets, chains)
|
||||||
|
|
||||||
|
// Verify the response structure
|
||||||
|
assert.ok(Array.isArray(balances), 'Should return an array of balances')
|
||||||
|
|
||||||
|
// If balances exist, verify their structure
|
||||||
|
if (balances.length > 0) {
|
||||||
|
const balance = balances[0]
|
||||||
|
|
||||||
|
// Verify Balance interface properties (all optional)
|
||||||
|
if (balance.tokenName !== undefined) {
|
||||||
|
assert.equal(typeof balance.tokenName, 'string', 'tokenName should be a string')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (balance.amount !== undefined) {
|
||||||
|
assert.equal(typeof balance.amount, 'number', 'amount should be a number')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (balance.tokenAdress !== undefined) {
|
||||||
|
assert.equal(typeof balance.tokenAdress, 'string', 'tokenAdress should be a string')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (balance.chain !== undefined && balance.chain !== null) {
|
||||||
|
assert.equal(typeof balance.chain, 'object', 'chain should be an object')
|
||||||
|
|
||||||
|
if (balance.chain.chainId !== undefined) {
|
||||||
|
// Chain ID should be either 42161 (Arbitrum) or 1 (Ethereum)
|
||||||
|
const validChainIds = [42161, 1];
|
||||||
|
assert.ok(validChainIds.includes(balance.chain.chainId), `chain.chainId should be one of ${validChainIds.join(', ')}, got ${balance.chain.chainId}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (balance.chain.name !== undefined) {
|
||||||
|
assert.equal(typeof balance.chain.name, 'string', 'chain.name should be a string')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`✓ Found ${balances.length} balance(s) for wallet ${testWalletId}`)
|
||||||
|
console.log('Sample balance:', JSON.stringify(balances[0], null, 2))
|
||||||
|
} else {
|
||||||
|
console.log(`✓ No balances found for wallet ${testWalletId} on chains ${chains.join(', ')}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
// If this is a real integration test, we might expect certain errors
|
||||||
|
console.log('Error details:', error.message)
|
||||||
|
|
||||||
|
// Common expected errors during testing:
|
||||||
|
if (error.message.includes('User not found for wallet address')) {
|
||||||
|
console.log('✓ Expected error: Wallet address not found in Privy (use real address from dashboard)')
|
||||||
|
} else if (error.message.includes('Failed to get wallet balance')) {
|
||||||
|
console.log('✓ Expected error: API call failed (check credentials and network)')
|
||||||
|
} else {
|
||||||
|
// Re-throw unexpected errors
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -3,18 +3,12 @@ import privyPlugin, {getAuthorizationSignature} from '../../src/plugins/custom/p
|
|||||||
import assert from 'node:assert'
|
import assert from 'node:assert'
|
||||||
import test from 'node:test'
|
import test from 'node:test'
|
||||||
|
|
||||||
// Set environment variables needed for the test
|
|
||||||
process.env.PRIVY_APP_ID = 'cm4db8x9t000ccn87pctvcg9j'
|
|
||||||
process.env.PRIVY_APP_SECRET = 'test-secret'
|
|
||||||
process.env.PRIVY_AUTHORIZATION_KEY = 'wallet-auth:MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgqOBE+hZld+PCaj051uOl0XpEwe3tKBC5tsYsKdnPymGhRANCAAQ2HyYUbLRcfj9obpViwjYU/S7FdNUehkcfjYdd+R2gH/1q0ZJx7mOF1zpiEbbBNRLuXzP0NPN6nonkI8umzLXZ'
|
|
||||||
|
|
||||||
test('getAuthorizationSignature generates valid signatures', async () => {
|
test('getAuthorizationSignature generates valid signatures', async () => {
|
||||||
const url = 'https://api.privy.io/v1/wallets'
|
const url = 'https://api.privy.io/v1/wallets'
|
||||||
const body = { chain_type: 'ethereum' }
|
const body = { chain_type: 'ethereum' }
|
||||||
|
|
||||||
const signature = getAuthorizationSignature({ url, body })
|
const signature = getAuthorizationSignature({ url, body })
|
||||||
|
|
||||||
// Basic validation - check if it's a non-empty string that looks like a base64 value
|
|
||||||
assert.ok(signature && typeof signature === 'string', 'Signature should be a string')
|
assert.ok(signature && typeof signature === 'string', 'Signature should be a string')
|
||||||
assert.ok(signature.length > 0, 'Signature should not be empty')
|
assert.ok(signature.length > 0, 'Signature should not be empty')
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user