Skip to content

Commit 17be395

Browse files
feat: add useDirectAllocation hook
1 parent 7601f00 commit 17be395

File tree

2 files changed

+279
-61
lines changed

2 files changed

+279
-61
lines changed
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
import { datadogLogs } from "@datadog/browser-logs";
2+
import { ethers } from "ethers";
3+
import React, {
4+
createContext,
5+
ReactNode,
6+
SetStateAction,
7+
useContext,
8+
useState,
9+
} from "react";
10+
11+
import { Allo, TToken } from "common";
12+
import { ProgressStatus } from "../../api/types";
13+
import { getAddress } from "viem";
14+
15+
export interface DirectAllocationState {
16+
tokenApprovalStatus: ProgressStatus;
17+
setTokenApprovalStatus: React.Dispatch<SetStateAction<ProgressStatus>>;
18+
fundStatus: ProgressStatus;
19+
setFundStatus: React.Dispatch<SetStateAction<ProgressStatus>>;
20+
indexingStatus: ProgressStatus;
21+
setIndexingStatus: React.Dispatch<SetStateAction<ProgressStatus>>;
22+
txHash: string;
23+
setTxHash: React.Dispatch<SetStateAction<string>>;
24+
txBlockNumber: number;
25+
setTxBlockNumber: React.Dispatch<SetStateAction<number>>;
26+
}
27+
28+
export type DirectAllocationParams = {
29+
allo: Allo;
30+
poolId: string;
31+
fundAmount: number;
32+
payoutToken: TToken;
33+
recipient: string;
34+
nonce: number;
35+
requireTokenApproval?: boolean;
36+
};
37+
38+
type SubmitFundParams = DirectAllocationParams & {
39+
context: DirectAllocationState;
40+
};
41+
42+
export const DirectAllocationProvider = ({
43+
children,
44+
}: {
45+
children: ReactNode;
46+
}) => {
47+
const [tokenApprovalStatus, setTokenApprovalStatus] = useState(
48+
initialDirectAllocationState.tokenApprovalStatus
49+
);
50+
const [fundStatus, setFundStatus] = useState(
51+
initialDirectAllocationState.fundStatus
52+
);
53+
const [indexingStatus, setIndexingStatus] = useState(
54+
initialDirectAllocationState.indexingStatus
55+
);
56+
const [txHash, setTxHash] = useState(initialDirectAllocationState.txHash);
57+
const [txBlockNumber, setTxBlockNumber] = useState(
58+
initialDirectAllocationState.txBlockNumber
59+
);
60+
61+
const providerProps: DirectAllocationState = {
62+
tokenApprovalStatus,
63+
setTokenApprovalStatus,
64+
fundStatus,
65+
setFundStatus,
66+
indexingStatus,
67+
setIndexingStatus,
68+
txHash,
69+
setTxHash,
70+
txBlockNumber,
71+
setTxBlockNumber,
72+
};
73+
74+
return (
75+
<DirectAllocationContext.Provider value={providerProps}>
76+
{children}
77+
</DirectAllocationContext.Provider>
78+
);
79+
};
80+
81+
export const initialDirectAllocationState: DirectAllocationState = {
82+
tokenApprovalStatus: ProgressStatus.NOT_STARTED,
83+
setTokenApprovalStatus: () => {
84+
/**/
85+
},
86+
fundStatus: ProgressStatus.NOT_STARTED,
87+
setFundStatus: () => {
88+
/**/
89+
},
90+
indexingStatus: ProgressStatus.NOT_STARTED,
91+
setIndexingStatus: () => {
92+
/**/
93+
},
94+
txHash: "",
95+
setTxHash: () => {
96+
/**/
97+
},
98+
txBlockNumber: -1,
99+
setTxBlockNumber: () => {
100+
/**/
101+
},
102+
};
103+
104+
export const DirectAllocationContext = createContext<DirectAllocationState>(
105+
initialDirectAllocationState
106+
);
107+
108+
export const useDirectAllocation = () => {
109+
const context = useContext<DirectAllocationState>(DirectAllocationContext);
110+
if (context === undefined) {
111+
throw new Error(
112+
"useDirectAllocation must be used within a DirectAllocationProvider"
113+
);
114+
}
115+
116+
const handleDirectAllocation = async (params: DirectAllocationParams) => {
117+
return _directAllocation({
118+
...params,
119+
context,
120+
});
121+
};
122+
return {
123+
directAllocation: handleDirectAllocation,
124+
tokenApprovalStatus: context.tokenApprovalStatus,
125+
fundStatus: context.fundStatus,
126+
indexingStatus: context.indexingStatus,
127+
txHash: context.txHash,
128+
txBlockNumber: context.txBlockNumber,
129+
};
130+
};
131+
132+
function resetToInitialState(context: DirectAllocationState) {
133+
const {
134+
setTokenApprovalStatus,
135+
setFundStatus,
136+
setIndexingStatus,
137+
setTxHash,
138+
setTxBlockNumber,
139+
} = context;
140+
141+
setTokenApprovalStatus(initialDirectAllocationState.tokenApprovalStatus);
142+
setFundStatus(initialDirectAllocationState.fundStatus);
143+
setIndexingStatus(initialDirectAllocationState.indexingStatus);
144+
setTxHash(initialDirectAllocationState.txHash);
145+
setTxBlockNumber(initialDirectAllocationState.txBlockNumber);
146+
}
147+
148+
async function _directAllocation({
149+
allo,
150+
context,
151+
poolId,
152+
fundAmount,
153+
payoutToken,
154+
recipient,
155+
nonce,
156+
requireTokenApproval,
157+
}: SubmitFundParams) {
158+
resetToInitialState(context);
159+
160+
try {
161+
const amount = ethers.utils
162+
.parseUnits(fundAmount.toString(), payoutToken.decimals)
163+
.toBigInt();
164+
165+
const recipientAddress = getAddress(recipient);
166+
const projectNonce = BigInt(nonce);
167+
168+
context.setTokenApprovalStatus(ProgressStatus.IN_PROGRESS);
169+
170+
const result = await allo
171+
.directAllocation({
172+
poolId,
173+
tokenAddress: payoutToken.address,
174+
amount,
175+
recipient: recipientAddress,
176+
nonce: projectNonce,
177+
requireTokenApproval,
178+
})
179+
.on("tokenApprovalStatus", (tx) => {
180+
if (tx.type === "error") {
181+
context.setTokenApprovalStatus(ProgressStatus.IS_ERROR);
182+
} else {
183+
context.setTokenApprovalStatus(ProgressStatus.IS_SUCCESS);
184+
context.setFundStatus(ProgressStatus.IN_PROGRESS);
185+
}
186+
})
187+
.on("transaction", (tx) => {
188+
if (tx.type === "error") {
189+
context.setFundStatus(ProgressStatus.IS_ERROR);
190+
}
191+
})
192+
.on("transactionStatus", (tx) => {
193+
if (tx.type === "error") {
194+
context.setFundStatus(ProgressStatus.IS_ERROR);
195+
} else {
196+
context.setFundStatus(ProgressStatus.IS_SUCCESS);
197+
context.setTxHash(tx.value.transactionHash);
198+
context.setTxBlockNumber(Number(tx.value.blockNumber));
199+
200+
context.setIndexingStatus(ProgressStatus.IN_PROGRESS);
201+
}
202+
})
203+
.execute();
204+
205+
if (result.type === "error") {
206+
throw result.error;
207+
}
208+
} catch (error) {
209+
datadogLogs.logger.error(`error: _directAllocation - ${error}`);
210+
console.error("Error while donating: ", error);
211+
}
212+
}

packages/grant-explorer/src/index.tsx

Lines changed: 67 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import AlloWrapper from "./features/api/AlloWrapper";
3535
import { PostHogProvider } from "posthog-js/react";
3636
import ViewProject from "./features/projects/ViewProject";
3737
import { ExploreProjectsPage } from "./features/discovery/ExploreProjectsPage";
38+
import { DirectAllocationProvider } from "./features/projects/hooks/useDirectAllocation";
3839

3940
initSentry();
4041
initDatadog();
@@ -68,67 +69,72 @@ root.render(
6869
<QueryClientProvider client={queryClient}>
6970
<RainbowKitProvider>
7071
<ChakraProvider>
71-
<AlloWrapper>
72-
<RoundProvider>
73-
<DataLayerProvider client={dataLayer}>
74-
<HashRouter>
75-
<Routes>
76-
{/* Protected Routes */}
77-
<Route element={<Auth />} />
78-
79-
{/* Default Route */}
80-
<Route path="/" element={<LandingPage />} />
81-
82-
<Route path="/rounds" element={<ExploreRoundsPage />} />
83-
84-
{/* Round Routes */}
85-
<Route
86-
path="/round/:chainId/:roundId"
87-
element={<ViewRound />}
88-
/>
89-
<Route
90-
path="/round/:chainId/:roundId/:applicationId"
91-
element={<ViewProjectDetails />}
92-
/>
93-
94-
{/* Project Routes */}
95-
96-
<Route
97-
path="/projects"
98-
element={<ExploreProjectsPage />}
99-
/>
100-
101-
<Route
102-
path="/projects/:projectId"
103-
element={<ViewProject />}
104-
/>
105-
106-
<Route path="/cart" element={<ViewCart />} />
107-
108-
<Route path="/thankyou" element={<ThankYou />} />
109-
110-
<Route
111-
path="/contributors/:address"
112-
element={<ViewContributionHistoryPage />}
113-
/>
114-
115-
{/* Access Denied */}
116-
<Route
117-
path="/access-denied"
118-
element={<AccessDenied />}
119-
/>
120-
<Route
121-
path="/collections/:collectionCid"
122-
element={<ExploreApplicationsPage />}
123-
/>
124-
125-
{/* 404 */}
126-
<Route path="*" element={<NotFound />} />
127-
</Routes>
128-
</HashRouter>
129-
</DataLayerProvider>
130-
</RoundProvider>
131-
</AlloWrapper>
72+
<DirectAllocationProvider>
73+
<AlloWrapper>
74+
<RoundProvider>
75+
<DataLayerProvider client={dataLayer}>
76+
<HashRouter>
77+
<Routes>
78+
{/* Protected Routes */}
79+
<Route element={<Auth />} />
80+
81+
{/* Default Route */}
82+
<Route path="/" element={<LandingPage />} />
83+
84+
<Route
85+
path="/rounds"
86+
element={<ExploreRoundsPage />}
87+
/>
88+
89+
{/* Round Routes */}
90+
<Route
91+
path="/round/:chainId/:roundId"
92+
element={<ViewRound />}
93+
/>
94+
<Route
95+
path="/round/:chainId/:roundId/:applicationId"
96+
element={<ViewProjectDetails />}
97+
/>
98+
99+
{/* Project Routes */}
100+
101+
<Route
102+
path="/projects"
103+
element={<ExploreProjectsPage />}
104+
/>
105+
106+
<Route
107+
path="/projects/:projectId"
108+
element={<ViewProject />}
109+
/>
110+
111+
<Route path="/cart" element={<ViewCart />} />
112+
113+
<Route path="/thankyou" element={<ThankYou />} />
114+
115+
<Route
116+
path="/contributors/:address"
117+
element={<ViewContributionHistoryPage />}
118+
/>
119+
120+
{/* Access Denied */}
121+
<Route
122+
path="/access-denied"
123+
element={<AccessDenied />}
124+
/>
125+
<Route
126+
path="/collections/:collectionCid"
127+
element={<ExploreApplicationsPage />}
128+
/>
129+
130+
{/* 404 */}
131+
<Route path="*" element={<NotFound />} />
132+
</Routes>
133+
</HashRouter>
134+
</DataLayerProvider>
135+
</RoundProvider>
136+
</AlloWrapper>
137+
</DirectAllocationProvider>
132138
</ChakraProvider>
133139
</RainbowKitProvider>
134140
</QueryClientProvider>

0 commit comments

Comments
 (0)