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 };