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

feat: add clear admin menuitem #98

Merged
merged 13 commits into from
Jan 26, 2023
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

- [#98](https://github.com/alleslabs/celatone-frontend/pull/98) Add clear admin menu in all contract list
- [#96](https://github.com/alleslabs/celatone-frontend/pull/96) Fix incorrect instantiated block height explorer link
- [#95](https://github.com/alleslabs/celatone-frontend/pull/95) Add network to url path
- [#89](https://github.com/alleslabs/celatone-frontend/pull/89) Update feedback link
Expand Down
73 changes: 73 additions & 0 deletions src/lib/app-fns/tx/clearAdmin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
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/formatter/denom";
songwongtp marked this conversation as resolved.
Show resolved Hide resolved

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} />
),
},
actionVariant: "clear_admin",
songwongtp marked this conversation as resolved.
Show resolved Hide resolved
} as TxResultRendering;
}
)().pipe(catchTxError());
};
35 changes: 35 additions & 0 deletions src/lib/app-provider/tx/clearAdmin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
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, Option } from "lib/types";

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

export const useClearAdminTx = (contractAddress: Option<ContractAddr>) => {
songwongtp marked this conversation as resolved.
Show resolved Hide resolved
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.");
if (!contractAddress) return null;
songwongtp marked this conversation as resolved.
Show resolved Hide resolved

return clearAdminTx({
address,
contractAddress,
fee: clearAdminFee,
client,
onTxSucceed,
});
},
[address, clearAdminFee, contractAddress, getCosmWasmClient]
);
};
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
46 changes: 46 additions & 0 deletions src/lib/components/modal/contract/ClearAdminContract.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Code, 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/tx/clearAdmin";
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>
&lsquo;Clear Admin&rsquo; will set the admin of the contract to{" "}
<Code>nil</Code> , while will disable further migrations/updates on this
contract.
</Text>
songwongtp marked this conversation as resolved.
Show resolved Hide resolved
</ActionModal>
);
};
4 changes: 3 additions & 1 deletion src/lib/data/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,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 @@ -117,6 +117,19 @@ export const getInstantiateDetailByContractQueryDocument = graphql(`
}
`);

export const getAdminByContractAddressesQueryDocument = graphql(`
query getAdminByContractAddressesQueryDocument(
$contractAddresses: [String!]
songwongtp marked this conversation as resolved.
Show resolved Hide resolved
) {
contracts(where: { address: { _in: $contractAddresses } }) {
address
account {
songwongtp marked this conversation as resolved.
Show resolved Hide resolved
address
}
}
}
`);

export const getExecuteTxsByContractAddress = graphql(`
query getExecuteTxsByContractAddress(
$contractAddress: String!
Expand Down Expand Up @@ -250,6 +263,9 @@ export const getContractListByCodeId = graphql(`
) {
address
label
account {
songwongtp marked this conversation as resolved.
Show resolved Hide resolved
address
}
transaction {
block {
timestamp
Expand Down
11 changes: 8 additions & 3 deletions src/lib/gql/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ const documents = {
types.GetInstantiatedListByUserQueryDocumentDocument,
'\n query getInstantiateDetailByContractQueryDocument($contractAddress: String!) {\n contracts_by_pk(address: $contractAddress) {\n init_msg\n transaction {\n hash\n }\n contract_proposals(\n where: {\n proposal: {\n type: { _in: ["InstantiateContract", "InstantiateContract2"] }\n }\n }\n limit: 1\n ) {\n proposal {\n id\n title\n }\n }\n }\n }\n':
types.GetInstantiateDetailByContractQueryDocumentDocument,
"\n query getAdminByContractAddressesQueryDocument(\n $contractAddresses: [String!]\n ) {\n contracts(where: { address: { _in: $contractAddresses } }) {\n address\n account {\n address\n }\n }\n }\n":
types.GetAdminByContractAddressesQueryDocumentDocument,
"\n query getExecuteTxsByContractAddress(\n $contractAddress: String!\n $offset: Int!\n $pageSize: Int!\n ) {\n contract_transactions(\n where: {\n contract: { address: { _eq: $contractAddress } }\n transaction: { is_execute: { _eq: true } }\n }\n order_by: { transaction: { block: { timestamp: desc } } }\n limit: $pageSize\n offset: $offset\n ) {\n transaction {\n hash\n messages\n success\n account {\n address\n }\n block {\n height\n timestamp\n }\n }\n }\n }\n":
types.GetExecuteTxsByContractAddressDocument,
"\n query getExecuteTxsCountByContractAddress($contractAddress: String!) {\n contract_transactions_aggregate(\n where: {\n contract: { address: { _eq: $contractAddress } }\n transaction: { is_execute: { _eq: true } }\n }\n ) {\n aggregate {\n count\n }\n }\n }\n":
Expand All @@ -29,7 +31,7 @@ const documents = {
types.GetRelatedProposalsByContractAddressDocument,
"\n query getRelatedProposalsCountByContractAddress($contractAddress: String!) {\n contract_proposals_aggregate(\n where: { contract: { address: { _eq: $contractAddress } } }\n ) {\n aggregate {\n count\n }\n }\n }\n":
types.GetRelatedProposalsCountByContractAddressDocument,
"\n query getContractListByCodeId($codeId: Int!, $offset: Int!, $pageSize: Int!) {\n contracts(\n where: { code_id: { _eq: $codeId } }\n order_by: { transaction: { block: { timestamp: desc } } }\n offset: $offset\n limit: $pageSize\n ) {\n address\n label\n transaction {\n block {\n timestamp\n }\n account {\n address\n }\n }\n contract_histories(order_by: { block: { timestamp: desc } }, limit: 1) {\n block {\n timestamp\n }\n account {\n address\n }\n }\n }\n }\n":
"\n query getContractListByCodeId($codeId: Int!, $offset: Int!, $pageSize: Int!) {\n contracts(\n where: { code_id: { _eq: $codeId } }\n order_by: { transaction: { block: { timestamp: desc } } }\n offset: $offset\n limit: $pageSize\n ) {\n address\n label\n account {\n address\n }\n transaction {\n block {\n timestamp\n }\n account {\n address\n }\n }\n contract_histories(order_by: { block: { timestamp: desc } }, limit: 1) {\n block {\n timestamp\n }\n account {\n address\n }\n }\n }\n }\n":
types.GetContractListByCodeIdDocument,
"\n query getContractListCountByCodeId($codeId: Int!) {\n contracts_aggregate(where: { code_id: { _eq: $codeId } }) {\n aggregate {\n count\n }\n }\n }\n":
types.GetContractListCountByCodeIdDocument,
Expand Down Expand Up @@ -62,6 +64,9 @@ export function graphql(
export function graphql(
source: '\n query getInstantiateDetailByContractQueryDocument($contractAddress: String!) {\n contracts_by_pk(address: $contractAddress) {\n init_msg\n transaction {\n hash\n }\n contract_proposals(\n where: {\n proposal: {\n type: { _in: ["InstantiateContract", "InstantiateContract2"] }\n }\n }\n limit: 1\n ) {\n proposal {\n id\n title\n }\n }\n }\n }\n'
): typeof documents['\n query getInstantiateDetailByContractQueryDocument($contractAddress: String!) {\n contracts_by_pk(address: $contractAddress) {\n init_msg\n transaction {\n hash\n }\n contract_proposals(\n where: {\n proposal: {\n type: { _in: ["InstantiateContract", "InstantiateContract2"] }\n }\n }\n limit: 1\n ) {\n proposal {\n id\n title\n }\n }\n }\n }\n'];
export function graphql(
source: "\n query getAdminByContractAddressesQueryDocument(\n $contractAddresses: [String!]\n ) {\n contracts(where: { address: { _in: $contractAddresses } }) {\n address\n account {\n address\n }\n }\n }\n"
): typeof documents["\n query getAdminByContractAddressesQueryDocument(\n $contractAddresses: [String!]\n ) {\n contracts(where: { address: { _in: $contractAddresses } }) {\n address\n account {\n address\n }\n }\n }\n"];
export function graphql(
source: "\n query getExecuteTxsByContractAddress(\n $contractAddress: String!\n $offset: Int!\n $pageSize: Int!\n ) {\n contract_transactions(\n where: {\n contract: { address: { _eq: $contractAddress } }\n transaction: { is_execute: { _eq: true } }\n }\n order_by: { transaction: { block: { timestamp: desc } } }\n limit: $pageSize\n offset: $offset\n ) {\n transaction {\n hash\n messages\n success\n account {\n address\n }\n block {\n height\n timestamp\n }\n }\n }\n }\n"
): typeof documents["\n query getExecuteTxsByContractAddress(\n $contractAddress: String!\n $offset: Int!\n $pageSize: Int!\n ) {\n contract_transactions(\n where: {\n contract: { address: { _eq: $contractAddress } }\n transaction: { is_execute: { _eq: true } }\n }\n order_by: { transaction: { block: { timestamp: desc } } }\n limit: $pageSize\n offset: $offset\n ) {\n transaction {\n hash\n messages\n success\n account {\n address\n }\n block {\n height\n timestamp\n }\n }\n }\n }\n"];
Expand All @@ -81,8 +86,8 @@ export function graphql(
source: "\n query getRelatedProposalsCountByContractAddress($contractAddress: String!) {\n contract_proposals_aggregate(\n where: { contract: { address: { _eq: $contractAddress } } }\n ) {\n aggregate {\n count\n }\n }\n }\n"
): typeof documents["\n query getRelatedProposalsCountByContractAddress($contractAddress: String!) {\n contract_proposals_aggregate(\n where: { contract: { address: { _eq: $contractAddress } } }\n ) {\n aggregate {\n count\n }\n }\n }\n"];
export function graphql(
source: "\n query getContractListByCodeId($codeId: Int!, $offset: Int!, $pageSize: Int!) {\n contracts(\n where: { code_id: { _eq: $codeId } }\n order_by: { transaction: { block: { timestamp: desc } } }\n offset: $offset\n limit: $pageSize\n ) {\n address\n label\n transaction {\n block {\n timestamp\n }\n account {\n address\n }\n }\n contract_histories(order_by: { block: { timestamp: desc } }, limit: 1) {\n block {\n timestamp\n }\n account {\n address\n }\n }\n }\n }\n"
): typeof documents["\n query getContractListByCodeId($codeId: Int!, $offset: Int!, $pageSize: Int!) {\n contracts(\n where: { code_id: { _eq: $codeId } }\n order_by: { transaction: { block: { timestamp: desc } } }\n offset: $offset\n limit: $pageSize\n ) {\n address\n label\n transaction {\n block {\n timestamp\n }\n account {\n address\n }\n }\n contract_histories(order_by: { block: { timestamp: desc } }, limit: 1) {\n block {\n timestamp\n }\n account {\n address\n }\n }\n }\n }\n"];
source: "\n query getContractListByCodeId($codeId: Int!, $offset: Int!, $pageSize: Int!) {\n contracts(\n where: { code_id: { _eq: $codeId } }\n order_by: { transaction: { block: { timestamp: desc } } }\n offset: $offset\n limit: $pageSize\n ) {\n address\n label\n account {\n address\n }\n transaction {\n block {\n timestamp\n }\n account {\n address\n }\n }\n contract_histories(order_by: { block: { timestamp: desc } }, limit: 1) {\n block {\n timestamp\n }\n account {\n address\n }\n }\n }\n }\n"
): typeof documents["\n query getContractListByCodeId($codeId: Int!, $offset: Int!, $pageSize: Int!) {\n contracts(\n where: { code_id: { _eq: $codeId } }\n order_by: { transaction: { block: { timestamp: desc } } }\n offset: $offset\n limit: $pageSize\n ) {\n address\n label\n account {\n address\n }\n transaction {\n block {\n timestamp\n }\n account {\n address\n }\n }\n contract_histories(order_by: { block: { timestamp: desc } }, limit: 1) {\n block {\n timestamp\n }\n account {\n address\n }\n }\n }\n }\n"];
export function graphql(
source: "\n query getContractListCountByCodeId($codeId: Int!) {\n contracts_aggregate(where: { code_id: { _eq: $codeId } }) {\n aggregate {\n count\n }\n }\n }\n"
): typeof documents["\n query getContractListCountByCodeId($codeId: Int!) {\n contracts_aggregate(where: { code_id: { _eq: $codeId } }) {\n aggregate {\n count\n }\n }\n }\n"];
Expand Down
Loading