From 680b2ac258c3d78dc0297012ada9761721e8dc94 Mon Sep 17 00:00:00 2001 From: Serkan Reis Date: Mon, 15 Jan 2024 14:55:16 +0300 Subject: [PATCH 01/13] Initial authz logic --- components/LinkTabs.data.ts | 13 +++ config/authz.ts | 160 ++++++++++++++++++++++++++ package.json | 1 + pages/authz/grant.tsx | 221 ++++++++++++++++++++++++++++++++++++ pages/authz/index.tsx | 1 + pages/authz/revoke.tsx | 120 ++++++++++++++++++++ pages/revoke.tsx | 97 ---------------- yarn.lock | 5 + 8 files changed, 521 insertions(+), 97 deletions(-) create mode 100644 config/authz.ts create mode 100644 pages/authz/grant.tsx create mode 100644 pages/authz/index.tsx create mode 100644 pages/authz/revoke.tsx delete mode 100644 pages/revoke.tsx diff --git a/components/LinkTabs.data.ts b/components/LinkTabs.data.ts index cfc268a..e5664b7 100644 --- a/components/LinkTabs.data.ts +++ b/components/LinkTabs.data.ts @@ -158,3 +158,16 @@ export const royaltyRegistryLinkTabs: LinkTabProps[] = [ href: '/contracts/royaltyRegistry/execute', }, ] + +export const authzLinkTabs: LinkTabProps[] = [ + { + title: 'Grant', + description: `Grant authorizations to a given address`, + href: '/authz/grant', + }, + { + title: 'Revoke', + description: `Revoke already granted authorizations`, + href: '/authz/revoke', + }, +] diff --git a/config/authz.ts b/config/authz.ts new file mode 100644 index 0000000..23b8a4f --- /dev/null +++ b/config/authz.ts @@ -0,0 +1,160 @@ +/* eslint-disable eslint-comments/disable-enable-pair */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable camelcase */ + +import { GenericAuthorization } from 'cosmjs-types/cosmos/authz/v1beta1/authz' +import { MsgGrant } from 'cosmjs-types/cosmos/authz/v1beta1/tx' +import { SendAuthorization } from 'cosmjs-types/cosmos/bank/v1beta1/authz' +import { Coin } from 'cosmjs-types/cosmos/base/v1beta1/coin' +import type { AuthorizationType } from 'cosmjs-types/cosmos/staking/v1beta1/authz' +import { StakeAuthorization, StakeAuthorization_Validators } from 'cosmjs-types/cosmos/staking/v1beta1/authz' +import type { AuthorizationMode, GenericAuthorizationType, GrantAuthorizationType } from 'pages/authz/grant' + +export interface Msg { + typeUrl: string + value: any +} + +export interface AuthzMessage { + authzMode: AuthorizationMode + authzType: GrantAuthorizationType + displayName: string + typeUrl: string + genericAuthzType?: GenericAuthorizationType +} + +export const grantGenericStakeAuthorization: AuthzMessage = { + authzMode: 'Grant', + authzType: 'Generic', + displayName: 'Stake', + typeUrl: '/cosmos.staking.v1beta1.MsgDelegate', + genericAuthzType: 'MsgDelegate', +} + +export const grantGenericSendAuthorization: AuthzMessage = { + authzMode: 'Grant', + authzType: 'Generic', + displayName: 'Send', + typeUrl: '/cosmos.bank.v1beta1.MsgSend', + genericAuthzType: 'MsgSend', +} + +export const authzMessages: AuthzMessage[] = [grantGenericStakeAuthorization, grantGenericSendAuthorization] + +const msgAuthzGrantTypeUrl = '/cosmos.authz.v1beta1.MsgGrant' + +export function AuthzSendGrantMsg( + granter: string, + grantee: string, + denom: string, + spendLimit: number, + expiration: number, +): Msg { + const sendAuthValue = SendAuthorization.encode( + SendAuthorization.fromPartial({ + spendLimit: [ + Coin.fromPartial({ + amount: String(spendLimit), + denom, + }), + ], + }), + ).finish() + const grantValue = MsgGrant.fromPartial({ + grant: { + authorization: { + typeUrl: '/cosmos.bank.v1beta1.SendAuthorization', + value: sendAuthValue, + }, + //expiration: { seconds: BigInt(expiration).valueOf() }, + }, + grantee, + granter, + }) + + return { + typeUrl: msgAuthzGrantTypeUrl, + value: grantValue, + } +} + +export function AuthzGenericGrantMsg(granter: string, grantee: string, typeURL: string, expiration: number): Msg { + return { + typeUrl: msgAuthzGrantTypeUrl, + value: { + grant: { + authorization: { + typeUrl: '/cosmos.authz.v1beta1.GenericAuthorization', + value: GenericAuthorization.encode( + GenericAuthorization.fromPartial({ + msg: typeURL, + }), + ).finish(), + }, + expiration: expiration ? { seconds: expiration } : undefined, + }, + grantee, + granter, + }, + } +} + +export function AuthzStakeGrantMsg({ + expiration, + grantee, + granter, + allowList, + denyList, + maxTokens, + denom, + stakeAuthzType, +}: { + granter: string + grantee: string + expiration: number + allowList?: string[] + denyList?: string[] + maxTokens?: string + denom?: string + stakeAuthzType: AuthorizationType +}): Msg { + const allow_list = StakeAuthorization_Validators.encode( + StakeAuthorization_Validators.fromPartial({ + address: allowList, + }), + ).finish() + const deny_list = StakeAuthorization_Validators.encode( + StakeAuthorization_Validators.fromPartial({ + address: denyList, + }), + ).finish() + const stakeAuthValue = StakeAuthorization.encode( + StakeAuthorization.fromPartial({ + authorizationType: stakeAuthzType, + allowList: allowList?.length ? StakeAuthorization_Validators.decode(allow_list) : undefined, + denyList: denyList?.length ? StakeAuthorization_Validators.decode(deny_list) : undefined, + maxTokens: maxTokens + ? Coin.fromPartial({ + amount: maxTokens, + denom, + }) + : undefined, + }), + ).finish() + const grantValue = MsgGrant.fromPartial({ + grant: { + authorization: { + typeUrl: '/cosmos.staking.v1beta1.StakeAuthorization', + value: stakeAuthValue, + }, + // expiration: { seconds: BigInt(expiration) }, + }, + grantee, + granter, + }) + + return { + typeUrl: msgAuthzGrantTypeUrl, + value: grantValue, + } +} diff --git a/package.json b/package.json index a3ac908..df2fa8b 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@cosmjs/math": "^0", "@cosmjs/proto-signing": "^0", "@cosmjs/stargate": "^0", + "cosmjs-types": "0.9.0", "@cosmos-kit/keplr": "^2.4.4", "@cosmos-kit/leap": "^2.4.3", "@cosmos-kit/leap-metamask-cosmos-snap": "^0.3.3", diff --git a/pages/authz/grant.tsx b/pages/authz/grant.tsx new file mode 100644 index 0000000..4126528 --- /dev/null +++ b/pages/authz/grant.tsx @@ -0,0 +1,221 @@ +/* eslint-disable eslint-comments/disable-enable-pair */ +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ +/* eslint-disable no-nested-ternary */ + +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable tailwindcss/classnames-order */ +/* eslint-disable react/button-has-type */ + +import { GasPrice, SigningStargateClient } from '@cosmjs/stargate' +import { Alert } from 'components/Alert' +import { Conditional } from 'components/Conditional' +import { ContractPageHeader } from 'components/ContractPageHeader' +import { FormControl } from 'components/FormControl' +import { TextInput } from 'components/forms/FormInput' +import { useInputState } from 'components/forms/FormInput.hooks' +import { InputDateTime } from 'components/InputDateTime' +import { LinkTabs } from 'components/LinkTabs' +import { authzLinkTabs } from 'components/LinkTabs.data' +import { getConfig } from 'config' +import type { Msg } from 'config/authz' +import { AuthzGenericGrantMsg } from 'config/authz' +import { useGlobalSettings } from 'contexts/globalSettings' +import type { NextPage } from 'next' +import { NextSeo } from 'next-seo' +import { useState } from 'react' +import toast from 'react-hot-toast' +import { NETWORK } from 'utils/constants' +import { withMetadata } from 'utils/layout' +import { useWallet } from 'utils/wallet' + +export type AuthorizationMode = 'Grant' | 'Revoke' +export type GrantAuthorizationType = 'Generic' | 'Send' | 'Execute Smart Contract' | 'Migrate Smart Contract' +export type GenericAuthorizationType = + | 'MsgDelegate' + | 'MsgUndelegate' + | 'MsgBeginRedelegate' + | 'MsgWithdrawDelegatorReward' + | 'MsgVote' + | 'MsgSend' + | 'MsgExecuteContract' + | 'MsgMigrateContract' + +const Grant: NextPage = () => { + const wallet = useWallet() + const { timezone } = useGlobalSettings() + + const [authMode, setAuthMode] = useState('Grant') + const [authType, setAuthType] = useState('Generic') + const [genericAuthType, setGenericAuthType] = useState('MsgSend') + const [expiration, setExpiration] = useState() + const [transactionHash, setTransactionHash] = useState(undefined) + + const granteeAddressState = useInputState({ + id: 'grantee-address', + name: 'granteeAddress', + title: 'Grantee Address', + placeholder: 'stars1...', + subtitle: 'The address to grant authorization to', + }) + + const spendLimitDenomState = useInputState({ + id: 'spend-limit-denom', + name: 'spendLimitDenom', + title: 'Spend Limit Denom', + placeholder: `ustars`, + subtitle: 'The spend limit denom', + }) + + const messageToSign = () => { + if (authType === 'Generic') { + if (genericAuthType === 'MsgSend') { + return AuthzGenericGrantMsg( + wallet.address || '', + granteeAddressState.value, + '/cosmos.bank.v1beta1.MsgSend', + (expiration?.getTime() as number) / 1000 || 0, + ) + } + if (genericAuthType === 'MsgDelegate') { + return AuthzGenericGrantMsg( + wallet.address || '', + granteeAddressState.value, + '/cosmos.staking.v1beta1.MsgDelegate', + (expiration?.getTime() as number) / 1000 || 0, + ) + } + if (genericAuthType === 'MsgUndelegate') { + return AuthzGenericGrantMsg( + wallet.address || '', + granteeAddressState.value, + '/cosmos.staking.v1beta1.MsgUndelegate', + (expiration?.getTime() as number) / 1000 || 0, + ) + } + } + } + const handleSendMessage = async () => { + try { + if (!wallet.isWalletConnected) return toast.error('Please connect your wallet.') + setTransactionHash(undefined) + const offlineSigner = wallet.getOfflineSignerDirect() + const stargateClient = await SigningStargateClient.connectWithSigner(getConfig(NETWORK).rpcUrl, offlineSigner, { + gasPrice: GasPrice.fromString('0.025ustars'), + }) + + const response = await stargateClient.signAndBroadcast(wallet.address || '', [messageToSign() as Msg], 'auto') + setTransactionHash(response.transactionHash) + toast.success(`${authType} authorization success.`, { style: { maxWidth: 'none' } }) + } catch (error: any) { + toast.error(error.message, { style: { maxWidth: 'none' } }) + setTransactionHash(undefined) + console.error('Error: ', error) + } + } + + return ( +
+ + + +
+ Authorization Type + +
+ +
+ Generic Authorization Type + +
+
+ + + + + + + date + ? setExpiration( + timezone === 'Local' ? date : new Date(date?.getTime() - new Date().getTimezoneOffset() * 60 * 1000), + ) + : setExpiration(undefined) + } + value={ + timezone === 'Local' + ? expiration + : expiration + ? new Date(expiration.getTime() + new Date().getTimezoneOffset() * 60 * 1000) + : undefined + } + /> + + + {transactionHash && ( + + {`Transaction Hash: ${transactionHash}`} + + )} +
+ ) +} + +export default withMetadata(Grant, { center: false }) diff --git a/pages/authz/index.tsx b/pages/authz/index.tsx new file mode 100644 index 0000000..52bf43c --- /dev/null +++ b/pages/authz/index.tsx @@ -0,0 +1 @@ +export { default } from './grant' diff --git a/pages/authz/revoke.tsx b/pages/authz/revoke.tsx new file mode 100644 index 0000000..67de2a6 --- /dev/null +++ b/pages/authz/revoke.tsx @@ -0,0 +1,120 @@ +/* eslint-disable eslint-comments/disable-enable-pair */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +import { GasPrice, SigningStargateClient } from '@cosmjs/stargate' +import { Alert } from 'components/Alert' +import { ContractPageHeader } from 'components/ContractPageHeader' +import { TextInput } from 'components/forms/FormInput' +import { useInputState } from 'components/forms/FormInput.hooks' +import { LinkTabs } from 'components/LinkTabs' +import { authzLinkTabs } from 'components/LinkTabs.data' +import { getConfig } from 'config' +import type { NextPage } from 'next' +import { NextSeo } from 'next-seo' +import { useState } from 'react' +import toast from 'react-hot-toast' +import { NETWORK } from 'utils/constants' +import { withMetadata } from 'utils/layout' +import { useWallet } from 'utils/wallet' + +const RevokeAuthorization: NextPage = () => { + const wallet = useWallet() + + const [transactionHash, setTransactionHash] = useState(undefined) + + const granteeAddressState = useInputState({ + id: 'grantee-address', + name: 'granteeAddress', + title: 'Grantee Address', + subtitle: 'Address to revoke message authorization', + placeholder: 'stars1...', + }) + + const messageState = useInputState({ + id: 'message', + name: 'message', + title: 'Message', + subtitle: 'Message to revoke authorization for', + placeholder: '/cosmos.bank.v1beta1.MsgSend', + defaultValue: '/cosmos.bank.v1beta1.MsgSend', + }) + + const revokeAuthorization = async (granteeAddress: string, msg: string) => { + console.log('Wallet Address: ', wallet.address) + try { + if (!wallet.isWalletConnected) throw new Error('Wallet not connected.') + setTransactionHash(undefined) + const offlineSigner = wallet.getOfflineSignerDirect() + const stargateClient = await SigningStargateClient.connectWithSigner(getConfig(NETWORK).rpcUrl, offlineSigner, { + gasPrice: GasPrice.fromString('0.25ustars'), + }) + + const result = await stargateClient.signAndBroadcast( + wallet.address || '', + [ + { + typeUrl: '/cosmos.authz.v1beta1.MsgRevoke', + value: { + granter: wallet.address, + grantee: granteeAddress, + msgTypeUrl: msg, + }, + }, + ], + { + amount: [{ amount: '500000', denom: 'ustars' }], + gas: '200000', + }, + ) + setTransactionHash(result.transactionHash) + toast.success(`Revoke authorization success.`, { style: { maxWidth: 'none' } }) + } catch (e: any) { + console.log(e) + setTransactionHash(undefined) + toast.error(e.message, { style: { maxWidth: 'none' } }) + } + } + + return ( +
+ + + +
+ Authorization Type + +
+ + {/* */} + + {transactionHash && ( + + {`Transaction Hash: ${transactionHash}`} + + )} +
+ ) +} +export default withMetadata(RevokeAuthorization, { center: false }) diff --git a/pages/revoke.tsx b/pages/revoke.tsx deleted file mode 100644 index 860c189..0000000 --- a/pages/revoke.tsx +++ /dev/null @@ -1,97 +0,0 @@ -/* eslint-disable eslint-comments/disable-enable-pair */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -import { coins } from '@cosmjs/proto-signing' -import { ContractPageHeader } from 'components/ContractPageHeader' -import { TextInput } from 'components/forms/FormInput' -import { useInputState } from 'components/forms/FormInput.hooks' -import type { NextPage } from 'next' -import { NextSeo } from 'next-seo' -import { useState } from 'react' -import toast from 'react-hot-toast' -import { withMetadata } from 'utils/layout' -import { links } from 'utils/links' -import { useWallet } from 'utils/wallet' - -const RevokeAuthorization: NextPage = () => { - const wallet = useWallet() - - const [transactionHash, setTransactionHash] = useState(undefined) - - const granteeAddressState = useInputState({ - id: 'grantee-address', - name: 'granteeAddress', - title: 'Grantee Address', - subtitle: 'Address to revoke message authorization', - placeholder: 'stars1234567890abcdefghijklmnopqrstuvwxyz...', - defaultValue: 'stars12vfpmlvmqrh9p0kcrtv6lw9ylkh7reuczdmmz5', - }) - - const messageState = useInputState({ - id: 'message', - name: 'message', - title: 'Message', - subtitle: 'Message to revoke authorization for', - placeholder: '/cosmos.bank.v1beta1.MsgSend', - defaultValue: '/cosmos.bank.v1beta1.MsgSend', - }) - - const revokeAuthorization = async (granteeAddress: string, msg: string) => { - console.log('Wallet Address: ', wallet.address) - try { - if (!wallet.isWalletConnected) throw new Error('Wallet not connected.') - const result = await ( - await wallet.getSigningCosmWasmClient() - ).signAndBroadcast( - wallet.address || '', - [ - { - typeUrl: '/cosmos.authz.v1beta1.MsgRevoke', - value: { - granter: wallet.address, - grantee: granteeAddress, - msgTypeUrl: msg, - funds: coins('100000', 'ustars'), - }, - }, - ], - 'auto', - ) - setTransactionHash(result.transactionHash) - } catch (e: any) { - console.log(e) - toast.error(e.message, { style: { maxWidth: 'none' } }) - } - } - - return ( -
- - - -
Message Types
- - - - {transactionHash && ( -
{`Transaction Hash: ${transactionHash}`}
- )} -
- ) -} -export default withMetadata(RevokeAuthorization, { center: false }) diff --git a/yarn.lock b/yarn.lock index b825ee2..6308bba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4727,6 +4727,11 @@ cosmiconfig@^7.0.1: path-type "^4.0.0" yaml "^1.10.0" +cosmjs-types@0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/cosmjs-types/-/cosmjs-types-0.9.0.tgz#c3bc482d28c7dfa25d1445093fdb2d9da1f6cfcc" + integrity sha512-MN/yUe6mkJwHnCFfsNPeCfXVhyxHYW6c/xDUzrSbBycYzw++XvWDMJArXp2pLdgD6FQ8DW79vkPjeNKVrXaHeQ== + cosmjs-types@^0.4.0: version "0.4.1" resolved "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.4.1.tgz" From f6253dd6c7f98ed4c3f8645a401ebf2c35e2032d Mon Sep 17 00:00:00 2001 From: Serkan Reis Date: Mon, 15 Jan 2024 15:05:16 +0300 Subject: [PATCH 02/13] Update sidebar --- components/Sidebar.tsx | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/components/Sidebar.tsx b/components/Sidebar.tsx index c58078d..10246ee 100644 --- a/components/Sidebar.tsx +++ b/components/Sidebar.tsx @@ -45,7 +45,7 @@ export const Sidebar = () => { }, []) const handleResize = () => { - setIsTallWindow(window.innerHeight > 700) + setIsTallWindow(window.innerHeight > 768) } useEffect(() => { @@ -67,7 +67,7 @@ export const Sidebar = () => { {/* main navigation routes */}
-
    +
    • {
    -
      +
      • {
    -
      +
      • {
    +
      +
    • + + Authz + +
    • +
From 7526cc91d1b1091c47c95f253ce15ab56c25eb5f Mon Sep 17 00:00:00 2001 From: Serkan Reis Date: Mon, 15 Jan 2024 15:06:48 +0300 Subject: [PATCH 03/13] Bump Studio version --- .env.example | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 8ae4fb6..3024342 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ -APP_VERSION=0.8.5 +APP_VERSION=0.8.6 NEXT_PUBLIC_PINATA_ENDPOINT_URL=https://api.pinata.cloud/pinning/pinFileToIPFS NEXT_PUBLIC_SG721_CODE_ID=2595 diff --git a/package.json b/package.json index df2fa8b..364c19b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "stargaze-studio", - "version": "0.8.5", + "version": "0.8.6", "workspaces": [ "packages/*" ], From ab801768b9840c1dd2d05ba967d49712eecd474d Mon Sep 17 00:00:00 2001 From: Serkan Reis Date: Mon, 15 Jan 2024 18:02:45 +0300 Subject: [PATCH 04/13] Include Send among available auth types --- config/authz.ts | 6 ++- pages/authz/grant.tsx | 95 ++++++++++++++++++++++++++++--------------- 2 files changed, 67 insertions(+), 34 deletions(-) diff --git a/config/authz.ts b/config/authz.ts index 23b8a4f..309512f 100644 --- a/config/authz.ts +++ b/config/authz.ts @@ -49,6 +49,7 @@ export function AuthzSendGrantMsg( denom: string, spendLimit: number, expiration: number, + allowList?: string[], ): Msg { const sendAuthValue = SendAuthorization.encode( SendAuthorization.fromPartial({ @@ -58,15 +59,18 @@ export function AuthzSendGrantMsg( denom, }), ], + //allowList, }), ).finish() + const grantValue = MsgGrant.fromPartial({ grant: { authorization: { typeUrl: '/cosmos.bank.v1beta1.SendAuthorization', value: sendAuthValue, }, - //expiration: { seconds: BigInt(expiration).valueOf() }, + // TODO: fix expiration issue + expiration: expiration ? { seconds: BigInt(expiration) } : undefined, }, grantee, granter, diff --git a/pages/authz/grant.tsx b/pages/authz/grant.tsx index 4126528..a93506f 100644 --- a/pages/authz/grant.tsx +++ b/pages/authz/grant.tsx @@ -11,14 +11,14 @@ import { Alert } from 'components/Alert' import { Conditional } from 'components/Conditional' import { ContractPageHeader } from 'components/ContractPageHeader' import { FormControl } from 'components/FormControl' -import { TextInput } from 'components/forms/FormInput' -import { useInputState } from 'components/forms/FormInput.hooks' +import { NumberInput, TextInput } from 'components/forms/FormInput' +import { useInputState, useNumberInputState } from 'components/forms/FormInput.hooks' import { InputDateTime } from 'components/InputDateTime' import { LinkTabs } from 'components/LinkTabs' import { authzLinkTabs } from 'components/LinkTabs.data' import { getConfig } from 'config' import type { Msg } from 'config/authz' -import { AuthzGenericGrantMsg } from 'config/authz' +import { AuthzGenericGrantMsg, AuthzSendGrantMsg } from 'config/authz' import { useGlobalSettings } from 'contexts/globalSettings' import type { NextPage } from 'next' import { NextSeo } from 'next-seo' @@ -66,6 +66,22 @@ const Grant: NextPage = () => { subtitle: 'The spend limit denom', }) + const spendLimitState = useNumberInputState({ + id: 'spend-limit', + name: 'spendLimit', + title: 'Spend Limit', + placeholder: `1000000`, + subtitle: 'The spend limit', + }) + + const allowListState = useInputState({ + id: 'allow-list', + name: 'allowList', + title: 'Allow List', + placeholder: `stars1..., stars1...`, + subtitle: 'Comma separated list of addresses to allow transactions to', + }) + const messageToSign = () => { if (authType === 'Generic') { if (genericAuthType === 'MsgSend') { @@ -92,6 +108,15 @@ const Grant: NextPage = () => { (expiration?.getTime() as number) / 1000 || 0, ) } + } else if (authType === 'Send') { + return AuthzSendGrantMsg( + wallet.address || '', + granteeAddressState.value, + spendLimitDenomState.value, + spendLimitState.value, + (expiration?.getTime() as number) / 1000 || 0, + allowListState.value ? allowListState.value.split(',').map((address) => address.trim()) : [], + ) } } const handleSendMessage = async () => { @@ -130,9 +155,7 @@ const Grant: NextPage = () => { value={authType} > - + @@ -170,36 +193,42 @@ const Grant: NextPage = () => { - + + + {/* */} + + + + + date + ? setExpiration( + timezone === 'Local' + ? date + : new Date(date?.getTime() - new Date().getTimezoneOffset() * 60 * 1000), + ) + : setExpiration(undefined) + } + value={ + timezone === 'Local' + ? expiration + : expiration + ? new Date(expiration.getTime() + new Date().getTimezoneOffset() * 60 * 1000) + : undefined + } + /> + - - - date - ? setExpiration( - timezone === 'Local' ? date : new Date(date?.getTime() - new Date().getTimezoneOffset() * 60 * 1000), - ) - : setExpiration(undefined) - } - value={ - timezone === 'Local' - ? expiration - : expiration - ? new Date(expiration.getTime() + new Date().getTimezoneOffset() * 60 * 1000) - : undefined - } - /> - + {transactionHash && ( {`Transaction Hash: ${transactionHash}`} diff --git a/pages/authz/revoke.tsx b/pages/authz/revoke.tsx index 67de2a6..929edb4 100644 --- a/pages/authz/revoke.tsx +++ b/pages/authz/revoke.tsx @@ -2,6 +2,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { GasPrice, SigningStargateClient } from '@cosmjs/stargate' import { Alert } from 'components/Alert' +import { Button } from 'components/Button' import { ContractPageHeader } from 'components/ContractPageHeader' import { TextInput } from 'components/forms/FormInput' import { useInputState } from 'components/forms/FormInput.hooks' @@ -20,6 +21,7 @@ const RevokeAuthorization: NextPage = () => { const wallet = useWallet() const [transactionHash, setTransactionHash] = useState(undefined) + const [isLoading, setIsLoading] = useState(false) const granteeAddressState = useInputState({ id: 'grantee-address', @@ -43,6 +45,7 @@ const RevokeAuthorization: NextPage = () => { try { if (!wallet.isWalletConnected) throw new Error('Wallet not connected.') setTransactionHash(undefined) + setIsLoading(true) const offlineSigner = wallet.getOfflineSignerDirect() const stargateClient = await SigningStargateClient.connectWithSigner(getConfig(NETWORK).rpcUrl, offlineSigner, { gasPrice: GasPrice.fromString('0.25ustars'), @@ -67,8 +70,10 @@ const RevokeAuthorization: NextPage = () => { ) setTransactionHash(result.transactionHash) toast.success(`Revoke authorization success.`, { style: { maxWidth: 'none' } }) + setIsLoading(false) } catch (e: any) { console.log(e) + setIsLoading(false) setTransactionHash(undefined) toast.error(e.message, { style: { maxWidth: 'none' } }) } @@ -98,17 +103,24 @@ const RevokeAuthorization: NextPage = () => { + + {/* */} - + {transactionHash && ( {`Transaction Hash: ${transactionHash}`} From 58ae7c05aff6734399e9b05111364769c071e02b Mon Sep 17 00:00:00 2001 From: Serkan Reis Date: Tue, 16 Jan 2024 18:05:31 +0300 Subject: [PATCH 07/13] Cover the rest of generic messages for authz grant & revoke --- pages/authz/grant.tsx | 66 +++++++++++++++++++++++++++++++----------- pages/authz/revoke.tsx | 28 ++++++++++++++---- 2 files changed, 71 insertions(+), 23 deletions(-) diff --git a/pages/authz/grant.tsx b/pages/authz/grant.tsx index cb43fd0..261d5f2 100644 --- a/pages/authz/grant.tsx +++ b/pages/authz/grant.tsx @@ -149,6 +149,46 @@ const Grant: NextPage = () => { (expiration?.getTime() as number) / 1000 || 0, ) } + if (genericAuthType === 'MsgBeginRedelegate') { + return AuthzGenericGrantMsg( + wallet.address || '', + granteeAddressState.value, + '/cosmos.staking.v1beta1.MsgBeginRedelegate', + (expiration?.getTime() as number) / 1000 || 0, + ) + } + if (genericAuthType === 'MsgWithdrawDelegatorReward') { + return AuthzGenericGrantMsg( + wallet.address || '', + granteeAddressState.value, + '/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward', + (expiration?.getTime() as number) / 1000 || 0, + ) + } + if (genericAuthType === 'MsgVote') { + return AuthzGenericGrantMsg( + wallet.address || '', + granteeAddressState.value, + '/cosmos.gov.v1beta1.MsgVote', + (expiration?.getTime() as number) / 1000 || 0, + ) + } + if (genericAuthType === 'MsgExecuteContract') { + return AuthzGenericGrantMsg( + wallet.address || '', + granteeAddressState.value, + '/cosmwasm.wasm.v1.MsgExecuteContract', + (expiration?.getTime() as number) / 1000 || 0, + ) + } + if (genericAuthType === 'MsgMigrateContract') { + return AuthzGenericGrantMsg( + wallet.address || '', + granteeAddressState.value, + '/cosmwasm.wasm.v1.MsgMigrateContract', + (expiration?.getTime() as number) / 1000 || 0, + ) + } } else if (authType === 'Send') { return AuthzSendGrantMsg( wallet.address || '', @@ -227,28 +267,20 @@ const Grant: NextPage = () => { - - - - - + + + + + - - +
+ + +
{/* */}
diff --git a/pages/authz/revoke.tsx b/pages/authz/revoke.tsx index 929edb4..3ed58af 100644 --- a/pages/authz/revoke.tsx +++ b/pages/authz/revoke.tsx @@ -51,7 +51,7 @@ const RevokeAuthorization: NextPage = () => { gasPrice: GasPrice.fromString('0.25ustars'), }) - const result = await stargateClient.signAndBroadcast( + const response = await stargateClient.signAndBroadcast( wallet.address || '', [ { @@ -68,8 +68,9 @@ const RevokeAuthorization: NextPage = () => { gas: '200000', }, ) - setTransactionHash(result.transactionHash) - toast.success(`Revoke authorization success.`, { style: { maxWidth: 'none' } }) + setTransactionHash(response.transactionHash) + if (response.rawLog?.includes('failed')) toast.error(response.rawLog, { style: { maxWidth: 'none' } }) + else toast.success(`Revoke authorization success.`, { style: { maxWidth: 'none' } }) setIsLoading(false) } catch (e: any) { console.log(e) @@ -103,15 +104,30 @@ const RevokeAuthorization: NextPage = () => { - + + + + + - - + {/* */}