diff --git a/README.md b/README.md index 4e73f5d1..48812fed 100644 --- a/README.md +++ b/README.md @@ -483,112 +483,121 @@ path: `component.meta[name]` Translation keys | -> | name | type | default value | description | -> | ----------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -> | `depositSlippageWarning` | string | Excludes entry fee. | | -> | `withdrawSlippageWarning` | string | Slippage only applies to single asset withdrawals and withdrawals from vaults with debt positions in Aave. | | -> | `minSlippageWarning` | string | Flexible min slippage value that is likely enough to process the transaction. | | -> | `highSlippageWarning` | string | We recommend using another asset to trade with lower slippage. | | -> | `recommendedMinSlippage` | string | Recommended Min Slippage | | -> | `projectedDailyEarningsTooltip` | string | Projected daily earnings are based on the current APY and may differ from actual earnings. | | -> | `dailyEarnings` | string | Daily Earnings | | -> | `projectedYearlyEarningsTooltip` | string | Projected yearly earnings are based on the current APY and may differ from actual earnings. | | -> | `yearlyEarnings` | string | Yearly Earnings | | -> | `fullReceiveDetails` | string | See full details influencing what you will receive. | | -> | `tradeDetails` | string | Trade details | | -> | `maxSlippage` | string | Max slippage | | -> | `minReceiveAmount` | string | You will receive no less than this amount. | | -> | `minReceived` | string | Minimum Received | | -> | `estimatedMultiAssetFractions` | string | Estimated multi asset fractions | | -> | `infinite` | string | Infinite | | -> | `tokenAllowance` | string | Token Allowance | | -> | `entryFee` | string | Entry Fee | | -> | `entryFeeExplanation` | string | When you deposit, the token takes a small entry fee. This fee helps cover the costs when we rebalance the underlying funds, and it's shared among all token holders. | | -> | `minDepositUsd` | string | Minimum deposit in USD. | | -> | `minDeposit` | string | Minimum Deposit | | -> | `tokensLockTime` | string | Purchased tokens will have a {lockTime} lock. | | -> | `slippageTolerance` | string | Slippage tolerance | | -> | `bypassEntryFee` | string | Bypass Entry Fee | | -> | `tokenAmountToApprove` | string | Amount of tokens to be approved. | | -> | `auto` | string | Auto | | -> | `lengthenLockup` | string | Lengthen lockup to remove entry fee | | -> | `deposit` | string | Buy | | -> | `withdraw` | string | Sell | | -> | `max` | string | Max | | -> | `allAssets` | string | All Assets | | -> | `all` | string | All | | -> | `sell` | string | Sell | | -> | `receiveEstimated` | string | Receive (estimated) | | -> | `confirmInWallet` | string | Please confirm in wallet | | -> | `pending` | string | Pending... | | -> | `approve` | string | Approve | | -> | `connectWallet` | string | Connect Wallet | | -> | `minimumPurchase` | string | Minimum purchase is {value} | | -> | `poolIsInactive` | string | {poolSymbol} token is no longer active. Please withdraw from them. | | -> | `poolDepositsAreMaintenance` | string | {poolSymbol} token is under maintenance. Deposits are temporarily blocked. | | -> | `poolWithdrawalsAreMaintenance` | string | {poolSymbol} token is under maintenance. Withdrawals are temporarily blocked. | | -> | `poolIsPrivate` | string | This vault is currently private | | -> | `confirmMaxSlippage` | string | Confirm {slippagePercentage}% max slippage | | -> | `withdrawalLiquidityDisabled` | string | Intended withdraw value is greater than available liquidity ({value}) | | -> | `withdrawCooldown` | string | You can sell your {tokenSymbol} tokens in {cooldownEndTime} | | -> | `termsOfUse` | string | Terms Of Use | | -> | `termOfUseDepositListTitle` | string | Please know the following before depositing | | -> | `termOfUseDepositAssetSlippage` | string | When exiting, investors receive single asset or the underlying vault assets. Withdraw slippage can be customized in withdraw settings | | -> | `termOfUseDepositBugs` | string | There may be interface bugs on the platform | | -> | `termOfUseDepositDowntime` | string | There may be interface downtime (planned and unplanned) | | -> | `termOfUseDepositAuditRisk` | string | Smart contracts are audited but a risk is still present | | -> | `termOfUseDepositAccept` | string | Accept & Deposit | | -> | `back` | string | Back | | -> | `done` | string | Done | | -> | `highSlippage` | string | High Slippage Alert | | -> | `responsibleHighSlippage` | string | By proceeding with this trade, you acknowledge and accept the possibility of experiencing high slippage, resulting in a potential difference between the expected and executed price. | | -> | `highSlippageListTitle` | string | Please consider the following before confirming | | -> | `highSlippageQuoteDiff` | string | Be aware that the final amount of assets you receive may be different from the initially quoted value. | | -> | `highSlippageRisk` | string | Ensure that you understand the risks associated with high slippage and are comfortable proceeding with the trade. | | -> | `confirm` | string | Confirm | | -> | `selectToken` | string | Select Token | | -> | `sendingOrderToWallet` | string | Sending order to your wallet | | -> | `settingUpTx` | string | Setting up transaction | | -> | `miningTx` | string | Processing | | -> | `approveSpending` | string | Approve {symbol} spending | | -> | `pay` | string | Pay | | -> | `multiAssetFractions` | string | multi asset fractions | | -> | `swappableAssets` | string | swappable assets | | -> | `explorer` | string | Explorer | | -> | `as` | string | As | | -> | `switchNetwork` | string | Switch Network | | -> | `depositAction` | string | Buy | | -> | `withdrawAction` | string | Sell | | -> | `swapAction` | string | Swap | | -> | `unrollAction` | string | Unroll | | -> | `unrollAndClaimAction` | string | Claim | | -> | `claimAction` | string | Claim Without Swap | | -> | `claimLabel` | string | Claim Assets | | -> | `createLimitSellOrder` | string | Stop order set | | -> | `swapAndClaimTo` | string | Swap and claim assets to | | -> | `initWithdrawDescription` | string | Unroll | | -> | `initWithdrawTooltip` | string | Unroll prepares assets for single asset withdrawal | | -> | `completeWithdrawDescription` | string | Claim | | -> | `completeWithdrawTooltip` | string | This final step swaps all assets to a single asset and sends it to your wallet | | -> | `unrollAndClaimDescription` | string | Claim | | -> | `total` | string | Total: | | -> | `showAll` | string | Show All | | -> | `hide` | string | Hide | | -> | `refreshSwapQuoteTooltip` | string | Refresh swap quote | | -> | `proceedWithNextStep` | string | Please proceed with the next step. | | -> | `aggregatorsLabel` | string | Swap source | | -> | `aggregatorsTooltip` | string | Choose which aggregators to use for your trades. | | -> | `batchTransactionsLabel` | string | Batch transactions | | -> | `batchTransactionsSwitchLabel` | string | Batch | | -> | `batchTransactionsTooltip` | string | Group approve and trade calls into a single batch transaction. Disable if you prefer separate prompts. | | -> | `openLimitOrderAfterBuySwitchLabel` | string | Add Stop Order | | -> | `orderingLabel` | string | Ordering | | -> | `paidLabel` | string | Paid | | -> | `priceLabel` | string | Price | | -> | `transactionLabel` | string | Transaction | | -> | `stopOrder` | string | Stop Order | | -> | `send` | string | Send | | -> | `cooldown` | string | Cooldown | | +> | name | type | default value | description | +> | ----------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | +> | `depositSlippageWarning` | string | Excludes entry fee. | | +> | `withdrawSlippageWarning` | string | Slippage only applies to single asset withdrawals and withdrawals from vaults with debt positions in Aave. | | +> | `minSlippageWarning` | string | Flexible min slippage value that is likely enough to process the transaction. | | +> | `highSlippageWarning` | string | We recommend using another asset to trade with lower slippage. | | +> | `recommendedMinSlippage` | string | Recommended Min Slippage | | +> | `projectedDailyEarningsTooltip` | string | Projected daily earnings are based on the current APY and may differ from actual earnings. | | +> | `dailyEarnings` | string | Daily Earnings | | +> | `projectedYearlyEarningsTooltip` | string | Projected yearly earnings are based on the current APY and may differ from actual earnings. | | +> | `yearlyEarnings` | string | Yearly Earnings | | +> | `fullReceiveDetails` | string | See full details influencing what you will receive. | | +> | `tradeDetails` | string | Trade details | | +> | `maxSlippage` | string | Max slippage | | +> | `minReceiveAmount` | string | You will receive no less than this amount. | | +> | `minReceived` | string | Minimum Received | | +> | `estimatedMultiAssetFractions` | string | Estimated multi asset fractions | | +> | `infinite` | string | Infinite | | +> | `tokenAllowance` | string | Token Allowance | | +> | `entryFee` | string | Entry Fee | | +> | `entryFeeExplanation` | string | When you deposit, the token takes a small entry fee. This fee helps cover the costs when we rebalance the underlying funds, and it's shared among all token holders. | | +> | `minDepositUsd` | string | Minimum deposit in USD. | | +> | `minDeposit` | string | Minimum Deposit | | +> | `tokensLockTime` | string | Purchased tokens will have a {lockTime} lock. | | +> | `slippageTolerance` | string | Slippage tolerance | | +> | `bypassEntryFee` | string | Bypass Entry Fee | | +> | `tokenAmountToApprove` | string | Amount of tokens to be approved. | | +> | `auto` | string | Auto | | +> | `lengthenLockup` | string | Lengthen lockup to remove entry fee | | +> | `deposit` | string | Buy | | +> | `withdraw` | string | Sell | | +> | `max` | string | Max | | +> | `allAssets` | string | All Assets | | +> | `all` | string | All | | +> | `sell` | string | Sell | | +> | `receiveEstimated` | string | Receive (estimated) | | +> | `confirmInWallet` | string | Please confirm in wallet | | +> | `pending` | string | Pending... | | +> | `approve` | string | Approve | | +> | `connectWallet` | string | Connect Wallet | | +> | `minimumPurchase` | string | Minimum purchase is {value} | | +> | `poolIsInactive` | string | {poolSymbol} token is no longer active. Please withdraw from them. | | +> | `poolDepositsAreMaintenance` | string | {poolSymbol} token is under maintenance. Deposits are temporarily blocked. | | +> | `poolWithdrawalsAreMaintenance` | string | {poolSymbol} token is under maintenance. Withdrawals are temporarily blocked. | | +> | `poolIsPrivate` | string | This vault is currently private | | +> | `confirmMaxSlippage` | string | Confirm {slippagePercentage}% max slippage | | +> | `withdrawalLiquidityDisabled` | string | Intended withdraw value is greater than available liquidity ({value}) | | +> | `withdrawCooldown` | string | You can sell your {tokenSymbol} tokens in {cooldownEndTime} | | +> | `termsOfUse` | string | Terms Of Use | | +> | `termOfUseDepositListTitle` | string | Please know the following before depositing | | +> | `termOfUseDepositAssetSlippage` | string | When exiting, investors receive single asset or the underlying vault assets. Withdraw slippage can be customized in withdraw settings | | +> | `termOfUseDepositBugs` | string | There may be interface bugs on the platform | | +> | `termOfUseDepositDowntime` | string | There may be interface downtime (planned and unplanned) | | +> | `termOfUseDepositAuditRisk` | string | Smart contracts are audited but a risk is still present | | +> | `termOfUseDepositAccept` | string | Accept & Deposit | | +> | `back` | string | Back | | +> | `done` | string | Done | | +> | `termOfUseWithdrawAcceptLabel` | string | I understand and accept the withdraw terms | | +> | `termOfUseWithdrawPoint1` | string | Withdrawal requests are intended to settle in USDC. In rare cases where market conditions or high slippage prevent a USDC swap, the order may be settled in the underlying tokens of the respective vault. | | +> | `termOfUseWithdrawPoint2` | string | Revoking token approval after placing an order will prevent the order from being executed. | | +> | `highSlippage` | string | High Slippage Alert | | +> | `responsibleHighSlippage` | string | By proceeding with this trade, you acknowledge and accept the possibility of experiencing high slippage, resulting in a potential difference between the expected and executed price. | | +> | `highSlippageListTitle` | string | Please consider the following before confirming | | +> | `highSlippageQuoteDiff` | string | Be aware that the final amount of assets you receive may be different from the initially quoted value. | | +> | `highSlippageRisk` | string | Ensure that you understand the risks associated with high slippage and are comfortable proceeding with the trade. | | +> | `confirm` | string | Confirm | | +> | `selectToken` | string | Select Token | | +> | `sendingOrderToWallet` | string | Sending order to your wallet | | +> | `settingUpTx` | string | Setting up transaction | | +> | `miningTx` | string | Processing | | +> | `approveSpending` | string | Approve {symbol} spending | | +> | `pay` | string | Pay | | +> | `multiAssetFractions` | string | multi asset fractions | | +> | `swappableAssets` | string | swappable assets | | +> | `explorer` | string | Explorer | | +> | `as` | string | As | | +> | `switchNetwork` | string | Switch Network | | +> | `depositAction` | string | Buy | | +> | `withdrawAction` | string | Sell | | +> | `swapAction` | string | Swap | | +> | `unrollAction` | string | Unroll | | +> | `unrollAndClaimAction` | string | Claim | | +> | `claimAction` | string | Claim Without Swap | | +> | `claimLabel` | string | Claim Assets | | +> | `createLimitSellOrder` | string | Stop order set | | +> | `swapAndClaimTo` | string | Swap and claim assets to | | +> | `initWithdrawDescription` | string | Unroll | | +> | `initWithdrawTooltip` | string | Unroll prepares assets for single asset withdrawal | | +> | `completeWithdrawDescription` | string | Claim | | +> | `completeWithdrawTooltip` | string | This final step swaps all assets to a single asset and sends it to your wallet | | +> | `unrollAndClaimDescription` | string | Claim | | +> | `limitOrderWithdrawDescription` | string | You are about to create a withdrawal request. Your vault tokens will be withdrawn within a few minutes. | | +> | `total` | string | Total: | | +> | `showAll` | string | Show All | | +> | `hide` | string | Hide | | +> | `refreshSwapQuoteTooltip` | string | Refresh swap quote | | +> | `proceedWithNextStep` | string | Please proceed with the next step. | | +> | `aggregatorsLabel` | string | Swap source | | +> | `aggregatorsTooltip` | string | Choose which aggregators to use for your trades. | | +> | `batchTransactionsLabel` | string | Batch transactions | | +> | `batchTransactionsSwitchLabel` | string | Batch | | +> | `batchTransactionsTooltip` | string | Group approve and trade calls into a single batch transaction. Disable if you prefer separate prompts. | | +> | `openLimitOrderAfterBuySwitchLabel` | string | Add Stop Order | | +> | `orderingLabel` | string | Ordering | | +> | `paidLabel` | string | Paid | | +> | `priceLabel` | string | Price | | +> | `transactionLabel` | string | Transaction | | +> | `stopOrder` | string | Stop Order | | +> | `send` | string | Send | | +> | `cooldown` | string | Cooldown | | +> | `delete` | string | Delete | | +> | `withdrawalRequest` | string | Withdrawal request | | +> | `deleteWithdrawalRequest` | string | Delete withdrawal request | | +> | `insufficientBalance` | string | Insufficient Balance | | +> | `expectToReceiveUsdcSoon` | string | Expect to receive your USDC over the next few minutes | | ###### Source: `packages/trading-widget/src/trading-widget/providers/translation-provider/translation-provider.tsx` diff --git a/package.json b/package.json index 3eb9548b..30cc4eb6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@dhedge/trading-widget", - "version": "4.4.1", + "version": "4.5.0", "license": "MIT", "type": "module", "main": "dist/index.cjs", diff --git a/src/core-kit/abi/limit-order.ts b/src/core-kit/abi/limit-order.ts index 0e6a3a2c..156fd1e1 100644 --- a/src/core-kit/abi/limit-order.ts +++ b/src/core-kit/abi/limit-order.ts @@ -115,6 +115,25 @@ export const LimitOrderAbi = [ name: 'LimitOrderModified', type: 'event', }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'user', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'destTokenAmountReceived', + type: 'uint256', + }, + ], + name: 'SettlementOrderExecuted', + type: 'event', + }, { inputs: [], name: 'SLIPPAGE_DENOMINATOR', diff --git a/src/core-kit/const/logger.ts b/src/core-kit/const/logger.ts index 6e0fa33e..2b9ceb73 100644 --- a/src/core-kit/const/logger.ts +++ b/src/core-kit/const/logger.ts @@ -15,6 +15,8 @@ export const TRADING_PANEL_LOG_EVENT = { BATCH_TRANSACTIONS_CHANGE: 'batch_transactions_change', CREATE_LIMIT_SELL_ORDER: 'create_limit_sell_order', OPEN_LIMIT_SELL_VIEW: 'open_limit_sell_view', + LIMIT_ORDER_WITHDRAW: 'limit_order_withdraw', + LIMIT_ORDER_WITHDRAW_DELETE: 'limit_order_withdraw_delete', } export const LOG_EVENT_BY_TRANSACTION_ACTION_MAP: Record< @@ -38,6 +40,10 @@ export const LOG_EVENT_BY_TRANSACTION_ACTION_MAP: Record< claim: [TRADING_PANEL_LOG_EVENT.CLAIM], swap: [TRADING_PANEL_LOG_EVENT.SWAP], create_limit_sell_order: [TRADING_PANEL_LOG_EVENT.CREATE_LIMIT_SELL_ORDER], + limit_order_withdraw: [TRADING_PANEL_LOG_EVENT.LIMIT_ORDER_WITHDRAW], + delete_limit_order_withdraw: [ + TRADING_PANEL_LOG_EVENT.LIMIT_ORDER_WITHDRAW_DELETE, + ], } /** diff --git a/src/core-kit/hooks/trading/limit-order-withdraw/use-is-limit-order-withdraw.ts b/src/core-kit/hooks/trading/limit-order-withdraw/use-is-limit-order-withdraw.ts new file mode 100644 index 00000000..f1ff8bde --- /dev/null +++ b/src/core-kit/hooks/trading/limit-order-withdraw/use-is-limit-order-withdraw.ts @@ -0,0 +1,15 @@ +import { useTradingPanelPoolConfig } from 'core-kit/hooks/state' +import { useLeveragedWithdrawalChecks } from 'trading-widget/hooks/use-leveraged-withdrawal-checks' + +export const useIsLimitOrderWithdraw = () => { + const { pricingAsset, onDemandWithdrawalEnabled } = + useTradingPanelPoolConfig() + const { requiresLeveragedCollateralLiquidity } = + useLeveragedWithdrawalChecks() + + return ( + requiresLeveragedCollateralLiquidity && + !!pricingAsset && + !!onDemandWithdrawalEnabled + ) +} diff --git a/src/core-kit/hooks/trading/limit-order-withdraw/use-is-limit-withdraw-order-approved.ts b/src/core-kit/hooks/trading/limit-order-withdraw/use-is-limit-withdraw-order-approved.ts new file mode 100644 index 00000000..f09160bd --- /dev/null +++ b/src/core-kit/hooks/trading/limit-order-withdraw/use-is-limit-withdraw-order-approved.ts @@ -0,0 +1,21 @@ +import { AddressZero } from 'core-kit/const' +import { useTradingPanelPoolConfig } from 'core-kit/hooks/state' +import { useLimitOrderWithdrawAmount } from 'core-kit/hooks/trading/limit-order-withdraw/use-limit-order-withdraw-amount' +import { useAccount, useTokenAllowance } from 'core-kit/hooks/web3' +import { getContractAddressById } from 'core-kit/utils' + +export const useIsLimitWithdrawOrderApproved = () => { + const { account = AddressZero } = useAccount() + const { address: vaultAddress, chainId } = useTradingPanelPoolConfig() + const limitOrderAddress = getContractAddressById('limitOrder', chainId) + const vaultAmount = useLimitOrderWithdrawAmount() + + const { data: allowance } = useTokenAllowance({ + tokenAddress: vaultAddress, + ownerAddress: account, + spenderAddress: limitOrderAddress, + chainId, + }) + + return allowance ? vaultAmount.lte(allowance.toString()) : false +} diff --git a/src/core-kit/hooks/trading/limit-order-withdraw/use-limit-order-withdraw-amount.ts b/src/core-kit/hooks/trading/limit-order-withdraw/use-limit-order-withdraw-amount.ts new file mode 100644 index 00000000..3a480d30 --- /dev/null +++ b/src/core-kit/hooks/trading/limit-order-withdraw/use-limit-order-withdraw-amount.ts @@ -0,0 +1,9 @@ +import BigNumber from 'bignumber.js' + +import { useSendTokenInput } from 'core-kit/hooks/state' + +export const useLimitOrderWithdrawAmount = () => { + const [sendToken] = useSendTokenInput() + + return new BigNumber(sendToken.value || '0').shiftedBy(sendToken.decimals) +} diff --git a/src/core-kit/hooks/trading/limit-order-withdraw/use-limit-order-withdraw-delete-transaction.ts b/src/core-kit/hooks/trading/limit-order-withdraw/use-limit-order-withdraw-delete-transaction.ts new file mode 100644 index 00000000..bdadaef8 --- /dev/null +++ b/src/core-kit/hooks/trading/limit-order-withdraw/use-limit-order-withdraw-delete-transaction.ts @@ -0,0 +1,33 @@ +import { useCallback } from 'react' + +import { + useTradingPanelPoolConfig, + useTradingPanelTransactions, +} from 'core-kit/hooks/state' +import { useTradingSettleHandler } from 'core-kit/hooks/trading' +import { useContractFunction } from 'core-kit/hooks/web3' + +const action = 'delete_limit_order_withdraw' + +export const useLimitOrderWithdrawDeleteTransaction = () => { + const { address: vaultAddress, symbol, chainId } = useTradingPanelPoolConfig() + const updatePendingTransactions = useTradingPanelTransactions()[1] + const onSettled = useTradingSettleHandler(action) + + const { send } = useContractFunction({ + onSettled, + contractId: 'limitOrder', + functionName: 'deleteLimitOrder', + }) + + return useCallback(async () => { + updatePendingTransactions({ + type: 'add', + action, + symbol, + chainId, + }) + + return send(vaultAddress) + }, [chainId, send, symbol, updatePendingTransactions, vaultAddress]) +} diff --git a/src/core-kit/hooks/trading/limit-order-withdraw/use-limit-order-withdraw-transaction.ts b/src/core-kit/hooks/trading/limit-order-withdraw/use-limit-order-withdraw-transaction.ts new file mode 100644 index 00000000..ec996efa --- /dev/null +++ b/src/core-kit/hooks/trading/limit-order-withdraw/use-limit-order-withdraw-transaction.ts @@ -0,0 +1,82 @@ +import { useCallback } from 'react' + +import { AddressZero, MaxUint256 } from 'core-kit/const' +import { + useTradingPanelPoolConfig, + useTradingPanelTransactions, +} from 'core-kit/hooks/state' +import { useTradingSettleHandler } from 'core-kit/hooks/trading' +import { useLimitOrderWithdrawAmount } from 'core-kit/hooks/trading/limit-order-withdraw/use-limit-order-withdraw-amount' +import { useAccount, useContractFunction } from 'core-kit/hooks/web3' +import type { UseWriteContractParameters } from 'core-kit/types/web3.types' +import { useUserLimitOrder } from 'limit-orders/hooks/use-user-limit-order' + +const action = 'limit_order_withdraw' + +export const useLimitOrderWithdrawTransaction = (txCallback: () => void) => { + const { account = AddressZero } = useAccount() + const { + address: vaultAddress, + chainId, + pricingAsset, + symbol, + } = useTradingPanelPoolConfig() + const updatePendingTransactions = useTradingPanelTransactions()[1] + const onTransactionSettle = useTradingSettleHandler(action) + + const onSettled = useCallback< + Required['mutation']>['onSettled'] + >( + (...args) => { + onTransactionSettle(...args) + txCallback() + }, + [onTransactionSettle, txCallback], + ) + + const vaultAmount = useLimitOrderWithdrawAmount() + + const { data: limitOrder } = useUserLimitOrder({ + userAddress: account, + vaultAddress, + chainId, + }) + const isModifyTransaction = !!limitOrder + const { send } = useContractFunction({ + onSettled, + contractId: 'limitOrder', + functionName: isModifyTransaction ? 'modifyLimitOrder' : 'createLimitOrder', + }) + + return useCallback(async () => { + updatePendingTransactions({ + type: 'add', + action, + symbol, + chainId, + }) + + const lowerLimitPriceD18 = BigInt(0) + const upperLimitPriceD18 = MaxUint256 + + const args = [ + BigInt(vaultAmount.toFixed()), + lowerLimitPriceD18, + upperLimitPriceD18, + account, + vaultAddress, + pricingAsset?.address, + ] + + return send(args) + }, [ + updatePendingTransactions, + symbol, + chainId, + vaultAmount, + account, + vaultAddress, + pricingAsset?.address, + send, + ]) +} diff --git a/src/core-kit/hooks/trading/limit-order-withdraw/use-limit-withdraw-approve-transaction.ts b/src/core-kit/hooks/trading/limit-order-withdraw/use-limit-withdraw-approve-transaction.ts new file mode 100644 index 00000000..a0140cc5 --- /dev/null +++ b/src/core-kit/hooks/trading/limit-order-withdraw/use-limit-withdraw-approve-transaction.ts @@ -0,0 +1,47 @@ +import { useState } from 'react' + +import { MaxUint256 } from 'core-kit/const' +import { + useTradingPanelPoolConfig, + useTradingPanelTransactions, +} from 'core-kit/hooks/state' +import { useTradingSettleHandler } from 'core-kit/hooks/trading' +import { useContractFunction } from 'core-kit/hooks/web3' +import { getContractAddressById } from 'core-kit/utils' + +const action = 'approve' + +export const useLimitWithdrawApproveTransaction = () => { + const { address: vaultAddress, chainId, symbol } = useTradingPanelPoolConfig() + const [isLoading, setIsLoading] = useState(false) + const limitOrderAddress = getContractAddressById('limitOrder', chainId) + const updatePendingTransactions = useTradingPanelTransactions()[1] + const onApproveSettled = useTradingSettleHandler(action) + + const { send } = useContractFunction({ + contractId: 'erc20', + dynamicContractAddress: vaultAddress, + functionName: 'approve', + onSettled(...args) { + setIsLoading(false) + onApproveSettled(...args) + }, + }) + + const approveLimitOrder = async () => { + updatePendingTransactions({ + type: 'add', + action, + symbol, + chainId, + }) + + setIsLoading(true) + send(limitOrderAddress, MaxUint256) + } + + return { + approveLimitOrder, + isApprovePending: isLoading, + } +} diff --git a/src/core-kit/hooks/trading/limit-order-withdraw/use-pending-limit-order-withdraw.ts b/src/core-kit/hooks/trading/limit-order-withdraw/use-pending-limit-order-withdraw.ts new file mode 100644 index 00000000..e8ed458b --- /dev/null +++ b/src/core-kit/hooks/trading/limit-order-withdraw/use-pending-limit-order-withdraw.ts @@ -0,0 +1,33 @@ +import { useMemo } from 'react' + +import { AddressZero, DEFAULT_PRECISION, MaxUint256 } from 'core-kit/const' +import { useTradingPanelPoolConfig } from 'core-kit/hooks/state' +import { useAccount } from 'core-kit/hooks/web3' +import { formatUnits } from 'core-kit/utils' +import { useUserLimitOrder } from 'limit-orders/hooks/use-user-limit-order' + +export const usePendingLimitOrderWithdraw = () => { + const { account = AddressZero } = useAccount() + const { address: vaultAddress, chainId } = useTradingPanelPoolConfig() + const { data: limitOrder } = useUserLimitOrder({ + userAddress: account, + vaultAddress, + chainId, + }) + + return useMemo(() => { + const hasPendingLimitOrderWithdraw = + !!limitOrder && + limitOrder.stopLossPriceD18 === BigInt(0) && + limitOrder.takeProfitPriceD18 === MaxUint256 + + const pendingLimitOrderWithdrawAmount = hasPendingLimitOrderWithdraw + ? formatUnits(limitOrder.amountD18, DEFAULT_PRECISION) + : '0' + + return { + hasPendingLimitOrderWithdraw, + pendingLimitOrderWithdrawAmount, + } + }, [limitOrder]) +} diff --git a/src/core-kit/hooks/trading/trade-handlers/use-handle-limit-order-withdraw.ts b/src/core-kit/hooks/trading/trade-handlers/use-handle-limit-order-withdraw.ts new file mode 100644 index 00000000..357943f3 --- /dev/null +++ b/src/core-kit/hooks/trading/trade-handlers/use-handle-limit-order-withdraw.ts @@ -0,0 +1,85 @@ +import { + useOnTransactionEstimationError, + useSendTokenInput, + useTradingPanelModal, + useTradingPanelPoolConfig, + useTradingPanelTransactions, +} from 'core-kit/hooks/state' +import { useAccount } from 'core-kit/hooks/web3' +import { EstimationError } from 'core-kit/models' +import type { ContractActionFunc } from 'core-kit/types/web3.types' +import { useOverlayDispatchContext } from 'trading-widget/providers/overlay-provider' +import { useTranslationContext } from 'trading-widget/providers/translation-provider' +import { OVERLAY } from 'trading-widget/types' + +interface UseHandleLimitOrderWithdrawProps { + limitOrderHandler: ContractActionFunc + action: 'limit_order_withdraw' | 'delete_limit_order_withdraw' +} + +export const useHandleLimitOrderWithdraw = ({ + limitOrderHandler, + action, +}: UseHandleLimitOrderWithdrawProps) => { + const t = useTranslationContext() + const dispatch = useOverlayDispatchContext() + const { account } = useAccount() + const poolConfig = useTradingPanelPoolConfig() + const [sendToken] = useSendTokenInput() + const updateTradingModal = useTradingPanelModal()[1] + const updatePendingTransactions = useTradingPanelTransactions()[1] + const onTransactionEstimationError = useOnTransactionEstimationError() + const isDeleteAction = action === 'delete_limit_order_withdraw' + + const handleLimitOrderWithdraw = async () => { + const chainId = poolConfig.chainId + + updateTradingModal({ + isOpen: true, + status: 'Wallet', + action, + link: '', + sendTokens: isDeleteAction ? [] : [sendToken], + receiveTokens: null, + meta: {}, + }) + + try { + await limitOrderHandler() + } catch (error) { + if (error instanceof EstimationError) { + dispatch({ + type: 'MERGE_OVERLAY', + payload: { + type: OVERLAY.ERROR_NOTIFICATION, + isOpen: true, + meta: { + error: error.message, + }, + }, + }) + + onTransactionEstimationError?.( + error, + poolConfig.address, + chainId, + account, + ) + } + updateTradingModal({ + isOpen: false, + status: 'None', + link: '', + sendTokens: null, + receiveTokens: null, + meta: {}, + }) + updatePendingTransactions({ type: 'remove', status: 'fail' }) + } + } + + return { + label: isDeleteAction ? t.delete : t.withdrawAction, + handleLimitOrderWithdraw, + } +} diff --git a/src/core-kit/hooks/trading/trade-handlers/use-handle-trade.ts b/src/core-kit/hooks/trading/trade-handlers/use-handle-trade.ts index 24265ba9..7e07114d 100644 --- a/src/core-kit/hooks/trading/trade-handlers/use-handle-trade.ts +++ b/src/core-kit/hooks/trading/trade-handlers/use-handle-trade.ts @@ -14,10 +14,7 @@ import { useIsMultiAssetWithdraw, useIsUnrollAndClaimTransaction, } from 'core-kit/hooks/trading/withdraw-v2/init-step' -import { - useIsInsufficientBalance, - useUserTokenBalance, -} from 'core-kit/hooks/user' +import { useUserTokenBalance } from 'core-kit/hooks/user' import { useAccount } from 'core-kit/hooks/web3' import { EstimationError } from 'core-kit/models' import type { ContractActionFunc } from 'core-kit/types/web3.types' @@ -45,7 +42,6 @@ export const useHandleTrade = (trade: ContractActionFunc) => { const isDeposit = type === 'deposit' const tradingEnabled = useIsTradingEnabled() - const insufficientBalance = useIsInsufficientBalance() const action = isDeposit ? 'deposit' @@ -113,16 +109,14 @@ export const useHandleTrade = (trade: ContractActionFunc) => { } return { - disabled: !tradingEnabled || insufficientBalance, - label: insufficientBalance - ? 'Insufficient Balance' - : isDeposit - ? t.depositAction - : isMultiAssetWithdraw - ? t.withdrawAction - : isUnrollAndClaimTransaction - ? t.unrollAndClaimAction - : t.unrollAction, + disabled: !tradingEnabled, + label: isDeposit + ? t.depositAction + : isMultiAssetWithdraw + ? t.withdrawAction + : isUnrollAndClaimTransaction + ? t.unrollAndClaimAction + : t.unrollAction, handleTrade, } } diff --git a/src/core-kit/hooks/web3/use-invalidate-trading-queries.ts b/src/core-kit/hooks/web3/use-invalidate-trading-queries.ts index b8841313..400c46bb 100644 --- a/src/core-kit/hooks/web3/use-invalidate-trading-queries.ts +++ b/src/core-kit/hooks/web3/use-invalidate-trading-queries.ts @@ -6,6 +6,10 @@ import type { ContractReadQueryKey, ContractReadsQueryKey, } from 'core-kit/types' +import { + GET_ALL_LIMIT_ORDER_IDS_FUNCTION_NAME, + LIMIT_ORDER_READ_FUNCTION_NAME, +} from 'limit-orders/constants' const tradingContractCalls = [ 'balanceOf', @@ -19,6 +23,8 @@ const tradingContractCalls = [ 'tokenPrice', 'limitOrders', 'calculateAvailableManagerFee', + LIMIT_ORDER_READ_FUNCTION_NAME, + GET_ALL_LIMIT_ORDER_IDS_FUNCTION_NAME, ] const allowanceContractCalls = ['allowance'] diff --git a/src/core-kit/types/config.types.ts b/src/core-kit/types/config.types.ts index 6a00a42f..d33cb583 100644 --- a/src/core-kit/types/config.types.ts +++ b/src/core-kit/types/config.types.ts @@ -27,6 +27,7 @@ export interface PoolConfig { maintenanceDeposits?: boolean maintenanceWithdrawals?: boolean pricingAsset?: PricingAsset + onDemandWithdrawalEnabled?: boolean } export interface PoolFallbackData { diff --git a/src/core-kit/types/trading-panel.types.ts b/src/core-kit/types/trading-panel.types.ts index cc4f4fc3..7477332d 100644 --- a/src/core-kit/types/trading-panel.types.ts +++ b/src/core-kit/types/trading-panel.types.ts @@ -44,6 +44,8 @@ export type TransactionAction = | 'claim' | 'single_withdraw_and_claim' | 'create_limit_sell_order' + | 'limit_order_withdraw' + | 'delete_limit_order_withdraw' export type SwapEntity = 'token' | 'pool' diff --git a/src/index.ts b/src/index.ts index 58b957fd..10e3b1a2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,6 +10,7 @@ export { PoolManagerLogicAbi, RewardDistributionAbi, EasySwapperV2Abi, + LimitOrderAbi, } from 'core-kit/abi' export type { GmxMarketAsset } from 'core-kit/const' @@ -343,4 +344,11 @@ export type { } from 'trading-widget/providers/translation-provider' // Limit Orders -export { LimitOrderModal, useUserLimitOrder } from 'limit-orders/index' +export { + LimitOrderModal, + useUserLimitOrder, + useInvalidateLimitOrderQuery, + LIMIT_ORDER_READ_FUNCTION_NAME, + GET_ALL_LIMIT_ORDER_IDS_FUNCTION_NAME, + getLimitOrderId, +} from 'limit-orders/index' diff --git a/src/limit-orders/constants.ts b/src/limit-orders/constants.ts index 17145974..b56df031 100644 --- a/src/limit-orders/constants.ts +++ b/src/limit-orders/constants.ts @@ -1,4 +1,5 @@ export const LIMIT_ORDER_READ_FUNCTION_NAME = 'limitOrders' +export const GET_ALL_LIMIT_ORDER_IDS_FUNCTION_NAME = 'getAllLimitOrderIds' export const DEFAULT_MIN_ORDER_AMOUNT = 0 export const DEFAULT_SELL_PERCENTAGE = '100' diff --git a/src/limit-orders/hooks/use-invalidate-limit-order-query.ts b/src/limit-orders/hooks/use-invalidate-limit-order-query.ts index 55c66d6f..b1b3a516 100644 --- a/src/limit-orders/hooks/use-invalidate-limit-order-query.ts +++ b/src/limit-orders/hooks/use-invalidate-limit-order-query.ts @@ -2,9 +2,16 @@ import { useQueryClient } from '@tanstack/react-query' import { useCallback } from 'react' import type { ContractReadQueryKey } from 'core-kit/types' -import { LIMIT_ORDER_READ_FUNCTION_NAME } from 'limit-orders/constants' +import { + GET_ALL_LIMIT_ORDER_IDS_FUNCTION_NAME, + LIMIT_ORDER_READ_FUNCTION_NAME, +} from 'limit-orders/constants' -const limitOrderReadCalls = [LIMIT_ORDER_READ_FUNCTION_NAME, 'allowance'] +const limitOrderReadCalls = [ + LIMIT_ORDER_READ_FUNCTION_NAME, + GET_ALL_LIMIT_ORDER_IDS_FUNCTION_NAME, + 'allowance', +] export const useInvalidateLimitOrderQuery = () => { const queryClient = useQueryClient() diff --git a/src/limit-orders/index.ts b/src/limit-orders/index.ts index df6df844..592e7607 100644 --- a/src/limit-orders/index.ts +++ b/src/limit-orders/index.ts @@ -1,2 +1,8 @@ export { LimitOrderModal } from 'limit-orders/component/limit-order-modal' export { useUserLimitOrder } from 'limit-orders/hooks/use-user-limit-order' +export { useInvalidateLimitOrderQuery } from 'limit-orders/hooks/use-invalidate-limit-order-query' +export { + LIMIT_ORDER_READ_FUNCTION_NAME, + GET_ALL_LIMIT_ORDER_IDS_FUNCTION_NAME, +} from 'limit-orders/constants' +export { getLimitOrderId } from 'limit-orders/utils' diff --git a/src/trading-widget/components/deposit/button/valid-deposit-button/valid-deposit-button.hooks.ts b/src/trading-widget/components/deposit/button/valid-deposit-button/valid-deposit-button.hooks.ts index fa14c2e3..a542d5bc 100644 --- a/src/trading-widget/components/deposit/button/valid-deposit-button/valid-deposit-button.hooks.ts +++ b/src/trading-widget/components/deposit/button/valid-deposit-button/valid-deposit-button.hooks.ts @@ -8,6 +8,7 @@ import { useIsVaultDepositLocked, useRequiresMinDeposit, } from 'core-kit/hooks/trading/deposit-v2' +import { useIsInsufficientBalance } from 'core-kit/hooks/user' import { useHighSlippageCheck } from 'trading-widget/hooks' export const useValidDepositButton = () => { @@ -15,6 +16,7 @@ export const useValidDepositButton = () => { const { deprecated, symbol, maintenance, maintenanceDeposits } = useTradingPanelPoolConfig() const [sendToken] = useSendTokenInput() + const insufficientBalance = useIsInsufficientBalance() const { isVaultDepositLocked, isAccountWhitelisted } = useIsVaultDepositLocked() @@ -38,5 +40,6 @@ export const useValidDepositButton = () => { slippageToBeUsed, maintenance: maintenance || maintenanceDeposits, isBatchContractWritesTrading, + insufficientBalance, } } diff --git a/src/trading-widget/components/deposit/button/valid-deposit-button/valid-deposit-button.tsx b/src/trading-widget/components/deposit/button/valid-deposit-button/valid-deposit-button.tsx index e970cdb0..9b2e89f5 100644 --- a/src/trading-widget/components/deposit/button/valid-deposit-button/valid-deposit-button.tsx +++ b/src/trading-widget/components/deposit/button/valid-deposit-button/valid-deposit-button.tsx @@ -33,6 +33,7 @@ export const ValidDepositButton: FC = () => { confirmHighSlippage, maintenance, isBatchContractWritesTrading, + insufficientBalance, } = useValidDepositButton() if (requiresMinDeposit) { @@ -59,6 +60,10 @@ export const ValidDepositButton: FC = () => { ) } + if (insufficientBalance) { + return + } + if (requiresHighSlippageConfirm) { return ( + ) + } + + return children +} diff --git a/src/trading-widget/components/widget/widget-overlay/limit-order-withdraw-overlay/limit-order-withdraw-button/limit-order-withdraw-button.hooks.ts b/src/trading-widget/components/widget/widget-overlay/limit-order-withdraw-overlay/limit-order-withdraw-button/limit-order-withdraw-button.hooks.ts new file mode 100644 index 00000000..a9ffaf89 --- /dev/null +++ b/src/trading-widget/components/widget/widget-overlay/limit-order-withdraw-overlay/limit-order-withdraw-button/limit-order-withdraw-button.hooks.ts @@ -0,0 +1,35 @@ +import { useCallback } from 'react' + +import { useLimitOrderWithdrawTransaction } from 'core-kit/hooks/trading/limit-order-withdraw/use-limit-order-withdraw-transaction' +import { useHandleLimitOrderWithdraw } from 'core-kit/hooks/trading/trade-handlers/use-handle-limit-order-withdraw' +import { useIsTransactionLoading } from 'core-kit/hooks/trading/use-is-transaction-loading' +import { useOverlayDispatchContext } from 'trading-widget/providers/overlay-provider' +import { OVERLAY } from 'trading-widget/types' + +const action = 'limit_order_withdraw' + +export const useLimitOrderWithdrawButton = () => { + const dispatch = useOverlayDispatchContext() + const isLoading = useIsTransactionLoading('limit_order_withdraw') + + const closeOverlay = useCallback( + () => + dispatch({ + type: 'MERGE_OVERLAY', + payload: { type: OVERLAY.LIMIT_ORDER_WITHDRAW, isOpen: false }, + }), + [dispatch], + ) + + const limitOrderHandler = useLimitOrderWithdrawTransaction(closeOverlay) + const { handleLimitOrderWithdraw, label } = useHandleLimitOrderWithdraw({ + limitOrderHandler, + action, + }) + + return { + label, + handleLimitOrderWithdraw, + isLoading, + } +} diff --git a/src/trading-widget/components/widget/widget-overlay/limit-order-withdraw-overlay/limit-order-withdraw-button/limit-order-withdraw-button.tsx b/src/trading-widget/components/widget/widget-overlay/limit-order-withdraw-overlay/limit-order-withdraw-button/limit-order-withdraw-button.tsx new file mode 100644 index 00000000..094d89fc --- /dev/null +++ b/src/trading-widget/components/widget/widget-overlay/limit-order-withdraw-overlay/limit-order-withdraw-button/limit-order-withdraw-button.tsx @@ -0,0 +1,27 @@ +import type { FC } from 'react' + +import { ActionButton } from 'trading-widget/components/common' + +import { useComponentContext } from 'trading-widget/providers/component-provider' + +import { useLimitOrderWithdrawButton } from './limit-order-withdraw-button.hooks' + +interface Props { + disabled?: boolean +} + +export const LimitOrderWithdrawButton: FC = ({ disabled }) => { + const { ActionButton: Button = ActionButton } = useComponentContext() + const { label, handleLimitOrderWithdraw, isLoading } = + useLimitOrderWithdrawButton() + + return ( + + ) +} diff --git a/src/trading-widget/components/widget/widget-overlay/limit-order-withdraw-overlay/limit-order-withdraw-overlay.tsx b/src/trading-widget/components/widget/widget-overlay/limit-order-withdraw-overlay/limit-order-withdraw-overlay.tsx new file mode 100644 index 00000000..4920f937 --- /dev/null +++ b/src/trading-widget/components/widget/widget-overlay/limit-order-withdraw-overlay/limit-order-withdraw-overlay.tsx @@ -0,0 +1,48 @@ +import type { FC } from 'react' +import { useState } from 'react' + +import { ActionButton, Layout } from 'trading-widget/components/common' + +import { CheckBox } from 'trading-widget/components/common/checkbox/checkbox' + +import { useComponentContext } from 'trading-widget/providers/component-provider' +import { useOverlayHandlers } from 'trading-widget/providers/overlay-provider' +import { useTranslationContext } from 'trading-widget/providers/translation-provider' +import type { OverlayProps } from 'trading-widget/types' + +import { LimitOrderWithdrawApproveButton } from './limit-order-withdraw-approve-button/limit-order-withdraw-approve-button' +import { LimitOrderWithdrawButton } from './limit-order-withdraw-button/limit-order-withdraw-button' +import { LimitOrderWithdrawTermsContent } from './limit-order-withdraw-terms-content' + +export const LimitOrderWithdrawOverlay: FC = ({ type }) => { + const t = useTranslationContext() + const { ActionButton: Button = ActionButton } = useComponentContext() + + const { handleReject } = useOverlayHandlers({ type }) + + const [termsAccepted, setTermsAccepted] = useState(false) + + return ( + +
+
+ +
+ setTermsAccepted(checked)} + label={t.termOfUseWithdrawAcceptLabel} + labelClassName="dtw-text-sm dtw-cursor-pointer" + /> +
+
+ + + + +
+
+ ) +} diff --git a/src/trading-widget/components/widget/widget-overlay/limit-order-withdraw-overlay/limit-order-withdraw-terms-content.tsx b/src/trading-widget/components/widget/widget-overlay/limit-order-withdraw-overlay/limit-order-withdraw-terms-content.tsx new file mode 100644 index 00000000..d986d192 --- /dev/null +++ b/src/trading-widget/components/widget/widget-overlay/limit-order-withdraw-overlay/limit-order-withdraw-terms-content.tsx @@ -0,0 +1,39 @@ +import classNames from 'classnames' +import type { FC } from 'react' + +import { useTranslationContext } from 'trading-widget/providers/translation-provider' + +interface WithdrawTermsContentProps { + className?: string +} + +const TERMS_KEYS = [ + 'termOfUseWithdrawPoint1', + 'termOfUseWithdrawPoint2', +] as const + +export const LimitOrderWithdrawTermsContent: FC = ({ + className, +}) => { + const t = useTranslationContext() + + return ( + <> +

{t.limitOrderWithdrawDescription}

+
+
    + {TERMS_KEYS.map((key, index) => ( +
  1. + {index + 1}. {t[key]} +
  2. + ))} +
+
+ + ) +} diff --git a/src/trading-widget/components/widget/widget-overlay/trading-overlay/trading-overlay.hooks.ts b/src/trading-widget/components/widget/widget-overlay/trading-overlay/trading-overlay.hooks.ts index 39f3f05d..f6e6cae5 100644 --- a/src/trading-widget/components/widget/widget-overlay/trading-overlay/trading-overlay.hooks.ts +++ b/src/trading-widget/components/widget/widget-overlay/trading-overlay/trading-overlay.hooks.ts @@ -17,6 +17,7 @@ export const useTradingOverlay = ({ type }: TradingOverlayProps) => { const displaySuccessDepositOverlay = isSuccessTx && action === 'deposit' const displayLimitOrderTransactionOverlay = action === 'create_limit_sell_order' + const displayWithdrawLimitOrderOverlay = action === 'limit_order_withdraw' const statusMap = useMemo>>( () => ({ @@ -40,5 +41,6 @@ export const useTradingOverlay = ({ type }: TradingOverlayProps) => { showNextStepTip, displaySuccessDepositOverlay, displayLimitOrderTransactionOverlay, + displayWithdrawLimitOrderOverlay, } } diff --git a/src/trading-widget/components/widget/widget-overlay/trading-overlay/trading-overlay.tsx b/src/trading-widget/components/widget/widget-overlay/trading-overlay/trading-overlay.tsx index 88abb7a3..552a05a1 100644 --- a/src/trading-widget/components/widget/widget-overlay/trading-overlay/trading-overlay.tsx +++ b/src/trading-widget/components/widget/widget-overlay/trading-overlay/trading-overlay.tsx @@ -13,6 +13,7 @@ import { LimitOrderTransactionOverlay } from 'trading-widget/components/widget/w import type { TradingOverlayProps } from 'trading-widget/components/widget/widget-overlay/trading-overlay/trading-overlay.hooks' import { useTradingOverlay } from 'trading-widget/components/widget/widget-overlay/trading-overlay/trading-overlay.hooks' import { TradingSummary } from 'trading-widget/components/widget/widget-overlay/trading-overlay/trading-summary/trading-summary' +import { LimitOrderWithdrawalOverlay } from 'trading-widget/components/widget/widget-overlay/trading-overlay/withdrawals/limit-order-withdrawal-overlay' import { useComponentContext } from 'trading-widget/providers/component-provider' import { useTranslationContext } from 'trading-widget/providers/translation-provider' @@ -25,6 +26,7 @@ export const TradingOverlay: FC = ({ type }) => { showNextStepTip, displaySuccessDepositOverlay, displayLimitOrderTransactionOverlay, + displayWithdrawLimitOrderOverlay, } = useTradingOverlay({ type }) const { LogoSpinner = Spinner } = useComponentContext() const t = useTranslationContext() @@ -37,6 +39,10 @@ export const TradingOverlay: FC = ({ type }) => { return } + if (displayWithdrawLimitOrderOverlay) { + return + } + return (
diff --git a/src/trading-widget/components/widget/widget-overlay/trading-overlay/trading-summary/trading-summary.tsx b/src/trading-widget/components/widget/widget-overlay/trading-overlay/trading-summary/trading-summary.tsx index 03e919a2..6e51ae2f 100644 --- a/src/trading-widget/components/widget/widget-overlay/trading-overlay/trading-summary/trading-summary.tsx +++ b/src/trading-widget/components/widget/widget-overlay/trading-overlay/trading-summary/trading-summary.tsx @@ -26,6 +26,10 @@ export const TradingSummary: FC = () => { return <>{t.createLimitSellOrder} } + if (action === 'delete_limit_order_withdraw') { + return <>{t.deleteWithdrawalRequest} + } + return ( { + const [, updateTradingModal] = useTradingPanelModal() + const { handleReject } = useOverlayHandlers({ type }) + const [{ link, status }] = useTradingPanelModal() + + const isSuccessTx = status == 'Success' + + const onClose = useCallback(() => { + updateTradingModal({ isOpen: false }) + handleReject() + }, [handleReject, updateTradingModal]) + + return { onClose, link, isSuccessTx } +} diff --git a/src/trading-widget/components/widget/widget-overlay/trading-overlay/withdrawals/limit-order-withdrawal-overlay.tsx b/src/trading-widget/components/widget/widget-overlay/trading-overlay/withdrawals/limit-order-withdrawal-overlay.tsx new file mode 100644 index 00000000..4278afcc --- /dev/null +++ b/src/trading-widget/components/widget/widget-overlay/trading-overlay/withdrawals/limit-order-withdrawal-overlay.tsx @@ -0,0 +1,52 @@ +import { CheckIcon } from '@heroicons/react/24/solid' +import type { FC } from 'react' + +import { + ActionButton, + ExternalLinkButton, + Layout, +} from 'trading-widget/components/common' +import { useLimitOrderWithdrawalOverlay } from 'trading-widget/components/widget/widget-overlay/trading-overlay/withdrawals/limit-order-withdrawal-overlay.hooks' +import { useComponentContext } from 'trading-widget/providers/component-provider' +import { useTranslationContext } from 'trading-widget/providers/translation-provider' +import type { OverlayProps } from 'trading-widget/types' + +export const LimitOrderWithdrawalOverlay: FC = (props) => { + const t = useTranslationContext() + const { ActionButton: Button = ActionButton } = useComponentContext() + const { onClose, link, isSuccessTx } = useLimitOrderWithdrawalOverlay(props) + + if (!isSuccessTx) return null + + return ( + +
+ +
+ {t.withdrawalRequest} +
+
+ {t.expectToReceiveUsdcSoon} +
+ {link && ( + + {t.explorer} + + )} +
+
+ +
+
+ ) +} diff --git a/src/trading-widget/components/widget/widget.tsx b/src/trading-widget/components/widget/widget.tsx index 0d472ac9..94c3e1eb 100644 --- a/src/trading-widget/components/widget/widget.tsx +++ b/src/trading-widget/components/widget/widget.tsx @@ -9,6 +9,7 @@ import { ErrorNotificationOverlay, FmpWithdrawalOverlay, HighSlippageOverlay, + LimitOrderWithdrawOverlay, LimitSellsOverlay, OverlaySwitch, PoolSelectOverlay, @@ -53,6 +54,7 @@ export const Widget: FC = () => { +
) diff --git a/src/trading-widget/components/withdraw/init-step/button/limit-order-withdraw-button/limit-order-withdraw-button.hooks.ts b/src/trading-widget/components/withdraw/init-step/button/limit-order-withdraw-button/limit-order-withdraw-button.hooks.ts new file mode 100644 index 00000000..716daa2c --- /dev/null +++ b/src/trading-widget/components/withdraw/init-step/button/limit-order-withdraw-button/limit-order-withdraw-button.hooks.ts @@ -0,0 +1,19 @@ +import { useOverlayDispatchContext } from 'trading-widget/providers/overlay-provider' +import { useTranslationContext } from 'trading-widget/providers/translation-provider' +import { OVERLAY } from 'trading-widget/types' + +export const useLimitOrderWithdrawButton = () => { + const t = useTranslationContext() + const dispatch = useOverlayDispatchContext() + + const openLimitOrderWithdrawOverlay = () => + dispatch({ + type: 'MERGE_OVERLAY', + payload: { type: OVERLAY.LIMIT_ORDER_WITHDRAW, isOpen: true }, + }) + + return { + label: t.withdrawAction, + openLimitOrderWithdrawOverlay, + } +} diff --git a/src/trading-widget/components/withdraw/init-step/button/limit-order-withdraw-button/limit-order-withdraw-button.tsx b/src/trading-widget/components/withdraw/init-step/button/limit-order-withdraw-button/limit-order-withdraw-button.tsx new file mode 100644 index 00000000..fca35b0e --- /dev/null +++ b/src/trading-widget/components/withdraw/init-step/button/limit-order-withdraw-button/limit-order-withdraw-button.tsx @@ -0,0 +1,13 @@ +import type { FC } from 'react' + +import { ActionButton } from 'trading-widget/components/common' + +import { useLimitOrderWithdrawButton } from 'trading-widget/components/withdraw/init-step/button/limit-order-withdraw-button/limit-order-withdraw-button.hooks' +import { useComponentContext } from 'trading-widget/providers/component-provider' + +export const LimitOrderWithdrawButton: FC = () => { + const { ActionButton: Button = ActionButton } = useComponentContext() + const { label, openLimitOrderWithdrawOverlay } = useLimitOrderWithdrawButton() + + return +} diff --git a/src/trading-widget/components/withdraw/init-step/button/valid-init-withdraw-button/valid-init-withdraw-button.hooks.ts b/src/trading-widget/components/withdraw/init-step/button/valid-init-withdraw-button/valid-init-withdraw-button.hooks.ts index e2c0fd81..38b32f18 100644 --- a/src/trading-widget/components/withdraw/init-step/button/valid-init-withdraw-button/valid-init-withdraw-button.hooks.ts +++ b/src/trading-widget/components/withdraw/init-step/button/valid-init-withdraw-button/valid-init-withdraw-button.hooks.ts @@ -5,8 +5,10 @@ import { useSendTokenInput, useTradingPanelPoolConfig, } from 'core-kit/hooks/state' +import { useIsLimitOrderWithdraw } from 'core-kit/hooks/trading/limit-order-withdraw/use-is-limit-order-withdraw' import { useInitWithdrawAllowance } from 'core-kit/hooks/trading/withdraw-v2/init-step' +import { useIsInsufficientBalance } from 'core-kit/hooks/user' import { useHighSlippageCheck } from 'trading-widget/hooks' import { useOverlayDispatchContext } from 'trading-widget/providers/overlay-provider' import { OVERLAY } from 'trading-widget/types' @@ -16,6 +18,8 @@ export const useValidInitWithdrawButton = () => { useTradingPanelPoolConfig() const [sendToken] = useSendTokenInput() const dispatch = useOverlayDispatchContext() + const isLimitOrderWithdraw = useIsLimitOrderWithdraw() + const insufficientBalance = useIsInsufficientBalance() const { data: dynamicCooldownMs = 0 } = usePoolDynamicExitRemainingCooldown({ address, @@ -53,5 +57,7 @@ export const useValidInitWithdrawButton = () => { handleHighSlippageClick, maintenance: maintenance || maintenanceWithdrawals, poolSymbol: symbol, + isLimitOrderWithdraw, + insufficientBalance, } } diff --git a/src/trading-widget/components/withdraw/init-step/button/valid-init-withdraw-button/valid-init-withdraw-button.tsx b/src/trading-widget/components/withdraw/init-step/button/valid-init-withdraw-button/valid-init-withdraw-button.tsx index 430ae625..785c4d9c 100644 --- a/src/trading-widget/components/withdraw/init-step/button/valid-init-withdraw-button/valid-init-withdraw-button.tsx +++ b/src/trading-widget/components/withdraw/init-step/button/valid-init-withdraw-button/valid-init-withdraw-button.tsx @@ -6,6 +6,7 @@ import { } from 'trading-widget/components/common' import { ApproveButton } from 'trading-widget/components/widget/widget-buttons' +import { LimitOrderWithdrawButton } from 'trading-widget/components/withdraw/init-step/button/limit-order-withdraw-button/limit-order-withdraw-button' import { useValidInitWithdrawButton } from 'trading-widget/components/withdraw/init-step/button/valid-init-withdraw-button/valid-init-withdraw-button.hooks' import { useTradingTypeName } from 'trading-widget/hooks' @@ -29,6 +30,8 @@ export const ValidInitWithdrawButton: FC = ({ handleHighSlippageClick, maintenance, poolSymbol, + isLimitOrderWithdraw, + insufficientBalance, } = useValidInitWithdrawButton() if (maintenance) { @@ -56,6 +59,14 @@ export const ValidInitWithdrawButton: FC = ({ ) } + if (insufficientBalance) { + return + } + + if (isLimitOrderWithdraw) { + return + } + if (requiresApprove) { return } diff --git a/src/trading-widget/components/withdraw/init-step/meta/meta.tsx b/src/trading-widget/components/withdraw/init-step/meta/meta.tsx index 567575a1..fd647b37 100644 --- a/src/trading-widget/components/withdraw/init-step/meta/meta.tsx +++ b/src/trading-widget/components/withdraw/init-step/meta/meta.tsx @@ -1,5 +1,6 @@ import type { FC, PropsWithChildren } from 'react' +import { useIsLimitOrderWithdraw } from 'core-kit/hooks/trading/limit-order-withdraw/use-is-limit-order-withdraw' import { Layout } from 'trading-widget/components/common' import { InitWithdrawTransactionOverviewDisclosure } from 'trading-widget/components/withdraw/init-step/meta/transaction-disclosure/transaction-disclosure' @@ -7,19 +8,29 @@ import { useLeveragedWithdrawalChecks } from 'trading-widget/hooks/use-leveraged import { useComponentContext } from 'trading-widget/providers/component-provider' export const InitWithdrawMeta: FC = ({ children }) => { - const { WithdrawMetaInfo, AvailableLiquidityAlert } = useComponentContext() + const { WithdrawMetaInfo, AvailableLiquidityAlert, OnDemandWithdrawAlert } = + useComponentContext() const { requiresLeveragedCollateralLiquidity, leveragedCollateralValueFormatted, } = useLeveragedWithdrawalChecks() + const isLimitOrderWithdraw = useIsLimitOrderWithdraw() return ( - {requiresLeveragedCollateralLiquidity && !!AvailableLiquidityAlert && ( - - )} +
+ {!requiresLeveragedCollateralLiquidity ? null : isLimitOrderWithdraw ? ( + <>{!!OnDemandWithdrawAlert && } + ) : ( + <> + {!!AvailableLiquidityAlert && ( + + )} + + )} +
{WithdrawMetaInfo && }
diff --git a/src/trading-widget/components/withdraw/stepper/withdraw-stepper.hooks.tsx b/src/trading-widget/components/withdraw/stepper/withdraw-stepper.hooks.tsx index 9d27f623..5b2c1e08 100644 --- a/src/trading-widget/components/withdraw/stepper/withdraw-stepper.hooks.tsx +++ b/src/trading-widget/components/withdraw/stepper/withdraw-stepper.hooks.tsx @@ -1,5 +1,6 @@ import { useMemo } from 'react' +import { useIsLimitOrderWithdraw } from 'core-kit/hooks/trading/limit-order-withdraw/use-is-limit-order-withdraw' import { useIsCompleteWithdrawStep } from 'core-kit/hooks/trading/withdraw-v2/complete-step' import { useInitWithdrawAllowance, @@ -20,6 +21,7 @@ export const useWithdrawStepper = () => { const { canSpend } = useInitWithdrawAllowance() const isMultiAssetWithdraw = useIsMultiAssetWithdraw() const isUnrollAndClaimTransaction = useIsUnrollAndClaimTransaction() + const isLimitOrderWithdraw = useIsLimitOrderWithdraw() const steps = useMemo(() => { const steps = [ @@ -73,5 +75,9 @@ export const useWithdrawStepper = () => { ? INIT_STEP_INDEX : APPROVE_STEP_INDEX - return { hideStepper: isMultiAssetWithdraw, activeStepIndex, steps } + return { + hideStepper: isMultiAssetWithdraw || isLimitOrderWithdraw, + activeStepIndex, + steps, + } } diff --git a/src/trading-widget/providers/component-provider/component-provider.defaults.tsx b/src/trading-widget/providers/component-provider/component-provider.defaults.tsx index a6a6f943..8c5c486b 100644 --- a/src/trading-widget/providers/component-provider/component-provider.defaults.tsx +++ b/src/trading-widget/providers/component-provider/component-provider.defaults.tsx @@ -35,4 +35,13 @@ export const DEFAULT_COMPONENT_PROVIDER_COMPONENTS: ComponentProviderProps['conf

), + OnDemandWithdrawAlert: () => ( + +

On demand sell

+

+ Sell will happen automatically over time as liquidity is made + available +

+
+ ), } diff --git a/src/trading-widget/providers/component-provider/component-provider.types.ts b/src/trading-widget/providers/component-provider/component-provider.types.ts index 8f125b2e..b6d1f728 100644 --- a/src/trading-widget/providers/component-provider/component-provider.types.ts +++ b/src/trading-widget/providers/component-provider/component-provider.types.ts @@ -40,5 +40,6 @@ export interface ComponentProviderProps { ActionButton?: ComponentType> AvailableLiquidityAlert?: ComponentType MaxSupplyReachedAlert?: ComponentType + OnDemandWithdrawAlert?: ComponentType } } diff --git a/src/trading-widget/providers/translation-provider/translation-provider.defaults.ts b/src/trading-widget/providers/translation-provider/translation-provider.defaults.ts index 51a8e442..f8d31d2a 100644 --- a/src/trading-widget/providers/translation-provider/translation-provider.defaults.ts +++ b/src/trading-widget/providers/translation-provider/translation-provider.defaults.ts @@ -72,6 +72,11 @@ export const DEFAULT_TRANSLATION_DATA: TranslationMap = { termOfUseDepositAccept: 'Accept & Deposit', back: 'Back', done: 'Done', + termOfUseWithdrawAcceptLabel: 'I understand and accept the withdraw terms', + termOfUseWithdrawPoint1: + 'Withdrawal requests are intended to settle in USDC. In rare cases where market conditions or high slippage prevent a USDC swap, the order may be settled in the underlying tokens of the respective vault.', + termOfUseWithdrawPoint2: + 'Revoking token approval after placing an order will prevent the order from being executed.', highSlippage: 'High Slippage Alert', responsibleHighSlippage: 'By proceeding with this trade, you acknowledge and accept the possibility of experiencing high slippage, resulting in a potential difference between the expected and executed price.', @@ -111,6 +116,8 @@ export const DEFAULT_TRANSLATION_DATA: TranslationMap = { completeWithdrawTooltip: 'This final step swaps all assets to a single asset and sends it to your wallet.', unrollAndClaimDescription: 'Claim', + limitOrderWithdrawDescription: + 'You are about to create a withdrawal request. Your vault tokens will be withdrawn within a few minutes.', total: 'Total:', showAll: 'Show all', hide: 'Hide', @@ -128,4 +135,10 @@ export const DEFAULT_TRANSLATION_DATA: TranslationMap = { rate: 'Rate', exchangeRate: 'Exchange rate', cooldown: 'Cooldown', + delete: 'Delete', + withdrawalRequest: 'Withdrawal request', + deleteWithdrawalRequest: 'Delete withdrawal request', + insufficientBalance: 'Insufficient Balance', + expectToReceiveUsdcSoon: + 'Expect to receive your USDC over the next few minutes', } diff --git a/src/trading-widget/providers/translation-provider/translation-provider.types.ts b/src/trading-widget/providers/translation-provider/translation-provider.types.ts index bc537b1a..6ea98ed4 100644 --- a/src/trading-widget/providers/translation-provider/translation-provider.types.ts +++ b/src/trading-widget/providers/translation-provider/translation-provider.types.ts @@ -56,6 +56,9 @@ export type TranslationMap = { termOfUseDepositAccept: string back: string done: string + termOfUseWithdrawAcceptLabel: string + termOfUseWithdrawPoint1: string + termOfUseWithdrawPoint2: string highSlippage: string responsibleHighSlippage: string highSlippageListTitle: string @@ -91,6 +94,7 @@ export type TranslationMap = { completeWithdrawDescription: string completeWithdrawTooltip: string unrollAndClaimDescription: string + limitOrderWithdrawDescription: string total: string showAll: string hide: string @@ -107,6 +111,11 @@ export type TranslationMap = { rate: string exchangeRate: string cooldown: string + delete: string + withdrawalRequest: string + deleteWithdrawalRequest: string + insufficientBalance: string + expectToReceiveUsdcSoon: string [key: string]: string } diff --git a/src/trading-widget/types/overlay.types.ts b/src/trading-widget/types/overlay.types.ts index 3b728a1e..543e04a6 100644 --- a/src/trading-widget/types/overlay.types.ts +++ b/src/trading-widget/types/overlay.types.ts @@ -7,6 +7,7 @@ export const OVERLAY = { TRADING: 'TRADING', ERROR_NOTIFICATION: 'ERROR_NOTIFICATION', LIMIT_SELLS: 'LIMIT_SELLS', + LIMIT_ORDER_WITHDRAW: 'LIMIT_ORDER_WITHDRAW', } as const export type OverlayType = (typeof OVERLAY)[keyof typeof OVERLAY]