Skip to content

Commit 88c88ea

Browse files
committed
feat: init
1 parent c921cb8 commit 88c88ea

File tree

7 files changed

+619
-108
lines changed

7 files changed

+619
-108
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
"dependencies": {
3434
"@aave/contract-helpers": "1.36.1",
3535
"@aave/math-utils": "1.36.1",
36-
"@aave/react": "^0.4.0",
36+
"@aave/react": "canary",
3737
"@amplitude/analytics-browser": "^2.13.0",
3838
"@bgd-labs/aave-address-book": "^4.25.1",
3939
"@cowprotocol/app-data": "^3.1.0",
@@ -156,4 +156,4 @@
156156
"budgetPercentIncreaseRed": 20,
157157
"showDetails": true
158158
}
159-
}
159+
}

src/components/transactions/ClaimRewards/ClaimRewardsActions.tsx

Lines changed: 140 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1-
import { ProtocolAction } from '@aave/contract-helpers';
1+
import { ProtocolAction, EthereumTransactionTypeExtended, eEthereumTxType } from '@aave/contract-helpers';
22
import { Trans } from '@lingui/macro';
3+
import { utils } from 'ethers';
34
import { Reward } from 'src/helpers/types';
45
import { useTransactionHandler } from 'src/helpers/useTransactionHandler';
56
import { useAppDataContext } from 'src/hooks/app-data-provider/useAppDataProvider';
67
import { useRootStore } from 'src/store/root';
78

9+
import { useMeritClaimRewards } from '@aave/react';
10+
import { useShallow } from 'zustand/shallow';
11+
import { useWeb3Context } from 'src/libs/hooks/useWeb3Context';
12+
13+
814
import { TxActionsWrapper } from '../TxActionsWrapper';
915

1016
export type ClaimRewardsActionsProps = {
@@ -21,6 +27,15 @@ export const ClaimRewardsActions = ({
2127
const claimRewards = useRootStore((state) => state.claimRewards);
2228
const { reserves } = useAppDataContext();
2329

30+
const { currentAccount } = useWeb3Context();
31+
32+
33+
const [currentMarketData] = useRootStore(
34+
useShallow((store) => [store.currentMarketData])
35+
);
36+
37+
const { data: meritClaimRewards } = useMeritClaimRewards({ user: currentAccount, chainId: currentMarketData.chainId });
38+
2439
const { action, loadingTxns, mainTxState, requiresApproval } = useTransactionHandler({
2540
protocolAction: ProtocolAction.claimRewards,
2641
eventTxInfo: {
@@ -29,12 +44,130 @@ export const ClaimRewardsActions = ({
2944
},
3045
tryPermit: false,
3146
handleGetTxns: async () => {
32-
return claimRewards({ isWrongNetwork, blocked, selectedReward, formattedReserves: reserves });
47+
// Check if we need to claim both protocol and merit rewards
48+
const isClaimingAll = selectedReward.symbol === 'all';
49+
const isClaimingMeritAll = selectedReward.symbol === 'merit-all';
50+
const isClaimingProtocolAll = selectedReward.symbol === 'protocol-all';
51+
const hasProtocolRewards = selectedReward.incentiveControllerAddress !== 'MERIT_REWARD';
52+
const hasMeritRewards = meritClaimRewards?.rewards && meritClaimRewards.rewards.length > 0;
53+
const isIndividualProtocolReward = hasProtocolRewards && !isClaimingAll && !isClaimingProtocolAll && !isClaimingMeritAll;
54+
55+
// Use simple approach for individual protocol rewards (most common case)
56+
if (isIndividualProtocolReward) {
57+
return claimRewards({ isWrongNetwork, blocked, selectedReward, formattedReserves: reserves });
58+
}
59+
60+
// Use complex multicall logic only when needed
61+
if (isClaimingAll && hasProtocolRewards && hasMeritRewards) {
62+
// Get protocol rewards transaction
63+
const protocolTxns = await claimRewards({
64+
isWrongNetwork,
65+
blocked,
66+
selectedReward,
67+
formattedReserves: reserves
68+
});
69+
70+
// Create multicall transaction that includes both protocol and merit claims
71+
if (!meritClaimRewards?.transaction) {
72+
throw new Error('Merit rewards transaction not available');
73+
}
74+
const multicallTx = await createMulticallTransaction(protocolTxns, meritClaimRewards.transaction);
75+
76+
// Check if there are any approval transactions that need to be handled separately
77+
const approvalTxns = protocolTxns.filter(tx => tx.txType === 'ERC20_APPROVAL');
78+
79+
return approvalTxns.length > 0 ? [...approvalTxns, multicallTx] : [multicallTx];
80+
} else if ((isClaimingAll && !hasProtocolRewards && hasMeritRewards) || isClaimingMeritAll) {
81+
// Only merit rewards - use merit transaction directly
82+
if (!meritClaimRewards?.transaction) {
83+
throw new Error('Merit rewards transaction not available');
84+
}
85+
return [convertMeritTransactionToEthereum(meritClaimRewards.transaction)];
86+
} else {
87+
// Protocol-all or other cases - use existing protocol logic
88+
return claimRewards({ isWrongNetwork, blocked, selectedReward, formattedReserves: reserves });
89+
}
3390
},
3491
skip: Object.keys(selectedReward).length === 0 || blocked,
35-
deps: [selectedReward],
92+
deps: [selectedReward, meritClaimRewards],
3693
});
3794

95+
// Helper function to create multicall transaction
96+
const createMulticallTransaction = async (
97+
protocolTxns: EthereumTransactionTypeExtended[],
98+
meritTransaction: any
99+
): Promise<EthereumTransactionTypeExtended> => {
100+
// Multicall3 contract address (same across chains)
101+
const multicallAddress = '0xcA11bde05977b3631167028862bE2a173976CA11';
102+
103+
// Prepare calls array for multicall
104+
const calls = [];
105+
106+
// Add protocol transaction calls
107+
for (const txExt of protocolTxns) {
108+
if (txExt.txType === 'ERC20_APPROVAL') continue; // Skip approvals for multicall
109+
110+
const tx = await txExt.tx();
111+
calls.push({
112+
target: tx.to,
113+
callData: tx.data,
114+
value: tx.value || '0'
115+
});
116+
}
117+
118+
// Add merit transaction call
119+
calls.push({
120+
target: meritTransaction.to,
121+
callData: meritTransaction.data,
122+
value: meritTransaction.value || '0'
123+
});
124+
125+
// Encode multicall
126+
const multicallInterface = new utils.Interface([
127+
'function aggregate3Value((address target, bool allowFailure, uint256 value, bytes callData)[] calls) payable returns ((bool success, bytes returnData)[])'
128+
]);
129+
130+
const callsWithFailure = calls.map(call => [
131+
call.target,
132+
false, // allowFailure = false
133+
call.value,
134+
call.callData
135+
]);
136+
137+
const data = multicallInterface.encodeFunctionData('aggregate3Value', [callsWithFailure]);
138+
139+
return {
140+
txType: eEthereumTxType.DLP_ACTION,
141+
tx: async () => ({
142+
to: multicallAddress,
143+
from: currentAccount,
144+
data,
145+
value: '0',
146+
}),
147+
gas: async () => ({
148+
gasLimit: '800000', // Conservative gas limit for multicall
149+
gasPrice: '0',
150+
}),
151+
};
152+
};
153+
154+
// Helper function to convert merit transaction to Ethereum format
155+
const convertMeritTransactionToEthereum = (meritTx: any): EthereumTransactionTypeExtended => {
156+
return {
157+
txType: eEthereumTxType.DLP_ACTION,
158+
tx: async () => ({
159+
to: meritTx.to,
160+
from: meritTx.from || currentAccount,
161+
data: meritTx.data,
162+
value: meritTx.value || '0',
163+
}),
164+
gas: async () => ({
165+
gasLimit: '400000', // Conservative gas limit for merit only
166+
gasPrice: '0',
167+
}),
168+
};
169+
};
170+
38171
return (
39172
<TxActionsWrapper
40173
requiresApproval={requiresApproval}
@@ -45,6 +178,10 @@ export const ClaimRewardsActions = ({
45178
actionText={
46179
selectedReward.symbol === 'all' ? (
47180
<Trans>Claim all</Trans>
181+
) : selectedReward.symbol === 'merit-all' ? (
182+
<Trans>Claim all merit rewards</Trans>
183+
) : selectedReward.symbol === 'protocol-all' ? (
184+
<Trans>Claim all protocol rewards</Trans>
48185
) : (
49186
<Trans>Claim {selectedReward.symbol}</Trans>
50187
)

0 commit comments

Comments
 (0)