Configure eslint for typescript and adapt code

This commit is contained in:
Simon Warta
2022-06-10 16:10:39 +02:00
parent d92e9a3acf
commit e5857afb4c
23 changed files with 1113 additions and 247 deletions
+10 -3
View File
@@ -7,6 +7,7 @@ module.exports = {
globals: {
process: "readonly",
},
parser: "@typescript-eslint/parser",
parserOptions: {
sourceType: "module",
ecmaVersion: 2020,
@@ -14,9 +15,10 @@ module.exports = {
jsx: true,
},
},
plugins: ["prettier"],
plugins: ["prettier", "@typescript-eslint"],
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"prettier",
"plugin:prettier/recommended",
@@ -27,12 +29,17 @@ module.exports = {
"no-console": "off",
"no-param-reassign": "warn",
"no-shadow": "warn",
"no-unused-vars": ["warn", { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }],
"no-unused-vars": "off", // Use @typescript-eslint/no-unused-vars instead
"prefer-const": "warn",
radix: ["warn", "always"],
"spaced-comment": ["warn", "always"],
"spaced-comment": ["warn", "always", { line: { markers: ["/ <reference"] } }],
"react/no-unescaped-entities": ["warn", { forbid: [">", "}"] }], // by default we can't use ' which is annoying
"react/prop-types": "off", // we take care of this with TypeScript
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-unused-vars": [
"warn",
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
],
},
overrides: [],
};
+1 -3
View File
@@ -1,8 +1,6 @@
import React from "react";
interface Props {}
const DevHelper = (_props: Props) => (
const DevHelper = () => (
<div className="dev-helper">
<h3>Dev Helper</h3>
<h4>Pages</h4>
+12 -5
View File
@@ -8,6 +8,7 @@ import Input from "../inputs/Input";
import { useAppContext } from "../../context/AppContext";
import Select from "../inputs/Select";
import StackableContainer from "../layout/StackableContainer";
import { assert } from "@cosmjs/utils";
interface ChainOption {
label: string;
@@ -87,7 +88,9 @@ const ChainSelect = () => {
return { label: name, value: index };
});
setChainOptions(options);
setSelectValue(findExistingOption(options, state!.chain.registryName!));
assert(state.chain.registryName, "registryName missing");
setSelectValue(findExistingOption(options, state.chain.registryName));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
console.log(error);
setShowSettings(true);
@@ -165,10 +168,11 @@ const ChainSelect = () => {
},
});
setShowSettings(false);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
console.log(error);
setShowSettings(true);
setChainError(error.message);
setChainError(error.toString());
}
};
@@ -198,7 +202,8 @@ const ChainSelect = () => {
setChainError(null);
try {
// test client connection
const client = await StargateClient.connect(tempNodeAddress!);
assert(tempNodeAddress, "tempNodeAddress missing");
const client = await StargateClient.connect(tempNodeAddress);
await client.getHeight();
// change app state
@@ -217,9 +222,11 @@ const ChainSelect = () => {
explorerLink: tempExplorerLink,
},
});
const selectedOption = findExistingOption(chainOptions, tempRegistryName!);
assert(tempRegistryName, "tempRegistryName missing");
const selectedOption = findExistingOption(chainOptions, tempRegistryName);
setSelectValue(selectedOption);
setShowSettings(false);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
console.log(error);
setShowSettings(true);
@@ -311,7 +318,7 @@ const ChainSelect = () => {
width="48%"
value={tempDisplayDenomExponent}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setDisplayDenomExponent(parseInt(e.target.value))
setDisplayDenomExponent(parseInt(e.target.value, 10))
}
label="Denom Exponent"
/>
+3 -3
View File
@@ -1,11 +1,11 @@
import React from "react";
import { Account } from "@cosmjs/stargate";
import { DbSignature } from "../../types";
import StackableContainer from "../layout/StackableContainer";
import { AccountWithPubkey } from "../../lib/multisigHelpers";
interface Props {
signatures: DbSignature[];
account: Account;
account: AccountWithPubkey;
}
const ThresholdInfo = ({ signatures, account }: Props) => (
@@ -21,7 +21,7 @@ const ThresholdInfo = ({ signatures, account }: Props) => (
<div className="threshold">
<div className="current">{signatures.length}</div>
<div className="label divider">of</div>
<div className="required">{account.pubkey!.value.threshold}</div>
<div className="required">{account.pubkey.value.threshold}</div>
<div className="label">signatures complete</div>
</div>
</StackableContainer>
+4 -1
View File
@@ -6,6 +6,7 @@ import Button from "../inputs/Button";
import StackableContainer from "../layout/StackableContainer";
import Input from "../inputs/Input";
import { exampleAddress } from "../../lib/displayHelpers";
import { assert } from "@cosmjs/utils";
interface Props {
router: NextRouter;
@@ -22,6 +23,8 @@ const FindMultisigForm = (props: Props) => {
props.router.push(`/multi/${address}`);
};
assert(state.chain.addressPrefix, "addressPrefix missing");
return (
<StackableContainer>
<StackableContainer lessPadding>
@@ -36,7 +39,7 @@ const FindMultisigForm = (props: Props) => {
value={address}
label="Multisig Address"
name="address"
placeholder={`E.g. ${exampleAddress(0, state!.chain.addressPrefix!)}`}
placeholder={`E.g. ${exampleAddress(0, state.chain.addressPrefix)}`}
/>
<Button label="Use this Multisig" onClick={handleSearch} primary />
</StackableContainer>
+52 -42
View File
@@ -1,6 +1,7 @@
import React, { useState } from "react";
import { withRouter, NextRouter } from "next/router";
import { StargateClient } from "@cosmjs/stargate";
import { assert } from "@cosmjs/utils";
import { useAppContext } from "../../context/AppContext";
import Button from "../inputs/Button";
@@ -25,7 +26,7 @@ const MultiSigForm = (props: Props) => {
const [_processing, setProcessing] = useState(false);
const handleChangeThreshold = (e: React.ChangeEvent<HTMLInputElement>) => {
let newThreshold = parseInt(e.target.value);
let newThreshold = parseInt(e.target.value, 10);
if (newThreshold > pubkeys.length || newThreshold <= 0) {
newThreshold = threshold;
}
@@ -57,7 +58,8 @@ const MultiSigForm = (props: Props) => {
};
const getPubkeyFromNode = async (address: string) => {
const client = await StargateClient.connect(state!.chain.nodeAddress!);
assert(state.chain.nodeAddress, "nodeAddress missing");
const client = await StargateClient.connect(state.chain.nodeAddress);
const accountOnChain = await client.getAccount(address);
console.log(accountOnChain);
if (!accountOnChain || !accountOnChain.pubkey) {
@@ -90,6 +92,7 @@ const MultiSigForm = (props: Props) => {
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];
@@ -103,11 +106,13 @@ const MultiSigForm = (props: Props) => {
const compressedPubkeys = pubkeys.map((item) => item.compressedPubkey);
let multisigAddress;
try {
assert(state.chain.addressPrefix, "addressPrefix missing");
assert(state.chain.chainId, "chainId missing");
multisigAddress = await createMultisigFromCompressedSecp256k1Pubkeys(
compressedPubkeys,
threshold,
state!.chain.addressPrefix!,
state!.chain.chainId!,
state.chain.addressPrefix,
state.chain.chainId,
);
props.router.push(`/multi/${multisigAddress}`);
} catch (error) {
@@ -127,45 +132,50 @@ const MultiSigForm = (props: Props) => {
<StackableContainer lessPadding>
<p>Add the addresses that will make up this multisig.</p>
</StackableContainer>
{pubkeys.map((pubkeyGroup, index) => (
<StackableContainer lessPadding lessMargin key={index}>
<div className="key-row">
{pubkeys.length > 2 && (
<button
className="remove"
onClick={() => {
handleRemove(index);
}}
>
</button>
)}
<div className="key-inputs">
<Input
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
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, state!.chain.addressPrefix!)
}`}
error={pubkeyGroup.keyError}
onBlur={(e: React.ChangeEvent<HTMLInputElement>) => {
handleKeyBlur(index, e);
}}
/>
<button className="toggle-type" onClick={() => togglePubkey(index)}>
Use {pubkeyGroup.isPubkey ? "Address" : "Public Key"}
</button>
{pubkeys.map((pubkeyGroup, index) => {
assert(state.chain.addressPrefix, "addressPrefix missing");
return (
<StackableContainer lessPadding lessMargin key={index}>
<div className="key-row">
{pubkeys.length > 2 && (
<button
className="remove"
onClick={() => {
handleRemove(index);
}}
>
</button>
)}
<div className="key-inputs">
<Input
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
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, state.chain.addressPrefix)
}`}
error={pubkeyGroup.keyError}
onBlur={(e: React.ChangeEvent<HTMLInputElement>) => {
handleKeyBlur(index, e);
}}
/>
<button className="toggle-type" onClick={() => togglePubkey(index)}>
Use {pubkeyGroup.isPubkey ? "Address" : "Public Key"}
</button>
</div>
</div>
</div>
</StackableContainer>
))}
</StackableContainer>
);
})}
<Button label="Add another address" onClick={() => handleAddKey()} />
</StackableContainer>
+24 -14
View File
@@ -1,6 +1,7 @@
import axios from "axios";
import { calculateFee, Account } from "@cosmjs/stargate";
import { calculateFee } from "@cosmjs/stargate";
import { Decimal } from "@cosmjs/math";
import { assert } from "@cosmjs/utils";
import React, { useState } from "react";
import { withRouter, NextRouter } from "next/router";
@@ -9,10 +10,11 @@ import Button from "../inputs/Button";
import Input from "../inputs/Input";
import StackableContainer from "../layout/StackableContainer";
import { checkAddress, exampleAddress } from "../../lib/displayHelpers";
import { AccountWithPubkey } from "../../lib/multisigHelpers";
interface Props {
address: string | null;
accountOnChain: Account | null;
accountOnChain: AccountWithPubkey | null;
router: NextRouter;
closeForm: () => void;
}
@@ -23,14 +25,14 @@ const TransactionForm = (props: Props) => {
const [amount, setAmount] = useState("0");
const [memo, setMemo] = useState("");
const [gas, setGas] = useState(200000);
const [gasPrice, _setGasPrice] = useState(state!.chain.gasPrice);
const [gasPrice, _setGasPrice] = useState(state.chain.gasPrice);
const [_processing, setProcessing] = useState(false);
const [addressError, setAddressError] = useState("");
const createTransaction = (txToAddress: string, txAmount: string, txGas: number) => {
const amountInAtomics = Decimal.fromUserInput(
txAmount,
Number(state!.chain.displayDenomExponent),
Number(state.chain.displayDenomExponent),
).atomics;
const msgSend = {
fromAddress: props.address,
@@ -38,7 +40,7 @@ const TransactionForm = (props: Props) => {
amount: [
{
amount: amountInAtomics,
denom: state!.chain.denom,
denom: state.chain.denom,
},
],
};
@@ -46,11 +48,14 @@ const TransactionForm = (props: Props) => {
typeUrl: "/cosmos.bank.v1beta1.MsgSend",
value: msgSend,
};
const fee = calculateFee(Number(txGas), gasPrice!);
assert(gasPrice, "gasPrice missing");
const fee = calculateFee(Number(txGas), gasPrice);
const { accountOnChain } = props;
assert(accountOnChain, "accountOnChain missing");
return {
accountNumber: props.accountOnChain!.accountNumber,
sequence: props.accountOnChain!.sequence,
chainId: state!.chain.chainId,
accountNumber: accountOnChain.accountNumber,
sequence: accountOnChain.sequence,
chainId: state.chain.chainId,
msgs: [msg],
fee: fee,
memo: memo,
@@ -58,9 +63,10 @@ const TransactionForm = (props: Props) => {
};
const handleCreate = async () => {
const toAddressError = checkAddress(toAddress, state!.chain.addressPrefix!);
assert(state.chain.addressPrefix, "addressPrefix missing");
const toAddressError = checkAddress(toAddress, state.chain.addressPrefix);
if (toAddressError) {
setAddressError(`Invalid address for network ${state!.chain.chainId}: ${toAddressError}`);
setAddressError(`Invalid address for network ${state.chain.chainId}: ${toAddressError}`);
return;
}
@@ -73,6 +79,8 @@ const TransactionForm = (props: Props) => {
props.router.push(`${props.address}/transaction/${transactionID}`);
};
assert(state.chain.addressPrefix, "addressPrefix missing");
return (
<StackableContainer lessPadding>
<button className="remove" onClick={() => props.closeForm()}>
@@ -86,12 +94,12 @@ const TransactionForm = (props: Props) => {
value={toAddress}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setToAddress(e.target.value)}
error={addressError}
placeholder={`E.g. ${exampleAddress(0, state!.chain.addressPrefix!)}`}
placeholder={`E.g. ${exampleAddress(0, state.chain.addressPrefix)}`}
/>
</div>
<div className="form-item">
<Input
label={`Amount (${state!.chain.displayDenom})`}
label={`Amount (${state.chain.displayDenom})`}
name="amount"
type="number"
value={amount}
@@ -104,7 +112,9 @@ const TransactionForm = (props: Props) => {
name="gas"
type="number"
value={gas}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setGas(parseInt(e.target.value))}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setGas(parseInt(e.target.value, 10))
}
/>
</div>
<div className="form-item">
+10 -7
View File
@@ -31,8 +31,9 @@ const TransactionSigning = (props: Props) => {
const connectKeplr = async () => {
try {
await window.keplr.enable(state!.chain.chainId!);
const tempWalletAccount = await window.keplr.getKey(state!.chain.chainId!);
assert(state.chain.chainId, "chainId missing");
await window.keplr.enable(state.chain.chainId);
const tempWalletAccount = await window.keplr.getKey(state.chain.chainId);
console.log(tempWalletAccount);
const tempHasSigned = props.signatures.some(
(sig) => sig.address === tempWalletAccount.bech32Address,
@@ -46,13 +47,15 @@ const TransactionSigning = (props: Props) => {
};
const connectLedger = async () => {
assert(state.chain.addressPrefix, "addressPrefix missing");
// Prepare ledger
const ledgerTransport = await TransportWebUSB.create(120000, 120000);
// Setup signer
const offlineSigner = new LedgerSigner(ledgerTransport, {
hdPaths: [makeCosmoshubPath(0)],
prefix: state!.chain.addressPrefix,
prefix: state.chain.addressPrefix,
});
console.log(offlineSigner);
const accounts = await offlineSigner.getAccounts();
@@ -73,10 +76,10 @@ const TransactionSigning = (props: Props) => {
};
const signTransaction = async () => {
assert(state.chain.chainId, "chainId missing");
const offlineSigner =
walletType === "keplr"
? window.getOfflineSignerOnlyAmino(state!.chain.chainId)
: ledgerSigner;
walletType === "keplr" ? window.getOfflineSignerOnlyAmino(state.chain.chainId) : ledgerSigner;
const signerAddress = walletAccount?.bech32Address;
assert(signerAddress, "Missing signer address");
@@ -85,7 +88,7 @@ const TransactionSigning = (props: Props) => {
const signerData = {
accountNumber: props.tx.accountNumber,
sequence: props.tx.sequence,
chainId: state!.chain.chainId as string,
chainId: state.chain.chainId,
};
const { bodyBytes, signatures } = await signingClient.sign(
+1
View File
@@ -5,6 +5,7 @@ interface Props {
disabled?: boolean;
href?: string;
label: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onClick?: () => any;
}
const Button = (props: Props) => (
+2
View File
@@ -1,6 +1,7 @@
import React, { CSSProperties } from "react";
import Select, { ControlProps } from "react-select";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const StyledSelect = (props: any) => {
const customStyles = {
control: (provided: CSSProperties, state: ControlProps) => ({
@@ -16,6 +17,7 @@ const StyledSelect = (props: any) => {
borderColor: "rgba(255, 255, 255, 1)",
},
}),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
option: (provided: CSSProperties, state: any) => ({
...provided,
background: state.isSelected ? "rgba(255, 255, 255, 0.2)" : "none",
+5 -4
View File
@@ -1,4 +1,4 @@
import { Bech32, fromBase64, toBase64 } from "@cosmjs/encoding";
import { fromBase64, fromBech32, toBase64, toBech32 } from "@cosmjs/encoding";
import { sha512 } from "@cosmjs/crypto";
import { Decimal } from "@cosmjs/math";
import { Coin } from "@cosmjs/amino";
@@ -73,11 +73,11 @@ const printableCoins = (coins: Coin[], chainInfo: ChainInfo) => {
*/
const exampleAddress = (index: number, chainAddressPrefix: string) => {
const usedIndex = index || 0;
let data = Bech32.decode("cosmos1vqpjljwsynsn58dugz0w8ut7kun7t8ls2qkmsq").data;
let data = fromBech32("cosmos1vqpjljwsynsn58dugz0w8ut7kun7t8ls2qkmsq").data;
for (let i = 0; i < usedIndex; ++i) {
data = sha512(data).slice(0, data.length); // hash one time and trim to original length
}
return Bech32.encode(chainAddressPrefix, data);
return toBech32(chainAddressPrefix, data);
};
/**
@@ -109,7 +109,8 @@ const checkAddress = (input: string, chainAddressPrefix: string) => {
let data;
let prefix;
try {
({ data, prefix } = Bech32.decode(input));
({ data, prefix } = fromBech32(input));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
return error.toString();
}
+16 -14
View File
@@ -1,5 +1,5 @@
import axios from "axios";
import { createMultisigThresholdPubkey, pubkeyToAddress } from "@cosmjs/amino";
import { createMultisigThresholdPubkey, Pubkey, pubkeyToAddress } from "@cosmjs/amino";
import { Account } from "@cosmjs/stargate";
import { StargateClient } from "@cosmjs/stargate";
@@ -40,6 +40,9 @@ const createMultisigFromCompressedSecp256k1Pubkeys = async (
return res.data.address;
};
/** Like Account but with non-optional pubkey */
export type AccountWithPubkey = Account & { readonly pubkey: Pubkey };
/**
* This gets a multisigs account (pubkey, sequence, account number, etc) from
* a node and/or the api if the multisig was made on this app
@@ -51,33 +54,32 @@ const createMultisigFromCompressedSecp256k1Pubkeys = async (
const getMultisigAccount = async (
address: string,
client: StargateClient,
): Promise<Account | null> => {
): Promise<AccountWithPubkey | null> => {
// we need the multisig pubkeys to create transactions, if the multisig
// is new, and has never submitted a transaction its pubkeys will not be
// available from a node. If the multisig was created with this instance
// of this tool its pubkey will be available in the fauna datastore
let accountOnChain: Mutable<Account> | null = await client.getAccount(address);
const accountOnChain = await client.getAccount(address);
const chainId = await client.getChainId();
if (!accountOnChain) return null;
if (!accountOnChain || !accountOnChain.pubkey) {
let pubkey: Pubkey;
if (accountOnChain.pubkey) {
pubkey = accountOnChain.pubkey;
} else {
console.log("No pubkey on chain for: ", address);
const res = await axios.get(`/api/chain/${chainId}/multisig/${address}`);
if (res.status !== 200) {
throw new Error("Multisig has no pubkey on node, and was not created using this tool.");
}
const pubkey = JSON.parse(res.data.pubkeyJSON);
if (!accountOnChain) {
accountOnChain = null;
}
accountOnChain!.pubkey = pubkey;
pubkey = JSON.parse(res.data.pubkeyJSON);
}
return accountOnChain;
};
type Mutable<Type> = {
-readonly [Key in keyof Type]: Type[Key];
return {
...accountOnChain,
pubkey: pubkey,
};
};
export { createMultisigFromCompressedSecp256k1Pubkeys, getMultisigAccount };
+923 -124
View File
File diff suppressed because it is too large Load Diff
+5 -2
View File
@@ -4,8 +4,8 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint --max-warnings 0 \"./**/*.js\"",
"lint:fix": "eslint --max-warnings 0 \"./**/*.js\" --fix"
"lint": "eslint --max-warnings 0 \"./**/*.{js,jsx,ts,tsx}\"",
"lint:fix": "eslint --max-warnings 0 \"./**/*.{js,jsx,ts,tsx}\" --fix"
},
"dependencies": {
"@cosmjs/amino": "^0.28.4",
@@ -15,6 +15,7 @@
"@cosmjs/math": "^0.28.4",
"@cosmjs/proto-signing": "^0.28.4",
"@cosmjs/stargate": "^0.28.4",
"@cosmjs/utils": "^0.28.4",
"@keplr-wallet/types": "^0.9.0-alpha.4",
"@ledgerhq/hw-transport-webusb": "^6.24.1",
"axios": "^0.21.1",
@@ -31,6 +32,8 @@
"@types/react": "^18.0.9",
"@types/react-dom": "^18.0.5",
"@types/react-select": "^5.0.1",
"@typescript-eslint/eslint-plugin": "^5.27.1",
"@typescript-eslint/parser": "^5.27.1",
"cosmjs-types": "^0.4.1",
"eslint": "^8.6.0",
"eslint-config-prettier": "^8.3.0",
+2 -1
View File
@@ -4,9 +4,10 @@ import ChainSelect from "../components/chainSelect/ChainSelect";
import type { AppProps } from "next/app";
function MultisigApp({ Component, pageProps }: AppProps) {
const showChainSelect = process.env.NEXT_PUBLIC_MULTICHAIN?.toLowerCase() === "true";
return (
<AppWrapper>
{process.env.NEXT_PUBLIC_MULTICHAIN!.toLowerCase() === "true" && <ChainSelect />}
{showChainSelect && <ChainSelect />}
<Component {...pageProps} />
</AppWrapper>
);
@@ -16,6 +16,7 @@ export default async function (req: NextApiRequest, res: NextApiResponse) {
console.log("success", getRes.data.data.getMultisig);
res.status(200).send(getRes.data.data.getMultisig);
return;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) {
console.log(err);
res.status(400).send(err.message);
@@ -11,6 +11,7 @@ export default async function (req: NextApiRequest, res: NextApiResponse) {
console.log("success", saveRes.data);
res.status(200).send(saveRes.data.data.createMultisig);
return;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) {
console.log(err);
res.status(400).send(err.message);
@@ -12,6 +12,7 @@ export default async function (req: NextApiRequest, res: NextApiResponse) {
console.log("success", saveRes.data);
res.status(200).send(saveRes.data.data.updateTransaction);
return;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) {
console.log(err);
res.status(400).send(err.message);
@@ -12,6 +12,7 @@ export default async function (req: NextApiRequest, res: NextApiResponse) {
console.log("success", saveRes.data);
res.status(200).send(saveRes.data.data.createSignature);
return;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) {
console.log(err);
res.status(400).send(err.message);
+1
View File
@@ -11,6 +11,7 @@ export default async function (req: NextApiRequest, res: NextApiResponse) {
console.log("success", saveRes.data);
res.status(200).send({ transactionID: saveRes.data.data.createTransaction._id });
return;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) {
console.log(err);
res.status(400).send(err.message);
+15 -12
View File
@@ -1,13 +1,13 @@
import React, { useState, useEffect } from "react";
import { pubkeyToAddress, Pubkey } from "@cosmjs/amino";
import { Account } from "@cosmjs/stargate";
import { StargateClient } from "@cosmjs/stargate";
import { assert } from "@cosmjs/utils";
import { Coin } from "cosmjs-types/cosmos/base/v1beta1/coin";
import { useRouter } from "next/router";
import { useAppContext } from "../../../context/AppContext";
import Button from "../../../components/inputs/Button";
import { getMultisigAccount } from "../../../lib/multisigHelpers";
import { AccountWithPubkey, getMultisigAccount } from "../../../lib/multisigHelpers";
import HashView from "../../../components/dataViews/HashView";
import MultisigHoldings from "../../../components/dataViews/MultisigHoldings";
import MultisigMembers from "../../../components/dataViews/MultisigMembers";
@@ -25,14 +25,12 @@ function participantAddressesFromMultisig(multisigPubkey: Pubkey, addressPrefix:
);
}
interface Props {}
const multipage = (_props: Props) => {
const multipage = () => {
const { state } = useAppContext();
const [showTxForm, setShowTxForm] = useState(false);
const [holdings, setHoldings] = useState<Coin | null>(null);
const [multisigAddress, setMultisigAddress] = useState("");
const [accountOnChain, setAccountOnChain] = useState<Account | null>(null);
const [accountOnChain, setAccountOnChain] = useState<AccountWithPubkey | null>(null);
const [accountError, setAccountError] = useState(null);
const router = useRouter();
@@ -47,17 +45,22 @@ const multipage = (_props: Props) => {
const fetchMultisig = async (address: string) => {
setAccountError(null);
try {
const client = await StargateClient.connect(state!.chain.nodeAddress!);
const tempHoldings = await client.getBalance(address, state!.chain.denom!);
assert(state.chain.nodeAddress, "Node address missing");
const client = await StargateClient.connect(state.chain.nodeAddress);
assert(state.chain.denom, "denom missing");
const tempHoldings = await client.getBalance(address, state.chain.denom);
const tempAccountOnChain = await getMultisigAccount(address, client);
setHoldings(tempHoldings);
setAccountOnChain(tempAccountOnChain);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
setAccountError(error.message);
console.log("Account error:", error);
}
};
assert(state.chain.addressPrefix, "address prefix missing");
return (
<Page>
<StackableContainer base>
@@ -71,13 +74,13 @@ const multipage = (_props: Props) => {
)}
</h1>
</StackableContainer>
{accountOnChain?.pubkey && (
{accountOnChain && (
<MultisigMembers
members={participantAddressesFromMultisig(
accountOnChain?.pubkey,
state!.chain.addressPrefix!,
accountOnChain.pubkey,
state.chain.addressPrefix,
)}
threshold={accountOnChain?.pubkey.value.threshold}
threshold={accountOnChain.pubkey.value.threshold}
/>
)}
{accountError && (
@@ -1,7 +1,7 @@
import axios from "axios";
import React from "react";
import { GetServerSideProps } from "next";
import { StargateClient, makeMultisignedTx, Account } from "@cosmjs/stargate";
import { StargateClient, makeMultisignedTx } from "@cosmjs/stargate";
import { Coin } from "cosmjs-types/cosmos/base/v1beta1/coin";
import { TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx";
import { useState, useEffect } from "react";
@@ -13,13 +13,14 @@ import { DbSignature, DbTransaction } from "../../../../types";
import { useAppContext } from "../../../../context/AppContext";
import Button from "../../../../components/inputs/Button";
import { findTransactionByID } from "../../../../lib/graphqlHelpers";
import { getMultisigAccount } from "../../../../lib/multisigHelpers";
import { AccountWithPubkey, getMultisigAccount } from "../../../../lib/multisigHelpers";
import Page from "../../../../components/layout/Page";
import StackableContainer from "../../../../components/layout/StackableContainer";
import ThresholdInfo from "../../../../components/dataViews/ThresholdInfo";
import TransactionInfo from "../../../../components/dataViews/TransactionInfo";
import TransactionSigning from "../../../../components/forms/TransactionSigning";
import CompletedTransaction from "../../../../components/dataViews/CompletedTransaction";
import { assert } from "@cosmjs/utils";
interface Props {
props: {
@@ -32,7 +33,8 @@ interface Props {
export const getServerSideProps: GetServerSideProps = async (context): Promise<Props> => {
// get transaction info
const transactionID = context.params!.transactionID!.toString();
const transactionID = context.params?.transactionID?.toString();
assert(transactionID, "Transaction ID missing");
let transactionJSON;
let txHash;
let signatures;
@@ -43,7 +45,7 @@ export const getServerSideProps: GetServerSideProps = async (context): Promise<P
txHash = getRes.data.data.findTransactionByID.txHash;
transactionJSON = getRes.data.data.findTransactionByID.dataJSON;
signatures = getRes.data.data.findTransactionByID.signatures.data || [];
} catch (err: any) {
} catch (err: unknown) {
console.log(err);
}
return {
@@ -75,7 +77,7 @@ const transactionPage = ({
const [isBroadcasting, setIsBroadcasting] = useState(false);
const [transactionHash, setTransactionHash] = useState(txHash);
const [_holdings, setHoldings] = useState<Coin | null>(null);
const [accountOnChain, setAccountOnChain] = useState<Account | null>(null);
const [accountOnChain, setAccountOnChain] = useState<AccountWithPubkey | null>(null);
const [accountError, setAccountError] = useState(null);
const txInfo: DbTransaction = (transactionJSON && JSON.parse(transactionJSON)) || null;
const router = useRouter();
@@ -93,13 +95,16 @@ const transactionPage = ({
const fetchMultisig = async (address: string) => {
try {
const client = await StargateClient.connect(state!.chain.nodeAddress!);
const tempHoldings = await client.getBalance(address, state!.chain.denom!);
assert(state.chain.nodeAddress, "Node address missing");
const client = await StargateClient.connect(state.chain.nodeAddress);
assert(state.chain.denom, "denom missing");
const tempHoldings = await client.getBalance(address, state.chain.denom);
const tempAccountOnChain = await getMultisigAccount(address, client);
setHoldings(tempHoldings);
setAccountOnChain(tempAccountOnChain);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
setAccountError(error.message);
setAccountError(error.toString());
console.log("Account error:", error);
}
};
@@ -109,15 +114,18 @@ const transactionPage = ({
setIsBroadcasting(true);
setBroadcastError("");
if (!accountOnChain) throw new Error("Account on chain value missing.");
const bodyBytes = fromBase64(currentSignatures[0].bodyBytes);
const signedTx = makeMultisignedTx(
accountOnChain!.pubkey as MultisigThresholdPubkey,
accountOnChain.pubkey as MultisigThresholdPubkey,
txInfo.sequence,
txInfo.fee,
bodyBytes,
new Map(currentSignatures.map((s) => [s.address, fromBase64(s.signature)])),
);
const broadcaster = await StargateClient.connect(state!.chain.nodeAddress!);
assert(state.chain.nodeAddress, "Node address missing");
const broadcaster = await StargateClient.connect(state.chain.nodeAddress);
const result = await broadcaster.broadcastTx(
Uint8Array.from(TxRaw.encode(signedTx).finish()),
);
@@ -126,9 +134,10 @@ const transactionPage = ({
txHash: result.transactionHash,
});
setTransactionHash(result.transactionHash);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) {
setIsBroadcasting(false);
setBroadcastError(e.message);
setBroadcastError(e.toString());
}
};
@@ -151,7 +160,7 @@ const transactionPage = ({
<ThresholdInfo signatures={currentSignatures} account={accountOnChain} />
)}
{accountOnChain &&
currentSignatures.length >= parseInt(accountOnChain!.pubkey!.value.threshold, 10) &&
currentSignatures.length >= parseInt(accountOnChain.pubkey.value.threshold, 10) &&
!transactionHash && (
<>
<Button
+2
View File
@@ -4,9 +4,11 @@ import { EncodeObject } from "@cosmjs/proto-signing";
declare global {
interface Window {
keplr: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
enable: (chainId: string) => any;
getKey: (chainId: string) => Promise<WalletAccount>;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
getOfflineSignerOnlyAmino: any;
}
}