@trustless-work/blocks 1.0.0 → 1.0.2
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/bin/index.js +78 -1
- package/package.json +1 -1
- package/templates/deps.json +1 -1
- package/templates/escrows/details/Actions.tsx +21 -1
- package/templates/escrows/indicators/balance-progress/bar/BalanceProgress.tsx +55 -0
- package/templates/escrows/indicators/balance-progress/donut/BalanceProgress.tsx +99 -0
- package/templates/escrows/multi-release/initialize-escrow/dialog/InitializeEscrow.tsx +1 -0
- package/templates/escrows/multi-release/initialize-escrow/form/InitializeEscrow.tsx +1 -0
- package/templates/escrows/multi-release/initialize-escrow/shared/schema.ts +0 -1
- package/templates/escrows/multi-release/initialize-escrow/shared/useInitializeEscrow.ts +0 -2
- package/templates/escrows/multi-release/resolve-dispute/button/ResolveDispute.tsx +10 -20
- package/templates/escrows/multi-release/resolve-dispute/dialog/ResolveDispute.tsx +117 -60
- package/templates/escrows/multi-release/resolve-dispute/form/ResolveDispute.tsx +111 -55
- package/templates/escrows/multi-release/resolve-dispute/shared/schema.ts +68 -71
- package/templates/escrows/multi-release/resolve-dispute/shared/useResolveDispute.ts +107 -21
- package/templates/escrows/multi-release/update-escrow/shared/schema.ts +0 -1
- package/templates/escrows/multi-release/update-escrow/shared/useUpdateEscrow.ts +0 -4
- package/templates/escrows/multi-release/withdraw-remaining-funds/button/WithdrawRemainingFunds.tsx +85 -0
- package/templates/escrows/multi-release/withdraw-remaining-funds/dialog/WithdrawRemainingFunds.tsx +176 -0
- package/templates/escrows/multi-release/withdraw-remaining-funds/form/WithdrawRemainingFunds.tsx +153 -0
- package/templates/escrows/multi-release/withdraw-remaining-funds/shared/schema.ts +81 -0
- package/templates/escrows/multi-release/withdraw-remaining-funds/shared/useWithdrawRemainingFunds.ts +160 -0
- package/templates/escrows/single-multi-release/approve-milestone/button/ApproveMilestone.tsx +0 -1
- package/templates/escrows/single-multi-release/approve-milestone/shared/useApproveMilestone.ts +0 -1
- package/templates/escrows/single-release/initialize-escrow/shared/schema.ts +0 -1
- package/templates/escrows/single-release/initialize-escrow/shared/useInitializeEscrow.ts +0 -2
- package/templates/escrows/single-release/resolve-dispute/button/ResolveDispute.tsx +15 -31
- package/templates/escrows/single-release/resolve-dispute/dialog/ResolveDispute.tsx +116 -60
- package/templates/escrows/single-release/resolve-dispute/form/ResolveDispute.tsx +98 -43
- package/templates/escrows/single-release/resolve-dispute/shared/schema.ts +65 -68
- package/templates/escrows/single-release/resolve-dispute/shared/useResolveDispute.ts +100 -22
- package/templates/escrows/single-release/update-escrow/shared/schema.ts +0 -1
- package/templates/escrows/single-release/update-escrow/shared/useUpdateEscrow.ts +0 -4
- package/templates/tanstack/useEscrowsMutations.ts +53 -0
- package/templates/tanstack/useGetMultipleEscrowBalances.ts +41 -0
- package/templates/wallet-kit/trustlines.ts +0 -4
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
} from "@/components/tw-blocks/handle-errors/handle";
|
|
13
13
|
import { useWalletContext } from "@/components/tw-blocks/wallet-kit/WalletProvider";
|
|
14
14
|
|
|
15
|
+
type DistributionInput = { address: string; amount: string | number };
|
|
16
|
+
|
|
15
17
|
export function useResolveDispute() {
|
|
16
18
|
const { resolveDispute } = useEscrowsMutations();
|
|
17
19
|
const { selectedEscrow, updateEscrow } = useEscrowContext();
|
|
@@ -20,38 +22,98 @@ export function useResolveDispute() {
|
|
|
20
22
|
const form = useForm<ResolveDisputeValues>({
|
|
21
23
|
resolver: zodResolver(resolveDisputeSchema),
|
|
22
24
|
defaultValues: {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
distributions: [
|
|
26
|
+
{ address: "", amount: "" },
|
|
27
|
+
{ address: "", amount: "" },
|
|
28
|
+
],
|
|
25
29
|
},
|
|
26
30
|
mode: "onChange",
|
|
27
31
|
});
|
|
28
32
|
|
|
33
|
+
const distributions = form.watch("distributions") as DistributionInput[];
|
|
34
|
+
|
|
29
35
|
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
|
30
36
|
|
|
37
|
+
const allowedAmount = React.useMemo(() => {
|
|
38
|
+
return Number(selectedEscrow?.amount || 0);
|
|
39
|
+
}, [selectedEscrow]);
|
|
40
|
+
|
|
41
|
+
const distributedSum = React.useMemo(() => {
|
|
42
|
+
return (distributions || []).reduce((acc, d) => {
|
|
43
|
+
const n = Number(d?.amount ?? 0);
|
|
44
|
+
return acc + (isNaN(n) ? 0 : n);
|
|
45
|
+
}, 0);
|
|
46
|
+
}, [distributions]);
|
|
47
|
+
|
|
48
|
+
const isExactMatch = React.useMemo(() => {
|
|
49
|
+
return Number(allowedAmount) === Number(distributedSum);
|
|
50
|
+
}, [allowedAmount, distributedSum]);
|
|
51
|
+
|
|
52
|
+
const difference = React.useMemo(() => {
|
|
53
|
+
return Math.abs(Number(allowedAmount) - Number(distributedSum));
|
|
54
|
+
}, [allowedAmount, distributedSum]);
|
|
55
|
+
|
|
56
|
+
const handleDistributionAddressChange = (index: number, value: string) => {
|
|
57
|
+
const updated = [...distributions];
|
|
58
|
+
updated[index] = { ...updated[index], address: value };
|
|
59
|
+
form.setValue("distributions", updated);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const handleDistributionAmountChange = (
|
|
63
|
+
index: number,
|
|
64
|
+
e: React.ChangeEvent<HTMLInputElement>
|
|
65
|
+
) => {
|
|
66
|
+
let rawValue = e.target.value;
|
|
67
|
+
rawValue = rawValue.replace(/[^0-9.]/g, "");
|
|
68
|
+
if (rawValue.split(".").length > 2) {
|
|
69
|
+
rawValue = rawValue.slice(0, -1);
|
|
70
|
+
}
|
|
71
|
+
if (rawValue.includes(".")) {
|
|
72
|
+
const parts = rawValue.split(".");
|
|
73
|
+
if (parts[1] && parts[1].length > 2) {
|
|
74
|
+
rawValue = parts[0] + "." + parts[1].slice(0, 2);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const updated = [...distributions];
|
|
78
|
+
updated[index] = { ...updated[index], amount: rawValue };
|
|
79
|
+
form.setValue("distributions", updated);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const handleAddDistribution = () => {
|
|
83
|
+
const updated = [...distributions, { address: "", amount: "" }];
|
|
84
|
+
form.setValue("distributions", updated);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const handleRemoveDistribution = (index: number) => {
|
|
88
|
+
if (distributions.length <= 2) return;
|
|
89
|
+
const updated = distributions.filter((_, i) => i !== index);
|
|
90
|
+
form.setValue("distributions", updated);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const isAnyDistributionEmpty = React.useMemo(() => {
|
|
94
|
+
if (!distributions.length) return true;
|
|
95
|
+
const last = distributions[distributions.length - 1];
|
|
96
|
+
return (last.address || "").trim() === "" || (last.amount ?? "") === "";
|
|
97
|
+
}, [distributions]);
|
|
98
|
+
|
|
31
99
|
const handleSubmit = form.handleSubmit(async (payload) => {
|
|
32
100
|
try {
|
|
33
101
|
setIsSubmitting(true);
|
|
34
102
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
*/
|
|
103
|
+
if (!isExactMatch) {
|
|
104
|
+
toast.error("The total distributions must equal the escrow amount");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
41
108
|
const finalPayload: SingleReleaseResolveDisputePayload = {
|
|
42
109
|
contractId: selectedEscrow?.contractId || "",
|
|
43
110
|
disputeResolver: walletAddress || "",
|
|
44
|
-
|
|
45
|
-
|
|
111
|
+
distributions: payload.distributions.map((d) => ({
|
|
112
|
+
address: d.address,
|
|
113
|
+
amount: Number(d.amount || 0),
|
|
114
|
+
})) as [{ address: string; amount: number }],
|
|
46
115
|
};
|
|
47
116
|
|
|
48
|
-
/**
|
|
49
|
-
* Call the resolve dispute mutation
|
|
50
|
-
*
|
|
51
|
-
* @param payload - The final payload for the resolve dispute mutation
|
|
52
|
-
* @param type - The type of the escrow
|
|
53
|
-
* @param address - The address of the escrow
|
|
54
|
-
*/
|
|
55
117
|
await resolveDispute.mutateAsync({
|
|
56
118
|
payload: finalPayload,
|
|
57
119
|
type: "single-release",
|
|
@@ -60,6 +122,11 @@ export function useResolveDispute() {
|
|
|
60
122
|
|
|
61
123
|
toast.success("Dispute resolved successfully");
|
|
62
124
|
|
|
125
|
+
const sumDistributed = payload.distributions.reduce((acc, d) => {
|
|
126
|
+
const n = Number(d.amount || 0);
|
|
127
|
+
return acc + (isNaN(n) ? 0 : n);
|
|
128
|
+
}, 0);
|
|
129
|
+
|
|
63
130
|
updateEscrow({
|
|
64
131
|
...selectedEscrow,
|
|
65
132
|
flags: {
|
|
@@ -67,10 +134,7 @@ export function useResolveDispute() {
|
|
|
67
134
|
disputed: false,
|
|
68
135
|
resolved: true,
|
|
69
136
|
},
|
|
70
|
-
balance:
|
|
71
|
-
(selectedEscrow?.balance || 0) -
|
|
72
|
-
(Number(payload.approverFunds) + Number(payload.receiverFunds)) ||
|
|
73
|
-
0,
|
|
137
|
+
balance: (selectedEscrow?.balance || 0) - sumDistributed || 0,
|
|
74
138
|
});
|
|
75
139
|
} catch (error) {
|
|
76
140
|
toast.error(handleError(error as ErrorResponse).message);
|
|
@@ -80,5 +144,19 @@ export function useResolveDispute() {
|
|
|
80
144
|
}
|
|
81
145
|
});
|
|
82
146
|
|
|
83
|
-
return {
|
|
147
|
+
return {
|
|
148
|
+
form,
|
|
149
|
+
handleSubmit,
|
|
150
|
+
isSubmitting,
|
|
151
|
+
distributions,
|
|
152
|
+
handleAddDistribution,
|
|
153
|
+
handleRemoveDistribution,
|
|
154
|
+
handleDistributionAddressChange,
|
|
155
|
+
handleDistributionAmountChange,
|
|
156
|
+
isAnyDistributionEmpty,
|
|
157
|
+
allowedAmount,
|
|
158
|
+
distributedSum,
|
|
159
|
+
isExactMatch,
|
|
160
|
+
difference,
|
|
161
|
+
};
|
|
84
162
|
}
|
|
@@ -44,7 +44,6 @@ export function useUpdateEscrow() {
|
|
|
44
44
|
: "",
|
|
45
45
|
trustline: {
|
|
46
46
|
address: selectedEscrow?.trustline?.address || "",
|
|
47
|
-
decimals: 10000000,
|
|
48
47
|
},
|
|
49
48
|
roles: {
|
|
50
49
|
approver: selectedEscrow?.roles?.approver || "",
|
|
@@ -82,7 +81,6 @@ export function useUpdateEscrow() {
|
|
|
82
81
|
: "",
|
|
83
82
|
trustline: {
|
|
84
83
|
address: selectedEscrow?.trustline?.address || "",
|
|
85
|
-
decimals: 10000000,
|
|
86
84
|
},
|
|
87
85
|
roles: {
|
|
88
86
|
approver: selectedEscrow?.roles?.approver || "",
|
|
@@ -171,7 +169,6 @@ export function useUpdateEscrow() {
|
|
|
171
169
|
: undefined,
|
|
172
170
|
trustline: {
|
|
173
171
|
address: payload.trustline.address,
|
|
174
|
-
decimals: 10000000,
|
|
175
172
|
},
|
|
176
173
|
roles: payload.roles,
|
|
177
174
|
milestones: payload.milestones,
|
|
@@ -202,7 +199,6 @@ export function useUpdateEscrow() {
|
|
|
202
199
|
(selectedEscrow.trustline?.address as string) ||
|
|
203
200
|
"",
|
|
204
201
|
address: finalPayload.escrow.trustline.address,
|
|
205
|
-
decimals: finalPayload.escrow.trustline.decimals,
|
|
206
202
|
},
|
|
207
203
|
};
|
|
208
204
|
|
|
@@ -23,6 +23,8 @@ import {
|
|
|
23
23
|
SingleReleaseReleaseFundsPayload,
|
|
24
24
|
MultiReleaseResolveDisputePayload,
|
|
25
25
|
SingleReleaseResolveDisputePayload,
|
|
26
|
+
WithdrawRemainingFundsPayload,
|
|
27
|
+
useWithdrawRemainingFunds,
|
|
26
28
|
} from "@trustless-work/escrow";
|
|
27
29
|
import { signTransaction } from "../wallet-kit/wallet-kit";
|
|
28
30
|
|
|
@@ -49,6 +51,7 @@ export const useEscrowsMutations = () => {
|
|
|
49
51
|
const { startDispute } = useStartDispute();
|
|
50
52
|
const { releaseFunds } = useReleaseFunds();
|
|
51
53
|
const { resolveDispute } = useResolveDispute();
|
|
54
|
+
const { withdrawRemainingFunds } = useWithdrawRemainingFunds();
|
|
52
55
|
|
|
53
56
|
/**
|
|
54
57
|
* Deploy Escrow
|
|
@@ -434,6 +437,55 @@ export const useEscrowsMutations = () => {
|
|
|
434
437
|
},
|
|
435
438
|
});
|
|
436
439
|
|
|
440
|
+
/**
|
|
441
|
+
* Withdraw Remaining Funds
|
|
442
|
+
*/
|
|
443
|
+
const withdrawRemainingFundsMutation = useMutation({
|
|
444
|
+
mutationFn: async ({
|
|
445
|
+
payload,
|
|
446
|
+
type,
|
|
447
|
+
address,
|
|
448
|
+
}: {
|
|
449
|
+
payload: WithdrawRemainingFundsPayload;
|
|
450
|
+
type: EscrowType;
|
|
451
|
+
address: string;
|
|
452
|
+
}) => {
|
|
453
|
+
const { unsignedTransaction } = await withdrawRemainingFunds(
|
|
454
|
+
payload,
|
|
455
|
+
type
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
if (!unsignedTransaction) {
|
|
459
|
+
throw new Error(
|
|
460
|
+
"Unsigned transaction is missing from withdrawRemainingFunds response."
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const signedTxXdr = await signTransaction({
|
|
465
|
+
unsignedTransaction,
|
|
466
|
+
address,
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
if (!signedTxXdr) {
|
|
470
|
+
throw new Error("Signed transaction is missing.");
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const response = await sendTransaction(signedTxXdr);
|
|
474
|
+
|
|
475
|
+
if (response.status !== "SUCCESS") {
|
|
476
|
+
throw new Error("Transaction failed to send");
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return response;
|
|
480
|
+
},
|
|
481
|
+
onSuccess: () => {
|
|
482
|
+
queryClient.invalidateQueries({ queryKey: ["escrows"] });
|
|
483
|
+
},
|
|
484
|
+
onError: (error) => {
|
|
485
|
+
console.error(error);
|
|
486
|
+
},
|
|
487
|
+
});
|
|
488
|
+
|
|
437
489
|
return {
|
|
438
490
|
deployEscrow: deployEscrowMutation,
|
|
439
491
|
updateEscrow: updateEscrowMutation,
|
|
@@ -443,5 +495,6 @@ export const useEscrowsMutations = () => {
|
|
|
443
495
|
startDispute: startDisputeMutation,
|
|
444
496
|
releaseFunds: releaseFundsMutation,
|
|
445
497
|
resolveDispute: resolveDisputeMutation,
|
|
498
|
+
withdrawRemainingFunds: withdrawRemainingFundsMutation,
|
|
446
499
|
};
|
|
447
500
|
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useQuery } from "@tanstack/react-query";
|
|
2
|
+
import {
|
|
3
|
+
GetEscrowBalancesResponse,
|
|
4
|
+
GetBalanceParams,
|
|
5
|
+
} from "@trustless-work/escrow/types";
|
|
6
|
+
import { useGetMultipleEscrowBalances } from "@trustless-work/escrow/hooks";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Use the query to get the escrows balances
|
|
10
|
+
*
|
|
11
|
+
* @param params - The parameters for the query
|
|
12
|
+
* @returns The query result
|
|
13
|
+
*/
|
|
14
|
+
export const useGetMultipleEscrowBalancesQuery = ({
|
|
15
|
+
addresses,
|
|
16
|
+
enabled = true,
|
|
17
|
+
}: GetBalanceParams & { enabled?: boolean }) => {
|
|
18
|
+
const { getMultipleBalances } = useGetMultipleEscrowBalances();
|
|
19
|
+
|
|
20
|
+
// Get the escrows by signer
|
|
21
|
+
return useQuery({
|
|
22
|
+
queryKey: ["escrows", addresses],
|
|
23
|
+
queryFn: async (): Promise<GetEscrowBalancesResponse[]> => {
|
|
24
|
+
/**
|
|
25
|
+
* Call the query to get the escrows from the Trustless Work Indexer
|
|
26
|
+
*
|
|
27
|
+
* @param params - The parameters for the query
|
|
28
|
+
* @returns The query result
|
|
29
|
+
*/
|
|
30
|
+
const balances = await getMultipleBalances({ addresses });
|
|
31
|
+
|
|
32
|
+
if (!balances) {
|
|
33
|
+
throw new Error("Escrows not found");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return balances;
|
|
37
|
+
},
|
|
38
|
+
enabled: enabled,
|
|
39
|
+
staleTime: 1000 * 60 * 5, // 5 min
|
|
40
|
+
});
|
|
41
|
+
};
|
|
@@ -10,26 +10,22 @@ export const trustlines = [
|
|
|
10
10
|
{
|
|
11
11
|
name: "USDC",
|
|
12
12
|
address: "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA",
|
|
13
|
-
decimals: 10000000,
|
|
14
13
|
network: "testnet",
|
|
15
14
|
},
|
|
16
15
|
{
|
|
17
16
|
name: "EURC",
|
|
18
17
|
address: "GB3Q6QDZYTHWT7E5PVS3W7FUT5GVAFC5KSZFFLPU25GO7VTC3NM2ZTVO",
|
|
19
|
-
decimals: 10000000,
|
|
20
18
|
network: "testnet",
|
|
21
19
|
},
|
|
22
20
|
// MAINNET
|
|
23
21
|
{
|
|
24
22
|
name: "USDC",
|
|
25
23
|
address: "CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75",
|
|
26
|
-
decimals: 10000000,
|
|
27
24
|
network: "mainnet",
|
|
28
25
|
},
|
|
29
26
|
{
|
|
30
27
|
name: "EURC",
|
|
31
28
|
address: "GB3Q6QDZYTHWT7E5PVS3W7FUT5GVAFC5KSZFFLPU25GO7VTC3NM2ZTVO",
|
|
32
|
-
decimals: 10000000,
|
|
33
29
|
network: "mainnet",
|
|
34
30
|
},
|
|
35
31
|
];
|