Skip to content

Commit

Permalink
Merge pull request #98 from alleslabs/feat/clear-admin
Browse files Browse the repository at this point in the history
feat: add clear admin menuitem
  • Loading branch information
evilpeach committed Jan 26, 2023
2 parents 33a351f + 317aa84 commit 4a0fa0d
Show file tree
Hide file tree
Showing 22 changed files with 652 additions and 148 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Features

- [#98](https://github.com/alleslabs/celatone-frontend/pull/98) Add migrate, update admin, clear admin menu on contract list and detail
- [#121](https://github.com/alleslabs/celatone-frontend/pull/121) Fix code snippet for query axios
- [#102](https://github.com/alleslabs/celatone-frontend/pull/102) Add quick menu in overview and add highlighted in left sidebar
- [#125](https://github.com/alleslabs/celatone-frontend/pull/125) Add connect wallet alert in instantiate page
- [#126](https://github.com/alleslabs/celatone-frontend/pull/126) Add port id copier for IBC port id
Expand Down
72 changes: 72 additions & 0 deletions src/lib/app-fns/tx/clearAdmin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Icon } from "@chakra-ui/react";
import type {
SigningCosmWasmClient,
ChangeAdminResult,
} 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 { TxStreamPhase } from "lib/types";
import type { TxResultRendering, ContractAddr } from "lib/types";
import { formatUFee } from "lib/utils";

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

interface ClearAdminTxParams {
address: string;
contractAddress: ContractAddr;
fee: StdFee;
memo?: string;
client: SigningCosmWasmClient;
onTxSucceed?: (txHash: string) => void;
}

export const clearAdminTx = ({
address,
contractAddress,
fee,
memo,
client,
onTxSucceed,
}: ClearAdminTxParams): Observable<TxResultRendering> => {
return pipe(
sendingTx(fee),
postTx<ChangeAdminResult>({
postFn: () => client.clearAdmin(address, contractAddress, fee, memo),
}),
({ 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: "Transaction Complete",
headerIcon: (
<Icon as={MdCheckCircle} color="success.main" boxSize={6} />
),
},
} as TxResultRendering;
}
)().pipe(catchTxError());
};
34 changes: 34 additions & 0 deletions src/lib/app-provider/tx/clearAdmin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useWallet } from "@cosmos-kit/react";
import { useCallback } from "react";

import { useFabricateFee } from "../hooks";
import { clearAdminTx } from "lib/app-fns/tx/clearAdmin";
import { CLEAR_ADMIN_GAS } from "lib/data";
import type { ContractAddr } from "lib/types";

export interface ClearAdminStreamParams {
onTxSucceed?: (txHash: string) => void;
}

export const useClearAdminTx = (contractAddress: ContractAddr) => {
const { address, getCosmWasmClient } = useWallet();
const fabricateFee = useFabricateFee();
const clearAdminFee = fabricateFee(CLEAR_ADMIN_GAS);

return useCallback(
async ({ onTxSucceed }: ClearAdminStreamParams) => {
const client = await getCosmWasmClient();
if (!address || !client)
throw new Error("Please check your wallet connection.");

return clearAdminTx({
address,
contractAddress,
fee: clearAdminFee,
client,
onTxSucceed,
});
},
[address, clearAdminFee, contractAddress, getCosmWasmClient]
);
};
3 changes: 3 additions & 0 deletions src/lib/app-provider/tx/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export * from "./upload";
export * from "./resend";
export * from "./clearAdmin";
export * from "./execute";
export * from "./instantiate";
102 changes: 102 additions & 0 deletions src/lib/components/button/AdminButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import {
Button,
chakra,
Icon,
Menu,
MenuButton,
MenuItem,
MenuList,
Tooltip,
} from "@chakra-ui/react";
import { useWallet } from "@cosmos-kit/react";
import {
MdKeyboardArrowDown,
MdPerson,
MdPersonRemove,
MdReadMore,
} from "react-icons/md";

import { ClearAdminContract } from "../modal/contract/ClearAdminContract";
import { useInternalNavigate } from "lib/app-provider";
import type { ContractAddr, HumanAddr, Option } from "lib/types";

const StyledMenuItem = chakra(MenuItem, {
baseStyle: {
fontSize: "14px",
},
});

const StyledIcon = chakra(Icon, {
baseStyle: {
boxSize: "4",
display: "flex",
alignItems: "center",
},
});

interface AdminButtonProps {
contractAddress: ContractAddr;
admin: Option<HumanAddr | ContractAddr>;
}

export const AdminButton = ({ contractAddress, admin }: AdminButtonProps) => {
const { address } = useWallet();
const navigate = useInternalNavigate();

const isAdmin = !!address && address === admin;
return (
<Menu>
<Tooltip
hasArrow
label="You don't have admin access to this contract."
placement="top"
bg="primary.dark"
arrowSize={8}
isDisabled={isAdmin}
>
<MenuButton
variant="outline-gray"
as={Button}
isDisabled={!isAdmin}
rightIcon={<Icon as={MdKeyboardArrowDown} boxSize="18px" />}
>
Admin
</MenuButton>
</Tooltip>
<MenuList>
<StyledMenuItem
icon={<StyledIcon as={MdReadMore} color="gray.600" />}
onClick={() => {
navigate({
pathname: "/migrate",
query: { contract: contractAddress },
});
}}
>
Migrate
</StyledMenuItem>
<StyledMenuItem
icon={<StyledIcon as={MdPerson} color="gray.600" />}
onClick={() => {
navigate({
pathname: "/admin",
query: { contract: contractAddress },
});
}}
>
Update Admin
</StyledMenuItem>
<ClearAdminContract
contractAddress={contractAddress}
triggerElement={
<StyledMenuItem
icon={<StyledIcon as={MdPersonRemove} color="gray.600" />}
>
Clear Admin
</StyledMenuItem>
}
/>
</MenuList>
</Menu>
);
};
2 changes: 2 additions & 0 deletions src/lib/components/button/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export * from "./BackButton";
export * from "./ConnectWallet";
export * from "./InstantiateButton";
export * from "./ShowMoreButton";
export * from "./AdminButton";
11 changes: 6 additions & 5 deletions src/lib/components/modal/ActionModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ export interface ActionModalProps {
children?: ReactNode;
mainBtnTitle?: string;
mainAction: () => void;
mainVariant?: string;
disabledMain?: boolean;
otherBtnTitle?: string;
otherAction?: () => void;
otherVariant?: string;
noHeaderBorder?: boolean;
noCloseButton?: boolean;
}
Expand All @@ -44,9 +46,11 @@ export function ActionModal({
children,
mainBtnTitle = "Proceed",
mainAction,
mainVariant = "primary",
disabledMain = false,
otherBtnTitle = "Cancel",
otherAction,
otherVariant = "outline-primary",
noHeaderBorder = false,
noCloseButton = false,
}: ActionModalProps) {
Expand Down Expand Up @@ -93,15 +97,12 @@ export function ActionModal({
<Button
w="200px"
onClick={handleOnMain}
variant={mainVariant}
isDisabled={disabledMain}
>
{mainBtnTitle}
</Button>
<Button
w="200px"
variant="outline-primary"
onClick={handleOnOther}
>
<Button w="200px" variant={otherVariant} onClick={handleOnOther}>
{otherBtnTitle}
</Button>
</Flex>
Expand Down
45 changes: 45 additions & 0 deletions src/lib/components/modal/contract/ClearAdminContract.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Text } from "@chakra-ui/react";
import { useCallback } from "react";
import { MdDeleteForever } from "react-icons/md";

import { ActionModal } from "../ActionModal";
import { useClearAdminTx } from "lib/app-provider";
import { useTxBroadcast } from "lib/providers/tx-broadcast";
import type { ContractAddr } from "lib/types";

interface ClearAdminContractProps {
contractAddress: ContractAddr;
triggerElement: JSX.Element;
}

export const ClearAdminContract = ({
contractAddress,
triggerElement,
}: ClearAdminContractProps) => {
const { broadcast } = useTxBroadcast();
const clearAdminTx = useClearAdminTx(contractAddress);

const proceed = useCallback(async () => {
const stream = await clearAdminTx({ onTxSucceed: () => {} });
if (stream) broadcast(stream);
}, [broadcast, clearAdminTx]);

return (
<ActionModal
title="You'll no longer have admin access"
icon={MdDeleteForever}
iconColor="error.light"
trigger={triggerElement}
mainBtnTitle="Yes, clear it"
mainAction={proceed}
mainVariant="error"
otherBtnTitle="No, keep it"
otherVariant="ghost-primary"
>
<Text>
Clearing the admin is a permanent action. You&apos;ll not be able to
reassign an admin and migrations will no longer be possible.
</Text>
</ActionModal>
);
};
4 changes: 3 additions & 1 deletion src/lib/data/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ export const DEFAULT_ADDRESS = "default-address";

export const MAX_FILE_SIZE = 800_000;

export const MICRO = 1000000;
export const CLEAR_ADMIN_GAS = 50_000;

export const MICRO = 1_000_000;

export const typeUrlDict = {
[MsgType.STORE_CODE]: "/cosmwasm.wasm.v1.MsgStoreCode",
Expand Down
16 changes: 16 additions & 0 deletions src/lib/data/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,19 @@ export const getInstantiateDetailByContractQueryDocument = graphql(`
}
`);

export const getAdminByContractAddressesQueryDocument = graphql(`
query getAdminByContractAddressesQueryDocument(
$contractAddresses: [String!]!
) {
contracts(where: { address: { _in: $contractAddresses } }) {
address
admin: account {
address
}
}
}
`);

export const getExecuteTxsByContractAddress = graphql(`
query getExecuteTxsByContractAddress(
$contractAddress: String!
Expand Down Expand Up @@ -278,6 +291,9 @@ export const getContractListByCodeId = graphql(`
) {
address
label
admin: account {
address
}
init_by: contract_histories(
order_by: { block: { timestamp: asc } }
limit: 1
Expand Down
Loading

2 comments on commit 4a0fa0d

@vercel
Copy link

@vercel vercel bot commented on 4a0fa0d Jan 26, 2023

Choose a reason for hiding this comment

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

@vercel
Copy link

@vercel vercel bot commented on 4a0fa0d Jan 26, 2023

Choose a reason for hiding this comment

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

Please sign in to comment.