@chakra-ui/icons#QuestionIcon TypeScript Examples
The following examples show how to use
@chakra-ui/icons#QuestionIcon.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: FusePoolCreatePage.tsx From rari-dApp with GNU Affero General Public License v3.0 | 4 votes |
PoolConfiguration = () => {
const { t } = useTranslation();
const toast = useToast();
const { fuse, address } = useRari();
const navigate = useNavigate();
const { isOpen, onOpen, onClose } = useDisclosure();
const [name, setName] = useState("");
const [isWhitelisted, setIsWhitelisted] = useState(false);
const [whitelist, setWhitelist] = useState<string[]>([]);
const [closeFactor, setCloseFactor] = useState(50);
const [liquidationIncentive, setLiquidationIncentive] = useState(8);
const [isUsingMPO, setIsUsingMPO] = useState(true)
const [customOracleAddress, setCustomOracleAddress] = useState('')
const [isCreating, setIsCreating] = useState(false);
const [activeStep, setActiveStep] = useState<number>(0);
const increaseActiveStep = (step: string) => {
setActiveStep(steps.indexOf(step));
};
const [needsRetry, setNeedsRetry] = useState<boolean>(false);
const [retryFlag, setRetryFlag] = useState<number>(1);
const [deployedPriceOracle, setDeployedPriceOracle] = useState<string>("");
const postDeploymentHandle = (priceOracle: string) => {
setDeployedPriceOracle(priceOracle);
};
const deployPool = async (
bigCloseFactor: string,
bigLiquidationIncentive: string,
options: any,
priceOracle: string
) => {
const [poolAddress] = await fuse.deployPool(
name,
isWhitelisted,
bigCloseFactor,
bigLiquidationIncentive,
priceOracle,
{},
options,
isWhitelisted ? whitelist : null
);
return poolAddress;
};
const onDeploy = async () => {
let priceOracle = deployedPriceOracle;
if (name === "") {
toast({
title: "Error!",
description: "You must specify a name for your Fuse pool!",
status: "error",
duration: 2000,
isClosable: true,
position: "top-right",
});
return;
}
if (isWhitelisted && whitelist.length < 2 ) {
toast({
title: "Error!",
description: "You must add an address to your whitelist!",
status:"error",
duration: 2000,
isClosable: true,
position: "top-right",
})
return
}
if (!isUsingMPO && !fuse.web3.utils.isAddress(customOracleAddress)) {
toast({
title: "Error!",
description: "You must add an address for your oracle or use the default oracle.",
status:"error",
duration: 2000,
isClosable: true,
position: "top-right",
})
return
}
setIsCreating(true);
onOpen();
// 50% -> 0.5 * 1e18
const bigCloseFactor = new BigNumber(closeFactor)
.dividedBy(100)
.multipliedBy(1e18)
.toFixed(0);
// 8% -> 1.08 * 1e8
const bigLiquidationIncentive = new BigNumber(liquidationIncentive)
.dividedBy(100)
.plus(1)
.multipliedBy(1e18)
.toFixed(0);
let _retryFlag = retryFlag;
try {
const options = { from: address };
setNeedsRetry(false);
if (!isUsingMPO && _retryFlag === 1) {
_retryFlag = 2;
priceOracle = customOracleAddress
}
if (_retryFlag === 1) {
priceOracle = await fuse.deployPriceOracle(
"MasterPriceOracle",
{
underlyings: [],
oracles: [],
canAdminOverwrite: true,
defaultOracle:
Fuse.PUBLIC_PRICE_ORACLE_CONTRACT_ADDRESSES.MasterPriceOracle, // We give the MasterPriceOracle a default "fallback" oracle of the Rari MasterPriceOracle
},
options
);
postDeploymentHandle(priceOracle);
increaseActiveStep("Deploying Pool!");
_retryFlag = 2;
}
let poolAddress: string;
if (_retryFlag === 2) {
poolAddress = await deployPool(
bigCloseFactor,
bigLiquidationIncentive,
options,
priceOracle
);
const event = (
await fuse.contracts.FusePoolDirectory.getPastEvents(
"PoolRegistered",
{
fromBlock: (await fuse.web3.eth.getBlockNumber()) - 10,
toBlock: "latest",
}
)
).filter(
(event) =>
event.returnValues.pool.comptroller.toLowerCase() ===
poolAddress.toLowerCase()
)[0];
LogRocket.track("Fuse-CreatePool");
toast({
title: "Your pool has been deployed!",
description: "You may now add assets to it.",
status: "success",
duration: 2000,
isClosable: true,
position: "top-right",
});
let id = event.returnValues.index;
onClose();
navigate(`/fuse/pool/${id}/edit`);
}
} catch (e) {
handleGenericError(e, toast);
setRetryFlag(_retryFlag);
setNeedsRetry(true);
}
};
return (
<>
<TransactionStepperModal
handleRetry={onDeploy}
needsRetry={needsRetry}
activeStep={activeStep}
isOpen={isOpen}
onClose={onClose}
/>
<DashboardBox width="100%" mt={4}>
<Column mainAxisAlignment="flex-start" crossAxisAlignment="flex-start">
<Heading size="sm" px={4} py={4}>
{t("Create Pool")}
</Heading>
<ModalDivider />
<OptionRow>
<Text fontWeight="bold" mr={4}>
{t("Name")}
</Text>
<Input
width="20%"
value={name}
onChange={(event) => setName(event.target.value)}
/>
</OptionRow>
<ModalDivider />
<ModalDivider />
<OptionRow>
<SimpleTooltip
label={t(
"If enabled you will be able to limit the ability to supply to the pool to a select group of addresses. The pool will not show up on the 'all pools' list."
)}
>
<Text fontWeight="bold">
{t("Whitelisted")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
<Switch
h="20px"
isChecked={isWhitelisted}
onChange={() => {
setIsWhitelisted((past) => !past);
// Add the user to the whitelist by default
if (whitelist.length === 0) {
setWhitelist([address]);
}
}}
className="black-switch"
colorScheme="#121212"
/>
</OptionRow>
{isWhitelisted ? (
<WhitelistInfo
whitelist={whitelist}
addToWhitelist={(user) => {
setWhitelist((past) => [...past, user]);
}}
removeFromWhitelist={(user) => {
setWhitelist((past) =>
past.filter(function (item) {
return item !== user;
})
);
}}
/>
) : null}
<ModalDivider />
<OptionRow>
<SimpleTooltip
label={t(
"The percent, ranging from 0% to 100%, of a liquidatable account's borrow that can be repaid in a single liquidate transaction. If a user has multiple borrowed assets, the closeFactor applies to any single borrowed asset, not the aggregated value of a user’s outstanding borrowing. Compound's close factor is 50%."
)}
>
<Text fontWeight="bold">
{t("Close Factor")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
<SliderWithLabel
value={closeFactor}
setValue={setCloseFactor}
formatValue={formatPercentage}
min={5}
max={90}
/>
</OptionRow>
<ModalDivider />
<OptionRow>
<SimpleTooltip
label={t(
"The additional collateral given to liquidators as an incentive to perform liquidation of underwater accounts. For example, if the liquidation incentive is 10%, liquidators receive an extra 10% of the borrowers collateral for every unit they close. Compound's liquidation incentive is 8%."
)}
>
<Text fontWeight="bold">
{t("Liquidation Incentive")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
<SliderWithLabel
value={liquidationIncentive}
setValue={setLiquidationIncentive}
formatValue={formatPercentage}
min={0}
max={50}
/>
</OptionRow>
<ModalDivider />
<OptionRow>
<SimpleTooltip
label={t(
"We will deploy a price oracle for your pool. This price oracle will contain price feeds for popular ERC20 tokens."
)}
>
<Text fontWeight="bold">
{isUsingMPO ? t("Default Price Oracle") : t("Custom Price Oracle")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
<Box display="flex" alignItems='flex-end' flexDirection="column">
<Checkbox
isChecked={isUsingMPO}
onChange={(e) => setIsUsingMPO(!isUsingMPO)}
marginBottom={3}
/>
{
!isUsingMPO ? (
<>
<Input
value={customOracleAddress}
onChange={(e) => setCustomOracleAddress(e.target.value)}
/>
<Text mt={3} opacity="0.6" fontSize="sm">
Please make sure you know what you're doing.
</Text>
</>
)
: null
}
</Box>
</OptionRow>
</Column>
</DashboardBox>
<DashboardBox
width="100%"
height="60px"
mt={4}
py={3}
fontSize="xl"
as="button"
onClick={useAuthedCallback(onDeploy)}
>
<Center expand fontWeight="bold">
{isCreating ? <Spinner /> : t("Create")}
</Center>
</DashboardBox>
</>
);
}
Example #2
Source File: AssetConfig.tsx From rari-dApp with GNU Affero General Public License v3.0 | 4 votes |
AssetConfig = () => {
const queryClient = useQueryClient();
const { fuse, address } = useRari();
const { t } = useTranslation();
const toast = useToast();
const {
cTokenData,
collateralFactor,
setCollateralFactor,
cTokenAddress,
isBorrowPaused,
adminFee,
setAdminFee,
activeOracleModel,
oracleData,
tokenAddress,
mode,
setInterestRateModel,
interestRateModel,
tokenData,
setReserveFactor,
reserveFactor,
comptrollerAddress,
} = useAddAssetContext();
const curves = useIRMCurves({ interestRateModel, adminFee, reserveFactor });
// Liquidation incentive. (This is configured at pool level)
const liquidationIncentiveMantissa =
useLiquidationIncentive(comptrollerAddress);
const scaleCollateralFactor = (_collateralFactor: number) => {
return _collateralFactor / 1e16;
};
const scaleReserveFactor = (_reserveFactor: number) => {
return _reserveFactor / 1e16;
};
const scaleAdminFee = (_adminFee: number) => {
return _adminFee / 1e16;
};
// Updates asset's Interest Rate Model.
const updateInterestRateModel = async () => {
const cToken = createCToken(fuse, cTokenAddress!);
try {
await testForCTokenErrorAndSend(
cToken.methods._setInterestRateModel(interestRateModel),
address,
""
);
LogRocket.track("Fuse-UpdateInterestRateModel");
queryClient.refetchQueries();
} catch (e) {
handleGenericError(e, toast);
}
};
// Determines if users can borrow an asset or not.
const togglePause = async () => {
const comptroller = createComptroller(comptrollerAddress, fuse);
try {
await comptroller.methods
._setBorrowPaused(cTokenAddress, !isBorrowPaused)
.send({ from: address });
LogRocket.track("Fuse-PauseToggle");
queryClient.refetchQueries();
} catch (e) {
handleGenericError(e, toast);
}
};
// Updates loan to Value ratio.
const updateCollateralFactor = async () => {
const comptroller = createComptroller(comptrollerAddress, fuse);
// 70% -> 0.7 * 1e18
const bigCollateralFactor = new BigNumber(collateralFactor)
.dividedBy(100)
.multipliedBy(1e18)
.toFixed(0);
try {
await testForComptrollerErrorAndSend(
comptroller.methods._setCollateralFactor(
cTokenAddress,
bigCollateralFactor
),
address,
""
);
LogRocket.track("Fuse-UpdateCollateralFactor");
queryClient.refetchQueries();
} catch (e) {
handleGenericError(e, toast);
}
};
// Updated portion of accrued reserves that goes into reserves.
const updateReserveFactor = async () => {
const cToken = createCToken(fuse, cTokenAddress!);
// 10% -> 0.1 * 1e18
const bigReserveFactor = new BigNumber(reserveFactor)
.dividedBy(100)
.multipliedBy(1e18)
.toFixed(0);
try {
await testForCTokenErrorAndSend(
cToken.methods._setReserveFactor(bigReserveFactor),
address,
""
);
LogRocket.track("Fuse-UpdateReserveFactor");
queryClient.refetchQueries();
} catch (e) {
handleGenericError(e, toast);
}
};
// Updates asset's admin fee.
const updateAdminFee = async () => {
const cToken = createCToken(fuse, cTokenAddress!);
// 5% -> 0.05 * 1e18
const bigAdminFee = new BigNumber(adminFee)
.dividedBy(100)
.multipliedBy(1e18)
.toFixed(0);
try {
await testForCTokenErrorAndSend(
cToken.methods._setAdminFee(bigAdminFee),
address,
""
);
LogRocket.track("Fuse-UpdateAdminFee");
queryClient.refetchQueries();
} catch (e) {
handleGenericError(e, toast);
}
};
return (
<>
<Column
width={mode === "Editing" ? "100%" : "60%"}
maxWidth={mode === "Editing" ? "100%" : "50%"}
height="100%"
overflowY="auto"
mainAxisAlignment={mode === "Adding" ? "center" : "flex-start"}
crossAxisAlignment={mode === "Adding" ? "center" : "flex-start"}
>
{mode === "Editing" ? (
<>
<MarketCapConfigurator
mode="Borrow"
tokenData={tokenData}
cTokenAddress={cTokenAddress}
comptrollerAddress={comptrollerAddress}
/>
<ModalDivider />
<MarketCapConfigurator
mode="Supply"
tokenData={tokenData}
cTokenAddress={cTokenAddress}
comptrollerAddress={comptrollerAddress}
/>
</>
) : null}
<ModalDivider />
<ConfigRow height="35px">
<SimpleTooltip
label={t(
"Collateral factor can range from 0-90%, and represents the proportionate increase in liquidity (borrow limit) that an account receives by depositing the asset."
)}
>
<Text fontWeight="bold">
{t("Collateral Factor")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
{cTokenData !== undefined &&
mode === "Editing" &&
collateralFactor !==
scaleCollateralFactor(cTokenData?.collateralFactorMantissa) ? (
<SaveButton ml={3} onClick={updateCollateralFactor} />
) : null}
<SliderWithLabel
ml="auto"
value={collateralFactor}
setValue={setCollateralFactor}
formatValue={formatPercentage}
step={0.5}
max={
liquidationIncentiveMantissa
? // 100% CF - Liquidation Incentive (ie: 8%) - 5% buffer
100 -
(liquidationIncentiveMantissa.toString() / 1e16 - 100) -
5
: 90
}
/>
</ConfigRow>
<ModalDivider />
{cTokenAddress ? (
<ConfigRow>
<SimpleTooltip
label={t("If enabled borrowing this asset will be disabled.")}
>
<Text fontWeight="bold">
{t("Pause Borrowing")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
<SaveButton
ml="auto"
onClick={togglePause}
fontSize="xs"
altText={
isBorrowPaused ? t("Enable Borrowing") : t("Pause Borrowing")
}
/>
</ConfigRow>
) : null}
<ModalDivider />
<ConfigRow height="35px">
<SimpleTooltip
label={t(
"The fraction of interest generated on a given asset that is routed to the asset's Reserve Pool. The Reserve Pool protects lenders against borrower default and liquidation malfunction."
)}
>
<Text fontWeight="bold">
{t("Reserve Factor")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
{cTokenData &&
reserveFactor !==
scaleReserveFactor(cTokenData.reserveFactorMantissa) ? (
<SaveButton ml={3} onClick={updateReserveFactor} />
) : null}
<SliderWithLabel
ml="auto"
value={reserveFactor}
setValue={setReserveFactor}
formatValue={formatPercentage}
max={50}
/>
</ConfigRow>
<ModalDivider />
<ConfigRow height="35px">
<SimpleTooltip
label={t(
"The fraction of interest generated on a given asset that is routed to the asset's admin address as a fee."
)}
>
<Text fontWeight="bold">
{t("Admin Fee")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
{cTokenData &&
adminFee !== scaleAdminFee(cTokenData.adminFeeMantissa) ? (
<SaveButton ml={3} onClick={updateAdminFee} />
) : null}
<SliderWithLabel
ml="auto"
value={adminFee}
setValue={setAdminFee}
formatValue={formatPercentage}
max={30}
/>
</ConfigRow>
<ModalDivider />
{(activeOracleModel === "MasterPriceOracleV2" ||
activeOracleModel === "MasterPriceOracleV3") &&
oracleData !== undefined &&
!isTokenETHOrWETH(tokenAddress) &&
mode === "Editing" && (
<>
<OracleConfig />
<ModalDivider />
</>
)}
<ModalDivider />
<ConfigRow>
<SimpleTooltip
label={t(
"The interest rate model chosen for an asset defines the rates of interest for borrowers and suppliers at different utilization levels."
)}
>
<Text fontWeight="bold">
{t("Interest Model")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
<Select
{...DASHBOARD_BOX_PROPS}
ml="auto"
borderRadius="7px"
fontWeight="bold"
_focus={{ outline: "none" }}
width="260px"
value={interestRateModel.toLowerCase()}
onChange={(event) => setInterestRateModel(event.target.value)}
>
{Object.entries(
Fuse.PUBLIC_INTEREST_RATE_MODEL_CONTRACT_ADDRESSES
).map(([key, value]) => {
return (
<option
className="black-bg-option"
value={value.toLowerCase()}
key={key}
>
{key}
</option>
);
})}
</Select>
{cTokenData &&
cTokenData.interestRateModelAddress.toLowerCase() !==
interestRateModel.toLowerCase() ? (
<SaveButton
height="40px"
borderRadius="7px"
onClick={updateInterestRateModel}
/>
) : null}
</ConfigRow>
{mode === "Editing" && (
<IRMChart curves={curves} tokenData={tokenData} />
)}
</Column>
{mode === "Adding" ? (
<Column
width="40%"
height="100%"
overflowY="auto"
mainAxisAlignment={mode === "Adding" ? "center" : "flex-start"}
crossAxisAlignment={mode === "Adding" ? "center" : "flex-start"}
>
<IRMChart curves={curves} tokenData={tokenData} />
<Text textAlign="center">
{fuse
.identifyInterestRateModelName(interestRateModel)
.replace("_", " ")}
</Text>
</Column>
) : null}
</>
);
}
Example #3
Source File: BaseTokenOracleConfig.tsx From rari-dApp with GNU Affero General Public License v3.0 | 4 votes |
BaseTokenOracleConfig = () => {
const { t } = useTranslation();
const { address } = useRari();
const {
mode,
oracleData,
uniV3BaseTokenAddress,
uniV3BaseTokenOracle,
setUniV3BaseTokenOracle,
baseTokenActiveOracleName,
setBaseTokenActiveOracleName,
} = useAddAssetContext();
const isUserAdmin = address === oracleData?.admin ?? false;
// We get all oracle options.
const options = useGetOracleOptions(oracleData, uniV3BaseTokenAddress);
console.log("helo there", { options });
// If we're editing the asset, show master price oracle as a default.
// Should run only once, when component renders.
useEffect(() => {
if (
mode === "Editing" &&
baseTokenActiveOracleName === "" &&
options &&
options["Current_Price_Oracle"]
)
setBaseTokenActiveOracleName("Current_Price_Oracle");
}, [mode, baseTokenActiveOracleName, options, setBaseTokenActiveOracleName]);
// This will update the oracle address, after user chooses which options they want to use.
// If option is Custom_Oracle oracle address is typed in by user, so we dont trigger this.
useEffect(() => {
if (
!!baseTokenActiveOracleName &&
baseTokenActiveOracleName !== "Custom_Oracle" &&
options
)
setUniV3BaseTokenOracle(options[baseTokenActiveOracleName]);
}, [baseTokenActiveOracleName, options, setUniV3BaseTokenOracle]);
return (
<>
<Row
crossAxisAlignment="center"
mainAxisAlignment="center"
width="100%"
my={2}
>
<Alert status="info" width="80%" height="50px" borderRadius={5} my={1}>
<AlertIcon />
<Text fontSize="sm" align="center" color="black">
{"This Uniswap V3 TWAP Oracle needs an oracle for the BaseToken."}
</Text>
</Alert>
</Row>
<Column
mainAxisAlignment="flex-start"
crossAxisAlignment="center"
w="100%"
h="100%"
>
<Column
my={4}
width="100%"
crossAxisAlignment="center"
mainAxisAlignment="space-between"
>
<Column
mainAxisAlignment="center"
crossAxisAlignment="center"
height="50%"
justifyContent="space-around"
>
<CTokenIcon address={uniV3BaseTokenAddress} boxSize={"50px"} />
<SimpleTooltip
label={t("Choose the best price oracle for this BaseToken.")}
>
<Text fontWeight="bold" fontSize="sm" align="center">
{t("BaseToken Price Oracle")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
</Column>
{options ? (
<Box alignItems="center" height="50%">
<Select
{...DASHBOARD_BOX_PROPS}
ml="auto"
my={2}
borderRadius="7px"
_focus={{ outline: "none" }}
width="260px"
placeholder={
baseTokenActiveOracleName.length === 0
? t("Choose Oracle")
: baseTokenActiveOracleName.replaceAll("_", " ")
}
value={baseTokenActiveOracleName.toLowerCase()}
disabled={
!isUserAdmin ||
(!oracleData?.adminOverwrite &&
!options.Current_Price_Oracle === null)
}
onChange={(event) =>
setBaseTokenActiveOracleName(event.target.value)
}
>
{Object.entries(options).map(([key, value]) =>
value !== null &&
value !== undefined &&
key !== "Uniswap_V3_Oracle" &&
key !== "Uniswap_V2_Oracle" ? (
<option className="black-bg-option" value={key} key={key}>
{key.replaceAll("_", " ")}
</option>
) : null
)}
</Select>
{baseTokenActiveOracleName.length > 0 ? (
<Input
width="100%"
textAlign="center"
height="40px"
variant="filled"
size="sm"
mt={2}
mb={2}
value={uniV3BaseTokenOracle}
onChange={(event) => {
const address = event.target.value;
setUniV3BaseTokenOracle(address);
}}
disabled={
baseTokenActiveOracleName === "Custom_Oracle" ? false : true
}
{...DASHBOARD_BOX_PROPS}
_placeholder={{ color: "#e0e0e0" }}
_focus={{ bg: "#121212" }}
_hover={{ bg: "#282727" }}
bg="#282727"
/>
) : null}
</Box>
) : null}
</Column>
</Column>
</>
);
}
Example #4
Source File: OracleConfig.tsx From rari-dApp with GNU Affero General Public License v3.0 | 4 votes |
OracleConfig = () => {
const toast = useToast();
const { t } = useTranslation();
const queryClient = useQueryClient();
const { fuse, address } = useRari();
const {
mode,
feeTier,
oracleData,
activeOracleModel,
tokenAddress,
oracleAddress,
oracleTouched,
uniV3BaseTokenAddress,
setOracleTouched,
activeUniSwapPair,
setActiveOracleModel,
setOracleAddress,
poolOracleAddress,
uniV3BaseTokenOracle,
baseTokenActiveOracleName,
shouldShowUniV3BaseTokenOracleForm,
} = useAddAssetContext();
const isUserAdmin = !!oracleData ? address === oracleData.admin : false;
// Available oracle options for asset
const options = useGetOracleOptions(oracleData, tokenAddress);
// Identify token oracle address
const oracleIdentity = useIdentifyOracle(oracleAddress);
const [inputTouched, setInputTouched] = useState(false);
// If user's editing the asset's properties, show the Ctoken's active Oracle
useEffect(() => {
// Map oracleIdentity to whatever the type of `activeOracle` can be
// "Current_Price_Oracle" would only be avialable if you are editing
if (
mode === "Editing" &&
options &&
options["Current_Price_Oracle"] &&
!oracleTouched
) {
setActiveOracleModel("Current_Price_Oracle");
}
// if avaiable, set to "Default_Price_Oracle" if you are adding
if (
mode === "Adding" &&
options &&
!!options["Default_Price_Oracle"] &&
!oracleTouched
) {
setActiveOracleModel("Default_Price_Oracle");
}
// if avaiable, set to "Default_Price_Oracle" if you are adding
if (
mode === "Adding" &&
options &&
!!options["Current_Price_Oracle"] &&
!oracleTouched
) {
setActiveOracleModel("Current_Price_Oracle");
}
}, [
mode,
activeOracleModel,
options,
setActiveOracleModel,
oracleIdentity,
oracleTouched,
]);
// Update the oracle address, after user chooses which option they want to use.
// If option is Custom_Oracle or Uniswap_V3_Oracle, oracle address is changed differently so we dont trigger this.
useEffect(() => {
if (
activeOracleModel.length > 0 &&
activeOracleModel !== "Custom_Oracle" &&
activeOracleModel !== "Uniswap_V3_Oracle" &&
activeOracleModel !== "Uniswap_V2_Oracle" &&
activeOracleModel !== "SushiSwap_Oracle" &&
options
)
setOracleAddress(options[activeOracleModel]);
if (
activeUniSwapPair === "" &&
(activeOracleModel === "Custom_Oracle" ||
activeOracleModel === "Uniswap_V3_Oracle" ||
activeOracleModel === "Uniswap_V2_Oracle" ||
activeOracleModel === "SushiSwap_Oracle") &&
!inputTouched
)
setOracleAddress("");
}, [activeOracleModel, options, setOracleAddress, activeUniSwapPair]);
// Will update oracle for the asset. This is used only if user is editing asset.
const updateOracle = async () => {
const poolOracleContract = createOracle(
poolOracleAddress,
fuse,
"MasterPriceOracle"
);
// This variable will change if we deploy an oracle. (i.e TWAP Oracles)
// If we're using an option that has been deployed it stays the same.
let oracleAddressToUse = oracleAddress;
try {
if (options === null) return null;
// If activeOracle if a TWAP Oracle
if (activeOracleModel === "Uniswap_V3_Oracle") {
// Check for observation cardinality and fix if necessary
await fuse.primeUniswapV3Oracle(oracleAddressToUse, { from: address });
// Deploy oracle
oracleAddressToUse = await fuse.deployPriceOracle(
"UniswapV3TwapPriceOracleV2",
{
feeTier,
baseToken: uniV3BaseTokenAddress,
},
{ from: address }
);
}
const tokenArray =
shouldShowUniV3BaseTokenOracleForm &&
!isTokenETHOrWETH(uniV3BaseTokenAddress)
? [tokenAddress, uniV3BaseTokenAddress]
: [tokenAddress];
const oracleAddressArray =
shouldShowUniV3BaseTokenOracleForm &&
!isTokenETHOrWETH(uniV3BaseTokenAddress)
? [oracleAddressToUse, uniV3BaseTokenOracle]
: [oracleAddressToUse];
console.log({ tokenArray, oracleAddressArray });
// Add oracle to Master Price Oracle
await poolOracleContract.methods
.add(tokenArray, oracleAddressArray)
.send({ from: address });
queryClient.refetchQueries();
// Wait 2 seconds for refetch and then close modal.
// We do this instead of waiting the refetch because some refetches take a while or error out and we want to close now.
await new Promise((resolve) => setTimeout(resolve, 2000));
toast({
title: "You have successfully updated the oracle to this asset!",
description: "Oracle will now point to the new selected address.",
status: "success",
duration: 2000,
isClosable: true,
position: "top-right",
});
setActiveOracleModel("Current_Price_Oracle");
setOracleAddress(options["Current_Price_Oracle"]);
} catch (e) {
handleGenericError(e, toast);
}
};
if (!options)
return (
<Center>
<Spinner />
</Center>
);
return (
<>
<Row
mainAxisAlignment={mode === "Editing" ? "space-between" : "flex-start"}
// background="gold"
crossAxisAlignment={"center"}
width={
mode === "Editing"
? !shouldShowUniV3BaseTokenOracleForm
? "100%"
: "50%"
: "100%"
}
flexGrow={1}
pt={mode === "Editing" ? 4 : 0}
pb={mode === "Editing" ? 1 : 0}
px={mode === "Editing" ? 4 : 0}
id="PRICEORACLE"
>
<SimpleTooltip label={t("Choose the best price oracle for the asset.")}>
<Text fontWeight="bold">
{t("Price Oracle")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
{/* Oracles */}
<Box
width={mode === "Editing" ? "50%" : "100%"}
alignItems="flex-end"
flexDirection="column"
alignContent="center"
display="flex"
>
<Select
mb={2}
ml="auto"
width="260px"
{...DASHBOARD_BOX_PROPS}
borderRadius="7px"
_focus={{ outline: "none" }}
value={activeOracleModel.toLowerCase()}
onChange={(event) => {
// if (mode === "Editing") {
// }
setOracleTouched(true);
setActiveOracleModel(event.target.value);
}}
placeholder={
activeOracleModel.length === 0
? t("Choose Oracle")
: activeOracleModel.replaceAll("_", " ")
}
disabled={
!isUserAdmin ||
(!oracleData?.adminOverwrite &&
!options.Current_Price_Oracle === null)
}
>
{Object.entries(options).map(([key, value]) =>
value !== null &&
value !== undefined &&
key !== activeOracleModel &&
(mode === "Adding" ? key !== "Current_Price_Oracle" : true) ? (
// dont show the selected choice in the list
<option key={key} value={key} className="black-bg-option">
{key.replaceAll("_", " ")}
</option>
) : null
)}
{/* <option disabled={true}>Loading...</option> */}
</Select>
{activeOracleModel.length > 0 ? (
<Input
mt={2}
mb={2}
ml="auto"
size="sm"
bg="#282727"
height="40px"
width="260px"
variant="filled"
textAlign="center"
value={oracleAddress}
onChange={(event) => {
const address = event.target.value;
setInputTouched(true);
setOracleAddress(address);
}}
{...DASHBOARD_BOX_PROPS}
_focus={{ bg: "#121212" }}
_hover={{ bg: "#282727" }}
_placeholder={{ color: "#e0e0e0" }}
disabled={activeOracleModel === "Custom_Oracle" ? false : true}
/>
) : null}
<Text color="grey" fontSize="sm" textAlign="center">
{oracleIdentity}
</Text>
{activeOracleModel === "Custom_Oracle" && (
<Text color="red" fontSize="sm" textAlign="center">
Make sure you know what you are doing!
</Text>
)}
</Box>
</Row>
<Row
mainAxisAlignment={mode === "Editing" ? "center" : "center"}
crossAxisAlignment={mode === "Editing" ? "flex-start" : "center"}
flexDirection="column"
width={
mode === "Adding" && !shouldShowUniV3BaseTokenOracleForm
? "100%"
: "50%"
}
// bg="pink"
ml={mode === "Editing" ? "auto" : ""}
px={mode === "Editing" ? 4 : 0}
id="UNIV3Config"
>
{activeOracleModel === "Uniswap_V3_Oracle" ? (
<UniswapV3PriceOracleConfigurator />
) : null}
{activeOracleModel === "Uniswap_V2_Oracle" ? (
<UniswapV2OrSushiPriceOracleConfigurator type="UniswapV2" />
) : null}
{activeOracleModel === "SushiSwap_Oracle" ? (
<UniswapV2OrSushiPriceOracleConfigurator type="Sushiswap" />
) : null}
</Row>
{shouldShowUniV3BaseTokenOracleForm && mode === "Editing" ? (
<BaseTokenOracleConfig />
) : null}
{activeOracleModel !== "Current_Price_Oracle" && mode === "Editing" ? (
<SaveButton
ml={"auto"}
mb={3}
mr={4}
fontSize="xs"
altText={t("Update")}
onClick={updateOracle}
/>
) : null}
</>
);
}
Example #5
Source File: UniswapV2OrSushiPriceOracleConfigurator.tsx From rari-dApp with GNU Affero General Public License v3.0 | 4 votes |
UniswapV2OrSushiPriceOracleConfigurator = ({
type,
}: {
// Asset's Address. i.e DAI, USDC
// Either SushiSwap or Uniswap V2
type: string;
}) => {
const { t } = useTranslation();
// This will be used to index whitelistPools array (fetched from the graph.)
// It also helps us know if user has selected anything or not. If they have, detail fields are shown.
const [activePool, setActivePair] = useState<string>("");
// Checks if user has started the TWAP bot.
const [checked, setChecked] = useState<boolean>(false);
// Will store oracle response. This helps us know if its safe to add it to Master Price Oracle
const [checkedStepTwo, setCheckedStepTwo] = useState<boolean>(false);
const { tokenAddress, setOracleAddress, setUniV3BaseTokenAddress } =
useAddAssetContext();
// Get pair options from sushiswap and uniswap
const { SushiPairs, SushiError, UniV2Pairs, univ2Error } =
useSushiOrUniswapV2Pairs(tokenAddress);
// This is where we conditionally store data depending on type.
// Uniswap V2 or SushiSwap
const Pairs = type === "UniswapV2" ? UniV2Pairs : SushiPairs;
const Error = type === "UniswapV2" ? univ2Error : SushiError;
// Will update active pair, set oracle address and base token.
const updateInfo = (value: string) => {
const pair = Pairs[value];
setActivePair(value);
setOracleAddress(pair.id);
setUniV3BaseTokenAddress(
pair.token1.id === tokenAddress ? pair.token0.id : pair.token1.id
);
};
// If pairs are still being fetched, if theres and error or if there are none, return nothing.
if (Pairs === undefined || Error || Pairs === null) return null;
return (
<>
<Row
crossAxisAlignment="center"
mainAxisAlignment="space-between"
width="260px"
my={3}
>
<Checkbox isChecked={checked} onChange={() => setChecked(!checked)}>
<Text fontSize="xs" align="center">
Using this type of oracle requires you to run a TWAP bot.
</Text>
</Checkbox>
</Row>
{checked ? (
<Row
crossAxisAlignment="center"
mainAxisAlignment="space-between"
width="260px"
my={3}
>
<Button colorScheme="teal">Check</Button>
<Text fontSize="xs" align="center">
After deploying your oracle, you have to wait about 15 - 25 minutes
for the oracle to be set.
</Text>
</Row>
) : null}
{true ? (
<Row
crossAxisAlignment="center"
mainAxisAlignment="space-between"
width="260px"
my={2}
>
<SimpleTooltip
label={t(
"This field will determine which pool your oracle reads from. Its safer with more liquidity."
)}
>
<Text fontWeight="bold">
{t("Pool:")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
<Select
{...DASHBOARD_BOX_PROPS}
ml={2}
mb={2}
borderRadius="7px"
_focus={{ outline: "none" }}
width="180px"
placeholder={
activePool.length === 0 ? t("Choose Pool") : activePool
}
value={activePool}
disabled={!checked}
onChange={(event) => {
updateInfo(event.target.value);
}}
>
{typeof Pairs !== undefined
? Object.entries(Pairs).map(([key, value]: any[]) =>
value.totalSupply !== null &&
value.totalSupply !== undefined &&
value.totalSupply >= 100 ? (
<option
className="black-bg-option"
value={key}
key={value.id}
>
{`${value.token0.symbol} / ${
value.token1.symbol
} (${shortUsdFormatter(value.totalSupply)})`}
</option>
) : null
)
: null}
</Select>
</Row>
) : null}
{activePool.length > 0 ? (
<Row
crossAxisAlignment="center"
mainAxisAlignment="space-between"
width="260px"
my={2}
>
<SimpleTooltip label={t("TVL in pool as of this moment.")}>
<Text fontWeight="bold">
{t("Liquidity:")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
<h1>
{activePool !== ""
? smallUsdFormatter(Pairs[activePool].totalSupply)
: null}
</h1>
</Row>
) : null}
</>
);
}
Example #6
Source File: UniswapV3PriceOracleConfigurator.tsx From rari-dApp with GNU Affero General Public License v3.0 | 4 votes |
UniswapV3PriceOracleConfigurator = () => {
const { t } = useTranslation();
const {
setFeeTier,
tokenAddress,
setOracleAddress,
setUniV3BaseTokenAddress,
activeUniSwapPair,
setActiveUniSwapPair,
} = useAddAssetContext();
// We get a list of whitelistedPools from uniswap-v3's the graph.
const { data: liquidity, error } = useQuery(
"UniswapV3 pool liquidity for " + tokenAddress,
async () =>
(
await axios.post(
"https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3",
{
query: `{
token(id:"${tokenAddress.toLowerCase()}") {
whitelistPools {
id,
feeTier,
volumeUSD,
totalValueLockedUSD,
token0 {
symbol,
id,
name
},
token1 {
symbol,
id,
name
}
}
}
}`,
}
)
).data,
{ refetchOnMount: false }
);
// When user selects an option this function will be called.
// Active pool, fee Tier, and base token are updated and we set the oracle address to the address of the pool we chose.
const updateBoth = (value: string) => {
const uniPool = liquidity.data.token.whitelistPools[value];
const baseToken: string =
uniPool.token0.id.toLowerCase() === tokenAddress.toLocaleLowerCase()
? uniPool.token1.id
: uniPool.token0.id;
setActiveUniSwapPair(value);
setFeeTier(uniPool.feeTier);
setOracleAddress(uniPool.id);
setUniV3BaseTokenAddress(baseToken);
};
// If liquidity is undefined, theres an error or theres no token found return nothing.
if (liquidity === undefined || liquidity.data === undefined)
return null;
// Sort whitelisted pools by TVL. Greatest to smallest. Greater TVL is safer for users so we show it first.
// Filter out pools where volume is less than $100,000
const liquiditySorted = liquidity.data.token.whitelistPools.sort(
(a: any, b: any): any =>
parseInt(a.totalValueLockedUSD) > parseInt(b.totalValueLockedUSD) ? -1 : 1
);
// .filter((pool: any) => pool.volumeUSD >= 100000);
const selectedOracle = liquidity.data.token.whitelistPools[activeUniSwapPair];
console.log({ selectedOracle });
// const warning = useMemo(() => {
// if (selectedOracle.liquidityProviderCount <=100)
// }, [selectedOracle]);
return (
<>
<Column
crossAxisAlignment="flex-start"
mainAxisAlignment="flex-start"
width={"100%"}
my={2}
px={4}
ml={"auto"}
// bg="aqua"
id="UNIv3COLUMN"
>
<Row
crossAxisAlignment="center"
mainAxisAlignment="space-between"
w="100%"
// bg="green"
>
<SimpleTooltip
label={t(
"This field will determine which pool your oracle reads from. Its safer with more liquidity."
)}
>
<Text fontWeight="bold">
{t("Pool:")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
<Select
{...DASHBOARD_BOX_PROPS}
ml={2}
mb={2}
width="180px"
borderRadius="7px"
value={activeUniSwapPair}
_focus={{ outline: "none" }}
placeholder={
activeUniSwapPair === "" ? t("Choose Pool") : activeUniSwapPair
}
onChange={(event) => {
updateBoth(event.target.value);
}}
>
{typeof liquidity !== undefined
? Object.entries(liquiditySorted).map(([key, value]: any[]) =>
value.totalValueLockedUSD !== null &&
value.totalValueLockedUSD !== undefined &&
value.totalValueLockedUSD >= 100 ? (
<option
className="black-bg-option"
value={key}
key={value.id}
>
{`${value.token0.symbol} / ${
value.token1.symbol
} (${shortUsdFormatter(value.totalValueLockedUSD)})`}
</option>
) : null
)
: null}
</Select>
</Row>
{activeUniSwapPair !== "" ? (
<Column
mainAxisAlignment="flex-start"
crossAxisAlignment="flex-start"
>
<Row mainAxisAlignment="flex-start" crossAxisAlignment="flex-start">
<Alert
status="warning"
width="100%"
height="70px"
borderRadius={5}
my={1}
>
<AlertIcon />
<Text fontSize="sm" align="center" color="black">
{
"Make sure this Uniswap V3 Pool has full-range liquidity. If not, your pool could be compromised."
}
</Text>
</Alert>
</Row>
<Row
mainAxisAlignment="space-between"
crossAxisAlignment="center"
my={2}
w="100%"
// bg="pink"
>
<SimpleTooltip label={t("TVL in pool as of this moment.")}>
<Text fontWeight="bold">
{t("Liquidity:")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
<h1>
{activeUniSwapPair !== ""
? shortUsdFormatter(
liquidity.data.token.whitelistPools[activeUniSwapPair]
.totalValueLockedUSD
)
: null}
</h1>
</Row>
<Row
mainAxisAlignment="space-between"
crossAxisAlignment="center"
my={2}
w="100%"
// bg="pink"
>
<SimpleTooltip label={t("Volume of pool.")}>
<Text fontWeight="bold">
{t("Volume:")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
<h1>
{activeUniSwapPair !== ""
? shortUsdFormatter(
liquidity.data.token.whitelistPools[activeUniSwapPair]
.volumeUSD
)
: null}
</h1>
</Row>
{/* <Row
mainAxisAlignment="space-between"
crossAxisAlignment="center"
my={2}
w="100%"
// bg="pink"
>
<SimpleTooltip
label={t(
"The fee percentage for the pool on Uniswap (0.05%, 0.3%, 1%)"
)}
>
<Text fontWeight="bold">
{t("Fee Tier:")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
<Text>
%
{activeUniSwapPair !== ""
? liquidity.data.token.whitelistPools[activeUniSwapPair]
.feeTier / 10000
: null}
</Text>
</Row> */}
<Row
crossAxisAlignment="center"
mainAxisAlignment="center"
width="260px"
my={0}
>
<Link
href={`https://info.uniswap.org/#/pools/${liquidity.data.token.whitelistPools[activeUniSwapPair].id}`}
isExternal
>
Visit Pool in Uniswap
</Link>
</Row>
</Column>
) : null}
</Column>
</>
);
}
Example #7
Source File: AddAssetModal.tsx From rari-dApp with GNU Affero General Public License v3.0 | 4 votes |
AssetSettings = ({
poolName,
poolID,
tokenData,
comptrollerAddress,
cTokenAddress,
existingAssets,
closeModal,
}: {
poolName: string;
poolID: string;
comptrollerAddress: string;
tokenData: TokenData;
// Only for editing mode
cTokenAddress?: string;
// Only for add asset modal
existingAssets?: USDPricedFuseAsset[];
closeModal: () => any;
}) => {
const { t } = useTranslation();
const { fuse, address } = useRari();
const toast = useToast();
const queryClient = useQueryClient();
const [isDeploying, setIsDeploying] = useState(false);
const [collateralFactor, setCollateralFactor] = useState(50);
const [reserveFactor, setReserveFactor] = useState(10);
const [adminFee, setAdminFee] = useState(0);
const [isBorrowPaused, setIsBorrowPaused] = useState(false);
const scaleCollateralFactor = (_collateralFactor: number) => {
return _collateralFactor / 1e16;
};
const scaleReserveFactor = (_reserveFactor: number) => {
return _reserveFactor / 1e16;
};
const scaleAdminFee = (_adminFee: number) => {
return _adminFee / 1e16;
};
const [interestRateModel, setInterestRateModel] = useState(
Fuse.PUBLIC_INTEREST_RATE_MODEL_CONTRACT_ADDRESSES
.JumpRateModel_Cream_Stables_Majors
);
const { data: curves } = useQuery(
interestRateModel + adminFee + reserveFactor + " irm",
async () => {
const IRM = await fuse.identifyInterestRateModel(interestRateModel);
if (IRM === null) {
return null;
}
await IRM._init(
fuse.web3,
interestRateModel,
// reserve factor
reserveFactor * 1e16,
// admin fee
adminFee * 1e16,
// hardcoded 10% Fuse fee
0.1e18
);
return convertIRMtoCurve(IRM, fuse);
}
);
const deploy = async () => {
// If pool already contains this asset:
if (
existingAssets!.some(
(asset) => asset.underlyingToken === tokenData.address
)
) {
toast({
title: "Error!",
description: "You have already added this asset to this pool.",
status: "error",
duration: 2000,
isClosable: true,
position: "top-right",
});
return;
}
setIsDeploying(true);
// 50% -> 0.5 * 1e18
const bigCollateralFacotr = new BigNumber(collateralFactor)
.dividedBy(100)
.multipliedBy(1e18)
.toFixed(0);
// 10% -> 0.1 * 1e18
const bigReserveFactor = new BigNumber(reserveFactor)
.dividedBy(100)
.multipliedBy(1e18)
.toFixed(0);
// 5% -> 0.05 * 1e18
const bigAdminFee = new BigNumber(adminFee)
.dividedBy(100)
.multipliedBy(1e18)
.toFixed(0);
const conf: any = {
underlying: tokenData.address,
comptroller: comptrollerAddress,
interestRateModel,
initialExchangeRateMantissa: fuse.web3.utils.toBN(1e18),
// Ex: BOGGED USDC
name: poolName + " " + tokenData.name,
// Ex: fUSDC-456
symbol: "f" + tokenData.symbol + "-" + poolID,
decimals: 8,
};
try {
await fuse.deployAsset(
conf,
bigCollateralFacotr,
bigReserveFactor,
bigAdminFee,
{ from: address },
// TODO: Disable this. This bypasses the price feed check. Only using now because only trusted partners are deploying assets.
true
);
LogRocket.track("Fuse-DeployAsset");
queryClient.refetchQueries();
// Wait 2 seconds for refetch and then close modal.
// We do this instead of waiting the refetch because some refetches take a while or error out and we want to close now.
await new Promise((resolve) => setTimeout(resolve, 2000));
toast({
title: "You have successfully added an asset to this pool!",
description: "You may now lend and borrow with this asset.",
status: "success",
duration: 2000,
isClosable: true,
position: "top-right",
});
closeModal();
} catch (e) {
handleGenericError(e, toast);
}
};
const liquidationIncentiveMantissa =
useLiquidationIncentive(comptrollerAddress);
const cTokenData = useCTokenData(comptrollerAddress, cTokenAddress);
// Update values on refetch!
useEffect(() => {
if (cTokenData) {
setCollateralFactor(cTokenData.collateralFactorMantissa / 1e16);
setReserveFactor(cTokenData.reserveFactorMantissa / 1e16);
setAdminFee(cTokenData.adminFeeMantissa / 1e16);
setInterestRateModel(cTokenData.interestRateModelAddress);
setIsBorrowPaused(cTokenData.isPaused);
}
}, [cTokenData]);
const togglePause = async () => {
const comptroller = createComptroller(comptrollerAddress, fuse);
try {
await comptroller.methods
._setBorrowPaused(cTokenAddress, !isBorrowPaused)
.send({ from: address });
LogRocket.track("Fuse-PauseToggle");
queryClient.refetchQueries();
} catch (e) {
handleGenericError(e, toast);
}
};
const updateCollateralFactor = async () => {
const comptroller = createComptroller(comptrollerAddress, fuse);
// 70% -> 0.7 * 1e18
const bigCollateralFactor = new BigNumber(collateralFactor)
.dividedBy(100)
.multipliedBy(1e18)
.toFixed(0);
try {
await testForComptrollerErrorAndSend(
comptroller.methods._setCollateralFactor(
cTokenAddress,
bigCollateralFactor
),
address,
""
);
LogRocket.track("Fuse-UpdateCollateralFactor");
queryClient.refetchQueries();
} catch (e) {
handleGenericError(e, toast);
}
};
const updateReserveFactor = async () => {
const cToken = createCToken(fuse, cTokenAddress!);
// 10% -> 0.1 * 1e18
const bigReserveFactor = new BigNumber(reserveFactor)
.dividedBy(100)
.multipliedBy(1e18)
.toFixed(0);
try {
await testForCTokenErrorAndSend(
cToken.methods._setReserveFactor(bigReserveFactor),
address,
""
);
LogRocket.track("Fuse-UpdateReserveFactor");
queryClient.refetchQueries();
} catch (e) {
handleGenericError(e, toast);
}
};
const updateAdminFee = async () => {
const cToken = createCToken(fuse, cTokenAddress!);
// 5% -> 0.05 * 1e18
const bigAdminFee = new BigNumber(adminFee)
.dividedBy(100)
.multipliedBy(1e18)
.toFixed(0);
try {
await testForCTokenErrorAndSend(
cToken.methods._setAdminFee(bigAdminFee),
address,
""
);
LogRocket.track("Fuse-UpdateAdminFee");
queryClient.refetchQueries();
} catch (e) {
handleGenericError(e, toast);
}
};
const updateInterestRateModel = async () => {
const cToken = createCToken(fuse, cTokenAddress!);
try {
await testForCTokenErrorAndSend(
cToken.methods._setInterestRateModel(interestRateModel),
address,
""
);
LogRocket.track("Fuse-UpdateInterestRateModel");
queryClient.refetchQueries();
} catch (e) {
handleGenericError(e, toast);
}
};
return (
cTokenAddress ? cTokenData?.cTokenAddress === cTokenAddress : true
) ? (
<Column
mainAxisAlignment="flex-start"
crossAxisAlignment="flex-start"
overflowY="auto"
width="100%"
height="100%"
>
<ConfigRow height="35px">
<SimpleTooltip
label={t(
"Collateral factor can range from 0-90%, and represents the proportionate increase in liquidity (borrow limit) that an account receives by depositing the asset."
)}
>
<Text fontWeight="bold">
{t("Collateral Factor")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
{cTokenData &&
collateralFactor !==
scaleCollateralFactor(cTokenData.collateralFactorMantissa) ? (
<SaveButton ml={3} onClick={updateCollateralFactor} />
) : null}
<SliderWithLabel
ml="auto"
value={collateralFactor}
setValue={setCollateralFactor}
formatValue={formatPercentage}
step={0.5}
max={
liquidationIncentiveMantissa
? // 100% CF - Liquidation Incentive (ie: 8%) - 5% buffer
100 - (liquidationIncentiveMantissa.toString() / 1e16 - 100) - 5
: 90
}
/>
</ConfigRow>
<ModalDivider />
{cTokenAddress ? (
<ConfigRow>
<SimpleTooltip
label={t("If enabled borrowing this asset will be disabled.")}
>
<Text fontWeight="bold">
{t("Pause Borrowing")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
<SaveButton
ml="auto"
onClick={togglePause}
fontSize="xs"
altText={
isBorrowPaused ? t("Enable Borrowing") : t("Pause Borrowing")
}
/>
</ConfigRow>
) : null}
<ModalDivider />
<ConfigRow height="35px">
<SimpleTooltip
label={t(
"The fraction of interest generated on a given asset that is routed to the asset's Reserve Pool. The Reserve Pool protects lenders against borrower default and liquidation malfunction."
)}
>
<Text fontWeight="bold">
{t("Reserve Factor")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
{cTokenData &&
reserveFactor !==
scaleReserveFactor(cTokenData.reserveFactorMantissa) ? (
<SaveButton ml={3} onClick={updateReserveFactor} />
) : null}
<SliderWithLabel
ml="auto"
value={reserveFactor}
setValue={setReserveFactor}
formatValue={formatPercentage}
max={50}
/>
</ConfigRow>
<ModalDivider />
<ConfigRow height="35px">
<SimpleTooltip
label={t(
"The fraction of interest generated on a given asset that is routed to the asset's admin address as a fee."
)}
>
<Text fontWeight="bold">
{t("Admin Fee")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
{cTokenData &&
adminFee !== scaleAdminFee(cTokenData.adminFeeMantissa) ? (
<SaveButton ml={3} onClick={updateAdminFee} />
) : null}
<SliderWithLabel
ml="auto"
value={adminFee}
setValue={setAdminFee}
formatValue={formatPercentage}
max={30}
/>
</ConfigRow>
<ModalDivider />
<ConfigRow>
<SimpleTooltip
label={t(
"The interest rate model chosen for an asset defines the rates of interest for borrowers and suppliers at different utilization levels."
)}
>
<Text fontWeight="bold">
{t("Interest Model")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
<Select
{...DASHBOARD_BOX_PROPS}
ml="auto"
borderRadius="7px"
fontWeight="bold"
_focus={{ outline: "none" }}
width="260px"
value={interestRateModel.toLowerCase()}
onChange={(event) => setInterestRateModel(event.target.value)}
>
{Object.entries(
Fuse.PUBLIC_INTEREST_RATE_MODEL_CONTRACT_ADDRESSES
).map(([key, value]) => {
return (
<option
className="black-bg-option"
value={value.toLowerCase()}
key={key}
>
{key}
</option>
);
})}
</Select>
{cTokenData &&
cTokenData.interestRateModelAddress.toLowerCase() !==
interestRateModel.toLowerCase() ? (
<SaveButton
height="40px"
borderRadius="7px"
onClick={updateInterestRateModel}
/>
) : null}
</ConfigRow>
<Box
height="170px"
width="100%"
color="#000000"
overflow="hidden"
pl={2}
pr={3}
className="hide-bottom-tooltip"
flexShrink={0}
>
{curves ? (
<Chart
options={
{
...FuseIRMDemoChartOptions,
colors: ["#FFFFFF", tokenData.color! ?? "#282727"],
} as any
}
type="line"
width="100%"
height="100%"
series={[
{
name: "Borrow Rate",
data: curves.borrowerRates,
},
{
name: "Deposit Rate",
data: curves.supplierRates,
},
]}
/>
) : curves === undefined ? (
<Center expand color="#FFF">
<Spinner my={8} />
</Center>
) : (
<Center expand color="#FFFFFF">
<Text>
{t("No graph is available for this asset's interest curves.")}
</Text>
</Center>
)}
</Box>
{cTokenAddress ? null : (
<Box px={4} mt={4} width="100%">
<Button
fontWeight="bold"
fontSize="2xl"
borderRadius="10px"
width="100%"
height="70px"
color={tokenData.overlayTextColor! ?? "#000"}
bg={tokenData.color! ?? "#FFF"}
_hover={{ transform: "scale(1.02)" }}
_active={{ transform: "scale(0.95)" }}
isLoading={isDeploying}
onClick={deploy}
>
{t("Confirm")}
</Button>
</Box>
)}
</Column>
) : (
<Center expand>
<Spinner my={8} />
</Center>
);
}
Example #8
Source File: MarketCapConfigurator.tsx From rari-dApp with GNU Affero General Public License v3.0 | 4 votes |
MarketCapConfigurator = ({
comptrollerAddress,
cTokenAddress,
tokenData,
mode,
}: {
comptrollerAddress: string;
cTokenAddress: string | undefined;
tokenData: TokenData;
mode: "Supply" | "Borrow";
}) => {
const { t } = useTranslation();
const [newSupplyCap, setNewSupplyCap] = useState<string>("");
const { fuse, address } = useRari();
const toast = useToast();
const tokenSymbol = tokenData.symbol
const comptroller = createComptroller(comptrollerAddress, fuse);
const { data: supplyCap } = useQuery(
"Get " + mode + " cap for: " + tokenSymbol,
async () => {
if (cTokenAddress) {
if (mode === "Supply") {
return await comptroller.methods.supplyCaps(cTokenAddress).call();
}
if (mode === "Borrow") {
return await comptroller.methods.borrowCaps(cTokenAddress).call();
}
}
}
);
const handleSubmit = async (
cTokenAddress: string | undefined,
newSupplyCap: number
) => {
const tokenDecimals = tokenData.decimals ?? 18
const newSupplyCapInWei = newSupplyCap * (10 ** tokenDecimals)
if (!cTokenAddress) return
try {
if (mode === "Supply")
await comptroller.methods
._setMarketSupplyCaps([cTokenAddress], [newSupplyCapInWei])
.send({ from: address });
if (mode === "Borrow")
await comptroller.methods
._setMarketBorrowCaps([cTokenAddress], [newSupplyCapInWei])
.send({ from: address });
toast({
title: "Success!",
description: "You've updated the asset's" + mode + " cap.",
status: "success",
duration: 2000,
isClosable: true,
position: "top-right",
});
} catch (e) {
handleGenericError(e, toast);
}
};
const displayedSupplyCap = (parseInt(supplyCap) / (10 ** (tokenData?.decimals ?? 18))).toLocaleString() + " " + tokenSymbol
return (
<>
<Row
mainAxisAlignment="center"
justifyContent="space-between"
crossAxisAlignment="center"
width="100%"
my={4}
px={4}
height="100%"
>
<SimpleTooltip
label={t(
"Sets cap for the market. Users will not be able to supply/borrow once the cap is met."
)}
>
<Text fontWeight="bold">
{mode + " caps"} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
<Box
width="50%"
height="auto"
display="flex"
flexDirection="column"
justifyContent="center"
alignItems="flex-end"
alignContent="center"
>
<Input
width="150px"
height="30px"
extAlign="center"
mb={newSupplyCap !== "" ? 5 : 0}
placeholder={
supplyCap > 0
? displayedSupplyCap
: t(`${tokenSymbol} Cap`)
}
type="number"
size="sm"
value={newSupplyCap}
onChange={(event) => {
setNewSupplyCap(event.target.value);
}}
/>
{newSupplyCap !== "" ? (
<Box
height="100%"
width="100%"
display="flex"
justifyContent="flex-end"
flexDirection="column"
>
<Box
display="flex"
justifyContent="space-between"
alignContent="center"
>
<Text size="sm" opacity="0.7">
New supply cap:
</Text>
<Box height="100%" width="40%">
<Text opacity="0.5" textAlign="end">
{parseInt(newSupplyCap).toLocaleString()} {tokenSymbol}
</Text>
</Box>
</Box>
{supplyCap === "0" ? null : (
<Box
display="flex"
justifyContent="space-between"
alignContent="center"
>
<Text size="sm" opacity="0.7">
Current supply cap:
</Text>
<Box height="100%" width="40%">
<Text opacity="0.5" textAlign="end">
{displayedSupplyCap}
</Text>
</Box>
</Box>
)}
<SaveButton
mt="2"
ml="auto"
onClick={() =>
handleSubmit(cTokenAddress, parseInt(newSupplyCap))
}
fontSize="xs"
altText={"Submit"}
/>
</Box>
) : null}
</Box>
</Row>
</>
);
}
Example #9
Source File: MultiPoolPortal.tsx From rari-dApp with GNU Affero General Public License v3.0 | 4 votes |
PoolDetailCard = ({ pool }: { pool: Pool }) => {
const { t } = useTranslation();
const { rari, isAuthed } = useRari();
const { poolType, poolName, poolLogo } = usePoolInfo(pool);
const {
isOpen: isDepositModalOpen,
onOpen: openDepositModal,
onClose: closeDepositModal,
} = useDisclosure();
const authedOpenModal = useAuthedCallback(openDepositModal);
const { data: balanceData, isLoading: isPoolBalanceLoading } =
usePoolBalance(pool);
const poolAPY = usePoolAPY(pool);
const noSlippageCurrencies = useNoSlippageCurrencies(pool);
if (isPoolBalanceLoading) {
return (
<Center
height={{
md: isAuthed ? "235px" : "110px",
base: isAuthed ? "330px" : "215px",
}}
>
<Spinner />
</Center>
);
}
const myBalance = balanceData!;
const formattedBalance = formatBalanceBN(rari, myBalance, pool === Pool.ETH);
// const rgtAPR = useRGTAPR();
return (
<>
{isDepositModalOpen ? (
<PoolTypeProvider pool={pool}>
<DepositModal
isOpen={isDepositModalOpen}
onClose={closeDepositModal}
/>
</PoolTypeProvider>
) : null}
<Column
mainAxisAlignment="flex-start"
crossAxisAlignment="center"
expand
p={4}
>
<Box width="50px" height="50px" flexShrink={0}>
<Image src={poolLogo} />
</Box>
<Row mainAxisAlignment="flex-start" crossAxisAlignment="center" mt={2}>
<Heading fontSize="xl" lineHeight="2.5rem" ml="12px">
{poolName}
</Heading>
<SimpleTooltip
label={
"Rebalances " +
(noSlippageCurrencies
? noSlippageCurrencies.join(" + ")
: " ? ") +
" between " +
getSDKPool({ rari, pool: poolType })
// Filter out empty pools
.allocations.POOLS.filter((el) => !!el)
.join(", ")
}
>
<QuestionIcon ml={2} mb="3px" boxSize="12px" />
</SimpleTooltip>
</Row>
<SimpleTooltip label={t("Your balance in this pool")}>
<Text mt={4} mb={5} fontSize="md" textAlign="center">
{isPoolBalanceLoading ? "$?" : formattedBalance}
</Text>
</SimpleTooltip>
<Text fontWeight="bold" textAlign="center">
{poolAPY ?? "?"}% APY
{/* +{" "}
<SimpleTooltip label={t("Extra returns from $RGT")}>
<span>
({rgtAPR ?? "?"}%{" "}
<Image display="inline" src={SmallLogo} boxSize="20px" />)
</span>
</SimpleTooltip> */}
</Text>
<Row
mainAxisAlignment="flex-start"
crossAxisAlignment="center"
width="100%"
mt="auto"
>
<Link
/* @ts-ignore */
as={RouterLink}
width="100%"
className="no-underline"
to={"/pools/" + pool.toString()}
>
<DashboardBox
mt={4}
width="100%"
height="45px"
borderRadius="7px"
fontSize="xl"
fontWeight="bold"
>
<Center expand>{t("Access")}</Center>
</DashboardBox>
</Link>
<DashboardBox
mt={4}
flexShrink={0}
as="button"
onClick={authedOpenModal}
height="45px"
ml={2}
width="45px"
borderRadius="7px"
fontSize="xl"
fontWeight="bold"
>
<Center expand>
<Icon as={MdSwapHoriz} boxSize="30px" />
</Center>
</DashboardBox>
</Row>
</Column>
</>
);
}