Skip to content

Commit

Permalink
add user data input to all pool operation forms
Browse files Browse the repository at this point in the history
  • Loading branch information
MattPereira committed Jan 5, 2025
1 parent 38c3e72 commit 5749f4d
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useCallback, useState } from "react";
import { ResultsDisplay, TokenField, TransactionButton } from ".";
import { ResultsDisplay, TokenField, TransactionButton, UserDataInput } from ".";
import { InputAmount, PERMIT2, calculateProportionalAmounts, erc20Abi } from "@balancer/sdk";
import { useQueryClient } from "@tanstack/react-query";
import debounce from "lodash.debounce";
Expand All @@ -10,6 +10,7 @@ import abis from "~~/contracts/abis";
import { useAddLiquidity, useQueryAddLiquidity, useTargetFork } from "~~/hooks/balancer/";
import { PoolActionsProps, PoolOperationReceipt, TokenAmountDetails } from "~~/hooks/balancer/types";
import { useAllowancesOnTokens, useApproveOnToken } from "~~/hooks/token/";
import { formatToHex } from "~~/utils/helpers";

/**
* 1. Query adding some amount of liquidity to the pool
Expand All @@ -32,14 +33,15 @@ export const AddLiquidityForm: React.FC<PoolActionsProps> = ({
const [addLiquidityReceipt, setAddLiquidityReceipt] = useState<PoolOperationReceipt>(null);
const [referenceAmount, setReferenceAmount] = useState<InputAmount>(); // only for the proportional add liquidity case
const [isCalculatingProportional, setIsCalculatingProportional] = useState(false);
const [userDataInputValue, setUserDataInputValue] = useState<string>("0x");

const queryClient = useQueryClient();
const {
data: queryResponse,
isFetching: isQueryFetching,
error: queryError,
refetch: refetchQueryAddLiquidity,
} = useQueryAddLiquidity(pool, tokenInputs, referenceAmount);
} = useQueryAddLiquidity(pool, tokenInputs, referenceAmount, formatToHex(userDataInputValue));
const { tokensToApprove, refetchTokenAllowances } = useAllowancesOnTokens(tokenInputs);
const {
mutate: addLiquidity,
Expand All @@ -57,7 +59,7 @@ export const AddLiquidityForm: React.FC<PoolActionsProps> = ({
[],
);

const handleInputChange = (index: number, value: string) => {
const handleAmountChange = (index: number, value: string) => {
queryClient.removeQueries({ queryKey: ["queryAddLiquidity"] });
setAddLiquidityReceipt(null);

Expand Down Expand Up @@ -91,6 +93,13 @@ export const AddLiquidityForm: React.FC<PoolActionsProps> = ({
}
};

const handleUserDataChange = (userData: string) => {
queryClient.removeQueries({ queryKey: ["queryAddLiquidity"] });
setAddLiquidityReceipt(null);

setUserDataInputValue(userData);
};

const handleQueryAddLiquidity = () => {
queryClient.removeQueries({ queryKey: ["queryAddLiquidity"] });
refetchQueryAddLiquidity();
Expand Down Expand Up @@ -137,10 +146,11 @@ export const AddLiquidityForm: React.FC<PoolActionsProps> = ({
token={pool.poolTokens[index]}
userBalance={tokenBalances[token.address]}
value={humanInputAmount != "0" ? humanInputAmount : ""}
onAmountChange={value => handleInputChange(index, value)}
onAmountChange={value => handleAmountChange(index, value)}
/>
);
})}
<UserDataInput onChange={handleUserDataChange} value={userDataInputValue} />

{!queryResponse || addLiquidityReceipt || isFormEmpty ? (
<TransactionButton
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState } from "react";
import { ResultsDisplay, TokenField, TransactionButton } from ".";
import { ResultsDisplay, TokenField, TransactionButton, UserDataInput } from ".";
import { BALANCER_ROUTER, VAULT_V3, vaultV3Abi } from "@balancer/sdk";
import { useQueryClient } from "@tanstack/react-query";
import { parseUnits } from "viem";
Expand All @@ -8,7 +8,7 @@ import { Alert } from "~~/components/common/";
import { useQueryRemoveLiquidity, useRemoveLiquidity, useTargetFork } from "~~/hooks/balancer/";
import { PoolActionsProps, PoolOperationReceipt, TokenAmountDetails } from "~~/hooks/balancer/types";
import { useAllowanceOnToken, useApproveOnToken } from "~~/hooks/token";
import { formatToHuman } from "~~/utils/";
import { formatToHex, formatToHuman } from "~~/utils/";

/**
* 1. Query removing some amount of liquidity from the pool
Expand All @@ -17,6 +17,7 @@ import { formatToHuman } from "~~/utils/";
*/
export const RemoveLiquidityForm: React.FC<PoolActionsProps> = ({ pool, refetchPool, refetchTokenBalances }) => {
const [removeLiquidityReceipt, setRemoveLiquidityReceipt] = useState<PoolOperationReceipt>(null);
const [userDataInputValue, setUserDataInputValue] = useState<string>("0x");
const [bptInput, setBptInput] = useState({
rawAmount: 0n,
displayValue: "",
Expand All @@ -29,7 +30,7 @@ export const RemoveLiquidityForm: React.FC<PoolActionsProps> = ({ pool, refetchP
isFetching: isQueryFetching,
error: queryError,
refetch: refetchQuery,
} = useQueryRemoveLiquidity("queryRemoveAmount", pool, bptInput.rawAmount);
} = useQueryRemoveLiquidity("queryRemoveAmount", pool, bptInput.rawAmount, formatToHex(userDataInputValue));
const { data: allowance, refetch: refetchAllowance } = useAllowanceOnToken(pool.address, BALANCER_ROUTER[chainId]);
const {
mutate: approveRouter,
Expand All @@ -43,14 +44,20 @@ export const RemoveLiquidityForm: React.FC<PoolActionsProps> = ({ pool, refetchP
} = useRemoveLiquidity();

const handleAmountChange = (amount: string) => {
queryClient.removeQueries({ queryKey: ["queryRemoveLiquidity"] });
queryClient.removeQueries({ queryKey: ["queryRemoveAmount"] });
setRemoveLiquidityReceipt(null);
const rawAmount = parseUnits(amount, pool.decimals);
setBptInput({ rawAmount, displayValue: amount });
};

const handleUserDataChange = (userData: string) => {
queryClient.removeQueries({ queryKey: ["queryRemoveAmount"] });
setRemoveLiquidityReceipt(null);
setUserDataInputValue(userData);
};

const handleQuery = () => {
queryClient.removeQueries({ queryKey: ["queryRemoveLiquidity"] });
queryClient.removeQueries({ queryKey: ["queryRemoveAmount"] });
setRemoveLiquidityReceipt(null);
refetchQuery();
};
Expand Down Expand Up @@ -109,6 +116,7 @@ export const RemoveLiquidityForm: React.FC<PoolActionsProps> = ({ pool, refetchP
userBalance={pool.userBalance}
setMaxAmount={setMaxAmount}
/>
<UserDataInput onChange={handleUserDataChange} value={userDataInputValue} />

{!queryResponse || removeLiquidityReceipt || isFormEmpty ? (
<TransactionButton label="Query" onClick={handleQuery} isDisabled={isQueryFetching} isFormEmpty={isFormEmpty} />
Expand Down
25 changes: 21 additions & 4 deletions packages/nextjs/app/pools/_components/operations/SwapForm.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { useMemo, useState } from "react";
import { ResultsDisplay, TokenField, TransactionButton } from ".";
import { PERMIT2, SwapKind, VAULT_V3, vaultV3Abi } from "@balancer/sdk";
import { ResultsDisplay, TokenField, TransactionButton, UserDataInput } from ".";
import { PERMIT2, SwapInput, SwapKind, VAULT_V3, vaultV3Abi } from "@balancer/sdk";
import { useQueryClient } from "@tanstack/react-query";
import { parseUnits } from "viem";
import { useContractEvent } from "wagmi";
import { Alert } from "~~/components/common";
import { useQuerySwap, useSwap, useTargetFork } from "~~/hooks/balancer/";
import { PoolActionsProps, PoolOperationReceipt, SwapConfig } from "~~/hooks/balancer/types";
import { useAllowanceOnToken, useApproveOnToken } from "~~/hooks/token";
import { formatToHex } from "~~/utils/helpers";

const initialSwapConfig = {
const initialSwapConfig: SwapConfig = {
tokenIn: {
poolTokensIndex: 0,
amount: "",
Expand All @@ -21,6 +22,7 @@ const initialSwapConfig = {
rawAmount: 0n,
},
swapKind: SwapKind.GivenOut,
userData: "0x",
};

/**
Expand All @@ -32,14 +34,15 @@ const initialSwapConfig = {
export const SwapForm: React.FC<PoolActionsProps> = ({ pool, refetchPool, tokenBalances, refetchTokenBalances }) => {
const [swapConfig, setSwapConfig] = useState<SwapConfig>(initialSwapConfig);
const [swapReceipt, setSwapReceipt] = useState<PoolOperationReceipt>(null);
const [userDataInputValue, setUserDataInputValue] = useState<string>("0x");

const { chainId } = useTargetFork();
const queryClient = useQueryClient();

const tokenIn = pool.poolTokens[swapConfig.tokenIn.poolTokensIndex];
const tokenOut = pool.poolTokens[swapConfig.tokenOut.poolTokensIndex];

const swapInput = {
const swapInput: SwapInput = {
chainId,
swapKind: swapConfig.swapKind,
paths: [
Expand All @@ -54,6 +57,7 @@ export const SwapForm: React.FC<PoolActionsProps> = ({ pool, refetchPool, tokenB
outputAmountRaw: swapConfig.tokenOut.rawAmount,
},
],
userData: swapConfig.userData,
};

const {
Expand Down Expand Up @@ -99,6 +103,7 @@ export const SwapForm: React.FC<PoolActionsProps> = ({ pool, refetchPool, tokenB
setSwapReceipt(null);
// Update the focused input amount with new value and reset the other input amount
setSwapConfig(prevConfig => ({
...prevConfig,
tokenIn: {
...prevConfig.tokenIn,
amount: swapConfigKey === "tokenIn" ? amount : "",
Expand All @@ -113,6 +118,17 @@ export const SwapForm: React.FC<PoolActionsProps> = ({ pool, refetchPool, tokenB
}));
};

const handleUserDataChange = (userData: string) => {
queryClient.removeQueries({ queryKey: ["querySwap"] });
setSwapReceipt(null);
setUserDataInputValue(userData);

setSwapConfig(prevConfig => ({
...prevConfig,
userData: formatToHex(userData),
}));
};

useContractEvent({
address: VAULT_V3[chainId],
abi: vaultV3Abi,
Expand Down Expand Up @@ -168,6 +184,7 @@ export const SwapForm: React.FC<PoolActionsProps> = ({ pool, refetchPool, tokenB
selectableTokens={pool.poolTokens.filter(token => token.symbol !== tokenOut.symbol)}
isHighlighted={queryResponse?.swapKind === SwapKind.GivenOut}
/>
<UserDataInput onChange={handleUserDataChange} value={userDataInputValue} />

{!queryResponse || isFormEmpty || swapReceipt ? (
<TransactionButton
Expand Down
17 changes: 17 additions & 0 deletions packages/nextjs/app/pools/_components/operations/UserDataInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { formatToHex } from "~~/utils/";

export const UserDataInput = ({ onChange, value }: { onChange: (userData: string) => void; value: string }) => {
return (
<div>
<div className="flex justify-between">
<div className="font-bold ml-2 mb-1">User Data</div>
<div className="mr-2 text-neutral-400">{formatToHex(value)}</div>
</div>
<input
className="input shadow-inner rounded-lg bg-base-300 w-full"
value={value}
onChange={e => onChange(e.target.value)}
/>
</div>
);
};
1 change: 1 addition & 0 deletions packages/nextjs/app/pools/_components/operations/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from "./TokenField";
export * from "./TransactionButton";
export * from "./ResultsDisplay";
export * from "./PoolOperations";
export * from "./UserDataInput";
18 changes: 9 additions & 9 deletions packages/nextjs/contracts/deployedContracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5057,7 +5057,7 @@ const deployedContracts = {
},
31337: {
MockToken1: {
address: "0x64386bc53c213f23c6960d3e080139a0f9ef1733",
address: "0xad3e631c01798f9aae4692dabf791a62c226c5d4",
abi: [
{
type: "constructor",
Expand Down Expand Up @@ -5415,7 +5415,7 @@ const deployedContracts = {
},
},
MockToken2: {
address: "0x4a65b9d13908487a1654be48e6aa9bc701735910",
address: "0x0ac85d55ebfc7f7b0cf4c13bb3bd6eaf3909d62d",
abi: [
{
type: "constructor",
Expand Down Expand Up @@ -5773,7 +5773,7 @@ const deployedContracts = {
},
},
MockVeBAL: {
address: "0x295129609d6876f5ecc62052ba6bc082139a982c",
address: "0x4b901e2db7c412d966689e8d3cf479294c456f1e",
abi: [
{
type: "constructor",
Expand Down Expand Up @@ -6131,7 +6131,7 @@ const deployedContracts = {
},
},
ConstantSumFactory: {
address: "0xb92257d74b8815ec711071889cb506c8d66a6a06",
address: "0x8c08821f5f94b519c853486eb131667aa528a460",
abi: [
{
type: "constructor",
Expand Down Expand Up @@ -6604,7 +6604,7 @@ const deployedContracts = {
},
},
VeBALFeeDiscountHookExample: {
address: "0x737b8f095e3c575a6ae5fe1711adb8f271e20269",
address: "0xdcaa80371bdf9ff638851713f145df074428db19",
abi: [
{
type: "constructor",
Expand Down Expand Up @@ -7314,7 +7314,7 @@ const deployedContracts = {
},
},
ConstantProductFactory: {
address: "0x427ee58a6c574032085aeb90dd05deea6f054930",
address: "0x23228469b3439d81dc64e3523068976201ba08c3",
abi: [
{
type: "constructor",
Expand Down Expand Up @@ -7787,7 +7787,7 @@ const deployedContracts = {
},
},
LotteryHookExample: {
address: "0x2963ff0196a901ec3f56d7531e7c4ce8f226462b",
address: "0xfd3e0cee740271f070607aeddd0bf4cf99c92204",
abi: [
{
type: "constructor",
Expand Down Expand Up @@ -8741,7 +8741,7 @@ const deployedContracts = {
},
},
WeightedPoolFactory: {
address: "0x2ca60d89144d4cdf85da87af4fe12abf9265f28c",
address: "0xaac7d4a36dab95955ef3c641c23f1fa46416cf71",
abi: [
{
type: "constructor",
Expand Down Expand Up @@ -9235,7 +9235,7 @@ const deployedContracts = {
},
},
ExitFeeHookExample: {
address: "0xf4fa0d1c10c47cde9f65d56c3ec977cbeb13449a",
address: "0x6b5cf024365d5d5d0786673780ca7e3f07f85b63",
abi: [
{
type: "constructor",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ import { useQuery } from "@tanstack/react-query";
import { useTargetFork } from "~~/hooks/balancer";
import { Pool } from "~~/hooks/balancer/types";

export const useQueryAddLiquidity = (pool: Pool, amountsIn: InputAmount[], referenceAmount?: InputAmount) => {
export const useQueryAddLiquidity = (
pool: Pool,
amountsIn: InputAmount[],
referenceAmount?: InputAmount,
userData?: `0x${string}`,
) => {
const { rpcUrl, chainId } = useTargetFork();

const queryAddLiquidity = async () => {
Expand All @@ -27,12 +32,14 @@ export const useQueryAddLiquidity = (pool: Pool, amountsIn: InputAmount[], refer
referenceAmount,
chainId,
rpcUrl,
userData,
}
: {
kind: AddLiquidityKind.Unbalanced,
amountsIn,
chainId,
rpcUrl,
userData,
};

// Query addLiquidity to get the amount of BPT out
Expand All @@ -42,9 +49,6 @@ export const useQueryAddLiquidity = (pool: Pool, amountsIn: InputAmount[], refer
return queryOutput;
};

// const serializedAmountsIn = amountsIn.map(amount => `${amount.address}-${amount.rawAmount}`);
// const serializedBptOut = bptOut ? `${bptOut.address}-${bptOut.rawAmount}` : "";

return useQuery({
queryKey: ["queryAddLiquidity"],
queryFn: queryAddLiquidity,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import { useQuery } from "@tanstack/react-query";
import { Pool, useTargetFork } from "~~/hooks/balancer";

export const useQueryRemoveLiquidity = (queryKey: string, pool: Pool, rawAmount: bigint) => {
export const useQueryRemoveLiquidity = (queryKey: string, pool: Pool, rawAmount: bigint, userData?: `0x${string}`) => {
const { rpcUrl, chainId } = useTargetFork();

const queryRemoveLiquidity = async () => {
Expand All @@ -30,6 +30,7 @@ export const useQueryRemoveLiquidity = (queryKey: string, pool: Pool, rawAmount:
rpcUrl,
bptIn,
kind: RemoveLiquidityKind.Proportional,
userData: userData ?? "0x",
};

// Query removeLiquidity to get the token out amounts
Expand Down
1 change: 1 addition & 0 deletions packages/nextjs/hooks/balancer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export interface PoolActionsProps {
}

export type SwapConfig = {
userData: `0x${string}`;
tokenIn: {
poolTokensIndex: number;
amount: string;
Expand Down
Loading

0 comments on commit 5749f4d

Please sign in to comment.