import { StargateClient } from "@cosmjs/stargate"; import { NextRouter, withRouter } from "next/router"; import { useState } from "react"; import { useChains } from "../../context/ChainsContext"; import { exampleAddress, examplePubkey } from "../../lib/displayHelpers"; import { createMultisigFromCompressedSecp256k1Pubkeys } from "../../lib/multisigHelpers"; import Button from "../inputs/Button"; import Input from "../inputs/Input"; import ThresholdInput from "../inputs/ThresholdInput"; import StackableContainer from "../layout/StackableContainer"; const emptyPubKeyGroup = () => { return { address: "", compressedPubkey: "", keyError: "", isPubkey: false }; }; interface Props { router: NextRouter; } const MultiSigForm = (props: Props) => { const { chain } = useChains(); const [pubkeys, setPubkeys] = useState([emptyPubKeyGroup(), emptyPubKeyGroup()]); const [threshold, setThreshold] = useState(2); const [processing, setProcessing] = useState(false); const handleChangeThreshold = (e: React.ChangeEvent) => { let newThreshold = parseInt(e.target.value, 10); if (newThreshold > pubkeys.length || newThreshold <= 0) { newThreshold = threshold; } setThreshold(newThreshold); }; const handleKeyGroupChange = (index: number, e: React.ChangeEvent) => { const tempPubkeys = [...pubkeys]; if (e.target.name === "compressedPubkey") { tempPubkeys[index].compressedPubkey = e.target.value; } else if (e.target.name === "address") { tempPubkeys[index].address = e.target.value; } setPubkeys(tempPubkeys); }; const handleAddKey = () => { const tempPubkeys = [...pubkeys]; setPubkeys(tempPubkeys.concat(emptyPubKeyGroup())); }; const handleRemove = (index: number) => { const tempPubkeys = [...pubkeys]; const oldLength = tempPubkeys.length; tempPubkeys.splice(index, 1); const newThreshold = threshold > tempPubkeys.length ? tempPubkeys.length : oldLength; setPubkeys(tempPubkeys); setThreshold(newThreshold); }; const getPubkeyFromNode = async (address: string) => { const client = await StargateClient.connect(chain.nodeAddress); const accountOnChain = await client.getAccount(address); console.log(accountOnChain); if (!accountOnChain || !accountOnChain.pubkey) { throw new Error( "Account has no pubkey on chain, this address will need to send a transaction to appear on chain.", ); } return accountOnChain.pubkey.value; }; const handleKeyBlur = async (index: number, e: React.ChangeEvent) => { try { const tempPubkeys = [...pubkeys]; let pubkey; // use pubkey console.log(tempPubkeys[index]); if (tempPubkeys[index].isPubkey) { pubkey = e.target.value; if (pubkey.length !== 44) { throw new Error("Invalid Secp256k1 pubkey"); } } else { // use address to fetch pubkey const address = e.target.value; if (address.length > 0) { pubkey = await getPubkeyFromNode(address); } } tempPubkeys[index].compressedPubkey = pubkey; tempPubkeys[index].keyError = ""; setPubkeys(tempPubkeys); // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { console.log(error); const tempPubkeys = [...pubkeys]; tempPubkeys[index].keyError = error.message; setPubkeys(tempPubkeys); } }; const handleCreate = async () => { setProcessing(true); const compressedPubkeys = pubkeys.map((item) => item.compressedPubkey); let multisigAddress; try { multisigAddress = await createMultisigFromCompressedSecp256k1Pubkeys( compressedPubkeys, threshold, chain.addressPrefix, chain.chainId, ); props.router.push(`/${chain.registryName}/${multisigAddress}`); } catch (error) { console.log("Failed to creat multisig: ", error); } }; const togglePubkey = (index: number) => { const tempPubkeys = [...pubkeys]; tempPubkeys[index].isPubkey = !tempPubkeys[index].isPubkey; setPubkeys(tempPubkeys); }; return ( <>

Add the addresses that will make up this multisig.

{pubkeys.map((pubkeyGroup, index) => { return (
{pubkeys.length > 2 && ( )}
) => { handleKeyGroupChange(index, e); }} value={ pubkeyGroup.isPubkey ? pubkeyGroup.compressedPubkey : pubkeyGroup.address } label={pubkeyGroup.isPubkey ? "Public Key (Secp256k1)" : "Address"} name={pubkeyGroup.isPubkey ? "compressedPubkey" : "address"} width="100%" placeholder={`E.g. ${ pubkeyGroup.isPubkey ? examplePubkey(index) : exampleAddress(index, chain.addressPrefix) }`} error={pubkeyGroup.keyError} onBlur={(e: React.ChangeEvent) => { handleKeyBlur(index, e); }} />
); })}