@trustless-work/blocks 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +96 -0
- package/bin/index.js +1123 -0
- package/package.json +44 -0
- package/templates/deps.json +29 -0
- package/templates/escrows/details/Actions.tsx +149 -0
- package/templates/escrows/details/Entities.tsx +48 -0
- package/templates/escrows/details/EntityCard.tsx +98 -0
- package/templates/escrows/details/EscrowDetailDialog.tsx +154 -0
- package/templates/escrows/details/GeneralInformation.tsx +329 -0
- package/templates/escrows/details/MilestoneCard.tsx +254 -0
- package/templates/escrows/details/MilestoneDetailDialog.tsx +276 -0
- package/templates/escrows/details/Milestones.tsx +87 -0
- package/templates/escrows/details/ProgressEscrow.tsx +191 -0
- package/templates/escrows/details/StatisticsCard.tsx +79 -0
- package/templates/escrows/details/SuccessReleaseDialog.tsx +101 -0
- package/templates/escrows/details/useDetailsEscrow.ts +126 -0
- package/templates/escrows/escrow-context/EscrowAmountProvider.tsx +86 -0
- package/templates/escrows/escrow-context/EscrowDialogsProvider.tsx +108 -0
- package/templates/escrows/escrow-context/EscrowProvider.tsx +124 -0
- package/templates/escrows/escrows-by-role/cards/EscrowsCards.tsx +503 -0
- package/templates/escrows/escrows-by-role/cards/Filters.tsx +421 -0
- package/templates/escrows/escrows-by-role/table/EscrowsTable.tsx +427 -0
- package/templates/escrows/escrows-by-role/table/Filters.tsx +421 -0
- package/templates/escrows/escrows-by-role/useEscrowsByRole.shared.ts +336 -0
- package/templates/escrows/escrows-by-signer/cards/EscrowsCards.tsx +502 -0
- package/templates/escrows/escrows-by-signer/cards/Filters.tsx +389 -0
- package/templates/escrows/escrows-by-signer/table/EscrowsTable.tsx +422 -0
- package/templates/escrows/escrows-by-signer/table/Filters.tsx +389 -0
- package/templates/escrows/escrows-by-signer/useEscrowsBySigner.shared.ts +320 -0
- package/templates/escrows/single-release/approve-milestone/button/ApproveMilestone.tsx +78 -0
- package/templates/escrows/single-release/approve-milestone/dialog/ApproveMilestone.tsx +102 -0
- package/templates/escrows/single-release/approve-milestone/form/ApproveMilestone.tsx +80 -0
- package/templates/escrows/single-release/approve-milestone/shared/schema.ts +9 -0
- package/templates/escrows/single-release/approve-milestone/shared/useApproveMilestone.ts +67 -0
- package/templates/escrows/single-release/change-milestone-status/button/ChangeMilestoneStatus.tsx +78 -0
- package/templates/escrows/single-release/change-milestone-status/dialog/ChangeMilestoneStatus.tsx +167 -0
- package/templates/escrows/single-release/change-milestone-status/form/ChangeMilestoneStatus.tsx +114 -0
- package/templates/escrows/single-release/change-milestone-status/shared/schema.ts +15 -0
- package/templates/escrows/single-release/change-milestone-status/shared/useChangeMilestoneStatus.ts +77 -0
- package/templates/escrows/single-release/dispute-escrow/button/DisputeEscrow.tsx +68 -0
- package/templates/escrows/single-release/fund-escrow/button/FundEscrow.tsx +84 -0
- package/templates/escrows/single-release/fund-escrow/dialog/FundEscrow.tsx +77 -0
- package/templates/escrows/single-release/fund-escrow/form/FundEscrow.tsx +54 -0
- package/templates/escrows/single-release/fund-escrow/shared/schema.ts +10 -0
- package/templates/escrows/single-release/fund-escrow/shared/useFundEscrow.ts +66 -0
- package/templates/escrows/single-release/initialize-escrow/dialog/InitializeEscrow.tsx +526 -0
- package/templates/escrows/single-release/initialize-escrow/form/InitializeEscrow.tsx +504 -0
- package/templates/escrows/single-release/initialize-escrow/shared/schema.ts +232 -0
- package/templates/escrows/single-release/initialize-escrow/shared/useInitializeEscrow.ts +115 -0
- package/templates/escrows/single-release/release-escrow/button/ReleaseEscrow.tsx +80 -0
- package/templates/escrows/single-release/resolve-dispute/button/ResolveDispute.tsx +94 -0
- package/templates/escrows/single-release/resolve-dispute/dialog/ResolveDispute.tsx +123 -0
- package/templates/escrows/single-release/resolve-dispute/form/ResolveDispute.tsx +82 -0
- package/templates/escrows/single-release/resolve-dispute/shared/schema.ts +82 -0
- package/templates/escrows/single-release/resolve-dispute/shared/useResolveDispute.ts +58 -0
- package/templates/escrows/single-release/update-escrow/dialog/UpdateEscrow.tsx +485 -0
- package/templates/escrows/single-release/update-escrow/form/UpdateEscrow.tsx +463 -0
- package/templates/escrows/single-release/update-escrow/shared/schema.ts +139 -0
- package/templates/escrows/single-release/update-escrow/shared/useUpdateEscrow.ts +211 -0
- package/templates/handle-errors/errors.enum.ts +6 -0
- package/templates/handle-errors/handle.ts +47 -0
- package/templates/helpers/format.helper.ts +27 -0
- package/templates/helpers/useCopy.ts +13 -0
- package/templates/providers/ReactQueryClientProvider.tsx +28 -0
- package/templates/providers/TrustlessWork.tsx +30 -0
- package/templates/tanstak/useEscrowsByRoleQuery.ts +87 -0
- package/templates/tanstak/useEscrowsBySignerQuery.ts +78 -0
- package/templates/tanstak/useEscrowsMutations.ts +411 -0
- package/templates/wallet-kit/WalletButtons.tsx +116 -0
- package/templates/wallet-kit/WalletProvider.tsx +94 -0
- package/templates/wallet-kit/trustlines.ts +40 -0
- package/templates/wallet-kit/useWallet.ts +77 -0
- package/templates/wallet-kit/validators.ts +12 -0
- package/templates/wallet-kit/wallet-kit.ts +30 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
2
|
+
import {
|
|
3
|
+
EscrowType,
|
|
4
|
+
FundEscrowPayload,
|
|
5
|
+
InitializeMultiReleaseEscrowPayload,
|
|
6
|
+
InitializeSingleReleaseEscrowPayload,
|
|
7
|
+
UpdateMultiReleaseEscrowPayload,
|
|
8
|
+
UpdateSingleReleaseEscrowPayload,
|
|
9
|
+
useFundEscrow,
|
|
10
|
+
useInitializeEscrow,
|
|
11
|
+
useUpdateEscrow,
|
|
12
|
+
ChangeMilestoneStatusPayload,
|
|
13
|
+
useChangeMilestoneStatus,
|
|
14
|
+
ApproveMilestonePayload,
|
|
15
|
+
useApproveMilestone,
|
|
16
|
+
useSendTransaction,
|
|
17
|
+
useStartDispute,
|
|
18
|
+
useReleaseFunds,
|
|
19
|
+
useResolveDispute,
|
|
20
|
+
MultiReleaseStartDisputePayload,
|
|
21
|
+
SingleReleaseStartDisputePayload,
|
|
22
|
+
MultiReleaseReleaseFundsPayload,
|
|
23
|
+
SingleReleaseReleaseFundsPayload,
|
|
24
|
+
MultiReleaseResolveDisputePayload,
|
|
25
|
+
SingleReleaseResolveDisputePayload,
|
|
26
|
+
} from "@trustless-work/escrow";
|
|
27
|
+
import { signTransaction } from "../wallet-kit/wallet-kit";
|
|
28
|
+
|
|
29
|
+
export const useEscrowsMutations = () => {
|
|
30
|
+
const queryClient = useQueryClient();
|
|
31
|
+
const { deployEscrow } = useInitializeEscrow();
|
|
32
|
+
const { updateEscrow } = useUpdateEscrow();
|
|
33
|
+
const { fundEscrow } = useFundEscrow();
|
|
34
|
+
const { changeMilestoneStatus } = useChangeMilestoneStatus();
|
|
35
|
+
const { approveMilestone } = useApproveMilestone();
|
|
36
|
+
const { sendTransaction } = useSendTransaction();
|
|
37
|
+
const { startDispute } = useStartDispute();
|
|
38
|
+
const { releaseFunds } = useReleaseFunds();
|
|
39
|
+
const { resolveDispute } = useResolveDispute();
|
|
40
|
+
|
|
41
|
+
const deployEscrowMutation = useMutation({
|
|
42
|
+
mutationFn: async ({
|
|
43
|
+
payload,
|
|
44
|
+
type,
|
|
45
|
+
address,
|
|
46
|
+
}: {
|
|
47
|
+
payload:
|
|
48
|
+
| InitializeSingleReleaseEscrowPayload
|
|
49
|
+
| InitializeMultiReleaseEscrowPayload;
|
|
50
|
+
type: EscrowType;
|
|
51
|
+
address: string;
|
|
52
|
+
}) => {
|
|
53
|
+
const { unsignedTransaction } = await deployEscrow(payload, type);
|
|
54
|
+
|
|
55
|
+
if (!unsignedTransaction) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
"Unsigned transaction is missing from deployEscrow response."
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const signedTxXdr = await signTransaction({
|
|
62
|
+
unsignedTransaction,
|
|
63
|
+
address,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (!signedTxXdr) {
|
|
67
|
+
throw new Error("Signed transaction is missing.");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const response = await sendTransaction(signedTxXdr);
|
|
71
|
+
|
|
72
|
+
if (response.status !== "SUCCESS") {
|
|
73
|
+
throw new Error("Transaction failed to send");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return response;
|
|
77
|
+
},
|
|
78
|
+
onSuccess: () => {
|
|
79
|
+
queryClient.invalidateQueries({ queryKey: ["escrows"] });
|
|
80
|
+
},
|
|
81
|
+
onError: (error) => {
|
|
82
|
+
console.error(error);
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const updateEscrowMutation = useMutation({
|
|
87
|
+
mutationFn: async ({
|
|
88
|
+
payload,
|
|
89
|
+
type,
|
|
90
|
+
address,
|
|
91
|
+
}: {
|
|
92
|
+
payload:
|
|
93
|
+
| UpdateSingleReleaseEscrowPayload
|
|
94
|
+
| UpdateMultiReleaseEscrowPayload;
|
|
95
|
+
type: EscrowType;
|
|
96
|
+
address: string;
|
|
97
|
+
}) => {
|
|
98
|
+
const { unsignedTransaction } = await updateEscrow(payload, type);
|
|
99
|
+
|
|
100
|
+
if (!unsignedTransaction) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
"Unsigned transaction is missing from updateEscrow response."
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const signedTxXdr = await signTransaction({
|
|
107
|
+
unsignedTransaction,
|
|
108
|
+
address,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
if (!signedTxXdr) {
|
|
112
|
+
throw new Error("Signed transaction is missing.");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const response = await sendTransaction(signedTxXdr);
|
|
116
|
+
|
|
117
|
+
if (response.status !== "SUCCESS") {
|
|
118
|
+
throw new Error("Transaction failed to send");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return response;
|
|
122
|
+
},
|
|
123
|
+
onSuccess: () => {
|
|
124
|
+
queryClient.invalidateQueries({ queryKey: ["escrows"] });
|
|
125
|
+
},
|
|
126
|
+
onError: (error) => {
|
|
127
|
+
console.error(error);
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const fundEscrowMutation = useMutation({
|
|
132
|
+
mutationFn: async ({
|
|
133
|
+
payload,
|
|
134
|
+
type,
|
|
135
|
+
address,
|
|
136
|
+
}: {
|
|
137
|
+
payload: FundEscrowPayload;
|
|
138
|
+
type: EscrowType;
|
|
139
|
+
address: string;
|
|
140
|
+
}) => {
|
|
141
|
+
// Step 1: Get unsigned transaction
|
|
142
|
+
const { unsignedTransaction } = await fundEscrow(payload, type);
|
|
143
|
+
|
|
144
|
+
if (!unsignedTransaction) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
"Unsigned transaction is missing from fundEscrow response."
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Step 2: Sign transaction
|
|
151
|
+
const signedTxXdr = await signTransaction({
|
|
152
|
+
unsignedTransaction,
|
|
153
|
+
address,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
if (!signedTxXdr) {
|
|
157
|
+
throw new Error("Signed transaction is missing.");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Step 3: Send transaction
|
|
161
|
+
const response = await sendTransaction(signedTxXdr);
|
|
162
|
+
|
|
163
|
+
if (response.status !== "SUCCESS") {
|
|
164
|
+
throw new Error("Transaction failed to send");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return response;
|
|
168
|
+
},
|
|
169
|
+
onSuccess: () => {
|
|
170
|
+
queryClient.invalidateQueries({ queryKey: ["escrows"] });
|
|
171
|
+
},
|
|
172
|
+
onError: (error) => {
|
|
173
|
+
console.error(error);
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const approveMilestoneMutation = useMutation({
|
|
178
|
+
mutationFn: async ({
|
|
179
|
+
payload,
|
|
180
|
+
type,
|
|
181
|
+
address,
|
|
182
|
+
}: {
|
|
183
|
+
payload: ApproveMilestonePayload;
|
|
184
|
+
type: EscrowType;
|
|
185
|
+
address: string;
|
|
186
|
+
}) => {
|
|
187
|
+
const { unsignedTransaction } = await approveMilestone(payload, type);
|
|
188
|
+
|
|
189
|
+
if (!unsignedTransaction) {
|
|
190
|
+
throw new Error(
|
|
191
|
+
"Unsigned transaction is missing from approveMilestone response."
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const signedTxXdr = await signTransaction({
|
|
196
|
+
unsignedTransaction,
|
|
197
|
+
address,
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
if (!signedTxXdr) {
|
|
201
|
+
throw new Error("Signed transaction is missing.");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const response = await sendTransaction(signedTxXdr);
|
|
205
|
+
|
|
206
|
+
if (response.status !== "SUCCESS") {
|
|
207
|
+
throw new Error("Transaction failed to send");
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return response;
|
|
211
|
+
},
|
|
212
|
+
onSuccess: () => {
|
|
213
|
+
queryClient.invalidateQueries({ queryKey: ["escrows"] });
|
|
214
|
+
},
|
|
215
|
+
onError: (error) => {
|
|
216
|
+
console.error(error);
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
const changeMilestoneStatusMutation = useMutation({
|
|
221
|
+
mutationFn: async ({
|
|
222
|
+
payload,
|
|
223
|
+
type,
|
|
224
|
+
address,
|
|
225
|
+
}: {
|
|
226
|
+
payload: ChangeMilestoneStatusPayload;
|
|
227
|
+
type: EscrowType;
|
|
228
|
+
address: string;
|
|
229
|
+
}) => {
|
|
230
|
+
const { unsignedTransaction } = await changeMilestoneStatus(
|
|
231
|
+
payload,
|
|
232
|
+
type
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
if (!unsignedTransaction) {
|
|
236
|
+
throw new Error(
|
|
237
|
+
"Unsigned transaction is missing from changeMilestoneStatus response."
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const signedTxXdr = await signTransaction({
|
|
242
|
+
unsignedTransaction,
|
|
243
|
+
address,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
if (!signedTxXdr) {
|
|
247
|
+
throw new Error("Signed transaction is missing.");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const response = await sendTransaction(signedTxXdr);
|
|
251
|
+
|
|
252
|
+
if (response.status !== "SUCCESS") {
|
|
253
|
+
throw new Error("Transaction failed to send");
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return response;
|
|
257
|
+
},
|
|
258
|
+
onSuccess: () => {
|
|
259
|
+
queryClient.invalidateQueries({ queryKey: ["escrows"] });
|
|
260
|
+
},
|
|
261
|
+
onError: (error) => {
|
|
262
|
+
console.error(error);
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
const startDisputeMutation = useMutation({
|
|
267
|
+
mutationFn: async ({
|
|
268
|
+
payload,
|
|
269
|
+
type,
|
|
270
|
+
address,
|
|
271
|
+
}: {
|
|
272
|
+
payload:
|
|
273
|
+
| MultiReleaseStartDisputePayload
|
|
274
|
+
| SingleReleaseStartDisputePayload;
|
|
275
|
+
type: EscrowType;
|
|
276
|
+
address: string;
|
|
277
|
+
}) => {
|
|
278
|
+
const { unsignedTransaction } = await startDispute(payload, type);
|
|
279
|
+
|
|
280
|
+
if (!unsignedTransaction) {
|
|
281
|
+
throw new Error(
|
|
282
|
+
"Unsigned transaction is missing from startDispute response."
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const signedTxXdr = await signTransaction({
|
|
287
|
+
unsignedTransaction,
|
|
288
|
+
address,
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
if (!signedTxXdr) {
|
|
292
|
+
throw new Error("Signed transaction is missing.");
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const response = await sendTransaction(signedTxXdr);
|
|
296
|
+
|
|
297
|
+
if (response.status !== "SUCCESS") {
|
|
298
|
+
throw new Error("Transaction failed to send");
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return response;
|
|
302
|
+
},
|
|
303
|
+
onSuccess: () => {
|
|
304
|
+
queryClient.invalidateQueries({ queryKey: ["escrows"] });
|
|
305
|
+
},
|
|
306
|
+
onError: (error) => {
|
|
307
|
+
console.error(error);
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
const releaseFundsMutation = useMutation({
|
|
312
|
+
mutationFn: async ({
|
|
313
|
+
payload,
|
|
314
|
+
type,
|
|
315
|
+
address,
|
|
316
|
+
}: {
|
|
317
|
+
payload:
|
|
318
|
+
| MultiReleaseReleaseFundsPayload
|
|
319
|
+
| SingleReleaseReleaseFundsPayload;
|
|
320
|
+
type: EscrowType;
|
|
321
|
+
address: string;
|
|
322
|
+
}) => {
|
|
323
|
+
const { unsignedTransaction } = await releaseFunds(payload, type);
|
|
324
|
+
|
|
325
|
+
if (!unsignedTransaction) {
|
|
326
|
+
throw new Error(
|
|
327
|
+
"Unsigned transaction is missing from releaseFunds response."
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const signedTxXdr = await signTransaction({
|
|
332
|
+
unsignedTransaction,
|
|
333
|
+
address,
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
if (!signedTxXdr) {
|
|
337
|
+
throw new Error("Signed transaction is missing.");
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const response = await sendTransaction(signedTxXdr);
|
|
341
|
+
|
|
342
|
+
if (response.status !== "SUCCESS") {
|
|
343
|
+
throw new Error("Transaction failed to send");
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return response;
|
|
347
|
+
},
|
|
348
|
+
onSuccess: () => {
|
|
349
|
+
queryClient.invalidateQueries({ queryKey: ["escrows"] });
|
|
350
|
+
},
|
|
351
|
+
onError: (error) => {
|
|
352
|
+
console.error(error);
|
|
353
|
+
},
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
const resolveDisputeMutation = useMutation({
|
|
357
|
+
mutationFn: async ({
|
|
358
|
+
payload,
|
|
359
|
+
type,
|
|
360
|
+
address,
|
|
361
|
+
}: {
|
|
362
|
+
payload:
|
|
363
|
+
| MultiReleaseResolveDisputePayload
|
|
364
|
+
| SingleReleaseResolveDisputePayload;
|
|
365
|
+
type: EscrowType;
|
|
366
|
+
address: string;
|
|
367
|
+
}) => {
|
|
368
|
+
const { unsignedTransaction } = await resolveDispute(payload, type);
|
|
369
|
+
|
|
370
|
+
if (!unsignedTransaction) {
|
|
371
|
+
throw new Error(
|
|
372
|
+
"Unsigned transaction is missing from resolveDispute response."
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const signedTxXdr = await signTransaction({
|
|
377
|
+
unsignedTransaction,
|
|
378
|
+
address,
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
if (!signedTxXdr) {
|
|
382
|
+
throw new Error("Signed transaction is missing.");
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const response = await sendTransaction(signedTxXdr);
|
|
386
|
+
|
|
387
|
+
if (response.status !== "SUCCESS") {
|
|
388
|
+
throw new Error("Transaction failed to send");
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return response;
|
|
392
|
+
},
|
|
393
|
+
onSuccess: () => {
|
|
394
|
+
queryClient.invalidateQueries({ queryKey: ["escrows"] });
|
|
395
|
+
},
|
|
396
|
+
onError: (error) => {
|
|
397
|
+
console.error(error);
|
|
398
|
+
},
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
return {
|
|
402
|
+
deployEscrow: deployEscrowMutation,
|
|
403
|
+
updateEscrow: updateEscrowMutation,
|
|
404
|
+
fundEscrow: fundEscrowMutation,
|
|
405
|
+
changeMilestoneStatus: changeMilestoneStatusMutation,
|
|
406
|
+
approveMilestone: approveMilestoneMutation,
|
|
407
|
+
startDispute: startDisputeMutation,
|
|
408
|
+
releaseFunds: releaseFundsMutation,
|
|
409
|
+
resolveDispute: resolveDisputeMutation,
|
|
410
|
+
};
|
|
411
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { useWallet } from "./useWallet";
|
|
5
|
+
import { useWalletContext } from "./WalletProvider";
|
|
6
|
+
import { Button } from "__UI_BASE__/button";
|
|
7
|
+
import { Popover, PopoverContent, PopoverTrigger } from "__UI_BASE__/popover";
|
|
8
|
+
import { Check, Copy, LogOut, ChevronDown, Wallet } from "lucide-react";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Wallet connection/disconnection button component
|
|
12
|
+
* Shows different states based on wallet connection status
|
|
13
|
+
*/
|
|
14
|
+
export const WalletButton = () => {
|
|
15
|
+
const { handleConnect, handleDisconnect } = useWallet();
|
|
16
|
+
const { walletAddress, walletName } = useWalletContext();
|
|
17
|
+
const [copied, setCopied] = React.useState(false);
|
|
18
|
+
|
|
19
|
+
const shortAddress = React.useMemo(() => {
|
|
20
|
+
if (!walletAddress) return "";
|
|
21
|
+
if (walletAddress.length <= 10) return walletAddress;
|
|
22
|
+
return `${walletAddress.slice(0, 6)}…${walletAddress.slice(-4)}`;
|
|
23
|
+
}, [walletAddress]);
|
|
24
|
+
|
|
25
|
+
const copyAddress = async () => {
|
|
26
|
+
if (!walletAddress) return;
|
|
27
|
+
try {
|
|
28
|
+
await navigator.clipboard.writeText(walletAddress);
|
|
29
|
+
setCopied(true);
|
|
30
|
+
setTimeout(() => setCopied(false), 1500);
|
|
31
|
+
} catch (_) {
|
|
32
|
+
// noop
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
if (walletAddress) {
|
|
37
|
+
return (
|
|
38
|
+
<Popover>
|
|
39
|
+
<PopoverTrigger asChild>
|
|
40
|
+
<Button
|
|
41
|
+
variant="outline"
|
|
42
|
+
className="h-10 px-4 gap-2 font-medium bg-transparent"
|
|
43
|
+
>
|
|
44
|
+
<Wallet className="h-4 w-4" />
|
|
45
|
+
<span className="hidden sm:inline">{walletName}</span>
|
|
46
|
+
<span className="font-mono text-sm text-muted-foreground">
|
|
47
|
+
{shortAddress}
|
|
48
|
+
</span>
|
|
49
|
+
<ChevronDown className="h-4 w-4 ml-1" />
|
|
50
|
+
</Button>
|
|
51
|
+
</PopoverTrigger>
|
|
52
|
+
<PopoverContent className="w-80 p-0" align="end">
|
|
53
|
+
<div className="p-4 space-y-3">
|
|
54
|
+
<div className="flex items-center justify-between">
|
|
55
|
+
<div className="flex items-center gap-2">
|
|
56
|
+
<Wallet className="h-4 w-4 text-muted-foreground" />
|
|
57
|
+
<span className="font-medium">{walletName}</span>
|
|
58
|
+
</div>
|
|
59
|
+
<span className="text-xs px-2 py-1 rounded-md bg-muted text-muted-foreground">
|
|
60
|
+
Testnet
|
|
61
|
+
</span>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<div className="p-3 rounded-lg bg-muted/50 border">
|
|
65
|
+
<p className="text-xs text-muted-foreground mb-1">Address</p>
|
|
66
|
+
<p className="font-mono text-sm break-all">{walletAddress}</p>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<div className="border-t p-4">
|
|
71
|
+
<div className="flex gap-2">
|
|
72
|
+
<Button
|
|
73
|
+
onClick={copyAddress}
|
|
74
|
+
variant="ghost"
|
|
75
|
+
size="sm"
|
|
76
|
+
className="flex-1 cursor-pointer"
|
|
77
|
+
disabled={copied}
|
|
78
|
+
>
|
|
79
|
+
{copied ? (
|
|
80
|
+
<>
|
|
81
|
+
<Check className="h-4 w-4 mr-2" />
|
|
82
|
+
Copied
|
|
83
|
+
</>
|
|
84
|
+
) : (
|
|
85
|
+
<>
|
|
86
|
+
<Copy className="h-4 w-4 mr-2" />
|
|
87
|
+
Copy
|
|
88
|
+
</>
|
|
89
|
+
)}
|
|
90
|
+
</Button>
|
|
91
|
+
<Button
|
|
92
|
+
onClick={handleDisconnect}
|
|
93
|
+
variant="outline"
|
|
94
|
+
size="sm"
|
|
95
|
+
className="flex-1 text-destructive hover:text-destructive bg-transparent cursor-pointer"
|
|
96
|
+
>
|
|
97
|
+
<LogOut className="h-4 w-4 mr-2" />
|
|
98
|
+
Disconnect
|
|
99
|
+
</Button>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</PopoverContent>
|
|
103
|
+
</Popover>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<Button
|
|
109
|
+
className="h-10 px-6 gap-2 font-medium cursor-pointer"
|
|
110
|
+
onClick={handleConnect}
|
|
111
|
+
>
|
|
112
|
+
<Wallet className="h-4 w-4" />
|
|
113
|
+
Connect Wallet
|
|
114
|
+
</Button>
|
|
115
|
+
);
|
|
116
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import {
|
|
5
|
+
createContext,
|
|
6
|
+
useContext,
|
|
7
|
+
useState,
|
|
8
|
+
useEffect,
|
|
9
|
+
ReactNode,
|
|
10
|
+
} from "react";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Type definition for the wallet context
|
|
14
|
+
* Contains wallet address, name, and functions to manage wallet state
|
|
15
|
+
*/
|
|
16
|
+
type WalletContextType = {
|
|
17
|
+
walletAddress: string | null;
|
|
18
|
+
walletName: string | null;
|
|
19
|
+
setWalletInfo: (address: string, name: string) => void;
|
|
20
|
+
clearWalletInfo: () => void;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Create the React context for wallet state management
|
|
25
|
+
*/
|
|
26
|
+
const WalletContext = createContext<WalletContextType | undefined>(undefined);
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Wallet Provider component that wraps the application
|
|
30
|
+
* Manages wallet state and provides wallet information to child components
|
|
31
|
+
* Automatically loads saved wallet information from localStorage on initialization
|
|
32
|
+
*/
|
|
33
|
+
export const WalletProvider = ({ children }: { children: ReactNode }) => {
|
|
34
|
+
const [walletAddress, setWalletAddress] = useState<string | null>(null);
|
|
35
|
+
const [walletName, setWalletName] = useState<string | null>(null);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Load saved wallet information from localStorage when the component mounts
|
|
39
|
+
* This ensures the wallet state persists across browser sessions
|
|
40
|
+
*/
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const storedAddress = localStorage.getItem("walletAddress");
|
|
43
|
+
const storedName = localStorage.getItem("walletName");
|
|
44
|
+
|
|
45
|
+
if (storedAddress) setWalletAddress(storedAddress);
|
|
46
|
+
if (storedName) setWalletName(storedName);
|
|
47
|
+
}, []);
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Set wallet information and save it to localStorage
|
|
51
|
+
* This function is called when a wallet is successfully connected
|
|
52
|
+
*
|
|
53
|
+
* @param address - The wallet's public address
|
|
54
|
+
* @param name - The name/identifier of the wallet (e.g., "Freighter", "Albedo")
|
|
55
|
+
*/
|
|
56
|
+
const setWalletInfo = (address: string, name: string) => {
|
|
57
|
+
setWalletAddress(address);
|
|
58
|
+
setWalletName(name);
|
|
59
|
+
localStorage.setItem("walletAddress", address);
|
|
60
|
+
localStorage.setItem("walletName", name);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Clear wallet information and remove it from localStorage
|
|
65
|
+
* This function is called when disconnecting a wallet
|
|
66
|
+
*/
|
|
67
|
+
const clearWalletInfo = () => {
|
|
68
|
+
setWalletAddress(null);
|
|
69
|
+
setWalletName(null);
|
|
70
|
+
localStorage.removeItem("walletAddress");
|
|
71
|
+
localStorage.removeItem("walletName");
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<WalletContext.Provider
|
|
76
|
+
value={{ walletAddress, walletName, setWalletInfo, clearWalletInfo }}
|
|
77
|
+
>
|
|
78
|
+
{children}
|
|
79
|
+
</WalletContext.Provider>
|
|
80
|
+
);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Custom hook to access the wallet context
|
|
85
|
+
* Provides wallet state and functions to components
|
|
86
|
+
* Throws an error if used outside of WalletProvider
|
|
87
|
+
*/
|
|
88
|
+
export const useWalletContext = () => {
|
|
89
|
+
const context = useContext(WalletContext);
|
|
90
|
+
if (!context) {
|
|
91
|
+
throw new Error("useWalletContext must be used within WalletProvider");
|
|
92
|
+
}
|
|
93
|
+
return context;
|
|
94
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export const trustlines = [
|
|
2
|
+
// TESTNET
|
|
3
|
+
{
|
|
4
|
+
name: "USDC",
|
|
5
|
+
address: "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA",
|
|
6
|
+
decimals: 10000000,
|
|
7
|
+
network: "testnet",
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
name: "EURC",
|
|
11
|
+
address: "GB3Q6QDZYTHWT7E5PVS3W7FUT5GVAFC5KSZFFLPU25GO7VTC3NM2ZTVO",
|
|
12
|
+
decimals: 10000000,
|
|
13
|
+
network: "testnet",
|
|
14
|
+
},
|
|
15
|
+
// MAINNET
|
|
16
|
+
{
|
|
17
|
+
name: "USDC",
|
|
18
|
+
address: "CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75",
|
|
19
|
+
decimals: 10000000,
|
|
20
|
+
network: "mainnet",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "EURC",
|
|
24
|
+
address: "GB3Q6QDZYTHWT7E5PVS3W7FUT5GVAFC5KSZFFLPU25GO7VTC3NM2ZTVO",
|
|
25
|
+
decimals: 10000000,
|
|
26
|
+
network: "mainnet",
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
// TODO: add network dynamic filter
|
|
31
|
+
export const trustlineOptions = Array.from(
|
|
32
|
+
new Map(
|
|
33
|
+
trustlines
|
|
34
|
+
.filter((trustline) => trustline.network === "testnet")
|
|
35
|
+
.map((trustline) => [
|
|
36
|
+
trustline.address,
|
|
37
|
+
{ value: trustline.address, label: trustline.name },
|
|
38
|
+
])
|
|
39
|
+
).values()
|
|
40
|
+
);
|