Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,26 @@ const ReviewRequiredSignersCard: React.FC<ReviewRequiredSignersCardProps> = ({
})()}
</span>
</div>

{/* Info message for "all" and "any" script types */}
{(nativeScriptType === 'all' || nativeScriptType === 'any') && (
<div className="flex items-start gap-2 p-3 bg-blue-50 dark:bg-blue-950/20 rounded-lg">
<svg className="w-4 h-4 mt-0.5 flex-shrink-0 text-blue-600 dark:text-blue-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<div className="text-xs text-blue-800 dark:text-blue-200">
<p className="font-medium mb-1">
{nativeScriptType === 'all' ? 'All Signers Required' : 'Any Signer Can Approve'}
</p>
<p>
With "{nativeScriptType === 'all' ? 'All' : 'Any'}" script type, no specific number is stored.
{nativeScriptType === 'all'
? ' Every signer must approve each transaction.'
: ' Any single signer can approve each transaction.'}
</p>
</div>
</div>
)}
{/* Edit button - only show for atLeast type */}
{nativeScriptType === "atLeast" && (
<div className="pt-2">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ export function useWalletFlowState(): WalletFlowState {
numRequiredSigners,
network,
stakeKey || undefined,
nativeScriptType,
);
}, [
name,
Expand All @@ -168,6 +169,7 @@ export function useWalletFlowState(): WalletFlowState {
numRequiredSigners,
network,
stakeKey,
nativeScriptType,
]);

// API Mutations
Expand Down
27 changes: 24 additions & 3 deletions src/components/pages/wallet/info/signers/card-signers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,30 @@ export default function CardSigners({ appWallet }: { appWallet: Wallet }) {
title="Signers"
description={
<>
This wallet requires{" "}
<b className="text-foreground">{appWallet.numRequiredSigners}</b> signers{" "}
to sign a transaction.
{(() => {
const signersCount = appWallet.signersAddresses.length;
if (appWallet.type === 'all') {
return (
<>
<b className="text-foreground">All signers</b> (of {signersCount}) must approve each transaction.
</>
);
} else if (appWallet.type === 'any') {
return (
<>
<b className="text-foreground">Any signer</b> (of {signersCount}) can approve each transaction.
</>
);
} else {
return (
<>
This wallet requires{" "}
<b className="text-foreground">{appWallet.numRequiredSigners}</b> of {signersCount} signers{" "}
to sign a transaction.
</>
);
}
})()}
</>
}
headerDom={
Expand Down
1 change: 1 addition & 0 deletions src/hooks/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export function buildMultisigWallet(
wallet.numRequiredSigners ?? 1,
network,
wallet.stakeCredentialHash ?? undefined,
(wallet.type as "all" | "any" | "atLeast") ?? "atLeast",
);
return multisigWallet;
}
Expand Down
9 changes: 6 additions & 3 deletions src/server/api/routers/wallets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,15 @@ export const walletRouter = createTRPCRouter({
}),
)
.mutation(async ({ ctx, input }) => {
const numRequired = (input.type === "all" || input.type === "any") ? null : input.numRequiredSigners;
return ctx.db.wallet.create({
data: {
name: input.name,
description: input.description,
signersAddresses: input.signersAddresses,
signersDescriptions: input.signersDescriptions,
signersStakeKeys: input.signersStakeKeys,
numRequiredSigners: input.numRequiredSigners,
numRequiredSigners: numRequired as any,
scriptCbor: input.scriptCbor,
stakeCredentialHash: input.stakeCredentialHash,
type: input.type,
Expand Down Expand Up @@ -168,14 +169,15 @@ export const walletRouter = createTRPCRouter({
}),
)
.mutation(async ({ ctx, input }) => {
const numRequired = (input.scriptType === "all" || input.scriptType === "any") ? null : input.numRequiredSigners;
return ctx.db.newWallet.create({
data: {
name: input.name,
description: input.description,
signersAddresses: input.signersAddresses,
signersDescriptions: input.signersDescriptions,
signersStakeKeys: input.signersStakeKeys,
numRequiredSigners: input.numRequiredSigners,
numRequiredSigners: numRequired as any,
ownerAddress: input.ownerAddress,
stakeCredentialHash: input.stakeCredentialHash,
scriptType: input.scriptType,
Expand All @@ -198,6 +200,7 @@ export const walletRouter = createTRPCRouter({
}),
)
.mutation(async ({ ctx, input }) => {
const numRequired = (input.scriptType === "all" || input.scriptType === "any") ? null : input.numRequiredSigners;
return ctx.db.newWallet.update({
where: {
id: input.walletId,
Expand All @@ -208,7 +211,7 @@ export const walletRouter = createTRPCRouter({
signersAddresses: input.signersAddresses,
signersDescriptions: input.signersDescriptions,
signersStakeKeys: input.signersStakeKeys,
numRequiredSigners: input.numRequiredSigners,
numRequiredSigners: numRequired as any,
stakeCredentialHash: input.stakeCredentialHash,
scriptType: input.scriptType,
} as any,
Expand Down
10 changes: 8 additions & 2 deletions src/utils/multisigSDK.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ export class MultisigWallet {
network: number;
/** Optional external stake credential hash (28-byte hex string) */
stakeCredentialHash: string | undefined;
/** Script type: "all", "any", or "atLeast" */
type: "all" | "any" | "atLeast";

/**
* Creates a new MultisigWallet instance.
Expand All @@ -154,6 +156,7 @@ export class MultisigWallet {
* @param required - Number of signatures required (default: 1)
* @param network - Network identifier: 0=testnet, 1=mainnet (default: 1)
* @param stakeCredentialHash - Optional external stake credential hash (28-byte hex string)
* @param type - Script type: "all", "any", or "atLeast" (default: "atLeast")
*
* @throws {Error} If no valid payment keys are provided
*
Expand All @@ -175,7 +178,8 @@ export class MultisigWallet {
* "Wallet with staking capabilities",
* 2, // require 2 signatures
* 0, // testnet
* "external_stake_credential_hash"
* "external_stake_credential_hash",
* "all" // all signers must approve
* );
* ```
*/
Expand All @@ -186,6 +190,7 @@ export class MultisigWallet {
required?: number,
network?: number,
stakeCredentialHash?: string,
type: "all" | "any" | "atLeast" = "atLeast",
) {
this.name = name;
// Filter out any keys that are not valid
Expand All @@ -200,6 +205,7 @@ export class MultisigWallet {
this.required = required ? required : 1;
this.network = network !== undefined ? network : 1;
this.stakeCredentialHash = stakeCredentialHash;
this.type = type;
}

/**
Expand Down Expand Up @@ -339,7 +345,7 @@ export class MultisigWallet {
const filteredKeys = this.getKeysByRole(role);
if (!filteredKeys) return undefined;
// Build the script using only the keys of the specified role
return buildNativeScript(filteredKeys, this.required);
return buildNativeScript(filteredKeys, this.required, this.type);
}

/**
Expand Down