131 lines
4.1 KiB
TypeScript
131 lines
4.1 KiB
TypeScript
import { ChainInfo } from "@/context/ChainsContext/types";
|
|
import { pubkeyToAddress } from "@cosmjs/amino";
|
|
import { StargateClient } from "@cosmjs/stargate";
|
|
import { z } from "zod";
|
|
import { checkAddressOrPubkey } from "../../../lib/displayHelpers";
|
|
|
|
export const getCreateMultisigSchema = (chain: ChainInfo) =>
|
|
z
|
|
.object({
|
|
members: z.array(
|
|
z.object({
|
|
member: z
|
|
.string()
|
|
.trim()
|
|
.superRefine(async (member, ctx) => {
|
|
if (!member) {
|
|
return z.NEVER;
|
|
}
|
|
|
|
const addressOrPubkeyError = checkAddressOrPubkey(member, chain.addressPrefix);
|
|
|
|
if (addressOrPubkeyError) {
|
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: addressOrPubkeyError });
|
|
} else {
|
|
try {
|
|
const address = member.startsWith(chain.addressPrefix)
|
|
? member
|
|
: pubkeyToAddress(
|
|
{ type: "tendermint/PubKeySecp256k1", value: member },
|
|
chain.addressPrefix,
|
|
);
|
|
|
|
const client = await StargateClient.connect(chain.nodeAddress);
|
|
const accountOnChain = await client.getAccount(address);
|
|
|
|
if (!accountOnChain || !accountOnChain.pubkey) {
|
|
ctx.addIssue({
|
|
code: z.ZodIssueCode.custom,
|
|
message: "This account needs to send a transaction to appear on chain",
|
|
});
|
|
}
|
|
} catch {
|
|
return z.NEVER;
|
|
}
|
|
}
|
|
}),
|
|
}),
|
|
),
|
|
threshold: z.coerce
|
|
.number({ invalid_type_error: "Threshold must be a number" })
|
|
.int("Threshold can't have decimals")
|
|
.min(1, "Threshold must be at least 1"),
|
|
})
|
|
.superRefine(({ members }, ctx) => {
|
|
if (members.length !== 2) {
|
|
return;
|
|
}
|
|
|
|
const firstEmptyMemberIndex = members.findIndex(({ member }) => member.trim() === "");
|
|
|
|
if (firstEmptyMemberIndex !== -1) {
|
|
const issue = {
|
|
code: z.ZodIssueCode.custom,
|
|
message: "At least 2 members needed",
|
|
path: [`members.${firstEmptyMemberIndex}.member`],
|
|
};
|
|
|
|
ctx.addIssue(issue);
|
|
}
|
|
})
|
|
.superRefine(({ members }, ctx) => {
|
|
const addresses = members.map(({ member }) => {
|
|
if (!member.startsWith(chain.addressPrefix)) {
|
|
try {
|
|
const address = pubkeyToAddress(
|
|
{ type: "tendermint/PubKeySecp256k1", value: member },
|
|
chain.addressPrefix,
|
|
);
|
|
|
|
return address;
|
|
} catch {}
|
|
}
|
|
|
|
return member;
|
|
});
|
|
|
|
const dupedAddresses = addresses.filter((member, i) => addresses.indexOf(member) !== i);
|
|
|
|
const dupedAddressesIndexes: number[][] = [];
|
|
|
|
for (const dupedAddress of dupedAddresses) {
|
|
const dupedIndexes = [];
|
|
|
|
for (let i = 0; i < addresses.length; ++i) {
|
|
const index = addresses.indexOf(dupedAddress, i);
|
|
if (index !== -1) {
|
|
dupedIndexes.push(index);
|
|
}
|
|
}
|
|
|
|
dupedAddressesIndexes.push(dupedIndexes.sort());
|
|
}
|
|
|
|
if (dupedAddressesIndexes.length) {
|
|
for (const dupedIndexes of dupedAddressesIndexes) {
|
|
for (const duplicateIndex of dupedIndexes) {
|
|
const issue = {
|
|
code: z.ZodIssueCode.custom,
|
|
message: `Members cannot be duplicate (${dupedIndexes
|
|
.map((index) => `#${index + 1}`)
|
|
.join(", ")})`,
|
|
path: [`members.${duplicateIndex}.member`],
|
|
};
|
|
|
|
ctx.addIssue(issue);
|
|
}
|
|
}
|
|
} else {
|
|
return z.NEVER;
|
|
}
|
|
})
|
|
.refine(
|
|
({ members, threshold }) => threshold <= members.filter(({ member }) => member !== "").length,
|
|
({ members }) => ({
|
|
message: `Threshold can't be higher than the number of members (${
|
|
members.filter(({ member }) => member !== "").length
|
|
})`,
|
|
path: ["threshold"],
|
|
}),
|
|
);
|