Merge branch 'master' into feat/undelegate-redelegate-claim
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
FAUNADB_SECRET=
|
||||
FAUNADB_URL=https://graphql.eu.fauna.com/graphql
|
||||
NEXT_PUBLIC_NODE_ADDRESS=https://cosmoshub.validator.network:443
|
||||
NEXT_PUBLIC_DENOM=uatom
|
||||
NEXT_PUBLIC_DISPLAY_DENOM=ATOM
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
FAUNADB_SECRET=
|
||||
FAUNADB_URL=https://graphql.eu.fauna.com/graphql
|
||||
NEXT_PUBLIC_NODE_ADDRESS=https://rpc.uni.junonetwork.io:443
|
||||
NEXT_PUBLIC_DENOM=ujunox
|
||||
NEXT_PUBLIC_DISPLAY_DENOM=JUNOX
|
||||
NEXT_PUBLIC_DISPLAY_DENOM_EXPONENT=6
|
||||
NEXT_PUBLIC_GAS_PRICE=0.04ujunox
|
||||
NEXT_PUBLIC_CHAIN_ID=uni-6
|
||||
NEXT_PUBLIC_ADDRESS_PREFIX=juno
|
||||
NEXT_PUBLIC_REGISTRY_NAME=junotestnet
|
||||
NEXT_PUBLIC_EXPLORER_LINK_TX="https://testnet.mintscan.io/juno-testnet/txs/\${txHash}"
|
||||
NEXT_PUBLIC_CHAIN_DISPLAY_NAME="Juno Testnet"
|
||||
NEXT_PUBLIC_MULTICHAIN=true
|
||||
+2
-9
@@ -15,14 +15,8 @@ module.exports = {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
plugins: ["prettier", "@typescript-eslint"],
|
||||
extends: [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:react/recommended",
|
||||
"prettier",
|
||||
"plugin:prettier/recommended",
|
||||
],
|
||||
plugins: ["@typescript-eslint"],
|
||||
extends: ["next/core-web-vitals", "plugin:@typescript-eslint/recommended", "prettier"],
|
||||
rules: {
|
||||
curly: ["warn", "multi-line", "consistent"],
|
||||
"no-bitwise": "warn",
|
||||
@@ -41,5 +35,4 @@ module.exports = {
|
||||
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
|
||||
],
|
||||
},
|
||||
overrides: [],
|
||||
};
|
||||
|
||||
+4
-1
@@ -21,4 +21,7 @@ yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Local Netlify folder
|
||||
.netlify
|
||||
.netlify
|
||||
|
||||
# IDE folder
|
||||
.idea
|
||||
|
||||
@@ -52,7 +52,7 @@ This app relies on FaunaDB as for storing account, transaction and signature det
|
||||
|
||||
- Create a [FaunaDB](https://dashboard.fauna.com/) account
|
||||
- Create a new database
|
||||
- Use the "Classic" region
|
||||
- Use the "Europe (EU)" region
|
||||
- Click the "Graphql" tab, and import the `db-schema.graphql` file in the root of this repo
|
||||
- Click the "Security" tab, and create a key. Copy that key into the `.env.local` file for the `FAUNADB_SECRET` value
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import Link from "next/link";
|
||||
|
||||
const DevHelper = () => (
|
||||
<div className="dev-helper">
|
||||
@@ -6,21 +6,21 @@ const DevHelper = () => (
|
||||
<h4>Pages</h4>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/">Index/Start</a>
|
||||
<Link href="/">Index/Start</Link>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/create">Create Multisig</a>
|
||||
<Link href="/create">Create Multisig</Link>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/multi/cosmos10nmdf6nt2qzvgn9q2nuwmmfc359yfesmu3gw22">View Multisig</a>
|
||||
<Link href="/multi/cosmos10nmdf6nt2qzvgn9q2nuwmmfc359yfesmu3gw22">View Multisig</Link>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/multi/cosmos10nmdf6nt2qzvgn9q2nuwmmfc359yfesmu3gw22/transaction/295630000375202310">
|
||||
<Link href="/multi/cosmos10nmdf6nt2qzvgn9q2nuwmmfc359yfesmu3gw22/transaction/295630000375202310">
|
||||
View/Sign Transaction
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
<a href="https://github.com/samepant/cosmoshub-legacy-multisig">View on Github</a>
|
||||
<Link href="https://github.com/samepant/cosmoshub-legacy-multisig">View on Github</Link>
|
||||
|
||||
<style jsx>{`
|
||||
.dev-helper {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
import { StargateClient } from "@cosmjs/stargate";
|
||||
|
||||
import { assert } from "@cosmjs/utils";
|
||||
import axios from "axios";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useAppContext } from "../../context/AppContext";
|
||||
import GearIcon from "../icons/Gear";
|
||||
import Button from "../inputs/Button";
|
||||
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";
|
||||
import { ChainRegistryAsset } from "./chainregistry";
|
||||
|
||||
interface ChainOption {
|
||||
label: string;
|
||||
@@ -58,25 +58,7 @@ const ChainSelect = () => {
|
||||
|
||||
const url = "https://api.github.com/repos/cosmos/chain-registry/contents";
|
||||
|
||||
useEffect(() => {
|
||||
getGhJson();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// set settings form fields to new values
|
||||
setChainId(state.chain.chainId);
|
||||
setNodeAddress(state.chain.nodeAddress);
|
||||
setAddressPrefix(state.chain.addressPrefix);
|
||||
setDenom(state.chain.denom);
|
||||
setDisplayDenom(state.chain.displayDenom);
|
||||
setDisplayDenomExponent(state.chain.displayDenomExponent);
|
||||
setGasPrice(state.chain.gasPrice);
|
||||
setChainName(state.chain.chainDisplayName);
|
||||
setExplorerLink(state.chain.explorerLink);
|
||||
setRegistryName(state.chain.registryName);
|
||||
}, [state]);
|
||||
|
||||
const getGhJson = async () => {
|
||||
const getGhJson = useCallback(async () => {
|
||||
// getting chain info from this repo: https://github.com/cosmos/chain-registry
|
||||
try {
|
||||
const res = await axios.get(url);
|
||||
@@ -96,14 +78,36 @@ const ChainSelect = () => {
|
||||
setShowSettings(true);
|
||||
setChainError(error.message);
|
||||
}
|
||||
};
|
||||
}, [state.chain.registryName]);
|
||||
|
||||
useEffect(() => {
|
||||
getGhJson();
|
||||
}, [getGhJson]);
|
||||
|
||||
useEffect(() => {
|
||||
// set settings form fields to new values
|
||||
setChainId(state.chain.chainId);
|
||||
setNodeAddress(state.chain.nodeAddress);
|
||||
setAddressPrefix(state.chain.addressPrefix);
|
||||
setDenom(state.chain.denom);
|
||||
setDisplayDenom(state.chain.displayDenom);
|
||||
setDisplayDenomExponent(state.chain.displayDenomExponent);
|
||||
setGasPrice(state.chain.gasPrice);
|
||||
setChainName(state.chain.chainDisplayName);
|
||||
setExplorerLink(state.chain.explorerLink);
|
||||
setRegistryName(state.chain.registryName);
|
||||
}, [state]);
|
||||
|
||||
const findExistingOption = (options: ChainOption[], registryName: string) => {
|
||||
const index = options.findIndex((option) => option.label === registryName);
|
||||
if (index >= 0) {
|
||||
return options[index];
|
||||
}
|
||||
return { label: "unkown chain", value: -1 };
|
||||
return {
|
||||
label:
|
||||
registryName === process.env.NEXT_PUBLIC_REGISTRY_NAME ? registryName : "unknown chain",
|
||||
value: -1,
|
||||
};
|
||||
};
|
||||
|
||||
const getChainInfo = async (chainOption: GithubChainRegistryItem) => {
|
||||
@@ -127,24 +131,26 @@ const ChainSelect = () => {
|
||||
const chainDisplayName = chainData["pretty_name"];
|
||||
const registryName = chainOption.name;
|
||||
const explorerLink = getExplorerFromArray(chainData.explorers);
|
||||
let asset = null;
|
||||
let denom = "";
|
||||
let displayDenom = "";
|
||||
const displayDenomExponent = 6;
|
||||
let gasPrice = "";
|
||||
let denom: string;
|
||||
let displayDenom: string;
|
||||
let displayDenomExponent: number;
|
||||
let gasPrice: string;
|
||||
|
||||
if (assetData.assets.length > 1) {
|
||||
denom = "";
|
||||
displayDenom = "";
|
||||
gasPrice = "";
|
||||
displayDenomExponent = 0;
|
||||
|
||||
setChainError("Multiple token denoms available, enter manually");
|
||||
setShowSettings(true);
|
||||
} else {
|
||||
asset = assetData.assets[0];
|
||||
const asset: ChainRegistryAsset = assetData.assets[0];
|
||||
denom = asset.base;
|
||||
displayDenom = asset.symbol;
|
||||
gasPrice = `0.03${asset.base}`;
|
||||
const displayUnit = asset.denom_units.find((u) => u.denom == asset.display);
|
||||
displayDenomExponent = displayUnit?.exponent ?? 6;
|
||||
}
|
||||
|
||||
// test client connection
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* See https://github.com/cosmos/chain-registry/blob/1e9ecde770951cab90f0853a624411d79af90b83/provenance/assetlist.json#L8-L12
|
||||
*/
|
||||
export interface ChainRegistryDemonUnit {
|
||||
denom: string;
|
||||
exponent: number;
|
||||
aliases: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* See https://github.com/cosmos/chain-registry/blob/1e9ecde770951cab90f0853a624411d79af90b83/provenance/assetlist.json#L5-L28
|
||||
*/
|
||||
export interface ChainRegistryAsset {
|
||||
description: string;
|
||||
denom_units: ChainRegistryDemonUnit[];
|
||||
base: string;
|
||||
name: string;
|
||||
display: string;
|
||||
symbol: string;
|
||||
logo_URIs: {
|
||||
png: string;
|
||||
svg: string;
|
||||
};
|
||||
coingecko_id: string;
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
import React from "react";
|
||||
import { isSecp256k1Pubkey, pubkeyToAddress, SinglePubkey } from "@cosmjs/amino";
|
||||
|
||||
import HashView from "./HashView";
|
||||
import StackableContainer from "../layout/StackableContainer";
|
||||
|
||||
interface Props {
|
||||
/** Addresses of the multisig members */
|
||||
members: string[];
|
||||
/** Pubkeys of the multisig members */
|
||||
members: readonly SinglePubkey[];
|
||||
addressPrefix: string;
|
||||
threshold: string;
|
||||
}
|
||||
|
||||
@@ -19,11 +21,16 @@ const MultisigMembers = (props: Props) => (
|
||||
<div>
|
||||
<h2>Members</h2>
|
||||
<ul>
|
||||
{props.members.map((address: string) => (
|
||||
<li key={address} className="info">
|
||||
<HashView hash={address} />
|
||||
</li>
|
||||
))}
|
||||
{props.members.map((pubkey) => {
|
||||
const address = pubkeyToAddress(pubkey, props.addressPrefix);
|
||||
// simplePubkey is base64 encoded compressed secp256k1 in almost every case. The fallback is added to be safe though.
|
||||
const simplePubkey = isSecp256k1Pubkey(pubkey) ? pubkey.value : `${pubkey.type} pubkey`;
|
||||
return (
|
||||
<li key={address} className="info">
|
||||
<HashView hash={`${address} (${simplePubkey})`} />
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import React from "react";
|
||||
import { Coin } from "cosmjs-types/cosmos/base/v1beta1/coin";
|
||||
|
||||
import { DbTransaction } from "../../types";
|
||||
import { useAppContext } from "../../context/AppContext";
|
||||
import HashView from "./HashView";
|
||||
import { printableCoin, printableCoins } from "../../lib/displayHelpers";
|
||||
import { DbTransaction } from "../../types";
|
||||
import StackableContainer from "../layout/StackableContainer";
|
||||
import { printableCoins, printableCoin } from "../../lib/displayHelpers";
|
||||
import HashView from "./HashView";
|
||||
|
||||
interface Props {
|
||||
tx: DbTransaction;
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
import { Decimal } from "@cosmjs/math";
|
||||
import { Account, calculateFee } from "@cosmjs/stargate";
|
||||
import { assert } from "@cosmjs/utils";
|
||||
import axios from "axios";
|
||||
import { NextRouter, withRouter } from "next/router";
|
||||
import React, { useState } from "react";
|
||||
import { useAppContext } from "../../context/AppContext";
|
||||
import { checkAddress, exampleValidatorAddress } from "../../lib/displayHelpers";
|
||||
import Button from "../inputs/Button";
|
||||
import Input from "../inputs/Input";
|
||||
import StackableContainer from "../layout/StackableContainer";
|
||||
|
||||
interface Props {
|
||||
address: string | null;
|
||||
accountOnChain: Account | null;
|
||||
router: NextRouter;
|
||||
closeForm: () => void;
|
||||
}
|
||||
|
||||
const DelegationForm = (props: Props) => {
|
||||
const { state } = useAppContext();
|
||||
const [validatorAddress, setValidatorAddress] = useState("");
|
||||
const [amount, setAmount] = useState("0");
|
||||
const [memo, setMemo] = useState("");
|
||||
const [gas, setGas] = useState(200000);
|
||||
const [gasPrice, _setGasPrice] = useState(state.chain.gasPrice);
|
||||
const [_processing, setProcessing] = useState(false);
|
||||
const [addressError, setAddressError] = useState("");
|
||||
|
||||
const createTransaction = (txValidatorAddress: string, txAmount: string, gasLimit: number) => {
|
||||
assert(Number.isSafeInteger(gasLimit) && gasLimit > 0, "gas limit must be a positive integer");
|
||||
|
||||
const amountInAtomics = Decimal.fromUserInput(
|
||||
txAmount,
|
||||
Number(state.chain.displayDenomExponent),
|
||||
).atomics;
|
||||
const msgDelegate = {
|
||||
delegatorAddress: props.address,
|
||||
validatorAddress: txValidatorAddress,
|
||||
amount: {
|
||||
amount: amountInAtomics,
|
||||
denom: state.chain.denom,
|
||||
},
|
||||
};
|
||||
const msg = {
|
||||
typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
|
||||
value: msgDelegate,
|
||||
};
|
||||
assert(gasPrice, "gasPrice missing");
|
||||
const fee = calculateFee(gasLimit, gasPrice);
|
||||
const { accountOnChain } = props;
|
||||
assert(accountOnChain, "accountOnChain missing");
|
||||
return {
|
||||
accountNumber: accountOnChain.accountNumber,
|
||||
sequence: accountOnChain.sequence,
|
||||
chainId: state.chain.chainId,
|
||||
msgs: [msg],
|
||||
fee: fee,
|
||||
memo: memo,
|
||||
};
|
||||
};
|
||||
|
||||
const handleCreate = async () => {
|
||||
assert(state.chain.addressPrefix, "addressPrefix missing");
|
||||
const validatorAddressError = checkAddress(validatorAddress, state.chain.addressPrefix);
|
||||
if (validatorAddressError) {
|
||||
setAddressError(
|
||||
`Invalid address for network ${state.chain.chainId}: ${validatorAddressError}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
setProcessing(true);
|
||||
const tx = createTransaction(validatorAddress, amount, gas);
|
||||
console.log(tx, "tx data");
|
||||
const dataJSON = JSON.stringify(tx);
|
||||
const res = await axios.post("/api/transaction", { dataJSON });
|
||||
console.log(dataJSON, "tx dataJSON", res);
|
||||
const { transactionID } = res.data;
|
||||
props.router.push(`${props.address}/transaction/${transactionID}`);
|
||||
};
|
||||
|
||||
assert(state.chain.addressPrefix, "addressPrefix missing");
|
||||
|
||||
return (
|
||||
<StackableContainer lessPadding>
|
||||
<button className="remove" onClick={() => props.closeForm()}>
|
||||
✕
|
||||
</button>
|
||||
<h2>Create Delegation</h2>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
label="Validator Address"
|
||||
name="validatorAddress"
|
||||
value={validatorAddress}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setValidatorAddress(e.target.value)}
|
||||
error={addressError}
|
||||
placeholder={`E.g. ${exampleValidatorAddress(0, state.chain.addressPrefix)}`}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
label={`Amount (${state.chain.displayDenom})`}
|
||||
name="amount"
|
||||
type="number"
|
||||
value={amount}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setAmount(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
label="Gas Limit"
|
||||
name="gas"
|
||||
type="number"
|
||||
value={gas}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setGas(parseInt(e.target.value, 10))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-item">
|
||||
<Input label="Gas Price" name="gas_price" type="string" value={gasPrice} disabled={true} />
|
||||
</div>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
label="Memo"
|
||||
name="memo"
|
||||
type="text"
|
||||
value={memo}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setMemo(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Button label="Delegate" onClick={handleCreate} />
|
||||
<style jsx>{`
|
||||
p {
|
||||
margin-top: 15px;
|
||||
}
|
||||
.form-item {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
button.remove {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
color: white;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
}
|
||||
`}</style>
|
||||
</StackableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default withRouter(DelegationForm);
|
||||
@@ -28,7 +28,9 @@ const TransactionForm = (props: Props) => {
|
||||
const [_processing, setProcessing] = useState(false);
|
||||
const [addressError, setAddressError] = useState("");
|
||||
|
||||
const createTransaction = (txToAddress: string, txAmount: string, txGas: number) => {
|
||||
const createTransaction = (txToAddress: string, txAmount: string, gasLimit: number) => {
|
||||
assert(Number.isSafeInteger(gasLimit) && gasLimit > 0, "gas limit must be a positive integer");
|
||||
|
||||
const amountInAtomics = Decimal.fromUserInput(
|
||||
txAmount,
|
||||
Number(state.chain.displayDenomExponent),
|
||||
@@ -48,7 +50,7 @@ const TransactionForm = (props: Props) => {
|
||||
value: msgSend,
|
||||
};
|
||||
assert(gasPrice, "gasPrice missing");
|
||||
const fee = calculateFee(Number(txGas), gasPrice);
|
||||
const fee = calculateFee(gasLimit, gasPrice);
|
||||
const { accountOnChain } = props;
|
||||
assert(accountOnChain, "accountOnChain missing");
|
||||
return {
|
||||
|
||||
@@ -33,6 +33,9 @@ const TransactionSigning = (props: Props) => {
|
||||
try {
|
||||
assert(state.chain.chainId, "chainId missing");
|
||||
await window.keplr.enable(state.chain.chainId);
|
||||
window.keplr.defaultOptions = {
|
||||
sign: { preferNoSetFee: true, preferNoSetMemo: true, disableBalanceCheck: true },
|
||||
};
|
||||
const tempWalletAccount = await window.keplr.getKey(state.chain.chainId);
|
||||
console.log(tempWalletAccount);
|
||||
const tempHasSigned = props.signatures.some(
|
||||
|
||||
+10
-7
@@ -17,14 +17,17 @@ function getChainInfoFromUrl(): ChainInfo {
|
||||
const params = new URLSearchParams(url);
|
||||
const chainInfo: ChainInfo = {
|
||||
nodeAddress: decodeURIComponent(params.get("nodeAddress") || ""),
|
||||
denom: params.get("denom") || "",
|
||||
displayDenom: params.get("displayDenom") || "",
|
||||
displayDenomExponent: parseInt(params.get("displayDenomExponent") || "", 10),
|
||||
gasPrice: params.get("gasPrice") || "",
|
||||
chainId: params.get("chainId") || "",
|
||||
denom: decodeURIComponent(params.get("denom") || ""),
|
||||
displayDenom: decodeURIComponent(params.get("displayDenom") || ""),
|
||||
displayDenomExponent: parseInt(
|
||||
decodeURIComponent(params.get("displayDenomExponent") || ""),
|
||||
10,
|
||||
),
|
||||
gasPrice: decodeURIComponent(params.get("gasPrice") || ""),
|
||||
chainId: decodeURIComponent(params.get("chainId") || ""),
|
||||
chainDisplayName: decodeURIComponent(params.get("chainDisplayName") || ""),
|
||||
registryName: params.get("registryName") || "",
|
||||
addressPrefix: params.get("addressPrefix") || "",
|
||||
registryName: decodeURIComponent(params.get("registryName") || ""),
|
||||
addressPrefix: decodeURIComponent(params.get("addressPrefix") || ""),
|
||||
explorerLink: decodeURIComponent(params.get("explorerLink") || ""),
|
||||
};
|
||||
|
||||
|
||||
+27
-14
@@ -1,7 +1,7 @@
|
||||
import { fromBase64, fromBech32, toBase64, toBech32 } from "@cosmjs/encoding";
|
||||
import { sha512 } from "@cosmjs/crypto";
|
||||
import { Decimal } from "@cosmjs/math";
|
||||
import { Coin } from "@cosmjs/amino";
|
||||
import { sha512 } from "@cosmjs/crypto";
|
||||
import { fromBase64, fromBech32, toBase64, toBech32 } from "@cosmjs/encoding";
|
||||
import { Decimal } from "@cosmjs/math";
|
||||
import { ChainInfo } from "../types";
|
||||
|
||||
/**
|
||||
@@ -69,34 +69,46 @@ const printableCoins = (coins: Coin[], chainInfo: ChainInfo) => {
|
||||
/**
|
||||
* Generates an example address for the configured blockchain.
|
||||
*
|
||||
* `index` can be set to a small integer in order to get different addresses. Defaults to 0.
|
||||
* `index` can be set to a small integer in order to get different addresses.
|
||||
*/
|
||||
const exampleAddress = (index: number, chainAddressPrefix: string) => {
|
||||
const usedIndex = index || 0;
|
||||
function exampleAddress(index: number, chainAddressPrefix: string): string {
|
||||
let data = fromBech32("cosmos1vqpjljwsynsn58dugz0w8ut7kun7t8ls2qkmsq").data;
|
||||
for (let i = 0; i < usedIndex; ++i) {
|
||||
for (let i = 0; i < index; ++i) {
|
||||
data = sha512(data).slice(0, data.length); // hash one time and trim to original length
|
||||
}
|
||||
return toBech32(chainAddressPrefix, data);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an example address for the configured blockchain.
|
||||
*
|
||||
* `index` can be set to a small integer in order to get different addresses.
|
||||
*/
|
||||
function exampleValidatorAddress(index: number, chainAddressPrefix: string): string {
|
||||
let data = fromBech32("cosmosvaloper10v6wvdenee8r9l6wlsphcgur2ltl8ztkfrvj9a").data;
|
||||
for (let i = 0; i < index; ++i) {
|
||||
data = sha512(data).slice(0, data.length); // hash one time and trim to original length
|
||||
}
|
||||
const validatorPrefix = chainAddressPrefix + "valoper";
|
||||
return toBech32(validatorPrefix, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an example pubkey (secp256k1, compressed).
|
||||
*
|
||||
* `index` can be set to a small integer in order to get different addresses. Defaults to 0.
|
||||
* `index` can be set to a small integer in order to get different addresses.
|
||||
*
|
||||
* Note: the keys are not necessarily valid (as in points on the chain) and should only be used
|
||||
* as dummy data.
|
||||
*/
|
||||
const examplePubkey = (index: number) => {
|
||||
const usedIndex = index || 0;
|
||||
function examplePubkey(index: number): string {
|
||||
let data = fromBase64("Akd/qKMWdZXyiMnSu6aFLpQEGDO0ijyal9mXUIcVaPNX");
|
||||
for (let i = 0; i < usedIndex; ++i) {
|
||||
for (let i = 0; i < index; ++i) {
|
||||
data = sha512(data).slice(0, data.length); // hash one time and trim to original length
|
||||
data[0] = index % 2 ? 0x02 : 0x03; // pubkeys have to start with 0x02 or 0x03
|
||||
}
|
||||
return toBase64(data);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an error message for invalid addresses.
|
||||
@@ -115,7 +127,7 @@ const checkAddress = (input: string, chainAddressPrefix: string) => {
|
||||
return error.toString();
|
||||
}
|
||||
|
||||
if (prefix !== chainAddressPrefix) {
|
||||
if (!prefix.startsWith(chainAddressPrefix)) {
|
||||
return `Expected address prefix '${chainAddressPrefix}' but got '${prefix}'`;
|
||||
}
|
||||
|
||||
@@ -166,6 +178,7 @@ export {
|
||||
printableCoin,
|
||||
printableCoins,
|
||||
exampleAddress,
|
||||
exampleValidatorAddress,
|
||||
examplePubkey,
|
||||
checkAddress,
|
||||
explorerLinkTx,
|
||||
|
||||
@@ -3,7 +3,9 @@ import { DbAccount, DbSignature, DbTransaction } from "../types";
|
||||
|
||||
// Graphql base request for Faunadb
|
||||
const graphqlReq = axios.create({
|
||||
baseURL: "https://graphql.fauna.com/graphql",
|
||||
// The fallback URL works for classic databases. See https://docs.fauna.com/fauna/current/learn/understanding/region_groups
|
||||
// for more information about regions.
|
||||
baseURL: process.env.FAUNADB_URL || "https://graphql.fauna.com/graphql",
|
||||
headers: {
|
||||
Authorization: `Bearer ${process.env.FAUNADB_SECRET}`,
|
||||
},
|
||||
|
||||
Generated
+4381
-2749
File diff suppressed because it is too large
Load Diff
+28
-31
@@ -8,38 +8,35 @@
|
||||
"lint:fix": "eslint --max-warnings 0 \"./**/*.{js,jsx,ts,tsx}\" --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cosmjs/amino": "^0.28.4",
|
||||
"@cosmjs/crypto": "^0.28.4",
|
||||
"@cosmjs/encoding": "^0.28.4",
|
||||
"@cosmjs/ledger-amino": "^0.28.4",
|
||||
"@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",
|
||||
"copy-to-clipboard": "^3.3.1",
|
||||
"faunadb": "^4.1.1",
|
||||
"next": "^12.1.6",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-select": "^5.2.2",
|
||||
"uuid": "^8.3.0"
|
||||
"@cosmjs/amino": "^0.30.1",
|
||||
"@cosmjs/crypto": "^0.30.1",
|
||||
"@cosmjs/encoding": "^0.30.1",
|
||||
"@cosmjs/ledger-amino": "^0.30.1",
|
||||
"@cosmjs/math": "^0.30.1",
|
||||
"@cosmjs/proto-signing": "^0.30.1",
|
||||
"@cosmjs/stargate": "^0.30.1",
|
||||
"@cosmjs/utils": "^0.30.1",
|
||||
"@keplr-wallet/types": "^0.11.56",
|
||||
"@ledgerhq/hw-transport-webusb": "^6.27.13",
|
||||
"axios": "^1.3.5",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"faunadb": "^4.8.0",
|
||||
"next": "^13.3.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-select": "^5.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^17.0.35",
|
||||
"@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",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-react": "^7.28.0",
|
||||
"prettier": "^2.5.1",
|
||||
"typescript": "^4.7.2"
|
||||
"@types/node": "^18.15.11",
|
||||
"@types/react": "^18.0.34",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "^5.58.0",
|
||||
"@typescript-eslint/parser": "^5.58.0",
|
||||
"cosmjs-types": "^0.7.2",
|
||||
"eslint": "^8.38.0",
|
||||
"eslint-config-next": "^13.3.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"prettier": "^2.8.7",
|
||||
"typescript": "^5.0.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { getMultisig } from "../../../../../../lib/graphqlHelpers";
|
||||
|
||||
export default async function (req: NextApiRequest, res: NextApiResponse) {
|
||||
export default async function multisigAddressApi(req: NextApiRequest, res: NextApiResponse) {
|
||||
switch (req.method) {
|
||||
case "GET":
|
||||
try {
|
||||
const multisigAddress = req.query.multisigAddress.toString();
|
||||
const chainId = req.query.chainId.toString();
|
||||
const multisigAddress = req.query.multisigAddress?.toString() || "";
|
||||
const chainId = req.query.chainId?.toString() || "";
|
||||
console.log("Function `getMultisig` invoked", multisigAddress, chainId);
|
||||
const getRes = await getMultisig(multisigAddress, chainId);
|
||||
if (!getRes.data.data.getMultisig) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { createMultisig } from "../../../../../lib/graphqlHelpers";
|
||||
|
||||
export default async function (req: NextApiRequest, res: NextApiResponse) {
|
||||
export default async function multisigApi(req: NextApiRequest, res: NextApiResponse) {
|
||||
switch (req.method) {
|
||||
case "POST":
|
||||
try {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { updateTxHash } from "../../../../lib/graphqlHelpers";
|
||||
|
||||
export default async function (req: NextApiRequest, res: NextApiResponse) {
|
||||
export default async function transactionIDApi(req: NextApiRequest, res: NextApiResponse) {
|
||||
switch (req.method) {
|
||||
case "POST":
|
||||
try {
|
||||
const transactionID = req.query.transactionID.toString();
|
||||
const transactionID = req.query.transactionID?.toString() || "";
|
||||
const { txHash } = req.body;
|
||||
console.log("Function `updateTransaction` invoked", txHash);
|
||||
const saveRes = await updateTxHash(transactionID, txHash);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { createSignature } from "../../../../lib/graphqlHelpers";
|
||||
|
||||
export default async function (req: NextApiRequest, res: NextApiResponse) {
|
||||
export default async function transactionIDApi(req: NextApiRequest, res: NextApiResponse) {
|
||||
switch (req.method) {
|
||||
case "POST":
|
||||
try {
|
||||
const transactionID = req.query.transactionID.toString();
|
||||
const transactionID = req.query.transactionID?.toString() || "";
|
||||
const data = req.body;
|
||||
console.log("Function `createSignature` invoked", data);
|
||||
const saveRes = await createSignature(data, transactionID);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createTransaction } from "../../../lib/graphqlHelpers";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { createTransaction } from "../../../lib/graphqlHelpers";
|
||||
|
||||
export default async function (req: NextApiRequest, res: NextApiResponse) {
|
||||
export default async function transactionApi(req: NextApiRequest, res: NextApiResponse) {
|
||||
switch (req.method) {
|
||||
case "POST":
|
||||
try {
|
||||
|
||||
@@ -1,36 +1,33 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { pubkeyToAddress, Pubkey, MultisigThresholdPubkey } from "@cosmjs/amino";
|
||||
import { MultisigThresholdPubkey, SinglePubkey } from "@cosmjs/amino";
|
||||
import { Account, 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 { useCallback, useEffect, useState } from "react";
|
||||
import HashView from "../../../components/dataViews/HashView";
|
||||
import MultisigHoldings from "../../../components/dataViews/MultisigHoldings";
|
||||
import MultisigMembers from "../../../components/dataViews/MultisigMembers";
|
||||
import Page from "../../../components/layout/Page";
|
||||
import StackableContainer from "../../../components/layout/StackableContainer";
|
||||
import TransactionForm from "../../../components/forms/TransactionForm";
|
||||
import UnDelegationForm from "../../../components/forms/UnDelegationForm";
|
||||
import DelegationForm from "../../../components/forms/DelegationForm";
|
||||
import ReDelegationForm from "../../../components/forms/ReDelegationForm";
|
||||
import RewardsForm from "../../../components/forms/RewardsForm";
|
||||
import TransactionForm from "../../../components/forms/TransactionForm";
|
||||
import UnDelegationForm from "../../../components/forms/UnDelegationForm";
|
||||
import Button from "../../../components/inputs/Button";
|
||||
import Page from "../../../components/layout/Page";
|
||||
import StackableContainer from "../../../components/layout/StackableContainer";
|
||||
import { useAppContext } from "../../../context/AppContext";
|
||||
import { getMultisigAccount } from "../../../lib/multisigHelpers";
|
||||
|
||||
function participantPubkeysFromMultisig(multisigPubkey: Pubkey) {
|
||||
return multisigPubkey.value.pubkeys;
|
||||
function participantPubkeysFromMultisig(
|
||||
multisig: MultisigThresholdPubkey,
|
||||
): readonly SinglePubkey[] {
|
||||
return multisig.value.pubkeys;
|
||||
}
|
||||
|
||||
function participantAddressesFromMultisig(multisigPubkey: Pubkey, addressPrefix: string) {
|
||||
return participantPubkeysFromMultisig(multisigPubkey).map((p: Pubkey) =>
|
||||
pubkeyToAddress(p, addressPrefix),
|
||||
);
|
||||
}
|
||||
|
||||
const multipage = () => {
|
||||
const Multipage = () => {
|
||||
const { state } = useAppContext();
|
||||
const [showSendTxForm, setShowSendTxForm] = useState(false);
|
||||
const [showDelegateTxForm, setShowDelegateTxForm] = useState(false);
|
||||
const [showUnDelegateTxForm, setShowUnDelegateTxForm] = useState(false);
|
||||
const [showReDelegateTxForm, setShowReDelegateTxForm] = useState(false);
|
||||
const [showRewardsTxForm, setShowRewardsTxForm] = useState(false);
|
||||
@@ -41,31 +38,34 @@ const multipage = () => {
|
||||
const [accountError, setAccountError] = useState(null);
|
||||
const router = useRouter();
|
||||
|
||||
const fetchMultisig = useCallback(
|
||||
async (address: string) => {
|
||||
setAccountError(null);
|
||||
try {
|
||||
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);
|
||||
setHoldings(tempHoldings);
|
||||
const result = await getMultisigAccount(address, client);
|
||||
setPubkey(result[0]);
|
||||
setAccountOnChain(result[1]);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (error: any) {
|
||||
setAccountError(error.message);
|
||||
console.log("Account error:", error);
|
||||
}
|
||||
},
|
||||
[state.chain.denom, state.chain.nodeAddress],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const address = router.query.address?.toString();
|
||||
if (address) {
|
||||
setMultisigAddress(address);
|
||||
fetchMultisig(address);
|
||||
}
|
||||
}, [state, router.query.address]);
|
||||
|
||||
const fetchMultisig = async (address: string) => {
|
||||
setAccountError(null);
|
||||
try {
|
||||
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);
|
||||
setHoldings(tempHoldings);
|
||||
const result = await getMultisigAccount(address, client);
|
||||
setPubkey(result[0]);
|
||||
setAccountOnChain(result[1]);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (error: any) {
|
||||
setAccountError(error.message);
|
||||
console.log("Account error:", error);
|
||||
}
|
||||
};
|
||||
}, [fetchMultisig, router.query.address]);
|
||||
|
||||
assert(state.chain.addressPrefix, "address prefix missing");
|
||||
|
||||
@@ -84,7 +84,8 @@ const multipage = () => {
|
||||
</StackableContainer>
|
||||
{pubkey && (
|
||||
<MultisigMembers
|
||||
members={participantAddressesFromMultisig(pubkey, state.chain.addressPrefix)}
|
||||
members={participantPubkeysFromMultisig(pubkey)}
|
||||
addressPrefix={state.chain.addressPrefix}
|
||||
threshold={pubkey.value.threshold}
|
||||
/>
|
||||
)}
|
||||
@@ -112,6 +113,15 @@ const multipage = () => {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{showDelegateTxForm && (
|
||||
<DelegationForm
|
||||
address={multisigAddress}
|
||||
accountOnChain={accountOnChain}
|
||||
closeForm={() => {
|
||||
setShowDelegateTxForm(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{showUnDelegateTxForm && (
|
||||
<UnDelegationForm
|
||||
address={multisigAddress}
|
||||
@@ -139,46 +149,56 @@ const multipage = () => {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{!showSendTxForm && !showUnDelegateTxForm && !showRewardsTxForm && !showReDelegateTxForm && (
|
||||
<div className="interfaces">
|
||||
<div className="col-1">
|
||||
<MultisigHoldings holdings={holdings} />
|
||||
{!showSendTxForm &&
|
||||
!showDelegateTxForm &&
|
||||
!showUnDelegateTxForm &&
|
||||
!showRewardsTxForm &&
|
||||
!showReDelegateTxForm && (
|
||||
<div className="interfaces">
|
||||
<div className="col-1">
|
||||
<MultisigHoldings holdings={holdings} />
|
||||
</div>
|
||||
<div className="col-2">
|
||||
<StackableContainer lessPadding>
|
||||
<h2>New transaction</h2>
|
||||
<p>
|
||||
Once a transaction is created, it can be signed by the multisig members, and
|
||||
then broadcast.
|
||||
</p>
|
||||
<Button
|
||||
label="Create Transaction"
|
||||
onClick={() => {
|
||||
setShowSendTxForm(true);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
label="Create Delegation"
|
||||
onClick={() => {
|
||||
setShowDelegateTxForm(true);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
label="Create UnDelegation"
|
||||
onClick={() => {
|
||||
setShowUnDelegateTxForm(true);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
label="Create Redelegate"
|
||||
onClick={() => {
|
||||
setShowReDelegateTxForm(true);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
label="Claim Rewards"
|
||||
onClick={() => {
|
||||
setShowRewardsTxForm(true);
|
||||
}}
|
||||
/>
|
||||
</StackableContainer>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-2">
|
||||
<StackableContainer lessPadding>
|
||||
<h2>New transaction</h2>
|
||||
<p>
|
||||
Once a transaction is created, it can be signed by the multisig members, and then
|
||||
broadcast.
|
||||
</p>
|
||||
<Button
|
||||
label="Create Transaction"
|
||||
onClick={() => {
|
||||
setShowSendTxForm(true);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
label="Create UnDelegation"
|
||||
onClick={() => {
|
||||
setShowUnDelegateTxForm(true);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
label="Claim Rewards"
|
||||
onClick={() => {
|
||||
setShowRewardsTxForm(true);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
label="Create Redelegate"
|
||||
onClick={() => {
|
||||
setShowReDelegateTxForm(true);
|
||||
}}
|
||||
/>
|
||||
</StackableContainer>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</StackableContainer>
|
||||
<style jsx>{`
|
||||
.interfaces {
|
||||
@@ -217,4 +237,4 @@ const multipage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default multipage;
|
||||
export default Multipage;
|
||||
|
||||
@@ -1,25 +1,22 @@
|
||||
import axios from "axios";
|
||||
import React from "react";
|
||||
import { GetServerSideProps } from "next";
|
||||
import { StargateClient, makeMultisignedTx, Account } from "@cosmjs/stargate";
|
||||
import { TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { fromBase64 } from "@cosmjs/encoding";
|
||||
import { MultisigThresholdPubkey } from "@cosmjs/amino";
|
||||
|
||||
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 Page from "../../../../components/layout/Page";
|
||||
import StackableContainer from "../../../../components/layout/StackableContainer";
|
||||
import { fromBase64 } from "@cosmjs/encoding";
|
||||
import { Account, StargateClient, makeMultisignedTxBytes } from "@cosmjs/stargate";
|
||||
import { assert } from "@cosmjs/utils";
|
||||
import axios from "axios";
|
||||
import { GetServerSideProps } from "next";
|
||||
import { useRouter } from "next/router";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import CompletedTransaction from "../../../../components/dataViews/CompletedTransaction";
|
||||
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";
|
||||
import Button from "../../../../components/inputs/Button";
|
||||
import Page from "../../../../components/layout/Page";
|
||||
import StackableContainer from "../../../../components/layout/StackableContainer";
|
||||
import { useAppContext } from "../../../../context/AppContext";
|
||||
import { findTransactionByID } from "../../../../lib/graphqlHelpers";
|
||||
import { getMultisigAccount } from "../../../../lib/multisigHelpers";
|
||||
import { DbSignature, DbTransaction } from "../../../../types";
|
||||
|
||||
interface Props {
|
||||
props: {
|
||||
@@ -57,7 +54,7 @@ export const getServerSideProps: GetServerSideProps = async (context): Promise<P
|
||||
};
|
||||
};
|
||||
|
||||
const transactionPage = ({
|
||||
const TransactionPage = ({
|
||||
multisigAddress,
|
||||
transactionJSON,
|
||||
transactionID,
|
||||
@@ -85,26 +82,29 @@ const transactionPage = ({
|
||||
setCurrentSignatures((prevState: DbSignature[]) => [...prevState, signature]);
|
||||
};
|
||||
|
||||
const fetchMultisig = useCallback(
|
||||
async (address: string) => {
|
||||
try {
|
||||
assert(state.chain.nodeAddress, "Node address missing");
|
||||
const client = await StargateClient.connect(state.chain.nodeAddress);
|
||||
const result = await getMultisigAccount(address, client);
|
||||
setPubkey(result[0]);
|
||||
setAccountOnChain(result[1]);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (error: any) {
|
||||
setAccountError(error.toString());
|
||||
console.log("Account error:", error);
|
||||
}
|
||||
},
|
||||
[state.chain.nodeAddress],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const address = router.query.address?.toString();
|
||||
if (address) {
|
||||
fetchMultisig(address);
|
||||
}
|
||||
}, [state, router.query.address]);
|
||||
|
||||
const fetchMultisig = async (address: string) => {
|
||||
try {
|
||||
assert(state.chain.nodeAddress, "Node address missing");
|
||||
const client = await StargateClient.connect(state.chain.nodeAddress);
|
||||
const result = await getMultisigAccount(address, client);
|
||||
setPubkey(result[0]);
|
||||
setAccountOnChain(result[1]);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (error: any) {
|
||||
setAccountError(error.toString());
|
||||
console.log("Account error:", error);
|
||||
}
|
||||
};
|
||||
}, [fetchMultisig, router.query.address]);
|
||||
|
||||
const broadcastTx = async () => {
|
||||
try {
|
||||
@@ -114,7 +114,7 @@ const transactionPage = ({
|
||||
assert(accountOnChain, "Account on chain value missing.");
|
||||
assert(pubkey, "Pubkey not found on chain or in database");
|
||||
const bodyBytes = fromBase64(currentSignatures[0].bodyBytes);
|
||||
const signedTx = makeMultisignedTx(
|
||||
const signedTxBytes = makeMultisignedTxBytes(
|
||||
pubkey,
|
||||
txInfo.sequence,
|
||||
txInfo.fee,
|
||||
@@ -123,9 +123,7 @@ const transactionPage = ({
|
||||
);
|
||||
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()),
|
||||
);
|
||||
const result = await broadcaster.broadcastTx(signedTxBytes);
|
||||
console.log(result);
|
||||
const _res = await axios.post(`/api/transaction/${transactionID}`, {
|
||||
txHash: result.transactionHash,
|
||||
@@ -200,4 +198,4 @@ const transactionPage = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default transactionPage;
|
||||
export default TransactionPage;
|
||||
|
||||
@@ -4,6 +4,9 @@ import { EncodeObject } from "@cosmjs/proto-signing";
|
||||
declare global {
|
||||
interface Window {
|
||||
keplr: {
|
||||
defaultOptions: {
|
||||
sign: { preferNoSetFee: boolean; preferNoSetMemo: boolean; disableBalanceCheck: boolean };
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
enable: (chainId: string) => any;
|
||||
getKey: (chainId: string) => Promise<WalletAccount>;
|
||||
|
||||
Reference in New Issue
Block a user