Skip to content

Commit

Permalink
Merge pull request #49 from bleu/withdraw-gas-limit-fix
Browse files Browse the repository at this point in the history
Withdraw gas limit fix
  • Loading branch information
yvesfracari authored Oct 25, 2024
2 parents eb6277f + df4d27b commit 84022d5
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 94 deletions.
18 changes: 3 additions & 15 deletions apps/create-vesting/src/app/signing/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,10 @@ import type { Address } from "viem";
export default function Page() {
const [currentStepIndex, setCurrentStepIndex] = useState(0);
const [permitTxs, setPermitTxs] = useState<BaseTransaction[]>([]);
const {
actions,
hookInfo,
cowShed,
signer,
context,
publicClient,
cowShedProxy,
} = useIFrameContext();
const { hookInfo, cowShed, signer, context, cowShedProxy } =
useIFrameContext();

const submitHook = useSubmitHook({
actions,
context,
publicClient,
recipientOverride: hookInfo?.recipientOverride,
});
const submitHook = useSubmitHook(hookInfo?.recipientOverride);
const cowShedSignature = useCowShedSignature({
cowShed,
signer,
Expand Down
17 changes: 3 additions & 14 deletions apps/deposit-pool/src/app/signing/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,11 @@ import type { Address } from "viem";
export default function Page() {
const [currentStepIndex, setCurrentStepIndex] = useState(0);
const [permitTxs, setPermitTxs] = useState<BaseTransaction[]>([]);
const {
actions,
hookInfo,
cowShed,
signer,
context,
publicClient,
cowShedProxy,
} = useIFrameContext();
const { hookInfo, cowShed, signer, context, cowShedProxy } =
useIFrameContext();
const [account, setAccount] = useState<string>();
const router = useRouter();
const submitHook = useSubmitHook({
actions,
context,
publicClient,
});
const submitHook = useSubmitHook();
const cowShedSignature = useCowShedSignature({
cowShed,
signer,
Expand Down
3 changes: 2 additions & 1 deletion apps/withdraw-pool/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export default function Page() {
if (!pools || pools.length === 0) {
return (
<span className="mt-10 text-center">
You don't have liquidity in a CoW AMM pool
You don't have unstaked liquidity in a CoW AMM pool
</span>
);
}
Expand All @@ -87,6 +87,7 @@ export default function Page() {
pools={pools || []}
PoolItemInfo={PoolItemInfo}
selectedPool={selectedPool}
tooltipText="Withdraw of staked liquidity or pool with low user balance are not supported"
/>
<PoolForm selectedPool={selectedPool} />
</div>
Expand Down
23 changes: 7 additions & 16 deletions apps/withdraw-pool/src/app/signing/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,12 @@ import type { WithdrawSchemaType } from "#/utils/schema";
export default function Page() {
const [currentStepIndex, setCurrentStepIndex] = useState(0);
const [permitTxs, setPermitTxs] = useState<BaseTransaction[]>([]);
const {
actions,
hookInfo,
cowShed,
signer,
context,
publicClient,
cowShedProxy,
} = useIFrameContext();
const { hookInfo, cowShed, signer, context, cowShedProxy } =
useIFrameContext();
const { getValues } = useFormContext<WithdrawSchemaType>();
const [account, setAccount] = useState<string>();
const router = useRouter();
const submitHook = useSubmitHook({
actions,
context,
publicClient,
});
const submitHook = useSubmitHook();
const cowShedSignature = useCowShedSignature({
cowShed,
signer,
Expand Down Expand Up @@ -68,8 +57,10 @@ export default function Page() {
const cowShedCall = await cowShedSignature(txs);
if (!cowShedCall) throw new Error("Error signing hooks");
const withdrawPct = getValues("withdrawPct");
const cowShedCallDataWithWithdrawPct =
cowShedCall + Number(Number(withdrawPct).toFixed()).toString(16); // adding to facilitate editing the hook
const withdrawPctHex = Number(Number(withdrawPct).toFixed())
.toString(16)
.padStart(4, "0");
const cowShedCallDataWithWithdrawPct = cowShedCall + withdrawPctHex; // adding to facilitate editing the hook
await submitHook({
target: cowShed.getFactoryAddress(),
callData: cowShedCallDataWithWithdrawPct,
Expand Down
6 changes: 3 additions & 3 deletions apps/withdraw-pool/src/components/PoolBalancePreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function PoolBalancesPreview({

const { withdrawPct } = useWatch({ control });

const _withdrawBalance = useMemo(() => {
const withdrawBalance = useMemo(() => {
if (!poolBalances || !withdrawPct) return [];
return poolBalances.map((poolBalance) => ({
...poolBalance,
Expand Down Expand Up @@ -55,7 +55,7 @@ export function PoolBalancesPreview({
</TableRow>
</TableHeader>
<TableBody>
{poolBalances.map((balance, _index) => {
{poolBalances.map((balance, index) => {
return (
<TableRow
className="hover:bg-transparent border-none"
Expand All @@ -81,7 +81,7 @@ export function PoolBalancesPreview({
token={balance.token}
balance={Number(
formatUnits(
BigInt(balance.balance.toString()),
BigInt(withdrawBalance[index].balance.toString()),
balance.token.decimals,
),
)}
Expand Down
7 changes: 6 additions & 1 deletion apps/withdraw-pool/src/hooks/useUserPools.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import type { SupportedChainId } from "@cowprotocol/cow-sdk";

import { usePools } from "@bleu/cow-hooks-ui";
import { BigNumber } from "ethers";
import type { Address } from "viem";

export function useUserPools(chainId?: SupportedChainId, user?: Address) {
return usePools(
const useSwrData = usePools(
{ poolTypeIn: ["COW_AMM"], userAddress: user },
chainId,
"userbalanceUsd",
);
const data = useSwrData.data?.filter((pool) =>
BigNumber.from(pool.userBalance.walletBalance).gt(BigNumber.from("10")),
);
return { ...useSwrData, data };
}
26 changes: 16 additions & 10 deletions packages/cow-hooks-ui/src/PoolsDropdownMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,20 @@ import {
CommandItem,
CommandList,
} from "./ui/Command";
import { InfoTooltip } from "./ui/TooltipBase";

export function PoolsDropdownMenu({
onSelect,
pools,
PoolItemInfo,
selectedPool,
tooltipText,
}: {
onSelect: (pool: IPool) => void;
pools: IPool[];
PoolItemInfo: React.ComponentType<{ pool: IPool }>;
selectedPool?: IPool;
tooltipText?: string;
}) {
const [open, setOpen] = useState(false);
const [search, setSearch] = useState("");
Expand All @@ -49,22 +52,25 @@ export function PoolsDropdownMenu({
return (
<div className="flex w-full flex-col items-center gap-2">
<Dialog.Root open={open} onOpenChange={setOpen}>
<Dialog.Trigger
className={
"w-full flex p-2 justify-between rounded-xl space-x-1 items-center text-sm bg-muted shadow-sm text-foreground group hover:bg-primary hover:text-primary-foreground"
}
onClick={() => setOpen(true)}
>
{selectedPool ? <PoolLogo pool={selectedPool} /> : "Select a pool"}
<ChevronDownIcon className="size-4" />
</Dialog.Trigger>
<div className="flex flex-row gap-1 w-full justify-between">
<Dialog.Trigger
className={
"w-full flex p-2 justify-between rounded-xl space-x-1 items-center text-sm bg-muted shadow-sm text-foreground group hover:bg-primary hover:text-primary-foreground"
}
onClick={() => setOpen(true)}
>
{selectedPool ? <PoolLogo pool={selectedPool} /> : "Select a pool"}
<ChevronDownIcon className="size-4" />
</Dialog.Trigger>
</div>
<Dialog.Portal>
<Dialog.Content className="fixed left-[50%] top-[50%] z-50 translate-x-[-50%] translate-y-[-50%] border bg-background p-[15px] w-screen h-screen bg-background border-none flex flex-col gap-2">
<div className="flex items-center gap-2">
<Dialog.Close className="cursor-pointer hover:opacity-50">
<ArrowLeftIcon className="size-5" />
</Dialog.Close>
<span>Select a pool</span>
<Dialog.Title>Select a pool</Dialog.Title>
{tooltipText && <InfoTooltip text={tooltipText} />}
</div>
<Command
filter={(value: string, search: string) => {
Expand Down
28 changes: 28 additions & 0 deletions packages/cow-hooks-ui/src/hooks/useEstimateGas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS } from "@cowprotocol/cow-sdk";
import type { CowHook } from "@cowprotocol/hook-dapp-lib";
import { useCallback } from "react";
import type { Address } from "viem";
import { useIFrameContext } from "../context/iframe";
import { retryAsync } from "../utils/retry";

export function useEstimateGas() {
const { context, actions, publicClient } = useIFrameContext();
return useCallback(
async (hook: Omit<CowHook, "gasLimit" | "dappId">) => {
if (!context || !actions || !publicClient)
throw new Error("Missing context");

return await retryAsync<bigint>(() => {
return publicClient.estimateGas({
account: COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS[
context.chainId
] as `0x${string}`,
to: hook.target as Address,
value: BigInt("0"),
data: hook.callData as `0x${string}`,
});
});
},
[context, actions, publicClient],
);
}
49 changes: 15 additions & 34 deletions packages/cow-hooks-ui/src/hooks/useSubmitHook.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,24 @@
import { COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS } from "@cowprotocol/cow-sdk";
import type { CoWHookDappActions, CowHook } from "@cowprotocol/hook-dapp-lib";
import type { CowHook } from "@cowprotocol/hook-dapp-lib";
import { BigNumber } from "ethers";
import { useCallback } from "react";
import type { Address, PublicClient } from "viem";
import type { HookDappContextAdjusted } from "../types";
import { useIFrameContext } from "../context/iframe";
import { useEstimateGas } from "./useEstimateGas";

export function useSubmitHook({
actions,
context,
publicClient,
recipientOverride,
}: {
actions: CoWHookDappActions | undefined;
context: HookDappContextAdjusted | undefined;
publicClient: PublicClient | undefined;
recipientOverride?: string;
}) {
export function useSubmitHook(recipientOverride?: string) {
const { context, actions } = useIFrameContext();
const estimateGas = useEstimateGas();
return useCallback(
async (hook: Omit<CowHook, "gasLimit" | "dappId">) => {
if (!context || !actions || !publicClient)
throw new Error("Missing context");
if (!context || !actions) throw new Error("Missing context");

const estimatedGas = await publicClient
.estimateGas({
account: COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS[
context.chainId
] as `0x${string}`,
to: hook.target as Address,
value: BigInt(0),
data: hook.callData as `0x${string}`,
})
.catch(() => {
console.error("Failed to estimated hook gas", {
chainId: context.chainId,
calldata: hook.callData,
target: hook.target,
});
throw new Error("Failed to estimate hook gas");
const estimatedGas = await estimateGas(hook).catch(() => {
console.error("Failed to estimated hook gas", {
chainId: context.chainId,
calldata: hook.callData,
target: hook.target,
});
throw new Error("Failed to estimated hook gas");
});

const gasLimit = BigNumber.from(estimatedGas)
.mul(120)
Expand All @@ -60,6 +41,6 @@ export function useSubmitHook({

actions.addHook({ hook: hookWithGasLimit, recipientOverride });
},
[actions, context, recipientOverride, publicClient],
[actions, context, recipientOverride, estimateGas],
);
}
40 changes: 40 additions & 0 deletions packages/cow-hooks-ui/src/utils/retry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
type RetryOptions = {
maxAttempts?: number;
delayMs?: number;
onRetry?: (error: Error, attempt: number) => void;
};

export async function retryAsync<T>(
fn: () => Promise<T>,
options: RetryOptions = {},
): Promise<T> {
const {
maxAttempts = 5,
delayMs = 1000,
onRetry = (error, attempt) =>
console.warn(
`Attempt ${attempt} failed with error: ${error.message}. Retrying...`,
),
} = options;

let lastError: Error | null = null;

for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error instanceof Error ? error : new Error(String(error));

if (attempt === maxAttempts) {
break;
}

onRetry(lastError, attempt);
await new Promise((resolve) => setTimeout(resolve, delayMs));
}
}

throw new Error(
`Failed after ${maxAttempts} attempts. Last error: ${lastError?.message}`,
);
}

0 comments on commit 84022d5

Please sign in to comment.