Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support EncodeObj #129

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions components/dataViews/TransactionInfo/TxEncodeObjectDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { EncodeObject } from "@cosmjs/proto-signing";

interface TxEncodeObjectDetailsProps {
readonly msg: EncodeObject;
}

const TxEncodeObjectDetails = ({ msg }: TxEncodeObjectDetailsProps) => (
<>
<li>
<h3>Custom EncodeObject</h3>
</li>
<li>
<label>Type URL:</label>
<div>{msg.typeUrl}</div>
</li>
<li>
<label>Value:</label>
<div>{JSON.stringify(msg.value, null, 2)}</div>
</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;
}
`}</style>
</>
);

export default TxEncodeObjectDetails;
3 changes: 3 additions & 0 deletions components/dataViews/TransactionInfo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import {
isTxMsgSend,
isTxMsgSetWithdrawAddress,
isTxMsgUndelegate,
isUnknownEncodeObject,
} from "../../../lib/txMsgHelpers";
import { DbTransaction } from "../../../types";
import StackableContainer from "../../layout/StackableContainer";
import TxEncodeObjectDetails from "./TxEncodeObjectDetails";
import TxMsgClaimRewardsDetails from "./TxMsgClaimRewardsDetails";
import TxMsgDelegateDetails from "./TxMsgDelegateDetails";
import TxMsgRedelegateDetails from "./TxMsgRedelegateDetails";
Expand Down Expand Up @@ -59,6 +61,7 @@ const TransactionInfo = ({ tx }: Props) => {
{isTxMsgSetWithdrawAddress(msg) ? (
<TxMsgSetWithdrawAddressDetails msg={msg} />
) : null}
{isUnknownEncodeObject(msg) ? <TxEncodeObjectDetails msg={msg} /> : null}
</StackableContainer>
))}
</StackableContainer>
Expand Down
108 changes: 108 additions & 0 deletions components/forms/CreateTxForm/MsgForm/EncodeObjectForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { EncodeObject } from "@cosmjs/proto-signing";
import { assert } from "@cosmjs/utils";
import { useEffect, useState } from "react";
import { MsgGetter } from "..";
import { useAppContext } from "../../../../context/AppContext";
import { isEncodeObject } from "../../../../lib/txMsgHelpers";
import { TxMsg } from "../../../../types/txMsg";
import Input from "../../../inputs/Input";
import StackableContainer from "../../../layout/StackableContainer";

interface EncodeObjectFormProps {
readonly setMsgGetter: (msgGetter: MsgGetter) => void;
readonly deleteMsg: () => void;
}

const EncodeObjectForm = ({ setMsgGetter, deleteMsg }: EncodeObjectFormProps) => {
const { state } = useAppContext();
assert(state.chain.addressPrefix, "addressPrefix missing");

const [msgTypeUrl, setMsgTypeUrl] = useState("");
const [msgValue, setMsgValue] = useState("");

const [msgTypeUrlError, setMsgTypeUrlError] = useState("");
const [msgValueError, setMsgValueError] = useState("");

useEffect(() => {
setMsgTypeUrlError("");
setMsgValueError("");

const isMsgValid = (msg: TxMsg): msg is EncodeObject => {
if (!msgTypeUrl) {
setMsgTypeUrlError("Type URL is required");
return false;
}

if (!msgValue) {
setMsgValueError("Value is required");
return false;
}

try {
JSON.parse(msgValue);
} catch {
setMsgValueError("Value must be valid JSON");
return false;
}

return isEncodeObject(msg);
};

const parsedMsgValue = (() => {
try {
return JSON.parse(msgValue);
} catch {
return {};
}
})();

const msg: EncodeObject = { typeUrl: msgTypeUrl, value: parsedMsgValue };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value: parsedMsgValue here is the tricky part: here we need the telescope-specific format. E.g. 64bit integers need to be represented as a Long type. Binary data needs to be Uint8Array. There is no standardized way to get this from JSON without message specific JSON -> JavaScript importers.


setMsgGetter({ isMsgValid, msg });
}, [msgTypeUrl, msgValue, setMsgGetter]);

return (
<StackableContainer lessPadding lessMargin>
<button className="remove" onClick={() => deleteMsg()}>
</button>
<h2>Custom EncodeObject</h2>
<div className="form-item">
<Input
label="Type URL"
name="msg-type-url"
value={msgTypeUrl}
onChange={({ target }) => setMsgTypeUrl(target.value)}
error={msgTypeUrlError}
/>
</div>
<div className="form-item">
<Input
label="Value"
name="msg-value"
value={msgValue}
onChange={({ target }) => setMsgValue(target.value)}
error={msgValueError}
/>
</div>
<style jsx>{`
.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 EncodeObjectForm;
36 changes: 17 additions & 19 deletions components/forms/CreateTxForm/MsgForm/MsgClaimRewardsForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,30 +26,28 @@ const MsgClaimRewardsForm = ({
const [validatorAddressError, setValidatorAddressError] = useState("");

useEffect(() => {
try {
setValidatorAddressError("");
setValidatorAddressError("");

const isMsgValid = (msg: TxMsg): msg is TxMsgClaimRewards => {
assert(state.chain.addressPrefix, "addressPrefix missing");
const isMsgValid = (msg: TxMsg): msg is TxMsgClaimRewards => {
assert(state.chain.addressPrefix, "addressPrefix missing");

const addressErrorMsg = checkAddress(validatorAddress, state.chain.addressPrefix);
if (addressErrorMsg) {
setValidatorAddressError(
`Invalid address for network ${state.chain.chainId}: ${addressErrorMsg}`,
);
return false;
}
const addressErrorMsg = checkAddress(validatorAddress, state.chain.addressPrefix);
if (addressErrorMsg) {
setValidatorAddressError(
`Invalid address for network ${state.chain.chainId}: ${addressErrorMsg}`,
);
return false;
}

return isTxMsgClaimRewards(msg);
};
return isTxMsgClaimRewards(msg);
};

const msg: TxMsgClaimRewards = {
typeUrl: "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward",
value: { delegatorAddress, validatorAddress },
};
const msg: TxMsgClaimRewards = {
typeUrl: "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward",
value: { delegatorAddress, validatorAddress },
};

setMsgGetter({ isMsgValid, msg });
} catch {}
setMsgGetter({ isMsgValid, msg });
}, [
delegatorAddress,
setMsgGetter,
Expand Down
36 changes: 17 additions & 19 deletions components/forms/CreateTxForm/MsgForm/MsgSetWithdrawAddressForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,30 +26,28 @@ const MsgSetWithdrawAddressForm = ({
const [withdrawAddressError, setWithdrawAddressError] = useState("");

useEffect(() => {
try {
setWithdrawAddressError("");
setWithdrawAddressError("");

const isMsgValid = (msg: TxMsg): msg is TxMsgSetWithdrawAddress => {
assert(state.chain.addressPrefix, "addressPrefix missing");
const isMsgValid = (msg: TxMsg): msg is TxMsgSetWithdrawAddress => {
assert(state.chain.addressPrefix, "addressPrefix missing");

const addressErrorMsg = checkAddress(withdrawAddress, state.chain.addressPrefix);
if (addressErrorMsg) {
setWithdrawAddressError(
`Invalid address for network ${state.chain.chainId}: ${addressErrorMsg}`,
);
return false;
}
const addressErrorMsg = checkAddress(withdrawAddress, state.chain.addressPrefix);
if (addressErrorMsg) {
setWithdrawAddressError(
`Invalid address for network ${state.chain.chainId}: ${addressErrorMsg}`,
);
return false;
}

return isTxMsgSetWithdrawAddress(msg);
};
return isTxMsgSetWithdrawAddress(msg);
};

const msg: TxMsgSetWithdrawAddress = {
typeUrl: "/cosmos.distribution.v1beta1.MsgSetWithdrawAddress",
value: { delegatorAddress, withdrawAddress },
};
const msg: TxMsgSetWithdrawAddress = {
typeUrl: "/cosmos.distribution.v1beta1.MsgSetWithdrawAddress",
value: { delegatorAddress, withdrawAddress },
};

setMsgGetter({ isMsgValid, msg });
} catch {}
setMsgGetter({ isMsgValid, msg });
}, [
delegatorAddress,
setMsgGetter,
Expand Down
3 changes: 3 additions & 0 deletions components/forms/CreateTxForm/MsgForm/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { MsgGetter } from "..";
import { MsgType } from "../../../../types/txMsg";
import EncodeObjectForm from "./EncodeObjectForm";
import MsgClaimRewardsForm from "./MsgClaimRewardsForm";
import MsgDelegateForm from "./MsgDelegateForm";
import MsgRedelegateForm from "./MsgRedelegateForm";
Expand Down Expand Up @@ -28,6 +29,8 @@ const MsgForm = ({ msgType, senderAddress, ...restProps }: MsgFormProps) => {
return <MsgClaimRewardsForm delegatorAddress={senderAddress} {...restProps} />;
case "setWithdrawAddress":
return <MsgSetWithdrawAddressForm delegatorAddress={senderAddress} {...restProps} />;
case "encodeObject":
return <EncodeObjectForm {...restProps} />;
default:
return null;
}
Expand Down
1 change: 1 addition & 0 deletions components/forms/CreateTxForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ const CreateTxForm = ({ router, senderAddress, accountOnChain }: CreateTxFormPro
label="Add MsgSetWithdrawAddress"
onClick={() => addMsgType("setWithdrawAddress")}
/>
<Button label="Add custom EncodeObject" onClick={() => addMsgType("encodeObject")} />
</StackableContainer>
<Button
label="Create Transaction"
Expand Down
28 changes: 22 additions & 6 deletions lib/txMsgHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
TxMsgUndelegate,
} from "../types/txMsg";

const isTxMsgSend = (msg: TxMsg | EncodeObject): msg is TxMsgSend =>
const isTxMsgSend = (msg: TxMsg): msg is TxMsgSend =>
msg.typeUrl === "/cosmos.bank.v1beta1.MsgSend" &&
"value" in msg &&
"fromAddress" in msg.value &&
Expand All @@ -20,7 +20,7 @@ const isTxMsgSend = (msg: TxMsg | EncodeObject): msg is TxMsgSend =>
!!msg.value.toAddress &&
!!msg.value.amount.length;

const isTxMsgDelegate = (msg: TxMsg | EncodeObject): msg is TxMsgDelegate =>
const isTxMsgDelegate = (msg: TxMsg): msg is TxMsgDelegate =>
msg.typeUrl === "/cosmos.staking.v1beta1.MsgDelegate" &&
"value" in msg &&
"delegatorAddress" in msg.value &&
Expand All @@ -30,7 +30,7 @@ const isTxMsgDelegate = (msg: TxMsg | EncodeObject): msg is TxMsgDelegate =>
!!msg.value.validatorAddress &&
!!msg.value.amount;

const isTxMsgUndelegate = (msg: TxMsg | EncodeObject): msg is TxMsgUndelegate =>
const isTxMsgUndelegate = (msg: TxMsg): msg is TxMsgUndelegate =>
msg.typeUrl === "/cosmos.staking.v1beta1.MsgUndelegate" &&
"value" in msg &&
"delegatorAddress" in msg.value &&
Expand All @@ -40,7 +40,7 @@ const isTxMsgUndelegate = (msg: TxMsg | EncodeObject): msg is TxMsgUndelegate =>
!!msg.value.validatorAddress &&
!!msg.value.amount;

const isTxMsgRedelegate = (msg: TxMsg | EncodeObject): msg is TxMsgRedelegate =>
const isTxMsgRedelegate = (msg: TxMsg): msg is TxMsgRedelegate =>
msg.typeUrl === "/cosmos.staking.v1beta1.MsgBeginRedelegate" &&
"value" in msg &&
"delegatorAddress" in msg.value &&
Expand All @@ -52,22 +52,34 @@ const isTxMsgRedelegate = (msg: TxMsg | EncodeObject): msg is TxMsgRedelegate =>
!!msg.value.validatorDstAddress &&
!!msg.value.amount;

const isTxMsgClaimRewards = (msg: TxMsg | EncodeObject): msg is TxMsgClaimRewards =>
const isTxMsgClaimRewards = (msg: TxMsg): msg is TxMsgClaimRewards =>
msg.typeUrl === "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward" &&
"value" in msg &&
"delegatorAddress" in msg.value &&
"validatorAddress" in msg.value &&
!!msg.value.delegatorAddress &&
!!msg.value.validatorAddress;

const isTxMsgSetWithdrawAddress = (msg: TxMsg | EncodeObject): msg is TxMsgSetWithdrawAddress =>
const isTxMsgSetWithdrawAddress = (msg: TxMsg): msg is TxMsgSetWithdrawAddress =>
msg.typeUrl === "/cosmos.distribution.v1beta1.MsgSetWithdrawAddress" &&
"value" in msg &&
"delegatorAddress" in msg.value &&
"withdrawAddress" in msg.value &&
!!msg.value.delegatorAddress &&
!!msg.value.withdrawAddress;

const isEncodeObject = (msg: TxMsg): msg is EncodeObject =>
"typeUrl" in msg && "value" in msg && !!msg.typeUrl && !!msg.value;

const isUnknownEncodeObject = (msg: TxMsg): msg is EncodeObject =>
isEncodeObject(msg) &&
!isTxMsgSend(msg) &&
!isTxMsgDelegate(msg) &&
!isTxMsgUndelegate(msg) &&
!isTxMsgRedelegate(msg) &&
!isTxMsgClaimRewards(msg) &&
!isTxMsgSetWithdrawAddress(msg);

const gasOfMsg = (msgType: MsgType): number => {
switch (msgType) {
case "send":
Expand All @@ -82,6 +94,8 @@ const gasOfMsg = (msgType: MsgType): number => {
return 100_000;
case "setWithdrawAddress":
return 100_000;
case "encodeObject":
return 100_000;
default:
throw new Error("Unknown msg type");
}
Expand All @@ -100,6 +114,8 @@ export {
isTxMsgRedelegate,
isTxMsgClaimRewards,
isTxMsgSetWithdrawAddress,
isEncodeObject,
isUnknownEncodeObject,
gasOfMsg,
gasOfTx,
};
Loading