@@ -0,0 +1,92 @@
|
||||
import { fromUtf8 } from "@cosmjs/encoding";
|
||||
import { MsgExecuteContract } from "cosmjs-types/cosmwasm/wasm/v1/tx";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useState } from "react";
|
||||
import { JSONValue } from "vanilla-jsoneditor";
|
||||
import { useChains } from "../../../context/ChainsContext";
|
||||
import { printableCoins } from "../../../lib/displayHelpers";
|
||||
import HashView from "../HashView";
|
||||
|
||||
const JsonEditor = dynamic(() => import("../../inputs/JsonEditor"), { ssr: false });
|
||||
|
||||
interface TxMsgExecuteContractDetailsProps {
|
||||
readonly msgValue: MsgExecuteContract;
|
||||
}
|
||||
|
||||
const TxMsgExecuteContractDetails = ({ msgValue }: TxMsgExecuteContractDetailsProps) => {
|
||||
const { chain } = useChains();
|
||||
const [parseError, setParseError] = useState("");
|
||||
|
||||
const json: JSONValue = (() => {
|
||||
if (parseError) {
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(fromUtf8(msgValue.msg));
|
||||
} catch (e) {
|
||||
setParseError(e instanceof Error ? e.message : "Failed to decode UTF-8 msg");
|
||||
return {};
|
||||
}
|
||||
})();
|
||||
|
||||
return (
|
||||
<>
|
||||
<li>
|
||||
<h3>MsgExecuteContract</h3>
|
||||
</li>
|
||||
<li>
|
||||
<label>Contract:</label>
|
||||
<div title={msgValue.contract}>
|
||||
<HashView hash={msgValue.contract} />
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<label>Funds:</label>
|
||||
<div>{printableCoins(msgValue.funds, chain)}</div>
|
||||
</li>
|
||||
{parseError ? (
|
||||
<li className="parse-error">
|
||||
<p>{parseError}</p>
|
||||
</li>
|
||||
) : (
|
||||
<li>
|
||||
<JsonEditor readOnly content={{ json }} />
|
||||
</li>
|
||||
)}
|
||||
<style jsx>{`
|
||||
li:not(:has(h3)) {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
padding: 6px 10px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
li + li:nth-child(2) {
|
||||
margin-top: 25px;
|
||||
}
|
||||
li + li {
|
||||
margin-top: 10px;
|
||||
}
|
||||
li div {
|
||||
padding: 3px 6px;
|
||||
}
|
||||
label {
|
||||
font-size: 12px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 3px 6px;
|
||||
border-radius: 5px;
|
||||
display: block;
|
||||
}
|
||||
.parse-error p {
|
||||
max-width: 550px;
|
||||
color: red;
|
||||
font-size: 16px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
`}</style>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TxMsgExecuteContractDetails;
|
||||
@@ -0,0 +1,108 @@
|
||||
import { fromUtf8, toHex } from "@cosmjs/encoding";
|
||||
import { MsgInstantiateContract2 } from "cosmjs-types/cosmwasm/wasm/v1/tx";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useState } from "react";
|
||||
import { JSONValue } from "vanilla-jsoneditor";
|
||||
import { useChains } from "../../../context/ChainsContext";
|
||||
import { printableCoins } from "../../../lib/displayHelpers";
|
||||
import HashView from "../HashView";
|
||||
|
||||
const JsonEditor = dynamic(() => import("../../inputs/JsonEditor"), { ssr: false });
|
||||
|
||||
interface TxMsgInstantiateContract2DetailsProps {
|
||||
readonly msgValue: MsgInstantiateContract2;
|
||||
}
|
||||
|
||||
const TxMsgInstantiateContract2Details = ({ msgValue }: TxMsgInstantiateContract2DetailsProps) => {
|
||||
const { chain } = useChains();
|
||||
const [parseError, setParseError] = useState("");
|
||||
|
||||
const json: JSONValue = (() => {
|
||||
if (parseError) {
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(fromUtf8(msgValue.msg));
|
||||
} catch (e) {
|
||||
setParseError(e instanceof Error ? e.message : "Failed to decode UTF-8 msg");
|
||||
return {};
|
||||
}
|
||||
})();
|
||||
|
||||
return (
|
||||
<>
|
||||
<li>
|
||||
<h3>MsgInstantiateContract2</h3>
|
||||
</li>
|
||||
<li>
|
||||
<label>Code ID:</label>
|
||||
<div>{msgValue.codeId.toString()}</div>
|
||||
</li>
|
||||
<li>
|
||||
<label>Label:</label>
|
||||
<div>{msgValue.label || "None"}</div>
|
||||
</li>
|
||||
<li>
|
||||
<label>Admin:</label>
|
||||
{msgValue.admin ? (
|
||||
<div title={msgValue.admin}>
|
||||
<HashView hash={msgValue.admin} />
|
||||
</div>
|
||||
) : (
|
||||
<div>None</div>
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
<label>Salt:</label>
|
||||
<div>{toHex(msgValue.salt)}</div>
|
||||
</li>
|
||||
<li>
|
||||
<label>Funds:</label>
|
||||
<div>{printableCoins(msgValue.funds, chain)}</div>
|
||||
</li>
|
||||
{parseError ? (
|
||||
<li className="parse-error">
|
||||
<p>{parseError}</p>
|
||||
</li>
|
||||
) : (
|
||||
<li>
|
||||
<JsonEditor readOnly content={{ json }} />
|
||||
</li>
|
||||
)}
|
||||
<style jsx>{`
|
||||
li:not(:has(h3)) {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
padding: 6px 10px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
li + li:nth-child(2) {
|
||||
margin-top: 25px;
|
||||
}
|
||||
li + li {
|
||||
margin-top: 10px;
|
||||
}
|
||||
li div {
|
||||
padding: 3px 6px;
|
||||
}
|
||||
label {
|
||||
font-size: 12px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 3px 6px;
|
||||
border-radius: 5px;
|
||||
display: block;
|
||||
}
|
||||
.parse-error p {
|
||||
max-width: 550px;
|
||||
color: red;
|
||||
font-size: 16px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
`}</style>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TxMsgInstantiateContract2Details;
|
||||
@@ -0,0 +1,104 @@
|
||||
import { fromUtf8 } from "@cosmjs/encoding";
|
||||
import { MsgInstantiateContract } from "cosmjs-types/cosmwasm/wasm/v1/tx";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useState } from "react";
|
||||
import { JSONValue } from "vanilla-jsoneditor";
|
||||
import { useChains } from "../../../context/ChainsContext";
|
||||
import { printableCoins } from "../../../lib/displayHelpers";
|
||||
import HashView from "../HashView";
|
||||
|
||||
const JsonEditor = dynamic(() => import("../../inputs/JsonEditor"), { ssr: false });
|
||||
|
||||
interface TxMsgInstantiateContractDetailsProps {
|
||||
readonly msgValue: MsgInstantiateContract;
|
||||
}
|
||||
|
||||
const TxMsgInstantiateContractDetails = ({ msgValue }: TxMsgInstantiateContractDetailsProps) => {
|
||||
const { chain } = useChains();
|
||||
const [parseError, setParseError] = useState("");
|
||||
|
||||
const json: JSONValue = (() => {
|
||||
if (parseError) {
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(fromUtf8(msgValue.msg));
|
||||
} catch (e) {
|
||||
setParseError(e instanceof Error ? e.message : "Failed to decode UTF-8 msg");
|
||||
return {};
|
||||
}
|
||||
})();
|
||||
|
||||
return (
|
||||
<>
|
||||
<li>
|
||||
<h3>MsgInstantiateContract</h3>
|
||||
</li>
|
||||
<li>
|
||||
<label>Code ID:</label>
|
||||
<div>{msgValue.codeId.toString()}</div>
|
||||
</li>
|
||||
<li>
|
||||
<label>Label:</label>
|
||||
<div>{msgValue.label || "None"}</div>
|
||||
</li>
|
||||
<li>
|
||||
<label>Admin:</label>
|
||||
{msgValue.admin ? (
|
||||
<div title={msgValue.admin}>
|
||||
<HashView hash={msgValue.admin} />
|
||||
</div>
|
||||
) : (
|
||||
<div>None</div>
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
<label>Funds:</label>
|
||||
<div>{printableCoins(msgValue.funds, chain)}</div>
|
||||
</li>
|
||||
{parseError ? (
|
||||
<li className="parse-error">
|
||||
<p>{parseError}</p>
|
||||
</li>
|
||||
) : (
|
||||
<li>
|
||||
<JsonEditor readOnly content={{ json }} />
|
||||
</li>
|
||||
)}
|
||||
<style jsx>{`
|
||||
li:not(:has(h3)) {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
padding: 6px 10px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
li + li:nth-child(2) {
|
||||
margin-top: 25px;
|
||||
}
|
||||
li + li {
|
||||
margin-top: 10px;
|
||||
}
|
||||
li div {
|
||||
padding: 3px 6px;
|
||||
}
|
||||
label {
|
||||
font-size: 12px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 3px 6px;
|
||||
border-radius: 5px;
|
||||
display: block;
|
||||
}
|
||||
.parse-error p {
|
||||
max-width: 550px;
|
||||
color: red;
|
||||
font-size: 16px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
`}</style>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TxMsgInstantiateContractDetails;
|
||||
@@ -0,0 +1,89 @@
|
||||
import { fromUtf8 } from "@cosmjs/encoding";
|
||||
import { MsgMigrateContract } from "cosmjs-types/cosmwasm/wasm/v1/tx";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useState } from "react";
|
||||
import { JSONValue } from "vanilla-jsoneditor";
|
||||
import HashView from "../HashView";
|
||||
|
||||
const JsonEditor = dynamic(() => import("../../inputs/JsonEditor"), { ssr: false });
|
||||
|
||||
interface TxMsgMigrateContractDetailsProps {
|
||||
readonly msgValue: MsgMigrateContract;
|
||||
}
|
||||
|
||||
const TxMsgMigrateContractDetails = ({ msgValue }: TxMsgMigrateContractDetailsProps) => {
|
||||
const [parseError, setParseError] = useState("");
|
||||
|
||||
const json: JSONValue = (() => {
|
||||
if (parseError) {
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(fromUtf8(msgValue.msg));
|
||||
} catch (e) {
|
||||
setParseError(e instanceof Error ? e.message : "Failed to decode UTF-8 msg");
|
||||
return {};
|
||||
}
|
||||
})();
|
||||
|
||||
return (
|
||||
<>
|
||||
<li>
|
||||
<h3>MsgMigrateContract</h3>
|
||||
</li>
|
||||
<li>
|
||||
<label>Contract:</label>
|
||||
<div title={msgValue.contract}>
|
||||
<HashView hash={msgValue.contract} />
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<label>Code ID:</label>
|
||||
<div>{msgValue.codeId.toString()}</div>
|
||||
</li>
|
||||
{parseError ? (
|
||||
<li className="parse-error">
|
||||
<p>{parseError}</p>
|
||||
</li>
|
||||
) : (
|
||||
<li>
|
||||
<JsonEditor readOnly content={{ json }} />
|
||||
</li>
|
||||
)}
|
||||
<style jsx>{`
|
||||
li:not(:has(h3)) {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
padding: 6px 10px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
li + li:nth-child(2) {
|
||||
margin-top: 25px;
|
||||
}
|
||||
li + li {
|
||||
margin-top: 10px;
|
||||
}
|
||||
li div {
|
||||
padding: 3px 6px;
|
||||
}
|
||||
label {
|
||||
font-size: 12px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 3px 6px;
|
||||
border-radius: 5px;
|
||||
display: block;
|
||||
}
|
||||
.parse-error p {
|
||||
max-width: 550px;
|
||||
color: red;
|
||||
font-size: 16px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
`}</style>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TxMsgMigrateContractDetails;
|
||||
@@ -7,6 +7,10 @@ import StackableContainer from "../../layout/StackableContainer";
|
||||
import TxMsgClaimRewardsDetails from "./TxMsgClaimRewardsDetails";
|
||||
import TxMsgCreateVestingAccountDetails from "./TxMsgCreateVestingAccountDetails";
|
||||
import TxMsgDelegateDetails from "./TxMsgDelegateDetails";
|
||||
import TxMsgExecuteContractDetails from "./TxMsgExecuteContractDetails";
|
||||
import TxMsgInstantiateContract2Details from "./TxMsgInstantiateContract2Details";
|
||||
import TxMsgInstantiateContractDetails from "./TxMsgInstantiateContractDetails";
|
||||
import TxMsgMigrateContractDetails from "./TxMsgMigrateContractDetails";
|
||||
import TxMsgRedelegateDetails from "./TxMsgRedelegateDetails";
|
||||
import TxMsgSendDetails from "./TxMsgSendDetails";
|
||||
import TxMsgSetWithdrawAddressDetails from "./TxMsgSetWithdrawAddressDetails";
|
||||
@@ -31,6 +35,14 @@ const TxMsgDetails = ({ typeUrl, value: msgValue }: EncodeObject) => {
|
||||
return <TxMsgCreateVestingAccountDetails msgValue={msgValue} />;
|
||||
case MsgTypeUrls.Transfer:
|
||||
return <TxMsgTransferDetails msgValue={msgValue} />;
|
||||
case MsgTypeUrls.Execute:
|
||||
return <TxMsgExecuteContractDetails msgValue={msgValue} />;
|
||||
case MsgTypeUrls.Instantiate:
|
||||
return <TxMsgInstantiateContractDetails msgValue={msgValue} />;
|
||||
case MsgTypeUrls.Instantiate2:
|
||||
return <TxMsgInstantiateContract2Details msgValue={msgValue} />;
|
||||
case MsgTypeUrls.Migrate:
|
||||
return <TxMsgMigrateContractDetails msgValue={msgValue} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
import { MsgExecuteContractEncodeObject } from "@cosmjs/cosmwasm-stargate";
|
||||
import { toUtf8 } from "@cosmjs/encoding";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { MsgGetter } from "..";
|
||||
import { useChains } from "../../../../context/ChainsContext";
|
||||
import { ChainInfo } from "../../../../context/ChainsContext/types";
|
||||
import { macroCoinToMicroCoin } from "../../../../lib/coinHelpers";
|
||||
import { checkAddress, exampleAddress } from "../../../../lib/displayHelpers";
|
||||
import { MsgCodecs, MsgTypeUrls } from "../../../../types/txMsg";
|
||||
import Input from "../../../inputs/Input";
|
||||
import Select from "../../../inputs/Select";
|
||||
import StackableContainer from "../../../layout/StackableContainer";
|
||||
|
||||
const JsonEditor = dynamic(() => import("../../../inputs/JsonEditor"), { ssr: false });
|
||||
|
||||
const customDenomOption = { label: "Custom (enter denom below)", value: "custom" } as const;
|
||||
|
||||
const getDenomOptions = (assets: ChainInfo["assets"]) => {
|
||||
if (!assets?.length) {
|
||||
return [customDenomOption];
|
||||
}
|
||||
|
||||
return [...assets.map((asset) => ({ label: asset.symbol, value: asset })), customDenomOption];
|
||||
};
|
||||
|
||||
interface MsgExecuteContractFormProps {
|
||||
readonly fromAddress: string;
|
||||
readonly setMsgGetter: (msgGetter: MsgGetter) => void;
|
||||
readonly deleteMsg: () => void;
|
||||
}
|
||||
|
||||
const MsgExecuteContractForm = ({
|
||||
fromAddress,
|
||||
setMsgGetter,
|
||||
deleteMsg,
|
||||
}: MsgExecuteContractFormProps) => {
|
||||
const { chain } = useChains();
|
||||
|
||||
const denomOptions = getDenomOptions(chain.assets);
|
||||
|
||||
const [contractAddress, setContractAddress] = useState("");
|
||||
const [msgContent, setMsgContent] = useState("{}");
|
||||
const [selectedDenom, setSelectedDenom] = useState(denomOptions[0]);
|
||||
const [customDenom, setCustomDenom] = useState("");
|
||||
const [amount, setAmount] = useState("0");
|
||||
|
||||
const jsonError = useRef(false);
|
||||
const [contractAddressError, setContractAddressError] = useState("");
|
||||
const [customDenomError, setCustomDenomError] = useState("");
|
||||
const [amountError, setAmountError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
setContractAddressError("");
|
||||
setCustomDenomError("");
|
||||
setAmountError("");
|
||||
|
||||
const isMsgValid = (): boolean => {
|
||||
if (jsonError.current) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const addressErrorMsg = checkAddress(contractAddress, chain.addressPrefix);
|
||||
if (addressErrorMsg) {
|
||||
setContractAddressError(`Invalid address for network ${chain.chainId}: ${addressErrorMsg}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (selectedDenom.value === customDenomOption.value && !customDenom) {
|
||||
setCustomDenomError("Custom denom must be set because of selection above");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (amount && Number(amount) < 0) {
|
||||
setAmountError("Amount must be empty or a positive number");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (selectedDenom.value === customDenomOption.value && !Number.isInteger(Number(amount))) {
|
||||
setAmountError("Amount cannot be decimal for custom denom");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const denom =
|
||||
selectedDenom.value === customDenomOption.value ? customDenom : selectedDenom.value.symbol;
|
||||
|
||||
const microCoin = (() => {
|
||||
try {
|
||||
return macroCoinToMicroCoin({ denom, amount }, chain.assets);
|
||||
} catch {
|
||||
return { denom, amount: "0" };
|
||||
}
|
||||
})();
|
||||
|
||||
const msgContentUtf8Array = (() => {
|
||||
try {
|
||||
// The JsonEditor does not escape \n or remove whitespaces, so we need to parse + stringify
|
||||
return toUtf8(JSON.stringify(JSON.parse(msgContent)));
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
})();
|
||||
|
||||
const msgValue = MsgCodecs[MsgTypeUrls.Execute].fromPartial({
|
||||
sender: fromAddress,
|
||||
contract: contractAddress,
|
||||
msg: msgContentUtf8Array,
|
||||
funds: [microCoin],
|
||||
});
|
||||
|
||||
const msg: MsgExecuteContractEncodeObject = { typeUrl: MsgTypeUrls.Execute, value: msgValue };
|
||||
|
||||
setMsgGetter({ isMsgValid, msg });
|
||||
}, [
|
||||
amount,
|
||||
chain.addressPrefix,
|
||||
chain.assets,
|
||||
chain.chainId,
|
||||
contractAddress,
|
||||
customDenom,
|
||||
fromAddress,
|
||||
msgContent,
|
||||
selectedDenom.value,
|
||||
setMsgGetter,
|
||||
]);
|
||||
|
||||
return (
|
||||
<StackableContainer lessPadding lessMargin>
|
||||
<button className="remove" onClick={() => deleteMsg()}>
|
||||
✕
|
||||
</button>
|
||||
<h2>MsgExecuteContract</h2>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
label="Contract Address"
|
||||
name="contract-address"
|
||||
value={contractAddress}
|
||||
onChange={({ target }) => setContractAddress(target.value)}
|
||||
error={contractAddressError}
|
||||
placeholder={`E.g. ${exampleAddress(0, chain.addressPrefix)}`}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-item">
|
||||
<JsonEditor
|
||||
label="Msg JSON"
|
||||
content={{ text: msgContent }}
|
||||
onChange={(newMsgContent, _, { contentErrors }) => {
|
||||
setMsgContent("text" in newMsgContent ? newMsgContent.text ?? "{}" : "{}");
|
||||
jsonError.current = !!contentErrors;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-item form-select">
|
||||
<label>Choose a denom:</label>
|
||||
<Select
|
||||
label="Select denom"
|
||||
name="denom-select"
|
||||
options={denomOptions}
|
||||
value={selectedDenom}
|
||||
onChange={(option: (typeof denomOptions)[number]) => {
|
||||
setSelectedDenom(option);
|
||||
if (option.value !== customDenomOption.value) {
|
||||
setCustomDenom("");
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
label="Custom denom"
|
||||
name="custom-denom"
|
||||
value={customDenom}
|
||||
onChange={({ target }) => setCustomDenom(target.value)}
|
||||
placeholder={
|
||||
selectedDenom.value === customDenomOption.value
|
||||
? "Enter custom denom"
|
||||
: "Select Custom denom above"
|
||||
}
|
||||
disabled={selectedDenom.value !== customDenomOption.value}
|
||||
error={customDenomError}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
type="number"
|
||||
label="Amount"
|
||||
name="amount"
|
||||
value={amount}
|
||||
onChange={({ target }) => setAmount(target.value)}
|
||||
error={amountError}
|
||||
/>
|
||||
</div>
|
||||
<style jsx>{`
|
||||
.form-item {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
.form-item label {
|
||||
font-style: italic;
|
||||
font-size: 12px;
|
||||
}
|
||||
.form-select {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.8em;
|
||||
}
|
||||
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 MsgExecuteContractForm;
|
||||
@@ -0,0 +1,301 @@
|
||||
import { MsgInstantiateContract2EncodeObject } from "@cosmjs/cosmwasm-stargate";
|
||||
import { fromHex, toUtf8 } from "@cosmjs/encoding";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { MsgGetter } from "..";
|
||||
import { useChains } from "../../../../context/ChainsContext";
|
||||
import { ChainInfo } from "../../../../context/ChainsContext/types";
|
||||
import { macroCoinToMicroCoin } from "../../../../lib/coinHelpers";
|
||||
import { checkAddress, exampleAddress } from "../../../../lib/displayHelpers";
|
||||
import { MsgCodecs, MsgTypeUrls } from "../../../../types/txMsg";
|
||||
import Input from "../../../inputs/Input";
|
||||
import Select from "../../../inputs/Select";
|
||||
import StackableContainer from "../../../layout/StackableContainer";
|
||||
|
||||
const JsonEditor = dynamic(() => import("../../../inputs/JsonEditor"), { ssr: false });
|
||||
|
||||
const customDenomOption = { label: "Custom (enter denom below)", value: "custom" } as const;
|
||||
|
||||
const getDenomOptions = (assets: ChainInfo["assets"]) => {
|
||||
if (!assets?.length) {
|
||||
return [customDenomOption];
|
||||
}
|
||||
|
||||
return [...assets.map((asset) => ({ label: asset.symbol, value: asset })), customDenomOption];
|
||||
};
|
||||
|
||||
interface MsgInstantiateContract2FormProps {
|
||||
readonly fromAddress: string;
|
||||
readonly setMsgGetter: (msgGetter: MsgGetter) => void;
|
||||
readonly deleteMsg: () => void;
|
||||
}
|
||||
|
||||
const MsgInstantiateContract2Form = ({
|
||||
fromAddress,
|
||||
setMsgGetter,
|
||||
deleteMsg,
|
||||
}: MsgInstantiateContract2FormProps) => {
|
||||
const { chain } = useChains();
|
||||
|
||||
const denomOptions = getDenomOptions(chain.assets);
|
||||
|
||||
const [codeId, setCodeId] = useState("");
|
||||
const [label, setLabel] = useState("");
|
||||
const [adminAddress, setAdminAddress] = useState("");
|
||||
const [salt, setSalt] = useState("");
|
||||
const [msgContent, setMsgContent] = useState("{}");
|
||||
const [selectedDenom, setSelectedDenom] = useState(denomOptions[0]);
|
||||
const [customDenom, setCustomDenom] = useState("");
|
||||
const [amount, setAmount] = useState("0");
|
||||
|
||||
const jsonError = useRef(false);
|
||||
const [codeIdError, setCodeIdError] = useState("");
|
||||
const [labelError, setLabelError] = useState("");
|
||||
const [adminAddressError, setAdminAddressError] = useState("");
|
||||
const [saltError, setSaltError] = useState("");
|
||||
const [customDenomError, setCustomDenomError] = useState("");
|
||||
const [amountError, setAmountError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
setCodeIdError("");
|
||||
setLabelError("");
|
||||
setAdminAddressError("");
|
||||
setSaltError("");
|
||||
setCustomDenomError("");
|
||||
setAmountError("");
|
||||
|
||||
const isMsgValid = (): boolean => {
|
||||
if (jsonError.current) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!codeId || !Number.isSafeInteger(Number(codeId)) || Number(codeId) <= 0) {
|
||||
setCodeIdError("Code ID must be a positive integer");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!label) {
|
||||
setLabelError("Label is required");
|
||||
return false;
|
||||
}
|
||||
|
||||
const addressErrorMsg = checkAddress(adminAddress, chain.addressPrefix);
|
||||
if (adminAddress && addressErrorMsg) {
|
||||
setAdminAddressError(`Invalid address for network ${chain.chainId}: ${addressErrorMsg}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!salt) {
|
||||
throw new Error("Salt is required");
|
||||
}
|
||||
|
||||
fromHex(salt);
|
||||
} catch (e) {
|
||||
setSaltError(e instanceof Error ? e.message : "Salt needs to be an hexadecimal string");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (selectedDenom.value === customDenomOption.value && !customDenom) {
|
||||
setCustomDenomError("Custom denom must be set because of selection above");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!amount || Number(amount) <= 0) {
|
||||
setAmountError("Amount must be greater than 0");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (selectedDenom.value === customDenomOption.value && !Number.isInteger(Number(amount))) {
|
||||
setAmountError("Amount cannot be decimal for custom denom");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const denom =
|
||||
selectedDenom.value === customDenomOption.value ? customDenom : selectedDenom.value.symbol;
|
||||
|
||||
const microCoin = (() => {
|
||||
try {
|
||||
return macroCoinToMicroCoin({ denom, amount }, chain.assets);
|
||||
} catch {
|
||||
return { denom, amount: "0" };
|
||||
}
|
||||
})();
|
||||
|
||||
const hexSalt = (() => {
|
||||
try {
|
||||
return fromHex(salt);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
})();
|
||||
|
||||
const msgContentUtf8Array = (() => {
|
||||
try {
|
||||
// The JsonEditor does not escape \n or remove whitespaces, so we need to parse + stringify
|
||||
return toUtf8(JSON.stringify(JSON.parse(msgContent)));
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
})();
|
||||
|
||||
const msgValue = MsgCodecs[MsgTypeUrls.Instantiate2].fromPartial({
|
||||
sender: fromAddress,
|
||||
codeId: codeId || 0,
|
||||
label,
|
||||
admin: adminAddress,
|
||||
fixMsg: false,
|
||||
salt: hexSalt,
|
||||
msg: msgContentUtf8Array,
|
||||
funds: [microCoin],
|
||||
});
|
||||
|
||||
const msg: MsgInstantiateContract2EncodeObject = {
|
||||
typeUrl: MsgTypeUrls.Instantiate2,
|
||||
value: msgValue,
|
||||
};
|
||||
|
||||
setMsgGetter({ isMsgValid, msg });
|
||||
}, [
|
||||
adminAddress,
|
||||
amount,
|
||||
chain.addressPrefix,
|
||||
chain.assets,
|
||||
chain.chainId,
|
||||
codeId,
|
||||
customDenom,
|
||||
fromAddress,
|
||||
label,
|
||||
msgContent,
|
||||
salt,
|
||||
selectedDenom.value,
|
||||
setMsgGetter,
|
||||
]);
|
||||
|
||||
return (
|
||||
<StackableContainer lessPadding lessMargin>
|
||||
<button className="remove" onClick={() => deleteMsg()}>
|
||||
✕
|
||||
</button>
|
||||
<h2>MsgInstantiateContract2</h2>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
label="Code ID"
|
||||
name="code-id"
|
||||
value={codeId}
|
||||
onChange={({ target }) => setCodeId(target.value)}
|
||||
error={codeIdError}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
label="Label"
|
||||
name="label"
|
||||
value={label}
|
||||
onChange={({ target }) => setLabel(target.value)}
|
||||
error={labelError}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
label="Admin Address"
|
||||
name="admin-address"
|
||||
value={adminAddress}
|
||||
onChange={({ target }) => setAdminAddress(target.value)}
|
||||
error={adminAddressError}
|
||||
placeholder={`E.g. ${exampleAddress(0, chain.addressPrefix)}`}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
label="Salt (hex encoded)"
|
||||
name="salt"
|
||||
placeholder="E.g. 1bac68"
|
||||
value={salt}
|
||||
onChange={({ target }) => setSalt(target.value)}
|
||||
error={saltError}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-item">
|
||||
<JsonEditor
|
||||
label="Msg JSON"
|
||||
content={{ text: msgContent }}
|
||||
onChange={(newMsgContent, _, { contentErrors }) => {
|
||||
setMsgContent("text" in newMsgContent ? newMsgContent.text ?? "{}" : "{}");
|
||||
jsonError.current = !!contentErrors;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-item form-select">
|
||||
<label>Choose a denom:</label>
|
||||
<Select
|
||||
label="Select denom"
|
||||
name="denom-select"
|
||||
options={denomOptions}
|
||||
value={selectedDenom}
|
||||
onChange={(option: (typeof denomOptions)[number]) => {
|
||||
setSelectedDenom(option);
|
||||
if (option.value !== customDenomOption.value) {
|
||||
setCustomDenom("");
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
label="Custom denom"
|
||||
name="custom-denom"
|
||||
value={customDenom}
|
||||
onChange={({ target }) => setCustomDenom(target.value)}
|
||||
placeholder={
|
||||
selectedDenom.value === customDenomOption.value
|
||||
? "Enter custom denom"
|
||||
: "Select Custom denom above"
|
||||
}
|
||||
disabled={selectedDenom.value !== customDenomOption.value}
|
||||
error={customDenomError}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
type="number"
|
||||
label="Amount"
|
||||
name="amount"
|
||||
value={amount}
|
||||
onChange={({ target }) => setAmount(target.value)}
|
||||
error={amountError}
|
||||
/>
|
||||
</div>
|
||||
<style jsx>{`
|
||||
.form-item {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
.form-item label {
|
||||
font-style: italic;
|
||||
font-size: 12px;
|
||||
}
|
||||
.form-select {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.8em;
|
||||
}
|
||||
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 MsgInstantiateContract2Form;
|
||||
@@ -0,0 +1,266 @@
|
||||
import { MsgInstantiateContractEncodeObject } from "@cosmjs/cosmwasm-stargate";
|
||||
import { toUtf8 } from "@cosmjs/encoding";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { MsgGetter } from "..";
|
||||
import { useChains } from "../../../../context/ChainsContext";
|
||||
import { ChainInfo } from "../../../../context/ChainsContext/types";
|
||||
import { macroCoinToMicroCoin } from "../../../../lib/coinHelpers";
|
||||
import { checkAddress, exampleAddress } from "../../../../lib/displayHelpers";
|
||||
import { MsgCodecs, MsgTypeUrls } from "../../../../types/txMsg";
|
||||
import Input from "../../../inputs/Input";
|
||||
import Select from "../../../inputs/Select";
|
||||
import StackableContainer from "../../../layout/StackableContainer";
|
||||
|
||||
const JsonEditor = dynamic(() => import("../../../inputs/JsonEditor"), { ssr: false });
|
||||
|
||||
const customDenomOption = { label: "Custom (enter denom below)", value: "custom" } as const;
|
||||
|
||||
const getDenomOptions = (assets: ChainInfo["assets"]) => {
|
||||
if (!assets?.length) {
|
||||
return [customDenomOption];
|
||||
}
|
||||
|
||||
return [...assets.map((asset) => ({ label: asset.symbol, value: asset })), customDenomOption];
|
||||
};
|
||||
|
||||
interface MsgInstantiateContractFormProps {
|
||||
readonly fromAddress: string;
|
||||
readonly setMsgGetter: (msgGetter: MsgGetter) => void;
|
||||
readonly deleteMsg: () => void;
|
||||
}
|
||||
|
||||
const MsgInstantiateContractForm = ({
|
||||
fromAddress,
|
||||
setMsgGetter,
|
||||
deleteMsg,
|
||||
}: MsgInstantiateContractFormProps) => {
|
||||
const { chain } = useChains();
|
||||
|
||||
const denomOptions = getDenomOptions(chain.assets);
|
||||
|
||||
const [codeId, setCodeId] = useState("");
|
||||
const [label, setLabel] = useState("");
|
||||
const [adminAddress, setAdminAddress] = useState("");
|
||||
const [msgContent, setMsgContent] = useState("{}");
|
||||
const [selectedDenom, setSelectedDenom] = useState(denomOptions[0]);
|
||||
const [customDenom, setCustomDenom] = useState("");
|
||||
const [amount, setAmount] = useState("0");
|
||||
|
||||
const jsonError = useRef(false);
|
||||
const [codeIdError, setCodeIdError] = useState("");
|
||||
const [labelError, setLabelError] = useState("");
|
||||
const [adminAddressError, setAdminAddressError] = useState("");
|
||||
const [customDenomError, setCustomDenomError] = useState("");
|
||||
const [amountError, setAmountError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
setCodeIdError("");
|
||||
setLabelError("");
|
||||
setAdminAddressError("");
|
||||
setCustomDenomError("");
|
||||
setAmountError("");
|
||||
|
||||
const isMsgValid = (): boolean => {
|
||||
if (jsonError.current) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!codeId || !Number.isSafeInteger(Number(codeId)) || Number(codeId) <= 0) {
|
||||
setCodeIdError("Code ID must be a positive integer");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!label) {
|
||||
setLabelError("Label is required");
|
||||
return false;
|
||||
}
|
||||
|
||||
const addressErrorMsg = checkAddress(adminAddress, chain.addressPrefix);
|
||||
if (adminAddress && addressErrorMsg) {
|
||||
setAdminAddressError(`Invalid address for network ${chain.chainId}: ${addressErrorMsg}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (selectedDenom.value === customDenomOption.value && !customDenom) {
|
||||
setCustomDenomError("Custom denom must be set because of selection above");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!amount || Number(amount) <= 0) {
|
||||
setAmountError("Amount must be greater than 0");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (selectedDenom.value === customDenomOption.value && !Number.isInteger(Number(amount))) {
|
||||
setAmountError("Amount cannot be decimal for custom denom");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const denom =
|
||||
selectedDenom.value === customDenomOption.value ? customDenom : selectedDenom.value.symbol;
|
||||
|
||||
const microCoin = (() => {
|
||||
try {
|
||||
return macroCoinToMicroCoin({ denom, amount }, chain.assets);
|
||||
} catch {
|
||||
return { denom, amount: "0" };
|
||||
}
|
||||
})();
|
||||
|
||||
const msgContentUtf8Array = (() => {
|
||||
try {
|
||||
// The JsonEditor does not escape \n or remove whitespaces, so we need to parse + stringify
|
||||
return toUtf8(JSON.stringify(JSON.parse(msgContent)));
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
})();
|
||||
|
||||
const msgValue = MsgCodecs[MsgTypeUrls.Instantiate].fromPartial({
|
||||
sender: fromAddress,
|
||||
codeId: codeId || 1,
|
||||
label,
|
||||
admin: adminAddress,
|
||||
msg: msgContentUtf8Array,
|
||||
funds: [microCoin],
|
||||
});
|
||||
|
||||
const msg: MsgInstantiateContractEncodeObject = {
|
||||
typeUrl: MsgTypeUrls.Instantiate,
|
||||
value: msgValue,
|
||||
};
|
||||
|
||||
setMsgGetter({ isMsgValid, msg });
|
||||
}, [
|
||||
adminAddress,
|
||||
amount,
|
||||
chain.addressPrefix,
|
||||
chain.assets,
|
||||
chain.chainId,
|
||||
codeId,
|
||||
customDenom,
|
||||
fromAddress,
|
||||
label,
|
||||
msgContent,
|
||||
selectedDenom.value,
|
||||
setMsgGetter,
|
||||
]);
|
||||
|
||||
return (
|
||||
<StackableContainer lessPadding lessMargin>
|
||||
<button className="remove" onClick={() => deleteMsg()}>
|
||||
✕
|
||||
</button>
|
||||
<h2>MsgInstantiateContract</h2>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
label="Code ID"
|
||||
name="code-id"
|
||||
value={codeId}
|
||||
onChange={({ target }) => setCodeId(target.value)}
|
||||
error={codeIdError}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
label="Label"
|
||||
name="label"
|
||||
value={label}
|
||||
onChange={({ target }) => setLabel(target.value)}
|
||||
error={labelError}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
label="Admin Address"
|
||||
name="admin-address"
|
||||
value={adminAddress}
|
||||
onChange={({ target }) => setAdminAddress(target.value)}
|
||||
error={adminAddressError}
|
||||
placeholder={`E.g. ${exampleAddress(0, chain.addressPrefix)}`}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-item">
|
||||
<JsonEditor
|
||||
label="Msg JSON"
|
||||
content={{ text: msgContent }}
|
||||
onChange={(newMsgContent, _, { contentErrors }) => {
|
||||
setMsgContent("text" in newMsgContent ? newMsgContent.text ?? "{}" : "{}");
|
||||
jsonError.current = !!contentErrors;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-item form-select">
|
||||
<label>Choose a denom:</label>
|
||||
<Select
|
||||
label="Select denom"
|
||||
name="denom-select"
|
||||
options={denomOptions}
|
||||
value={selectedDenom}
|
||||
onChange={(option: (typeof denomOptions)[number]) => {
|
||||
setSelectedDenom(option);
|
||||
if (option.value !== customDenomOption.value) {
|
||||
setCustomDenom("");
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
label="Custom denom"
|
||||
name="custom-denom"
|
||||
value={customDenom}
|
||||
onChange={({ target }) => setCustomDenom(target.value)}
|
||||
placeholder={
|
||||
selectedDenom.value === customDenomOption.value
|
||||
? "Enter custom denom"
|
||||
: "Select Custom denom above"
|
||||
}
|
||||
disabled={selectedDenom.value !== customDenomOption.value}
|
||||
error={customDenomError}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
type="number"
|
||||
label="Amount"
|
||||
name="amount"
|
||||
value={amount}
|
||||
onChange={({ target }) => setAmount(target.value)}
|
||||
error={amountError}
|
||||
/>
|
||||
</div>
|
||||
<style jsx>{`
|
||||
.form-item {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
.form-item label {
|
||||
font-style: italic;
|
||||
font-size: 12px;
|
||||
}
|
||||
.form-select {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.8em;
|
||||
}
|
||||
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 MsgInstantiateContractForm;
|
||||
@@ -0,0 +1,151 @@
|
||||
import { MsgMigrateContractEncodeObject } from "@cosmjs/cosmwasm-stargate";
|
||||
import { toUtf8 } from "@cosmjs/encoding";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { MsgGetter } from "..";
|
||||
import { useChains } from "../../../../context/ChainsContext";
|
||||
import { checkAddress, exampleAddress } from "../../../../lib/displayHelpers";
|
||||
import { MsgCodecs, MsgTypeUrls } from "../../../../types/txMsg";
|
||||
import Input from "../../../inputs/Input";
|
||||
import StackableContainer from "../../../layout/StackableContainer";
|
||||
|
||||
const JsonEditor = dynamic(() => import("../../../inputs/JsonEditor"), { ssr: false });
|
||||
|
||||
interface MsgMigrateContractFormProps {
|
||||
readonly fromAddress: string;
|
||||
readonly setMsgGetter: (msgGetter: MsgGetter) => void;
|
||||
readonly deleteMsg: () => void;
|
||||
}
|
||||
|
||||
const MsgMigrateContractForm = ({
|
||||
fromAddress,
|
||||
setMsgGetter,
|
||||
deleteMsg,
|
||||
}: MsgMigrateContractFormProps) => {
|
||||
const { chain } = useChains();
|
||||
|
||||
const [contractAddress, setContractAddress] = useState("");
|
||||
const [codeId, setCodeId] = useState("");
|
||||
const [msgContent, setMsgContent] = useState("{}");
|
||||
|
||||
const jsonError = useRef(false);
|
||||
const [contractAddressError, setContractAddressError] = useState("");
|
||||
const [codeIdError, setCodeIdError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
setCodeIdError("");
|
||||
setContractAddressError("");
|
||||
|
||||
const isMsgValid = (): boolean => {
|
||||
if (jsonError.current) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const addressErrorMsg = checkAddress(contractAddress, chain.addressPrefix);
|
||||
if (addressErrorMsg) {
|
||||
setContractAddressError(`Invalid address for network ${chain.chainId}: ${addressErrorMsg}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!codeId || !Number.isSafeInteger(Number(codeId)) || Number(codeId) <= 0) {
|
||||
setCodeIdError("Code ID must be a positive integer");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const msgContentUtf8Array = (() => {
|
||||
try {
|
||||
// The JsonEditor does not escape \n or remove whitespaces, so we need to parse + stringify
|
||||
return toUtf8(JSON.stringify(JSON.parse(msgContent)));
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
})();
|
||||
|
||||
const msgValue = MsgCodecs[MsgTypeUrls.Migrate].fromPartial({
|
||||
sender: fromAddress,
|
||||
contract: contractAddress,
|
||||
codeId: codeId || 1,
|
||||
msg: msgContentUtf8Array,
|
||||
});
|
||||
|
||||
const msg: MsgMigrateContractEncodeObject = { typeUrl: MsgTypeUrls.Migrate, value: msgValue };
|
||||
|
||||
setMsgGetter({ isMsgValid, msg });
|
||||
}, [
|
||||
chain.addressPrefix,
|
||||
chain.chainId,
|
||||
codeId,
|
||||
contractAddress,
|
||||
fromAddress,
|
||||
msgContent,
|
||||
setMsgGetter,
|
||||
]);
|
||||
|
||||
return (
|
||||
<StackableContainer lessPadding lessMargin>
|
||||
<button className="remove" onClick={() => deleteMsg()}>
|
||||
✕
|
||||
</button>
|
||||
<h2>MsgMigrateContract</h2>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
label="Contract Address"
|
||||
name="contract-address"
|
||||
value={contractAddress}
|
||||
onChange={({ target }) => setContractAddress(target.value)}
|
||||
error={contractAddressError}
|
||||
placeholder={`E.g. ${exampleAddress(0, chain.addressPrefix)}`}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
label="Code ID"
|
||||
name="code-id"
|
||||
value={codeId}
|
||||
onChange={({ target }) => setCodeId(target.value)}
|
||||
error={codeIdError}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-item">
|
||||
<JsonEditor
|
||||
label="Msg JSON"
|
||||
content={{ text: msgContent }}
|
||||
onChange={(newMsgContent, _, { contentErrors }) => {
|
||||
setMsgContent("text" in newMsgContent ? newMsgContent.text ?? "{}" : "{}");
|
||||
jsonError.current = !!contentErrors;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<style jsx>{`
|
||||
.form-item {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
.form-item label {
|
||||
font-style: italic;
|
||||
font-size: 12px;
|
||||
}
|
||||
.form-select {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.8em;
|
||||
}
|
||||
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 MsgMigrateContractForm;
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Decimal } from "@cosmjs/math";
|
||||
import { MsgSendEncodeObject } from "@cosmjs/stargate";
|
||||
import { assert } from "@cosmjs/utils";
|
||||
import { useEffect, useState } from "react";
|
||||
import { MsgGetter } from "..";
|
||||
import { useChains } from "../../../../context/ChainsContext";
|
||||
import { macroCoinToMicroCoin } from "../../../../lib/coinHelpers";
|
||||
import { checkAddress, exampleAddress } from "../../../../lib/displayHelpers";
|
||||
import { RegistryAsset } from "../../../../types/chainRegistry";
|
||||
import { MsgCodecs, MsgTypeUrls } from "../../../../types/txMsg";
|
||||
@@ -71,40 +70,21 @@ const MsgSendForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgSendFormProps)
|
||||
return true;
|
||||
};
|
||||
|
||||
const symbol =
|
||||
const denom =
|
||||
selectedDenom.value === customDenomOption.value ? customDenom : selectedDenom.value.symbol;
|
||||
|
||||
const [denom, amountInAtomics] = (() => {
|
||||
const microCoin = (() => {
|
||||
try {
|
||||
if (selectedDenom.value === customDenomOption.value) {
|
||||
return [symbol, Decimal.fromUserInput(amount, 0).atomics];
|
||||
}
|
||||
|
||||
const foundAsset = chain.assets.find((asset) => asset.symbol === symbol);
|
||||
assert(foundAsset, `An asset with the given symbol ${symbol} was not found`);
|
||||
if (!foundAsset) return [undefined, undefined];
|
||||
|
||||
const units = foundAsset.denom_units ?? [];
|
||||
const macroUnit = units.find(
|
||||
(unit) => unit.denom.toLowerCase() === foundAsset.symbol.toLowerCase(),
|
||||
);
|
||||
assert(macroUnit, `An unit with the given denom ${symbol} was not found`);
|
||||
if (!macroUnit) return [undefined, undefined];
|
||||
|
||||
const smallestUnit = units.reduce((prevUnit, currentUnit) =>
|
||||
currentUnit.exponent < prevUnit.exponent ? currentUnit : prevUnit,
|
||||
);
|
||||
|
||||
return [smallestUnit.denom, Decimal.fromUserInput(amount, macroUnit.exponent).atomics];
|
||||
return macroCoinToMicroCoin({ denom, amount }, chain.assets);
|
||||
} catch {
|
||||
return "0";
|
||||
return { denom, amount: "0" };
|
||||
}
|
||||
})();
|
||||
|
||||
const msgValue = MsgCodecs[MsgTypeUrls.Send].fromPartial({
|
||||
fromAddress,
|
||||
toAddress,
|
||||
amount: [{ denom, amount: amountInAtomics }],
|
||||
amount: [microCoin],
|
||||
});
|
||||
|
||||
const msg: MsgSendEncodeObject = { typeUrl: MsgTypeUrls.Send, value: msgValue };
|
||||
|
||||
@@ -3,6 +3,10 @@ import { MsgTypeUrl, MsgTypeUrls } from "../../../../types/txMsg";
|
||||
import MsgClaimRewardsForm from "./MsgClaimRewardsForm";
|
||||
import MsgCreateVestingAccountForm from "./MsgCreateVestingAccountForm";
|
||||
import MsgDelegateForm from "./MsgDelegateForm";
|
||||
import MsgExecuteContractForm from "./MsgExecuteContractForm";
|
||||
import MsgInstantiateContract2Form from "./MsgInstantiateContract2Form";
|
||||
import MsgInstantiateContractForm from "./MsgInstantiateContractForm";
|
||||
import MsgMigrateContractForm from "./MsgMigrateContractForm";
|
||||
import MsgRedelegateForm from "./MsgRedelegateForm";
|
||||
import MsgSendForm from "./MsgSendForm";
|
||||
import MsgSetWithdrawAddressForm from "./MsgSetWithdrawAddressForm";
|
||||
@@ -34,6 +38,14 @@ const MsgForm = ({ msgType, senderAddress, ...restProps }: MsgFormProps) => {
|
||||
return <MsgCreateVestingAccountForm fromAddress={senderAddress} {...restProps} />;
|
||||
case MsgTypeUrls.Transfer:
|
||||
return <MsgTransferForm fromAddress={senderAddress} {...restProps} />;
|
||||
case MsgTypeUrls.Execute:
|
||||
return <MsgExecuteContractForm fromAddress={senderAddress} {...restProps} />;
|
||||
case MsgTypeUrls.Instantiate:
|
||||
return <MsgInstantiateContractForm fromAddress={senderAddress} {...restProps} />;
|
||||
case MsgTypeUrls.Instantiate2:
|
||||
return <MsgInstantiateContract2Form fromAddress={senderAddress} {...restProps} />;
|
||||
case MsgTypeUrls.Migrate:
|
||||
return <MsgMigrateContractForm fromAddress={senderAddress} {...restProps} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -173,6 +173,16 @@ const CreateTxForm = ({ router, senderAddress, accountOnChain }: CreateTxFormPro
|
||||
onClick={() => addMsgType(MsgTypeUrls.CreateVestingAccount)}
|
||||
/>
|
||||
<Button label="Add MsgTransfer" onClick={() => addMsgType(MsgTypeUrls.Transfer)} />
|
||||
<Button label="Add MsgExecuteContract" onClick={() => addMsgType(MsgTypeUrls.Execute)} />
|
||||
<Button
|
||||
label="Add MsgInstantiateContract"
|
||||
onClick={() => addMsgType(MsgTypeUrls.Instantiate)}
|
||||
/>
|
||||
<Button
|
||||
label="Add MsgInstantiateContract2"
|
||||
onClick={() => addMsgType(MsgTypeUrls.Instantiate2)}
|
||||
/>
|
||||
<Button label="Add MsgMigrateContract" onClick={() => addMsgType(MsgTypeUrls.Migrate)} />
|
||||
</StackableContainer>
|
||||
{showCreateTxError ? (
|
||||
<StackableContainer lessMargin lessPadding>
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { MultisigThresholdPubkey, makeCosmoshubPath } from "@cosmjs/amino";
|
||||
import { createWasmAminoConverters, wasmTypes } from "@cosmjs/cosmwasm-stargate";
|
||||
import { toBase64 } from "@cosmjs/encoding";
|
||||
import { LedgerSigner } from "@cosmjs/ledger-amino";
|
||||
import { SigningStargateClient } from "@cosmjs/stargate";
|
||||
import { Registry } from "@cosmjs/proto-signing";
|
||||
import {
|
||||
AminoTypes,
|
||||
SigningStargateClient,
|
||||
createDefaultAminoConverters,
|
||||
defaultRegistryTypes,
|
||||
} from "@cosmjs/stargate";
|
||||
import { assert } from "@cosmjs/utils";
|
||||
import TransportWebUSB from "@ledgerhq/hw-transport-webusb";
|
||||
import { useCallback, useLayoutEffect, useState } from "react";
|
||||
@@ -142,7 +149,13 @@ const TransactionSigning = (props: TransactionSigningProps) => {
|
||||
|
||||
const signerAddress = walletAccount?.bech32Address;
|
||||
assert(signerAddress, "Missing signer address");
|
||||
const signingClient = await SigningStargateClient.offline(offlineSigner);
|
||||
const signingClient = await SigningStargateClient.offline(offlineSigner, {
|
||||
registry: new Registry([...defaultRegistryTypes, ...wasmTypes]),
|
||||
aminoTypes: new AminoTypes({
|
||||
...createDefaultAminoConverters(),
|
||||
...createWasmAminoConverters(),
|
||||
}),
|
||||
});
|
||||
|
||||
const signerData = {
|
||||
accountNumber: props.tx.accountNumber,
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { CSSProperties, useEffect, useRef } from "react";
|
||||
import { JSONEditorPropsOptional, Mode, JSONEditor as VanillaJsonEditor } from "vanilla-jsoneditor";
|
||||
|
||||
const editorStyle: { [key: string]: string } & CSSProperties = {
|
||||
"--jse-a-color": "white",
|
||||
"--jse-delimiter-color": "white",
|
||||
"--jse-key-color": "white",
|
||||
"--jse-text-color": "white",
|
||||
"--jse-value-color": "white",
|
||||
"--jse-value-color-boolean": "white",
|
||||
"--jse-value-color-null": "white",
|
||||
"--jse-value-color-number": "white",
|
||||
"--jse-value-color-string": "white",
|
||||
"--jse-value-color-url": "white",
|
||||
"--jse-background-color": "transparent",
|
||||
"--jse-panel-background": "transparent",
|
||||
"--jse-selection-background-color": "rgba(255, 255, 255, 0.5)",
|
||||
"--jse-main-border": "2px solid rgba(255, 255, 255, 0.5)",
|
||||
"--jse-panel-border": "2px solid rgba(255, 255, 255, 0.5)",
|
||||
};
|
||||
|
||||
interface JsonEditorProps extends JSONEditorPropsOptional {
|
||||
readonly label?: string;
|
||||
}
|
||||
|
||||
export default function JsonEditor({ label, ...editorProps }: JsonEditorProps) {
|
||||
const refContainer = useRef<HTMLDivElement>(null);
|
||||
const refEditor = useRef<VanillaJsonEditor | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!refContainer.current) return;
|
||||
|
||||
refEditor.current = new VanillaJsonEditor({ target: refContainer.current, props: {} });
|
||||
|
||||
return () => {
|
||||
if (refEditor.current) {
|
||||
refEditor.current.destroy();
|
||||
refEditor.current = null;
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (refEditor.current) {
|
||||
refEditor.current.updateProps({ mode: Mode.text, mainMenuBar: false, ...editorProps });
|
||||
}
|
||||
}, [editorProps]);
|
||||
|
||||
return (
|
||||
<div className="container" style={editorStyle} ref={refContainer}>
|
||||
{label ? <label>{label}</label> : null}
|
||||
<style jsx>{`
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.8em;
|
||||
}
|
||||
label {
|
||||
font-style: italic;
|
||||
font-size: 12px;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
import { RegistryAsset } from "../types/chainRegistry";
|
||||
import { macroCoinToMicroCoin } from "./coinHelpers";
|
||||
|
||||
const assets: readonly RegistryAsset[] = [
|
||||
{
|
||||
denom_units: [
|
||||
{
|
||||
denom: "ujunox",
|
||||
exponent: 0,
|
||||
},
|
||||
{
|
||||
denom: "junox",
|
||||
exponent: 6,
|
||||
},
|
||||
],
|
||||
symbol: "JUNOX",
|
||||
display: "junox",
|
||||
name: "Juno Testnet",
|
||||
base: "ujunox",
|
||||
},
|
||||
{
|
||||
denom_units: [
|
||||
{
|
||||
denom: "ncheq",
|
||||
exponent: 0,
|
||||
},
|
||||
{
|
||||
denom: "cheq",
|
||||
exponent: 9,
|
||||
},
|
||||
],
|
||||
symbol: "CHEQ",
|
||||
display: "cheq",
|
||||
name: "cheqd",
|
||||
base: "ncheq",
|
||||
},
|
||||
{
|
||||
denom_units: [
|
||||
{
|
||||
denom: "erc20/0x2Cbea61fdfDFA520Ee99700F104D5b75ADf50B0c",
|
||||
exponent: 0,
|
||||
},
|
||||
{
|
||||
denom: "arusd",
|
||||
exponent: 18,
|
||||
},
|
||||
],
|
||||
symbol: "arUSD",
|
||||
display: "arusd",
|
||||
name: "Arable USD",
|
||||
base: "erc20/0x2Cbea61fdfDFA520Ee99700F104D5b75ADf50B0c",
|
||||
},
|
||||
{
|
||||
denom_units: [
|
||||
{
|
||||
denom: "wei",
|
||||
exponent: 0,
|
||||
},
|
||||
{
|
||||
denom: "gwei",
|
||||
exponent: 9,
|
||||
},
|
||||
{
|
||||
denom: "eth",
|
||||
exponent: 18,
|
||||
aliases: ["ether"],
|
||||
},
|
||||
],
|
||||
symbol: "ETH",
|
||||
display: "eth",
|
||||
name: "Ether",
|
||||
base: "wei",
|
||||
},
|
||||
];
|
||||
|
||||
describe("macroCoinToMicroCoin", () => {
|
||||
it("works with symbol", () => {
|
||||
expect(macroCoinToMicroCoin({ denom: "JUNOX", amount: "12" }, assets)).toEqual({
|
||||
denom: "ujunox",
|
||||
amount: "12000000",
|
||||
});
|
||||
expect(macroCoinToMicroCoin({ denom: "CHEQ", amount: "34" }, assets)).toEqual({
|
||||
denom: "ncheq",
|
||||
amount: "34000000000",
|
||||
});
|
||||
expect(macroCoinToMicroCoin({ denom: "arUSD", amount: "56" }, assets)).toEqual({
|
||||
denom: "erc20/0x2Cbea61fdfDFA520Ee99700F104D5b75ADf50B0c",
|
||||
amount: "56000000000000000000",
|
||||
});
|
||||
expect(macroCoinToMicroCoin({ denom: "ETH", amount: "78" }, assets)).toEqual({
|
||||
denom: "wei",
|
||||
amount: "78000000000000000000",
|
||||
});
|
||||
});
|
||||
|
||||
it("works with base unit", () => {
|
||||
expect(macroCoinToMicroCoin({ denom: "ujunox", amount: "12" }, assets)).toEqual({
|
||||
denom: "ujunox",
|
||||
amount: "12",
|
||||
});
|
||||
expect(macroCoinToMicroCoin({ denom: "ncheq", amount: "34" }, assets)).toEqual({
|
||||
denom: "ncheq",
|
||||
amount: "34",
|
||||
});
|
||||
expect(
|
||||
macroCoinToMicroCoin(
|
||||
{ denom: "erc20/0x2Cbea61fdfDFA520Ee99700F104D5b75ADf50B0c", amount: "56" },
|
||||
assets,
|
||||
),
|
||||
).toEqual({ denom: "erc20/0x2Cbea61fdfDFA520Ee99700F104D5b75ADf50B0c", amount: "56" });
|
||||
expect(macroCoinToMicroCoin({ denom: "wei", amount: "78" }, assets)).toEqual({
|
||||
denom: "wei",
|
||||
amount: "78",
|
||||
});
|
||||
});
|
||||
|
||||
it("works with biggest unit", () => {
|
||||
expect(macroCoinToMicroCoin({ denom: "junox", amount: "12" }, assets)).toEqual({
|
||||
denom: "ujunox",
|
||||
amount: "12000000",
|
||||
});
|
||||
expect(macroCoinToMicroCoin({ denom: "cheq", amount: "34" }, assets)).toEqual({
|
||||
denom: "ncheq",
|
||||
amount: "34000000000",
|
||||
});
|
||||
expect(macroCoinToMicroCoin({ denom: "arusd", amount: "56" }, assets)).toEqual({
|
||||
denom: "erc20/0x2Cbea61fdfDFA520Ee99700F104D5b75ADf50B0c",
|
||||
amount: "56000000000000000000",
|
||||
});
|
||||
expect(macroCoinToMicroCoin({ denom: "eth", amount: "78" }, assets)).toEqual({
|
||||
denom: "wei",
|
||||
amount: "78000000000000000000",
|
||||
});
|
||||
});
|
||||
|
||||
it("works with intermediate unit", () => {
|
||||
expect(macroCoinToMicroCoin({ denom: "gwei", amount: "78" }, assets)).toEqual({
|
||||
denom: "wei",
|
||||
amount: "78000000000",
|
||||
});
|
||||
expect(macroCoinToMicroCoin({ denom: "GWEI", amount: "78" }, assets)).toEqual({
|
||||
denom: "wei",
|
||||
amount: "78000000000",
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Decimal } from "@cosmjs/math";
|
||||
import { Coin } from "@cosmjs/stargate";
|
||||
import { assert } from "@cosmjs/utils";
|
||||
import { RegistryAsset } from "../types/chainRegistry";
|
||||
|
||||
const macroCoinToMicroCoin = (macroCoin: Coin, assets: readonly RegistryAsset[]): Coin => {
|
||||
const lowerCaseDenom = macroCoin.denom.toLowerCase();
|
||||
|
||||
const asset = assets.find(
|
||||
(currentAsset) =>
|
||||
lowerCaseDenom === currentAsset.symbol.toLowerCase() ||
|
||||
lowerCaseDenom === currentAsset.display.toLowerCase() ||
|
||||
lowerCaseDenom === currentAsset.name.toLowerCase() ||
|
||||
lowerCaseDenom === currentAsset.base.toLowerCase() ||
|
||||
currentAsset.denom_units.find(
|
||||
(unit) => unit.denom === lowerCaseDenom || unit.aliases?.includes(lowerCaseDenom),
|
||||
),
|
||||
);
|
||||
|
||||
assert(asset, `An asset with the given symbol ${macroCoin.denom} was not found`);
|
||||
|
||||
const macroUnit = asset.denom_units.find(
|
||||
(currentUnit) => lowerCaseDenom === currentUnit.denom.toLowerCase(),
|
||||
);
|
||||
assert(macroUnit, `A unit with the given symbol ${lowerCaseDenom} was not found`);
|
||||
|
||||
const baseUnit = asset.denom_units.find((currentUnit) => currentUnit.exponent === 0);
|
||||
assert(baseUnit, `A base unit with exponent = 0 was not found`);
|
||||
|
||||
const denom = baseUnit.denom;
|
||||
const amount = Decimal.fromUserInput(macroCoin.amount, macroUnit.exponent).atomics;
|
||||
|
||||
return { denom, amount };
|
||||
};
|
||||
|
||||
export { macroCoinToMicroCoin };
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Coin } from "@cosmjs/stargate";
|
||||
import { ChainInfo } from "../types";
|
||||
import { ChainInfo } from "../context/ChainsContext/types";
|
||||
import { printableCoin, printableCoins, thinSpace } from "./displayHelpers";
|
||||
|
||||
const testChainInfo: ChainInfo = {
|
||||
|
||||
@@ -20,6 +20,14 @@ const gasOfMsg = (msgType: MsgTypeUrl): number => {
|
||||
return 100_000;
|
||||
case MsgTypeUrls.Transfer:
|
||||
return 180_000;
|
||||
case MsgTypeUrls.Execute:
|
||||
return 150_000;
|
||||
case MsgTypeUrls.Instantiate:
|
||||
return 150_000;
|
||||
case MsgTypeUrls.Instantiate2:
|
||||
return 150_000;
|
||||
case MsgTypeUrls.Migrate:
|
||||
return 150_000;
|
||||
default:
|
||||
throw new Error("Unknown msg type");
|
||||
}
|
||||
|
||||
Generated
+136
-1
@@ -7,6 +7,7 @@
|
||||
"name": "create-next-example-app",
|
||||
"dependencies": {
|
||||
"@cosmjs/amino": "^0.31.0",
|
||||
"@cosmjs/cosmwasm-stargate": "^0.31.0",
|
||||
"@cosmjs/crypto": "^0.31.0",
|
||||
"@cosmjs/encoding": "^0.31.0",
|
||||
"@cosmjs/ledger-amino": "^0.31.0",
|
||||
@@ -22,7 +23,8 @@
|
||||
"next": "^13.3.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-select": "^5.7.2"
|
||||
"react-select": "^5.7.2",
|
||||
"vanilla-jsoneditor": "^0.17.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
@@ -608,6 +610,24 @@
|
||||
"@cosmjs/utils": "^0.31.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@cosmjs/cosmwasm-stargate": {
|
||||
"version": "0.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@cosmjs/cosmwasm-stargate/-/cosmwasm-stargate-0.31.0.tgz",
|
||||
"integrity": "sha512-l6aX++3LhaAGZO46qIgrrNF40lYhOrdPfl35Z32ks6Wf3mwgbQEZwaxnoGzwUePY7/yaIiEFJ1JO6MlVPZVuag==",
|
||||
"dependencies": {
|
||||
"@cosmjs/amino": "^0.31.0",
|
||||
"@cosmjs/crypto": "^0.31.0",
|
||||
"@cosmjs/encoding": "^0.31.0",
|
||||
"@cosmjs/math": "^0.31.0",
|
||||
"@cosmjs/proto-signing": "^0.31.0",
|
||||
"@cosmjs/stargate": "^0.31.0",
|
||||
"@cosmjs/tendermint-rpc": "^0.31.0",
|
||||
"@cosmjs/utils": "^0.31.0",
|
||||
"cosmjs-types": "^0.8.0",
|
||||
"long": "^4.0.0",
|
||||
"pako": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@cosmjs/crypto": {
|
||||
"version": "0.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.31.0.tgz",
|
||||
@@ -1529,6 +1549,66 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-arm64": {
|
||||
"version": "13.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.3.0.tgz",
|
||||
"integrity": "sha512-DmIQCNq6JtccLPPBzf0dgh2vzMWt5wjxbP71pCi5EWpWYE3MsP6FcRXi4MlAmFNDQOfcFXR2r7kBeG1LpZUh1w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-x64": {
|
||||
"version": "13.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.3.0.tgz",
|
||||
"integrity": "sha512-oQoqFa88OGgwnYlnAGHVct618FRI/749se0N3S8t9Bzdv5CRbscnO0RcX901+YnNK4Q6yeiizfgO3b7kogtsZg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-gnu": {
|
||||
"version": "13.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.3.0.tgz",
|
||||
"integrity": "sha512-Wzz2p/WqAJUqTVoLo6H18WMeAXo3i+9DkPDae4oQG8LMloJ3if4NEZTnOnTUlro6cq+S/W4pTGa97nWTrOjbGw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-musl": {
|
||||
"version": "13.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.3.0.tgz",
|
||||
"integrity": "sha512-xPVrIQOQo9WXJYgmoTlMnAD/HlR/1e1ZIWGbwIzEirXBVBqMARUulBEIKdC19zuvoJ477qZJgBDCKtKEykCpyQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-gnu": {
|
||||
"version": "13.3.0",
|
||||
"cpu": [
|
||||
@@ -1557,6 +1637,51 @@
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-arm64-msvc": {
|
||||
"version": "13.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.3.0.tgz",
|
||||
"integrity": "sha512-OeHiA6YEvndxT46g+rzFK/MQTfftKxJmzslERMu9LDdC6Kez0bdrgEYed5eXFK2Z1viKZJCGRlhd06rBusyztA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-ia32-msvc": {
|
||||
"version": "13.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.3.0.tgz",
|
||||
"integrity": "sha512-4aB7K9mcVK1lYEzpOpqWrXHEZympU3oK65fnNcY1Qc4HLJFLJj8AViuqQd4jjjPNuV4sl8jAwTz3gN5VNGWB7w==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-x64-msvc": {
|
||||
"version": "13.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.3.0.tgz",
|
||||
"integrity": "sha512-Reer6rkLLcoOvB0dd66+Y7WrWVFH7sEEkF/4bJCIfsSKnTStTYaHtwIJAwbqnt9I392Tqvku0KkoqZOryWV9LQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/hashes": {
|
||||
"version": "1.0.0",
|
||||
"license": "MIT"
|
||||
@@ -6659,6 +6784,11 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
|
||||
"integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="
|
||||
},
|
||||
"node_modules/parent-module": {
|
||||
"version": "1.0.1",
|
||||
"license": "MIT",
|
||||
@@ -7857,6 +7987,11 @@
|
||||
"node": ">=10.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vanilla-jsoneditor": {
|
||||
"version": "0.17.8",
|
||||
"resolved": "https://registry.npmjs.org/vanilla-jsoneditor/-/vanilla-jsoneditor-0.17.8.tgz",
|
||||
"integrity": "sha512-DP9GP/IBQjYOnC820CYoFuXs3vgrL+zdGGp1X83qFirSkRWPeP+6zB/14a0LYhvomQNNezes5Gwel89MKc4Qbg=="
|
||||
},
|
||||
"node_modules/w3c-xmlserializer": {
|
||||
"version": "4.0.0",
|
||||
"dev": true,
|
||||
|
||||
+3
-1
@@ -11,6 +11,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@cosmjs/amino": "^0.31.0",
|
||||
"@cosmjs/cosmwasm-stargate": "^0.31.0",
|
||||
"@cosmjs/crypto": "^0.31.0",
|
||||
"@cosmjs/encoding": "^0.31.0",
|
||||
"@cosmjs/ledger-amino": "^0.31.0",
|
||||
@@ -26,7 +27,8 @@
|
||||
"next": "^13.3.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-select": "^5.7.2"
|
||||
"react-select": "^5.7.2",
|
||||
"vanilla-jsoneditor": "^0.17.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
|
||||
@@ -9,6 +9,12 @@ import {
|
||||
MsgUndelegate,
|
||||
} from "cosmjs-types/cosmos/staking/v1beta1/tx";
|
||||
import { MsgCreateVestingAccount } from "cosmjs-types/cosmos/vesting/v1beta1/tx";
|
||||
import {
|
||||
MsgExecuteContract,
|
||||
MsgInstantiateContract,
|
||||
MsgInstantiateContract2,
|
||||
MsgMigrateContract,
|
||||
} from "cosmjs-types/cosmwasm/wasm/v1/tx";
|
||||
import { MsgTransfer } from "cosmjs-types/ibc/applications/transfer/v1/tx";
|
||||
|
||||
export const MsgTypeUrls = {
|
||||
@@ -20,6 +26,10 @@ export const MsgTypeUrls = {
|
||||
Undelegate: "/cosmos.staking.v1beta1.MsgUndelegate",
|
||||
CreateVestingAccount: "/cosmos.vesting.v1beta1.MsgCreateVestingAccount",
|
||||
Transfer: "/ibc.applications.transfer.v1.MsgTransfer",
|
||||
Execute: "/cosmwasm.wasm.v1.MsgExecuteContract",
|
||||
Instantiate: "/cosmwasm.wasm.v1.MsgInstantiateContract",
|
||||
Instantiate2: "/cosmwasm.wasm.v1.MsgInstantiateContract2",
|
||||
Migrate: "/cosmwasm.wasm.v1.MsgMigrateContract",
|
||||
} as const;
|
||||
|
||||
export type MsgTypeUrl = (typeof MsgTypeUrls)[keyof typeof MsgTypeUrls];
|
||||
@@ -33,4 +43,8 @@ export const MsgCodecs = {
|
||||
[MsgTypeUrls.Undelegate]: MsgUndelegate,
|
||||
[MsgTypeUrls.CreateVestingAccount]: MsgCreateVestingAccount,
|
||||
[MsgTypeUrls.Transfer]: MsgTransfer,
|
||||
[MsgTypeUrls.Execute]: MsgExecuteContract,
|
||||
[MsgTypeUrls.Instantiate]: MsgInstantiateContract,
|
||||
[MsgTypeUrls.Instantiate2]: MsgInstantiateContract2,
|
||||
[MsgTypeUrls.Migrate]: MsgMigrateContract,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user