Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useEffect } from "react";
import { ChooseTokenAmounts } from "../../PoolCreation/ChooseTokenAmounts";
import { PoolType } from "@balancer/sdk";
import { sonic } from "viem/chains";
import { TextField } from "~~/components/common";
Expand Down Expand Up @@ -38,8 +39,8 @@ export const ChooseInfo = () => {
<div className="flex flex-col gap-5">
<div>
<div className="text-xl mb-3">Choose pool information:</div>
<div className="flex flex-col gap-4">
<div className="bg-base-100 p-3 rounded-xl">
<div className="flex flex-col gap-4 bg-base-100 p-3 pb-5 rounded-xl">
<div>
<TextField
label="Pool name"
placeholder="Enter pool name"
Expand All @@ -52,7 +53,7 @@ export const ChooseInfo = () => {
/>
</div>

<div className="bg-base-100 p-3 rounded-xl">
<div>
<TextField
label="Pool symbol"
placeholder="Enter pool symbol"
Expand All @@ -66,6 +67,7 @@ export const ChooseInfo = () => {
</div>
</div>
</div>
<ChooseTokenAmounts />
</div>
);
};
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import ReactECharts from "echarts-for-react";
import { formatUnits, parseUnits } from "viem";
import { formatUnits } from "viem";
import { ArrowTopRightOnSquareIcon, ArrowsRightLeftIcon } from "@heroicons/react/24/outline";
import { Alert, TextField } from "~~/components/common";
import { useAutofillStarterParams, useEclpParamValidations, useEclpPoolChart } from "~~/hooks/gyro";
import { useInvertEclpParams } from "~~/hooks/gyro";
import { useFetchTokenRate, usePoolCreationStore, useUserDataStore } from "~~/hooks/v3";
import { calculateRotationComponents, formatEclpParamValues } from "~~/utils/gryo";
import { calculateRotationComponents } from "~~/utils/gryo";
import { truncateNumber } from "~~/utils/helpers";

export function EclpParams() {
Expand Down Expand Up @@ -46,41 +47,8 @@ export function EclpParams() {

export function EclpChartDisplay({ size }: { size: "full" | "mini" }) {
const { options } = useEclpPoolChart();
const { eclpParams, updateEclpParam, updatePool, tokenConfigs } = usePoolCreationStore();

/**
* SDK handles un-inversion for on chain call,
* so we just want to:
* 1. flip the tokenConfig order in pool creation store
* 2. invert the eclp params
* 3. make sure all parts of app rely on order of tokens in tokenConfigs
*/
const handleInvertEclpParams = () => {
const D18 = 10n ** 18n;

const { alpha, beta, peakPrice, c, s, lambda, usdPerTokenInput0, usdPerTokenInput1 } = eclpParams;

// take reciprocal and flip alpha to beta
const invertedAlpha = Number(formatUnits((D18 * D18) / parseUnits(beta, 18), 18));
// take reciprocal and flip beta to alpha
const invertedBeta = Number(formatUnits((D18 * D18) / parseUnits(alpha, 18), 18));
// take reciprocal of peakPrice
const invertedPeakPrice = Number(formatUnits((D18 * D18) / parseUnits(peakPrice, 18), 18));

const invertedParams = {
alpha: formatEclpParamValues(invertedAlpha),
beta: formatEclpParamValues(invertedBeta),
peakPrice: formatEclpParamValues(invertedPeakPrice),
c: s, // flip c and s
s: c, // flip s and c
usdPerTokenInput0: usdPerTokenInput1,
usdPerTokenInput1: usdPerTokenInput0,
lambda, // stays the same
};

updateEclpParam(invertedParams);
updatePool({ tokenConfigs: [...tokenConfigs].reverse() });
};

const { invertEclpParams } = useInvertEclpParams();

return (
<div className="bg-base-300 p-5 rounded-lg relative">
Expand All @@ -89,7 +57,7 @@ export function EclpChartDisplay({ size }: { size: "full" | "mini" }) {
{size === "full" && (
<div
className="btn btn-sm rounded-lg absolute bottom-3 right-3 btn-primary px-2 py-0.5 text-neutral-700 bg-gradient-to-r from-violet-300 via-violet-200 to-orange-300 [box-shadow:0_0_10px_5px_rgba(139,92,246,0.5)] border-none"
onClick={handleInvertEclpParams}
onClick={invertEclpParams}
>
<ArrowsRightLeftIcon className="w-[18px] h-[18px]" />
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ export function LiquidityManagement() {
</label>
{poolType === PoolType.StableSurge ? (
<div className="flex flex-col gap-2">
<div className="flex items-center gap-1 text-lg">
Stable Surge pools must set disable unbalanced liquidity to false
<input type="checkbox" disabled={true} checked={false} className="checkbox ml-2 rounded-md" />
<div className="flex items-center gap-3 text-lg">
<input type="checkbox" disabled={true} checked={true} className="checkbox ml-2 rounded-md" />
Stable Surge pools must allow unbalanced liquidity operations
</div>
<Checkbox
label="Should this pool accept donations?"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ export function PoolHooks() {

{poolType === PoolType.StableSurge ? (
<div className="flex flex-col gap-2">
<div className="flex items-center gap-1 text-lg">
Stable surge pools must use Balancer&apos;s stable surge hook
<div className="flex items-center gap-3 text-lg">
<input type="checkbox" disabled={true} checked={true} className="checkbox ml-2 rounded-md" />
Stable surge pools must use Balancer&apos;s stable surge hook
</div>
</div>
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export function ChooseToken({ index }: { index: number }) {
tokenInfo: { ...tokenInfo },
useBoostedVariant: false,
paysYieldFees: false,
amount: "",
});

// Gross pattern but works for triggering new autofill of pool type specific params?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,12 @@ export function ChooseType() {

return (
<>
<div className="flex flex-col justify-center h-full gap-10">
<div className="flex flex-col gap-5">
<div className="text-xl">Choose a pool type:</div>
<div className="flex flex-col gap-4 px-24">
{poolTypes.map((type: SupportedPoolTypes) => (
<PoolTypeButton key={type} selectedPoolType={type} />
))}
</div>
<div className="flex flex-col justify-center h-full gap-10 mt-10">
<div className="text-xl">Choose a pool type:</div>
<div className="grid grid-cols-2 gap-4 ">
{poolTypes.map((type: SupportedPoolTypes) => (
<PoolTypeButton key={type} selectedPoolType={type} />
))}
</div>
</div>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,19 @@ import { PoolType } from "@balancer/sdk";
import { useQueryClient } from "@tanstack/react-query";
import { erc20Abi, formatUnits } from "viem";
import { useAccount, useReadContract } from "wagmi";
import { useEclpInitAmountsRatio } from "~~/hooks/gyro";
import { useTokenUsdValue } from "~~/hooks/token";
import { type TokenConfig, usePoolCreationStore, useUserDataStore } from "~~/hooks/v3";
import { type TokenConfig, useFetchTokenRate, usePoolCreationStore, useUserDataStore } from "~~/hooks/v3";

export function ChooseTokenAmount({ index, tokenConfig }: { index: number; tokenConfig: TokenConfig }) {
export function ChooseTokenAmount({
index,
tokenConfig,
useSuggestedAmounts,
}: {
index: number;
tokenConfig: TokenConfig;
useSuggestedAmounts: boolean;
}) {
const { updateUserData, userTokenBalances } = useUserDataStore();
const { poolType, updateTokenConfig, eclpParams, tokenConfigs } = usePoolCreationStore();
const { tokenInfo, amount, address, weight } = tokenConfig;
Expand All @@ -16,7 +25,6 @@ export function ChooseTokenAmount({ index, tokenConfig }: { index: number; token
const [usdValue, setUsdValue] = useState<number | null>(null);

const queryClient = useQueryClient();

const usdPerToken0 = Number(usdPerTokenInput0);
const usdPerToken1 = Number(usdPerTokenInput1);

Expand Down Expand Up @@ -44,10 +52,36 @@ export function ChooseTokenAmount({ index, tokenConfig }: { index: number; token
return basePrice * Number(formatUnits(rate, 18));
};

const { data: rateTokenA } = useFetchTokenRate(tokenConfigs[0].rateProvider);
const { data: rateTokenB } = useFetchTokenRate(tokenConfigs[1].rateProvider);

const { alpha, beta, c, s, lambda } = eclpParams;

const initAmountsRatio = useEclpInitAmountsRatio({
alpha: Number(alpha),
beta: Number(beta),
c: Number(c),
s: Number(s),
lambda: Number(lambda),
rateA: rateTokenA ? +formatUnits(rateTokenA, 18) : 1,
rateB: rateTokenB ? +formatUnits(rateTokenB, 18) : 1,
});

const handleAmountChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const inputValue = e.target.value.trim();
if (Number(inputValue) >= 0) {
updateTokenConfig(index, { amount: inputValue });
const referenceAmount = Number(e.target.value.trim());
if (referenceAmount >= 0) {
if (poolType === PoolType.GyroE && initAmountsRatio && useSuggestedAmounts) {
// app forces tokens to be sorted before offering init amounts inputs
const isReferenceAmountForTokenA = index === 0;
const otherIndex = isReferenceAmountForTokenA ? 1 : 0;

const otherTokenAmount = isReferenceAmountForTokenA
? referenceAmount / initAmountsRatio // If entering tokenA, divide to get tokenB amount
: referenceAmount * initAmountsRatio; // If entering tokenB, multiply to get tokenA amount

updateTokenConfig(otherIndex, { amount: Math.abs(otherTokenAmount).toString() });
}
updateTokenConfig(index, { amount: referenceAmount.toString() });
} else {
updateTokenConfig(index, { amount: "" });
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,54 +1,76 @@
import React from "react";
import React, { useEffect, useRef, useState } from "react";
import { ChooseTokenAmount } from "./ChooseTokenAmount";
import { PoolType } from "@balancer/sdk";
import { XMarkIcon } from "@heroicons/react/24/outline";
import { Alert, TransactionButton } from "~~/components/common";
import { usePoolCreationStore, useUserDataStore, useValidateInitializationInputs } from "~~/hooks/v3";
import { Alert } from "~~/components/common";
import { Checkbox } from "~~/components/common";
import { useInvertEclpParams } from "~~/hooks/gyro";
import { usePoolCreationStore, useUserDataStore } from "~~/hooks/v3";

export function ChooseTokenAmounts() {
const { tokenConfigs, poolType, updatePool, step, setIsChooseTokenAmountsModalOpen } = usePoolCreationStore();
const [useSuggestedAmounts, setUseSuggestedAmounts] = useState(false);
const { tokenConfigs, poolType } = usePoolCreationStore();
const { updateUserData, hasAgreedToWarning } = useUserDataStore();
const { isInitializePoolInputsValid } = useValidateInitializationInputs();

const isGyroEclp = poolType === PoolType.GyroE;
const isWeightedPool = poolType === PoolType.Weighted;

const isTokenConfigsSorted = tokenConfigs.every((token, index) => {
if (index === 0) return true;
return token.address.toLowerCase() >= tokenConfigs[index - 1].address.toLowerCase();
});

const shouldInvertEclpParams = !isTokenConfigsSorted && poolType === PoolType.GyroE;

const { invertEclpParams } = useInvertEclpParams();
const hasInvertedRef = useRef(false);

// force token configs to be sorted in order before user enters amounts
useEffect(() => {
if (shouldInvertEclpParams && !hasInvertedRef.current) {
invertEclpParams();
hasInvertedRef.current = true;
}
}, [shouldInvertEclpParams, invertEclpParams]);

return (
<div className="fixed inset-0 bg-black bg-opacity-75 flex gap-7 justify-center items-center z-[100]">
<div className="bg-base-300 rounded-xl flex flex-col gap-7 p-7 min-w-[550px] relative">
<button className="absolute top-4 right-4" onClick={() => setIsChooseTokenAmountsModalOpen(false)}>
<XMarkIcon className="w-6 h-6" />
</button>
<div className="font-bold text-2xl text-center">Choose Token Amounts</div>
<div className="flex flex-col gap-5 bg-base-100 p-4 rounded-xl">
{tokenConfigs.map((tokenConfig, index) => (
<ChooseTokenAmount key={tokenConfig.address} index={index} tokenConfig={tokenConfig} />
))}
</div>

{poolType === PoolType.Weighted && (
<Alert type="warning">
<label className="label cursor-pointer py-0 gap-3">
<span className="font-bold">Please Confirm:</span> USD values are proportional to token weights?
<input
type="checkbox"
className="checkbox rounded-lg border-neutral-700"
onChange={() => {
updateUserData({ hasAgreedToWarning: !hasAgreedToWarning });
}}
checked={hasAgreedToWarning}
/>
</label>
</Alert>
)}
<div className="rounded-xl flex flex-col gap-4">
<div className="text-xl">Choose initialization amounts:</div>

<TransactionButton
onClick={() => {
updatePool({ step: step + 1 });
setIsChooseTokenAmountsModalOpen(false);
}}
title="Confirm Amounts"
isDisabled={!isInitializePoolInputsValid}
isPending={false}
/>
<div className="flex flex-col gap-5 rounded-xl l bg-base-100 p-4">
{isGyroEclp && (
<Checkbox
label="Autofill other token amount based on ECLP parameters"
checked={useSuggestedAmounts}
onChange={() => {
setUseSuggestedAmounts(!useSuggestedAmounts);
}}
/>
)}
{tokenConfigs.map((tokenConfig, index) => (
<ChooseTokenAmount
key={tokenConfig.address}
index={index}
tokenConfig={tokenConfig}
useSuggestedAmounts={useSuggestedAmounts}
/>
))}
</div>

{isWeightedPool && (
<Alert type="warning">
<label className="label cursor-pointer py-0 gap-3">
<span className="font-bold">Please Confirm:</span> USD values are proportional to token weights?
<input
type="checkbox"
className="checkbox rounded-lg border-neutral-700"
onChange={() => {
updateUserData({ hasAgreedToWarning: !hasAgreedToWarning });
}}
checked={hasAgreedToWarning}
/>
</label>
</Alert>
)}
</div>
);
}
Loading