This repository has been archived by the owner on Sep 11, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature(walletkit-ui): userPreferences, loans and DeFiScanContext (#45)
* feature(walletkit-ui): userPreferences, loans and DeFiScanContext * feature(walletkit-ui): userPreferences, loans and DeFiScanContext
- Loading branch information
1 parent
2aaa7f8
commit 4e13280
Showing
12 changed files
with
1,364 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { EnvironmentNetwork } from "@waveshq/walletkit-core"; | ||
import React, { createContext, useContext, useMemo } from "react"; | ||
|
||
import { useNetworkContext } from "./NetworkContext"; | ||
|
||
interface DeFiScanContextI { | ||
getTransactionUrl: (txid: string, rawtx?: string) => string; | ||
getBlocksUrl: (blockCount: number) => string; | ||
getTokenUrl: (tokenId: number | string) => string; | ||
getAddressUrl: (address: string) => string; | ||
getVaultsUrl: (vaultId: string) => string; | ||
getAuctionsUrl: (vaultId: string, index: number) => string; | ||
getBlocksCountdownUrl: (blockCount: number) => string; | ||
} | ||
|
||
const DeFiScanContext = createContext<DeFiScanContextI>(undefined as any); | ||
const baseDefiScanUrl = "https://defiscan.live"; | ||
|
||
export function useDeFiScanContext(): DeFiScanContextI { | ||
return useContext(DeFiScanContext); | ||
} | ||
|
||
function getNetworkParams(network: EnvironmentNetwork): string { | ||
switch (network) { | ||
case EnvironmentNetwork.MainNet: | ||
// no-op: network param not required for MainNet | ||
return ""; | ||
case EnvironmentNetwork.TestNet: | ||
return `?network=${EnvironmentNetwork.TestNet}`; | ||
|
||
case EnvironmentNetwork.LocalPlayground: | ||
case EnvironmentNetwork.RemotePlayground: | ||
return `?network=${EnvironmentNetwork.RemotePlayground}`; | ||
default: | ||
return ""; | ||
} | ||
} | ||
|
||
export function getURLByNetwork( | ||
path: string, | ||
network: EnvironmentNetwork, | ||
id: number | string | ||
): string { | ||
return `${baseDefiScanUrl}/${path}/${id}${getNetworkParams(network)}`; | ||
} | ||
|
||
export function getTxURLByNetwork( | ||
network: EnvironmentNetwork, | ||
txid: string, | ||
rawtx?: string | ||
): string { | ||
let baseUrl = `${baseDefiScanUrl}/transactions/${txid}`; | ||
|
||
baseUrl += getNetworkParams(network); | ||
|
||
if (typeof rawtx === "string" && rawtx.length !== 0) { | ||
if (network === EnvironmentNetwork.MainNet) { | ||
baseUrl += `?rawtx=${rawtx}`; | ||
} else { | ||
baseUrl += `&rawtx=${rawtx}`; | ||
} | ||
} | ||
|
||
return baseUrl; | ||
} | ||
|
||
export function DeFiScanProvider( | ||
props: React.PropsWithChildren<any> | ||
): JSX.Element | null { | ||
const { network } = useNetworkContext(); | ||
const { children } = props; | ||
|
||
const context: DeFiScanContextI = useMemo( | ||
() => ({ | ||
getTransactionUrl: (txid: string, rawtx?: string): string => | ||
getTxURLByNetwork(network, txid, rawtx), | ||
getBlocksUrl: (blockCount: number) => | ||
getURLByNetwork("blocks", network, blockCount), | ||
getTokenUrl: (tokenId: number | string) => | ||
getURLByNetwork("tokens", network, tokenId), | ||
getAddressUrl: (address: string) => | ||
getURLByNetwork("address", network, address), | ||
getVaultsUrl: (vaultId: string) => | ||
getURLByNetwork("vaults", network, vaultId), | ||
getAuctionsUrl: (vaultId: string, index: number) => | ||
getURLByNetwork(`vaults/${vaultId}/auctions`, network, index), | ||
getBlocksCountdownUrl: (blockCount: number) => | ||
getURLByNetwork("blocks/countdown", network, blockCount), | ||
}), | ||
[network] | ||
); | ||
|
||
return ( | ||
<DeFiScanContext.Provider value={context}> | ||
{children} | ||
</DeFiScanContext.Provider> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from "./useCollateralizationRatio"; | ||
export * from "./useVaultStatus"; |
197 changes: 197 additions & 0 deletions
197
packages/walletkit-ui/src/hooks/useCollateralizationRatio.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
/* eslint-disable @typescript-eslint/no-shadow */ | ||
import BigNumber from "bignumber.js"; | ||
|
||
import { | ||
CollateralizationRatioProps, | ||
CollateralizationRatioStats, | ||
VaultStatus, | ||
} from "../store/types/VaultStatus"; | ||
|
||
export function useCollateralRatioStats({ | ||
colRatio, | ||
minColRatio, | ||
totalLoanAmount, | ||
totalCollateralValue, | ||
}: CollateralizationRatioProps): CollateralizationRatioStats { | ||
const atRiskThreshold = new BigNumber(minColRatio).multipliedBy(1.5); | ||
const liquidatedThreshold = new BigNumber(minColRatio).multipliedBy(1.25); | ||
const isInLiquidation = | ||
totalLoanAmount.gt(0) && colRatio.isLessThan(liquidatedThreshold); | ||
const isAtRisk = | ||
totalLoanAmount.gt(0) && colRatio.isLessThan(atRiskThreshold); | ||
return { | ||
atRiskThreshold, | ||
liquidatedThreshold, | ||
isInLiquidation, | ||
isAtRisk, | ||
isHealthy: !isInLiquidation && !isAtRisk && totalLoanAmount.gt(0), | ||
isReady: | ||
!isInLiquidation && | ||
!isAtRisk && | ||
totalLoanAmount.eq(0) && | ||
totalCollateralValue !== undefined && | ||
totalCollateralValue.gt(0), | ||
}; | ||
} | ||
|
||
// export function useCollateralizationRatioColor( | ||
// props: CollateralizationRatioProps | ||
// ): ThemedProps { | ||
// const style: ThemedProps = {}; | ||
// const stats = useCollateralRatioStats(props); | ||
|
||
// if (stats.isInLiquidation) { | ||
// style.light = tailwind("text-error-500"); | ||
// style.dark = tailwind("text-darkerror-500"); | ||
// } else if (stats.isAtRisk) { | ||
// style.light = tailwind("text-warning-500"); | ||
// style.dark = tailwind("text-darkwarning-500"); | ||
// } else if (stats.isHealthy) { | ||
// style.light = tailwind("text-success-500"); | ||
// style.dark = tailwind("text-darksuccess-500"); | ||
// } | ||
// return style; | ||
// } | ||
|
||
// export function getVaultStatusColor( | ||
// status: string, | ||
// isLight: boolean, | ||
// isText: boolean = false | ||
// ): string { | ||
// if (status === VaultStatus.NearLiquidation) { | ||
// return isText ? "text-red-v2" : getColor("red-v2"); | ||
// } else if (status === VaultStatus.AtRisk) { | ||
// return isText ? "text-orange-v2" : getColor("orange-v2"); | ||
// } else if (status === VaultStatus.Healthy || status === VaultStatus.Ready) { | ||
// return isText ? "text-green-v2" : getColor("green-v2"); | ||
// } | ||
// return isText | ||
// ? isLight | ||
// ? "text-mono-light-v2-500" | ||
// : "text-mono-dark-v2-500" | ||
// : getColor(isLight ? "mono-light-v2-300" : "mono-dark-v2-300"); | ||
// } | ||
|
||
export function getVaultStatusText(status: string): string { | ||
switch (status) { | ||
case VaultStatus.Ready: | ||
return "Ready"; | ||
case VaultStatus.Halted: | ||
return "Halted"; | ||
default: | ||
return "Empty"; | ||
} | ||
} | ||
|
||
export function useResultingCollateralizationRatioByCollateral({ | ||
collateralValue, | ||
collateralRatio, | ||
minCollateralRatio, | ||
totalLoanAmount, | ||
numOfColorBars = 6, | ||
totalCollateralValueInUSD, | ||
}: { | ||
collateralValue: string; | ||
collateralRatio: BigNumber; | ||
minCollateralRatio: BigNumber; | ||
totalLoanAmount: BigNumber; | ||
totalCollateralValue?: BigNumber; | ||
numOfColorBars?: number; | ||
totalCollateralValueInUSD: BigNumber; | ||
}): { | ||
resultingColRatio: BigNumber; | ||
displayedColorBars: number; | ||
} { | ||
const hasCollateralRatio = | ||
!new BigNumber(collateralRatio).isNaN() && | ||
new BigNumber(collateralRatio).isPositive(); | ||
const resultingColRatio = | ||
collateralValue === "" || | ||
!hasCollateralRatio || | ||
new BigNumber(collateralValue).isZero() | ||
? new BigNumber(collateralRatio) | ||
: totalCollateralValueInUSD.dividedBy(totalLoanAmount).multipliedBy(100); | ||
|
||
const numOfColorBarPerStatus = numOfColorBars / 3; // (3): liquidation, at risk, healthy | ||
const healthyThresholdRatio = 1.75; | ||
const atRiskThresholdRatio = 1.5; | ||
const liquidatedThresholdRatio = 1.25; | ||
const atRiskThreshold = new BigNumber(minCollateralRatio).multipliedBy( | ||
atRiskThresholdRatio | ||
); | ||
const liquidatedThreshold = new BigNumber(minCollateralRatio).multipliedBy( | ||
liquidatedThresholdRatio | ||
); | ||
|
||
const isAtRisk = | ||
totalLoanAmount.gt(0) && resultingColRatio.isLessThan(atRiskThreshold); | ||
const isInLiquidation = | ||
totalLoanAmount.gt(0) && resultingColRatio.isLessThan(liquidatedThreshold); | ||
const isHealthy = !isInLiquidation && !isAtRisk && totalLoanAmount.gt(0); | ||
|
||
const getRatio = (): number => { | ||
if (isHealthy) { | ||
return healthyThresholdRatio; | ||
} | ||
if (isAtRisk && !isInLiquidation) { | ||
return atRiskThresholdRatio; | ||
} | ||
return liquidatedThresholdRatio; | ||
}; | ||
|
||
const getColorBarsCount = ( | ||
numOfColorBarPerStatus: number, | ||
minCollateralRatio: BigNumber, | ||
resultingCollateralRatio: BigNumber, | ||
thresholdRatio: number, | ||
isHealthy: boolean | ||
): number => { | ||
let colorBarsCount = -1; | ||
let index = 1; | ||
while (colorBarsCount === -1 && index <= numOfColorBarPerStatus) { | ||
const colorBarMaxAmount = minCollateralRatio.plus( | ||
minCollateralRatio.multipliedBy( | ||
new BigNumber(thresholdRatio) | ||
.minus(1) | ||
.dividedBy(isHealthy ? 1 : numOfColorBarPerStatus) | ||
.times(index) // divide threshold to number of bars | ||
) | ||
); | ||
|
||
if (resultingCollateralRatio.isLessThanOrEqualTo(colorBarMaxAmount)) { | ||
colorBarsCount = index; | ||
} | ||
|
||
index += 1; | ||
} | ||
|
||
return colorBarsCount; | ||
}; | ||
|
||
const colorBarsCount = getColorBarsCount( | ||
numOfColorBarPerStatus, | ||
minCollateralRatio, | ||
resultingColRatio, | ||
getRatio(), | ||
isHealthy | ||
); | ||
|
||
let displayedColorBars = -1; | ||
|
||
if (resultingColRatio.isLessThanOrEqualTo(0)) { | ||
displayedColorBars = -1; | ||
} else if (isHealthy && colorBarsCount > 0) { | ||
displayedColorBars = colorBarsCount + numOfColorBarPerStatus * 2; | ||
} else if (isHealthy && colorBarsCount === -1) { | ||
displayedColorBars = numOfColorBars; // display full color bar | ||
} else if (isAtRisk && !isInLiquidation) { | ||
displayedColorBars = colorBarsCount + numOfColorBarPerStatus; | ||
} else { | ||
displayedColorBars = colorBarsCount; | ||
} | ||
|
||
return { | ||
displayedColorBars, | ||
resultingColRatio, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { LoanVaultState } from "@defichain/whale-api-client/dist/api/loan"; | ||
import BigNumber from "bignumber.js"; | ||
|
||
import { VaultHealthItem, VaultStatus } from "../store/types/VaultStatus"; | ||
import { useCollateralRatioStats } from "./useCollateralizationRatio"; | ||
|
||
export function useVaultStatus( | ||
status: LoanVaultState | undefined, | ||
collateralRatio: BigNumber, | ||
minColRatio: BigNumber, | ||
totalLoanAmount: BigNumber, | ||
totalCollateralValue: BigNumber | ||
): VaultHealthItem { | ||
const colRatio = collateralRatio.gte(0) ? collateralRatio : new BigNumber(0); | ||
const stats = useCollateralRatioStats({ | ||
colRatio, | ||
minColRatio, | ||
totalLoanAmount, | ||
totalCollateralValue, | ||
}); | ||
let vaultStatus: VaultStatus; | ||
if (status === LoanVaultState.FROZEN) { | ||
vaultStatus = VaultStatus.Halted; | ||
} else if (status === LoanVaultState.UNKNOWN) { | ||
vaultStatus = VaultStatus.Unknown; | ||
} else if (status === LoanVaultState.IN_LIQUIDATION) { | ||
vaultStatus = VaultStatus.Liquidated; | ||
} else if (stats.isInLiquidation) { | ||
vaultStatus = VaultStatus.NearLiquidation; | ||
} else if (stats.isAtRisk) { | ||
vaultStatus = VaultStatus.AtRisk; | ||
} else if (stats.isHealthy) { | ||
vaultStatus = VaultStatus.Healthy; | ||
} else if (stats.isReady) { | ||
vaultStatus = VaultStatus.Ready; | ||
} else { | ||
vaultStatus = VaultStatus.Empty; | ||
} | ||
return { | ||
status: vaultStatus, | ||
vaultStats: stats, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export * from "./contexts"; | ||
export * from "./hooks"; | ||
export * from "./store"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,8 @@ | ||
export * from "./block"; | ||
export * from "./loans"; | ||
export * from "./ocean"; | ||
export * from "./transaction_queue"; | ||
export * from "./types"; | ||
export * from "./userPreferences"; | ||
export * from "./wallet"; | ||
export * from "./website"; |
Oops, something went wrong.