/ src / helpers / useGovernanceDelegate.tsx
useGovernanceDelegate.tsx
  1  import {
  2    DelegationType,
  3    EthereumTransactionTypeExtended,
  4    gasLimitRecommendations,
  5    MetaDelegateParams,
  6    ProtocolAction,
  7  } from '@aave/contract-helpers';
  8  import { SignatureLike } from '@ethersproject/bytes';
  9  import { TransactionResponse } from '@ethersproject/providers';
 10  import { useQueryClient } from '@tanstack/react-query';
 11  import { utils } from 'ethers';
 12  import { useEffect, useState } from 'react';
 13  import { DelegationTokenType } from 'src/components/transactions/GovDelegation/DelegationTokenSelector';
 14  import { useModalContext } from 'src/hooks/useModal';
 15  import { useWeb3Context } from 'src/libs/hooks/useWeb3Context';
 16  import { useRootStore } from 'src/store/root';
 17  import { getErrorTextFromError, TxAction } from 'src/ui-config/errorMapping';
 18  import { governanceV3Config } from 'src/ui-config/governanceConfig';
 19  import { queryKeysFactory } from 'src/ui-config/queries';
 20  import { useSharedDependencies } from 'src/ui-config/SharedDependenciesProvider';
 21  import { useShallow } from 'zustand/shallow';
 22  
 23  import { MOCK_SIGNED_HASH } from './useTransactionHandler';
 24  
 25  export const useGovernanceDelegate = (
 26    delegationTokenType: DelegationTokenType,
 27    delegationType: DelegationType,
 28    skip: boolean,
 29    delegatee: string
 30  ) => {
 31    const getTokenNonce = useRootStore((state) => state.getTokenNonce);
 32    // const delegateTokensBySig = useRootStore((state) => state.delegateTokensBySig);
 33    // const delegateTokensByTypeBySig = useRootStore((state) => state.delegateTokensByTypeBySig);
 34    const [user, estimateGasLimit, delegateByType, delegate] = useRootStore(
 35      useShallow((state) => [
 36        state.account,
 37        state.estimateGasLimit,
 38        state.delegateByType,
 39        state.delegate,
 40      ])
 41    );
 42    const { signTxData, sendTx, chainId: connectedChainId, getTxError } = useWeb3Context();
 43  
 44    const [signatures, setSignatures] = useState<SignatureLike[]>([]);
 45    const [actionTx, setActionTx] = useState<EthereumTransactionTypeExtended | undefined>();
 46    // const [aaveNonce, setAaveNonce] = useState(0);
 47    // const [stkAaveNonce, setStkAaveNonce] = useState(0);
 48    // const [aAaveNonce, setAAaveNonce] = useState(0);
 49    const [deadline, setDeadline] = useState(Math.floor(Date.now() / 1000 + 3600).toString());
 50    // const prepareDelegateSignature = useRootStore((state) => state.prepareDelegateSignature);
 51    // const prepareDelegateByTypeSignature = useRootStore(
 52    //   (state) => state.prepareDelegateByTypeSignature
 53    // );
 54  
 55    const isSignatureAction = delegationTokenType === DelegationTokenType.ALL;
 56  
 57    const {
 58      approvalTxState,
 59      mainTxState,
 60      setMainTxState,
 61      setGasLimit,
 62      loadingTxns,
 63      setLoadingTxns,
 64      setTxError,
 65      setApprovalTxState,
 66    } = useModalContext();
 67  
 68    const { delegationTokenService } = useSharedDependencies();
 69    const queryClient = useQueryClient();
 70  
 71    const processTx = async ({
 72      tx,
 73      errorCallback,
 74      successCallback,
 75    }: {
 76      tx: () => Promise<TransactionResponse>;
 77      // eslint-disable-next-line @typescript-eslint/no-explicit-any
 78      errorCallback?: (error: any, hash?: string) => void;
 79      successCallback?: (param: TransactionResponse) => void;
 80      action: TxAction;
 81    }) => {
 82      try {
 83        const txnResult = await tx();
 84        try {
 85          await txnResult.wait(1);
 86          successCallback && successCallback(txnResult);
 87        } catch (e) {
 88          try {
 89            const error = await getTxError(txnResult.hash);
 90            errorCallback && errorCallback(new Error(error), txnResult.hash);
 91            return;
 92          } catch (e) {
 93            errorCallback && errorCallback(e, txnResult.hash);
 94            return;
 95          }
 96        }
 97  
 98        return;
 99      } catch (e) {
100        errorCallback && errorCallback(e);
101      }
102    };
103  
104    const action = async () => {
105      if (isSignatureAction) {
106        setMainTxState({ ...mainTxState, loading: true });
107  
108        const { v: v1, r: r1, s: s1 } = utils.splitSignature(signatures[0]);
109        const { v: v2, r: r2, s: s2 } = utils.splitSignature(signatures[1]);
110        const { v: v3, r: r3, s: s3 } = utils.splitSignature(signatures[2]);
111  
112        let txs: MetaDelegateParams[] = [];
113  
114        txs = [
115          {
116            delegator: user,
117            delegatee,
118            underlyingAsset: governanceV3Config.votingAssets.aaveTokenAddress,
119            deadline,
120            v: v1,
121            r: r1,
122            s: s1,
123            delegationType: delegationType,
124          },
125          {
126            delegator: user,
127            delegatee,
128            underlyingAsset: governanceV3Config.votingAssets.stkAaveTokenAddress,
129            deadline,
130            v: v2,
131            r: r2,
132            s: s2,
133            delegationType: delegationType,
134          },
135          {
136            delegator: user,
137            delegatee,
138            underlyingAsset: governanceV3Config.votingAssets.aAaveTokenAddress,
139            deadline,
140            v: v3,
141            r: r3,
142            s: s3,
143            delegationType: delegationType,
144          },
145        ];
146  
147        let txData = await delegationTokenService.batchMetaDelegate(user, txs, connectedChainId);
148        txData = await estimateGasLimit(txData);
149  
150        return processTx({
151          tx: () => sendTx(txData),
152          successCallback: (txnResponse: TransactionResponse) => {
153            setMainTxState({
154              txHash: txnResponse.hash,
155              loading: false,
156              success: true,
157            });
158            setTxError(undefined);
159            queryClient.invalidateQueries({
160              queryKey: queryKeysFactory.powers(user, governanceV3Config.coreChainId),
161            });
162          },
163          errorCallback: (error, hash) => {
164            const parsedError = getErrorTextFromError(error, TxAction.MAIN_ACTION);
165            setTxError(parsedError);
166            setMainTxState({
167              txHash: hash,
168              loading: false,
169            });
170          },
171          action: TxAction.MAIN_ACTION,
172        });
173      } else if (actionTx) {
174        setMainTxState({ ...mainTxState, loading: true });
175        const params = await actionTx.tx();
176        delete params.gasPrice;
177        return processTx({
178          tx: () => sendTx(params),
179          successCallback: (txnResponse: TransactionResponse) => {
180            setMainTxState({
181              txHash: txnResponse.hash,
182              loading: false,
183              success: true,
184            });
185            setTxError(undefined);
186            queryClient.invalidateQueries({
187              queryKey: queryKeysFactory.powers(user, governanceV3Config.coreChainId),
188            });
189          },
190          errorCallback: (error, hash) => {
191            const parsedError = getErrorTextFromError(error, TxAction.MAIN_ACTION);
192            setTxError(parsedError);
193            setMainTxState({
194              txHash: hash,
195              loading: false,
196            });
197          },
198          action: TxAction.MAIN_ACTION,
199        });
200      }
201    };
202  
203    const signMetaTxs = async () => {
204      if (delegationTokenType === DelegationTokenType.ALL) {
205        setApprovalTxState({ ...approvalTxState, loading: true });
206        const [aaveNonce, stkAaveNonce, aAaveNonce] = await Promise.all([
207          getTokenNonce(user, governanceV3Config.votingAssets.aaveTokenAddress),
208          getTokenNonce(user, governanceV3Config.votingAssets.stkAaveTokenAddress),
209          getTokenNonce(user, governanceV3Config.votingAssets.aAaveTokenAddress),
210        ]);
211        const deadline = Math.floor(Date.now() / 1000 + 3600).toString();
212        setDeadline(deadline);
213        // setAaveNonce(aaveNonce);
214        // setStkAaveNonce(stkAaveNonce);
215        // setAAaveNonce(aAaveNonce);
216  
217        const delegationParameters = [
218          {
219            delegator: user,
220            delegatee: delegatee,
221            underlyingAsset: governanceV3Config.votingAssets.aaveTokenAddress,
222            deadline,
223            nonce: String(aaveNonce),
224            delegationType: delegationType,
225            governanceTokenName: 'Aave token V3',
226            increaseNonce: false,
227            connectedChainId,
228          },
229          {
230            delegator: user,
231            delegatee: delegatee,
232            underlyingAsset: governanceV3Config.votingAssets.stkAaveTokenAddress,
233            deadline,
234            nonce: String(stkAaveNonce),
235            delegationType: delegationType,
236            governanceTokenName: 'Staked Aave',
237            increaseNonce: false,
238            connectedChainId,
239          },
240          {
241            delegator: user,
242            delegatee: delegatee,
243            underlyingAsset: governanceV3Config.votingAssets.aAaveTokenAddress,
244            governanceTokenName: 'Aave Ethereum AAVE',
245            deadline,
246            nonce: String(aAaveNonce),
247            delegationType: delegationType,
248            increaseNonce: false,
249            connectedChainId,
250          },
251        ];
252  
253        const unsignedPayloads: string[] = [];
254        for (const tx of delegationParameters) {
255          if (delegationType !== DelegationType.ALL) {
256            const payload = await delegationTokenService.prepareV3DelegateByTypeSignature(tx);
257            unsignedPayloads.push(payload);
258          } else {
259            const payload = await delegationTokenService.prepareV3DelegateByTypeSignature(tx);
260            unsignedPayloads.push(payload);
261          }
262        }
263        try {
264          const signedPayload: SignatureLike[] = [];
265          for (const unsignedPayload of unsignedPayloads) {
266            signedPayload.push(await signTxData(unsignedPayload));
267          }
268          setApprovalTxState({
269            txHash: MOCK_SIGNED_HASH,
270            loading: false,
271            success: true,
272          });
273          setTxError(undefined);
274          setSignatures(signedPayload);
275        } catch (error) {
276          const parsedError = getErrorTextFromError(error, TxAction.APPROVAL, false);
277          setTxError(parsedError);
278          setApprovalTxState({
279            txHash: undefined,
280            loading: false,
281          });
282        }
283      }
284    };
285  
286    useEffect(() => {
287      if (skip) {
288        setLoadingTxns(false);
289        setSignatures([]);
290        setActionTx(undefined);
291        return;
292      }
293      setLoadingTxns(true);
294      const timeout = setTimeout(async () => {
295        if (delegationTokenType === DelegationTokenType.ALL) {
296          const gas = gasLimitRecommendations[ProtocolAction.default];
297          setGasLimit(gas.limit);
298          setMainTxState({
299            txHash: undefined,
300          });
301          setTxError(undefined);
302          setLoadingTxns(false);
303        } else {
304          let txs: EthereumTransactionTypeExtended[] = [];
305          if (delegationType === DelegationType.ALL) {
306            // TODO check if this is working as normal
307            txs = await delegate({
308              delegatee,
309              governanceToken:
310                delegationTokenType === DelegationTokenType.AAVE
311                  ? governanceV3Config.votingAssets.aaveTokenAddress
312                  : delegationTokenType === DelegationTokenType.STKAAVE
313                  ? governanceV3Config.votingAssets.stkAaveTokenAddress
314                  : governanceV3Config.votingAssets.aAaveTokenAddress,
315              // delegationTokenType === DelegationTokenType.AAVE
316              //   ? governanceConfig.aaveTokenAddress
317              //   : governanceConfig.stkAaveTokenAddress,
318            });
319          } else {
320            // TODO check if this is working as normal
321  
322            txs = await delegateByType({
323              delegatee,
324              delegationType: delegationType.toString(),
325              governanceToken:
326                delegationTokenType === DelegationTokenType.AAVE
327                  ? governanceV3Config.votingAssets.aaveTokenAddress
328                  : delegationTokenType === DelegationTokenType.STKAAVE
329                  ? governanceV3Config.votingAssets.stkAaveTokenAddress
330                  : governanceV3Config.votingAssets.aAaveTokenAddress,
331              // delegationTokenType === DelegationTokenType.AAVE
332              //   ? governanceConfig.aaveTokenAddress
333              //   : governanceConfig.stkAaveTokenAddress,
334            });
335          }
336          setActionTx(txs[0]);
337          setMainTxState({ txHash: undefined });
338          setTxError(undefined);
339          let gasLimit = 0;
340          try {
341            for (const tx of txs) {
342              const txGas = await tx.gas();
343              if (txGas && txGas.gasLimit) {
344                gasLimit = gasLimit + Number(txGas.gasLimit);
345              }
346            }
347          } catch (error) {
348            const parsedError = getErrorTextFromError(error, TxAction.GAS_ESTIMATION, false);
349            setTxError(parsedError);
350          }
351          setGasLimit(gasLimit.toString() || '');
352          setLoadingTxns(false);
353        }
354      }, 1000);
355      return () => clearTimeout(timeout);
356    }, [delegationTokenType, delegationType, delegatee, skip]);
357  
358    return { approvalTxState, signMetaTxs, mainTxState, loadingTxns, action, isSignatureAction };
359  };