Skip to content

Commit

Permalink
Merge pull request #120 from alleslabs/component/migrate-step2
Browse files Browse the repository at this point in the history
Component/migrate step2 [merge #108 first]
  • Loading branch information
songwongtp committed Jan 27, 2023
2 parents a0c5fec + 65d1622 commit b6261a2
Show file tree
Hide file tree
Showing 14 changed files with 540 additions and 111 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Features

- [#120](https://github.com/alleslabs/celatone-frontend/pull/120) Add simulate migrate fee and the final migration step
- [#108](https://github.com/alleslabs/celatone-frontend/pull/108) Add migrate options on migrate page and upload new code for migration
- [#113](https://github.com/alleslabs/celatone-frontend/pull/113) Update admin page ui and wireup
- [#98](https://github.com/alleslabs/celatone-frontend/pull/98) Add migrate, update admin, clear admin menu on contract list and detail
Expand Down
85 changes: 85 additions & 0 deletions src/lib/app-fns/tx/migrate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { Icon } from "@chakra-ui/react";
import type {
MigrateResult,
SigningCosmWasmClient,
} from "@cosmjs/cosmwasm-stargate";
import type { StdFee } from "@cosmjs/stargate";
import { pipe } from "@rx-stream/pipe";
import { MdCheckCircle } from "react-icons/md";
import type { Observable } from "rxjs";

import { ExplorerLink } from "lib/components/ExplorerLink";
import type { ContractAddr, TxResultRendering } from "lib/types";
import { TxStreamPhase } from "lib/types";
import { formatUFee } from "lib/utils";

import { catchTxError } from "./common/catchTxError";
import { postTx } from "./common/post";
import { sendingTx } from "./common/sending";

interface MigrateTxParams {
sender: string;
contractAddress: ContractAddr;
codeId: number;
migrateMsg: object;
fee: StdFee;
client: SigningCosmWasmClient;
onTxSucceed?: (txHash: string) => void;
onTxFailed?: () => void;
}

export const migrateContractTx = ({
sender,
contractAddress,
codeId,
migrateMsg,
fee,
client,
onTxSucceed,
onTxFailed,
}: MigrateTxParams): Observable<TxResultRendering> => {
return pipe(
sendingTx(fee),
postTx<MigrateResult>({
postFn: () =>
client.migrate(
sender,
contractAddress,
codeId,
migrateMsg,
fee,
undefined
),
}),
({ value: txInfo }) => {
onTxSucceed?.(txInfo.transactionHash);
return {
value: null,
phase: TxStreamPhase.SUCCEED,
receipts: [
{
title: "Tx Hash",
value: txInfo.transactionHash,
html: (
<ExplorerLink type="tx_hash" value={txInfo.transactionHash} />
),
},
{
title: "Tx Fee",
value: `${formatUFee(
txInfo.events.find((e) => e.type === "tx")?.attributes[0].value ??
"0u"
)}`,
},
],
receiptInfo: {
header: "Migration Completed",
headerIcon: (
<Icon as={MdCheckCircle} fontSize="24px" color="success.main" />
),
},
actionVariant: "migrate",
} as TxResultRendering;
}
)().pipe(catchTxError(onTxFailed));
};
47 changes: 47 additions & 0 deletions src/lib/app-provider/tx/migrate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import type { StdFee } from "@cosmjs/stargate";
import { useWallet } from "@cosmos-kit/react";
import { useCallback } from "react";

import { migrateContractTx } from "lib/app-fns/tx/migrate";
import type { ContractAddr, Option } from "lib/types";

export interface MigrateStreamParams {
contractAddress: ContractAddr;
codeId: number;
migrateMsg: object;
estimatedFee: Option<StdFee>;
onTxSucceed?: (txHash: string) => void;
onTxFailed?: () => void;
}

export const useMigrateTx = () => {
const { address, getCosmWasmClient } = useWallet();

return useCallback(
async ({
contractAddress,
codeId,
migrateMsg,
estimatedFee,
onTxSucceed,
onTxFailed,
}: MigrateStreamParams) => {
const client = await getCosmWasmClient();
if (!address || !client)
throw new Error("Please check your wallet connection.");
if (!estimatedFee) return null;

return migrateContractTx({
sender: address,
contractAddress,
codeId,
migrateMsg,
fee: estimatedFee,
client,
onTxSucceed,
onTxFailed,
});
},
[address, getCosmWasmClient]
);
};
76 changes: 76 additions & 0 deletions src/lib/components/CodeSelectSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { Flex, Radio, RadioGroup } from "@chakra-ui/react";
import { useState } from "react";
import type { Control, FieldPath, FieldValues } from "react-hook-form";

import { CodeSelect } from "lib/pages/instantiate/component";
import type { Option } from "lib/types";

import type { FormStatus } from "./forms";
import { ControllerInput } from "./forms";

interface CodeSelectSectionProps<T extends FieldValues> {
codeId: string;
name: FieldPath<T>;
control: Control<T>;
error: Option<string>;
onCodeSelect: (codeId: string) => void;
status: FormStatus;
}

export const CodeSelectSection = <T extends FieldValues>({
codeId,
name,
control,
error,
onCodeSelect,
status,
}: CodeSelectSectionProps<T>) => {
const [method, setMethod] = useState<"select-existing" | "fill-manually">(
"select-existing"
);

return (
<>
<RadioGroup
onChange={(nextVal: "select-existing" | "fill-manually") =>
setMethod(nextVal)
}
value={method}
w="100%"
>
<Flex justify="space-around">
<Radio value="select-existing" size="lg">
Select from your code
</Radio>
<Radio value="fill-manually" size="lg">
Fill Code ID manually
</Radio>
</Flex>
</RadioGroup>
<form style={{ width: "100%" }}>
{method === "select-existing" ? (
<CodeSelect
mt="16px"
mb="32px"
onCodeSelect={onCodeSelect}
codeId={codeId}
status={status}
/>
) : (
<ControllerInput
name={name}
control={control}
type="number"
status={status}
error={error}
label="Code ID"
helperText="Input existing Code ID manually"
variant="floating"
my="32px"
rules={{ required: "Code ID is required" }}
/>
)}
</form>
</>
);
};
28 changes: 17 additions & 11 deletions src/lib/components/forms/ControllerInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {
FormHelperText,
FormLabel,
Input,
InputGroup,
InputRightElement,
Text,
} from "@chakra-ui/react";
import type {
Expand All @@ -15,7 +17,7 @@ import type {
import { useWatch, useController } from "react-hook-form";

import type { FormStatus } from "./FormStatus";
import { getResponseMsg } from "./FormStatus";
import { getStatusIcon, getResponseMsg } from "./FormStatus";
import type { TextInputProps } from "./TextInput";

interface ControllerInputProps<T extends FieldValues>
Expand Down Expand Up @@ -58,7 +60,7 @@ export const ControllerInput = <T extends FieldValues>({
return (
<FormControl
size={size}
isInvalid={isError}
isInvalid={!!error || status?.state === "error"}
isRequired={isRequired}
{...componentProps}
{...field}
Expand All @@ -68,15 +70,19 @@ export const ControllerInput = <T extends FieldValues>({
{label}
</FormLabel>
)}
<Input
size={size}
placeholder={placeholder}
type={type}
value={watcher}
onChange={field.onChange}
maxLength={maxLength}
/>
{/* TODO: add status */}
<InputGroup>
<Input
size={size}
placeholder={placeholder}
type={type}
value={watcher}
onChange={field.onChange}
maxLength={maxLength}
/>
<InputRightElement h="full">
{status && getStatusIcon(status.state)}
</InputRightElement>
</InputGroup>
{isError ? (
<FormErrorMessage className="error-text">{error}</FormErrorMessage>
) : (
Expand Down
1 change: 1 addition & 0 deletions src/lib/components/modal/tx/ButtonSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export const ButtonSection = ({
Proceed to Migrate
</Button>
);
case "migrate":
case "update-admin":
return (
<>
Expand Down
1 change: 1 addition & 0 deletions src/lib/data/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export const typeUrlDict = {
[MsgType.STORE_CODE]: "/cosmwasm.wasm.v1.MsgStoreCode",
[MsgType.INSTANTIATE]: "/cosmwasm.wasm.v1.MsgInstantiateContract",
[MsgType.EXECUTE]: "/cosmwasm.wasm.v1.MsgExecuteContract",
[MsgType.MIGRATE]: "/cosmwasm.wasm.v1.MsgMigrateContract",
[MsgType.UPDATE_ADMIN]: "/cosmwasm.wasm.v1.MsgUpdateAdmin",
};

Expand Down
78 changes: 45 additions & 33 deletions src/lib/pages/instantiate/component/code-select/CodeSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { FlexProps } from "@chakra-ui/react";
import { Flex, Text } from "@chakra-ui/react";

import type { FormStatus } from "lib/components/forms";
import { UploadIcon } from "lib/components/icon/UploadIcon";
import { useCodeStore } from "lib/hooks/store";
import { useUserKey } from "lib/hooks/useUserKey";
Expand All @@ -10,53 +11,64 @@ import { CodeSelectModalButton } from "./CodeSelectModalButton";
interface CodeSelectProps extends Omit<FlexProps, "onSelect"> {
onCodeSelect: (code: string) => void;
codeId: string;
status: FormStatus;
}

export const CodeSelect = ({
onCodeSelect,
codeId,
status,
...componentProps
}: CodeSelectProps) => {
const { codeInfo } = useCodeStore();
const userKey = useUserKey();
const description = codeInfo?.[userKey]?.[Number(codeId)]?.description;

const isError = status.state === "error";
return (
<Flex
align="center"
p="16px"
gap="16px"
w="100%"
bgColor="gray.900"
borderRadius="4px"
{...componentProps}
>
<UploadIcon variant={codeId ? "primary" : "muted"} />
{codeId ? (
<Flex direction="column" w="60%">
<Text
variant="body1"
color="text.main"
fontWeight={500}
overflow="hidden"
whiteSpace="nowrap"
textOverflow="ellipsis"
>
{description ?? "No description"}
</Text>
<Text variant="body2" color="text.dark">
Code ID {codeId}
<Flex direction="column" {...componentProps}>
<Flex
align="center"
p="16px"
gap="16px"
w="100%"
bgColor="gray.900"
borderRadius="4px"
borderWidth="1px"
borderColor={isError ? "error.main" : "gray.900"}
>
<UploadIcon variant={codeId ? "primary" : "muted"} />
{codeId ? (
<Flex direction="column" w="60%">
<Text
variant="body1"
color="text.main"
fontWeight={500}
overflow="hidden"
whiteSpace="nowrap"
textOverflow="ellipsis"
>
{description ?? "No description"}
</Text>
<Text variant="body2" color="text.dark">
Code ID {codeId}
</Text>
</Flex>
) : (
<Text variant="body1" color="text.main" fontWeight={500}>
Please select code
</Text>
</Flex>
) : (
<Text variant="body1" color="text.main" fontWeight={500}>
Please select code
)}
<CodeSelectModalButton
onCodeSelect={onCodeSelect}
buttonText={codeId ? "Change" : "Select Code"}
/>
</Flex>
{isError && (
<Text variant="body3" color="error.main" mt={1} ml={3}>
{status.message}
</Text>
)}

<CodeSelectModalButton
onCodeSelect={onCodeSelect}
buttonText={codeId ? "Change" : "Select Code"}
/>
</Flex>
);
};
Loading

0 comments on commit b6261a2

Please sign in to comment.