Skip to content

Commit a41b8ed

Browse files
authored
Merge pull request #167 from MeshJS/feature/summon-tx
Enhance wallet functionality with rawImportBodies support
2 parents 538a4ec + 73bc9a0 commit a41b8ed

File tree

22 files changed

+515
-294
lines changed

22 files changed

+515
-294
lines changed

src/components/pages/homepage/wallets/new-wallet-flow/create/index.tsx

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import CollapsibleAdvancedSection from "@/components/pages/homepage/wallets/new-
55
import { Button } from "@/components/ui/button";
66
import WalletFlowPageLayout from "@/components/pages/homepage/wallets/new-wallet-flow/shared/WalletFlowPageLayout";
77
import { useWalletFlowState } from "@/components/pages/homepage/wallets/new-wallet-flow/shared/useWalletFlowState";
8+
import { Check } from "lucide-react";
89

910
export default function PageReviewWallet() {
1011
const walletFlow = useWalletFlowState();
@@ -70,35 +71,60 @@ export default function PageReviewWallet() {
7071
/>
7172

7273
{/* Action Section - Warning and Create Button */}
73-
<div className="mt-6 sm:mt-8 flex flex-col sm:flex-row items-start sm:items-center gap-4 sm:justify-between">
74-
{/* Warning Message */}
75-
<div className="flex items-start gap-2 p-4 bg-muted/50 rounded-lg w-fit">
76-
<svg className="w-5 h-5 mt-0.5 flex-shrink-0 text-foreground" fill="none" viewBox="0 0 24 24" stroke="currentColor">
77-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
78-
</svg>
79-
{walletFlow.usesStored ? (
80-
<p className="text-sm text-foreground">
81-
<strong>Not yet compatible:</strong> This wallet was created with a stored script format that is not supported for creation here yet. Please check back soon.
82-
</p>
83-
) : walletFlow.hasSignerHashInAddresses ? (
84-
<p className="text-sm text-foreground">
85-
<strong>Invite not completed:</strong> Not all signers visited the invite link.
86-
</p>
87-
) : (
88-
<p className="text-sm text-foreground">
89-
<strong>Important:</strong> Creation is final - signers and rules can not be changed afterwards.
90-
</p>
91-
)}
74+
<div className="mt-6 sm:mt-8 flex flex-col gap-4">
75+
<div className="flex flex-col sm:flex-row items-start sm:items-center gap-4 sm:justify-between">
76+
{/* Warning Message */}
77+
<div className="flex items-start gap-2 p-4 bg-muted/50 rounded-lg w-fit">
78+
<svg className="w-5 h-5 mt-0.5 flex-shrink-0 text-foreground" fill="none" viewBox="0 0 24 24" stroke="currentColor">
79+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
80+
</svg>
81+
{walletFlow.usesStored && !walletFlow.hasValidRawImportBodies ? (
82+
<p className="text-sm text-foreground">
83+
<strong>Not yet compatible:</strong> This wallet was created with a stored script format that is not supported for creation here yet. Please check back soon.
84+
</p>
85+
) : walletFlow.hasSignerHashInAddresses ? (
86+
<p className="text-sm text-foreground">
87+
<strong>Invite not completed:</strong> Not all signers visited the invite link.
88+
</p>
89+
) : (
90+
<p className="text-sm text-foreground">
91+
<strong>Important:</strong> Creation is final - signers and rules can not be changed afterwards.
92+
</p>
93+
)}
94+
</div>
95+
{/* Create Button */}
96+
<Button
97+
onClick={walletFlow.createNativeScript}
98+
disabled={!walletFlow.isValidForCreate || (walletFlow.usesStored && !walletFlow.hasValidRawImportBodies)}
99+
className="w-full sm:w-auto"
100+
size="lg"
101+
>
102+
{walletFlow.loading ? "Creating..." : "Create"}
103+
</Button>
92104
</div>
93-
{/* Create Button */}
94-
<Button
95-
onClick={walletFlow.createNativeScript}
96-
disabled={!walletFlow.isValidForCreate || walletFlow.usesStored}
97-
className="w-full sm:w-auto"
98-
size="lg"
99-
>
100-
{walletFlow.loading ? "Creating..." : "Create"}
101-
</Button>
105+
{/* Bypass option for hash signers */}
106+
{walletFlow.hasSignerHashInAddresses && (
107+
<button
108+
type="button"
109+
onClick={() => {
110+
walletFlow.setAllowCreateWithHashSigners(!walletFlow.allowCreateWithHashSigners);
111+
}}
112+
className="flex items-center space-x-2 p-4 bg-muted/30 rounded-lg hover:bg-muted/50 transition-colors w-full text-left"
113+
>
114+
<div className={`flex items-center justify-center h-5 w-5 rounded border-2 transition-colors ${
115+
walletFlow.allowCreateWithHashSigners
116+
? "bg-primary border-primary"
117+
: "border-muted-foreground/50"
118+
}`}>
119+
{walletFlow.allowCreateWithHashSigners && (
120+
<Check className="h-4 w-4 text-primary-foreground" />
121+
)}
122+
</div>
123+
<span className="text-sm font-normal select-none flex-1">
124+
I understand that not all signers have visited the invite link. Create wallet anyway.
125+
</span>
126+
</button>
127+
)}
102128
</div>
103129
</>
104130
)}

src/components/pages/homepage/wallets/new-wallet-flow/ready/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { Button } from "@/components/ui/button";
2020
import ProgressIndicator from "@/components/pages/homepage/wallets/new-wallet-flow/shared/ProgressIndicator";
2121
import WalletFlowPageLayout from "@/components/pages/homepage/wallets/new-wallet-flow/shared/WalletFlowPageLayout";
2222
import { buildMultisigWallet } from "@/utils/common";
23+
import { DbWalletWithLegacy } from "@/types/wallet";
2324

2425
export default function PageSuccessWallet() {
2526
const router = useRouter();
@@ -36,7 +37,7 @@ export default function PageSuccessWallet() {
3637
);
3738

3839
// Build wallet with address and other computed fields
39-
const wallet = walletData ? buildMultisigWallet(walletData, network) : null;
40+
const wallet = walletData ? buildMultisigWallet(walletData as DbWalletWithLegacy, network) : null;
4041

4142
const handleViewWallets = () => {
4243
setLoading(true);

src/components/pages/homepage/wallets/new-wallet-flow/shared/useWalletFlowState.tsx

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ export interface WalletFlowState {
5858
isValidForSave: boolean;
5959
isValidForCreate: boolean;
6060
hasSignerHashInAddresses: boolean;
61+
hasValidRawImportBodies: boolean;
62+
63+
// Bypass options
64+
allowCreateWithHashSigners: boolean;
65+
setAllowCreateWithHashSigners: React.Dispatch<React.SetStateAction<boolean>>;
6166

6267
// Router info
6368
router: ReturnType<typeof useRouter>;
@@ -107,6 +112,7 @@ export function useWalletFlowState(): WalletFlowState {
107112
const [loading, setLoading] = useState<boolean>(false);
108113
const [nativeScriptType, setNativeScriptType] = useState<"all" | "any" | "atLeast">("atLeast");
109114
const [stakeKey, setStakeKey] = useState<string>("");
115+
const [allowCreateWithHashSigners, setAllowCreateWithHashSigners] = useState<boolean>(false);
110116

111117
// Dependencies
112118
const userAddress = useUserStore((state) => state.userAddress);
@@ -394,11 +400,6 @@ export function useWalletFlowState(): WalletFlowState {
394400
function createNativeScript() {
395401
setLoading(true);
396402

397-
if (!multisigWallet) {
398-
setLoading(false);
399-
throw new Error("Multisig wallet could not be built.");
400-
}
401-
402403
// Prefer imported payment CBOR from walletInvite when available
403404
type WalletInviteExtras = {
404405
paymentCbor?: string;
@@ -407,11 +408,21 @@ export function useWalletFlowState(): WalletFlowState {
407408
};
408409
const inviteExtras = (walletInvite as unknown as WalletInviteExtras) || {};
409410
const importedPaymentCbor = inviteExtras.paymentCbor;
411+
const hasRawImportBodies = !!inviteExtras.rawImportBodies?.multisig;
412+
410413
let scriptCborToUse: string | undefined;
411414

412-
if (importedPaymentCbor && importedPaymentCbor.length > 0) {
415+
// For wallets with rawImportBodies, use stored payment script
416+
if (hasRawImportBodies && inviteExtras.rawImportBodies?.multisig?.payment_script) {
417+
scriptCborToUse = inviteExtras.rawImportBodies.multisig.payment_script;
418+
} else if (importedPaymentCbor && importedPaymentCbor.length > 0) {
413419
scriptCborToUse = importedPaymentCbor;
414420
} else {
421+
// For regular wallets, require multisigWallet to derive script
422+
if (!multisigWallet) {
423+
setLoading(false);
424+
throw new Error("Multisig wallet could not be built.");
425+
}
415426
const { scriptCbor } = multisigWallet.getScript();
416427
scriptCborToUse = scriptCbor;
417428
}
@@ -612,12 +623,21 @@ export function useWalletFlowState(): WalletFlowState {
612623
});
613624
}, [signersAddresses]);
614625

626+
// Check if we have rawImportBodies with required data
627+
const hasValidRawImportBodies = useMemo(() => {
628+
if (!walletInvite) return false;
629+
const inviteExtras = (walletInvite as any) || {};
630+
return !!inviteExtras.rawImportBodies?.multisig?.payment_script;
631+
}, [walletInvite]);
632+
615633
const isValidForCreate = signersAddresses.length > 0 &&
616634
!signersAddresses.some((signer) => !signer || signer.length === 0) &&
617635
(nativeScriptType !== "atLeast" || numRequiredSigners > 0) &&
618636
name.length > 0 &&
619637
!loading &&
620-
!hasSignerHashInAddresses;
638+
(!hasSignerHashInAddresses || allowCreateWithHashSigners) &&
639+
// Allow creation if we have valid rawImportBodies, otherwise require multisigWallet
640+
(hasValidRawImportBodies || !!multisigWallet);
621641

622642
return {
623643
// Core wallet data
@@ -660,6 +680,11 @@ export function useWalletFlowState(): WalletFlowState {
660680
isValidForSave,
661681
isValidForCreate,
662682
hasSignerHashInAddresses,
683+
hasValidRawImportBodies,
684+
685+
// Bypass options
686+
allowCreateWithHashSigners,
687+
setAllowCreateWithHashSigners,
663688

664689
// Router info
665690
router,

src/components/pages/homepage/wallets/new-wallet/index.tsx

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { useUserStore } from "@/lib/zustand/user";
99
import { useSiteStore } from "@/lib/zustand/site";
1010
import { useToast } from "@/hooks/use-toast";
1111
import useUser from "@/hooks/useUser";
12+
import type { RawImportBodies } from "@/types/wallet";
1213

1314
import PageHeader from "@/components/common/page-header";
1415
import WalletInfoCard from "@/components/pages/homepage/wallets/new-wallet/nWInfoCard";
@@ -189,13 +190,34 @@ export default function PageNewWallet() {
189190
async function createNativeScript() {
190191
setLoading(true);
191192

192-
if (!multisigWallet) {
193-
setLoading(false);
194-
throw new Error("Multisig wallet could not be built.");
193+
// Check for rawImportBodies from wallet invite
194+
type WalletInviteExtras = {
195+
paymentCbor?: string;
196+
stakeCbor?: string | null;
197+
rawImportBodies?: RawImportBodies | null;
198+
};
199+
const inviteExtras = (walletInvite as unknown as WalletInviteExtras) || {};
200+
const importedPaymentCbor = inviteExtras.paymentCbor;
201+
const hasRawImportBodies = !!inviteExtras.rawImportBodies?.multisig;
202+
203+
let scriptCborToUse: string | undefined;
204+
205+
// For wallets with rawImportBodies, use stored payment script
206+
if (hasRawImportBodies && inviteExtras.rawImportBodies?.multisig?.payment_script) {
207+
scriptCborToUse = inviteExtras.rawImportBodies.multisig.payment_script;
208+
} else if (importedPaymentCbor && importedPaymentCbor.length > 0) {
209+
scriptCborToUse = importedPaymentCbor;
210+
} else {
211+
// For regular wallets, require multisigWallet to derive script
212+
if (!multisigWallet) {
213+
setLoading(false);
214+
throw new Error("Multisig wallet could not be built.");
215+
}
216+
const { scriptCbor } = multisigWallet.getScript();
217+
scriptCborToUse = scriptCbor;
195218
}
196219

197-
const { scriptCbor } = multisigWallet.getScript();
198-
if (!scriptCbor) {
220+
if (!scriptCborToUse) {
199221
setLoading(false);
200222
throw new Error("scriptCbor is undefined");
201223
}
@@ -208,7 +230,8 @@ export default function PageNewWallet() {
208230
signersStakeKeys: signersStakeKeys,
209231
signersDRepKeys: signersDRepKeys,
210232
numRequiredSigners: numRequiredSigners,
211-
scriptCbor: scriptCbor,
233+
scriptCbor: scriptCborToUse,
234+
rawImportBodies: inviteExtras.rawImportBodies ?? null,
212235
stakeCredentialHash: stakeKey.length > 0 ? stakeKey : undefined,
213236
type: nativeScriptType,
214237
});

src/components/pages/wallet/governance/ballot/ballot.tsx

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ export default function BallotCard({
8080
{ enabled: !!(appWallet?.id || userAddress) }
8181
);
8282

83+
// Check if we have valid proxy data (proxy enabled, selected, proxies exist, and selected proxy is found)
84+
const hasValidProxy = !!(isProxyEnabled && selectedProxyId && proxies && proxies.length > 0 && proxies.find((p: any) => p.id === selectedProxyId));
85+
8386
// CreateBallot mutation
8487
const createBallot = api.ballot.create.useMutation();
8588
// Get ballots for wallet
@@ -122,26 +125,18 @@ export default function BallotCard({
122125
});
123126
return;
124127
}
125-
if (!isProxyEnabled || !selectedProxyId) {
126-
toast({
127-
title: "Proxy Error",
128-
description: "Proxy mode not enabled or no proxy selected",
129-
variant: "destructive",
130-
});
131-
return;
128+
if (!hasValidProxy) {
129+
// Fall back to standard vote if no valid proxy
130+
return handleSubmitVote();
132131
}
133132

134133
setLoading(true);
135134
try {
136135
// Get the selected proxy
137136
const proxy = proxies?.find((p: any) => p.id === selectedProxyId);
138137
if (!proxy) {
139-
toast({
140-
title: "Proxy Error",
141-
description: "Selected proxy not found",
142-
variant: "destructive",
143-
});
144-
return;
138+
// Fall back to standard vote if proxy not found
139+
return handleSubmitVote();
145140
}
146141

147142
// Create proxy contract instance
@@ -486,7 +481,7 @@ export default function BallotCard({
486481
)}
487482
{selectedBallot && (
488483
<>
489-
{isProxyEnabled && !selectedProxyId && (
484+
{isProxyEnabled && proxies && proxies.length > 0 && !selectedProxyId && (
490485
<div className="mt-2 p-2 bg-yellow-50 dark:bg-yellow-950/20 border border-yellow-200 dark:border-yellow-800 rounded-md">
491486
<p className="text-xs text-yellow-800 dark:text-yellow-200 font-medium">
492487
Proxy Mode Active - Select a proxy to continue
@@ -499,10 +494,10 @@ export default function BallotCard({
499494
<Button
500495
variant="default"
501496
className="mt-4"
502-
onClick={isProxyEnabled ? handleSubmitProxyVote : handleSubmitVote}
503-
disabled={loading || (isProxyEnabled && !selectedProxyId)}
497+
onClick={hasValidProxy ? handleSubmitProxyVote : handleSubmitVote}
498+
disabled={loading}
504499
>
505-
{loading ? "Loading..." : `Submit Ballot Vote${isProxyEnabled ? " (Proxy Mode)" : ""}`}
500+
{loading ? "Loading..." : `Submit Ballot Vote${hasValidProxy ? " (Proxy Mode)" : ""}`}
506501
</Button>
507502
<Button
508503
variant="destructive"

0 commit comments

Comments
 (0)