From 14a0287d9936c348a0181680dcd178aa1d075d14 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Fri, 28 Nov 2025 12:25:44 -0300 Subject: [PATCH 1/7] =?UTF-8?q?=E2=9C=A8=20Migrate=20dapp=20to=20@defindex?= =?UTF-8?q?/sdk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add DefindexSDK client and API routes - Add UserContext for wallet management - Add useDefindexSDK hook with createVault and createVaultAutoInvest - Remove deprecated stellar-react integration files - Update MainProvider to use new SDK --- apps/dapp/package.json | 3 +- .../app/api/defindex/create-vault/route.ts | 49 + apps/dapp/src/app/api/defindex/send/route.ts | 38 + .../app/api/defindex/vault-balance/route.ts | 51 + .../src/app/api/defindex/vault-info/route.ts | 39 + apps/dapp/src/contexts/UserContext.tsx | 172 ++++ apps/dapp/src/helpers/getTokenInfo.ts | 22 - apps/dapp/src/helpers/vault.ts | 179 ---- apps/dapp/src/hooks/useDefindexSDK.ts | 257 +++++ apps/dapp/src/hooks/useFactory.ts | 112 --- apps/dapp/src/hooks/useVault.ts | 281 ------ apps/dapp/src/lib/defindexClient.ts | 10 + apps/dapp/src/providers/MainProvider.tsx | 6 +- apps/dapp/src/providers/SorobanProvider.tsx | 29 - yarn.lock | 877 +++++++++++++++++- 15 files changed, 1473 insertions(+), 652 deletions(-) create mode 100644 apps/dapp/src/app/api/defindex/create-vault/route.ts create mode 100644 apps/dapp/src/app/api/defindex/send/route.ts create mode 100644 apps/dapp/src/app/api/defindex/vault-balance/route.ts create mode 100644 apps/dapp/src/app/api/defindex/vault-info/route.ts create mode 100644 apps/dapp/src/contexts/UserContext.tsx delete mode 100644 apps/dapp/src/helpers/getTokenInfo.ts delete mode 100644 apps/dapp/src/helpers/vault.ts create mode 100644 apps/dapp/src/hooks/useDefindexSDK.ts delete mode 100644 apps/dapp/src/hooks/useFactory.ts delete mode 100644 apps/dapp/src/hooks/useVault.ts create mode 100644 apps/dapp/src/lib/defindexClient.ts delete mode 100644 apps/dapp/src/providers/SorobanProvider.tsx diff --git a/apps/dapp/package.json b/apps/dapp/package.json index d2392693..317c679a 100644 --- a/apps/dapp/package.json +++ b/apps/dapp/package.json @@ -10,6 +10,8 @@ }, "dependencies": { "@chakra-ui/react": "^3.17.0", + "@creit.tech/stellar-wallets-kit": "^1.9.5", + "@defindex/sdk": "^0.1.2", "@emotion/react": "^11.14.0", "@stellar/stellar-sdk": "^14.1.1", "next": "15.3.1", @@ -17,7 +19,6 @@ "react": "^18.0.0", "react-dom": "^18.0.0", "react-icons": "^5.5.0", - "stellar-react": "^0.0.6", "swr": "^2.3.3" }, "devDependencies": { diff --git a/apps/dapp/src/app/api/defindex/create-vault/route.ts b/apps/dapp/src/app/api/defindex/create-vault/route.ts new file mode 100644 index 00000000..1d87bb75 --- /dev/null +++ b/apps/dapp/src/app/api/defindex/create-vault/route.ts @@ -0,0 +1,49 @@ +import { defindexClient } from '@/lib/defindexClient'; +import { CreateDefindexVault, CreateVaultAutoInvestParams, SupportedNetworks } from '@defindex/sdk'; +import { NextRequest, NextResponse } from 'next/server'; + +export async function POST(request: NextRequest) { + try { + const body = await request.json(); + const { vaultConfig, network, withDeposit } = body; + + if (!vaultConfig) { + return NextResponse.json( + { error: 'vaultConfig is required' }, + { status: 400 } + ); + } + + if (!network || !['mainnet', 'testnet'].includes(network)) { + return NextResponse.json( + { error: 'network must be mainnet or testnet' }, + { status: 400 } + ); + } + + const sdkNetwork = network === 'mainnet' + ? SupportedNetworks.MAINNET + : SupportedNetworks.TESTNET; + + let result; + if (withDeposit) { + result = await defindexClient.createVaultAutoInvest( + vaultConfig as CreateVaultAutoInvestParams, + sdkNetwork + ); + } else { + result = await defindexClient.createVault( + vaultConfig as CreateDefindexVault, + sdkNetwork + ); + } + + return NextResponse.json({ data: result }); + } catch (error) { + console.error('Error creating vault:', error); + return NextResponse.json( + { error: error instanceof Error ? error.message : 'Failed to create vault' }, + { status: 500 } + ); + } +} diff --git a/apps/dapp/src/app/api/defindex/send/route.ts b/apps/dapp/src/app/api/defindex/send/route.ts new file mode 100644 index 00000000..faa36760 --- /dev/null +++ b/apps/dapp/src/app/api/defindex/send/route.ts @@ -0,0 +1,38 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { defindexClient } from '@/lib/defindexClient'; +import { SupportedNetworks } from '@defindex/sdk'; + +export async function POST(request: NextRequest) { + try { + const body = await request.json(); + const { signedXdr, network } = body; + + if (!signedXdr) { + return NextResponse.json( + { error: 'signedXdr is required' }, + { status: 400 } + ); + } + + if (!network || !['mainnet', 'testnet'].includes(network)) { + return NextResponse.json( + { error: 'network must be mainnet or testnet' }, + { status: 400 } + ); + } + + const sdkNetwork = network === 'mainnet' + ? SupportedNetworks.MAINNET + : SupportedNetworks.TESTNET; + + const result = await defindexClient.sendTransaction(signedXdr, sdkNetwork); + + return NextResponse.json({ data: result }); + } catch (error) { + console.error('Error sending transaction:', error); + return NextResponse.json( + { error: error instanceof Error ? error.message : 'Failed to send transaction' }, + { status: 500 } + ); + } +} diff --git a/apps/dapp/src/app/api/defindex/vault-balance/route.ts b/apps/dapp/src/app/api/defindex/vault-balance/route.ts new file mode 100644 index 00000000..037e1a0e --- /dev/null +++ b/apps/dapp/src/app/api/defindex/vault-balance/route.ts @@ -0,0 +1,51 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { defindexClient } from '@/lib/defindexClient'; +import { SupportedNetworks } from '@defindex/sdk'; + +export async function GET(request: NextRequest) { + try { + const { searchParams } = new URL(request.url); + const vaultAddress = searchParams.get('vaultAddress'); + const userAddress = searchParams.get('userAddress'); + const network = searchParams.get('network') as 'mainnet' | 'testnet'; + + if (!vaultAddress) { + return NextResponse.json( + { error: 'vaultAddress is required' }, + { status: 400 } + ); + } + + if (!userAddress) { + return NextResponse.json( + { error: 'userAddress is required' }, + { status: 400 } + ); + } + + if (!network || !['mainnet', 'testnet'].includes(network)) { + return NextResponse.json( + { error: 'network must be mainnet or testnet' }, + { status: 400 } + ); + } + + const sdkNetwork = network === 'mainnet' + ? SupportedNetworks.MAINNET + : SupportedNetworks.TESTNET; + + const balance = await defindexClient.getVaultBalance( + vaultAddress, + userAddress, + sdkNetwork + ); + + return NextResponse.json({ data: balance }); + } catch (error) { + console.error('Error fetching vault balance:', error); + return NextResponse.json( + { error: error instanceof Error ? error.message : 'Failed to fetch vault balance' }, + { status: 500 } + ); + } +} diff --git a/apps/dapp/src/app/api/defindex/vault-info/route.ts b/apps/dapp/src/app/api/defindex/vault-info/route.ts new file mode 100644 index 00000000..7fae6b5c --- /dev/null +++ b/apps/dapp/src/app/api/defindex/vault-info/route.ts @@ -0,0 +1,39 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { defindexClient } from '@/lib/defindexClient'; +import { SupportedNetworks } from '@defindex/sdk'; + +export async function GET(request: NextRequest) { + try { + const { searchParams } = new URL(request.url); + const vaultAddress = searchParams.get('vaultAddress'); + const network = searchParams.get('network') as 'mainnet' | 'testnet'; + + if (!vaultAddress) { + return NextResponse.json( + { error: 'vaultAddress is required' }, + { status: 400 } + ); + } + + if (!network || !['mainnet', 'testnet'].includes(network)) { + return NextResponse.json( + { error: 'network must be mainnet or testnet' }, + { status: 400 } + ); + } + + const sdkNetwork = network === 'mainnet' + ? SupportedNetworks.MAINNET + : SupportedNetworks.TESTNET; + + const vaultInfo = await defindexClient.getVaultInfo(vaultAddress, sdkNetwork); + + return NextResponse.json({ data: vaultInfo }); + } catch (error) { + console.error('Error fetching vault info:', error); + return NextResponse.json( + { error: error instanceof Error ? error.message : 'Failed to fetch vault info' }, + { status: 500 } + ); + } +} diff --git a/apps/dapp/src/contexts/UserContext.tsx b/apps/dapp/src/contexts/UserContext.tsx new file mode 100644 index 00000000..d06d0a6d --- /dev/null +++ b/apps/dapp/src/contexts/UserContext.tsx @@ -0,0 +1,172 @@ +"use client"; + +import { + allowAllModules, + FREIGHTER_ID, + ISupportedWallet, + StellarWalletsKit, + WalletNetwork, +} from "@creit.tech/stellar-wallets-kit"; +import { LedgerModule } from "@creit.tech/stellar-wallets-kit/modules/ledger.module"; +import { + WalletConnectAllowedMethods, + WalletConnectModule, +} from "@creit.tech/stellar-wallets-kit/modules/walletconnect.module"; +import { + createContext, + ReactNode, + useContext, + useEffect, + useRef, + useState, +} from "react"; + +// Network configuration +const NETWORKS = { + mainnet: { + network: WalletNetwork.PUBLIC, + networkPassphrase: "Public Global Stellar Network ; September 2015", + sorobanRpcUrl: "https://soroban-rpc.creit.tech/", + horizonRpcUrl: "https://horizon.stellar.org", + }, + testnet: { + network: WalletNetwork.TESTNET, + networkPassphrase: "Test SDF Network ; September 2015", + sorobanRpcUrl: "https://soroban-testnet.stellar.org/", + horizonRpcUrl: "https://horizon-testnet.stellar.org", + }, +} as const; + +type NetworkType = keyof typeof NETWORKS; + +type NetworkConfig = typeof NETWORKS[NetworkType]; + +interface UserContextProps { + address: string | null; + setAddress: (address: string | null) => void; + kit: StellarWalletsKit | null; + connectWallet: () => Promise; + disconnect: () => void; + signTransaction: (xdr: string, userAddress: string) => Promise; + selectedWallet: ISupportedWallet | null; + activeNetwork: NetworkType; + setActiveNetwork: (network: NetworkType) => void; + networkConfig: NetworkConfig; +} + +interface UserProviderProps { + children: ReactNode; +} + +export const UserContext = createContext({ + address: null, + setAddress: () => {}, + kit: null, + connectWallet: async () => {}, + disconnect: () => {}, + signTransaction: async () => "", + selectedWallet: null, + activeNetwork: "mainnet", + setActiveNetwork: () => {}, + networkConfig: NETWORKS.mainnet, +}); + +export const UserProvider = ({ children }: UserProviderProps) => { + const [address, setAddress] = useState(null); + const [kit, setKit] = useState(null); + const [selectedWallet, setSelectedWallet] = useState(null); + const [activeNetwork, setActiveNetwork] = useState("mainnet"); + const kitRef = useRef(null); + + const networkConfig = NETWORKS[activeNetwork]; + + useEffect(() => { + if (typeof window !== "undefined") { + try { + const walletKit = new StellarWalletsKit({ + network: networkConfig.network, + selectedWalletId: FREIGHTER_ID, + modules: [ + ...allowAllModules(), + new LedgerModule(), + new WalletConnectModule({ + url: typeof window !== "undefined" ? window.location.origin : "https://app.defindex.io", + projectId: "4ee1d28f1fe3c70aa8ebc4677e623e1d", + method: WalletConnectAllowedMethods.SIGN, + description: "DeFindex - DeFi Yield Infrastructure", + name: "DeFindex", + icons: ["/favicon.ico"], + network: networkConfig.network, + }), + ], + }); + kitRef.current = walletKit; + setKit(walletKit); + } catch (error) { + console.error("Failed to initialize wallet kit:", error); + } + } + }, [networkConfig.network]); + + const connectWallet = async () => { + if (!kit) return; + + await kit.openModal({ + onWalletSelected: async (option: ISupportedWallet) => { + kit.setWallet(option.id); + setSelectedWallet(option); + const { address } = await kit.getAddress(); + setAddress(address); + }, + }); + }; + + const disconnect = () => { + if (kit) { + kit.disconnect(); + setAddress(null); + setSelectedWallet(null); + } + }; + + const signTransaction = async (xdr: string, userAddress: string): Promise => { + if (!kit) throw new Error("Wallet kit not initialized"); + + const { signedTxXdr } = await kit.signTransaction(xdr, { + address: userAddress, + networkPassphrase: networkConfig.networkPassphrase, + }); + + if (!signedTxXdr) throw new Error("Failed to sign transaction"); + return signedTxXdr; + }; + + const handleSetActiveNetwork = (network: NetworkType) => { + setActiveNetwork(network); + // Disconnect wallet when switching networks + if (address) { + disconnect(); + } + }; + + return ( + + {children} + + ); +}; + +export const useUser = () => useContext(UserContext); diff --git a/apps/dapp/src/helpers/getTokenInfo.ts b/apps/dapp/src/helpers/getTokenInfo.ts deleted file mode 100644 index 1c364ff2..00000000 --- a/apps/dapp/src/helpers/getTokenInfo.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { contractInvoke, SorobanContextType } from "stellar-react"; -import { scValToNative, xdr } from "@stellar/stellar-sdk"; - - -export const getTokenSymbol = async ( - tokenId: string, - sorobanContext: SorobanContextType, -): Promise => { - try { - const result = await contractInvoke({ - contractAddress: tokenId as string, - method: 'symbol', - args: [], - sorobanContext, - }); - - return scValToNative(result as xdr.ScVal); - } catch (error) { - console.log(`Error fetching token symbol for ${tokenId}:`, error); - return null; - } -}; diff --git a/apps/dapp/src/helpers/vault.ts b/apps/dapp/src/helpers/vault.ts deleted file mode 100644 index 4147eb6b..00000000 --- a/apps/dapp/src/helpers/vault.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { Asset } from "@/contexts" -import { Address, nativeToScVal, xdr } from "@stellar/stellar-sdk"; - -export function getAssetAmountsSCVal(assets: Asset[]) { - return assets.map((asset, index) => { - const parsedAmount = assets[index]?.amount || 0; - const truncatedAmount = Math.floor(parsedAmount * 1e7) / 1e7; - const convertedAmount = Number(truncatedAmount) * Math.pow(10, 7) - if (assets[index]?.amount === 0) return nativeToScVal(0, { type: "i128" }); - return nativeToScVal(Math.ceil(convertedAmount), { type: "i128" }); - }) -} - -export function getAssetParamsSCVal(assets: Asset[]) { - return assets.map((asset) => { - const strategyParamsScVal = asset.strategies.map((param) => { - return xdr.ScVal.scvMap([ - new xdr.ScMapEntry({ - key: xdr.ScVal.scvSymbol('address'), - val: new Address(param.address).toScVal(), - }), - new xdr.ScMapEntry({ - key: xdr.ScVal.scvSymbol('name'), - val: nativeToScVal(param.name, { type: "string" }), - }), - new xdr.ScMapEntry({ - key: xdr.ScVal.scvSymbol('paused'), - val: nativeToScVal(false, { type: "bool" }), - }), - ]); - }); - const strategyParamsScValVec = xdr.ScVal.scvVec(strategyParamsScVal); - return xdr.ScVal.scvMap([ - new xdr.ScMapEntry({ - key: xdr.ScVal.scvSymbol('address'), - val: new Address(asset.address).toScVal(), - }), - new xdr.ScMapEntry({ - key: xdr.ScVal.scvSymbol('strategies'), - val: strategyParamsScValVec, - }), - ]); - }); -} - -export function getCreateDeFindexVaultParams( - emergency_manager: string, - rebalance_manager: string, - fee_receiver: string, - manager: string, - vault_fee: number, - vault_name: string, - vault_symbol: string, - asset_allocations: xdr.ScVal[], - router_address: string, - upgradable: boolean, -): xdr.ScVal[] { - const roles = xdr.ScVal.scvMap([ - new xdr.ScMapEntry({ - key: xdr.ScVal.scvU32(0), - val: new Address(emergency_manager).toScVal(), - }), - new xdr.ScMapEntry({ - key: xdr.ScVal.scvU32(1), - val: new Address(fee_receiver).toScVal(), - }), - new xdr.ScMapEntry({ - key: xdr.ScVal.scvU32(2), - val: new Address(manager).toScVal(), - }), - new xdr.ScMapEntry({ - key: xdr.ScVal.scvU32(3), - val: new Address(rebalance_manager).toScVal(), - }), - ]); - - const nameSymbol = xdr.ScVal.scvMap([ - new xdr.ScMapEntry({ - key: xdr.ScVal.scvString("name"), - val: nativeToScVal(vault_name ?? "TestVault", { type: "string" }), - }), - new xdr.ScMapEntry({ - key: xdr.ScVal.scvString("symbol"), - val: nativeToScVal(vault_symbol ?? "TSTV", { type: "string" }), - }), - ]); - - return [ - roles, - nativeToScVal(vault_fee * 100, { type: "u32" }), // Converting vault_fee to basis points (bps) - xdr.ScVal.scvVec(asset_allocations), - new Address(router_address).toScVal(), - nameSymbol, - nativeToScVal(!!upgradable, { type: "bool" }) - ]; -} - -export function getCreateDeFindexVaultDepositParams( - caller: string, - emergency_manager: string, - rebalance_manager: string, - fee_receiver: string, - manager: string, - vault_fee: number, - vault_name: string, - vault_symbol: string, - asset_allocations: xdr.ScVal[], - router_address: string, - upgradable: boolean, - assets: Asset[] -){ - const defindexVaultParams = getCreateDeFindexVaultParams( - emergency_manager, - rebalance_manager, - fee_receiver, - manager, - vault_fee, - vault_name, - vault_symbol, - asset_allocations, - router_address, - upgradable - ); - const callerAddress = new Address(caller); - const amounts = getAssetAmountsSCVal(assets); - return [callerAddress.toScVal(), ...defindexVaultParams, xdr.ScVal.scvVec(amounts)]; -} - -/* export function mapInstructionsToParams( - instructions: RebalanceInstruction[] -): xdr.ScVal { - return xdr.ScVal.scvVec( - instructions.map((instruction) => { - let SCALING_FACTOR = 10 ** 7; - let parsedAmount = Math.ceil(instruction.amount * SCALING_FACTOR); - switch (instruction.action) { - case ActionType.Invest: - return xdr.ScVal.scvVec([ - // Invest action - xdr.ScVal.scvSymbol("Invest"), - new Address(instruction.strategy).toScVal(), - nativeToScVal(parsedAmount, { type: "i128" }), // amount - ]); - case ActionType.Unwind: - return xdr.ScVal.scvVec([ - // Unwind action - xdr.ScVal.scvSymbol("Unwind"), - new Address(instruction.strategy).toScVal(), - nativeToScVal(parsedAmount, { type: "i128" }), // amount - ]); - - case ActionType.SwapExactIn: - // Handle SwapExactIn action - return xdr.ScVal.scvVec([ - xdr.ScVal.scvSymbol("SwapExactIn"), - new Address(instruction.swapDetailsExactIn.token_in).toScVal(), - new Address(instruction.swapDetailsExactIn.token_out).toScVal(), - nativeToScVal(Math.ceil(instruction.swapDetailsExactIn.amount_in * SCALING_FACTOR), { type: "i128" }), - nativeToScVal(Math.ceil(instruction.swapDetailsExactIn.amount_out_min * SCALING_FACTOR), { type: "i128" }), - nativeToScVal(instruction.swapDetailsExactIn.deadline, { type: "u64" }), - ]); - - case ActionType.SwapExactOut: - // Handle SwapExactOut action - return xdr.ScVal.scvVec([ - xdr.ScVal.scvSymbol("SwapExactOut"), - new Address(instruction.swapDetailsExactOut.token_in).toScVal(), - new Address(instruction.swapDetailsExactOut.token_out).toScVal(), - nativeToScVal(Math.ceil(instruction.swapDetailsExactOut.amount_out * SCALING_FACTOR), { type: "i128" }), - nativeToScVal(Math.ceil(instruction.swapDetailsExactOut.amount_in_max * SCALING_FACTOR), { type: "i128" }), - nativeToScVal(instruction.swapDetailsExactOut.deadline, { type: "u64" }), - ]); - - default: - throw new Error(`Unsupported action type: ${instruction.action}`); - } - }) - ); -} */ \ No newline at end of file diff --git a/apps/dapp/src/hooks/useDefindexSDK.ts b/apps/dapp/src/hooks/useDefindexSDK.ts new file mode 100644 index 00000000..817806c7 --- /dev/null +++ b/apps/dapp/src/hooks/useDefindexSDK.ts @@ -0,0 +1,257 @@ +import { useCallback } from 'react'; +import { useUser } from '@/contexts/UserContext'; + +// Types based on SDK +interface VaultAsset { + address: string; + strategies: Array<{ + address: string; + name: string; + paused: boolean; + }>; +} + +interface VaultInfoResponse { + name: string; + symbol: string; + roles: { + manager: string; + emergencyManager: string; + rebalanceManager: string; + feeReceiver: string; + }; + assets: VaultAsset[]; + totalManagedFunds: string[]; + feesBps: { + vaultFee: number; + defindexFee: number; + }; + apy?: number; +} + +interface VaultBalanceResponse { + dfTokens: string; + underlyingBalance: string[]; +} + +// Config for createVault (no deposit) +interface CreateVaultConfig { + roles: { + 0: string; // Emergency Manager + 1: string; // Fee Receiver + 2: string; // Manager + 3: string; // Rebalance Manager + }; + vault_fee_bps: number; + assets: Array<{ + address: string; + strategies: Array<{ + address: string; + name: string; + paused: boolean; + }>; + }>; + name_symbol: { + name: string; + symbol: string; + }; + upgradable: boolean; + caller: string; +} + +// Config for createVaultAutoInvest (with deposit + auto-invest) +interface CreateVaultAutoInvestConfig { + caller: string; + roles: { + emergencyManager: string; + rebalanceManager: string; + feeReceiver: string; + manager: string; + }; + name: string; + symbol: string; + vaultFee: number; // basis points + upgradable: boolean; + assets: Array<{ + address: string; + symbol: string; + amount: number; // Total amount in stroops + strategies: Array<{ + address: string; + name: string; + amount: number; // Amount to invest in this strategy + }>; + }>; +} + +interface CreateVaultResponse { + xdr: string; + predictedVaultAddress?: string; + warning?: string; +} + +interface SendTransactionResponse { + txHash?: string; + hash?: string; + status: string; + predictedVaultAddress?: string; +} + +export function useDefindexSDK() { + const { address, signTransaction, activeNetwork } = useUser(); + + // Get vault information + const getVaultInfo = useCallback( + async (vaultAddress: string): Promise => { + const response = await fetch( + `/api/defindex/vault-info?vaultAddress=${vaultAddress}&network=${activeNetwork}` + ); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.error || 'Failed to fetch vault info'); + } + + const { data } = await response.json(); + return data; + }, + [activeNetwork] + ); + + // Get user balance in vault + const getVaultBalance = useCallback( + async (vaultAddress: string, userAddress?: string): Promise => { + const user = userAddress || address; + if (!user) { + throw new Error('User address is required'); + } + + const response = await fetch( + `/api/defindex/vault-balance?vaultAddress=${vaultAddress}&userAddress=${user}&network=${activeNetwork}` + ); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.error || 'Failed to fetch vault balance'); + } + + const { data } = await response.json(); + return data; + }, + [activeNetwork, address] + ); + + // Create vault without deposit + const createVault = useCallback( + async (vaultConfig: CreateVaultConfig): Promise => { + if (!address) { + throw new Error('Wallet not connected'); + } + + // Step 1: Build XDR + const buildResponse = await fetch('/api/defindex/create-vault', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + vaultConfig, + network: activeNetwork, + withDeposit: false, + }), + }); + + if (!buildResponse.ok) { + const error = await buildResponse.json(); + throw new Error(error.error || 'Failed to build vault creation transaction'); + } + + const { data: buildResult } = await buildResponse.json() as { data: CreateVaultResponse }; + const { xdr } = buildResult; + + // Step 2: Sign XDR + const signedXdr = await signTransaction(xdr, address); + + // Step 3: Send signed transaction + const sendResponse = await fetch('/api/defindex/send', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + signedXdr, + network: activeNetwork, + }), + }); + + if (!sendResponse.ok) { + const error = await sendResponse.json(); + throw new Error(error.error || 'Failed to send transaction'); + } + + const { data: sendResult } = await sendResponse.json(); + return sendResult; + }, + [activeNetwork, address, signTransaction] + ); + + // Create vault with deposit and auto-invest into strategies + const createVaultAutoInvest = useCallback( + async (vaultConfig: CreateVaultAutoInvestConfig): Promise => { + if (!address) { + throw new Error('Wallet not connected'); + } + + // Step 1: Build XDR + const buildResponse = await fetch('/api/defindex/create-vault', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + vaultConfig, + network: activeNetwork, + withDeposit: true, + }), + }); + + if (!buildResponse.ok) { + const error = await buildResponse.json(); + throw new Error(error.error || 'Failed to build vault creation transaction'); + } + + const { data: buildResult } = await buildResponse.json() as { data: CreateVaultResponse }; + const { xdr, predictedVaultAddress } = buildResult; + + // Step 2: Sign XDR + const signedXdr = await signTransaction(xdr, address); + + // Step 3: Send signed transaction + const sendResponse = await fetch('/api/defindex/send', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + signedXdr, + network: activeNetwork, + }), + }); + + if (!sendResponse.ok) { + const error = await sendResponse.json(); + throw new Error(error.error || 'Failed to send transaction'); + } + + const { data: sendResult } = await sendResponse.json(); + return { + ...sendResult, + predictedVaultAddress, + }; + }, + [activeNetwork, address, signTransaction] + ); + + return { + getVaultInfo, + getVaultBalance, + createVault, + createVaultAutoInvest, + address, + activeNetwork, + }; +} + +export type { CreateVaultConfig, CreateVaultAutoInvestConfig }; diff --git a/apps/dapp/src/hooks/useFactory.ts b/apps/dapp/src/hooks/useFactory.ts deleted file mode 100644 index 9cc7df6c..00000000 --- a/apps/dapp/src/hooks/useFactory.ts +++ /dev/null @@ -1,112 +0,0 @@ -import * as StellarSdk from '@stellar/stellar-sdk'; -import { useCallback, useEffect, useState } from "react"; -import { contractInvoke, SorobanContextType, useSorobanReact } from 'stellar-react'; - -import { getNetworkName } from "@/helpers/networkName"; -import { TxResponse } from 'stellar-react/dist/contracts/types'; -import { usePublicAddresses } from './usePublicAddresses'; - -export enum FactoryMethod { - CREATE_DEFINDEX_VAULT = "create_defindex_vault", - CREATE_DEFINDEX_VAULT_DEPOSIT = "create_defindex_vault_deposit", - TOTAL_VAULTS = "total_vaults", - GET_VAULT_BY_INDEX = "get_vault_by_index", - DEFINDEX_FEE = "defindex_fee", -} - -const isObject = (val: unknown) => typeof val === 'object' && val !== null && !Array.isArray(val); - -const findFactoryAddress = (publicAddresses: Record): string | undefined => { - if (!publicAddresses || Object.keys(publicAddresses).length === 0) { - throw new Error('No public addresses found'); - } - const factoryAddress = publicAddresses['defindex_factory']; - if (!factoryAddress) { - throw new Error('Factory address not found in public addresses'); - } - return factoryAddress; -} -export const useFactory = () => { - const sorobanContext: SorobanContextType = useSorobanReact(); - const publicAddresses = usePublicAddresses(getNetworkName(sorobanContext.activeNetwork)); - const { activeNetwork } = sorobanContext; - if (!activeNetwork) { - throw new Error('No active network found'); - } - const [address, setAddress] = useState(); - const networkName = getNetworkName(activeNetwork); - useEffect(() => { - if (!sorobanContext || !publicAddresses) return; - if (networkName !== 'mainnet' && networkName !== 'testnet') { - throw new Error(`Invalid network when fetching factory address: ${networkName}. It should be mainnet or testnet`); - } - - if (publicAddresses.isLoading) return; - if (publicAddresses.error || !publicAddresses.data) { - throw new Error(`Failed to fetch public addresses: ${publicAddresses.error}`); - } - const factoryAddress = findFactoryAddress(publicAddresses.data); - setAddress(factoryAddress); - - }, [activeNetwork, publicAddresses, networkName, sorobanContext]); - - return { address }; -} - -export function useFactoryCallback() { - const sorobanContext = useSorobanReact(); - const {activeNetwork} = sorobanContext; - const publicAddresses = usePublicAddresses( - activeNetwork? getNetworkName(activeNetwork) : 'mainnet' - ).data; - const { address: factoryAddress } = useFactory(); - if (!activeNetwork) { - throw new Error('No active network found'); - } - - return useCallback( - async (method: FactoryMethod, args?: StellarSdk.xdr.ScVal[], signAndSend?: boolean) => { - try { - let result: TxResponse; - if(!factoryAddress) { - const fallbackAddress = findFactoryAddress(publicAddresses); - if (!fallbackAddress) { - throw new Error('Failed to fetch fallback address'); - } - result = (await contractInvoke({ - contractAddress: fallbackAddress, - method: method, - args: args, - sorobanContext, - signAndSend: signAndSend, - reconnectAfterTx: false, - })) as TxResponse; - return result; - } else { - result = (await contractInvoke({ - contractAddress: factoryAddress as string, - method: method, - args: args, - sorobanContext, - signAndSend: signAndSend, - reconnectAfterTx: false, - })) as TxResponse; - } - if (!signAndSend) return result; - if ( - isObject(result) && - result?.status !== StellarSdk.rpc.Api.GetTransactionStatus.SUCCESS - ) throw result; - return result - } catch (e: unknown) { - const error = e as Error; - if (error.message.includes('ExistingValue')) throw new Error('Index already exists.') - if (error.message.includes('The user rejected')) throw new Error('Request denied by user. Please try to sign again.') - if (error.message.includes('UnexpectedSize')) throw new Error('Invalid arguments length.') - if (error.message.includes('Error(Contract, #10)')) throw new Error('Insufficient funds.') - if (error.message.includes('invoke non-existent contract function')) throw new Error('Contract function does not exist.') - if (error.message.includes('MissingValue')) throw new Error('Contract not found.') - throw new Error(error.message) - } - }, [sorobanContext, factoryAddress, publicAddresses]) -} \ No newline at end of file diff --git a/apps/dapp/src/hooks/useVault.ts b/apps/dapp/src/hooks/useVault.ts deleted file mode 100644 index 1af89140..00000000 --- a/apps/dapp/src/hooks/useVault.ts +++ /dev/null @@ -1,281 +0,0 @@ -import { contractInvoke, useSorobanReact } from 'stellar-react'; -import * as StellarSdk from '@stellar/stellar-sdk'; -import { xdr } from '@stellar/stellar-sdk'; -import { scValToNative } from "@stellar/stellar-sdk"; -import { useCallback } from "react"; - -import { AssetAmmount, Vault } from "@/contexts"; -import { TxResponse } from 'stellar-react/dist/contracts/types'; -import { getTokenSymbol } from '@/helpers/getTokenInfo'; - -export enum VaultMethod { - // VaultTrait methods - DEPOSIT = "deposit", - WITHDRAW = "withdraw", - RESCUE = "rescue", - PAUSE = "pause_strategy", - UNPAUSE = "unpause_strategy", - GET_ASSETS = "get_assets", - TOTAL_MANAGED_FUNDS = "fetch_total_managed_funds", - GET_ASSET_AMOUNT = "get_asset_amounts_per_shares", - GET_FEES = "get_fees", - REPORT = "report", - - // AdminInterfaceTrait methods - SET_FEE_RECEIVER = "set_fee_receiver", - GET_FEE_RECEIVER = "get_fee_receiver", - SET_MANAGER = "set_manager", - GET_MANAGER = "get_manager", - SET_EMERGENCY_MANAGER = "set_emergency_manager", - GET_EMERGENCY_MANAGER = "get_emergency_manager", - SET_REBALANCE_MANAGER = "set_rebalance_manager", - GET_REBALANCE_MANAGER = "get_rebalance_manager", - UPGRADE = "upgrade", - - // VaultManagementTrait methods - REBALANCE = "rebalance", - LOCK_FEES = "lock_fees", - RELEASE_FEES = "release_fees", - DISTRIBUTE_FEES = "distribute_fees", - - // Additional methods - BALANCE = "balance", - GET_NAME = "name", - GET_SYMBOL = "symbol", - TOTAL_SUPPLY = "total_supply", -} - -const isObject = (val: unknown) => typeof val === 'object' && val !== null && !Array.isArray(val); - -export function useVaultCallback() { - const sorobanContext = useSorobanReact(); - return useCallback( - async (method: VaultMethod, address: string, args?: StellarSdk.xdr.ScVal[], signAndSend?: boolean) => { - try { - const result = (await contractInvoke({ - contractAddress: address, - method: method, - args: args, - sorobanContext, - signAndSend: signAndSend, - reconnectAfterTx: false, - })) as TxResponse; - - if (!signAndSend) return result; - - if ( - isObject(result) && - result?.status !== StellarSdk.rpc.Api.GetTransactionStatus.SUCCESS - ) throw result; - return result - } catch (e: unknown) { - console.log(e) - const error = String(e) - if (error.includes('The user rejected')) throw new Error('Request denied by user. Please try to sign again.') - if (error.includes('UnexpectedSize')) throw new Error('Invalid arguments length.') - if (error.includes('Error(Contract, #10)')) throw new Error('Insufficient funds.') - if (error.includes('Error(Contract, #117)')) throw new Error('Insufficient amount.') - if (error.includes('Error(Contract, #128)')) throw new Error('Unwind more than available.') - if (error.includes('Error(Contract, #130)')) throw new Error('Action requires authorization.') - if (error.includes('Error(Contract, #144)')) throw new Error('Strategy paused.') - throw new Error('Failed to process the request.') - } - } - , [sorobanContext]) -} -export const useVault = (vaultAddress?: string | undefined) => { - const vault = useVaultCallback(); - const sorobanContext = useSorobanReact(); - const getVaultInfo = async (vaultAddress: string) => { - if (!vaultAddress) return; - try { - const [ - manager, - emergencyManager, - feeReceiver, - name, - assets, - fees - ] = await Promise.all([ - getVaultManager(vaultAddress), - getVaultEmergencyManager(vaultAddress), - getVaultFeeReceiver(vaultAddress), - getVaultName(vaultAddress), - getVaultAssets(vaultAddress), - getFees(vaultAddress) - ]); - for (const asset of assets){ - const symbol = await getTokenSymbol(asset.address, sorobanContext); - if(symbol === 'native') asset.symbol = 'XLM' - else asset.symbol = symbol - } - getInvestedFunds(vaultAddress); - const newData: Vault = { - name: name || '', - symbol: name || '', - address: vaultAddress, - vaultManager: manager, - rebalanceManager: manager, - emergencyManager: emergencyManager, - feeReceiver: feeReceiver, - assetAllocation: assets || [], - feePercent: fees || [50,0], - upgradable: true, - - } - return newData - } catch (error) { - console.error(error); - } -} - - const getVaultManager = async (selectedVault: string) => { - try { - const manager = await vault(VaultMethod.GET_MANAGER, selectedVault, undefined, false).then((res: unknown) => scValToNative(res as xdr.ScVal)); - return manager; - } catch (error) { - console.error(error); - throw new Error('Failed to fetch vault manager'); - } - } - const getVaultEmergencyManager = async (selectedVault: string) => { - try { - const emergencyManager = await vault(VaultMethod.GET_EMERGENCY_MANAGER, selectedVault, undefined, false).then((res: unknown) => scValToNative(res as xdr.ScVal)); - return emergencyManager; - } catch (error) { - console.error(error); - throw new Error('Failed to fetch vault emergency manager'); - } - } - const getVaultFeeReceiver = async (selectedVault: string) => { - try { - const feeReceiver = await vault(VaultMethod.GET_FEE_RECEIVER, selectedVault, undefined, false).then((res: unknown) => scValToNative(res as xdr.ScVal)); - return feeReceiver; - } catch (error) { - console.error(error); - throw new Error('Failed to fetch vault fee receiver'); - } - } - const getVaultName = async (selectedVault: string) => { - try { - const name = await vault(VaultMethod.GET_NAME, selectedVault, undefined, false).then((res: unknown) => scValToNative(res as xdr.ScVal)); - return name; - } catch (error) { - console.error(error); - } - } - const getVaultAssets = async (selectedVault: string) => { - try { - const assets = await vault(VaultMethod.GET_ASSETS, selectedVault, undefined, false).then((res: unknown) => scValToNative(res as xdr.ScVal)); - return assets; - } catch (error) { - console.error(error); - } - } - const getVaultTotalSupply = async (selectedVault: string) => { - try { - const totalSupply = await vault(VaultMethod.TOTAL_SUPPLY, selectedVault, undefined, false).then((res: unknown) => scValToNative(res as xdr.ScVal)); - const parsedTotalSupply = Number(totalSupply) / 10 ** 7; - return parsedTotalSupply; - } catch (error) { - console.error(error); - } - } - interface TotalManagedFunds { - asset: string; - idle_amounts: number; - invested_amounts: number; - strategy_allocation: unknown[]; - total_amount: number; - } - - const getTVL = async (selectedVault: string) => { - try { - const totalValues = await vault(VaultMethod.TOTAL_MANAGED_FUNDS, selectedVault, undefined, false).then((res: unknown) => scValToNative(res as xdr.ScVal)); - const {total_amount:value} = Object.values(totalValues)[0] as TotalManagedFunds; - const parsedValue = Number(value) / 10 ** 7; - return parsedValue; - } catch (error) { - console.error(error); - } - } - const getUserBalance = async (vaultAddress: string, address: string) => { - try { - const formattedAddress = new StellarSdk.Address(address).toScVal(); - const dfTokens = await vault(VaultMethod.BALANCE, vaultAddress, [formattedAddress], false).then((res: unknown) => res); - const parsedDfTokens = scValToNative(dfTokens as xdr.ScVal); - if(parsedDfTokens == '0') return 0; - const amount = await vault(VaultMethod.GET_ASSET_AMOUNT, vaultAddress, [dfTokens as xdr.ScVal], false).then((res: unknown) => scValToNative(res as xdr.ScVal)); - const amountValue = amount[0]; - const parsedAmount = Number(amountValue) / 10 ** 7; - return parsedAmount; - } catch (error) { - console.error(error); - } - } - - const getIdleFunds = async (vaultAddress: string) => { - try { - const assets = await getVaultAssets(vaultAddress); - console.log('🚀 « assets:', assets); - const idleFunds: AssetAmmount[] = []; - for (const asset of assets) { - const rawBalance: unknown = await contractInvoke({ - contractAddress: asset.address, - method: "balance", - args: [new StellarSdk.Address(vaultAddress).toScVal()], - sorobanContext, - signAndSend: false, - }); - const balance = scValToNative(rawBalance as xdr.ScVal); - console.log('🚀 « balance:', balance); - idleFunds.push({ address: asset.address, amount: Number(balance) / 10 ** 7 }); - } - return idleFunds; - } catch (error) { - console.error(error); - } - } - - const getInvestedFunds = async (vaultAddress: string) => { - try { - const rawInvestedFunds = await vault(VaultMethod.TOTAL_MANAGED_FUNDS, vaultAddress, undefined, false).then((res: unknown) => scValToNative(res as xdr.ScVal)); - const assets = Object.keys(rawInvestedFunds); - const investedFunds: AssetAmmount[] = []; - assets.forEach((asset)=>{ - const address = rawInvestedFunds[asset].asset; - const amount = Number(rawInvestedFunds[asset].invested_amount) / 10 ** 7; - investedFunds.push({address: address, amount: amount}); - }) - return investedFunds; - } catch (error) { - console.error(error); - } - } - - const getFees = async (vaultAddress: string) => { - try { - const fees = await vault(VaultMethod.GET_FEES, vaultAddress, undefined, false).then((res: unknown) => scValToNative(res as xdr.ScVal)); - return fees || [50,0]; - } catch (error) { - console.error(error); - } - } - - const vaultInfo = getVaultInfo(vaultAddress!); - return { - vaultInfo, - getVaultInfo, - getVaultManager, - getVaultEmergencyManager, - getVaultFeeReceiver, - getVaultName, - getVaultAssets, - getVaultTotalSupply, - getUserBalance, - getTVL, - getIdleFunds, - getInvestedFunds, - getFees - }; -} \ No newline at end of file diff --git a/apps/dapp/src/lib/defindexClient.ts b/apps/dapp/src/lib/defindexClient.ts new file mode 100644 index 00000000..7d95a115 --- /dev/null +++ b/apps/dapp/src/lib/defindexClient.ts @@ -0,0 +1,10 @@ +import { DefindexSDK } from '@defindex/sdk'; + +if (!process.env.DEFINDEX_API_KEY) { + console.warn('Warning: DEFINDEX_API_KEY is not set'); +} + +export const defindexClient = new DefindexSDK({ + baseUrl: process.env.DEFINDEX_API_URL || 'http://localhost:4555', + apiKey: process.env.DEFINDEX_API_KEY || '', +}); diff --git a/apps/dapp/src/providers/MainProvider.tsx b/apps/dapp/src/providers/MainProvider.tsx index 61754a88..904d2b8e 100644 --- a/apps/dapp/src/providers/MainProvider.tsx +++ b/apps/dapp/src/providers/MainProvider.tsx @@ -1,6 +1,6 @@ 'use client' import { ReactNode } from "react" -import MySorobanReactProvider from "./SorobanProvider" +import { UserProvider } from "@/contexts/UserContext" import { ThemeProvider } from "@/components/ui/provider" import useMounted from "@/hooks/useMounted" import { StrategiesProvider } from "./StrategiesProvider" @@ -11,7 +11,7 @@ export const MainProvider = ({ children }: { children: ReactNode }) => { const mounted = useMounted(); if (!mounted) return null; return ( - + @@ -19,6 +19,6 @@ export const MainProvider = ({ children }: { children: ReactNode }) => { - + ) } \ No newline at end of file diff --git a/apps/dapp/src/providers/SorobanProvider.tsx b/apps/dapp/src/providers/SorobanProvider.tsx deleted file mode 100644 index 90b2da71..00000000 --- a/apps/dapp/src/providers/SorobanProvider.tsx +++ /dev/null @@ -1,29 +0,0 @@ -'use client' -import useMounted from '@/hooks/useMounted'; -import { NetworkDetails, SorobanReactProvider, WalletNetwork } from 'stellar-react'; - -const mainnetNetworkDetails: NetworkDetails = { - network: WalletNetwork.PUBLIC, - sorobanRpcUrl: 'https://soroban-rpc.creit.tech/', - horizonRpcUrl: 'https://horizon.stellar.org' -} - -const testnetNetworkDetails: NetworkDetails = { - network: WalletNetwork.TESTNET, - sorobanRpcUrl: 'https://soroban-testnet.stellar.org/', - horizonRpcUrl: 'https://horizon-testnet.stellar.org' -} - -export default function MySorobanReactProvider({ children }: { children: React.ReactNode }) { - const mounted = useMounted(); - if (!mounted) return null; - return ( - - {children} - - ) -} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index cca4008f..9b8c6eaa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1288,6 +1288,32 @@ lit "3.2.0" rxjs "7.8.1" +"@creit.tech/stellar-wallets-kit@^1.9.5": + version "1.9.5" + resolved "https://registry.yarnpkg.com/@creit.tech/stellar-wallets-kit/-/stellar-wallets-kit-1.9.5.tgz#8fef23a169ecb75f0c4cbcc7c02b1720cff2c8f5" + integrity sha512-b9E77r+o6Opow0CttmHdFBPeJpdLhOcLFTx3CbnwMQVivo94niap70y3A03F5cYgwVk9HhM4CJr0aimm0eNwxA== + dependencies: + "@albedo-link/intent" "0.12.0" + "@creit.tech/xbull-wallet-connect" "^0.4.0" + "@hot-wallet/sdk" "1.0.11" + "@ledgerhq/hw-app-str" "7.0.4" + "@ledgerhq/hw-transport" "6.31.4" + "@ledgerhq/hw-transport-webusb" "6.29.4" + "@lobstrco/signer-extension-api" "1.0.0-beta.0" + "@ngneat/elf" "2.5.1" + "@ngneat/elf-devtools" "1.3.0" + "@ngneat/elf-entities" "5.0.2" + "@ngneat/elf-persist-state" "1.2.1" + "@stellar/freighter-api" "5.0.0" + "@trezor/connect-plugin-stellar" "9.2.1" + "@trezor/connect-web" "9.6.2" + "@walletconnect/modal" "2.6.2" + "@walletconnect/sign-client" "2.11.2" + buffer "6.0.3" + events "3.3.0" + lit "3.2.0" + rxjs "7.8.1" + "@creit.tech/xbull-wallet-connect@0.3.0": version "0.3.0" resolved "https://registry.npmjs.org/@creit.tech/xbull-wallet-connect/-/xbull-wallet-connect-0.3.0.tgz" @@ -1313,6 +1339,13 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@defindex/sdk@^0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@defindex/sdk/-/sdk-0.1.2.tgz#8e48ab4b5e1ff2b8aaa4787a580fed7dd9f57ee3" + integrity sha512-9yDrYpF0yZOm1Mt5xVtMXEEyenj6cStTOzUtfcV4Xq++ThXBmBFBSE+LbW9DfFAYSJ3UzkJRmmtfKSMEsQGj3w== + dependencies: + axios "^1.10.0" + "@discoveryjs/json-ext@^0.5.3": version "0.5.7" resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz" @@ -1926,6 +1959,14 @@ "@eslint/core" "^0.15.2" levn "^0.4.1" +"@ethereumjs/common@^10.0.0", "@ethereumjs/common@^10.1.0": + version "10.1.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-10.1.0.tgz#f80e5589feae32ba33a818c50b18a361d8bde527" + integrity sha512-zIHCy0i2LFmMDp+QkENyoPGxcoD3QzeNVhx6/vE4nJk4uWGNXzO8xJ2UC4gtGW4UJTAOXja8Z1yZMVeRc2/+Ew== + dependencies: + "@ethereumjs/util" "^10.1.0" + eventemitter3 "^5.0.1" + "@ethereumjs/common@^4.4.0": version "4.4.0" resolved "https://registry.npmjs.org/@ethereumjs/common/-/common-4.4.0.tgz" @@ -1933,11 +1974,26 @@ dependencies: "@ethereumjs/util" "^9.1.0" +"@ethereumjs/rlp@^10.1.0": + version "10.1.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-10.1.0.tgz#fe681ed0fd2f55ed8623c0d445353d1411703b5d" + integrity sha512-r67BJbwilammAqYI4B5okA66cNdTlFzeWxPNJOolKV52ZS/flo0tUBf4x4gxWXBgh48OgsdFV1Qp5pRoSe8IhQ== + "@ethereumjs/rlp@^5.0.2": version "5.0.2" resolved "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-5.0.2.tgz" integrity sha512-DziebCdg4JpGlEqEdGgXmjqcFoJi+JGulUXwEjsZGAscAQ7MyD/7LE/GVCP29vEQxKc7AAwjT3A2ywHp2xfoCA== +"@ethereumjs/tx@^10.0.0": + version "10.1.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-10.1.0.tgz#90b568f7a1a8c02a20b2531dbc0375be38dff573" + integrity sha512-svG6pyzUZDpunafszf2BaolA6Izuvo8ZTIETIegpKxAXYudV1hmzPQDdSI+d8nHCFyQfEFbQ6tfUq95lNArmmg== + dependencies: + "@ethereumjs/common" "^10.1.0" + "@ethereumjs/rlp" "^10.1.0" + "@ethereumjs/util" "^10.1.0" + ethereum-cryptography "^3.2.0" + "@ethereumjs/tx@^5.4.0": version "5.4.0" resolved "https://registry.npmjs.org/@ethereumjs/tx/-/tx-5.4.0.tgz" @@ -1948,6 +2004,14 @@ "@ethereumjs/util" "^9.1.0" ethereum-cryptography "^2.2.1" +"@ethereumjs/util@^10.1.0": + version "10.1.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-10.1.0.tgz#56ba2abd5ca0030a1bb6d543bf205c27307cd592" + integrity sha512-GGTCkRu1kWXbz2JoUnIYtJBOoA9T5akzsYa91Bh+DZQ3Cj4qXj3hkNU0Rx6wZlbcmkmhQfrjZfVt52eJO/y2nA== + dependencies: + "@ethereumjs/rlp" "^10.1.0" + ethereum-cryptography "^3.2.0" + "@ethereumjs/util@^9.1.0": version "9.1.0" resolved "https://registry.npmjs.org/@ethereumjs/util/-/util-9.1.0.tgz" @@ -3331,7 +3395,7 @@ dependencies: eslint-scope "5.1.1" -"@noble/ciphers@^1.3.0": +"@noble/ciphers@1.3.0", "@noble/ciphers@^1.3.0": version "1.3.0" resolved "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz" integrity sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw== @@ -3357,7 +3421,14 @@ dependencies: "@noble/hashes" "1.7.1" -"@noble/curves@^1.4.2", "@noble/curves@^1.9.1", "@noble/curves@^1.9.6": +"@noble/curves@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.9.0.tgz#13e0ca8be4a0ce66c113693a94514e5599f40cfc" + integrity sha512-7YDlXiNMdO1YZeH6t/kvopHHbIZzlxrCV9WLqCY6QhcXOoXiNCMDqJIglZ9Yjx5+w7Dz30TITFrlTjnRg7sKEg== + dependencies: + "@noble/hashes" "1.8.0" + +"@noble/curves@^1.0.0", "@noble/curves@^1.4.2", "@noble/curves@^1.9.1", "@noble/curves@^1.9.6", "@noble/curves@~1.9.0": version "1.9.7" resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz" integrity sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw== @@ -3379,7 +3450,7 @@ resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz" integrity sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ== -"@noble/hashes@1.8.0", "@noble/hashes@^1.2.0", "@noble/hashes@^1.4.0", "@noble/hashes@^1.6.1", "@noble/hashes@^1.8.0", "@noble/hashes@~1.8.0": +"@noble/hashes@1.8.0", "@noble/hashes@^1.0.0", "@noble/hashes@^1.2.0", "@noble/hashes@^1.4.0", "@noble/hashes@^1.6.1", "@noble/hashes@^1.8.0", "@noble/hashes@~1.8.0": version "1.8.0" resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz" integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== @@ -3711,7 +3782,7 @@ resolved "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.12.0.tgz" integrity sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw== -"@scure/base@^1.2.0", "@scure/base@~1.2.5": +"@scure/base@^1.1.3", "@scure/base@^1.2.0", "@scure/base@~1.2.5": version "1.2.6" resolved "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz" integrity sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg== @@ -3730,6 +3801,15 @@ "@noble/hashes" "~1.4.0" "@scure/base" "~1.1.6" +"@scure/bip32@1.7.0", "@scure/bip32@^1.3.1": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.7.0.tgz#b8683bab172369f988f1589640e53c4606984219" + integrity sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw== + dependencies: + "@noble/curves" "~1.9.0" + "@noble/hashes" "~1.8.0" + "@scure/base" "~1.2.5" + "@scure/bip39@1.3.0": version "1.3.0" resolved "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz" @@ -3738,7 +3818,7 @@ "@noble/hashes" "~1.4.0" "@scure/base" "~1.1.6" -"@scure/bip39@^1.5.1": +"@scure/bip39@1.6.0", "@scure/bip39@^1.2.1", "@scure/bip39@^1.5.1": version "1.6.0" resolved "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz" integrity sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A== @@ -3853,26 +3933,51 @@ resolved "https://registry.npmjs.org/@solana-program/compute-budget/-/compute-budget-0.6.1.tgz" integrity sha512-PWcVmRx2gSQ8jd5va5HzSlKqQmR8Q1sYaPcqpCzhOHcApJ4YsVWY6QhaOD5Nx7z1UXkP12vNq3KDsSCZnT3Hkw== +"@solana-program/compute-budget@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@solana-program/compute-budget/-/compute-budget-0.8.0.tgz#0930aca4de1170ed607d64d89375074930aa8b93" + integrity sha512-qPKxdxaEsFxebZ4K5RPuy7VQIm/tfJLa1+Nlt3KNA8EYQkz9Xm8htdoEaXVrer9kpgzzp9R3I3Bh6omwCM06tQ== + "@solana-program/stake@^0.1.0": version "0.1.0" resolved "https://registry.npmjs.org/@solana-program/stake/-/stake-0.1.0.tgz" integrity sha512-8U3ax8RFvE7NegZmxn2SKE0927iG6Z9eXwBGgZaocEnZ/V3x7q/r0or1DZOV86RVyl6MQ9cuW8ExrRdorVNAVg== +"@solana-program/stake@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@solana-program/stake/-/stake-0.2.1.tgz#cc3367e5aa0258fa6599658b0ed48b02f86487a2" + integrity sha512-ssNPsJv9XHaA+L7ihzmWGYcm/+XYURQ8UA3wQMKf6ccEHyHOUgoglkkDU/BoA0+wul6HxZUN0tHFymC0qFw6sg== + "@solana-program/system@^0.6.2": version "0.6.2" resolved "https://registry.npmjs.org/@solana-program/system/-/system-0.6.2.tgz" integrity sha512-q0ZnylK+LISjuP2jH5GWV9IJPtpzQctj5KQwij9XCDRSGkcFr2fpqptNnVupTLQiNL6Q4c1OZuG8WBmyFXVXZw== +"@solana-program/system@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@solana-program/system/-/system-0.7.0.tgz#3e21c9fb31d3795eb65ba5cb663947c19b305bad" + integrity sha512-FKTBsKHpvHHNc1ATRm7SlC5nF/VdJtOSjldhcyfMN9R7xo712Mo2jHIzvBgn8zQO5Kg0DcWuKB7268Kv1ocicw== + "@solana-program/token-2022@^0.3.4": version "0.3.4" resolved "https://registry.npmjs.org/@solana-program/token-2022/-/token-2022-0.3.4.tgz" integrity sha512-URHA91F9sDibbL6RbuhnKHWGeAONCDcCmHq8tMtpVOhse9/WKp0JOvdLSiGuRkKZqLHo74xF8otmgPVchgVZXQ== +"@solana-program/token-2022@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@solana-program/token-2022/-/token-2022-0.4.2.tgz#f90a638de82acb7933a114e884a24ac4be8ef635" + integrity sha512-zIpR5t4s9qEU3hZKupzIBxJ6nUV5/UVyIT400tu9vT1HMs5JHxaTTsb5GUhYjiiTvNwU0MQavbwc4Dl29L0Xvw== + "@solana-program/token@^0.4.1": version "0.4.1" resolved "https://registry.npmjs.org/@solana-program/token/-/token-0.4.1.tgz" integrity sha512-eSYmjsapzE9jXT2J9xydlMj/zsangMEIZAy9dy75VCXM6kgDCSnH5R7+HsIoKOTvb2VggU7GojC+YhMwWGCIBw== +"@solana-program/token@^0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@solana-program/token/-/token-0.5.1.tgz#10e327df23f05a7f892fd33a9b6418f17dd62296" + integrity sha512-bJvynW5q9SFuVOZ5vqGVkmaPGA0MCC+m9jgJj1nk5m20I389/ms69ASnhWGoOPNcie7S9OwBX0gTj2fiyWpfag== + "@solana/accounts@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/accounts/-/accounts-2.0.0.tgz" @@ -3885,6 +3990,18 @@ "@solana/rpc-spec" "2.0.0" "@solana/rpc-types" "2.0.0" +"@solana/accounts@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/accounts/-/accounts-2.3.0.tgz#957360edd572c1294772ee0eae3abd598189b16e" + integrity sha512-QgQTj404Z6PXNOyzaOpSzjgMOuGwG8vC66jSDB+3zHaRcEPRVRd2sVSrd1U6sHtnV3aiaS6YyDuPQMheg4K2jw== + dependencies: + "@solana/addresses" "2.3.0" + "@solana/codecs-core" "2.3.0" + "@solana/codecs-strings" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/rpc-spec" "2.3.0" + "@solana/rpc-types" "2.3.0" + "@solana/addresses@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/addresses/-/addresses-2.0.0.tgz" @@ -3895,6 +4012,17 @@ "@solana/codecs-strings" "2.0.0" "@solana/errors" "2.0.0" +"@solana/addresses@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/addresses/-/addresses-2.3.0.tgz#d89cba142819f01905a4bf30a1b990a1b55e490d" + integrity sha512-ypTNkY2ZaRFpHLnHAgaW8a83N0/WoqdFvCqf4CQmnMdFsZSdC7qOwcbd7YzdaQn9dy+P2hybewzB+KP7LutxGA== + dependencies: + "@solana/assertions" "2.3.0" + "@solana/codecs-core" "2.3.0" + "@solana/codecs-strings" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/nominal-types" "2.3.0" + "@solana/assertions@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/assertions/-/assertions-2.0.0.tgz" @@ -3902,6 +4030,13 @@ dependencies: "@solana/errors" "2.0.0" +"@solana/assertions@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/assertions/-/assertions-2.3.0.tgz#f96f655088dea6fe9f79604da7615c745c64173b" + integrity sha512-Ekoet3khNg3XFLN7MIz8W31wPQISpKUGDGTylLptI+JjCDWx3PIa88xjEMqFo02WJ8sBj2NLV64Xg1sBcsHjZQ== + dependencies: + "@solana/errors" "2.3.0" + "@solana/buffer-layout@^4.0.1": version "4.0.1" resolved "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz" @@ -3932,6 +4067,15 @@ "@solana/codecs-numbers" "2.0.0" "@solana/errors" "2.0.0" +"@solana/codecs-data-structures@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/codecs-data-structures/-/codecs-data-structures-2.3.0.tgz#ae4ea2b3177d79a95fdcde20c04fde93b9fd190d" + integrity sha512-qvU5LE5DqEdYMYgELRHv+HMOx73sSoV1ZZkwIrclwUmwTbTaH8QAJURBj0RhQ/zCne7VuLLOZFFGv6jGigWhSw== + dependencies: + "@solana/codecs-core" "2.3.0" + "@solana/codecs-numbers" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/codecs-numbers@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/codecs-numbers/-/codecs-numbers-2.0.0.tgz" @@ -3940,7 +4084,7 @@ "@solana/codecs-core" "2.0.0" "@solana/errors" "2.0.0" -"@solana/codecs-numbers@^2.1.0": +"@solana/codecs-numbers@2.3.0", "@solana/codecs-numbers@^2.1.0": version "2.3.0" resolved "https://registry.npmjs.org/@solana/codecs-numbers/-/codecs-numbers-2.3.0.tgz" integrity sha512-jFvvwKJKffvG7Iz9dmN51OGB7JBcy2CJ6Xf3NqD/VP90xak66m/Lg48T01u5IQ/hc15mChVHiBm+HHuOFDUrQg== @@ -3957,6 +4101,15 @@ "@solana/codecs-numbers" "2.0.0" "@solana/errors" "2.0.0" +"@solana/codecs-strings@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/codecs-strings/-/codecs-strings-2.3.0.tgz#1b3a855dcd260283a732060aa6220f78b41251ae" + integrity sha512-y5pSBYwzVziXu521hh+VxqUtp0hYGTl1eWGoc1W+8mdvBdC1kTqm/X7aYQw33J42hw03JjryvYOvmGgk3Qz/Ug== + dependencies: + "@solana/codecs-core" "2.3.0" + "@solana/codecs-numbers" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/codecs@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/codecs/-/codecs-2.0.0.tgz" @@ -3968,6 +4121,17 @@ "@solana/codecs-strings" "2.0.0" "@solana/options" "2.0.0" +"@solana/codecs@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/codecs/-/codecs-2.3.0.tgz#75ea5811e2792d7344409b83ffbfd1d096292e36" + integrity sha512-JVqGPkzoeyU262hJGdH64kNLH0M+Oew2CIPOa/9tR3++q2pEd4jU2Rxdfye9sd0Ce3XJrR5AIa8ZfbyQXzjh+g== + dependencies: + "@solana/codecs-core" "2.3.0" + "@solana/codecs-data-structures" "2.3.0" + "@solana/codecs-numbers" "2.3.0" + "@solana/codecs-strings" "2.3.0" + "@solana/options" "2.3.0" + "@solana/errors@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/errors/-/errors-2.0.0.tgz" @@ -3989,11 +4153,21 @@ resolved "https://registry.npmjs.org/@solana/fast-stable-stringify/-/fast-stable-stringify-2.0.0.tgz" integrity sha512-EsIx9z+eoxOmC+FpzhEb+H67CCYTbs/omAqXD4EdEYnCHWrI1li1oYBV+NoKzfx8fKlX+nzNB7S/9kc4u7Etpw== +"@solana/fast-stable-stringify@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/fast-stable-stringify/-/fast-stable-stringify-2.3.0.tgz#723b94e373952bad4549bdd2318f79f46313d85a" + integrity sha512-KfJPrMEieUg6D3hfQACoPy0ukrAV8Kio883llt/8chPEG3FVTX9z/Zuf4O01a15xZmBbmQ7toil2Dp0sxMJSxw== + "@solana/functional@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/functional/-/functional-2.0.0.tgz" integrity sha512-Sj+sLiUTimnMEyGnSLGt0lbih2xPDUhxhonnrIkPwA+hjQ3ULGHAxeevHU06nqiVEgENQYUJ5rCtHs4xhUFAkQ== +"@solana/functional@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/functional/-/functional-2.3.0.tgz#ac33815655e954bb78151446a571bc6c9fd9be28" + integrity sha512-AgsPh3W3tE+nK3eEw/W9qiSfTGwLYEvl0rWaxHht/lRcuDVwfKRzeSa5G79eioWFFqr+pTtoCr3D3OLkwKz02Q== + "@solana/instructions@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/instructions/-/instructions-2.0.0.tgz" @@ -4001,6 +4175,14 @@ dependencies: "@solana/errors" "2.0.0" +"@solana/instructions@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/instructions/-/instructions-2.3.0.tgz#ff25cbe545000a33fb3604d83f4e2b683de94ad3" + integrity sha512-PLMsmaIKu7hEAzyElrk2T7JJx4D+9eRwebhFZpy2PXziNSmFF929eRHKUsKqBFM3cYR1Yy3m6roBZfA+bGE/oQ== + dependencies: + "@solana/codecs-core" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/keys@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/keys/-/keys-2.0.0.tgz" @@ -4011,6 +4193,46 @@ "@solana/codecs-strings" "2.0.0" "@solana/errors" "2.0.0" +"@solana/keys@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/keys/-/keys-2.3.0.tgz#9d0b0ec09c2789a051b4df1945ed52631261186e" + integrity sha512-ZVVdga79pNH+2pVcm6fr2sWz9HTwfopDVhYb0Lh3dh+WBmJjwkabXEIHey2rUES7NjFa/G7sV8lrUn/v8LDCCQ== + dependencies: + "@solana/assertions" "2.3.0" + "@solana/codecs-core" "2.3.0" + "@solana/codecs-strings" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/nominal-types" "2.3.0" + +"@solana/kit@^2.1.1": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/kit/-/kit-2.3.0.tgz#92deb7c4883293617209aecac9a43d5e41ccf092" + integrity sha512-sb6PgwoW2LjE5oTFu4lhlS/cGt/NB3YrShEyx7JgWFWysfgLdJnhwWThgwy/4HjNsmtMrQGWVls0yVBHcMvlMQ== + dependencies: + "@solana/accounts" "2.3.0" + "@solana/addresses" "2.3.0" + "@solana/codecs" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/functional" "2.3.0" + "@solana/instructions" "2.3.0" + "@solana/keys" "2.3.0" + "@solana/programs" "2.3.0" + "@solana/rpc" "2.3.0" + "@solana/rpc-parsed-types" "2.3.0" + "@solana/rpc-spec-types" "2.3.0" + "@solana/rpc-subscriptions" "2.3.0" + "@solana/rpc-types" "2.3.0" + "@solana/signers" "2.3.0" + "@solana/sysvars" "2.3.0" + "@solana/transaction-confirmation" "2.3.0" + "@solana/transaction-messages" "2.3.0" + "@solana/transactions" "2.3.0" + +"@solana/nominal-types@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/nominal-types/-/nominal-types-2.3.0.tgz#b67637241b4a45c756464e049c7a830880b6e944" + integrity sha512-uKlMnlP4PWW5UTXlhKM8lcgIaNj8dvd8xO4Y9l+FVvh9RvW2TO0GwUO6JCo7JBzCB0PSqRJdWWaQ8pu1Ti/OkA== + "@solana/options@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/options/-/options-2.0.0.tgz" @@ -4022,6 +4244,17 @@ "@solana/codecs-strings" "2.0.0" "@solana/errors" "2.0.0" +"@solana/options@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/options/-/options-2.3.0.tgz#f8a967b9ebae703b2c8adb8f4294df303ab866e8" + integrity sha512-PPnnZBRCWWoZQ11exPxf//DRzN2C6AoFsDI/u2AsQfYih434/7Kp4XLpfOMT/XESi+gdBMFNNfbES5zg3wAIkw== + dependencies: + "@solana/codecs-core" "2.3.0" + "@solana/codecs-data-structures" "2.3.0" + "@solana/codecs-numbers" "2.3.0" + "@solana/codecs-strings" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/programs@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/programs/-/programs-2.0.0.tgz" @@ -4030,11 +4263,24 @@ "@solana/addresses" "2.0.0" "@solana/errors" "2.0.0" +"@solana/programs@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/programs/-/programs-2.3.0.tgz#344193a0a4443217c177e2ec21bac7bc52afe4da" + integrity sha512-UXKujV71VCI5uPs+cFdwxybtHZAIZyQkqDiDnmK+DawtOO9mBn4Nimdb/6RjR2CXT78mzO9ZCZ3qfyX+ydcB7w== + dependencies: + "@solana/addresses" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/promises@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/promises/-/promises-2.0.0.tgz" integrity sha512-4teQ52HDjK16ORrZe1zl+Q9WcZdQ+YEl0M1gk59XG7D0P9WqaVEQzeXGnKSCs+Y9bnB1u5xCJccwpUhHYWq6gg== +"@solana/promises@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/promises/-/promises-2.3.0.tgz#ae3fc000f4aef65561d9e4f9724d4635ed042750" + integrity sha512-GjVgutZKXVuojd9rWy1PuLnfcRfqsaCm7InCiZc8bqmJpoghlyluweNc7ml9Y5yQn1P2IOyzh9+p/77vIyNybQ== + "@solana/rpc-api@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/rpc-api/-/rpc-api-2.0.0.tgz" @@ -4052,16 +4298,43 @@ "@solana/transaction-messages" "2.0.0" "@solana/transactions" "2.0.0" +"@solana/rpc-api@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-api/-/rpc-api-2.3.0.tgz#c6e5f7353910bd7c7d2f8a6d4dab44d080bd313a" + integrity sha512-UUdiRfWoyYhJL9PPvFeJr4aJ554ob2jXcpn4vKmRVn9ire0sCbpQKYx6K8eEKHZWXKrDW8IDspgTl0gT/aJWVg== + dependencies: + "@solana/addresses" "2.3.0" + "@solana/codecs-core" "2.3.0" + "@solana/codecs-strings" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/keys" "2.3.0" + "@solana/rpc-parsed-types" "2.3.0" + "@solana/rpc-spec" "2.3.0" + "@solana/rpc-transformers" "2.3.0" + "@solana/rpc-types" "2.3.0" + "@solana/transaction-messages" "2.3.0" + "@solana/transactions" "2.3.0" + "@solana/rpc-parsed-types@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/rpc-parsed-types/-/rpc-parsed-types-2.0.0.tgz" integrity sha512-VCeY/oKVEtBnp8EDOc5LSSiOeIOLFIgLndcxqU0ij/cZaQ01DOoHbhluvhZtU80Z3dUeicec8TiMgkFzed+WhQ== +"@solana/rpc-parsed-types@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-parsed-types/-/rpc-parsed-types-2.3.0.tgz#132b03f6b4c1b4688336ad48e76c2eea0d8c91d7" + integrity sha512-B5pHzyEIbBJf9KHej+zdr5ZNAdSvu7WLU2lOUPh81KHdHQs6dEb310LGxcpCc7HVE8IEdO20AbckewDiAN6OCg== + "@solana/rpc-spec-types@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/rpc-spec-types/-/rpc-spec-types-2.0.0.tgz" integrity sha512-G2lmhFhgtxMQd/D6B04BHGE7bm5dMZdIPQNOqVGhzNAVjrmyapD3JN2hKAbmaYPe97wLfZERw0Ux1u4Y6q7TqA== +"@solana/rpc-spec-types@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-spec-types/-/rpc-spec-types-2.3.0.tgz#010ea9de2f720e84bec2b93ca77ad3664b77235f" + integrity sha512-xQsb65lahjr8Wc9dMtP7xa0ZmDS8dOE2ncYjlvfyw/h4mpdXTUdrSMi6RtFwX33/rGuztQ7Hwaid5xLNSLvsFQ== + "@solana/rpc-spec@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/rpc-spec/-/rpc-spec-2.0.0.tgz" @@ -4070,6 +4343,14 @@ "@solana/errors" "2.0.0" "@solana/rpc-spec-types" "2.0.0" +"@solana/rpc-spec@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-spec/-/rpc-spec-2.3.0.tgz#2b679eb750c0f9270da6d451ea1bdc2c7783eb42" + integrity sha512-fA2LMX4BMixCrNB2n6T83AvjZ3oUQTu7qyPLyt8gHQaoEAXs8k6GZmu6iYcr+FboQCjUmRPgMaABbcr9j2J9Sw== + dependencies: + "@solana/errors" "2.3.0" + "@solana/rpc-spec-types" "2.3.0" + "@solana/rpc-subscriptions-api@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/rpc-subscriptions-api/-/rpc-subscriptions-api-2.0.0.tgz" @@ -4083,6 +4364,19 @@ "@solana/transaction-messages" "2.0.0" "@solana/transactions" "2.0.0" +"@solana/rpc-subscriptions-api@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-subscriptions-api/-/rpc-subscriptions-api-2.3.0.tgz#e779b8ad10e89b2f4a4ccb0fcd1a722d8bdd7729" + integrity sha512-9mCjVbum2Hg9KGX3LKsrI5Xs0KX390lS+Z8qB80bxhar6MJPugqIPH8uRgLhCW9GN3JprAfjRNl7our8CPvsPQ== + dependencies: + "@solana/addresses" "2.3.0" + "@solana/keys" "2.3.0" + "@solana/rpc-subscriptions-spec" "2.3.0" + "@solana/rpc-transformers" "2.3.0" + "@solana/rpc-types" "2.3.0" + "@solana/transaction-messages" "2.3.0" + "@solana/transactions" "2.3.0" + "@solana/rpc-subscriptions-channel-websocket@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/rpc-subscriptions-channel-websocket/-/rpc-subscriptions-channel-websocket-2.0.0.tgz" @@ -4093,6 +4387,16 @@ "@solana/rpc-subscriptions-spec" "2.0.0" "@solana/subscribable" "2.0.0" +"@solana/rpc-subscriptions-channel-websocket@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-subscriptions-channel-websocket/-/rpc-subscriptions-channel-websocket-2.3.0.tgz#11352ed281eccfa89a782a1b27444613ebeacca4" + integrity sha512-2oL6ceFwejIgeWzbNiUHI2tZZnaOxNTSerszcin7wYQwijxtpVgUHiuItM/Y70DQmH9sKhmikQp+dqeGalaJxw== + dependencies: + "@solana/errors" "2.3.0" + "@solana/functional" "2.3.0" + "@solana/rpc-subscriptions-spec" "2.3.0" + "@solana/subscribable" "2.3.0" + "@solana/rpc-subscriptions-spec@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/rpc-subscriptions-spec/-/rpc-subscriptions-spec-2.0.0.tgz" @@ -4103,6 +4407,16 @@ "@solana/rpc-spec-types" "2.0.0" "@solana/subscribable" "2.0.0" +"@solana/rpc-subscriptions-spec@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-subscriptions-spec/-/rpc-subscriptions-spec-2.3.0.tgz#a718a4ea97f57ed62291526b70740a42d576fada" + integrity sha512-rdmVcl4PvNKQeA2l8DorIeALCgJEMSu7U8AXJS1PICeb2lQuMeaR+6cs/iowjvIB0lMVjYN2sFf6Q3dJPu6wWg== + dependencies: + "@solana/errors" "2.3.0" + "@solana/promises" "2.3.0" + "@solana/rpc-spec-types" "2.3.0" + "@solana/subscribable" "2.3.0" + "@solana/rpc-subscriptions@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/rpc-subscriptions/-/rpc-subscriptions-2.0.0.tgz" @@ -4120,6 +4434,23 @@ "@solana/rpc-types" "2.0.0" "@solana/subscribable" "2.0.0" +"@solana/rpc-subscriptions@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-subscriptions/-/rpc-subscriptions-2.3.0.tgz#12639c17603e1a30113825350ddbfc3b50b6a031" + integrity sha512-Uyr10nZKGVzvCOqwCZgwYrzuoDyUdwtgQRefh13pXIrdo4wYjVmoLykH49Omt6abwStB0a4UL5gX9V4mFdDJZg== + dependencies: + "@solana/errors" "2.3.0" + "@solana/fast-stable-stringify" "2.3.0" + "@solana/functional" "2.3.0" + "@solana/promises" "2.3.0" + "@solana/rpc-spec-types" "2.3.0" + "@solana/rpc-subscriptions-api" "2.3.0" + "@solana/rpc-subscriptions-channel-websocket" "2.3.0" + "@solana/rpc-subscriptions-spec" "2.3.0" + "@solana/rpc-transformers" "2.3.0" + "@solana/rpc-types" "2.3.0" + "@solana/subscribable" "2.3.0" + "@solana/rpc-transformers@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/rpc-transformers/-/rpc-transformers-2.0.0.tgz" @@ -4130,6 +4461,17 @@ "@solana/rpc-spec-types" "2.0.0" "@solana/rpc-types" "2.0.0" +"@solana/rpc-transformers@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-transformers/-/rpc-transformers-2.3.0.tgz#e008d2782047d574dbc74985c6cce26d7c3555f3" + integrity sha512-UuHYK3XEpo9nMXdjyGKkPCOr7WsZsxs7zLYDO1A5ELH3P3JoehvrDegYRAGzBS2VKsfApZ86ZpJToP0K3PhmMA== + dependencies: + "@solana/errors" "2.3.0" + "@solana/functional" "2.3.0" + "@solana/nominal-types" "2.3.0" + "@solana/rpc-spec-types" "2.3.0" + "@solana/rpc-types" "2.3.0" + "@solana/rpc-transport-http@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/rpc-transport-http/-/rpc-transport-http-2.0.0.tgz" @@ -4140,6 +4482,16 @@ "@solana/rpc-spec-types" "2.0.0" undici-types "^6.20.0" +"@solana/rpc-transport-http@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-transport-http/-/rpc-transport-http-2.3.0.tgz#581601b9579b2a7fed9e0cb6fbcb95b4186e5b49" + integrity sha512-HFKydmxGw8nAF5N+S0NLnPBDCe5oMDtI2RAmW8DMqP4U3Zxt2XWhvV1SNkAldT5tF0U1vP+is6fHxyhk4xqEvg== + dependencies: + "@solana/errors" "2.3.0" + "@solana/rpc-spec" "2.3.0" + "@solana/rpc-spec-types" "2.3.0" + undici-types "^7.11.0" + "@solana/rpc-types@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/rpc-types/-/rpc-types-2.0.0.tgz" @@ -4151,6 +4503,18 @@ "@solana/codecs-strings" "2.0.0" "@solana/errors" "2.0.0" +"@solana/rpc-types@2.3.0", "@solana/rpc-types@^2.1.1": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-types/-/rpc-types-2.3.0.tgz#38adf5cb1c79c08086bd672edf7a56d19581d19f" + integrity sha512-O09YX2hED2QUyGxrMOxQ9GzH1LlEwwZWu69QbL4oYmIf6P5dzEEHcqRY6L1LsDVqc/dzAdEs/E1FaPrcIaIIPw== + dependencies: + "@solana/addresses" "2.3.0" + "@solana/codecs-core" "2.3.0" + "@solana/codecs-numbers" "2.3.0" + "@solana/codecs-strings" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/nominal-types" "2.3.0" + "@solana/rpc@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/rpc/-/rpc-2.0.0.tgz" @@ -4166,6 +4530,21 @@ "@solana/rpc-transport-http" "2.0.0" "@solana/rpc-types" "2.0.0" +"@solana/rpc@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc/-/rpc-2.3.0.tgz#a65919520d14c122625fb887a2d72c95bf8691cf" + integrity sha512-ZWN76iNQAOCpYC7yKfb3UNLIMZf603JckLKOOLTHuy9MZnTN8XV6uwvDFhf42XvhglgUjGCEnbUqWtxQ9pa/pQ== + dependencies: + "@solana/errors" "2.3.0" + "@solana/fast-stable-stringify" "2.3.0" + "@solana/functional" "2.3.0" + "@solana/rpc-api" "2.3.0" + "@solana/rpc-spec" "2.3.0" + "@solana/rpc-spec-types" "2.3.0" + "@solana/rpc-transformers" "2.3.0" + "@solana/rpc-transport-http" "2.3.0" + "@solana/rpc-types" "2.3.0" + "@solana/signers@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/signers/-/signers-2.0.0.tgz" @@ -4179,6 +4558,20 @@ "@solana/transaction-messages" "2.0.0" "@solana/transactions" "2.0.0" +"@solana/signers@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/signers/-/signers-2.3.0.tgz#94569a7fb025a3f473661078fbca15b34ceacf94" + integrity sha512-OSv6fGr/MFRx6J+ZChQMRqKNPGGmdjkqarKkRzkwmv7v8quWsIRnJT5EV8tBy3LI4DLO/A8vKiNSPzvm1TdaiQ== + dependencies: + "@solana/addresses" "2.3.0" + "@solana/codecs-core" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/instructions" "2.3.0" + "@solana/keys" "2.3.0" + "@solana/nominal-types" "2.3.0" + "@solana/transaction-messages" "2.3.0" + "@solana/transactions" "2.3.0" + "@solana/subscribable@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/subscribable/-/subscribable-2.0.0.tgz" @@ -4186,6 +4579,13 @@ dependencies: "@solana/errors" "2.0.0" +"@solana/subscribable@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/subscribable/-/subscribable-2.3.0.tgz#4e48f1a4eeb1ccf22065b46fb8e3ed80d1a27f80" + integrity sha512-DkgohEDbMkdTWiKAoatY02Njr56WXx9e/dKKfmne8/Ad6/2llUIrax78nCdlvZW9quXMaXPTxZvdQqo9N669Og== + dependencies: + "@solana/errors" "2.3.0" + "@solana/sysvars@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/sysvars/-/sysvars-2.0.0.tgz" @@ -4196,6 +4596,16 @@ "@solana/errors" "2.0.0" "@solana/rpc-types" "2.0.0" +"@solana/sysvars@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/sysvars/-/sysvars-2.3.0.tgz#eeea82f89716682014e801de5870344ddd02becd" + integrity sha512-LvjADZrpZ+CnhlHqfI5cmsRzX9Rpyb1Ox2dMHnbsRNzeKAMhu9w4ZBIaeTdO322zsTr509G1B+k2ABD3whvUBA== + dependencies: + "@solana/accounts" "2.3.0" + "@solana/codecs" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/rpc-types" "2.3.0" + "@solana/transaction-confirmation@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/transaction-confirmation/-/transaction-confirmation-2.0.0.tgz" @@ -4212,6 +4622,22 @@ "@solana/transaction-messages" "2.0.0" "@solana/transactions" "2.0.0" +"@solana/transaction-confirmation@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/transaction-confirmation/-/transaction-confirmation-2.3.0.tgz#f66e70334d797b5010b4ae27dc59de2f90b8ebe6" + integrity sha512-UiEuiHCfAAZEKdfne/XljFNJbsKAe701UQHKXEInYzIgBjRbvaeYZlBmkkqtxwcasgBTOmEaEKT44J14N9VZDw== + dependencies: + "@solana/addresses" "2.3.0" + "@solana/codecs-strings" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/keys" "2.3.0" + "@solana/promises" "2.3.0" + "@solana/rpc" "2.3.0" + "@solana/rpc-subscriptions" "2.3.0" + "@solana/rpc-types" "2.3.0" + "@solana/transaction-messages" "2.3.0" + "@solana/transactions" "2.3.0" + "@solana/transaction-messages@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/transaction-messages/-/transaction-messages-2.0.0.tgz" @@ -4226,6 +4652,21 @@ "@solana/instructions" "2.0.0" "@solana/rpc-types" "2.0.0" +"@solana/transaction-messages@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/transaction-messages/-/transaction-messages-2.3.0.tgz#e2a9c2f5565c7cc720aa09816aa3c17fb79c2abc" + integrity sha512-bgqvWuy3MqKS5JdNLH649q+ngiyOu5rGS3DizSnWwYUd76RxZl1kN6CoqHSrrMzFMvis6sck/yPGG3wqrMlAww== + dependencies: + "@solana/addresses" "2.3.0" + "@solana/codecs-core" "2.3.0" + "@solana/codecs-data-structures" "2.3.0" + "@solana/codecs-numbers" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/functional" "2.3.0" + "@solana/instructions" "2.3.0" + "@solana/nominal-types" "2.3.0" + "@solana/rpc-types" "2.3.0" + "@solana/transactions@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@solana/transactions/-/transactions-2.0.0.tgz" @@ -4243,6 +4684,24 @@ "@solana/rpc-types" "2.0.0" "@solana/transaction-messages" "2.0.0" +"@solana/transactions@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/transactions/-/transactions-2.3.0.tgz#fc99f6ce6cc5706f2b8116bbf8a2f396c3ec3177" + integrity sha512-LnTvdi8QnrQtuEZor5Msje61sDpPstTVwKg4y81tNxDhiyomjuvnSNLAq6QsB9gIxUqbNzPZgOG9IU4I4/Uaug== + dependencies: + "@solana/addresses" "2.3.0" + "@solana/codecs-core" "2.3.0" + "@solana/codecs-data-structures" "2.3.0" + "@solana/codecs-numbers" "2.3.0" + "@solana/codecs-strings" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/functional" "2.3.0" + "@solana/instructions" "2.3.0" + "@solana/keys" "2.3.0" + "@solana/nominal-types" "2.3.0" + "@solana/rpc-types" "2.3.0" + "@solana/transaction-messages" "2.3.0" + "@solana/wallet-adapter-base@^0.9.23": version "0.9.27" resolved "https://registry.npmjs.org/@solana/wallet-adapter-base/-/wallet-adapter-base-0.9.27.tgz" @@ -4497,6 +4956,14 @@ buffer "^6.0.3" semver "^7.6.3" +"@stellar/freighter-api@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@stellar/freighter-api/-/freighter-api-5.0.0.tgz#54c4a95db2ddfaf2d95f699fd7b401e782278c70" + integrity sha512-MydzLg+WpSzmws24uUs4mVME2LPN8xhUWkwyGEP0N1Hr519swC6I/W7K6cdVBzghBiVv7f/vvGFNT+0p1a33Vg== + dependencies: + buffer "6.0.3" + semver "7.7.1" + "@stellar/freighter-api@^3.1.0": version "3.1.0" resolved "https://registry.npmjs.org/@stellar/freighter-api/-/freighter-api-3.1.0.tgz" @@ -4591,7 +5058,7 @@ toml "^3.0.0" urijs "^1.19.1" -"@stellar/stellar-sdk@^14.0.0", "@stellar/stellar-sdk@^14.0.0-rc.3": +"@stellar/stellar-sdk@^14.0.0": version "14.0.0" resolved "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-14.0.0.tgz" integrity sha512-nZUxsxdCCF+YygS4ViUKjNUkCXY9Ly+En3Wsrd9jc5pdV9b+lWXiyv5AM5WLGLVB+qQrOeQWIBrQQuiYLsxTIQ== @@ -5506,6 +5973,14 @@ "@trezor/env-utils" "1.3.0" "@trezor/utils" "9.3.0" +"@trezor/analytics@1.4.2": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@trezor/analytics/-/analytics-1.4.2.tgz#4eb7a8ed8687ebbc2993659eb1b31e89e3bdeda5" + integrity sha512-FgjJekuDvx1TjiDemvpnPiRck7Kp/v1ZeppsBYpQR3yGKyKzbG1pVpcl0RyI2237raXxbORaz7XV8tcyjq4BXg== + dependencies: + "@trezor/env-utils" "1.4.2" + "@trezor/utils" "9.4.2" + "@trezor/blockchain-link-types@1.3.1": version "1.3.1" resolved "https://registry.npmjs.org/@trezor/blockchain-link-types/-/blockchain-link-types-1.3.1.tgz" @@ -5515,6 +5990,13 @@ "@trezor/type-utils" "1.1.4" "@trezor/utxo-lib" "2.3.1" +"@trezor/blockchain-link-types@1.4.2": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@trezor/blockchain-link-types/-/blockchain-link-types-1.4.2.tgz#ccdcdbee6eb5789ee60978930fd181212f867798" + integrity sha512-KThBmGOFLJAFnmou9ThQhnjEVxfYPfEwMOaVTVNgJ+NAkt5rEMx0SKBBelCGZ63XtOLWdVPglFo83wtm+I9Vpg== + dependencies: + "@trezor/utxo-lib" "2.4.2" + "@trezor/blockchain-link-utils@1.3.1": version "1.3.1" resolved "https://registry.npmjs.org/@trezor/blockchain-link-utils/-/blockchain-link-utils-1.3.1.tgz" @@ -5524,6 +6006,17 @@ "@trezor/env-utils" "1.3.1" "@trezor/utils" "9.3.1" +"@trezor/blockchain-link-utils@1.4.2": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@trezor/blockchain-link-utils/-/blockchain-link-utils-1.4.2.tgz#56884209e8111cee91d4bd20f4e728fcaec38eb4" + integrity sha512-PBEBrdtHn0dn/c9roW6vjdHI/CucMywJm5gthETZAZmzBOtg6ZDpLTn+qL8+jZGIbwcAkItrQ3iHrHhR6xTP5g== + dependencies: + "@mobily/ts-belt" "^3.13.1" + "@stellar/stellar-sdk" "^13.3.0" + "@trezor/env-utils" "1.4.2" + "@trezor/utils" "9.4.2" + xrpl "^4.3.0" + "@trezor/blockchain-link@2.4.1": version "2.4.1" resolved "https://registry.npmjs.org/@trezor/blockchain-link/-/blockchain-link-2.4.1.tgz" @@ -5544,6 +6037,28 @@ ripple-lib "^1.10.1" socks-proxy-agent "8.0.4" +"@trezor/blockchain-link@2.5.2": + version "2.5.2" + resolved "https://registry.yarnpkg.com/@trezor/blockchain-link/-/blockchain-link-2.5.2.tgz#dc4cfc13638674c96d91a553eb92a8c82c1456b1" + integrity sha512-/egUnIt/fR57QY33ejnkPMhZwRvVRS/pUCoqdVIGitN1Q7QZsdopoR4hw37hdK/Ux/q1ZLH6LZz7U2UFahjppw== + dependencies: + "@solana-program/stake" "^0.2.1" + "@solana-program/token" "^0.5.1" + "@solana-program/token-2022" "^0.4.2" + "@solana/kit" "^2.1.1" + "@solana/rpc-types" "^2.1.1" + "@stellar/stellar-sdk" "^13.3.0" + "@trezor/blockchain-link-types" "1.4.2" + "@trezor/blockchain-link-utils" "1.4.2" + "@trezor/env-utils" "1.4.2" + "@trezor/utils" "9.4.2" + "@trezor/utxo-lib" "2.4.2" + "@trezor/websocket-client" "1.2.2" + "@types/web" "^0.0.197" + events "^3.3.0" + socks-proxy-agent "8.0.5" + xrpl "^4.3.0" + "@trezor/connect-analytics@1.3.0": version "1.3.0" resolved "https://registry.npmjs.org/@trezor/connect-analytics/-/connect-analytics-1.3.0.tgz" @@ -5551,6 +6066,13 @@ dependencies: "@trezor/analytics" "1.3.0" +"@trezor/connect-analytics@1.3.5": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@trezor/connect-analytics/-/connect-analytics-1.3.5.tgz#d965ac376268de1cd80a881df3f6ec4c8d04952d" + integrity sha512-Aoi+EITpZZycnELQJEp9XV0mHFfaCQ6JE0Ka5mWuHtOny3nJdFLBrih4ipcEXJdJbww6pBxRJB09sJ19cTyacA== + dependencies: + "@trezor/analytics" "1.4.2" + "@trezor/connect-common@0.3.1": version "0.3.1" resolved "https://registry.npmjs.org/@trezor/connect-common/-/connect-common-0.3.1.tgz" @@ -5559,6 +6081,14 @@ "@trezor/env-utils" "1.3.1" "@trezor/utils" "9.3.1" +"@trezor/connect-common@0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@trezor/connect-common/-/connect-common-0.4.2.tgz#472e8e46c09c0e0879c25739809d0e55c1437d3d" + integrity sha512-ND5TTjrTPnJdfl8Wlhl9YtFWnY2u6FHM1dsPkNYCmyUKIMoflJ5cLn95Xabl6l1btHERYn3wTUvgEYQG7r8OVQ== + dependencies: + "@trezor/env-utils" "1.4.2" + "@trezor/utils" "9.4.2" + "@trezor/connect-plugin-stellar@9.0.6": version "9.0.6" resolved "https://registry.npmjs.org/@trezor/connect-plugin-stellar/-/connect-plugin-stellar-9.0.6.tgz" @@ -5566,6 +6096,13 @@ dependencies: "@trezor/utils" "9.2.2" +"@trezor/connect-plugin-stellar@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@trezor/connect-plugin-stellar/-/connect-plugin-stellar-9.2.1.tgz#93bcab849b4f53aa8a81817c68f28945c2b7c908" + integrity sha512-Orz5gFZzYFZs1+cTsgg8fz/VWFjhl7pqMCqD5DVNZpXW+wrjwBaRbcGJZ+ibkPKU3AlM7Uv3SVD/pjaQmAkZ2Q== + dependencies: + "@trezor/utils" "9.4.1" + "@trezor/connect-web@9.5.1": version "9.5.1" resolved "https://registry.npmjs.org/@trezor/connect-web/-/connect-web-9.5.1.tgz" @@ -5575,6 +6112,16 @@ "@trezor/connect-common" "0.3.1" "@trezor/utils" "9.3.1" +"@trezor/connect-web@9.6.2": + version "9.6.2" + resolved "https://registry.yarnpkg.com/@trezor/connect-web/-/connect-web-9.6.2.tgz#bcbb423447e4f4cd0bcbabdf385d80f2eb7fb06c" + integrity sha512-QGuCjX8Bx9aCq1Pg52KifbbzYn00FQu9mCTDSgCVGH/HAzbxhcRkDKc86kFwW8z9NdJxw+XeVJq5Ky/js3iEDA== + dependencies: + "@trezor/connect" "9.6.2" + "@trezor/connect-common" "0.4.2" + "@trezor/utils" "9.4.2" + "@trezor/websocket-client" "1.2.2" + "@trezor/connect@9.5.1": version "9.5.1" resolved "https://registry.npmjs.org/@trezor/connect/-/connect-9.5.1.tgz" @@ -5608,11 +6155,58 @@ bs58check "^4.0.0" cross-fetch "^4.0.0" +"@trezor/connect@9.6.2": + version "9.6.2" + resolved "https://registry.yarnpkg.com/@trezor/connect/-/connect-9.6.2.tgz#4d48a0bc8f571a8e4c8cdd11fd2d75652c697c8b" + integrity sha512-XsSERBK+KnF6FPsATuhB9AEM0frekVLwAwFo35MRV9I4P+mdv6tnUiZUq8O8aoPbfJwDjtNJSYv+PMsKuRH6rg== + dependencies: + "@ethereumjs/common" "^10.0.0" + "@ethereumjs/tx" "^10.0.0" + "@fivebinaries/coin-selection" "3.0.0" + "@mobily/ts-belt" "^3.13.1" + "@noble/hashes" "^1.6.1" + "@scure/bip39" "^1.5.1" + "@solana-program/compute-budget" "^0.8.0" + "@solana-program/system" "^0.7.0" + "@solana-program/token" "^0.5.1" + "@solana-program/token-2022" "^0.4.2" + "@solana/kit" "^2.1.1" + "@trezor/blockchain-link" "2.5.2" + "@trezor/blockchain-link-types" "1.4.2" + "@trezor/blockchain-link-utils" "1.4.2" + "@trezor/connect-analytics" "1.3.5" + "@trezor/connect-common" "0.4.2" + "@trezor/crypto-utils" "1.1.4" + "@trezor/device-utils" "1.1.2" + "@trezor/env-utils" "^1.4.2" + "@trezor/protobuf" "1.4.2" + "@trezor/protocol" "1.2.8" + "@trezor/schema-utils" "1.3.4" + "@trezor/transport" "1.5.2" + "@trezor/type-utils" "1.1.8" + "@trezor/utils" "9.4.2" + "@trezor/utxo-lib" "2.4.2" + blakejs "^1.2.1" + bs58 "^6.0.0" + bs58check "^4.0.0" + cross-fetch "^4.0.0" + jws "^4.0.0" + "@trezor/crypto-utils@1.1.1": version "1.1.1" resolved "https://registry.npmjs.org/@trezor/crypto-utils/-/crypto-utils-1.1.1.tgz" integrity sha512-qnQ6TAZoS6uMv8jPqY/zZu7oUnut/RCUyDwh9iZz74vH0CCq5WU/ul7BvqBjOeoxWlMVdMIX1pBNXtCucvJElQ== +"@trezor/crypto-utils@1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@trezor/crypto-utils/-/crypto-utils-1.1.4.tgz#86ebde3ff745d3672f4ed475f00ee68d2ff05e43" + integrity sha512-Y6VziniqMPoMi70IyowEuXKqRvBYQzgPAekJaUZTHhR+grtYNRKRH2HJCvuZ8MGmSKUFSYfa7y8AvwALA8mQmA== + +"@trezor/device-utils@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@trezor/device-utils/-/device-utils-1.1.2.tgz#541c2bf623b7acda7e445b876336a0579da0c504" + integrity sha512-R3AJvAo+a3wYVmcGZO2VNl9PZOmDEzCZIlmCJn0BlSRWWd8G9u1qyo/fL9zOwij/YhCaJyokmSHmIEmbY9qpgw== + "@trezor/env-utils@1.3.0": version "1.3.0" resolved "https://registry.npmjs.org/@trezor/env-utils/-/env-utils-1.3.0.tgz" @@ -5627,6 +6221,20 @@ dependencies: ua-parser-js "^1.0.37" +"@trezor/env-utils@1.4.2": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@trezor/env-utils/-/env-utils-1.4.2.tgz#b1608e98a975f2a071c9005a30b606f5a5a8cd00" + integrity sha512-lQvrqcNK5I4dy2MuiLyMuEm0KzY59RIu2GLtc9GsvqyxSPZkADqVzGeLJjXj/vI2ajL8leSpMvmN4zPw3EK8AA== + dependencies: + ua-parser-js "^2.0.4" + +"@trezor/env-utils@^1.4.2": + version "1.4.3" + resolved "https://registry.yarnpkg.com/@trezor/env-utils/-/env-utils-1.4.3.tgz#56d3648f3ba9d655dba78524c93c2ab5048452bc" + integrity sha512-sWC828NRNQi5vc9W4M9rHOJDeI9XlsgnzZaML/lHju7WhlZCmSq5BOntZQvD8d1W0fSwLMLdlcBKBr/gQkvFZQ== + dependencies: + ua-parser-js "^2.0.4" + "@trezor/protobuf@1.3.1": version "1.3.1" resolved "https://registry.npmjs.org/@trezor/protobuf/-/protobuf-1.3.1.tgz" @@ -5636,11 +6244,25 @@ long "5.2.0" protobufjs "7.4.0" +"@trezor/protobuf@1.4.2": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@trezor/protobuf/-/protobuf-1.4.2.tgz#92a1732262f991fea911eec1b21010c6806b430a" + integrity sha512-AeIYKCgKcE9cWflggGL8T9gD+IZLSGrwkzqCk3wpIiODd5dUCgEgA4OPBufR6OMu3RWu/Tgu2xviHunijG3LXQ== + dependencies: + "@trezor/schema-utils" "1.3.4" + long "5.2.5" + protobufjs "7.4.0" + "@trezor/protocol@1.2.3": version "1.2.3" resolved "https://registry.npmjs.org/@trezor/protocol/-/protocol-1.2.3.tgz" integrity sha512-jdCnkrZmvERaqMM+rilS5KE6gj3pqRl3gUF69ftFIgb14PNYS2bYjMDnVxgXkeKb94fjbu2PizDF78hnai7yEw== +"@trezor/protocol@1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@trezor/protocol/-/protocol-1.2.8.tgz#3980018b4c5c3d2b126fb17cd11d26b575b9f1ce" + integrity sha512-8EH+EU4Z1j9X4Ljczjbl9G7vVgcUz41qXcdE+6FOG3BFvMDK4KUVvaOtWqD+1dFpeo5yvWSTEKdhgXMPFprWYQ== + "@trezor/schema-utils@1.3.1": version "1.3.1" resolved "https://registry.npmjs.org/@trezor/schema-utils/-/schema-utils-1.3.1.tgz" @@ -5649,6 +6271,14 @@ "@sinclair/typebox" "^0.33.7" ts-mixer "^6.0.3" +"@trezor/schema-utils@1.3.4": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@trezor/schema-utils/-/schema-utils-1.3.4.tgz#6ebd17aadf9534c1054e852d44e3c351bb919a79" + integrity sha512-guP5TKjQEWe6c5HGx+7rhM0SAdEL5gylpkvk9XmJXjZDnl1Ew81nmLHUs2ghf8Od3pKBe4qjBIMBHUQNaOqWUg== + dependencies: + "@sinclair/typebox" "^0.33.7" + ts-mixer "^6.0.3" + "@trezor/transport@1.4.1": version "1.4.1" resolved "https://registry.npmjs.org/@trezor/transport/-/transport-1.4.1.tgz" @@ -5660,11 +6290,28 @@ cross-fetch "^4.0.0" usb "^2.14.0" +"@trezor/transport@1.5.2": + version "1.5.2" + resolved "https://registry.yarnpkg.com/@trezor/transport/-/transport-1.5.2.tgz#d5373c4364546933944a76dc817c65b8660dc123" + integrity sha512-rYP87zdVll2bNBtsD3VxJq0yjaNvIClcgszZjQwVTQxpKGFPkx8bLSpAGI05R9qfxusZJCfYarjX3qki9nHYPw== + dependencies: + "@trezor/protobuf" "1.4.2" + "@trezor/protocol" "1.2.8" + "@trezor/type-utils" "1.1.8" + "@trezor/utils" "9.4.2" + cross-fetch "^4.0.0" + usb "^2.15.0" + "@trezor/type-utils@1.1.4": version "1.1.4" resolved "https://registry.npmjs.org/@trezor/type-utils/-/type-utils-1.1.4.tgz" integrity sha512-pzrIdskmTZRocHellMZxCDPQ3IpmTr749qn1xdIN29pIKuI4ms0OfNUPk/rfR4Iug0kEiWt+n+Hw7+lIBzc2LA== +"@trezor/type-utils@1.1.8": + version "1.1.8" + resolved "https://registry.yarnpkg.com/@trezor/type-utils/-/type-utils-1.1.8.tgz#3cddcdfb483342459e22fa6d89a2e918da1ac718" + integrity sha512-VtvkPXpwtMtTX9caZWYlMMTmhjUeDq4/1LGn0pSdjd4OuL/vQyuPWXCT/0RtlnRraW6R2dZF7rX2UON2kQIMTQ== + "@trezor/utils@9.2.2": version "9.2.2" resolved "https://registry.npmjs.org/@trezor/utils/-/utils-9.2.2.tgz" @@ -5686,6 +6333,20 @@ dependencies: bignumber.js "^9.1.2" +"@trezor/utils@9.4.1": + version "9.4.1" + resolved "https://registry.yarnpkg.com/@trezor/utils/-/utils-9.4.1.tgz#837a7c6fa366bae610d9c1be90f57acebf582279" + integrity sha512-9MYNa99tzXiTBnKadABoY2D80YL9Mh3ntM5wziwVhjZ4HyhqFH6BsCxwFpWYLUIKBctD55QEdE4bASoqp7Ad1A== + dependencies: + bignumber.js "^9.3.0" + +"@trezor/utils@9.4.2": + version "9.4.2" + resolved "https://registry.yarnpkg.com/@trezor/utils/-/utils-9.4.2.tgz#8d62bea5f032b3b368a06ed97a4e942c5496a0cc" + integrity sha512-Fm3m2gmfXsgv4chqn5HX8e8dElEr2ibBJSJ7HE3bsHh/1OSQcDdzsSioAK04Fo9ws/v7n6lt+QBZ6fGmwyIkZQ== + dependencies: + bignumber.js "^9.3.0" + "@trezor/utxo-lib@2.3.1": version "2.3.1" resolved "https://registry.npmjs.org/@trezor/utxo-lib/-/utxo-lib-2.3.1.tgz" @@ -5709,6 +6370,29 @@ varuint-bitcoin "2.0.0" wif "^5.0.0" +"@trezor/utxo-lib@2.4.2": + version "2.4.2" + resolved "https://registry.yarnpkg.com/@trezor/utxo-lib/-/utxo-lib-2.4.2.tgz#1d2cc81863c2c89bc207ff3eb013aa1cbfde9719" + integrity sha512-dTXfBg/cEKnmHM5CLG5+0qrp6fqOfwxqe8YPACdKeM7g1XJKCGDAuFpDUVeT3lrcUsTh6bEMHM06z4H3gZp5MQ== + dependencies: + "@trezor/utils" "9.4.2" + bchaddrjs "^0.5.2" + bech32 "^2.0.0" + bip66 "^2.0.0" + bitcoin-ops "^1.4.1" + blake-hash "^2.0.0" + blakejs "^1.2.1" + bn.js "^5.2.2" + bs58 "^6.0.0" + bs58check "^4.0.0" + create-hmac "^1.1.7" + int64-buffer "^1.1.0" + pushdata-bitcoin "^1.0.1" + tiny-secp256k1 "^1.1.7" + typeforce "^1.18.0" + varuint-bitcoin "2.0.0" + wif "^5.0.0" + "@trezor/websocket-client@1.1.1": version "1.1.1" resolved "https://registry.npmjs.org/@trezor/websocket-client/-/websocket-client-1.1.1.tgz" @@ -5717,6 +6401,14 @@ "@trezor/utils" "9.3.1" ws "^8.18.0" +"@trezor/websocket-client@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@trezor/websocket-client/-/websocket-client-1.2.2.tgz#f7fe1da844df847d83e3de66906b22fe092aed10" + integrity sha512-vu9L1V/5yh8LHQCmsGC9scCnihELsVuR5Tri1IvW3CdgTUFFcfjsEgXsFqFME3HlxuUmx6qokw0Gx/o0/hzaSQ== + dependencies: + "@trezor/utils" "9.4.2" + ws "^8.18.0" + "@ts-morph/common@~0.27.0": version "0.27.0" resolved "https://registry.npmjs.org/@ts-morph/common/-/common-0.27.0.tgz" @@ -7365,6 +8057,23 @@ "@webassemblyjs/ast" "1.14.1" "@xtuc/long" "4.2.2" +"@xrplf/isomorphic@^1.0.0", "@xrplf/isomorphic@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@xrplf/isomorphic/-/isomorphic-1.0.1.tgz#d7676e0ec0e55a39f37ddc1f3cc30eeab52e0739" + integrity sha512-0bIpgx8PDjYdrLFeC3csF305QQ1L7sxaWnL5y71mCvhenZzJgku9QsA+9QCXBC1eNYtxWO/xR91zrXJy2T/ixg== + dependencies: + "@noble/hashes" "^1.0.0" + eventemitter3 "5.0.1" + ws "^8.13.0" + +"@xrplf/secret-numbers@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@xrplf/secret-numbers/-/secret-numbers-2.0.0.tgz#36ffa45c41e78efc6179ca4fe9d950260103dbce" + integrity sha512-z3AOibRTE9E8MbjgzxqMpG1RNaBhQ1jnfhNCa1cGf2reZUJzPMYs4TggQTc7j8+0WyV3cr7y/U8Oz99SXIkN5Q== + dependencies: + "@xrplf/isomorphic" "^1.0.1" + ripple-keypairs "^2.0.0" + "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz" @@ -8642,6 +9351,15 @@ axe-core@^4.10.0: resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz" integrity sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg== +axios@^1.10.0: + version "1.13.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.13.2.tgz#9ada120b7b5ab24509553ec3e40123521117f687" + integrity sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.4" + proxy-from-env "^1.1.0" + axios@^1.7.2, axios@^1.8.4: version "1.11.0" resolved "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz" @@ -9041,7 +9759,7 @@ bn.js@^4.11.8, bn.js@^4.11.9: resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz" integrity sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw== -bn.js@^5.1.1, bn.js@^5.2.0, bn.js@^5.2.1: +bn.js@^5.1.1, bn.js@^5.2.0, bn.js@^5.2.1, bn.js@^5.2.2: version "5.2.2" resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz" integrity sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw== @@ -9254,6 +9972,11 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +buffer-equal-constant-time@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== + buffer-from@1.x, buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" @@ -10631,6 +11354,11 @@ detect-browser@5.3.0: resolved "https://registry.npmjs.org/detect-browser/-/detect-browser-5.3.0.tgz" integrity sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w== +detect-europe-js@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/detect-europe-js/-/detect-europe-js-0.1.2.tgz#aa76642e05dae786efc2e01a23d4792cd24c7b88" + integrity sha512-lgdERlL3u0aUdHocoouzT10d9I89VVhk0qNRmll7mXdGfJT1/wqZ2ZLA4oJAjeACPY5fT1wsbq2AT+GkuInsow== + detect-indent@^6.1.0: version "6.1.0" resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz" @@ -10892,6 +11620,13 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + eciesjs@^0.4.10: version "0.4.15" resolved "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.15.tgz" @@ -12021,7 +12756,18 @@ ethereum-cryptography@^2.2.1: "@scure/bip32" "1.4.0" "@scure/bip39" "1.3.0" -eventemitter3@^5.0.1: +ethereum-cryptography@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-3.2.0.tgz#42a04b57834bf536e552b50a70b9ee5057c71dc6" + integrity sha512-Urr5YVsalH+Jo0sYkTkv1MyI9bLYZwW8BENZCeE1QYaTHETEYx0Nv/SVsWkSqpYrzweg6d8KMY1wTjH/1m/BIg== + dependencies: + "@noble/ciphers" "1.3.0" + "@noble/curves" "1.9.0" + "@noble/hashes" "1.8.0" + "@scure/bip32" "1.7.0" + "@scure/bip39" "1.6.0" + +eventemitter3@5.0.1, eventemitter3@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz" integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== @@ -14307,6 +15053,11 @@ is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.4: dependencies: call-bound "^1.0.3" +is-standalone-pwa@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-standalone-pwa/-/is-standalone-pwa-0.1.1.tgz#7a1b0459471a95378aa0764d5dc0a9cec95f2871" + integrity sha512-9Cbovsa52vNQCjdXOzeQq5CnCbAcRk05aU62K20WO372NrTv0NxibLFCK6lQ4/iZEFdEA3p3t2VNOn8AJ53F5g== + is-stream@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz" @@ -15604,6 +16355,23 @@ jsprim@^1.2.2: object.assign "^4.1.4" object.values "^1.1.6" +jwa@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.1.tgz#bf8176d1ad0cd72e0f3f58338595a13e110bc804" + integrity sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg== + dependencies: + buffer-equal-constant-time "^1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4" + integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== + dependencies: + jwa "^2.0.0" + safe-buffer "^5.0.1" + keyv@^4.5.3, keyv@^4.5.4: version "4.5.4" resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz" @@ -15870,6 +16638,11 @@ long@5.2.0: resolved "https://registry.npmjs.org/long/-/long-5.2.0.tgz" integrity sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w== +long@5.2.5: + version "5.2.5" + resolved "https://registry.yarnpkg.com/long/-/long-5.2.5.tgz#716dcb0807c406345b3fc0f34d8042b41edb9d16" + integrity sha512-e0r9YBBgNCq1D1o5Dp8FMH0N5hsFtXDBiVa0qoJPHpakvZkmDKPRoGffZJII/XsHvj9An9blm+cRJ01yQqU+Dw== + long@^5.0.0: version "5.3.2" resolved "https://registry.npmjs.org/long/-/long-5.3.2.tgz" @@ -19451,6 +20224,14 @@ ripple-address-codec@^4.1.1, ripple-address-codec@^4.3.1: base-x "^3.0.9" create-hash "^1.1.2" +ripple-address-codec@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ripple-address-codec/-/ripple-address-codec-5.0.0.tgz#97059f7bba6f9ed7a52843de8aa427723fb529f6" + integrity sha512-de7osLRH/pt5HX2xw2TRJtbdLLWHu0RXirpQaEeCnWKY5DYHykh3ETSkofvm0aX0LJiV7kwkegJxQkmbO94gWw== + dependencies: + "@scure/base" "^1.1.3" + "@xrplf/isomorphic" "^1.0.0" + ripple-binary-codec@^1.1.3: version "1.11.0" resolved "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-1.11.0.tgz" @@ -19463,6 +20244,15 @@ ripple-binary-codec@^1.1.3: decimal.js "^10.2.0" ripple-address-codec "^4.3.1" +ripple-binary-codec@^2.5.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/ripple-binary-codec/-/ripple-binary-codec-2.5.1.tgz#81bf57e8b79754277e6ccdf6b29a32015a496fbd" + integrity sha512-rzN4GTorLRH0bQD7Tccgn6Eq4aunMhZaUTXDEUJ+3xrjo0m1Av5AY1Doc/jsCIaxPIAnyoVg5rWlmI93U7pGdg== + dependencies: + "@xrplf/isomorphic" "^1.0.1" + bignumber.js "^9.0.0" + ripple-address-codec "^5.0.0" + ripple-keypairs@^1.0.3: version "1.3.1" resolved "https://registry.npmjs.org/ripple-keypairs/-/ripple-keypairs-1.3.1.tgz" @@ -19474,6 +20264,15 @@ ripple-keypairs@^1.0.3: hash.js "^1.0.3" ripple-address-codec "^4.3.1" +ripple-keypairs@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ripple-keypairs/-/ripple-keypairs-2.0.0.tgz#4a1a8142e9a58c07e61b3cc6cfe7317db718d289" + integrity sha512-b5rfL2EZiffmklqZk1W+dvSy97v3V/C7936WxCCgDynaGPp7GE6R2XO7EU9O2LlM/z95rj870IylYnOQs+1Rag== + dependencies: + "@noble/curves" "^1.0.0" + "@xrplf/isomorphic" "^1.0.0" + ripple-address-codec "^5.0.0" + ripple-lib-transactionparser@0.8.2: version "0.8.2" resolved "https://registry.npmjs.org/ripple-lib-transactionparser/-/ripple-lib-transactionparser-0.8.2.tgz" @@ -19769,6 +20568,11 @@ semver@6.x, semver@^6.0.0, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== +semver@7.7.1: + version "7.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" + integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== + semver@^7.1.1, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3, semver@^7.7.1, semver@^7.7.2: version "7.7.2" resolved "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz" @@ -20235,7 +21039,7 @@ socks-proxy-agent@8.0.4: debug "^4.3.4" socks "^2.8.3" -socks-proxy-agent@^8.0.5: +socks-proxy-agent@8.0.5, socks-proxy-agent@^8.0.5: version "8.0.5" resolved "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz" integrity sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw== @@ -20488,18 +21292,6 @@ stellar-react@^0.0.4: react "^19.0.0" react-dom "^19.0.0" -stellar-react@^0.0.6: - version "0.0.6" - resolved "https://registry.npmjs.org/stellar-react/-/stellar-react-0.0.6.tgz" - integrity sha512-0ahoXlkkAz5gxGei7R9MEQDYxytWoEKxEFIxop2iHT4yy5qfCGr0PjW64oKlledym6axJrxRtcaPqOozBWULbw== - dependencies: - "@creit.tech/stellar-wallets-kit" "^1.7.3" - "@stellar/freighter-api" "^3.1.0" - "@stellar/stellar-sdk" "^14.0.0-rc.3" - "@types/react" "^19.0.10" - react "^19.0.0" - react-dom "^19.0.0" - stop-iteration-iterator@^1.0.0, stop-iteration-iterator@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz" @@ -21173,7 +21965,7 @@ tiny-invariant@^1.3.1, tiny-invariant@^1.3.3: resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz" integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg== -tiny-secp256k1@^1.1.6: +tiny-secp256k1@^1.1.6, tiny-secp256k1@^1.1.7: version "1.1.7" resolved "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.7.tgz" integrity sha512-eb+F6NabSnjbLwNoC+2o5ItbmP1kg7HliWue71JgLegQt6A5mTN8YbvTLCazdlg6e5SV6A+r8OGvZYskdlmhqQ== @@ -21805,11 +22597,25 @@ typescript@^5, typescript@^5.3.3, typescript@^5.8.3: resolved "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz" integrity sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A== +ua-is-frozen@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ua-is-frozen/-/ua-is-frozen-0.1.2.tgz#bfbc5f06336e379590e36beca444188c7dc3a7f3" + integrity sha512-RwKDW2p3iyWn4UbaxpP2+VxwqXh0jpvdxsYpZ5j/MLLiQOfbsV5shpgQiw93+KMYQPcteeMQ289MaAFzs3G9pw== + ua-parser-js@^1.0.37: version "1.0.41" resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.41.tgz" integrity sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug== +ua-parser-js@^2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-2.0.6.tgz#1dd221f7f2a27357c6a342296852f6391d77d4f0" + integrity sha512-EmaxXfltJaDW75SokrY4/lXMrVyXomE/0FpIIqP2Ctic93gK7rlme55Cwkz8l3YZ6gqf94fCU7AnIkidd/KXPg== + dependencies: + detect-europe-js "^0.1.2" + is-standalone-pwa "^0.1.1" + ua-is-frozen "^0.1.2" + uc.micro@^2.0.0, uc.micro@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz" @@ -21862,6 +22668,11 @@ undici-types@^6.20.0, undici-types@~6.21.0: resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz" integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== +undici-types@^7.11.0: + version "7.16.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" + integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== + undici-types@~5.26.4: version "5.26.5" resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz" @@ -22155,7 +22966,7 @@ url@^0.11.0: punycode "^1.4.1" qs "^6.12.3" -usb@^2.14.0: +usb@^2.14.0, usb@^2.15.0: version "2.16.0" resolved "https://registry.npmjs.org/usb/-/usb-2.16.0.tgz" integrity sha512-jD88fvzDViMDH5KmmNJgzMBDj/95bDTt6+kBNaNxP4G98xUTnDMiLUY2CYmToba6JAFhM9VkcaQuxCNRLGR7zg== @@ -22735,7 +23546,7 @@ ws@^7.0.0, ws@^7.2.0, ws@^7.5.1, ws@^7.5.10: resolved "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz" integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== -ws@^8.11.0, ws@^8.18.0, ws@^8.2.3, ws@^8.5.0: +ws@^8.11.0, ws@^8.13.0, ws@^8.18.0, ws@^8.2.3, ws@^8.5.0: version "8.18.3" resolved "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz" integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== @@ -22762,6 +23573,22 @@ xmlchars@^2.1.1, xmlchars@^2.2.0: resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== +xrpl@^4.3.0: + version "4.4.3" + resolved "https://registry.yarnpkg.com/xrpl/-/xrpl-4.4.3.tgz#b5211d9ce58d70caac1ccbb68f3c13e63f9b2a5c" + integrity sha512-vi2OjuNkiaP8nv1j+nqHp8GZwwEjO6Y8+j/OuVMg6M4LwXEwyHdIj33dlg7cyY1Lw5+jb9HqFOQvABhaywVbTQ== + dependencies: + "@scure/bip32" "^1.3.1" + "@scure/bip39" "^1.2.1" + "@xrplf/isomorphic" "^1.0.1" + "@xrplf/secret-numbers" "^2.0.0" + bignumber.js "^9.0.0" + eventemitter3 "^5.0.1" + fast-json-stable-stringify "^2.1.0" + ripple-address-codec "^5.0.0" + ripple-binary-codec "^2.5.0" + ripple-keypairs "^2.0.0" + xtend@^4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" From 100da46dee08ebe2ca3c15ee812524f0f83ec012 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Fri, 28 Nov 2025 12:26:00 -0300 Subject: [PATCH 2/7] =?UTF-8?q?=E2=9C=A8=20Add=20createVaultAutoInvest=20w?= =?UTF-8?q?ith=20strategy=20allocation=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add StrategyAllocationSliders for percentage-based distribution - Implement auto-invest flow when deposit has strategy allocation - Add enhanced toast with copy address and external link - Replace upgradable button with checkbox --- .../components/CreateVault/CreateVault.tsx | 423 +++++++++++++----- 1 file changed, 320 insertions(+), 103 deletions(-) diff --git a/apps/dapp/src/components/CreateVault/CreateVault.tsx b/apps/dapp/src/components/CreateVault/CreateVault.tsx index 498b015e..a1ad5ef1 100644 --- a/apps/dapp/src/components/CreateVault/CreateVault.tsx +++ b/apps/dapp/src/components/CreateVault/CreateVault.tsx @@ -1,14 +1,12 @@ import { Asset, AssetContext, Strategy, Vault, VaultContext } from '@/contexts' +import { useUser } from '@/contexts/UserContext' import { isValidAddress } from '@/helpers/address' import { decimalRegex, parseNumericInput } from '@/helpers/input' import { parsePascalCase } from '@/helpers/utils' -import { getAssetParamsSCVal, getCreateDeFindexVaultDepositParams, getCreateDeFindexVaultParams } from '@/helpers/vault' -import { FactoryMethod, useFactoryCallback } from '@/hooks/useFactory' -import { soroswapRouterAddress } from '@/hooks/usePublicAddresses' -import { Button, createListCollection, Fieldset, Flex, HStack, Stack } from '@chakra-ui/react' -import { xdr } from '@stellar/stellar-sdk' +import { useDefindexSDK } from '@/hooks/useDefindexSDK' +import { Box, Button, Checkbox, createListCollection, Fieldset, Flex, HStack, IconButton, Slider, Stack, Text } from '@chakra-ui/react' import React, { useContext, useEffect, useState } from 'react' -import { useSorobanReact, WalletNetwork } from 'stellar-react' +import { HiExternalLink } from 'react-icons/hi' import BackgroundCard from '../ui/BackgroundCard' import { baseMargin } from '../ui/Common' import { CustomSelect, FormField } from '../ui/CustomInputFields' @@ -87,7 +85,20 @@ function SelectStrategies({ asset }: { asset: Asset }) { } useEffect(() => { - const newStrategies: Strategy[] = asset.strategies.filter((strategy) => selectedStrategies.includes(strategy.address)) + const newStrategies: Strategy[] = asset.strategies + .filter((strategy) => selectedStrategies.includes(strategy.address)) + .map((strategy, _, arr) => ({ + ...strategy, + // Initialize with equal distribution + amount: arr.length > 0 ? Math.floor(100 / arr.length) : 0, + })); + + // Adjust last strategy to ensure sum is exactly 100 + if (newStrategies.length > 0) { + const sum = newStrategies.reduce((acc, s) => acc + (s.amount || 0), 0); + newStrategies[newStrategies.length - 1].amount = (newStrategies[newStrategies.length - 1].amount || 0) + (100 - sum); + } + const assetAllocation = vaultContext?.newVault.assetAllocation.map((item) => { if (item.address === asset.address) { return { @@ -114,6 +125,122 @@ function SelectStrategies({ asset }: { asset: Asset }) { ) } +interface StrategyAllocationSlidersProps { + assetIndex: number; + assetAmount: number; + assetSymbol: string; +} + +function StrategyAllocationSliders({ assetIndex, assetAmount, assetSymbol }: StrategyAllocationSlidersProps) { + const vaultContext = useContext(VaultContext); + const strategies = vaultContext?.newVault.assetAllocation[assetIndex]?.strategies || []; + + if (strategies.length === 0 || assetAmount <= 0) { + return null; + } + + const handlePercentageChange = (strategyIndex: number, newPercentage: number) => { + if (!vaultContext) return; + + const currentStrategies = [...strategies]; + const oldPercentage = currentStrategies[strategyIndex].amount || 0; + const diff = newPercentage - oldPercentage; + + // Update the changed strategy + currentStrategies[strategyIndex] = { + ...currentStrategies[strategyIndex], + amount: newPercentage, + }; + + // Distribute the difference among other strategies proportionally + const otherStrategies = currentStrategies.filter((_, i) => i !== strategyIndex); + const otherTotal = otherStrategies.reduce((sum, s) => sum + (s.amount || 0), 0); + + if (otherTotal > 0 && diff !== 0) { + const remaining = -diff; + otherStrategies.forEach((_, i) => { + const actualIndex = i >= strategyIndex ? i + 1 : i; + const currentAmount = currentStrategies[actualIndex].amount || 0; + const proportion = currentAmount / otherTotal; + const adjustment = Math.round(remaining * proportion); + const newAmount = Math.max(0, Math.min(100, currentAmount + adjustment)); + currentStrategies[actualIndex] = { + ...currentStrategies[actualIndex], + amount: newAmount, + }; + }); + + // Ensure total is exactly 100 + const total = currentStrategies.reduce((sum, s) => sum + (s.amount || 0), 0); + if (total !== 100 && currentStrategies.length > 1) { + const lastOtherIndex = strategyIndex === currentStrategies.length - 1 ? 0 : currentStrategies.length - 1; + currentStrategies[lastOtherIndex] = { + ...currentStrategies[lastOtherIndex], + amount: (currentStrategies[lastOtherIndex].amount || 0) + (100 - total), + }; + } + } + + const newAssetAllocation = vaultContext.newVault.assetAllocation.map((item, i) => { + if (i === assetIndex) { + return { ...item, strategies: currentStrategies }; + } + return item; + }); + + vaultContext.setNewVault({ + ...vaultContext.newVault, + assetAllocation: newAssetAllocation, + }); + }; + + const formatAmount = (percentage: number) => { + const amount = (assetAmount * percentage) / 100; + return amount.toFixed(2); + }; + + return ( + + + Strategy Allocation + + + {strategies.map((strategy, idx) => ( + + + + {parsePascalCase(strategy.name)} + + + {strategy.amount || 0}% = {formatAmount(strategy.amount || 0)} {assetSymbol} + + + handlePercentageChange(idx, details.value[0])} + > + + + + + + + + + ))} + + sum + (s.amount || 0), 0) === 100 ? 'green.400' : 'red.400'}> + Total: {strategies.reduce((sum, s) => sum + (s.amount || 0), 0)}% + + + + + ); +} + function AddStrategies() { const assetContext = useContext(AssetContext); const vaultContext = useContext(VaultContext); @@ -140,7 +267,7 @@ function AddStrategies() { return ( - + {vaultContext?.newVault.assetAllocation.map((item, index) => ( handleDepositAmount(e, index)} /> a.address === item.address)!} /> + ))} @@ -203,23 +335,28 @@ function VaultConfig() { /> - + + + {upgradable ? 'Upgradable' : 'Non-Upgradable'} + + { + setUpgradable(!!e.checked) + vaultContext?.setNewVault({ + ...vaultContext.newVault, + upgradable: !!e.checked, + }) + }} + size="lg" + > + + + + + + + ); @@ -346,14 +483,14 @@ function FeeConfig() { function CreateVaultButton() { const vaultContext = useContext(VaultContext); - const factoryCallback = useFactoryCallback(); - const sorobanContext = useSorobanReact(); + const { address, activeNetwork } = useUser(); + const { createVault, createVaultAutoInvest } = useDefindexSDK(); const [loading, setLoading] = useState(false); const [disabled, setDisabled] = useState(false); useEffect(() => { if ( - !sorobanContext.address + !address || !vaultContext || !vaultContext.newVault.name || !vaultContext.newVault.symbol @@ -368,9 +505,8 @@ function CreateVaultButton() { } else { setDisabled(false); } - // eslint-disable-next-line react-hooks/exhaustive-deps }, [ - sorobanContext.address, + address, vaultContext?.newVault.name, vaultContext?.newVault.symbol, vaultContext?.newVault.vaultManager, @@ -378,107 +514,188 @@ function CreateVaultButton() { vaultContext?.newVault.rebalanceManager, vaultContext?.newVault.feeReceiver, vaultContext?.newVault.feePercent, - vaultContext?.newVault.assetAllocation.length + vaultContext?.newVault.assetAllocation.length, + vaultContext ]); const handleCreateVault = async () => { - if (!vaultContext) return; + if (!vaultContext || !address) return; setLoading(true); - const soroswapRouter = await soroswapRouterAddress(sorobanContext.activeNetwork); const newVault = vaultContext.newVault; if (!newVault) return; - const assetParams = getAssetParamsSCVal( - newVault.assetAllocation + // Convert fee percentage to basis points (1% = 100 bps) + const vaultFeeBps = Math.round(newVault.feePercent * 100); + + // Check if there's a deposit with strategy allocation + const hasDeposit = newVault.assetAllocation.some((asset) => asset.amount > 0); + const hasStrategyAllocation = newVault.assetAllocation.some((asset) => + asset.strategies.some((strategy) => (strategy.amount || 0) > 0) ); - let params: xdr.ScVal[] = []; - - const isCreateAndDeposit = newVault.assetAllocation.some((asset) => asset.amount > 0); - if (isCreateAndDeposit) { - if (!sorobanContext.address) return; - params = getCreateDeFindexVaultDepositParams( - sorobanContext.address, - newVault.emergencyManager, - newVault.rebalanceManager, - newVault.feeReceiver, - newVault.vaultManager, - newVault.feePercent, - newVault.name, - newVault.symbol, - assetParams, - soroswapRouter, - newVault.upgradable, - newVault.assetAllocation - ); - try { - const result = await factoryCallback(FactoryMethod.CREATE_DEFINDEX_VAULT_DEPOSIT, params, true); + + try { + let result; + + if (hasDeposit && hasStrategyAllocation) { + // Use createVaultAutoInvest - creates vault, deposits, and invests in strategies + const autoInvestConfig = { + caller: address, + roles: { + emergencyManager: newVault.emergencyManager, + rebalanceManager: newVault.rebalanceManager, + feeReceiver: newVault.feeReceiver, + manager: newVault.vaultManager, + }, + name: newVault.name, + symbol: newVault.symbol, + vaultFee: vaultFeeBps, + upgradable: newVault.upgradable, + assets: newVault.assetAllocation.map((asset) => ({ + address: asset.address, + symbol: asset.symbol || '', + amount: Math.round(asset.amount * 10_000_000), // Convert to stroops + strategies: asset.strategies.map((strategy) => ({ + address: strategy.address, + name: strategy.name, + // Calculate amount based on percentage + amount: Math.round((asset.amount * (strategy.amount || 0) / 100) * 10_000_000), + })), + })), + }; + + result = await createVaultAutoInvest(autoInvestConfig); + + const txHash = result.txHash || result.hash; + const vaultAddress = result.predictedVaultAddress; + const network = activeNetwork === 'mainnet' ? 'public' : 'testnet'; + toaster.create({ - title: 'Vault created', - description: 'Vault created successfully', + title: 'Vault created with auto-invest', + description: vaultAddress ? ( + + { + navigator.clipboard.writeText(vaultAddress); + toaster.create({ + title: 'Address copied', + type: 'success', + duration: 2000, + }); + }} + > + {vaultAddress.slice(0, 8)}...{vaultAddress.slice(-8)} + + { + window.open(`https://stellar.expert/explorer/${network}/contract/${vaultAddress}`, '_blank'); + }} + > + + + + ) : 'Vault created and funds invested successfully', type: 'success', - duration: 5000, + duration: 10000, action: { label: 'View transaction', onClick: () => { - window.open(`https://stellar.expert/explorer/${sorobanContext.activeNetwork == WalletNetwork.PUBLIC ? 'public' : 'testnet'}/search?term=${result.txHash}`, '_blank'); + window.open(`https://stellar.expert/explorer/${network}/search?term=${txHash}`, '_blank'); } } }); - setLoading(false); - } catch (error: unknown) { - console.error('Error creating vault:', error); - toaster.create({ - title: 'Error creating vault', - description: error instanceof Error ? error.message : 'An error occurred', - type: 'error', - duration: 5000, - }); - setLoading(false); - return; - } - } else { - params = getCreateDeFindexVaultParams( - newVault.emergencyManager, - newVault.rebalanceManager, - newVault.feeReceiver, - newVault.vaultManager, - newVault.feePercent, - newVault.name, - newVault.symbol, - assetParams, - soroswapRouter, - newVault.upgradable, - ); - try { - const result = await factoryCallback(FactoryMethod.CREATE_DEFINDEX_VAULT, params, true); + } else { + // Use createVault - creates vault without deposit + const vaultConfig = { + roles: { + 0: newVault.emergencyManager, + 1: newVault.feeReceiver, + 2: newVault.vaultManager, + 3: newVault.rebalanceManager, + }, + vault_fee_bps: vaultFeeBps, + assets: newVault.assetAllocation.map((asset) => ({ + address: asset.address, + strategies: asset.strategies.map((strategy) => ({ + address: strategy.address, + name: strategy.name, + paused: strategy.paused, + })), + })), + name_symbol: { + name: newVault.name, + symbol: newVault.symbol, + }, + upgradable: newVault.upgradable, + caller: address, + }; + + result = await createVault(vaultConfig); + + const txHash = result.txHash || result.hash; + const vaultAddr = result.predictedVaultAddress; + const net = activeNetwork === 'mainnet' ? 'public' : 'testnet'; + toaster.create({ title: 'Vault created', - description: `Vault created successfully`, + description: vaultAddr ? ( + + { + navigator.clipboard.writeText(vaultAddr); + toaster.create({ + title: 'Address copied', + type: 'success', + duration: 2000, + }); + }} + > + {vaultAddr.slice(0, 8)}...{vaultAddr.slice(-8)} + + { + window.open(`https://stellar.expert/explorer/${net}/contract/${vaultAddr}`, '_blank'); + }} + > + + + + ) : 'Vault created successfully', type: 'success', - duration: 5000, + duration: 10000, action: { label: 'View transaction', onClick: () => { - window.open(`https://stellar.expert/explorer/${sorobanContext.activeNetwork == WalletNetwork.PUBLIC ? 'public' : 'testnet'}/search?term=${result.txHash}`, '_blank'); + window.open(`https://stellar.expert/explorer/${net}/search?term=${txHash}`, '_blank'); } } }); - setLoading(false); - } catch (error: unknown) { - console.error('Error creating vault:', error); - toaster.create({ - title: 'Error creating vault', - description: error instanceof Error ? error.message : 'An error occurred', - type: 'error', - duration: 5000, - }); - setLoading(false); - return; } + } catch (error: unknown) { + console.error('Error creating vault:', error); + toaster.create({ + title: 'Error creating vault', + description: error instanceof Error ? error.message : 'An error occurred', + type: 'error', + duration: 5000, + }); + } finally { + setLoading(false); } - setLoading(false); } + return ( ) From 3d848779286a23f79ec2faf304aec678e3a48ce5 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Fri, 28 Nov 2025 12:29:54 -0300 Subject: [PATCH 3/7] =?UTF-8?q?=F0=9F=8E=A8=20Improve=20NavBar=20network?= =?UTF-8?q?=20selector=20dropdown?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add Portal and MenuPositioner for proper menu rendering - Fix menu positioning to dropdown below trigger --- apps/dapp/src/components/NavBar/NavBar.css | 33 +++++++++++++ apps/dapp/src/components/NavBar/NavBar.tsx | 56 ++++++++++++++++++++-- 2 files changed, 84 insertions(+), 5 deletions(-) diff --git a/apps/dapp/src/components/NavBar/NavBar.css b/apps/dapp/src/components/NavBar/NavBar.css index 02337d7e..05c3b08f 100644 --- a/apps/dapp/src/components/NavBar/NavBar.css +++ b/apps/dapp/src/components/NavBar/NavBar.css @@ -15,3 +15,36 @@ width: 100vw; backdrop-filter: blur(18px); } + +.network-selector-btn { + font-weight: 500; + text-transform: capitalize; + background-color: #ffffff00 !important; + border: solid 1px #d3ffb4 !important; + color: #D3FFB4; + font-family: var(--font-inter); + font-weight: 600; + line-height: 60px; + letter-spacing: 0%; +} + +.network-menu-content { + background: rgba(26, 44, 34, 0.063); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 8px; + backdrop-filter: blur(10px); + min-width: 170px; + z-index: 1001; +} + +.network-item-active { + background-color: #49ff7d0e !important; +} + +.network-item { + padding: 8px 16px; +} + +.network-item:hover { + background-color: #5eff8c23 !important; +} diff --git a/apps/dapp/src/components/NavBar/NavBar.tsx b/apps/dapp/src/components/NavBar/NavBar.tsx index 1b4ba96f..ab8ee1ab 100644 --- a/apps/dapp/src/components/NavBar/NavBar.tsx +++ b/apps/dapp/src/components/NavBar/NavBar.tsx @@ -1,14 +1,59 @@ 'use client' -import { Box, Flex, Image, Link } from '@chakra-ui/react' -import { navBarHeight } from '../ui/Common' -import ConnectButton from '../web3/ConnectWallet' -import './NavBar.css' - +import { useUser } from '@/contexts/UserContext'; +import { Box, Button, Flex, Image, Link, MenuContent, MenuItem, MenuPositioner, MenuRoot, MenuTrigger, Portal } from '@chakra-ui/react'; +import { RiArrowDropDownLine } from "react-icons/ri"; +import { navBarHeight } from '../ui/Common'; +import ConnectButton from '../web3/ConnectWallet'; +import './NavBar.css'; const links = [ { name: 'Docs', href: 'https://docs.defindex.io/' }, { name: 'Defindex Home', href: 'https://defindex.io/' }, ] + +function NetworkSelector() { + const { activeNetwork, setActiveNetwork } = useUser(); + + const networks = [ + { id: 'mainnet', name: 'Mainnet' }, + { id: 'testnet', name: 'Testnet' }, + ] as const; + + return ( + + + + + + + + {networks.map((network) => ( + setActiveNetwork(network.id)} + className={`network-item ${activeNetwork === network.id ? 'network-item-active' : ''} `} + > + {network.name} + + ))} + + + + + ); +} + function NavBar() { return ( @@ -20,6 +65,7 @@ function NavBar() { {link.name} ))} + From 7f33e164eb5674e9212ad06eeff28f64f0974ca2 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Fri, 28 Nov 2025 12:30:04 -0300 Subject: [PATCH 4/7] =?UTF-8?q?=F0=9F=94=A7=20Update=20helpers=20and=20hoo?= =?UTF-8?q?ks=20for=20SDK=20migration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update AddressToClipboard component - Update ConnectWallet for new wallet kit - Update networkName helper - Update usePublicAddresses and useStrategy hooks - Update StrategiesProvider --- .../src/components/ui/AddressToClipboard.tsx | 6 +- .../src/components/web3/ConnectWallet.tsx | 8 +- apps/dapp/src/helpers/networkName.ts | 23 +---- apps/dapp/src/hooks/usePublicAddresses.ts | 11 +-- apps/dapp/src/hooks/useStrategy.ts | 99 ++++++++++++------- .../dapp/src/providers/StrategiesProvider.tsx | 9 +- 6 files changed, 86 insertions(+), 70 deletions(-) diff --git a/apps/dapp/src/components/ui/AddressToClipboard.tsx b/apps/dapp/src/components/ui/AddressToClipboard.tsx index 8c67d34e..c632af1d 100644 --- a/apps/dapp/src/components/ui/AddressToClipboard.tsx +++ b/apps/dapp/src/components/ui/AddressToClipboard.tsx @@ -3,11 +3,11 @@ import { HStack, IconButton, Text } from '@chakra-ui/react'; import React from 'react' import { FaCopy, FaExternalLinkAlt } from "react-icons/fa"; import './AddressToClipboard.css' -import { useSorobanReact, WalletNetwork } from 'stellar-react'; +import { useUser } from '@/contexts/UserContext'; import { toaster } from './toaster'; function AddressToClipboard({ label, vaultAddress }: { label: string, vaultAddress: string }) { - const sorobanContext = useSorobanReact(); + const { activeNetwork } = useUser(); const copyToClipboard = () => { navigator.clipboard.writeText(vaultAddress).then(() => { @@ -27,7 +27,7 @@ function AddressToClipboard({ label, vaultAddress }: { label: string, vaultAddre }; const openInExplorer = () => { - const networkName = sorobanContext.activeNetwork === WalletNetwork.PUBLIC ? 'public' : 'testnet'; + const networkName = activeNetwork === 'mainnet' ? 'public' : 'testnet'; const url = `https://stellar.expert/explorer/${networkName}/contract/${vaultAddress}`; window.open(url, '_blank'); } diff --git a/apps/dapp/src/components/web3/ConnectWallet.tsx b/apps/dapp/src/components/web3/ConnectWallet.tsx index 16b8e25b..c9a74460 100644 --- a/apps/dapp/src/components/web3/ConnectWallet.tsx +++ b/apps/dapp/src/components/web3/ConnectWallet.tsx @@ -1,17 +1,17 @@ import React from "react" import { Button } from '@chakra-ui/react' -import { useSorobanReact } from 'stellar-react' +import { useUser } from "@/contexts/UserContext" import { shortenAddress } from "@/helpers/address" import './ConnectWallet.css' export const ConnectButton = () => { - const { address, disconnect, connect } = useSorobanReact(); + const { address, disconnect, connectWallet } = useUser(); const handleConnect = async () => { if (address) { - await disconnect(); + disconnect(); } else { - await connect(); + await connectWallet(); } } return ( diff --git a/apps/dapp/src/helpers/networkName.ts b/apps/dapp/src/helpers/networkName.ts index 9f7e99bb..222567e2 100644 --- a/apps/dapp/src/helpers/networkName.ts +++ b/apps/dapp/src/helpers/networkName.ts @@ -1,21 +1,8 @@ -import { WalletNetwork } from "stellar-react" +export type NetworkType = 'mainnet' | 'testnet'; -export const getNetworkName = (networkPassphrase?: WalletNetwork): string => { - if(!networkPassphrase) { - return 'testnet' - } - switch (networkPassphrase) { - case WalletNetwork.TESTNET: - return 'testnet' - case WalletNetwork.PUBLIC: - return 'mainnet' - case WalletNetwork.FUTURENET: - return 'futurenet' - case WalletNetwork.SANDBOX: - return 'sandbox' - case WalletNetwork.STANDALONE: - return 'standalone' - default: - return 'testnet' +export const getNetworkName = (network?: NetworkType): NetworkType => { + if (!network) { + return 'mainnet'; } + return network; } \ No newline at end of file diff --git a/apps/dapp/src/hooks/usePublicAddresses.ts b/apps/dapp/src/hooks/usePublicAddresses.ts index c257df45..1b25040d 100644 --- a/apps/dapp/src/hooks/usePublicAddresses.ts +++ b/apps/dapp/src/hooks/usePublicAddresses.ts @@ -1,6 +1,5 @@ import { Strategy } from '@/contexts'; -import { getNetworkName } from '@/helpers/networkName'; -import { WalletNetwork } from 'stellar-react'; +import { NetworkType } from '@/helpers/networkName'; import useSWR from 'swr'; @@ -14,7 +13,7 @@ export enum AllowedAssets { USDGLO = 'usdglo' } -export const usePublicAddresses = (network: string) => { +export const usePublicAddresses = (network: NetworkType) => { const fetcher = async (url: string) => { const response = await fetch(url, {cache: 'reload'}); const result = await response.json(); @@ -32,17 +31,17 @@ export const usePublicAddresses = (network: string) => { }; }; -export const soroswapRouterAddress = async (network: WalletNetwork | undefined) => { +export const soroswapRouterAddress = async (network: NetworkType | undefined) => { if (!network) { throw new Error('Network is undefined'); } - const response = await fetch(`https://raw.githubusercontent.com/soroswap/core/refs/heads/main/public/${network === WalletNetwork.PUBLIC ? 'mainnet' : 'testnet'}.contracts.json`, { + const response = await fetch(`https://raw.githubusercontent.com/soroswap/core/refs/heads/main/public/${network}.contracts.json`, { cache: 'reload', }); if (!response.ok) { - throw new Error(`Failed to fetch router address for network: ${getNetworkName(network)}`); + throw new Error(`Failed to fetch router address for network: ${network}`); } const data = await response.json(); diff --git a/apps/dapp/src/hooks/useStrategy.ts b/apps/dapp/src/hooks/useStrategy.ts index 6fd3b945..fbb67f45 100644 --- a/apps/dapp/src/hooks/useStrategy.ts +++ b/apps/dapp/src/hooks/useStrategy.ts @@ -1,55 +1,86 @@ -import { contractInvoke, useSorobanReact } from 'stellar-react'; -import { TxResponse } from 'stellar-react/dist/contracts/types'; import * as StellarSdk from '@stellar/stellar-sdk'; import { useCallback } from "react"; +import { useUser } from "@/contexts/UserContext"; export enum StrategyMethod { - INITIALIZE = "initialize", ASSET = "asset", - DEPOSIT = "deposit", - HARVEST = "harvest", - BALANCE = "balance", - WITHDRAW = "withdraw", } -const isObject = (val: unknown) => typeof val === 'object' && val !== null && !Array.isArray(val); +/** + * Simulates a contract call for read-only operations + * Uses Soroban RPC to invoke contract methods without signing + */ +async function simulateContractCall( + rpcUrl: string, + networkPassphrase: string, + contractAddress: string, + method: string, + args: StellarSdk.xdr.ScVal[] = [] +): Promise { + const server = new StellarSdk.rpc.Server(rpcUrl); + const contract = new StellarSdk.Contract(contractAddress); -// Type guard to check if result is TxResponse -function isTxResponse(result: unknown): result is TxResponse { - return Boolean(result && typeof result === 'object' && result !== null && 'status' in result); -} + // Create a temporary source account for simulation + const sourceKeypair = StellarSdk.Keypair.random(); + const sourceAccount = new StellarSdk.Account(sourceKeypair.publicKey(), "0"); + + // Build the transaction + const transaction = new StellarSdk.TransactionBuilder(sourceAccount, { + fee: "100", + networkPassphrase, + }) + .addOperation(contract.call(method, ...args)) + .setTimeout(30) + .build(); + + // Simulate the transaction + const simulation = await server.simulateTransaction(transaction); + + if (StellarSdk.rpc.Api.isSimulationError(simulation)) { + throw new Error(`Simulation failed: ${simulation.error}`); + } + if (!StellarSdk.rpc.Api.isSimulationSuccess(simulation)) { + throw new Error('Simulation did not return a successful result'); + } + // Extract the result + const result = simulation.result?.retval; + if (!result) { + throw new Error('No return value from simulation'); + } + + return result; +} export function useStrategyCallback() { - const sorobanContext = useSorobanReact(); + const { networkConfig } = useUser(); return useCallback( - async (address: string, method: StrategyMethod, args?: StellarSdk.xdr.ScVal[], signAndSend?: boolean) => { + async ( + address: string, + method: StrategyMethod, + args?: StellarSdk.xdr.ScVal[], + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _signAndSend?: boolean + ): Promise => { try { - const result = (await contractInvoke({ - contractAddress: address, - method: method, - args: args, - sorobanContext, - signAndSend: signAndSend, - reconnectAfterTx: false, - })); - if (!signAndSend) return result; - if (isTxResponse(result)) { - if ( - isObject(result) && - result?.status !== StellarSdk.rpc.Api.GetTransactionStatus.SUCCESS - ) throw result; - return result; - } + const result = await simulateContractCall( + networkConfig.sorobanRpcUrl, + networkConfig.networkPassphrase, + address, + method, + args + ); + return result; } catch (e: unknown) { const error = String(e); - if (error.includes('The user rejected')) throw new Error('Request denied by user. Please try to sign again.') - if (error.includes('Sign')) throw new Error('Request denied by user. Please try to sign again.'); - if (error.includes('non-existing value for contract instance')) throw new Error(`Strategy: ${address} not found.`); + if (error.includes('non-existing value for contract instance')) { + throw new Error(`Strategy: ${address} not found.`); + } throw new Error('Failed to interact with strategy. If the problem persists, please contact support.'); } - }, [sorobanContext] + }, + [networkConfig] ); } diff --git a/apps/dapp/src/providers/StrategiesProvider.tsx b/apps/dapp/src/providers/StrategiesProvider.tsx index 1a034521..df974c13 100644 --- a/apps/dapp/src/providers/StrategiesProvider.tsx +++ b/apps/dapp/src/providers/StrategiesProvider.tsx @@ -3,17 +3,16 @@ import { useState, useEffect, useMemo } from "react" import { Asset, AssetContext, AssetContextType } from "@/contexts" import useMounted from "@/hooks/useMounted" import { extractStrategies, usePublicAddresses } from "@/hooks/usePublicAddresses" -import { useSorobanReact } from "stellar-react" +import { useUser } from "@/contexts/UserContext" import { StrategyMethod, useStrategyCallback } from "@/hooks/useStrategy" import { scValToNative, xdr } from "@stellar/stellar-sdk" -import { getNetworkName } from "@/helpers/networkName" export const StrategiesProvider = ({ children }: { children: React.ReactNode }) => { const [assets, setAssets] = useState([]); - const sorobanContext = useSorobanReact(); - const { data: publicAddresses } = usePublicAddresses(getNetworkName(sorobanContext!.activeNetwork)); + const { activeNetwork } = useUser(); + const { data: publicAddresses } = usePublicAddresses(activeNetwork); const isMounted = useMounted(); const strategyCallback = useStrategyCallback(); @@ -72,7 +71,7 @@ export const StrategiesProvider = ({ children }: { children: React.ReactNode }) fetchStrategies(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [sorobanContext.activeNetwork, publicAddresses]); + }, [activeNetwork, publicAddresses]); const AssetContextValue: AssetContextType = useMemo(() => ({ assets, From 9cac6cda8a573822766a2c30d58cece761711101 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Fri, 28 Nov 2025 12:30:10 -0300 Subject: [PATCH 5/7] =?UTF-8?q?=F0=9F=93=9D=20Add=20CSS=20module=20type=20?= =?UTF-8?q?declaration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/dapp/src/global.d.ts | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 apps/dapp/src/global.d.ts diff --git a/apps/dapp/src/global.d.ts b/apps/dapp/src/global.d.ts new file mode 100644 index 00000000..444f7937 --- /dev/null +++ b/apps/dapp/src/global.d.ts @@ -0,0 +1,4 @@ +declare module '*.css' { + const content: { [className: string]: string }; + export default content; +} From b845b84f7d07e4193f8d7673c6c1adb24f87c493 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Fri, 28 Nov 2025 13:20:01 -0300 Subject: [PATCH 6/7] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Split=20CreateVault=20?= =?UTF-8?q?into=20separate=20components?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - VaultConfigSection: Wrapper component - SelectAssets: Asset selection - SelectStrategies: Strategy selection - StrategyAllocationSliders: Percentage distribution sliders - AddStrategies: Strategies section - ManagerConfig: Manager addresses config - FeeConfig: Fee configuration - CreateVaultButton: Submit button with vault creation logic - Add index.ts for exports --- .../components/CreateVault/AddStrategies.tsx | 58 ++ .../components/CreateVault/CreateVault.tsx | 727 +----------------- .../CreateVault/CreateVaultButton.tsx | 243 ++++++ .../src/components/CreateVault/FeeConfig.tsx | 73 ++ .../components/CreateVault/ManagerConfig.tsx | 59 ++ .../components/CreateVault/SelectAssets.tsx | 46 ++ .../CreateVault/SelectStrategies.tsx | 66 ++ .../CreateVault/StrategyAllocationSliders.tsx | 120 +++ .../CreateVault/VaultConfigSection.tsx | 23 + apps/dapp/src/components/CreateVault/index.ts | 10 + 10 files changed, 705 insertions(+), 720 deletions(-) create mode 100644 apps/dapp/src/components/CreateVault/AddStrategies.tsx create mode 100644 apps/dapp/src/components/CreateVault/CreateVaultButton.tsx create mode 100644 apps/dapp/src/components/CreateVault/FeeConfig.tsx create mode 100644 apps/dapp/src/components/CreateVault/ManagerConfig.tsx create mode 100644 apps/dapp/src/components/CreateVault/SelectAssets.tsx create mode 100644 apps/dapp/src/components/CreateVault/SelectStrategies.tsx create mode 100644 apps/dapp/src/components/CreateVault/StrategyAllocationSliders.tsx create mode 100644 apps/dapp/src/components/CreateVault/VaultConfigSection.tsx create mode 100644 apps/dapp/src/components/CreateVault/index.ts diff --git a/apps/dapp/src/components/CreateVault/AddStrategies.tsx b/apps/dapp/src/components/CreateVault/AddStrategies.tsx new file mode 100644 index 00000000..576a2fab --- /dev/null +++ b/apps/dapp/src/components/CreateVault/AddStrategies.tsx @@ -0,0 +1,58 @@ +import { AssetContext, VaultContext } from '@/contexts' +import { decimalRegex, parseNumericInput } from '@/helpers/input' +import { HStack, Stack } from '@chakra-ui/react' +import React, { useContext } from 'react' +import BackgroundCard from '../ui/BackgroundCard' +import { baseMargin } from '../ui/Common' +import { FormField } from '../ui/CustomInputFields' +import { SelectStrategies } from './SelectStrategies' +import { StrategyAllocationSliders } from './StrategyAllocationSliders' + +export function AddStrategies() { + const assetContext = useContext(AssetContext); + const vaultContext = useContext(VaultContext); + + const handleDepositAmount = (e: React.ChangeEvent, i: number) => { + if (!decimalRegex.test(e.target.value) && e.target.value != '') return + const assetAllocation = vaultContext?.newVault.assetAllocation.map((item) => { + let newItem = item + const amount = parseNumericInput(e.target.value, 7); + if (item.address === vaultContext?.newVault.assetAllocation[i].address) { + newItem = { + ...item, + amount: Number(amount), + } + } + return newItem + }); + vaultContext?.setNewVault({ + ...vaultContext.newVault, + assetAllocation: assetAllocation!, + }) + } + + return ( + + + {vaultContext?.newVault.assetAllocation.map((item, index) => ( + + handleDepositAmount(e, index)} + /> + a.address === item.address)!} /> + + + ))} + + + ) +} diff --git a/apps/dapp/src/components/CreateVault/CreateVault.tsx b/apps/dapp/src/components/CreateVault/CreateVault.tsx index a1ad5ef1..c6ce4528 100644 --- a/apps/dapp/src/components/CreateVault/CreateVault.tsx +++ b/apps/dapp/src/components/CreateVault/CreateVault.tsx @@ -1,724 +1,11 @@ -import { Asset, AssetContext, Strategy, Vault, VaultContext } from '@/contexts' -import { useUser } from '@/contexts/UserContext' -import { isValidAddress } from '@/helpers/address' -import { decimalRegex, parseNumericInput } from '@/helpers/input' -import { parsePascalCase } from '@/helpers/utils' -import { useDefindexSDK } from '@/hooks/useDefindexSDK' -import { Box, Button, Checkbox, createListCollection, Fieldset, Flex, HStack, IconButton, Slider, Stack, Text } from '@chakra-ui/react' -import React, { useContext, useEffect, useState } from 'react' -import { HiExternalLink } from 'react-icons/hi' -import BackgroundCard from '../ui/BackgroundCard' -import { baseMargin } from '../ui/Common' -import { CustomSelect, FormField } from '../ui/CustomInputFields' -import { toaster } from '../ui/toaster' - -interface VaultConfigSectionProps { - title: string; - children: React.ReactNode; -} - -function VaultConfigSection({ title, children }: VaultConfigSectionProps) { - return ( - - - - - {children} - - - - - ); -} - -function SelectAssets() { - const assetContext = useContext(AssetContext); - const vaultContext = useContext(VaultContext); - const [selectedAssets, setSelectedAssets] = React.useState([]) - const handleSelect = (e: string[]) => { - const selected = assetContext?.assets.filter((asset) => e.includes(asset.address)) - setSelectedAssets(selected || []) - } - useEffect(() => { - const newAssets: Asset[] = selectedAssets.map((asset) => ({ - address: asset.address, - strategies: [], - symbol: asset.symbol, - amount: 0, - })); - vaultContext?.setNewVault({ - ...vaultContext.newVault, - assetAllocation: newAssets, - }) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedAssets]) - const assetsCollection = createListCollection({ - items: assetContext?.assets.map((asset) => ({ - label: asset.symbol?.toUpperCase() || '', - value: asset.address, - })) || [] - }) - return ( - asset.address)} - onSelect={handleSelect} - /> - ) -} - -function SelectStrategies({ asset }: { asset: Asset }) { - const vaultContext = useContext(VaultContext); - const [selectedStrategies, setSelectedStrategies] = React.useState([]) - const strategiesCollection = createListCollection({ - items: asset.strategies.map((strategy) => - ({ - label: parsePascalCase(strategy.name), - value: strategy.address, - })) - }) - - const handleSelect = (e: string[]) => { - setSelectedStrategies(e) - } - - useEffect(() => { - const newStrategies: Strategy[] = asset.strategies - .filter((strategy) => selectedStrategies.includes(strategy.address)) - .map((strategy, _, arr) => ({ - ...strategy, - // Initialize with equal distribution - amount: arr.length > 0 ? Math.floor(100 / arr.length) : 0, - })); - - // Adjust last strategy to ensure sum is exactly 100 - if (newStrategies.length > 0) { - const sum = newStrategies.reduce((acc, s) => acc + (s.amount || 0), 0); - newStrategies[newStrategies.length - 1].amount = (newStrategies[newStrategies.length - 1].amount || 0) + (100 - sum); - } - - const assetAllocation = vaultContext?.newVault.assetAllocation.map((item) => { - if (item.address === asset.address) { - return { - ...item, - strategies: newStrategies, - } - } - return item - }); - vaultContext?.setNewVault({ - ...vaultContext.newVault, - assetAllocation: assetAllocation!, - }) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedStrategies, asset.address, asset.strategies]) - return ( - - ) -} - -interface StrategyAllocationSlidersProps { - assetIndex: number; - assetAmount: number; - assetSymbol: string; -} - -function StrategyAllocationSliders({ assetIndex, assetAmount, assetSymbol }: StrategyAllocationSlidersProps) { - const vaultContext = useContext(VaultContext); - const strategies = vaultContext?.newVault.assetAllocation[assetIndex]?.strategies || []; - - if (strategies.length === 0 || assetAmount <= 0) { - return null; - } - - const handlePercentageChange = (strategyIndex: number, newPercentage: number) => { - if (!vaultContext) return; - - const currentStrategies = [...strategies]; - const oldPercentage = currentStrategies[strategyIndex].amount || 0; - const diff = newPercentage - oldPercentage; - - // Update the changed strategy - currentStrategies[strategyIndex] = { - ...currentStrategies[strategyIndex], - amount: newPercentage, - }; - - // Distribute the difference among other strategies proportionally - const otherStrategies = currentStrategies.filter((_, i) => i !== strategyIndex); - const otherTotal = otherStrategies.reduce((sum, s) => sum + (s.amount || 0), 0); - - if (otherTotal > 0 && diff !== 0) { - const remaining = -diff; - otherStrategies.forEach((_, i) => { - const actualIndex = i >= strategyIndex ? i + 1 : i; - const currentAmount = currentStrategies[actualIndex].amount || 0; - const proportion = currentAmount / otherTotal; - const adjustment = Math.round(remaining * proportion); - const newAmount = Math.max(0, Math.min(100, currentAmount + adjustment)); - currentStrategies[actualIndex] = { - ...currentStrategies[actualIndex], - amount: newAmount, - }; - }); - - // Ensure total is exactly 100 - const total = currentStrategies.reduce((sum, s) => sum + (s.amount || 0), 0); - if (total !== 100 && currentStrategies.length > 1) { - const lastOtherIndex = strategyIndex === currentStrategies.length - 1 ? 0 : currentStrategies.length - 1; - currentStrategies[lastOtherIndex] = { - ...currentStrategies[lastOtherIndex], - amount: (currentStrategies[lastOtherIndex].amount || 0) + (100 - total), - }; - } - } - - const newAssetAllocation = vaultContext.newVault.assetAllocation.map((item, i) => { - if (i === assetIndex) { - return { ...item, strategies: currentStrategies }; - } - return item; - }); - - vaultContext.setNewVault({ - ...vaultContext.newVault, - assetAllocation: newAssetAllocation, - }); - }; - - const formatAmount = (percentage: number) => { - const amount = (assetAmount * percentage) / 100; - return amount.toFixed(2); - }; - - return ( - - - Strategy Allocation - - - {strategies.map((strategy, idx) => ( - - - - {parsePascalCase(strategy.name)} - - - {strategy.amount || 0}% = {formatAmount(strategy.amount || 0)} {assetSymbol} - - - handlePercentageChange(idx, details.value[0])} - > - - - - - - - - - ))} - - sum + (s.amount || 0), 0) === 100 ? 'green.400' : 'red.400'}> - Total: {strategies.reduce((sum, s) => sum + (s.amount || 0), 0)}% - - - - - ); -} - -function AddStrategies() { - const assetContext = useContext(AssetContext); - const vaultContext = useContext(VaultContext); - - const handleDepositAmount = (e: React.ChangeEvent, i: number) => { - if (!decimalRegex.test(e.target.value) && e.target.value != '') return - const assetAllocation = vaultContext?.newVault.assetAllocation.map((item) => { - let newItem = item - const amount = parseNumericInput(e.target.value, 7); - if (item.address === vaultContext?.newVault.assetAllocation[i].address) { - newItem = { - ...item, - amount: Number(amount), - } - } - return newItem - }); - vaultContext?.setNewVault({ - ...vaultContext.newVault, - assetAllocation: assetAllocation!, - }) - } - - - return ( - - - {vaultContext?.newVault.assetAllocation.map((item, index) => ( - - handleDepositAmount(e, index)} - /> - a.address === item.address)!} /> - - - ))} - - - ) -} - -function VaultConfig() { - const vaultContext = useContext(VaultContext); - const [upgradable, setUpgradable] = React.useState(true) - const [vaultConfig, setVaultConfig] = React.useState>({ - name: '', - symbol: '', - }) - - useEffect(() => { - vaultContext?.setNewVault({ - ...vaultContext.newVault, - name: vaultConfig.name || '', - symbol: vaultConfig.symbol || '', - }) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [vaultConfig]) - - const handleVaultNameChange = (e: React.ChangeEvent) => { - const value = e.target.value; - if (value.length <= 20) { - setVaultConfig({ ...vaultConfig, name: value }); - } - } - - return ( - - = 20 ? 'Vault name must be 20 characters or less' : ''} - /> - { - setVaultConfig({ ...vaultConfig, symbol: e.target.value }) - }} - /> - - - - - {upgradable ? 'Upgradable' : 'Non-Upgradable'} - - { - setUpgradable(!!e.checked) - vaultContext?.setNewVault({ - ...vaultContext.newVault, - upgradable: !!e.checked, - }) - }} - size="lg" - > - - - - - - - - - - ); -} - -function ManagerConfig() { - const vaultContext = useContext(VaultContext); - const [managerConfig, setManagerConfig] = React.useState>({ - vaultManager: '', - emergencyManager: '', - rebalanceManager: '', - }) - useEffect(() => { - vaultContext?.setNewVault({ - ...vaultContext.newVault, - vaultManager: managerConfig.vaultManager || '', - emergencyManager: managerConfig.emergencyManager || '', - rebalanceManager: managerConfig.rebalanceManager || '', - }) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [managerConfig]) - - return ( - - { - setManagerConfig({ ...managerConfig, vaultManager: e.target.value }) - }} - invalid={!isValidAddress(managerConfig.vaultManager!)} - errorMessage={!managerConfig.vaultManager || !isValidAddress(managerConfig.vaultManager) ? 'Invalid address' : ''} - /> - { - setManagerConfig({ ...managerConfig, emergencyManager: e.target.value }) - }} - invalid={!isValidAddress(managerConfig.emergencyManager!)} - errorMessage={!managerConfig.emergencyManager || !isValidAddress(managerConfig.emergencyManager) ? 'Invalid address' : ''} - /> - { - setManagerConfig({ ...managerConfig, rebalanceManager: e.target.value }) - }} - invalid={!isValidAddress(managerConfig.rebalanceManager!)} - errorMessage={!managerConfig.rebalanceManager || !isValidAddress(managerConfig.rebalanceManager) ? 'Invalid address' : ''} - /> - - ); -} - -function FeeConfig() { - const vaultContext = useContext(VaultContext); - const [showWarning, setShowWarning] = useState(false) - - useEffect(() => { - const handleWarning = () => { - if (vaultContext && vaultContext.newVault.feePercent > 50) { - console.log('show warning') - setShowWarning(true) - } - else { - setShowWarning(false) - } - } - handleWarning() - }, [vaultContext]) - const handleInput = (e: React.ChangeEvent) => { - if (!decimalRegex.test(e.target.value) && e.target.value != '') return - if (e.target.value == '') { - vaultContext?.setNewVault({ - ...vaultContext.newVault, - feePercent: 0, - }) - return - } - if (parseFloat(e.target.value) >= 100) { - e.target.value = '100' - } - vaultContext?.setNewVault({ - ...vaultContext.newVault, - feePercent: Number(parseNumericInput(e.target.value, 2) || vaultContext.newVault.feePercent), - }) - } - - const handleFeeReceiver = (e: React.ChangeEvent) => { - vaultContext?.setNewVault({ - ...vaultContext.newVault, - feeReceiver: e.target.value, - }) - } - - return ( - - - - - ); -} - -function CreateVaultButton() { - const vaultContext = useContext(VaultContext); - const { address, activeNetwork } = useUser(); - const { createVault, createVaultAutoInvest } = useDefindexSDK(); - const [loading, setLoading] = useState(false); - const [disabled, setDisabled] = useState(false); - - useEffect(() => { - if ( - !address - || !vaultContext - || !vaultContext.newVault.name - || !vaultContext.newVault.symbol - || !vaultContext.newVault.vaultManager - || !vaultContext.newVault.emergencyManager - || !vaultContext.newVault.rebalanceManager - || !vaultContext.newVault.feeReceiver - || !vaultContext.newVault.feePercent - || vaultContext.newVault.assetAllocation.length === 0 - ) { - setDisabled(true); - } else { - setDisabled(false); - } - }, [ - address, - vaultContext?.newVault.name, - vaultContext?.newVault.symbol, - vaultContext?.newVault.vaultManager, - vaultContext?.newVault.emergencyManager, - vaultContext?.newVault.rebalanceManager, - vaultContext?.newVault.feeReceiver, - vaultContext?.newVault.feePercent, - vaultContext?.newVault.assetAllocation.length, - vaultContext - ]); - - const handleCreateVault = async () => { - if (!vaultContext || !address) return; - setLoading(true); - - const newVault = vaultContext.newVault; - if (!newVault) return; - - // Convert fee percentage to basis points (1% = 100 bps) - const vaultFeeBps = Math.round(newVault.feePercent * 100); - - // Check if there's a deposit with strategy allocation - const hasDeposit = newVault.assetAllocation.some((asset) => asset.amount > 0); - const hasStrategyAllocation = newVault.assetAllocation.some((asset) => - asset.strategies.some((strategy) => (strategy.amount || 0) > 0) - ); - - try { - let result; - - if (hasDeposit && hasStrategyAllocation) { - // Use createVaultAutoInvest - creates vault, deposits, and invests in strategies - const autoInvestConfig = { - caller: address, - roles: { - emergencyManager: newVault.emergencyManager, - rebalanceManager: newVault.rebalanceManager, - feeReceiver: newVault.feeReceiver, - manager: newVault.vaultManager, - }, - name: newVault.name, - symbol: newVault.symbol, - vaultFee: vaultFeeBps, - upgradable: newVault.upgradable, - assets: newVault.assetAllocation.map((asset) => ({ - address: asset.address, - symbol: asset.symbol || '', - amount: Math.round(asset.amount * 10_000_000), // Convert to stroops - strategies: asset.strategies.map((strategy) => ({ - address: strategy.address, - name: strategy.name, - // Calculate amount based on percentage - amount: Math.round((asset.amount * (strategy.amount || 0) / 100) * 10_000_000), - })), - })), - }; - - result = await createVaultAutoInvest(autoInvestConfig); - - const txHash = result.txHash || result.hash; - const vaultAddress = result.predictedVaultAddress; - const network = activeNetwork === 'mainnet' ? 'public' : 'testnet'; - - toaster.create({ - title: 'Vault created with auto-invest', - description: vaultAddress ? ( - - { - navigator.clipboard.writeText(vaultAddress); - toaster.create({ - title: 'Address copied', - type: 'success', - duration: 2000, - }); - }} - > - {vaultAddress.slice(0, 8)}...{vaultAddress.slice(-8)} - - { - window.open(`https://stellar.expert/explorer/${network}/contract/${vaultAddress}`, '_blank'); - }} - > - - - - ) : 'Vault created and funds invested successfully', - type: 'success', - duration: 10000, - action: { - label: 'View transaction', - onClick: () => { - window.open(`https://stellar.expert/explorer/${network}/search?term=${txHash}`, '_blank'); - } - } - }); - } else { - // Use createVault - creates vault without deposit - const vaultConfig = { - roles: { - 0: newVault.emergencyManager, - 1: newVault.feeReceiver, - 2: newVault.vaultManager, - 3: newVault.rebalanceManager, - }, - vault_fee_bps: vaultFeeBps, - assets: newVault.assetAllocation.map((asset) => ({ - address: asset.address, - strategies: asset.strategies.map((strategy) => ({ - address: strategy.address, - name: strategy.name, - paused: strategy.paused, - })), - })), - name_symbol: { - name: newVault.name, - symbol: newVault.symbol, - }, - upgradable: newVault.upgradable, - caller: address, - }; - - result = await createVault(vaultConfig); - - const txHash = result.txHash || result.hash; - const vaultAddr = result.predictedVaultAddress; - const net = activeNetwork === 'mainnet' ? 'public' : 'testnet'; - - toaster.create({ - title: 'Vault created', - description: vaultAddr ? ( - - { - navigator.clipboard.writeText(vaultAddr); - toaster.create({ - title: 'Address copied', - type: 'success', - duration: 2000, - }); - }} - > - {vaultAddr.slice(0, 8)}...{vaultAddr.slice(-8)} - - { - window.open(`https://stellar.expert/explorer/${net}/contract/${vaultAddr}`, '_blank'); - }} - > - - - - ) : 'Vault created successfully', - type: 'success', - duration: 10000, - action: { - label: 'View transaction', - onClick: () => { - window.open(`https://stellar.expert/explorer/${net}/search?term=${txHash}`, '_blank'); - } - } - }); - } - } catch (error: unknown) { - console.error('Error creating vault:', error); - toaster.create({ - title: 'Error creating vault', - description: error instanceof Error ? error.message : 'An error occurred', - type: 'error', - duration: 5000, - }); - } finally { - setLoading(false); - } - } - - return ( - - - - ) -} - +import { Stack } from '@chakra-ui/react' +import { AddStrategies } from './AddStrategies' +import './CreateVault.css' +import { CreateVaultButton } from './CreateVaultButton' +import { FeeConfig } from './FeeConfig' +import { ManagerConfig } from './ManagerConfig' +import { VaultConfig } from './VaultConfig' function CreateVault() { - - return ( diff --git a/apps/dapp/src/components/CreateVault/CreateVaultButton.tsx b/apps/dapp/src/components/CreateVault/CreateVaultButton.tsx new file mode 100644 index 00000000..0722ab3e --- /dev/null +++ b/apps/dapp/src/components/CreateVault/CreateVaultButton.tsx @@ -0,0 +1,243 @@ +import { VaultContext } from '@/contexts' +import { useUser } from '@/contexts/UserContext' +import { useDefindexSDK } from '@/hooks/useDefindexSDK' +import { Button, Flex, IconButton, Text } from '@chakra-ui/react' +import { useContext, useEffect, useState } from 'react' +import { HiExternalLink } from 'react-icons/hi' +import { baseMargin } from '../ui/Common' +import { toaster } from '../ui/toaster' + +export function CreateVaultButton() { + const vaultContext = useContext(VaultContext); + const { address, activeNetwork } = useUser(); + const { createVault, createVaultAutoInvest } = useDefindexSDK(); + const [loading, setLoading] = useState(false); + const [disabled, setDisabled] = useState(false); + + useEffect(() => { + if ( + !address + || !vaultContext + || !vaultContext.newVault.name + || !vaultContext.newVault.symbol + || !vaultContext.newVault.vaultManager + || !vaultContext.newVault.emergencyManager + || !vaultContext.newVault.rebalanceManager + || !vaultContext.newVault.feeReceiver + || !vaultContext.newVault.feePercent + || vaultContext.newVault.assetAllocation.length === 0 + ) { + setDisabled(true); + } else { + setDisabled(false); + } + }, [ + address, + vaultContext?.newVault.name, + vaultContext?.newVault.symbol, + vaultContext?.newVault.vaultManager, + vaultContext?.newVault.emergencyManager, + vaultContext?.newVault.rebalanceManager, + vaultContext?.newVault.feeReceiver, + vaultContext?.newVault.feePercent, + vaultContext?.newVault.assetAllocation.length, + vaultContext + ]); + + const handleCreateVault = async () => { + if (!vaultContext || !address) return; + setLoading(true); + + const newVault = vaultContext.newVault; + if (!newVault) return; + + // Convert fee percentage to basis points (1% = 100 bps) + const vaultFeeBps = Math.round(newVault.feePercent * 100); + + // Check if there's a deposit with strategy allocation + const hasDeposit = newVault.assetAllocation.some((asset) => asset.amount > 0); + const hasStrategyAllocation = newVault.assetAllocation.some((asset) => + asset.strategies.some((strategy) => (strategy.amount || 0) > 0) + ); + + try { + let result; + + if (hasDeposit && hasStrategyAllocation) { + // Use createVaultAutoInvest - creates vault, deposits, and invests in strategies + const autoInvestConfig = { + caller: address, + roles: { + emergencyManager: newVault.emergencyManager, + rebalanceManager: newVault.rebalanceManager, + feeReceiver: newVault.feeReceiver, + manager: newVault.vaultManager, + }, + name: newVault.name, + symbol: newVault.symbol, + vaultFee: vaultFeeBps, + upgradable: newVault.upgradable, + assets: newVault.assetAllocation.map((asset) => ({ + address: asset.address, + symbol: asset.symbol || '', + amount: Math.round(asset.amount * 10_000_000), // Convert to stroops + strategies: asset.strategies.map((strategy) => ({ + address: strategy.address, + name: strategy.name, + // Calculate amount based on percentage + amount: Math.round((asset.amount * (strategy.amount || 0) / 100) * 10_000_000), + })), + })), + }; + + result = await createVaultAutoInvest(autoInvestConfig); + + const txHash = result.txHash || result.hash; + const vaultAddress = result.predictedVaultAddress; + const network = activeNetwork === 'mainnet' ? 'public' : 'testnet'; + + toaster.create({ + title: 'Vault created with auto-invest', + description: vaultAddress ? ( + + { + navigator.clipboard.writeText(vaultAddress); + toaster.create({ + title: 'Address copied', + type: 'success', + duration: 2000, + }); + }} + > + {vaultAddress.slice(0, 8)}...{vaultAddress.slice(-8)} + + { + window.open(`https://stellar.expert/explorer/${network}/contract/${vaultAddress}`, '_blank'); + }} + > + + + + ) : 'Vault created and funds invested successfully', + type: 'success', + duration: 10000, + action: { + label: 'View transaction', + onClick: () => { + window.open(`https://stellar.expert/explorer/${network}/search?term=${txHash}`, '_blank'); + } + } + }); + } else { + // Use createVault - creates vault without deposit + const vaultConfig = { + roles: { + 0: newVault.emergencyManager, + 1: newVault.feeReceiver, + 2: newVault.vaultManager, + 3: newVault.rebalanceManager, + }, + vault_fee_bps: vaultFeeBps, + assets: newVault.assetAllocation.map((asset) => ({ + address: asset.address, + strategies: asset.strategies.map((strategy) => ({ + address: strategy.address, + name: strategy.name, + paused: strategy.paused, + })), + })), + name_symbol: { + name: newVault.name, + symbol: newVault.symbol, + }, + upgradable: newVault.upgradable, + caller: address, + }; + + result = await createVault(vaultConfig); + + const txHash = result.txHash || result.hash; + const vaultAddr = result.predictedVaultAddress; + const net = activeNetwork === 'mainnet' ? 'public' : 'testnet'; + + toaster.create({ + title: 'Vault created', + description: vaultAddr ? ( + + { + navigator.clipboard.writeText(vaultAddr); + toaster.create({ + title: 'Address copied', + type: 'success', + duration: 2000, + }); + }} + > + {vaultAddr.slice(0, 8)}...{vaultAddr.slice(-8)} + + { + window.open(`https://stellar.expert/explorer/${net}/contract/${vaultAddr}`, '_blank'); + }} + > + + + + ) : 'Vault created successfully', + type: 'success', + duration: 10000, + action: { + label: 'View transaction', + onClick: () => { + window.open(`https://stellar.expert/explorer/${net}/search?term=${txHash}`, '_blank'); + } + } + }); + } + } catch (error: unknown) { + console.error('Error creating vault:', error); + toaster.create({ + title: 'Error creating vault', + description: error instanceof Error ? error.message : 'An error occurred', + type: 'error', + duration: 5000, + }); + } finally { + setLoading(false); + } + } + + return ( + + + + ) +} diff --git a/apps/dapp/src/components/CreateVault/FeeConfig.tsx b/apps/dapp/src/components/CreateVault/FeeConfig.tsx new file mode 100644 index 00000000..6d467881 --- /dev/null +++ b/apps/dapp/src/components/CreateVault/FeeConfig.tsx @@ -0,0 +1,73 @@ +import { VaultContext } from '@/contexts' +import { isValidAddress } from '@/helpers/address' +import { decimalRegex, parseNumericInput } from '@/helpers/input' +import { useContext, useEffect, useState } from 'react' +import { FormField } from '../ui/CustomInputFields' +import { VaultConfigSection } from './VaultConfigSection' + +export function FeeConfig() { + const vaultContext = useContext(VaultContext); + const [showWarning, setShowWarning] = useState(false) + + useEffect(() => { + const handleWarning = () => { + if (vaultContext && vaultContext.newVault.feePercent > 50) { + console.log('show warning') + setShowWarning(true) + } + else { + setShowWarning(false) + } + } + handleWarning() + }, [vaultContext]) + + const handleInput = (e: React.ChangeEvent) => { + if (!decimalRegex.test(e.target.value) && e.target.value != '') return + if (e.target.value == '') { + vaultContext?.setNewVault({ + ...vaultContext.newVault, + feePercent: 0, + }) + return + } + if (parseFloat(e.target.value) >= 100) { + e.target.value = '100' + } + vaultContext?.setNewVault({ + ...vaultContext.newVault, + feePercent: Number(parseNumericInput(e.target.value, 2) || vaultContext.newVault.feePercent), + }) + } + + const handleFeeReceiver = (e: React.ChangeEvent) => { + vaultContext?.setNewVault({ + ...vaultContext.newVault, + feeReceiver: e.target.value, + }) + } + + return ( + + + + + ); +} diff --git a/apps/dapp/src/components/CreateVault/ManagerConfig.tsx b/apps/dapp/src/components/CreateVault/ManagerConfig.tsx new file mode 100644 index 00000000..286c25d3 --- /dev/null +++ b/apps/dapp/src/components/CreateVault/ManagerConfig.tsx @@ -0,0 +1,59 @@ +import { Vault, VaultContext } from '@/contexts' +import { isValidAddress } from '@/helpers/address' +import React, { useContext, useEffect } from 'react' +import { FormField } from '../ui/CustomInputFields' +import { VaultConfigSection } from './VaultConfigSection' + +export function ManagerConfig() { + const vaultContext = useContext(VaultContext); + const [managerConfig, setManagerConfig] = React.useState>({ + vaultManager: '', + emergencyManager: '', + rebalanceManager: '', + }) + + useEffect(() => { + vaultContext?.setNewVault({ + ...vaultContext.newVault, + vaultManager: managerConfig.vaultManager || '', + emergencyManager: managerConfig.emergencyManager || '', + rebalanceManager: managerConfig.rebalanceManager || '', + }) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [managerConfig]) + + return ( + + { + setManagerConfig({ ...managerConfig, vaultManager: e.target.value }) + }} + invalid={!isValidAddress(managerConfig.vaultManager!)} + errorMessage={!managerConfig.vaultManager || !isValidAddress(managerConfig.vaultManager) ? 'Invalid address' : ''} + /> + { + setManagerConfig({ ...managerConfig, emergencyManager: e.target.value }) + }} + invalid={!isValidAddress(managerConfig.emergencyManager!)} + errorMessage={!managerConfig.emergencyManager || !isValidAddress(managerConfig.emergencyManager) ? 'Invalid address' : ''} + /> + { + setManagerConfig({ ...managerConfig, rebalanceManager: e.target.value }) + }} + invalid={!isValidAddress(managerConfig.rebalanceManager!)} + errorMessage={!managerConfig.rebalanceManager || !isValidAddress(managerConfig.rebalanceManager) ? 'Invalid address' : ''} + /> + + ); +} diff --git a/apps/dapp/src/components/CreateVault/SelectAssets.tsx b/apps/dapp/src/components/CreateVault/SelectAssets.tsx new file mode 100644 index 00000000..0e020445 --- /dev/null +++ b/apps/dapp/src/components/CreateVault/SelectAssets.tsx @@ -0,0 +1,46 @@ +import { Asset, AssetContext, VaultContext } from '@/contexts' +import { createListCollection } from '@chakra-ui/react' +import React, { useContext, useEffect } from 'react' +import { CustomSelect } from '../ui/CustomInputFields' + +export function SelectAssets() { + const assetContext = useContext(AssetContext); + const vaultContext = useContext(VaultContext); + const [selectedAssets, setSelectedAssets] = React.useState([]) + + const handleSelect = (e: string[]) => { + const selected = assetContext?.assets.filter((asset) => e.includes(asset.address)) + setSelectedAssets(selected || []) + } + + useEffect(() => { + const newAssets: Asset[] = selectedAssets.map((asset) => ({ + address: asset.address, + strategies: [], + symbol: asset.symbol, + amount: 0, + })); + vaultContext?.setNewVault({ + ...vaultContext.newVault, + assetAllocation: newAssets, + }) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedAssets]) + + const assetsCollection = createListCollection({ + items: assetContext?.assets.map((asset) => ({ + label: asset.symbol?.toUpperCase() || '', + value: asset.address, + })) || [] + }) + + return ( + asset.address)} + onSelect={handleSelect} + /> + ) +} diff --git a/apps/dapp/src/components/CreateVault/SelectStrategies.tsx b/apps/dapp/src/components/CreateVault/SelectStrategies.tsx new file mode 100644 index 00000000..e5a2a290 --- /dev/null +++ b/apps/dapp/src/components/CreateVault/SelectStrategies.tsx @@ -0,0 +1,66 @@ +import { Asset, Strategy, VaultContext } from '@/contexts' +import { parsePascalCase } from '@/helpers/utils' +import { createListCollection } from '@chakra-ui/react' +import React, { useContext, useEffect } from 'react' +import { CustomSelect } from '../ui/CustomInputFields' + +interface SelectStrategiesProps { + asset: Asset; +} + +export function SelectStrategies({ asset }: SelectStrategiesProps) { + const vaultContext = useContext(VaultContext); + const [selectedStrategies, setSelectedStrategies] = React.useState([]) + + const strategiesCollection = createListCollection({ + items: asset.strategies.map((strategy) => ({ + label: parsePascalCase(strategy.name), + value: strategy.address, + })) + }) + + const handleSelect = (e: string[]) => { + setSelectedStrategies(e) + } + + useEffect(() => { + const newStrategies: Strategy[] = asset.strategies + .filter((strategy) => selectedStrategies.includes(strategy.address)) + .map((strategy, _, arr) => ({ + ...strategy, + // Initialize with equal distribution + amount: arr.length > 0 ? Math.floor(100 / arr.length) : 0, + })); + + // Adjust last strategy to ensure sum is exactly 100 + if (newStrategies.length > 0) { + const sum = newStrategies.reduce((acc, s) => acc + (s.amount || 0), 0); + newStrategies[newStrategies.length - 1].amount = (newStrategies[newStrategies.length - 1].amount || 0) + (100 - sum); + } + + const assetAllocation = vaultContext?.newVault.assetAllocation.map((item) => { + if (item.address === asset.address) { + return { + ...item, + strategies: newStrategies, + } + } + return item + }); + vaultContext?.setNewVault({ + ...vaultContext.newVault, + assetAllocation: assetAllocation!, + }) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedStrategies, asset.address, asset.strategies]) + + return ( + + ) +} diff --git a/apps/dapp/src/components/CreateVault/StrategyAllocationSliders.tsx b/apps/dapp/src/components/CreateVault/StrategyAllocationSliders.tsx new file mode 100644 index 00000000..d3f24b87 --- /dev/null +++ b/apps/dapp/src/components/CreateVault/StrategyAllocationSliders.tsx @@ -0,0 +1,120 @@ +import { VaultContext } from '@/contexts' +import { parsePascalCase } from '@/helpers/utils' +import { Box, Flex, Slider, Stack, Text } from '@chakra-ui/react' +import { useContext } from 'react' + +interface StrategyAllocationSlidersProps { + assetIndex: number; + assetAmount: number; + assetSymbol: string; +} + +export function StrategyAllocationSliders({ assetIndex, assetAmount, assetSymbol }: StrategyAllocationSlidersProps) { + const vaultContext = useContext(VaultContext); + const strategies = vaultContext?.newVault.assetAllocation[assetIndex]?.strategies || []; + + if (strategies.length === 0 || assetAmount <= 0) { + return null; + } + + const handlePercentageChange = (strategyIndex: number, newPercentage: number) => { + if (!vaultContext) return; + + const currentStrategies = [...strategies]; + const oldPercentage = currentStrategies[strategyIndex].amount || 0; + const diff = newPercentage - oldPercentage; + + // Update the changed strategy + currentStrategies[strategyIndex] = { + ...currentStrategies[strategyIndex], + amount: newPercentage, + }; + + // Distribute the difference among other strategies proportionally + const otherStrategies = currentStrategies.filter((_, i) => i !== strategyIndex); + const otherTotal = otherStrategies.reduce((sum, s) => sum + (s.amount || 0), 0); + + if (otherTotal > 0 && diff !== 0) { + const remaining = -diff; + otherStrategies.forEach((_, i) => { + const actualIndex = i >= strategyIndex ? i + 1 : i; + const currentAmount = currentStrategies[actualIndex].amount || 0; + const proportion = currentAmount / otherTotal; + const adjustment = Math.round(remaining * proportion); + const newAmount = Math.max(0, Math.min(100, currentAmount + adjustment)); + currentStrategies[actualIndex] = { + ...currentStrategies[actualIndex], + amount: newAmount, + }; + }); + + // Ensure total is exactly 100 + const total = currentStrategies.reduce((sum, s) => sum + (s.amount || 0), 0); + if (total !== 100 && currentStrategies.length > 1) { + const lastOtherIndex = strategyIndex === currentStrategies.length - 1 ? 0 : currentStrategies.length - 1; + currentStrategies[lastOtherIndex] = { + ...currentStrategies[lastOtherIndex], + amount: (currentStrategies[lastOtherIndex].amount || 0) + (100 - total), + }; + } + } + + const newAssetAllocation = vaultContext.newVault.assetAllocation.map((item, i) => { + if (i === assetIndex) { + return { ...item, strategies: currentStrategies }; + } + return item; + }); + + vaultContext.setNewVault({ + ...vaultContext.newVault, + assetAllocation: newAssetAllocation, + }); + }; + + const formatAmount = (percentage: number) => { + const amount = (assetAmount * percentage) / 100; + return amount.toFixed(2); + }; + + return ( + + + Strategy Allocation + + + {strategies.map((strategy, idx) => ( + + + + {parsePascalCase(strategy.name)} + + + {strategy.amount || 0}% = {formatAmount(strategy.amount || 0)} {assetSymbol} + + + handlePercentageChange(idx, details.value[0])} + > + + + + + + + + + ))} + + sum + (s.amount || 0), 0) === 100 ? 'green.400' : 'red.400'}> + Total: {strategies.reduce((sum, s) => sum + (s.amount || 0), 0)}% + + + + + ); +} diff --git a/apps/dapp/src/components/CreateVault/VaultConfigSection.tsx b/apps/dapp/src/components/CreateVault/VaultConfigSection.tsx new file mode 100644 index 00000000..c9bc96e5 --- /dev/null +++ b/apps/dapp/src/components/CreateVault/VaultConfigSection.tsx @@ -0,0 +1,23 @@ +import { Fieldset, HStack } from '@chakra-ui/react' +import React from 'react' +import BackgroundCard from '../ui/BackgroundCard' +import { baseMargin } from '../ui/Common' + +interface VaultConfigSectionProps { + title: string; + children: React.ReactNode; +} + +export function VaultConfigSection({ title, children }: VaultConfigSectionProps) { + return ( + + + + + {children} + + + + + ); +} diff --git a/apps/dapp/src/components/CreateVault/index.ts b/apps/dapp/src/components/CreateVault/index.ts new file mode 100644 index 00000000..ebd704f9 --- /dev/null +++ b/apps/dapp/src/components/CreateVault/index.ts @@ -0,0 +1,10 @@ +export { AddStrategies } from './AddStrategies' +export { CreateVaultButton } from './CreateVaultButton' +export { default as CreateVault } from './CreateVault' +export { FeeConfig } from './FeeConfig' +export { ManagerConfig } from './ManagerConfig' +export { SelectAssets } from './SelectAssets' +export { SelectStrategies } from './SelectStrategies' +export { StrategyAllocationSliders } from './StrategyAllocationSliders' +export { VaultConfig } from './VaultConfig' +export { VaultConfigSection } from './VaultConfigSection' From fc8aae7e15073115517521be96464bb55674a290 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Fri, 28 Nov 2025 13:20:07 -0300 Subject: [PATCH 7/7] =?UTF-8?q?=F0=9F=8E=A8=20Style=20upgradable=20checkbo?= =?UTF-8?q?x=20with=20custom=20colors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add border-radius 25% to checkbox - Semi-transparent background - Light green (#D3FFB4) stroke and text when checked - Fixed width container to prevent layout shift --- .../components/CreateVault/CreateVault.css | 19 +++++ .../components/CreateVault/VaultConfig.tsx | 76 +++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 apps/dapp/src/components/CreateVault/CreateVault.css create mode 100644 apps/dapp/src/components/CreateVault/VaultConfig.tsx diff --git a/apps/dapp/src/components/CreateVault/CreateVault.css b/apps/dapp/src/components/CreateVault/CreateVault.css new file mode 100644 index 00000000..00a7aacb --- /dev/null +++ b/apps/dapp/src/components/CreateVault/CreateVault.css @@ -0,0 +1,19 @@ +[data-scope="checkbox"][data-part="control"] { + border-radius: 25%; + background-color: rgba(255, 255, 255, 0.1) !important; + border-color: rgba(211, 255, 180, 0.5) !important; +} + +[data-scope="checkbox"][data-part="control"][data-state="checked"] { + background-color: rgba(211, 255, 180, 0.2) !important; + border-color: #D3FFB4 !important; +} + +[data-scope="checkbox"][data-part="indicator"], +[data-scope="checkbox"][data-part="indicator"] svg, +.checkbox, +.checkbox svg { + color: #D3FFB4 !important; + stroke: #D3FFB4 !important; + fill: none !important; +} \ No newline at end of file diff --git a/apps/dapp/src/components/CreateVault/VaultConfig.tsx b/apps/dapp/src/components/CreateVault/VaultConfig.tsx new file mode 100644 index 00000000..0a0136d9 --- /dev/null +++ b/apps/dapp/src/components/CreateVault/VaultConfig.tsx @@ -0,0 +1,76 @@ +import { Vault, VaultContext } from '@/contexts' +import { Checkbox, Flex, Text } from '@chakra-ui/react' +import React, { useContext, useEffect } from 'react' +import { FormField } from '../ui/CustomInputFields' +import { SelectAssets } from './SelectAssets' +import { VaultConfigSection } from './VaultConfigSection' + +export function VaultConfig() { + const vaultContext = useContext(VaultContext); + const [upgradable, setUpgradable] = React.useState(true) + const [vaultConfig, setVaultConfig] = React.useState>({ + name: '', + symbol: '', + }) + + useEffect(() => { + vaultContext?.setNewVault({ + ...vaultContext.newVault, + name: vaultConfig.name || '', + symbol: vaultConfig.symbol || '', + }) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [vaultConfig]) + + const handleVaultNameChange = (e: React.ChangeEvent) => { + const value = e.target.value; + if (value.length <= 20) { + setVaultConfig({ ...vaultConfig, name: value }); + } + } + + return ( + + = 20 ? 'Vault name must be 20 characters or less' : ''} + /> + { + setVaultConfig({ ...vaultConfig, symbol: e.target.value }) + }} + /> + + + + + {upgradable ? 'Upgradable' : 'Non-Upgradable'} + + { + setUpgradable(!!e.checked) + vaultContext?.setNewVault({ + ...vaultContext.newVault, + upgradable: !!e.checked, + }) + }} + size="lg" + > + + + + + + + + ); +}