diff --git a/packages/app/control/package.json b/packages/app/control/package.json index eaee75d2a..2c6f21f32 100644 --- a/packages/app/control/package.json +++ b/packages/app/control/package.json @@ -37,6 +37,8 @@ "dependencies": { "@auth/core": "^0.40.0", "@auth/prisma-adapter": "^2.10.0", + "@coinbase/cdp-sdk": "^1.34.0", + "@coinbase/x402": "^0.6.4", "@hookform/resolvers": "^5.2.1", "@icons-pack/react-simple-icons": "^13.7.0", "@merit-systems/sdk": "0.0.8", @@ -88,7 +90,6 @@ "autonumeric": "^4.10.8", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "@coinbase/x402": "^0.6.4", "cors": "^2.8.5", "date-fns": "^4.1.0", "dotenv": "^16.4.5", @@ -137,9 +138,9 @@ "devDependencies": { "@eslint/eslintrc": "^3", "@faker-js/faker": "^9.9.0", + "@next/eslint-plugin-next": "^15.5.3", "@types/cors": "^2.8.17", "@types/express": "^5.0.0", - "@next/eslint-plugin-next": "^15.5.3", "@types/node": "^20", "@types/react": "19.1.10", "@types/react-dom": "19.1.7", diff --git a/packages/app/control/prisma/migrations/20251027210418_x402_transactions/migration.sql b/packages/app/control/prisma/migrations/20251027210418_x402_transactions/migration.sql new file mode 100644 index 000000000..158045262 --- /dev/null +++ b/packages/app/control/prisma/migrations/20251027210418_x402_transactions/migration.sql @@ -0,0 +1,13 @@ +-- CreateEnum +CREATE TYPE "EnumTransactionType" AS ENUM ('X402', 'BALANCE'); + +-- DropForeignKey +ALTER TABLE "public"."transactions" DROP CONSTRAINT "transactions_echoAppId_fkey"; + +-- AlterTable +ALTER TABLE "transactions" ADD COLUMN "transactionType" "EnumTransactionType" NOT NULL DEFAULT 'BALANCE', +ALTER COLUMN "userId" DROP NOT NULL, +ALTER COLUMN "echoAppId" DROP NOT NULL; + +-- AddForeignKey +ALTER TABLE "transactions" ADD CONSTRAINT "transactions_echoAppId_fkey" FOREIGN KEY ("echoAppId") REFERENCES "echo_apps"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/packages/app/control/prisma/migrations/20251028204423_add_echo_profit/migration.sql b/packages/app/control/prisma/migrations/20251028204423_add_echo_profit/migration.sql new file mode 100644 index 000000000..3910cd516 --- /dev/null +++ b/packages/app/control/prisma/migrations/20251028204423_add_echo_profit/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "transactions" ADD COLUMN "echoProfit" DECIMAL(65,14) NOT NULL DEFAULT 0.0; diff --git a/packages/app/control/prisma/migrations/20251029185610_echo_profit_payout/migration.sql b/packages/app/control/prisma/migrations/20251029185610_echo_profit_payout/migration.sql new file mode 100644 index 000000000..0b97c9e8c --- /dev/null +++ b/packages/app/control/prisma/migrations/20251029185610_echo_profit_payout/migration.sql @@ -0,0 +1,2 @@ +-- AlterEnum +ALTER TYPE "EnumPayoutType" ADD VALUE 'ECHO_PROFIT'; diff --git a/packages/app/control/prisma/migrations/20251029193331_app_profit/migration.sql b/packages/app/control/prisma/migrations/20251029193331_app_profit/migration.sql new file mode 100644 index 000000000..31772870f --- /dev/null +++ b/packages/app/control/prisma/migrations/20251029193331_app_profit/migration.sql @@ -0,0 +1,2 @@ +-- AlterEnum +ALTER TYPE "EnumPayoutType" ADD VALUE 'APP_PROFIT'; diff --git a/packages/app/control/prisma/schema.prisma b/packages/app/control/prisma/schema.prisma index 1978962bc..b18061082 100644 --- a/packages/app/control/prisma/schema.prisma +++ b/packages/app/control/prisma/schema.prisma @@ -231,6 +231,8 @@ enum EnumPayoutStatus { enum EnumPayoutType { MARKUP REFERRAL + ECHO_PROFIT + APP_PROFIT } model Payout { @@ -301,11 +303,17 @@ enum EnumPaymentSource { balance } +enum EnumTransactionType { + X402 + BALANCE +} + model Transaction { id String @id @default(uuid()) @db.Uuid transactionMetadataId String? @db.Uuid totalCost Decimal @default(0.0) @db.Decimal(65, 14) appProfit Decimal @default(0.0) @db.Decimal(65, 14) + echoProfit Decimal @default(0.0) @db.Decimal(65, 14) markUpProfit Decimal @default(0.0) @db.Decimal(65, 14) referralProfit Decimal @default(0.0) @db.Decimal(65, 14) rawTransactionCost Decimal @default(0.0) @db.Decimal(65, 14) @@ -313,8 +321,9 @@ model Transaction { isArchived Boolean @default(false) archivedAt DateTime? @db.Timestamptz(6) createdAt DateTime @default(now()) @db.Timestamptz(6) - userId String @db.Uuid - echoAppId String @db.Uuid + transactionType EnumTransactionType @default(BALANCE) + userId String? @db.Uuid + echoAppId String? @db.Uuid apiKeyId String? @db.Uuid markUpId String? @db.Uuid spendPoolId String? @db.Uuid @@ -322,11 +331,11 @@ model Transaction { referralCodeId String? @db.Uuid referrerRewardId String? @db.Uuid apiKey ApiKey? @relation(fields: [apiKeyId], references: [id], onDelete: Cascade) - echoApp EchoApp @relation(fields: [echoAppId], references: [id]) + echoApp EchoApp? @relation(fields: [echoAppId], references: [id]) markUp MarkUp? @relation(fields: [markUpId], references: [id]) spendPool SpendPool? @relation(fields: [spendPoolId], references: [id]) transactionMetadata TransactionMetadata? @relation(fields: [transactionMetadataId], references: [id]) - user User @relation(fields: [userId], references: [id], onDelete: Cascade) + user User? @relation(fields: [userId], references: [id], onDelete: Cascade) userSpendPoolUsage UserSpendPoolUsage? @relation(fields: [userSpendPoolUsageId], references: [id]) referralCode ReferralCode? @relation(fields: [referralCodeId], references: [id]) referrerReward ReferralReward? @relation(fields: [referrerRewardId], references: [id]) @@ -506,4 +515,4 @@ model VideoGenerationX402 { user User? @relation(fields: [userId], references: [id], onDelete: Cascade) echoApp EchoApp? @relation(fields: [echoAppId], references: [id], onDelete: Cascade) @@map("video_generation_x402") -} +} \ No newline at end of file diff --git a/packages/app/control/src/app/(app)/admin/_components/wallet/AppX402ProfitTotal.tsx b/packages/app/control/src/app/(app)/admin/_components/wallet/AppX402ProfitTotal.tsx new file mode 100644 index 000000000..0ac7eefc2 --- /dev/null +++ b/packages/app/control/src/app/(app)/admin/_components/wallet/AppX402ProfitTotal.tsx @@ -0,0 +1,225 @@ +'use client'; + +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from '@/components/ui/card'; +import { Skeleton } from '@/components/ui/skeleton'; +import { Button } from '@/components/ui/button'; +import { api } from '@/trpc/client'; +import { toast } from 'sonner'; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '@/components/ui/table'; +import { useState } from 'react'; + +export function AppX402ProfitTotal() { + const [processingAppIds, setProcessingAppIds] = useState>( + new Set() + ); + const [isSendingAll, setIsSendingAll] = useState(false); + const utils = api.useUtils(); + + const { data: totalProfit, isLoading: isTotalLoading } = + api.admin.wallet.getX402AppProfit.useQuery(); + + const { data: appBreakdown, isLoading: isBreakdownLoading } = + api.admin.wallet.getX402AppProfitByApp.useQuery(); + + const payoutMutation = api.admin.wallet.payoutX402AppProfit.useMutation({ + onSuccess: data => { + toast.success('Payout successful!', { + description: `Sent to ECHO_PAYOUTS. Tx: ${data.userOpHash.slice(0, 10)}...`, + }); + void utils.admin.wallet.getX402AppProfit.invalidate(); + void utils.admin.wallet.getX402AppProfitByApp.invalidate(); + }, + onError: error => { + toast.error('Payout failed', { + description: error.message, + }); + }, + onSettled: (data, error, variables) => { + setProcessingAppIds(prev => { + const next = new Set(prev); + next.delete(variables.appId); + return next; + }); + }, + }); + + const handlePayout = (appId: string, amount: number) => { + if (amount <= 0) { + toast.error('Invalid amount', { + description: 'Amount must be greater than 0', + }); + return; + } + + setProcessingAppIds(prev => new Set(prev).add(appId)); + payoutMutation.mutate({ appId, amount }); + }; + + const handleSendAll = async () => { + if (!appBreakdown || appBreakdown.length === 0) { + toast.error('No apps to payout'); + return; + } + + const appsWithProfit = appBreakdown.filter(app => app.remainingProfit > 0); + + if (appsWithProfit.length === 0) { + toast.error('No apps with remaining profit'); + return; + } + + setIsSendingAll(true); + + for (const app of appsWithProfit) { + setProcessingAppIds(prev => new Set(prev).add(app.appId)); + payoutMutation.mutate({ + appId: app.appId, + amount: app.remainingProfit, + }); + } + + setIsSendingAll(false); + }; + + return ( +
+ + + App X402 Profit Total + + Total unclaimed profit generated by apps from X402 transactions + + + + +
+

+ Total App Profit +

+ {isTotalLoading ? ( + + ) : ( +
+ ${(totalProfit ?? 0).toFixed(6)} +
+ )} +

+ Available for payout to applications +

+
+ +

+ The App Profit represents the sum of all appProfit from X402 + transactions minus any payouts already made to applications. +

+
+
+ + + +
+
+ Profit Breakdown by Application + + X402 profit generated by each application + +
+ {appBreakdown && + appBreakdown.some(app => app.remainingProfit > 0) && ( + + )} +
+
+ + {isBreakdownLoading ? ( +
+ + + +
+ ) : !appBreakdown || appBreakdown.length === 0 ? ( +

+ No app profit data available +

+ ) : ( +
+ + + + Application + Total Profit + Total Payouts + Remaining + Actions + + + + {appBreakdown.map(app => ( + + + {app.appName} + + + ${app.totalProfit.toFixed(6)} + + + ${app.totalPayouts.toFixed(6)} + + + 0 + ? 'text-green-600 font-semibold' + : '' + } + > + ${app.remainingProfit.toFixed(6)} + + + + + + + ))} + +
+
+ )} +
+
+
+ ); +} diff --git a/packages/app/control/src/app/(app)/admin/_components/wallet/EchoX402ProfitTotal.tsx b/packages/app/control/src/app/(app)/admin/_components/wallet/EchoX402ProfitTotal.tsx new file mode 100644 index 000000000..e9078a5cd --- /dev/null +++ b/packages/app/control/src/app/(app)/admin/_components/wallet/EchoX402ProfitTotal.tsx @@ -0,0 +1,254 @@ +'use client'; + +import { Button } from '@/components/ui/button'; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from '@/components/ui/card'; +import { Skeleton } from '@/components/ui/skeleton'; +import { api } from '@/trpc/client'; +import { toast } from 'sonner'; +import { formatDistance } from 'date-fns'; + +export function EchoX402ProfitTotal() { + const { + data: profitData, + isLoading: isProfitLoading, + refetch: refetchProfit, + isFetching: isProfitFetching, + } = api.admin.wallet.getEchoX402ProfitTotal.useQuery(); + + const { data: walletAddress, isLoading: isAddressLoading } = + api.admin.wallet.getSmartAccountAddress.useQuery(); + + const { + data: usdcBalance, + isLoading: isUSDCLoading, + refetch: refetchUSDC, + isFetching: isUSDCFetching, + } = api.admin.wallet.getSmartAccountUSDCBalance.useQuery(); + + const { + data: ethBalance, + isLoading: isETHLoading, + refetch: refetchETH, + isFetching: isETHFetching, + } = api.admin.wallet.getSmartAccountETHBalance.useQuery(); + + const { + data: payoutHistory, + isLoading: isHistoryLoading, + refetch: refetchHistory, + } = api.admin.wallet.getEchoPayoutHistory.useQuery(); + + const fundRepoMutation = api.admin.wallet.fundEchoRepo.useMutation({ + onSuccess: data => { + toast.success('Successfully funded repo!', { + description: `Transaction hash: ${data.userOpHash}`, + }); + void refetchProfit(); + void refetchUSDC(); + void refetchETH(); + void refetchHistory(); + }, + onError: error => { + toast.error('Failed to fund repo', { + description: error.message, + }); + }, + }); + + const isAnyFetching = isProfitFetching || isUSDCFetching || isETHFetching; + + const handleRefresh = () => { + void refetchProfit(); + void refetchUSDC(); + void refetchETH(); + void refetchHistory(); + }; + + const handleFundRepo = () => { + if (!profitData || profitData <= 0) { + toast.error('Invalid amount', { + description: 'Profit total must be greater than 0', + }); + return; + } + + fundRepoMutation.mutate({ amount: profitData }); + }; + + return ( +
+ + +
+ Echo X402 Wallet +
+ {isAnyFetching && ( + + Refreshing… + + )} + +
+
+ Smart Account Wallet Status +
+ + +
+

+ Wallet Address +

+ {isAddressLoading ? ( + + ) : ( + + {walletAddress} + + )} +
+ +
+
+

+ Total Echo Profit +

+ {isProfitLoading ? ( + + ) : ( +
+ ${(profitData ?? 0).toFixed(6)} +
+ )} +

+ From X402 transactions +

+
+ +
+

+ USDC Balance +

+ {isUSDCLoading ? ( + + ) : ( +
+ ${(usdcBalance ?? 0).toFixed(6)} +
+ )} +

USDC on Base

+
+ +
+

+ ETH Balance +

+ {isETHLoading ? ( + + ) : ( +
+ {(ethBalance ?? 0).toFixed(6)} ETH +
+ )} +

+ Native ETH on Base +

+
+
+ +

+ The Echo Profit represents the sum of all echoProfit from X402 + transactions that should be deposited into the Echo repository. +

+
+ + + + +
+ + + + Payout History + + Recent Echo payouts to the Merit repository + + + + {isHistoryLoading ? ( +
+ + + +
+ ) : !payoutHistory || payoutHistory.length === 0 ? ( +

+ No payout history yet +

+ ) : ( +
+ + + + + + + + + + {payoutHistory.map(payout => ( + + + + + + ))} + +
+ Date + + Amount + + Status +
+ {formatDistance( + new Date(payout.createdAt), + new Date(), + { addSuffix: true } + )} + + ${Number(payout.amount).toFixed(6)} + + + {payout.status} + +
+
+ )} +
+
+
+ ); +} diff --git a/packages/app/control/src/app/(app)/admin/_components/wallet/X402TransactionCostTotal.tsx b/packages/app/control/src/app/(app)/admin/_components/wallet/X402TransactionCostTotal.tsx new file mode 100644 index 000000000..5b145f4c3 --- /dev/null +++ b/packages/app/control/src/app/(app)/admin/_components/wallet/X402TransactionCostTotal.tsx @@ -0,0 +1,45 @@ +'use client'; + +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from '@/components/ui/card'; +import { Skeleton } from '@/components/ui/skeleton'; +import { api } from '@/trpc/client'; + +export function X402TransactionCostTotal() { + const { data: totalCost, isLoading } = + api.admin.wallet.getX402RawTransactionCostTotal.useQuery(); + + return ( + + + X402 Transaction Costs + + Total raw transaction costs for X402 transactions + + + + +
+

+ Total Raw Transaction Cost +

+ {isLoading ? ( + + ) : ( +
+ ${(totalCost ?? 0).toFixed(6)} +
+ )} +

+ Sum of inference costs from X402 transactions +

+
+
+
+ ); +} diff --git a/packages/app/control/src/app/(app)/admin/layout.tsx b/packages/app/control/src/app/(app)/admin/layout.tsx index cdd7ecab7..ede020614 100644 --- a/packages/app/control/src/app/(app)/admin/layout.tsx +++ b/packages/app/control/src/app/(app)/admin/layout.tsx @@ -30,6 +30,10 @@ export default async function AdminLayout({ children }: LayoutProps<'/admin'>) { label: 'Payouts', href: '/admin/payouts', }, + { + label: 'X402 Payouts', + href: '/admin/x402-payouts', + }, { label: 'Credit Grants', href: '/admin/credit-grants', diff --git a/packages/app/control/src/app/(app)/admin/x402-payouts/page.tsx b/packages/app/control/src/app/(app)/admin/x402-payouts/page.tsx new file mode 100644 index 000000000..a5e7518bd --- /dev/null +++ b/packages/app/control/src/app/(app)/admin/x402-payouts/page.tsx @@ -0,0 +1,20 @@ +import TableLayout from '../_components/TableLayout'; +import { EchoX402ProfitTotal } from '../_components/wallet/EchoX402ProfitTotal'; +import { AppX402ProfitTotal } from '../_components/wallet/AppX402ProfitTotal'; +import { X402TransactionCostTotal } from '../_components/wallet/X402TransactionCostTotal'; + +export default function X402PayoutsPage() { + return ( + + <> +

+ On this page, you can claim the Echo Payouts from X402 transactions + and manage the distribution of funds. +

+ + + + +
+ ); +} diff --git a/packages/app/control/src/app/(app)/app/[id]/(overview)/_components/overview/activity/charts.tsx b/packages/app/control/src/app/(app)/app/[id]/(overview)/_components/overview/activity/charts.tsx index 066b150e7..805a47e57 100644 --- a/packages/app/control/src/app/(app)/app/[id]/(overview)/_components/overview/activity/charts.tsx +++ b/packages/app/control/src/app/(app)/app/[id]/(overview)/_components/overview/activity/charts.tsx @@ -5,7 +5,7 @@ import { Charts, LoadingCharts } from '@/app/(app)/_components/charts'; import type { ChartData } from '@/app/(app)/_components/charts/base-chart'; import { api } from '@/trpc/client'; -import { useActivityContext } from '../../../../../../_components/time-range-selector/context'; +import { useActivityContext } from '@/app/(app)/_components/time-range-selector/context'; import { formatCurrency } from '@/lib/utils'; import { useMemo } from 'react'; @@ -30,14 +30,13 @@ export const ActivityCharts: React.FC = ({ appId }) => { ); const [isOwner] = api.apps.app.isOwner.useSuspenseQuery(appId); - const [numTokens] = api.apps.app.getNumTokens.useSuspenseQuery({ appId }); const [numTransactions] = api.apps.app.transactions.count.useSuspenseQuery({ appId, }); const isInitialized = useMemo(() => { - return !isOwner || (numTokens > 0 && numTransactions > 0); - }, [isOwner, numTokens, numTransactions]); + return !isOwner || numTransactions > 0; + }, [isOwner, numTransactions]); // Transform data for the chart const chartData: ChartData>[] = @@ -80,7 +79,7 @@ export const ActivityCharts: React.FC = ({ appId }) => { trigger: { value: 'profit', label: 'Profit', - amount: numTokens === 0 ? '--' : formatCurrency(totalProfit), + amount: numTransactions === 0 ? '--' : formatCurrency(totalProfit), }, bars: [ { @@ -112,7 +111,7 @@ export const ActivityCharts: React.FC = ({ appId }) => { value: 'tokens', label: 'Tokens', amount: - numTokens === 0 + numTransactions === 0 ? '--' : totalTokens.toLocaleString(undefined, { notation: 'compact', @@ -161,7 +160,7 @@ export const ActivityCharts: React.FC = ({ appId }) => { value: 'transactions', label: 'Transactions', amount: - numTokens === 0 + numTransactions === 0 ? '--' : totalTransactions.toLocaleString(undefined, { notation: 'compact', diff --git a/packages/app/control/src/app/(app)/app/[id]/(overview)/_components/overview/transactions/rows.tsx b/packages/app/control/src/app/(app)/app/[id]/(overview)/_components/overview/transactions/rows.tsx index 25ee2840c..034d4f078 100644 --- a/packages/app/control/src/app/(app)/app/[id]/(overview)/_components/overview/transactions/rows.tsx +++ b/packages/app/control/src/app/(app)/app/[id]/(overview)/_components/overview/transactions/rows.tsx @@ -30,8 +30,10 @@ export const TransactionRows = async ({ appId }: { appId: string }) => {

- {transaction.user.name} made{' '} - {transaction.callCount} requests + + {transaction.user.name ?? 'x402 Users'} + {' '} + made {transaction.callCount} requests

{formatDistanceToNow(transaction.date, { diff --git a/packages/app/control/src/app/(app)/app/[id]/_hooks/use-app-setup.ts b/packages/app/control/src/app/(app)/app/[id]/_hooks/use-app-setup.ts index b5ffd9e7a..2665159bf 100644 --- a/packages/app/control/src/app/(app)/app/[id]/_hooks/use-app-setup.ts +++ b/packages/app/control/src/app/(app)/app/[id]/_hooks/use-app-setup.ts @@ -25,10 +25,18 @@ export const useAppConnectionSetup = (appId: string) => { refetchInterval: shouldRefetchConnection ? 2500 : undefined, } ); + const [transactionsCount] = api.apps.app.transactions.count.useSuspenseQuery( + { + appId, + }, + { + refetchInterval: shouldRefetchTransactions ? 2500 : undefined, + } + ); const isConnected = useMemo(() => { - return numTokens > 0 || numApiKeys > 0; - }, [numTokens, numApiKeys]); + return numTokens > 0 || numApiKeys > 0 || transactionsCount > 0; + }, [numTokens, numApiKeys, transactionsCount]); useEffect(() => { setShouldRefetchConnection(!isConnected); diff --git a/packages/app/control/src/app/(app)/app/[id]/transactions/_components/transactions.tsx b/packages/app/control/src/app/(app)/app/[id]/transactions/_components/transactions.tsx index bc3a7239e..3d6c0a5bf 100644 --- a/packages/app/control/src/app/(app)/app/[id]/transactions/_components/transactions.tsx +++ b/packages/app/control/src/app/(app)/app/[id]/transactions/_components/transactions.tsx @@ -23,8 +23,8 @@ import { api } from '@/trpc/client'; interface Transaction { id: string; - user: { - id: string; + user?: { + id: string | null; name: string | null; image: string | null; }; @@ -71,7 +71,7 @@ export const TransactionsTable: React.FC = ({ appId }) => { ({ id: row.id, - user: row.user, + user: row.user ?? undefined, date: row.date, callCount: row.callCount, markUpProfit: row.markUpProfit, @@ -104,11 +104,16 @@ const TransactionRow = ({ transaction }: { transaction: Transaction }) => {

- +

- {transaction.user.name} made{' '} - {transaction.callCount} requests + + {transaction.user?.name ?? 'x402 Users'} + {' '} + made {transaction.callCount} requests

{formatDistanceToNow(transaction.date, { diff --git a/packages/app/control/src/env.ts b/packages/app/control/src/env.ts index de7d41d11..d6e371205 100644 --- a/packages/app/control/src/env.ts +++ b/packages/app/control/src/env.ts @@ -152,6 +152,44 @@ export const env = createEnv({ MERIT_SENDER_GITHUB_ID: IS_STRICT ? z.coerce.number() : z.coerce.number().default(1), + MERIT_CONTRACT_ADDRESS: IS_STRICT + ? z.string().regex(/^0x[a-fA-F0-9]{40}$/, { + message: 'MERIT_CONTRACT_ADDRESS must be a valid Ethereum address', + }) + : z.string().default('0x1234567890123456789012345678901234567890'), + MERIT_REPO_ID: IS_STRICT ? z.string() : z.string().default('1'), + + // coinbase cdp + + CDP_API_KEY_ID: IS_STRICT + ? z.string() + : z.string().default('cdp-api-key-id'), + CDP_API_KEY_SECRET: IS_STRICT + ? z.string() + : z.string().default('cdp-api-key-secret'), + CDP_WALLET_SECRET: IS_STRICT + ? z.string() + : z.string().default('cdp-wallet-secret'), + WALLET_OWNER: IS_STRICT ? z.string() : z.string().default('wallet-owner'), + BASE_RPC_URL: IS_STRICT ? z.string().url() : z.string().url().optional(), + + // crypto addresses + + USDC_ADDRESS: IS_STRICT + ? z.string().regex(/^0x[a-fA-F0-9]{40}$/, { + message: 'USDC_ADDRESS must be a valid Ethereum address', + }) + : z.string().default('0x1234567890123456789012345678901234567890'), + ETH_ADDRESS: IS_STRICT + ? z.string().regex(/^0x[a-fA-F0-9]{40}$/, { + message: 'ETH_ADDRESS must be a valid Ethereum address', + }) + : z.string().default('0x1234567890123456789012345678901234567890'), + ECHO_PAYOUTS_ADDRESS: IS_STRICT + ? z.string().regex(/^0x[a-fA-F0-9]{40}$/, { + message: 'ECHO_PAYOUTS_ADDRESS must be a valid Ethereum address', + }) + : z.string().default('0x1234567890123456789012345678901234567890'), // qstash diff --git a/packages/app/control/src/services/crypto/abi.ts b/packages/app/control/src/services/crypto/abi.ts new file mode 100644 index 000000000..1e7c48ca8 --- /dev/null +++ b/packages/app/control/src/services/crypto/abi.ts @@ -0,0 +1,66 @@ +// Constants +export const MERIT_ABI = [ + { + inputs: [ + { + internalType: 'uint256', + name: 'repoId', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'instanceId', + type: 'uint256', + }, + { + internalType: 'contract ERC20', + name: 'token', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'bytes', + name: 'data', + type: 'bytes', + }, + ], + name: 'fundRepo', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +]; + +export const ERC20_CONTRACT_ABI = [ + { + name: 'approve', + type: 'function', + stateMutability: 'nonpayable', + inputs: [ + { name: 'spender', type: 'address' }, + { name: 'amount', type: 'uint256' }, + ], + outputs: [], + }, + { + name: 'balanceOf', + type: 'function', + stateMutability: 'view', + inputs: [{ name: 'account', type: 'address' }], + outputs: [{ name: 'balance', type: 'uint256' }], + }, + { + name: 'transfer', + type: 'function', + stateMutability: 'nonpayable', + inputs: [ + { name: 'to', type: 'address' }, + { name: 'amount', type: 'uint256' }, + ], + outputs: [{ name: 'success', type: 'bool' }], + }, +] as const; diff --git a/packages/app/control/src/services/crypto/fund-repo.ts b/packages/app/control/src/services/crypto/fund-repo.ts new file mode 100644 index 000000000..c3fe5ab8f --- /dev/null +++ b/packages/app/control/src/services/crypto/fund-repo.ts @@ -0,0 +1,105 @@ +import { encodeFunctionData, type Abi } from 'viem'; +import { getSmartAccount } from './smart-account'; +import { logger } from '@/logger'; +import { ERC20_CONTRACT_ABI, MERIT_ABI } from './abi'; +import { env } from '@/env'; + +export const MERIT_CONTRACT_ADDRESS = + env.MERIT_CONTRACT_ADDRESS as `0x${string}`; +const USDC_ADDRESS = env.USDC_ADDRESS as `0x${string}`; + +// Types +export interface FundRepoResult { + success: boolean; + userOpHash: string; + smartAccountAddress: string; + amount: number; + repoId: string; + tokenAddress: string; +} +// Main functions +export async function fundRepo( + amount: number, + repoId: number +): Promise { + try { + if (!amount || typeof amount !== 'number') { + throw new Error('Invalid amount provided'); + } + + if (!USDC_ADDRESS || !MERIT_CONTRACT_ADDRESS) { + throw new Error('Missing required environment variables'); + } + + const tokenAddress = USDC_ADDRESS; + const repoInstanceId = 0; + // Convert to BigInt safely by avoiding floating point precision issues + // USDC has 6 decimals, so multiply by 10^6 + // Use Math.ceil for defensive rounding to avoid undercharging + const amountBigInt = BigInt(Math.ceil(amount * 10 ** 6)); + + const { smartAccount } = await getSmartAccount(); + + // Send user operation to fund the repo + const result = await smartAccount.sendUserOperation({ + network: 'base', + calls: [ + { + to: tokenAddress, + value: BigInt(0), + data: encodeFunctionData({ + abi: ERC20_CONTRACT_ABI as Abi, + functionName: 'approve', + args: [MERIT_CONTRACT_ADDRESS, amountBigInt], + }), + }, + { + to: MERIT_CONTRACT_ADDRESS, + value: BigInt(0), + data: encodeFunctionData({ + abi: MERIT_ABI as Abi, + functionName: 'fundRepo', + args: [ + BigInt(repoId), + BigInt(repoInstanceId), + tokenAddress, + amountBigInt, + '0x', + ], + }), + }, + ], + }); + + // Wait for the user operation to be processed + await smartAccount.waitForUserOperation({ + userOpHash: result.userOpHash, + }); + + logger.emit({ + severityText: 'INFO', + body: 'User operation processed successfully', + }); + + return { + success: true, + userOpHash: result.userOpHash, + smartAccountAddress: smartAccount.address, + amount: amount, + repoId: repoId.toString(), + tokenAddress: tokenAddress, + }; + } catch (error) { + logger.emit({ + severityText: 'ERROR', + body: `Error in funding repo: ${error instanceof Error ? error.message : 'Unknown error'}`, + attributes: { + amount, + stack: error instanceof Error ? error.stack : 'No stack', + timestamp: new Date().toISOString(), + }, + }); + + throw error; + } +} diff --git a/packages/app/control/src/services/crypto/get-balance.ts b/packages/app/control/src/services/crypto/get-balance.ts new file mode 100644 index 000000000..f9aa6b772 --- /dev/null +++ b/packages/app/control/src/services/crypto/get-balance.ts @@ -0,0 +1,61 @@ +import { createPublicClient, http, type Address } from 'viem'; +import { base, baseSepolia } from 'viem/chains'; +import { ERC20_CONTRACT_ABI } from './abi'; +import { env } from '@/env'; + +export type Network = 'base' | 'base-sepolia'; + +const NETWORK_TO_CHAIN = { + base: base, + 'base-sepolia': baseSepolia, +} as const; + +export async function getERC20Balance( + network: Network, + erc20Address: Address, + userAddress: Address +): Promise { + const chain = NETWORK_TO_CHAIN[network]; + if (!chain) { + throw new Error(`Unsupported network for balance check: ${network}`); + } + + const baseRpcUrl = env.BASE_RPC_URL ?? undefined; + + const client = createPublicClient({ + chain, + transport: http(baseRpcUrl), + }); + + const balance = await client.readContract({ + address: erc20Address, + abi: ERC20_CONTRACT_ABI, + functionName: 'balanceOf', + args: [userAddress], + }); + + return balance; +} + +export async function getEthereumBalance( + network: Network, + userAddress: Address +): Promise { + const chain = NETWORK_TO_CHAIN[network]; + if (!chain) { + throw new Error(`Unsupported network for balance check: ${network}`); + } + + const baseRpcUrl = env.BASE_RPC_URL ?? undefined; + + const client = createPublicClient({ + chain, + transport: http(baseRpcUrl), + }); + + const balance = await client.getBalance({ + address: userAddress, + }); + + return balance; +} diff --git a/packages/app/control/src/services/crypto/smart-account.ts b/packages/app/control/src/services/crypto/smart-account.ts new file mode 100644 index 000000000..92786e5d6 --- /dev/null +++ b/packages/app/control/src/services/crypto/smart-account.ts @@ -0,0 +1,35 @@ +import { CdpClient, type EvmSmartAccount } from '@coinbase/cdp-sdk'; +import { env } from '@/env'; + +const API_KEY_ID = env.CDP_API_KEY_ID ?? 'your-api-key-id'; +const API_KEY_SECRET = env.CDP_API_KEY_SECRET ?? 'your-api-key-secret'; +const WALLET_SECRET = env.CDP_WALLET_SECRET ?? 'your-wallet-secret'; +const WALLET_OWNER = env.WALLET_OWNER ?? 'your-wallet-owner'; +const WALLET_SMART_ACCOUNT = env.WALLET_OWNER + '-smart-account'; + +export async function getSmartAccount(): Promise<{ + smartAccount: EvmSmartAccount; +}> { + try { + const cdp = new CdpClient({ + apiKeyId: API_KEY_ID, + apiKeySecret: API_KEY_SECRET, + walletSecret: WALLET_SECRET, + }); + + const owner = await cdp.evm.getOrCreateAccount({ + name: WALLET_OWNER, + }); + + const smartAccount = await cdp.evm.getOrCreateSmartAccount({ + name: WALLET_SMART_ACCOUNT, + owner, + }); + + return { smartAccount }; + } catch (error) { + throw new Error( + `CDP authentication failed: ${error instanceof Error ? error.message : 'Unknown error'}` + ); + } +} diff --git a/packages/app/control/src/services/crypto/transfer.ts b/packages/app/control/src/services/crypto/transfer.ts new file mode 100644 index 000000000..eb29a082b --- /dev/null +++ b/packages/app/control/src/services/crypto/transfer.ts @@ -0,0 +1,42 @@ +import type { Address, Hex } from 'viem'; +import { getSmartAccount } from './smart-account'; +import type { Network } from './get-balance'; + +interface TransferParams { + recipientAddress: string; + amount: bigint; + token?: 'eth' | 'usdc' | Hex; + network?: Network; +} + +export interface TransferResult { + userOpHash: Hex; + status: string; + smartAccountAddress: Address; + recipientAddress: string; + amount: bigint; +} + +export async function transfer({ + recipientAddress, + amount, + token = 'usdc', + network = 'base', +}: TransferParams): Promise { + const { smartAccount } = await getSmartAccount(); + + const result = await smartAccount.transfer({ + to: recipientAddress as Address, + amount, + token, + network, + }); + + return { + userOpHash: result.userOpHash, + status: result.status, + smartAccountAddress: result.smartAccountAddress, + recipientAddress, + amount, + }; +} diff --git a/packages/app/control/src/services/db/admin/wallet.ts b/packages/app/control/src/services/db/admin/wallet.ts new file mode 100644 index 000000000..9a5a93097 --- /dev/null +++ b/packages/app/control/src/services/db/admin/wallet.ts @@ -0,0 +1,299 @@ +import { db } from '@/services/db/client'; +import { + EnumTransactionType, + EnumPayoutStatus, + EnumPayoutType, + type Payout, +} from '@/generated/prisma'; +import { getSmartAccount } from '@/services/crypto/smart-account'; +import { + getERC20Balance, + getEthereumBalance, +} from '@/services/crypto/get-balance'; +import { + fundRepo, + type FundRepoResult, + MERIT_CONTRACT_ADDRESS, +} from '@/services/crypto/fund-repo'; +import { transfer, type TransferResult } from '@/services/crypto/transfer'; +import { logger } from '@/logger'; +import type { Address } from 'viem'; +import { Decimal } from '@prisma/client/runtime/library'; +import { env } from '@/env'; + +const USDC_ADDRESS = env.USDC_ADDRESS as Address; +const MERIT_REPO_ID = env.MERIT_REPO_ID; +const ECHO_PAYOUTS_ADDRESS = env.ECHO_PAYOUTS_ADDRESS as Address; +const NETWORK = 'base' as const; +const USDC_DECIMALS = 6; +const ETH_DECIMALS = 18; + +export async function getEchoX402ProfitTotal(): Promise { + const profitResult = await db.transaction.aggregate({ + where: { + transactionType: EnumTransactionType.X402, + isArchived: false, + }, + _sum: { + echoProfit: true, + }, + }); + + const payoutResult = await db.payout.aggregate({ + where: { + type: EnumPayoutType.ECHO_PROFIT, + }, + _sum: { + amount: true, + }, + }); + + const totalProfit = new Decimal(profitResult._sum.echoProfit ?? 0); + const totalPayouts = new Decimal(payoutResult._sum.amount ?? 0); + + return totalProfit.minus(totalPayouts).toNumber(); +} + +export async function getX402RawTransactionCostTotal(): Promise { + const result = await db.transaction.aggregate({ + where: { + transactionType: EnumTransactionType.X402, + isArchived: false, + }, + _sum: { + rawTransactionCost: true, + }, + }); + + return new Decimal(result._sum.rawTransactionCost ?? 0).toNumber(); +} + +export async function getSmartAccountAddress(): Promise { + const { smartAccount } = await getSmartAccount(); + return smartAccount.address; +} + +export async function getSmartAccountUSDCBalance(): Promise { + if (!USDC_ADDRESS) { + throw new Error('USDC_ADDRESS environment variable not set'); + } + + const { smartAccount } = await getSmartAccount(); + const balanceBigInt = await getERC20Balance( + NETWORK, + USDC_ADDRESS, + smartAccount.address + ); + + return Number(balanceBigInt) / 10 ** USDC_DECIMALS; +} + +export async function getSmartAccountETHBalance(): Promise { + const { smartAccount } = await getSmartAccount(); + const balanceBigInt = await getEthereumBalance(NETWORK, smartAccount.address); + + return Number(balanceBigInt) / 10 ** ETH_DECIMALS; +} + +export async function fundEchoRepo(amount: number): Promise { + if (!MERIT_REPO_ID) { + throw new Error('MERIT_REPO_ID environment variable not set'); + } + + if (!amount || amount <= 0) { + throw new Error('Amount must be greater than 0'); + } + + const result = await fundRepo(amount, Number(MERIT_REPO_ID)); + + await db.payout.create({ + data: { + type: EnumPayoutType.ECHO_PROFIT, + status: EnumPayoutStatus.COMPLETED, + amount: amount, + description: `Echo profit payout to Merit repository`, + transactionId: result.userOpHash, + senderAddress: result.smartAccountAddress, + recipientAddress: MERIT_CONTRACT_ADDRESS, + }, + }); + + logger.emit({ + severityText: 'INFO', + body: 'Created ECHO_PROFIT payout record', + attributes: { + amount, + userOpHash: result.userOpHash, + smartAccountAddress: result.smartAccountAddress, + }, + }); + + return result; +} + +export async function getEchoPayoutHistory(): Promise { + return await db.payout.findMany({ + where: { + type: EnumPayoutType.ECHO_PROFIT, + }, + orderBy: { + createdAt: 'desc', + }, + take: 50, + }); +} + +export async function getX402AppProfit(): Promise { + const profitResult = await db.transaction.aggregate({ + where: { + transactionType: EnumTransactionType.X402, + isArchived: false, + }, + _sum: { + appProfit: true, + }, + }); + + const payoutResult = await db.payout.aggregate({ + where: { + type: EnumPayoutType.APP_PROFIT, + }, + _sum: { + amount: true, + }, + }); + + const totalProfit = new Decimal(profitResult._sum.appProfit ?? 0); + const totalPayouts = new Decimal(payoutResult._sum.amount ?? 0); + + return totalProfit.minus(totalPayouts).toNumber(); +} + +interface AppProfitBreakdown { + appId: string; + appName: string; + totalProfit: number; + totalPayouts: number; + remainingProfit: number; +} + +export async function getX402AppProfitByApp(): Promise { + const profitByApp = await db.transaction.groupBy({ + by: ['echoAppId'], + where: { + transactionType: EnumTransactionType.X402, + isArchived: false, + echoAppId: { not: null }, + }, + _sum: { + appProfit: true, + }, + }); + + const payoutsByApp = await db.payout.groupBy({ + by: ['echoAppId'], + where: { + type: EnumPayoutType.APP_PROFIT, + echoAppId: { not: null }, + }, + _sum: { + amount: true, + }, + }); + + const payoutsMap = new Map( + payoutsByApp.map(p => [p.echoAppId, new Decimal(p._sum.amount ?? 0)]) + ); + + const appIds = profitByApp + .map(p => p.echoAppId) + .filter((id): id is string => id !== null); + + const apps = await db.echoApp.findMany({ + where: { + id: { in: appIds }, + }, + select: { + id: true, + name: true, + }, + }); + + const appsMap = new Map(apps.map(a => [a.id, a.name])); + + return profitByApp + .filter((p): p is typeof p & { echoAppId: string } => p.echoAppId !== null) + .map(p => { + const totalProfit = new Decimal(p._sum.appProfit ?? 0); + const totalPayouts = payoutsMap.get(p.echoAppId) ?? new Decimal(0); + const remainingProfit = totalProfit.minus(totalPayouts); + + return { + appId: p.echoAppId, + appName: appsMap.get(p.echoAppId) ?? 'Unknown App', + totalProfit: totalProfit.toNumber(), + totalPayouts: totalPayouts.toNumber(), + remainingProfit: remainingProfit.toNumber(), + }; + }) + .sort((a, b) => b.remainingProfit - a.remainingProfit); +} + +export async function PayoutX402AppProfit( + appId: string, + amount: number +): Promise { + if (!ECHO_PAYOUTS_ADDRESS) { + throw new Error('ECHO_PAYOUTS_ADDRESS environment variable not set'); + } + + if (!amount || amount <= 0) { + throw new Error('Amount must be greater than 0'); + } + + const app = await db.echoApp.findUnique({ + where: { id: appId }, + select: { id: true, name: true }, + }); + + if (!app) { + throw new Error(`App with ID ${appId} not found`); + } + + const amountBigInt = BigInt(Math.round(amount * 10 ** USDC_DECIMALS)); + + const result = await transfer({ + recipientAddress: ECHO_PAYOUTS_ADDRESS, + amount: amountBigInt, + token: 'usdc', + network: NETWORK, + }); + + await db.payout.create({ + data: { + type: EnumPayoutType.APP_PROFIT, + status: EnumPayoutStatus.COMPLETED, + amount: amount, + echoAppId: appId, + description: `App profit payout for ${app.name}`, + transactionId: result.userOpHash, + senderAddress: result.smartAccountAddress, + recipientAddress: ECHO_PAYOUTS_ADDRESS, + }, + }); + + logger.emit({ + severityText: 'INFO', + body: 'Created APP_PROFIT payout record', + attributes: { + appId, + appName: app.name, + amount, + userOpHash: result.userOpHash, + smartAccountAddress: result.smartAccountAddress, + recipientAddress: ECHO_PAYOUTS_ADDRESS, + }, + }); + + return result; +} diff --git a/packages/app/control/src/services/db/apps/transactions.ts b/packages/app/control/src/services/db/apps/transactions.ts index 4b1c17ca6..a9c2b885b 100644 --- a/packages/app/control/src/services/db/apps/transactions.ts +++ b/packages/app/control/src/services/db/apps/transactions.ts @@ -67,7 +67,7 @@ export const listAppTransactions = async ( { id: string; user: { - id: string; + id: string | null; name: string | null; image: string | null; }; @@ -78,7 +78,9 @@ export const listAppTransactions = async ( >(); for (const transaction of transactions) { - const userKey = `${transaction.userId}-${format(transaction.createdAt, 'yyyy-MM-dd')}`; + // Use 'unknown' as userId for null userIds, ensuring they all group together + const userId = transaction.userId ?? 'unknown'; + const userKey = `${userId}-${format(transaction.createdAt, 'yyyy-MM-dd')}`; if (groupedTransactions.has(userKey)) { // Aggregate existing group @@ -91,8 +93,10 @@ export const listAppTransactions = async ( id: transaction.id, user: { id: transaction.userId, - name: transaction.user.name, - image: transaction.user.image, + name: + transaction.user?.name ?? + (transaction.userId === null ? 'x402 Users' : null), + image: transaction.user?.image ?? null, }, callCount: 1, markUpProfit: Number(transaction.markUpProfit), diff --git a/packages/app/control/src/services/db/apps/users.ts b/packages/app/control/src/services/db/apps/users.ts index 2024e30e7..2cd00f03b 100644 --- a/packages/app/control/src/services/db/apps/users.ts +++ b/packages/app/control/src/services/db/apps/users.ts @@ -19,6 +19,9 @@ export const listAppUsers = async ( { page, page_size }: PaginationParams ) => { const where: Prisma.TransactionWhereInput = { + userId: { + not: null, + }, echoAppId: appId, isArchived: false, ...((startDate !== undefined || endDate !== undefined) && { @@ -56,7 +59,9 @@ export const listAppUsers = async ( }); // Get user details and membership info for the top users - const userIds = topUsersWithStats.map(stat => stat.userId); + const userIds = topUsersWithStats + .map(stat => stat.userId) + .filter((id): id is string => id !== null); const usersWithDetails = await db.user.findMany({ where: { diff --git a/packages/app/control/src/services/db/user/payouts/markup.ts b/packages/app/control/src/services/db/user/payouts/markup.ts index 97ca2fece..64a798361 100644 --- a/packages/app/control/src/services/db/user/payouts/markup.ts +++ b/packages/app/control/src/services/db/user/payouts/markup.ts @@ -124,6 +124,7 @@ export async function calculateUserMarkupEarnings( const byApp: Record = {}; for (const row of groupedByApp) { + if (!row.echoAppId) continue; const gross = row._sum.markUpProfit ? Number(row._sum.markUpProfit) : 0; const claimed = claimedMap[row.echoAppId] || 0; const net = Math.max(0, gross - claimed); diff --git a/packages/app/control/src/services/db/user/payouts/referrals.ts b/packages/app/control/src/services/db/user/payouts/referrals.ts index f9892e0c6..685acb751 100644 --- a/packages/app/control/src/services/db/user/payouts/referrals.ts +++ b/packages/app/control/src/services/db/user/payouts/referrals.ts @@ -81,6 +81,7 @@ export async function calculateUserReferralEarnings( const byApp: Record = {}; for (const row of groupedByApp) { + if (!row.echoAppId) continue; const gross = row._sum.referralProfit ? Number(row._sum.referralProfit) : 0; const claimed = claimedMap[row.echoAppId] || 0; const net = Math.max(0, gross - claimed); diff --git a/packages/app/control/src/trpc/routers/admin/admin.ts b/packages/app/control/src/trpc/routers/admin/admin.ts index 0c403dcce..d6aa09230 100644 --- a/packages/app/control/src/trpc/routers/admin/admin.ts +++ b/packages/app/control/src/trpc/routers/admin/admin.ts @@ -10,6 +10,7 @@ import { adminPaymentsRouter } from './payments'; import { adminEmailCampaignsRouter } from './email-campaigns'; import { adminCreditGrantsRouter } from './credit-grants'; import { adminTokensRouter } from './tokens'; +import { adminWalletRouter } from './wallet'; export const adminRouter = createTRPCRouter({ ...adminBaseProcedures, @@ -22,4 +23,5 @@ export const adminRouter = createTRPCRouter({ emailCampaigns: adminEmailCampaignsRouter, creditGrants: adminCreditGrantsRouter, tokens: adminTokensRouter, + wallet: adminWalletRouter, }); diff --git a/packages/app/control/src/trpc/routers/admin/wallet.ts b/packages/app/control/src/trpc/routers/admin/wallet.ts new file mode 100644 index 000000000..eed3f40d2 --- /dev/null +++ b/packages/app/control/src/trpc/routers/admin/wallet.ts @@ -0,0 +1,56 @@ +import { adminProcedure, createTRPCRouter } from '../../trpc'; +import { + getEchoX402ProfitTotal, + getSmartAccountAddress, + getSmartAccountUSDCBalance, + getSmartAccountETHBalance, + fundEchoRepo, + getEchoPayoutHistory, + getX402AppProfit, + getX402AppProfitByApp, + PayoutX402AppProfit, + getX402RawTransactionCostTotal, +} from '@/services/db/admin/wallet'; +import { z } from 'zod'; + +export const adminWalletRouter = createTRPCRouter({ + getEchoX402ProfitTotal: adminProcedure.query(async () => { + return await getEchoX402ProfitTotal(); + }), + getSmartAccountAddress: adminProcedure.query(async () => { + return await getSmartAccountAddress(); + }), + getSmartAccountUSDCBalance: adminProcedure.query(async () => { + return await getSmartAccountUSDCBalance(); + }), + getSmartAccountETHBalance: adminProcedure.query(async () => { + return await getSmartAccountETHBalance(); + }), + getEchoPayoutHistory: adminProcedure.query(async () => { + return await getEchoPayoutHistory(); + }), + fundEchoRepo: adminProcedure + .input(z.object({ amount: z.number().positive() })) + .mutation(async ({ input }) => { + return await fundEchoRepo(input.amount); + }), + getX402AppProfit: adminProcedure.query(async () => { + return await getX402AppProfit(); + }), + getX402AppProfitByApp: adminProcedure.query(async () => { + return await getX402AppProfitByApp(); + }), + payoutX402AppProfit: adminProcedure + .input( + z.object({ + appId: z.string().uuid(), + amount: z.number().positive(), + }) + ) + .mutation(async ({ input }) => { + return await PayoutX402AppProfit(input.appId, input.amount); + }), + getX402RawTransactionCostTotal: adminProcedure.query(async () => { + return await getX402RawTransactionCostTotal(); + }), +}); diff --git a/packages/app/server/package.json b/packages/app/server/package.json index f0a2fcc47..04048d256 100644 --- a/packages/app/server/package.json +++ b/packages/app/server/package.json @@ -12,7 +12,7 @@ "prebuild": "pnpm run copy-schema && pnpm run prisma:generate", "predev": "pnpm run copy-prisma", "prestart": "pnpm run copy-prisma", - "dev": "npx tsx src/server.ts", + "dev": "pnpm run build && node dist/server.js", "start": "node dist/server.js", "prisma:migrate-deploy": "pnpx prisma migrate deploy", "anthropic-client": "npx tsx src/clients/anthropic-client.ts", diff --git a/packages/app/server/src/clients/openrouter-client.ts b/packages/app/server/src/clients/openrouter-client.ts index bb3f687c3..edbda19df 100644 --- a/packages/app/server/src/clients/openrouter-client.ts +++ b/packages/app/server/src/clients/openrouter-client.ts @@ -25,7 +25,7 @@ async function makeRequest(useStreaming: boolean = false) { // Initialize OpenAI client with custom baseURL const openai = new OpenAI({ - baseURL: 'https://echo-staging.up.railway.app', + baseURL: 'http://localhost:3070', apiKey: process.env.ECHO_API_KEY, // Required by the client but not used with local server }); diff --git a/packages/app/server/src/handlers.ts b/packages/app/server/src/handlers.ts index 8d110eb5b..5d4034782 100644 --- a/packages/app/server/src/handlers.ts +++ b/packages/app/server/src/handlers.ts @@ -1,175 +1,17 @@ import { TransactionEscrowMiddleware } from 'middleware/transaction-escrow-middleware'; import { modelRequestService } from 'services/ModelRequestService'; -import { HandlerInput, Network, Transaction, X402HandlerInput } from 'types'; +import { ApiKeyHandlerInput, X402HandlerInput } from 'types'; import { - usdcBigIntToDecimal, - decimalToUsdcBigInt, - buildX402Response, - getSmartAccount, calculateRefundAmount, - validateXPaymentHeader, } from 'utils'; -import { transfer } from 'transferWithAuth'; import { checkBalance } from 'services/BalanceCheckService'; import { prisma } from 'server'; import { makeProxyPassthroughRequest } from 'services/ProxyPassthroughService'; -import { USDC_ADDRESS } from 'services/fund-repo/constants'; -import { FacilitatorClient } from 'services/facilitator/facilitatorService'; -import { - ExactEvmPayload, - PaymentPayload, - PaymentRequirementsSchema, - SettleRequestSchema, - ExactEvmPayloadSchema, -} from 'services/facilitator/x402-types'; -import { Decimal } from '@prisma/client/runtime/library'; import logger from 'logger'; -import { Request, Response } from 'express'; import { ProviderType } from 'providers/ProviderType'; -import { safeFundRepoIfWorthwhile } from 'services/fund-repo/fundRepoService'; -import { applyMaxCostMarkup } from 'services/PricingService'; - -export async function refund( - paymentAmountDecimal: Decimal, - payload: ExactEvmPayload -) { - try { - const refundAmountUsdcBigInt = decimalToUsdcBigInt(paymentAmountDecimal); - const authPayload = payload.authorization; - await transfer(authPayload.from as `0x${string}`, refundAmountUsdcBigInt); - } catch (error) { - logger.error('Failed to refund', error); - } -} - -export async function settle( - req: Request, - res: Response, - headers: Record, - maxCost: Decimal -): Promise< - { payload: ExactEvmPayload; paymentAmountDecimal: Decimal } | undefined -> { - const network = process.env.NETWORK as Network; - - let recipient: string; - try { - recipient = (await getSmartAccount()).smartAccount.address; - } catch (error) { - buildX402Response(req, res, maxCost); - return undefined; - } - - let xPaymentData: PaymentPayload; - try { - xPaymentData = validateXPaymentHeader(headers, req); - } catch (error) { - buildX402Response(req, res, maxCost); - return undefined; - } - - const parseResult = ExactEvmPayloadSchema.safeParse(xPaymentData.payload); - - if (!parseResult.success) { - logger.error('Invalid EVM payload', { - error: parseResult.error.format() - }); - buildX402Response(req, res, maxCost); - return undefined; - } - - const payload = parseResult.data; - logger.info(`Payment payload: ${JSON.stringify(payload)}`); - - const paymentAmount = payload.authorization.value; - const paymentAmountDecimal = usdcBigIntToDecimal(paymentAmount); - - // Note(shafu, alvaro): Edge case where client sends the x402-challenge - // but the payment amount is less than what we returned in the first response - if (BigInt(paymentAmount) < decimalToUsdcBigInt(maxCost)) { - buildX402Response(req, res, maxCost); - return undefined; - } - - const facilitatorClient = new FacilitatorClient(); - const paymentRequirements = PaymentRequirementsSchema.parse({ - scheme: 'exact', - network, - maxAmountRequired: paymentAmount, - resource: `${req.protocol}://${req.get('host')}${req.url}`, - description: 'Echo x402', - mimeType: 'application/json', - payTo: recipient, - maxTimeoutSeconds: 60, - asset: USDC_ADDRESS, - extra: { - name: 'USD Coin', - version: '2', - }, - }); - - const settleRequest = SettleRequestSchema.parse({ - paymentPayload: xPaymentData, - paymentRequirements, - }); - - const settleResult = await facilitatorClient.settle(settleRequest); - - if (!settleResult.success || !settleResult.transaction) { - buildX402Response(req, res, maxCost); - return undefined; - } - - return { payload, paymentAmountDecimal }; -} - -export async function finalize( - paymentAmountDecimal: Decimal, - transaction: Transaction, - payload: ExactEvmPayload -) { - const transactionCostWithMarkup = applyMaxCostMarkup( - transaction.rawTransactionCost - ); - - // rawTransactionCost is what we pay to OpenAI - // transactionCostWithMarkup is what we charge the user - // markup is the difference between the two, and is sent with fundRepo (not every time, just when it is worthwhile to send a payment) - - // The user should be refunded paymentAmountDecimal - transactionCostWithMarkup\ - - const refundAmount = calculateRefundAmount( - paymentAmountDecimal, - transactionCostWithMarkup - ); - logger.info(`Payment amount decimal: ${paymentAmountDecimal.toNumber()} USD`); - logger.info(`Refunding ${refundAmount.toNumber()} USD`); - logger.info( - `Transaction cost with markup: ${transactionCostWithMarkup.toNumber()} USD` - ); - logger.info( - `Transaction cost: ${transaction.rawTransactionCost.toNumber()} USD` - ); - - if (!refundAmount.equals(0) && refundAmount.greaterThan(0)) { - const refundAmountUsdcBigInt = decimalToUsdcBigInt(refundAmount); - const authPayload = payload.authorization; - await transfer(authPayload.from as `0x${string}`, refundAmountUsdcBigInt); - } - - const markUpAmount = transactionCostWithMarkup.minus( - transaction.rawTransactionCost - ); - if (markUpAmount.greaterThan(0)) { - logger.info(`PROFIT RECEIVED: ${markUpAmount.toNumber()} USD, checking for a repo send operation`); - try { - await safeFundRepoIfWorthwhile(); - } catch (error) { - logger.error('Failed to fund repo', error); - // Don't re-throw - repo funding is not critical to the transaction - } - } -} +import { settle } from 'handlers/settle'; +import { finalize } from 'handlers/finalize'; +import { refund } from 'handlers/refund'; export async function handleX402Request({ req, @@ -179,11 +21,11 @@ export async function handleX402Request({ isPassthroughProxyRoute, provider, isStream, + x402AuthenticationService, }: X402HandlerInput) { if (isPassthroughProxyRoute) { return await makeProxyPassthroughRequest(req, res, provider, headers); } - const settleResult = await settle(req, res, headers, maxCost); if (!settleResult) { return; @@ -200,7 +42,6 @@ export async function handleX402Request({ isStream ); const transaction = transactionResult.transaction; - if (provider.getType() === ProviderType.OPENAI_VIDEOS) { await prisma.videoGenerationX402.create({ data: { @@ -218,9 +59,14 @@ export async function handleX402Request({ transactionResult.data ); + logger.info(`Creating X402 transaction for app. Metadata: ${JSON.stringify(transaction.metadata)}`); + const transactionCosts = await x402AuthenticationService.createX402Transaction(transaction); + await finalize( paymentAmountDecimal, - transactionResult.transaction, + transactionCosts.rawTransactionCost, + transactionCosts.totalAppProfit, + transactionCosts.echoProfit, payload ); } catch (error) { @@ -237,7 +83,7 @@ export async function handleApiKeyRequest({ isPassthroughProxyRoute, provider, isStream, -}: HandlerInput) { +}: ApiKeyHandlerInput) { const transactionEscrowMiddleware = new TransactionEscrowMiddleware(prisma); if (isPassthroughProxyRoute) { @@ -274,7 +120,7 @@ export async function handleApiKeyRequest({ modelRequestService.handleResolveResponse(res, isStream, data); - await echoControlService.createTransaction(transaction, maxCost); + await echoControlService.createTransaction(transaction); if (provider.getType() === ProviderType.OPENAI_VIDEOS) { const transactionCost = await echoControlService.computeTransactionCosts( diff --git a/packages/app/server/src/handlers/finalize.ts b/packages/app/server/src/handlers/finalize.ts new file mode 100644 index 000000000..997108cdc --- /dev/null +++ b/packages/app/server/src/handlers/finalize.ts @@ -0,0 +1,45 @@ +import { decimalToUsdcBigInt, calculateRefundAmount } from 'utils'; +import { transfer } from 'transferWithAuth'; +import { ExactEvmPayload } from 'services/facilitator/x402-types'; +import { Decimal } from '@prisma/client/runtime/library'; +import { Transaction } from 'types'; + +export async function finalize( + originalPaymentAmountDecimal: Decimal, + rawTransactionCost: Decimal, + appMarkupProfit: Decimal, + echoMarkupProfit: Decimal, + payload: ExactEvmPayload +) { + const appMarkupAmount = rawTransactionCost.plus(appMarkupProfit); + + const totalCostToUser = appMarkupAmount.add(echoMarkupProfit); + + const refundAmount = calculateRefundAmount( + originalPaymentAmountDecimal, + totalCostToUser + ); + + if (!refundAmount.equals(0) && refundAmount.greaterThan(0)) { + const refundAmountUsdcBigInt = decimalToUsdcBigInt(refundAmount); + const authPayload = payload.authorization; + await transfer(authPayload.from as `0x${string}`, refundAmountUsdcBigInt); + } +} + +export async function finalizeResource( + originalPaymentAmountDecimal: Decimal, + transaction: Transaction, + payload: ExactEvmPayload +) { + const refundAmount = calculateRefundAmount( + originalPaymentAmountDecimal, + transaction.rawTransactionCost + ); + + if (!refundAmount.equals(0) && refundAmount.greaterThan(0)) { + const refundAmountUsdcBigInt = decimalToUsdcBigInt(refundAmount); + const authPayload = payload.authorization; + await transfer(authPayload.from as `0x${string}`, refundAmountUsdcBigInt); + } +} diff --git a/packages/app/server/src/handlers/refund.ts b/packages/app/server/src/handlers/refund.ts new file mode 100644 index 000000000..9279303e4 --- /dev/null +++ b/packages/app/server/src/handlers/refund.ts @@ -0,0 +1,18 @@ +import { decimalToUsdcBigInt } from 'utils'; +import { transfer } from 'transferWithAuth'; +import { ExactEvmPayload } from 'services/facilitator/x402-types'; +import { Decimal } from '@prisma/client/runtime/library'; +import logger from 'logger'; + +export async function refund( + paymentAmountDecimal: Decimal, + payload: ExactEvmPayload +) { + try { + const refundAmountUsdcBigInt = decimalToUsdcBigInt(paymentAmountDecimal); + const authPayload = payload.authorization; + await transfer(authPayload.from as `0x${string}`, refundAmountUsdcBigInt); + } catch (error) { + logger.error('Failed to refund', error); + } +} diff --git a/packages/app/server/src/handlers/settle.ts b/packages/app/server/src/handlers/settle.ts new file mode 100644 index 000000000..99567eb9a --- /dev/null +++ b/packages/app/server/src/handlers/settle.ts @@ -0,0 +1,99 @@ +import { + usdcBigIntToDecimal, + decimalToUsdcBigInt, + buildX402Response, + getSmartAccount, + validateXPaymentHeader, +} from 'utils'; +import { USDC_ADDRESS } from 'services/fund-repo/constants'; +import { FacilitatorClient } from 'services/facilitator/facilitatorService'; +import { + ExactEvmPayload, + ExactEvmPayloadSchema, + PaymentPayload, + PaymentRequirementsSchema, + SettleRequestSchema, + Network, +} from 'services/facilitator/x402-types'; +import { Decimal } from '@prisma/client/runtime/library'; +import logger from 'logger'; +import { Request, Response } from 'express'; + +export async function settle( + req: Request, + res: Response, + headers: Record, + maxCost: Decimal +): Promise< + { payload: ExactEvmPayload; paymentAmountDecimal: Decimal } | undefined +> { + const network = process.env.NETWORK as Network; + + let recipient: string; + try { + recipient = (await getSmartAccount()).smartAccount.address; + } catch (error) { + buildX402Response(req, res, maxCost); + return undefined; + } + + let xPaymentData: PaymentPayload; + try { + xPaymentData = validateXPaymentHeader(headers, req); + } catch (error) { + buildX402Response(req, res, maxCost); + return undefined; + } + + const payloadResult = ExactEvmPayloadSchema.safeParse(xPaymentData.payload); + if (!payloadResult.success) { + logger.error('Invalid ExactEvmPayload in settle', { + error: payloadResult.error, + payload: xPaymentData.payload, + }); + buildX402Response(req, res, maxCost); + return undefined; + } + const payload = payloadResult.data; + + const paymentAmount = payload.authorization.value; + const paymentAmountDecimal = usdcBigIntToDecimal(paymentAmount); + + // Note(shafu, alvaro): Edge case where client sends the x402-challenge + // but the payment amount is less than what we returned in the first response + if (BigInt(paymentAmount) < decimalToUsdcBigInt(maxCost)) { + buildX402Response(req, res, maxCost); + return undefined; + } + + const facilitatorClient = new FacilitatorClient(); + const paymentRequirements = PaymentRequirementsSchema.parse({ + scheme: 'exact', + network, + maxAmountRequired: paymentAmount, + resource: `${req.protocol}://${req.get('host')}${req.url}`, + description: 'Echo x402', + mimeType: 'application/json', + payTo: recipient, + maxTimeoutSeconds: 60, + asset: USDC_ADDRESS, + extra: { + name: 'USD Coin', + version: '2', + }, + }); + + const settleRequest = SettleRequestSchema.parse({ + paymentPayload: xPaymentData, + paymentRequirements, + }); + + const settleResult = await facilitatorClient.settle(settleRequest); + + if (!settleResult.success || !settleResult.transaction) { + buildX402Response(req, res, maxCost); + return undefined; + } + + return { payload, paymentAmountDecimal }; +} diff --git a/packages/app/server/src/resources/handler.ts b/packages/app/server/src/resources/handler.ts index 4cfd7107f..397c9a0b5 100644 --- a/packages/app/server/src/resources/handler.ts +++ b/packages/app/server/src/resources/handler.ts @@ -4,7 +4,9 @@ import { Decimal } from '@prisma/client/runtime/library'; import { buildX402Response, isApiRequest, isX402Request } from 'utils'; import { authenticateRequest } from 'auth'; import { prisma } from 'server'; -import { settle, finalize, refund } from 'handlers'; +import { settle } from 'handlers/settle'; +import { finalizeResource } from 'handlers/finalize'; +import { refund } from 'handlers/refund'; import logger from 'logger'; import { ExactEvmPayload } from 'services/facilitator/x402-types'; import { HttpError, PaymentRequiredError } from 'errors/http'; @@ -32,7 +34,7 @@ async function handleApiRequest( const actualCost = calculateActualCost(parsedBody, output); const transaction = createTransaction(parsedBody, output, actualCost); - await echoControlService.createTransaction(transaction, actualCost); + await echoControlService.createTransaction(transaction); return output; } @@ -64,7 +66,7 @@ async function handle402Request( const actualCost = calculateActualCost(parsedBody, output); const transaction = createTransaction(parsedBody, output, actualCost); - finalize(paymentAmountDecimal, transaction, payload).catch(error => { + finalizeResource(paymentAmountDecimal, transaction, payload).catch(error => { logger.error('Failed to finalize transaction', error); }); diff --git a/packages/app/server/src/server.ts b/packages/app/server/src/server.ts index 958e913ce..20905c394 100644 --- a/packages/app/server/src/server.ts +++ b/packages/app/server/src/server.ts @@ -34,6 +34,7 @@ import { } from './services/PricingService'; import { Decimal } from '@prisma/client/runtime/library'; import resourceRouter from './routers/resource'; +import { X402AuthenticationService } from 'services/x402AuthenticationService'; dotenv.config(); @@ -107,11 +108,14 @@ app.all('*', async (req: EscrowRequest, res: Response, next: NextFunction) => { const headers = req.headers as Record; const { provider, isStream, isPassthroughProxyRoute, is402Sniffer } = await initializeProvider(req, res); + + const x402AuthenticationService = new X402AuthenticationService(prisma); + const x402AuthenticationResult = await x402AuthenticationService.authenticateX402Request(headers); if (!provider || is402Sniffer) { return buildX402Response(req, res, new Decimal(0)); } const maxCost = getRequestMaxCost(req, provider, isPassthroughProxyRoute); - const maxCostWithMarkup = applyMaxCostMarkup(maxCost); + const maxCostWithMarkup = applyMaxCostMarkup(maxCost, x402AuthenticationResult?.markUp || null); if ( !isApiRequest(headers) && @@ -148,6 +152,7 @@ app.all('*', async (req: EscrowRequest, res: Response, next: NextFunction) => { isPassthroughProxyRoute, provider, isStream, + x402AuthenticationService }); return; } diff --git a/packages/app/server/src/services/DbService.ts b/packages/app/server/src/services/DbService.ts index a019cf089..3b08d9c59 100644 --- a/packages/app/server/src/services/DbService.ts +++ b/packages/app/server/src/services/DbService.ts @@ -5,6 +5,7 @@ import { TransactionRequest, isLlmTransactionMetadata, isVeoTransactionMetadata, + EchoApp, } from '../types'; import { createHmac } from 'crypto'; import { jwtVerify } from 'jose'; @@ -13,6 +14,7 @@ import { Prisma, Transaction, UserSpendPoolUsage, + EnumTransactionType, } from '../generated/prisma'; import { Decimal } from '@prisma/client/runtime/library'; import logger from '../logger'; @@ -329,9 +331,12 @@ export class EchoDbService { markUpProfit: transaction.markUpProfit, referralProfit: transaction.referralProfit, rawTransactionCost: transaction.rawTransactionCost, - status: transaction.status, - userId: transaction.userId, - echoAppId: transaction.echoAppId, + echoProfit: transaction.echoProfit, + transactionType: + transaction.transactionType ?? EnumTransactionType.BALANCE, + status: transaction.status ?? null, + userId: transaction.userId ?? null, + echoAppId: transaction.echoAppId ?? null, apiKeyId: transaction.apiKeyId || null, markUpId: transaction.markUpId || null, spendPoolId: transaction.spendPoolId || null, @@ -412,13 +417,14 @@ export class EchoDbService { transaction ); - // Update user's total spent amount - await this.updateUserTotalSpent( - tx, - transaction.userId, - transaction.totalCost - ); - + if (transaction.userId) { + // Update user's total spent amount + await this.updateUserTotalSpent( + tx, + transaction.userId, + transaction.totalCost + ); + } // Update API key's last used timestamp if provided if (transaction.apiKeyId) { await this.updateApiKeyLastUsed(tx, transaction.apiKeyId); @@ -449,7 +455,7 @@ export class EchoDbService { spendPoolId: string ): Promise<{ transaction: Transaction; - userSpendPoolUsage: UserSpendPoolUsage; + userSpendPoolUsage: UserSpendPoolUsage | null; }> { try { return await this.db.$transaction(async tx => { @@ -462,15 +468,15 @@ export class EchoDbService { if (!spendPool) { throw new Error('Spend pool not found'); } - // 2. Upsert UserSpendPoolUsage record using helper - const userSpendPoolUsage = await this.upsertUserSpendPoolUsage( - tx, - transactionData.userId, - spendPoolId, - transactionData.totalCost - ); - + const userSpendPoolUsage = transactionData.userId + ? await this.upsertUserSpendPoolUsage( + tx, + transactionData.userId, + spendPoolId, + transactionData.totalCost + ) + : null; // 3. Create the transaction record const transaction = await this.createTransactionRecord( tx, @@ -522,4 +528,30 @@ export class EchoDbService { return !!transaction; } + + async getEchoAppById(echoAppId: string): Promise { + const echoApp = await this.db.echoApp.findUnique({ + where: { id: echoAppId }, + }); + if (!echoApp) { + return null; + } + return { + id: echoApp.id, + name: echoApp.name, + createdAt: echoApp.createdAt.toISOString(), + updatedAt: echoApp.updatedAt.toISOString(), + }; + } + async getCurrentMarkupByEchoAppId(echoAppId: string) { + const echoApp = await this.getEchoAppById(echoAppId); + if (!echoApp) { + return null; + } + const markup = await this.db.echoApp.findUnique({ + where: { id: echoAppId }, + select: { markUp: true }, + }); + return markup?.markUp || null; + } } diff --git a/packages/app/server/src/services/EchoControlService.ts b/packages/app/server/src/services/EchoControlService.ts index dca4e6558..861db6a62 100644 --- a/packages/app/server/src/services/EchoControlService.ts +++ b/packages/app/server/src/services/EchoControlService.ts @@ -4,25 +4,34 @@ import type { ApiKeyValidationResult, EchoApp, Transaction, + TransactionCosts, TransactionRequest, User, + X402AuthenticationResult, } from '../types'; import { EchoDbService } from './DbService'; import { Decimal } from '@prisma/client/runtime/library'; import { PaymentRequiredError, UnauthorizedError } from '../errors/http'; -import { PrismaClient, SpendPool } from '../generated/prisma'; +import { + EnumTransactionType, + MarkUp, + PrismaClient, + SpendPool, +} from '../generated/prisma'; import logger from '../logger'; import { EarningsService } from './EarningsService'; import FreeTierService from './FreeTierService'; +import { applyEchoMarkup } from './PricingService'; export class EchoControlService { private readonly db: PrismaClient; private readonly dbService: EchoDbService; private readonly freeTierService: FreeTierService; private earningsService: EarningsService; - private readonly apiKey: string; + private readonly apiKey: string | undefined; private authResult: ApiKeyValidationResult | null = null; + private x402AuthenticationResult: X402AuthenticationResult | null = null; private markUpAmount: Decimal | null = null; private markUpId: string | null = null; private referralAmount: Decimal | null = null; @@ -30,7 +39,7 @@ export class EchoControlService { private referralCodeId: string | null = null; private freeTierSpendPool: SpendPool | null = null; - constructor(db: PrismaClient, apiKey: string) { + constructor(db: PrismaClient, apiKey?: string) { // Check if the generated Prisma client exists const generatedPrismaPath = join(__dirname, 'generated', 'prisma'); if (!existsSync(generatedPrismaPath)) { @@ -53,7 +62,9 @@ export class EchoControlService { */ async verifyApiKey(): Promise { try { - this.authResult = await this.dbService.validateApiKey(this.apiKey); + if (this.apiKey) { + this.authResult = await this.dbService.validateApiKey(this.apiKey); + } } catch (error) { logger.error(`Error verifying API key: ${error}`); return null; @@ -81,6 +92,30 @@ export class EchoControlService { return this.authResult; } + identifyX402Request(echoApp: EchoApp | null, markUp: MarkUp | null): void { + if (echoApp) { + this.x402AuthenticationResult = { + echoApp: echoApp, + echoAppId: echoApp.id, + }; + } + + if (markUp) { + this.markUpAmount = markUp.amount; + this.markUpId = markUp.id; + } else { + // Default markup amount when no markup is configured + this.markUpAmount = new Decimal(1.0); + this.markUpId = null; + } + + this.referralAmount = new Decimal(1.0); + this.referrerRewardId = null; + this.referralCodeId = null; + this.freeTierSpendPool = null; + this.referralCodeId = null; + } + /** * Get the cached authentication result */ @@ -141,10 +176,7 @@ export class EchoControlService { * Create an LLM transaction record directly in the database * Uses centralized logic from EchoDbService */ - async createTransaction( - transaction: Transaction, - maxCost: Decimal - ): Promise { + async createTransaction(transaction: Transaction): Promise { try { if (!this.authResult) { logger.error('No authentication result available'); @@ -160,7 +192,7 @@ export class EchoControlService { await this.createFreeTierTransaction(transaction); return; } else { - await this.createPaidTransaction(transaction, maxCost); + await this.createPaidTransaction(transaction); return; } } catch (error) { @@ -186,14 +218,9 @@ export class EchoControlService { async computeTransactionCosts( transaction: Transaction, - referralCodeId: string | null - ): Promise<{ - rawTransactionCost: Decimal; - totalTransactionCost: Decimal; - totalAppProfit: Decimal; - referralProfit: Decimal; - markUpProfit: Decimal; - }> { + referralCodeId: string | null, + addEchoProfit: boolean = false + ): Promise { if (!this.markUpAmount) { logger.error('User has not authenticated'); throw new UnauthorizedError('User has not authenticated'); @@ -229,9 +256,14 @@ export class EchoControlService { const markUpProfitDecimal = totalAppProfitDecimal.minus( referralProfitDecimal ); - const totalTransactionCostDecimal = transaction.rawTransactionCost.plus( - totalAppProfitDecimal - ); + + const echoProfitDecimal = addEchoProfit + ? applyEchoMarkup(transaction.rawTransactionCost) + : new Decimal(0); + + const totalTransactionCostDecimal = transaction.rawTransactionCost + .plus(totalAppProfitDecimal) + .plus(echoProfitDecimal); // Return Decimal values directly return { @@ -240,6 +272,7 @@ export class EchoControlService { totalAppProfit: totalAppProfitDecimal, referralProfit: referralProfitDecimal, markUpProfit: markUpProfitDecimal, + echoProfit: echoProfitDecimal, }; } async createFreeTierTransaction(transaction: Transaction): Promise { @@ -263,6 +296,7 @@ export class EchoControlService { rawTransactionCost, totalTransactionCost, totalAppProfit, + echoProfit, referralProfit, markUpProfit, } = await this.computeTransactionCosts(transaction, this.referralCodeId); @@ -273,6 +307,7 @@ export class EchoControlService { markUpProfit: markUpProfit, referralProfit: referralProfit, rawTransactionCost: rawTransactionCost, + echoProfit: echoProfit, metadata: transaction.metadata, status: transaction.status, userId: userId, @@ -292,10 +327,7 @@ export class EchoControlService { ); } - async createPaidTransaction( - transaction: Transaction, - maxCost: Decimal - ): Promise { + async createPaidTransaction(transaction: Transaction): Promise { if (!this.authResult) { logger.error('No authentication result available'); throw new UnauthorizedError('No authentication result available'); @@ -307,15 +339,9 @@ export class EchoControlService { totalAppProfit, referralProfit, markUpProfit, + echoProfit, } = await this.computeTransactionCosts(transaction, this.referralCodeId); - logger.info( - `Transaction cost: ${rawTransactionCost}, Max cost: ${maxCost}` - ); - if (rawTransactionCost.greaterThan(maxCost)) { - logger.info(` Difference: ${rawTransactionCost.minus(maxCost)}`); - } - const { userId, echoAppId, apiKeyId } = this.authResult; const transactionData: TransactionRequest = { @@ -324,6 +350,7 @@ export class EchoControlService { markUpProfit: markUpProfit, referralProfit: referralProfit, rawTransactionCost: rawTransactionCost, + echoProfit: echoProfit, metadata: transaction.metadata, status: transaction.status, userId: userId, @@ -336,4 +363,47 @@ export class EchoControlService { await this.dbService.createPaidTransaction(transactionData); } + + async identifyX402Transaction( + echoApp: EchoApp, + markUp: MarkUp + ): Promise { + this.markUpId = markUp.id; + this.markUpAmount = markUp.amount; + this.x402AuthenticationResult = { + echoApp, + echoAppId: echoApp.id, + }; + } + + async createX402Transaction( + transaction: Transaction, + addEchoProfit: boolean = true + ): Promise { + const transactionCosts = await this.computeTransactionCosts( + transaction, + null, + addEchoProfit + ); + + const transactionData: TransactionRequest = { + totalCost: transactionCosts.totalTransactionCost, + appProfit: transactionCosts.totalAppProfit, + markUpProfit: transactionCosts.markUpProfit, + referralProfit: transactionCosts.referralProfit, + rawTransactionCost: transactionCosts.rawTransactionCost, + echoProfit: transactionCosts.echoProfit, + metadata: transaction.metadata, + status: transaction.status, + ...(this.x402AuthenticationResult?.echoAppId && { + echoAppId: this.x402AuthenticationResult?.echoAppId, + }), + ...(this.markUpId && { markUpId: this.markUpId }), + transactionType: EnumTransactionType.X402, + }; + + await this.dbService.createPaidTransaction(transactionData); + + return transactionCosts; + } } diff --git a/packages/app/server/src/services/PricingService.ts b/packages/app/server/src/services/PricingService.ts index 101f39c9b..983e1e682 100644 --- a/packages/app/server/src/services/PricingService.ts +++ b/packages/app/server/src/services/PricingService.ts @@ -14,10 +14,25 @@ import { EscrowRequest } from '../middleware/transaction-escrow-middleware'; import { ProviderType } from 'providers/ProviderType'; import { Tool } from 'openai/resources/responses/responses'; import { SupportedVideoModel } from '@merit-systems/echo-typescript-sdk'; +import { MarkUp } from 'generated/prisma/client'; -export function applyMaxCostMarkup(maxCost: Decimal): Decimal { - const markup = process.env.MAX_COST_MARKUP || '1.25'; - return maxCost.mul(new Decimal(markup)); +export function applyEchoMarkup(cost: Decimal): Decimal { + const echoMarkup = process.env.ECHO_MARKUP || '1.25'; + const applyEchoMarkup = process.env.APPLY_ECHO_MARKUP === 'true'; + if (applyEchoMarkup) { + return cost.mul(new Decimal(echoMarkup)).minus(cost); + } + return new Decimal(0); +} + +export function applyMaxCostMarkup( + maxCost: Decimal, + markUp: MarkUp | null +): Decimal { + const appMarkup = markUp?.amount || 1.0; + const appMarkupApplied = maxCost.mul(new Decimal(appMarkup)).minus(maxCost); + const echoMarkupApplied = applyEchoMarkup(maxCost); + return maxCost.add(echoMarkupApplied).add(appMarkupApplied); } export function getRequestMaxCost( diff --git a/packages/app/server/src/services/fund-repo/fundRepoService.ts b/packages/app/server/src/services/fund-repo/fundRepoService.ts deleted file mode 100644 index 0310eec8f..000000000 --- a/packages/app/server/src/services/fund-repo/fundRepoService.ts +++ /dev/null @@ -1,194 +0,0 @@ -import { encodeFunctionData, Abi, formatUnits, parseUnits } from 'viem'; -import { - MERIT_ABI, - MERIT_CONTRACT_ADDRESS, - USDC_ADDRESS, - ERC20_CONTRACT_ABI, - ETH_ADDRESS, -} from './constants'; -import logger, { logMetric } from '../../logger'; -import { getSmartAccount } from 'utils'; - -export interface FundRepoResult { - success: boolean; - userOpHash: string; - smartAccountAddress: string; - amount: number; - repoId: string; - tokenAddress: string; -} -export async function fundRepo( - amount: number, - repoId: number -): Promise { - try { - if (!amount || typeof amount !== 'number') { - throw new Error('Invalid amount provided'); - } - - if (!USDC_ADDRESS || !MERIT_CONTRACT_ADDRESS) { - throw new Error('Missing required environment variables'); - } - - const tokenAddress = USDC_ADDRESS; - const repoInstanceId = 0; - // Convert to BigInt safely by avoiding floating point precision issues - // USDC has 6 decimals, so multiply by 10^6 - // Use Math.ceil for defensive rounding to avoid undercharging - const amountBigInt = BigInt(Math.ceil(amount * 10 ** 6)); - - const { smartAccount } = await getSmartAccount(); - - // Send user operation to fund the repo - const result = await smartAccount.sendUserOperation({ - network: 'base', - calls: [ - { - to: tokenAddress as `0x${string}`, - value: 0n, - data: encodeFunctionData({ - abi: ERC20_CONTRACT_ABI as Abi, - functionName: 'approve', - args: [MERIT_CONTRACT_ADDRESS, amountBigInt], - }), - }, - { - to: MERIT_CONTRACT_ADDRESS as `0x${string}`, - value: 0n, - data: encodeFunctionData({ - abi: MERIT_ABI as Abi, - functionName: 'fundRepo', - args: [ - BigInt(repoId), - BigInt(repoInstanceId), - tokenAddress, - amountBigInt, - '0x', - ], - }), - }, - ], - }); - - // Wait for the user operation to be processed - await smartAccount.waitForUserOperation({ - userOpHash: result.userOpHash, - }); - - logger.info('User operation processed successfully'); - - return { - success: true, - userOpHash: result.userOpHash, - smartAccountAddress: smartAccount.address, - amount: amount, - repoId: repoId.toString(), - tokenAddress: tokenAddress, - }; - } catch (error) { - logger.error( - `Error in funding repo: ${error instanceof Error ? error.message : 'Unknown error'} | Amount: ${amount} | Stack: ${error instanceof Error ? error.stack : 'No stack'} | Timestamp: ${new Date().toISOString()}` - ); - - throw error; - } -} - -export async function safeFundRepo(amount: number): Promise { - try { - const repoId = process.env.MERIT_REPO_ID; - if (!repoId) { - throw new Error('Missing required environment variables'); - } - await fundRepo(amount, Number(repoId)); - } catch (error) { - logger.error( - `Error in safe funding repo: ${error instanceof Error ? error.message : 'Unknown error'} | Amount: ${amount}` - ); - } -} - -export async function safeFundRepoIfWorthwhile(): Promise { - const repoId = process.env.MERIT_REPO_ID; - if (!repoId) { - throw new Error('Missing required environment variables'); - } - - // check balance of wallet. If it is > 100 USD, send all of the USD to the repo. - - const { smartAccount } = await getSmartAccount(); - const balances = await smartAccount.listTokenBalances({ - network: 'base', - }); - const baseUsdcBalance = balances.balances.find( - balance => balance.token.contractAddress === USDC_ADDRESS - ); - - const ethereumBalance = balances.balances.find( - balance => balance.token.contractAddress === ETH_ADDRESS - ); - - if (!ethereumBalance) { - logger.info('No Ethereum balance found, skipping fundRepo event'); - return; - } - - if (!baseUsdcBalance) { - logger.info('No base USDC balance found, skipping fundRepo event'); - return; - } - - const ethereumBalanceAmount = ethereumBalance.amount.amount; - const ethBalanceFormatted = formatUnits( - ethereumBalanceAmount, - ethereumBalance.amount.decimals - ); - logger.info(`Ethereum balance is ${ethBalanceFormatted} ETH`, { - amount: ethBalanceFormatted, - address: smartAccount.address, - }); - - const baseUsdcBalanceAmount = baseUsdcBalance.amount.amount; - const usdcBalanceFormatted = formatUnits( - baseUsdcBalanceAmount, - baseUsdcBalance.amount.decimals - ); - logger.info(`Base USDC balance is ${usdcBalanceFormatted} USD`, { - amount: usdcBalanceFormatted, - address: smartAccount.address, - }); - - const ETH_WARNING_THRESHOLD = parseUnits( - String(process.env.ETH_WARNING_THRESHOLD || '0.0001'), - ethereumBalance.amount.decimals - ); - const BASE_USDC_WARNING_THRESHOLD = parseUnits( - String(process.env.BASE_USDC_TRANSFER_THRESHOLD || '5'), - baseUsdcBalance.amount.decimals - ); - - if (ethereumBalanceAmount < ETH_WARNING_THRESHOLD) { - const readableEthWarningThreshold = formatUnits( - ETH_WARNING_THRESHOLD, - ethereumBalance.amount.decimals - ); - logger.error( - `[Critical] Ethereum balance is less than ${readableEthWarningThreshold} ETH, skipping fundRepo event` - ); - logMetric('server_wallet.ethereum_balance_running_low', 1, { - amount: ethBalanceFormatted, - address: smartAccount.address, - }); - return; - } - - if (baseUsdcBalanceAmount < BASE_USDC_WARNING_THRESHOLD) { - logger.info( - 'Base USDC balance is less than threshold, skipping fundRepo event' - ); - return; - } - logger.info(`Base USDC balance is ${usdcBalanceFormatted} USD, funding repo`); - - await safeFundRepo(Number(usdcBalanceFormatted)); -} diff --git a/packages/app/server/src/services/x402AuthenticationService.ts b/packages/app/server/src/services/x402AuthenticationService.ts new file mode 100644 index 000000000..b46748cd9 --- /dev/null +++ b/packages/app/server/src/services/x402AuthenticationService.ts @@ -0,0 +1,55 @@ +import { MarkUp, PrismaClient } from '../generated/prisma'; +import { EchoDbService } from './DbService'; +import { EchoApp, Transaction, TransactionCosts } from '../types'; +import { EchoControlService } from './EchoControlService'; +import logger from 'logger'; + +export class X402AuthenticationService { + private readonly dbService: EchoDbService; + private readonly echoControlService: EchoControlService; + + constructor(prisma: PrismaClient) { + this.dbService = new EchoDbService(prisma); + this.echoControlService = new EchoControlService(prisma); + } + + async authenticateX402Request(headers: Record): Promise<{ + echoApp: EchoApp | null; + markUp: MarkUp | null; + } | null> { + const requestedAppId = headers['x-echo-app-id']; + + logger.info(`Authenticating X402 request for echo app ${requestedAppId}`); + + if (!requestedAppId) { + this.echoControlService.identifyX402Request(null, null); + return null; + } + + const echoApp = await this.dbService.getEchoAppById(requestedAppId); + + const markUp = + await this.dbService.getCurrentMarkupByEchoAppId(requestedAppId); + + this.echoControlService.identifyX402Request(echoApp, markUp); + + return { echoApp, markUp }; + } + + async createX402Transaction( + transaction: Transaction + ): Promise { + const applyEchoMarkup = process.env.APPLY_ECHO_MARKUP === 'true'; + const transactionCosts = + await this.echoControlService.createX402Transaction( + transaction, + applyEchoMarkup + ); + + logger.info( + `Created X402 transaction for echo app ${transaction.metadata.provider}` + ); + + return transactionCosts; + } +} diff --git a/packages/app/server/src/types.ts b/packages/app/server/src/types.ts index b92451816..a0cf7daa0 100644 --- a/packages/app/server/src/types.ts +++ b/packages/app/server/src/types.ts @@ -4,6 +4,8 @@ import { EchoControlService } from 'services/EchoControlService'; import { Response } from 'express'; import { BaseProvider } from 'providers/BaseProvider'; import { Hex } from 'viem'; +import { X402AuthenticationService } from 'services/x402AuthenticationService'; +import { EnumTransactionType } from 'generated/prisma'; export interface EchoApp { id: string; @@ -81,13 +83,15 @@ export interface TransactionRequest extends Transaction { appProfit: Decimal; markUpProfit: Decimal; referralProfit: Decimal; - userId: string; - echoAppId: string; + echoProfit: Decimal; + userId?: string; + echoAppId?: string; apiKeyId?: string; markUpId?: string; spendPoolId?: string; referralCodeId?: string; referrerRewardId?: string; + transactionType?: EnumTransactionType; } export interface ApiKeyValidationResult { @@ -99,6 +103,11 @@ export interface ApiKeyValidationResult { apiKey?: ApiKey; } +export interface X402AuthenticationResult { + echoApp: EchoApp; + echoAppId: string; +} + /** * JWT payload for Echo Access Tokens */ @@ -234,8 +243,11 @@ export type HandlerInput = { isPassthroughProxyRoute: boolean; provider: BaseProvider; isStream: boolean; + x402AuthenticationService: X402AuthenticationService; }; +export type ApiKeyHandlerInput = Omit; + export type X402HandlerInput = Omit; /** @@ -247,3 +259,13 @@ export type SendUserOperationReturnType = { /** The hash of the user operation. This is not the transaction hash which is only available after the operation is completed.*/ userOpHash: Hex; }; + + +export interface TransactionCosts { + rawTransactionCost: Decimal; + totalTransactionCost: Decimal; + totalAppProfit: Decimal; + referralProfit: Decimal; + markUpProfit: Decimal; + echoProfit: Decimal; +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c92b4dd4e..ffae9677b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -65,9 +65,12 @@ importers: '@auth/prisma-adapter': specifier: ^2.10.0 version: 2.10.0(@prisma/client@6.16.0(prisma@6.16.0(magicast@0.3.5)(typescript@5.9.2))(typescript@5.9.2)) + '@coinbase/cdp-sdk': + specifier: ^1.34.0 + version: 1.34.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10) '@coinbase/x402': specifier: ^0.6.4 - version: 0.6.5(@tanstack/react-query@5.90.2(react@19.1.1))(@types/react@19.1.10)(@vercel/blob@0.25.1)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 0.6.5(@tanstack/react-query@5.90.2(react@19.1.1))(@types/react@19.1.10)(@vercel/blob@0.25.1)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@hookform/resolvers': specifier: ^5.2.1 version: 5.2.1(react-hook-form@7.62.0(react@19.1.1)) @@ -349,7 +352,7 @@ importers: version: 2.33.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.11) x402-next: specifier: ^0.5.0 - version: 0.5.0(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(@tanstack/react-query@5.90.2(react@19.1.1))(@types/react@19.1.10)(@vercel/blob@0.25.1)(bufferutil@4.0.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10) + version: 0.5.0(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(@tanstack/react-query@5.90.2(react@19.1.1))(@types/react@19.1.10)(@vercel/blob@0.25.1)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10) zod: specifier: ^4.0.15 version: 4.1.11 @@ -416,7 +419,7 @@ importers: version: 1.34.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10) '@coinbase/x402': specifier: ^0.6.5 - version: 0.6.5(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 0.6.5(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@e2b/code-interpreter': specifier: ^2.0.1 version: 2.0.1 @@ -506,7 +509,7 @@ importers: version: 2.7.0 openai: specifier: ^6.2.0 - version: 6.2.0(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.11) + version: 6.2.0(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.11) prisma: specifier: 6.16.0 version: 6.16.0(magicast@0.3.5)(typescript@5.9.2) @@ -527,7 +530,7 @@ importers: version: 3.17.0 x402-express: specifier: ^0.6.5 - version: 0.6.5(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 0.6.5(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) zod: specifier: ^4.1.11 version: 4.1.11 @@ -12670,11 +12673,11 @@ snapshots: - utf-8-validate - zod - '@coinbase/x402@0.6.5(@tanstack/react-query@5.90.2(react@19.1.1))(@types/react@19.1.10)(@vercel/blob@0.25.1)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@coinbase/x402@0.6.5(@tanstack/react-query@5.90.2(react@19.1.1))(@types/react@19.1.10)(@vercel/blob@0.25.1)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@coinbase/cdp-sdk': 1.34.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10) viem: 2.33.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - x402: 0.6.5(@tanstack/react-query@5.90.2(react@19.1.1))(@types/react@19.1.10)(@vercel/blob@0.25.1)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + x402: 0.6.5(@tanstack/react-query@5.90.2(react@19.1.1))(@types/react@19.1.10)(@vercel/blob@0.25.1)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) zod: 3.25.76 transitivePeerDependencies: - '@azure/app-configuration' @@ -12710,11 +12713,11 @@ snapshots: - utf-8-validate - ws - '@coinbase/x402@0.6.5(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@coinbase/x402@0.6.5(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@coinbase/cdp-sdk': 1.34.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10) viem: 2.33.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - x402: 0.6.5(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + x402: 0.6.5(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) zod: 3.25.76 transitivePeerDependencies: - '@azure/app-configuration' @@ -16053,7 +16056,7 @@ snapshots: dependencies: '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) - '@walletconnect/universal-provider': 2.21.0(@vercel/blob@0.25.1)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2(@types/react@19.1.10)(react@18.3.1) viem: 2.33.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: @@ -16087,7 +16090,7 @@ snapshots: dependencies: '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) - '@walletconnect/universal-provider': 2.21.0(@vercel/blob@0.25.1)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2(@types/react@19.1.10)(react@19.1.0) viem: 2.33.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: @@ -16155,7 +16158,7 @@ snapshots: dependencies: '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) - '@walletconnect/universal-provider': 2.21.0(@vercel/blob@0.25.1)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2 viem: 2.33.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: @@ -16758,7 +16761,7 @@ snapshots: '@reown/appkit-polyfills': 1.7.8 '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.21.0(@vercel/blob@0.25.1)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2(@types/react@19.1.10)(react@18.3.1) viem: 2.33.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: @@ -16795,7 +16798,7 @@ snapshots: '@reown/appkit-polyfills': 1.7.8 '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.21.0(@vercel/blob@0.25.1)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2(@types/react@19.1.10)(react@19.1.0) viem: 2.33.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: @@ -16869,7 +16872,7 @@ snapshots: '@reown/appkit-polyfills': 1.7.8 '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.21.0(@vercel/blob@0.25.1)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2 viem: 2.33.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: @@ -16963,7 +16966,7 @@ snapshots: '@reown/appkit-utils': 1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.10)(react@18.3.1))(zod@3.25.76) '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) '@walletconnect/types': 2.21.0(@vercel/blob@0.25.1) - '@walletconnect/universal-provider': 2.21.0(@vercel/blob@0.25.1)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) bs58: 6.0.0 valtio: 1.13.2(@types/react@19.1.10)(react@18.3.1) viem: 2.33.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -17005,7 +17008,7 @@ snapshots: '@reown/appkit-utils': 1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.9.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.10)(react@19.1.0))(zod@3.25.76) '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) '@walletconnect/types': 2.21.0(@vercel/blob@0.25.1) - '@walletconnect/universal-provider': 2.21.0(@vercel/blob@0.25.1)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) bs58: 6.0.0 valtio: 1.13.2(@types/react@19.1.10)(react@19.1.0) viem: 2.33.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -17089,7 +17092,7 @@ snapshots: '@reown/appkit-utils': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(valtio@1.13.2)(zod@3.25.76) '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) '@walletconnect/types': 2.21.0(@vercel/blob@0.25.1) - '@walletconnect/universal-provider': 2.21.0(@vercel/blob@0.25.1)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) bs58: 6.0.0 valtio: 1.13.2 viem: 2.33.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -17429,10 +17432,6 @@ snapshots: '@socket.io/component-emitter@3.1.2': {} - '@solana-program/compute-budget@0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': - dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana-program/compute-budget@0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': dependencies: '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) @@ -17441,14 +17440,6 @@ snapshots: dependencies: '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana-program/compute-budget@0.8.0(@solana/kit@2.3.0(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': - dependencies: - '@solana/kit': 2.3.0(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - - '@solana-program/token-2022@0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': - dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana-program/token-2022@0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2))': dependencies: '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) @@ -17459,14 +17450,6 @@ snapshots: '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana-program/token-2022@0.4.2(@solana/kit@2.3.0(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': - dependencies: - '@solana/kit': 2.3.0(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - - '@solana-program/token@0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': - dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana-program/token@0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': dependencies: '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) @@ -17475,10 +17458,6 @@ snapshots: dependencies: '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana-program/token@0.5.1(@solana/kit@2.3.0(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': - dependencies: - '@solana/kit': 2.3.0(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/accounts@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': dependencies: '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) @@ -17634,31 +17613,6 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': - dependencies: - '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/functional': 2.3.0(typescript@5.9.2) - '@solana/instructions': 2.3.0(typescript@5.9.2) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/programs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-parsed-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - typescript: 5.9.2 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - - ws - '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) @@ -17709,31 +17663,6 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/kit@2.3.0(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))': - dependencies: - '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/functional': 2.3.0(typescript@5.9.2) - '@solana/instructions': 2.3.0(typescript@5.9.2) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/programs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-parsed-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transaction-confirmation': 2.3.0(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - typescript: 5.9.2 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - - ws - '@solana/nominal-types@2.3.0(typescript@5.9.2)': dependencies: typescript: 5.9.2 @@ -17816,15 +17745,6 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': - dependencies: - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/functional': 2.3.0(typescript@5.9.2) - '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.2) - '@solana/subscribable': 2.3.0(typescript@5.9.2) - typescript: 5.9.2 - ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 2.3.0(typescript@5.9.2) @@ -17851,24 +17771,6 @@ snapshots: '@solana/subscribable': 2.3.0(typescript@5.9.2) typescript: 5.9.2 - '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': - dependencies: - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/fast-stable-stringify': 2.3.0(typescript@5.9.2) - '@solana/functional': 2.3.0(typescript@5.9.2) - '@solana/promises': 2.3.0(typescript@5.9.2) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.2) - '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/subscribable': 2.3.0(typescript@5.9.2) - typescript: 5.9.2 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - - ws - '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 2.3.0(typescript@5.9.2) @@ -18011,23 +17913,6 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': - dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/promises': 2.3.0(typescript@5.9.2) - '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - typescript: 5.9.2 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - - ws - '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) @@ -18062,23 +17947,6 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/transaction-confirmation@2.3.0(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))': - dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/promises': 2.3.0(typescript@5.9.2) - '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - typescript: 5.9.2 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - - ws - '@solana/transaction-messages@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': dependencies: '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) @@ -19576,6 +19444,49 @@ snapshots: - utf-8-validate - zod + '@walletconnect/core@2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@walletconnect/keyvaluestorage': 1.1.1(@vercel/blob@0.25.1) + '@walletconnect/logger': 2.1.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.0(@vercel/blob@0.25.1) + '@walletconnect/utils': 2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/window-getters': 1.0.1 + es-toolkit: 1.33.0 + events: 3.3.0 + uint8arrays: 3.1.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + '@walletconnect/core@2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.11)': dependencies: '@walletconnect/heartbeat': 1.2.2 @@ -19662,6 +19573,49 @@ snapshots: - utf-8-validate - zod + '@walletconnect/core@2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@walletconnect/keyvaluestorage': 1.1.1(@vercel/blob@0.25.1) + '@walletconnect/logger': 2.1.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.1(@vercel/blob@0.25.1) + '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/window-getters': 1.0.1 + es-toolkit: 1.33.0 + events: 3.3.0 + uint8arrays: 3.1.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + '@walletconnect/core@2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.11)': dependencies: '@walletconnect/heartbeat': 1.2.2 @@ -19757,10 +19711,10 @@ snapshots: '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1(@vercel/blob@0.25.1) - '@walletconnect/sign-client': 2.21.1(@vercel/blob@0.25.1)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/types': 2.21.1(@vercel/blob@0.25.1) - '@walletconnect/universal-provider': 2.21.1(@vercel/blob@0.25.1)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/utils': 2.21.1(@vercel/blob@0.25.1)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -19797,10 +19751,10 @@ snapshots: '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1(@vercel/blob@0.25.1) - '@walletconnect/sign-client': 2.21.1(@vercel/blob@0.25.1)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/types': 2.21.1(@vercel/blob@0.25.1) - '@walletconnect/universal-provider': 2.21.1(@vercel/blob@0.25.1)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/utils': 2.21.1(@vercel/blob@0.25.1)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -19877,10 +19831,10 @@ snapshots: '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1(@vercel/blob@0.25.1) - '@walletconnect/sign-client': 2.21.1(@vercel/blob@0.25.1)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/types': 2.21.1(@vercel/blob@0.25.1) - '@walletconnect/universal-provider': 2.21.1(@vercel/blob@0.25.1)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/utils': 2.21.1(@vercel/blob@0.25.1)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -20036,6 +19990,41 @@ snapshots: - utf-8-validate - zod + '@walletconnect/sign-client@2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/core': 2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/logger': 2.1.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.0(@vercel/blob@0.25.1) + '@walletconnect/utils': 2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + '@walletconnect/sign-client@2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.11)': dependencies: '@walletconnect/core': 2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.11) @@ -20106,6 +20095,41 @@ snapshots: - utf-8-validate - zod + '@walletconnect/sign-client@2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/core': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/logger': 2.1.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.1(@vercel/blob@0.25.1) + '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + '@walletconnect/sign-client@2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.11)': dependencies: '@walletconnect/core': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.11) @@ -20240,6 +20264,45 @@ snapshots: - utf-8-validate - zod + '@walletconnect/universal-provider@2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1(@vercel/blob@0.25.1) + '@walletconnect/logger': 2.1.2 + '@walletconnect/sign-client': 2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.21.0(@vercel/blob@0.25.1) + '@walletconnect/utils': 2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + es-toolkit: 1.33.0 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + '@walletconnect/universal-provider@2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.11)': dependencies: '@walletconnect/events': 1.0.1 @@ -20318,6 +20381,45 @@ snapshots: - utf-8-validate - zod + '@walletconnect/universal-provider@2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1(@vercel/blob@0.25.1) + '@walletconnect/logger': 2.1.2 + '@walletconnect/sign-client': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.21.1(@vercel/blob@0.25.1) + '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + es-toolkit: 1.33.0 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + '@walletconnect/universal-provider@2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.11)': dependencies: '@walletconnect/events': 1.0.1 @@ -20400,6 +20502,49 @@ snapshots: - utf-8-validate - zod + '@walletconnect/utils@2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@noble/ciphers': 1.2.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1(@vercel/blob@0.25.1) + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.0(@vercel/blob@0.25.1) + '@walletconnect/window-getters': 1.0.1 + '@walletconnect/window-metadata': 1.0.1 + bs58: 6.0.0 + detect-browser: 5.3.0 + query-string: 7.1.3 + uint8arrays: 3.1.0 + viem: 2.23.2(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + '@walletconnect/utils@2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.11)': dependencies: '@noble/ciphers': 1.2.1 @@ -20486,6 +20631,49 @@ snapshots: - utf-8-validate - zod + '@walletconnect/utils@2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@noble/ciphers': 1.2.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1(@vercel/blob@0.25.1) + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.1(@vercel/blob@0.25.1) + '@walletconnect/window-getters': 1.0.1 + '@walletconnect/window-metadata': 1.0.1 + bs58: 6.0.0 + detect-browser: 5.3.0 + query-string: 7.1.3 + uint8arrays: 3.1.0 + viem: 2.23.2(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + '@walletconnect/utils@2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.11)': dependencies: '@noble/ciphers': 1.2.1 @@ -22028,7 +22216,7 @@ snapshots: '@typescript-eslint/parser': 8.34.1(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) eslint: 9.35.0(jiti@2.5.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.34.1(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.35.0(jiti@2.5.1)))(eslint@9.35.0(jiti@2.5.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@9.35.0(jiti@2.5.1)) eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.34.1(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.35.0(jiti@2.5.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.35.0(jiti@2.5.1)) eslint-plugin-react: 7.37.5(eslint@9.35.0(jiti@2.5.1)) @@ -22048,7 +22236,7 @@ snapshots: '@typescript-eslint/parser': 8.34.1(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) eslint: 9.35.0(jiti@2.5.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.34.1(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.35.0(jiti@2.5.1)))(eslint@9.35.0(jiti@2.5.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@9.35.0(jiti@2.5.1)) eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.34.1(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.35.0(jiti@2.5.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.35.0(jiti@2.5.1)) eslint-plugin-react: 7.37.5(eslint@9.35.0(jiti@2.5.1)) @@ -22072,7 +22260,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.34.1(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.35.0(jiti@2.5.1)))(eslint@9.35.0(jiti@2.5.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@9.35.0(jiti@2.5.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.1 @@ -22087,14 +22275,14 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.34.1(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.34.1(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.35.0(jiti@2.5.1)))(eslint@9.35.0(jiti@2.5.1)))(eslint@9.35.0(jiti@2.5.1)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.34.1(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@9.35.0(jiti@2.5.1)))(eslint@9.35.0(jiti@2.5.1)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.34.1(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) eslint: 9.35.0(jiti@2.5.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.34.1(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.35.0(jiti@2.5.1)))(eslint@9.35.0(jiti@2.5.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@9.35.0(jiti@2.5.1)) transitivePeerDependencies: - supports-color @@ -22109,7 +22297,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.35.0(jiti@2.5.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.34.1(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.34.1(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.35.0(jiti@2.5.1)))(eslint@9.35.0(jiti@2.5.1)))(eslint@9.35.0(jiti@2.5.1)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.34.1(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@9.35.0(jiti@2.5.1)))(eslint@9.35.0(jiti@2.5.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -25050,9 +25238,9 @@ snapshots: ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) zod: 4.1.11 - openai@6.2.0(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.11): + openai@6.2.0(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.11): optionalDependencies: - ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) zod: 4.1.11 openapi-fetch@0.13.8: @@ -28332,13 +28520,13 @@ snapshots: bufferutil: 4.0.9 utf-8-validate: 5.0.10 - x402-express@0.6.5(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + x402-express@0.6.5(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: '@coinbase/cdp-sdk': 1.34.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10) - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) express: 4.21.2 viem: 2.33.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - x402: 0.6.5(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + x402: 0.6.5(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) zod: 3.25.76 transitivePeerDependencies: - '@azure/app-configuration' @@ -28412,7 +28600,7 @@ snapshots: - utf-8-validate - ws - x402-next@0.5.0(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(@tanstack/react-query@5.90.2(react@19.1.1))(@types/react@19.1.10)(@vercel/blob@0.25.1)(bufferutil@4.0.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10): + x402-next@0.5.0(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(@tanstack/react-query@5.90.2(react@19.1.1))(@types/react@19.1.10)(@vercel/blob@0.25.1)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10): dependencies: '@coinbase/cdp-sdk': 1.34.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10) next: 15.5.2(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -28581,14 +28769,14 @@ snapshots: - utf-8-validate - ws - x402@0.6.5(@tanstack/react-query@5.90.2(react@19.1.1))(@types/react@19.1.10)(@vercel/blob@0.25.1)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + x402@0.6.5(@tanstack/react-query@5.90.2(react@19.1.1))(@types/react@19.1.10)(@vercel/blob@0.25.1)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: '@scure/base': 1.2.6 - '@solana-program/compute-budget': 0.8.0(@solana/kit@2.3.0(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))) - '@solana-program/token': 0.5.1(@solana/kit@2.3.0(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))) - '@solana-program/token-2022': 0.4.2(@solana/kit@2.3.0(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))) - '@solana/kit': 2.3.0(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/transaction-confirmation': 2.3.0(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana-program/compute-budget': 0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + '@solana-program/token': 0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + '@solana-program/token-2022': 0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) viem: 2.33.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) wagmi: 2.17.5(@tanstack/react-query@5.90.2(react@19.1.1))(@types/react@19.1.10)(@vercel/blob@0.25.1)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.33.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.11))(zod@3.25.76) zod: 3.25.76 @@ -28625,14 +28813,14 @@ snapshots: - utf-8-validate - ws - x402@0.6.5(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + x402@0.6.5(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: '@scure/base': 1.2.6 - '@solana-program/compute-budget': 0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))) - '@solana-program/token': 0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))) - '@solana-program/token-2022': 0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))) - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana-program/compute-budget': 0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + '@solana-program/token': 0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + '@solana-program/token-2022': 0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) viem: 2.33.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) wagmi: 2.17.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.33.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.11))(zod@3.25.76) zod: 3.25.76