From 1ac32982ad2669e5e210f02b9d7c5daad801cfe7 Mon Sep 17 00:00:00 2001 From: eliasmpw Date: Wed, 11 Dec 2024 13:55:45 +0100 Subject: [PATCH 1/5] feat: authz amino converters support --- CHANGELOG.md | 11 ++ package.json | 2 +- packages/arch3-core/package.json | 2 +- packages/arch3-core/src/index.ts | 1 + .../arch3-core/src/modules/authz/index.ts | 1 + packages/arch3-core/src/modules/authz/tx.ts | 108 ++++++++++++++++++ packages/arch3-core/src/modules/index.ts | 1 + .../arch3-core/src/signingarchwayclient.ts | 71 ++++++------ packages/arch3-proto/package.json | 2 +- 9 files changed, 161 insertions(+), 38 deletions(-) create mode 100644 packages/arch3-core/src/modules/authz/index.ts create mode 100644 packages/arch3-core/src/modules/authz/tx.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ffd0474..1f8d36c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## v0.7.4 (2024-12-11) + +### Added + +#### **arch3-core** + +- new `createAuthzAminoConverters` that allows Amino/Ledger support for +`/cosmos.authz.v1beta1.MsgGrant` with inner `/cosmos.authz.v1beta1.GenericAuthorization` +or `/cosmos.bank.v1beta1.SendAuthorization` authorization, +and also support for `/cosmos.authz.v1beta1.MsgRevoke` ([#123]) + ## v0.7.3 (2024-09-12) ### Fixed diff --git a/package.json b/package.json index c39e9ef5..d9905cff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@archwayhq/arch3.js", - "version": "0.7.3", + "version": "0.7.4", "description": "The all-in-one library for your awesome Archway dApp", "homepage": "https://docs.archway.io", "repository": "github:archway-network/arch3.js", diff --git a/packages/arch3-core/package.json b/packages/arch3-core/package.json index e6a3b744..572054e5 100644 --- a/packages/arch3-core/package.json +++ b/packages/arch3-core/package.json @@ -1,6 +1,6 @@ { "name": "@archwayhq/arch3-core", - "version": "0.7.3", + "version": "0.7.4", "description": "Core library to interact with Archway Network", "homepage": "https://docs.archway.io", "repository": "github:archway-network/arch3.js", diff --git a/packages/arch3-core/src/index.ts b/packages/arch3-core/src/index.ts index 7f5513eb..abe87717 100644 --- a/packages/arch3-core/src/index.ts +++ b/packages/arch3-core/src/index.ts @@ -1,6 +1,7 @@ export { ArchwayClient } from './archwayclient'; export { + createAuthzAminoConverters, createRewardsAminoConverters, RewardsExtension, RewardsMsgEncoder, diff --git a/packages/arch3-core/src/modules/authz/index.ts b/packages/arch3-core/src/modules/authz/index.ts new file mode 100644 index 00000000..cbb35562 --- /dev/null +++ b/packages/arch3-core/src/modules/authz/index.ts @@ -0,0 +1 @@ +export * from './tx'; diff --git a/packages/arch3-core/src/modules/authz/tx.ts b/packages/arch3-core/src/modules/authz/tx.ts new file mode 100644 index 00000000..da3e90b3 --- /dev/null +++ b/packages/arch3-core/src/modules/authz/tx.ts @@ -0,0 +1,108 @@ +import { AminoConverters } from "@cosmjs/stargate"; +import { GenericAuthorization } from "cosmjs-types/cosmos/authz/v1beta1/authz"; +import { MsgGrant, MsgRevoke } from "cosmjs-types/cosmos/authz/v1beta1/tx"; +import { SendAuthorization } from "cosmjs-types/cosmos/bank/v1beta1/authz"; +import { Timestamp } from "cosmjs-types/google/protobuf/timestamp"; +import { fromJsonTimestamp, fromTimestamp } from "cosmjs-types/helpers"; + +export function createAuthzAminoConverters(): AminoConverters { + return { + [MsgGrant.typeUrl]: { + aminoType: "cosmos-sdk/MsgGrant", + toAmino: ({ granter, grantee, grant }) => { + if (!grant || !grant.authorization) { + throw new Error(`Unsupported grant type: '${grant?.authorization?.typeUrl}'`); + } + let authorizationValue; + switch (grant?.authorization?.typeUrl) { + case GenericAuthorization.typeUrl: { + const generic = GenericAuthorization.decode(grant.authorization.value); + authorizationValue = { + type: "cosmos-sdk/GenericAuthorization", + value: { + msg: generic.msg, + }, + }; + break; + } + case SendAuthorization.typeUrl: { + const spend = SendAuthorization.decode(grant.authorization.value); + authorizationValue = { + type: "cosmos-sdk/SendAuthorization", + value: { + spend_limit: spend.spendLimit, + }, + }; + break; + } + default: + throw new Error(`Unsupported grant type: '${grant.authorization.typeUrl}'`); + } + const expiration = grant.expiration?.seconds; + + return { + granter, + grantee, + grant: { + authorization: authorizationValue, + expiration: grant.expiration + ? fromTimestamp(grant.expiration) + .toISOString() + .replace(/\.\d{3}Z$/, "Z") + : undefined, + }, + }; + }, + fromAmino: ({ granter, grantee, grant }) => { + const authorizationType = grant?.authorization?.type; + let authorizationValue; + switch (authorizationType) { + case "cosmos-sdk/GenericAuthorization": { + authorizationValue = { + typeUrl: GenericAuthorization.typeUrl, + value: GenericAuthorization.encode({ + msg: grant.authorization.value.msg, + }).finish(), + }; + break; + } + case "cosmos-sdk/SendAuthorization": { + authorizationValue = { + typeUrl: SendAuthorization.typeUrl, + value: SendAuthorization.encode( + SendAuthorization.fromPartial({ + spendLimit: grant.authorization.value.spend_limit, + }), + ).finish(), + }; + break; + } + default: + throw new Error(`Unsupported grant type: '${grant?.authorization?.type}'`); + } + return MsgGrant.fromPartial({ + granter, + grantee, + grant: { + authorization: authorizationValue, + expiration: grant.expiration ? Timestamp.fromPartial(fromJsonTimestamp(grant.expiration)) : undefined, + }, + }); + }, + }, + [MsgRevoke.typeUrl]: { + aminoType: "cosmos-sdk/MsgRevoke", + toAmino: ({ granter, grantee, msgTypeUrl }) => ({ + granter, + grantee, + msg_type_url: msgTypeUrl, + }), + fromAmino: ({ granter, grantee, msg_type_url }) => + MsgRevoke.fromPartial({ + granter, + grantee, + msgTypeUrl: msg_type_url, + }), + }, + }; +} diff --git a/packages/arch3-core/src/modules/index.ts b/packages/arch3-core/src/modules/index.ts index 272b0f44..69197c28 100644 --- a/packages/arch3-core/src/modules/index.ts +++ b/packages/arch3-core/src/modules/index.ts @@ -1,2 +1,3 @@ +export * from './authz'; export * from './rewards'; export * from './tx'; diff --git a/packages/arch3-core/src/signingarchwayclient.ts b/packages/arch3-core/src/signingarchwayclient.ts index b6d570c9..1cff8266 100644 --- a/packages/arch3-core/src/signingarchwayclient.ts +++ b/packages/arch3-core/src/signingarchwayclient.ts @@ -1,13 +1,13 @@ -import { Coin, Pubkey, addCoins, encodeSecp256k1Pubkey } from '@cosmjs/amino'; +import { Coin, Pubkey, addCoins, encodeSecp256k1Pubkey } from "@cosmjs/amino"; import { createWasmAminoConverters, HttpEndpoint, SigningCosmWasmClient, SigningCosmWasmClientOptions, -} from '@cosmjs/cosmwasm-stargate'; -import { wasmTypes } from '@cosmjs/cosmwasm-stargate/build/modules'; -import { Uint53 } from '@cosmjs/math'; -import { EncodeObject, OfflineSigner, Registry } from '@cosmjs/proto-signing'; +} from "@cosmjs/cosmwasm-stargate"; +import { wasmTypes } from "@cosmjs/cosmwasm-stargate/build/modules"; +import { Uint53 } from "@cosmjs/math"; +import { EncodeObject, OfflineSigner, Registry } from "@cosmjs/proto-signing"; import { AminoTypes, assertIsDeliverTxSuccess, @@ -18,14 +18,14 @@ import { GasPrice, logs, StdFee, -} from '@cosmjs/stargate'; -import { CometClient, HttpBatchClient, HttpBatchClientOptions, RpcClient, connectComet } from '@cosmjs/tendermint-rpc'; -import { assertDefined } from '@cosmjs/utils'; -import { SimulateResponse } from 'cosmjs-types/cosmos/tx/v1beta1/service'; -import _ from 'lodash'; - -import { createRewardsAminoConverters, RewardsMsgEncoder, rewardsTypes } from './modules'; -import { IArchwayQueryClient, createArchwayQueryClient } from './queryclient'; +} from "@cosmjs/stargate"; +import { CometClient, HttpBatchClient, HttpBatchClientOptions, RpcClient, connectComet } from "@cosmjs/tendermint-rpc"; +import { assertDefined } from "@cosmjs/utils"; +import { SimulateResponse } from "cosmjs-types/cosmos/tx/v1beta1/service"; +import _ from "lodash"; + +import { createAuthzAminoConverters, createRewardsAminoConverters, RewardsMsgEncoder, rewardsTypes } from "./modules"; +import { IArchwayQueryClient, createArchwayQueryClient } from "./queryclient"; import { BlockTracking, ContractMetadata, @@ -34,8 +34,8 @@ import { OutstandingRewards, RewardsPool, RewardsRecord, -} from './types'; -import { connectCometWithBatchClient } from './utils'; +} from "./types"; +import { connectCometWithBatchClient } from "./utils"; export interface SigningArchwayClientOptions extends SigningCosmWasmClientOptions { /** @@ -117,8 +117,8 @@ function buildResult(response: DeliverTxResponseWithLogs): TxResult { } const flatFeeRequiredTypes: readonly string[] = [ - '/cosmwasm.wasm.v1.MsgExecuteContract', - '/cosmwasm.wasm.v1.MsgMigrateContract', + "/cosmwasm.wasm.v1.MsgExecuteContract", + "/cosmwasm.wasm.v1.MsgMigrateContract", ]; /** @@ -141,6 +141,7 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch ...createDefaultAminoConverters(), ...createWasmAminoConverters(), ...createRewardsAminoConverters(), + ...createAuthzAminoConverters(), }), gasAdjustment = defaultGasAdjustment, } = options; @@ -248,23 +249,23 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch public async setContractMetadata( senderAddress: string, metadata: ContractMetadata, - fee: StdFee | 'auto' | number, + fee: StdFee | "auto" | number, memo?: string, ): Promise { const message = RewardsMsgEncoder.setContractMetadata({ senderAddress, metadata: { contractAddress: metadata.contractAddress, - ownerAddress: metadata.ownerAddress ?? '', - rewardsAddress: metadata.rewardsAddress ?? '', + ownerAddress: metadata.ownerAddress ?? "", + rewardsAddress: metadata.rewardsAddress ?? "", withdrawToWallet: metadata.withdrawToWallet ?? false, }, }); const response = await this.assertSignAndBroadcast(senderAddress, [message], fee, memo); const metadataAttr = logs.findAttribute( response.parsedLogs, - 'archway.rewards.v1.ContractMetadataSetEvent', - 'metadata', + "archway.rewards.v1.ContractMetadataSetEvent", + "metadata", ); /* eslint-disable @typescript-eslint/naming-convention */ const contractMetadata = JSON.parse(metadataAttr.value) as { @@ -305,7 +306,7 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch senderAddress: string, contractAddress: string, flatFee: Coin, - fee: StdFee | 'auto' | number, + fee: StdFee | "auto" | number, memo?: string, ): Promise { const message = RewardsMsgEncoder.setFlatFee({ @@ -316,8 +317,8 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch const response = await this.assertSignAndBroadcast(senderAddress, [message], fee, memo); const flatFeeAttr = logs.findAttribute( response.parsedLogs, - 'archway.rewards.v1.ContractFlatFeeSetEvent', - 'flat_fee', + "archway.rewards.v1.ContractFlatFeeSetEvent", + "flat_fee", ); return { ...buildResult(response), @@ -351,7 +352,7 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch public async withdrawContractRewards( senderAddress: string, limit: number, - fee: StdFee | 'auto' | number, + fee: StdFee | "auto" | number, memo?: string, ): Promise { const rewardsAddress = senderAddress; @@ -365,8 +366,8 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch const firstLogs = response.parsedLogs.find(() => true); const rewardsAttr = firstLogs?.events - .find(event => event.type === 'archway.rewards.v1.RewardsWithdrawEvent') - ?.attributes.find(attr => attr.key === 'rewards')?.value; + .find(event => event.type === "archway.rewards.v1.RewardsWithdrawEvent") + ?.attributes.find(attr => attr.key === "rewards")?.value; const rewards: Coin[] = rewardsAttr ? (JSON.parse(rewardsAttr) as Coin[]) : []; return { @@ -397,12 +398,12 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch public override async signAndBroadcast( signerAddress: string, messages: readonly EncodeObject[], - fee: number | StdFee | 'auto', + fee: number | StdFee | "auto", memo?: string, ): Promise { let usedFee: StdFee; - if (fee === 'auto' || typeof fee === 'number') { - const gasAdjustment = typeof fee === 'number' ? fee : this.gasAdjustment; + if (fee === "auto" || typeof fee === "number") { + const gasAdjustment = typeof fee === "number" ? fee : this.gasAdjustment; usedFee = await this.calculateFee(signerAddress, messages, memo, gasAdjustment); } else { usedFee = fee; @@ -457,7 +458,7 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch messages .filter(({ typeUrl }) => flatFeeRequiredTypes.includes(typeUrl)) .map(async ({ value }) => { - const contractAddress = _.get(value, 'contract') as string; + const contractAddress = _.get(value, "contract") as string; const { flatFee } = await _getContractPremium(contractAddress); return flatFee; }), @@ -473,7 +474,7 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch private async assertSignAndBroadcast( signerAddress: string, messages: readonly EncodeObject[], - fee: StdFee | 'auto' | number, + fee: StdFee | "auto" | number, memo?: string, ): Promise { const response = await this.signAndBroadcast(signerAddress, messages, fee, memo); @@ -500,7 +501,7 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch public override async withdrawRewards( delegatorAddress: string, validatorAddress: string, - fee: number | StdFee | 'auto', + fee: number | StdFee | "auto", memo?: string, ): Promise { return await super.withdrawRewards(delegatorAddress, validatorAddress, fee, memo); @@ -536,7 +537,7 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch account => account.address === signerAddress, ); if (!accountFromSigner) { - throw new Error('Failed to retrieve account from signer'); + throw new Error("Failed to retrieve account from signer"); } const pubkey = encodeSecp256k1Pubkey(accountFromSigner.pubkey); const { sequence } = await this.getSequence(signerAddress); diff --git a/packages/arch3-proto/package.json b/packages/arch3-proto/package.json index 69ea179e..4c8217f2 100644 --- a/packages/arch3-proto/package.json +++ b/packages/arch3-proto/package.json @@ -1,6 +1,6 @@ { "name": "@archwayhq/arch3-proto", - "version": "0.7.3", + "version": "0.7.4", "description": "Protobuf definitions and RPC clients for the Archway Network", "homepage": "https://docs.archway.io", "repository": "github:archway-network/arch3.js", From 8c65c05ff55a0135477f2f02c05d99c2c7404dc3 Mon Sep 17 00:00:00 2001 From: eliasmpw Date: Wed, 11 Dec 2024 14:02:21 +0100 Subject: [PATCH 2/5] fix: go back to previous lint --- .../arch3-core/src/signingarchwayclient.ts | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/packages/arch3-core/src/signingarchwayclient.ts b/packages/arch3-core/src/signingarchwayclient.ts index 1cff8266..b20f54b4 100644 --- a/packages/arch3-core/src/signingarchwayclient.ts +++ b/packages/arch3-core/src/signingarchwayclient.ts @@ -1,13 +1,13 @@ -import { Coin, Pubkey, addCoins, encodeSecp256k1Pubkey } from "@cosmjs/amino"; +import { Coin, Pubkey, addCoins, encodeSecp256k1Pubkey } from '@cosmjs/amino'; import { createWasmAminoConverters, HttpEndpoint, SigningCosmWasmClient, SigningCosmWasmClientOptions, -} from "@cosmjs/cosmwasm-stargate"; -import { wasmTypes } from "@cosmjs/cosmwasm-stargate/build/modules"; -import { Uint53 } from "@cosmjs/math"; -import { EncodeObject, OfflineSigner, Registry } from "@cosmjs/proto-signing"; +} from '@cosmjs/cosmwasm-stargate'; +import { wasmTypes } from '@cosmjs/cosmwasm-stargate/build/modules'; +import { Uint53 } from '@cosmjs/math'; +import { EncodeObject, OfflineSigner, Registry } from '@cosmjs/proto-signing'; import { AminoTypes, assertIsDeliverTxSuccess, @@ -18,14 +18,14 @@ import { GasPrice, logs, StdFee, -} from "@cosmjs/stargate"; -import { CometClient, HttpBatchClient, HttpBatchClientOptions, RpcClient, connectComet } from "@cosmjs/tendermint-rpc"; -import { assertDefined } from "@cosmjs/utils"; -import { SimulateResponse } from "cosmjs-types/cosmos/tx/v1beta1/service"; -import _ from "lodash"; - -import { createAuthzAminoConverters, createRewardsAminoConverters, RewardsMsgEncoder, rewardsTypes } from "./modules"; -import { IArchwayQueryClient, createArchwayQueryClient } from "./queryclient"; +} from '@cosmjs/stargate'; +import { CometClient, HttpBatchClient, HttpBatchClientOptions, RpcClient, connectComet } from '@cosmjs/tendermint-rpc'; +import { assertDefined } from '@cosmjs/utils'; +import { SimulateResponse } from 'cosmjs-types/cosmos/tx/v1beta1/service'; +import _ from 'lodash'; + +import { createAuthzAminoConverters, createRewardsAminoConverters, RewardsMsgEncoder, rewardsTypes } from './modules'; +import { IArchwayQueryClient, createArchwayQueryClient } from './queryclient'; import { BlockTracking, ContractMetadata, @@ -34,8 +34,8 @@ import { OutstandingRewards, RewardsPool, RewardsRecord, -} from "./types"; -import { connectCometWithBatchClient } from "./utils"; +} from './types'; +import { connectCometWithBatchClient } from './utils'; export interface SigningArchwayClientOptions extends SigningCosmWasmClientOptions { /** @@ -117,8 +117,8 @@ function buildResult(response: DeliverTxResponseWithLogs): TxResult { } const flatFeeRequiredTypes: readonly string[] = [ - "/cosmwasm.wasm.v1.MsgExecuteContract", - "/cosmwasm.wasm.v1.MsgMigrateContract", + '/cosmwasm.wasm.v1.MsgExecuteContract', + '/cosmwasm.wasm.v1.MsgMigrateContract', ]; /** @@ -249,23 +249,23 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch public async setContractMetadata( senderAddress: string, metadata: ContractMetadata, - fee: StdFee | "auto" | number, + fee: StdFee | 'auto' | number, memo?: string, ): Promise { const message = RewardsMsgEncoder.setContractMetadata({ senderAddress, metadata: { contractAddress: metadata.contractAddress, - ownerAddress: metadata.ownerAddress ?? "", - rewardsAddress: metadata.rewardsAddress ?? "", + ownerAddress: metadata.ownerAddress ?? '', + rewardsAddress: metadata.rewardsAddress ?? '', withdrawToWallet: metadata.withdrawToWallet ?? false, }, }); const response = await this.assertSignAndBroadcast(senderAddress, [message], fee, memo); const metadataAttr = logs.findAttribute( response.parsedLogs, - "archway.rewards.v1.ContractMetadataSetEvent", - "metadata", + 'archway.rewards.v1.ContractMetadataSetEvent', + 'metadata', ); /* eslint-disable @typescript-eslint/naming-convention */ const contractMetadata = JSON.parse(metadataAttr.value) as { @@ -306,7 +306,7 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch senderAddress: string, contractAddress: string, flatFee: Coin, - fee: StdFee | "auto" | number, + fee: StdFee | 'auto' | number, memo?: string, ): Promise { const message = RewardsMsgEncoder.setFlatFee({ @@ -317,8 +317,8 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch const response = await this.assertSignAndBroadcast(senderAddress, [message], fee, memo); const flatFeeAttr = logs.findAttribute( response.parsedLogs, - "archway.rewards.v1.ContractFlatFeeSetEvent", - "flat_fee", + 'archway.rewards.v1.ContractFlatFeeSetEvent', + 'flat_fee', ); return { ...buildResult(response), @@ -352,7 +352,7 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch public async withdrawContractRewards( senderAddress: string, limit: number, - fee: StdFee | "auto" | number, + fee: StdFee | 'auto' | number, memo?: string, ): Promise { const rewardsAddress = senderAddress; @@ -366,8 +366,8 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch const firstLogs = response.parsedLogs.find(() => true); const rewardsAttr = firstLogs?.events - .find(event => event.type === "archway.rewards.v1.RewardsWithdrawEvent") - ?.attributes.find(attr => attr.key === "rewards")?.value; + .find(event => event.type === 'archway.rewards.v1.RewardsWithdrawEvent') + ?.attributes.find(attr => attr.key === 'rewards')?.value; const rewards: Coin[] = rewardsAttr ? (JSON.parse(rewardsAttr) as Coin[]) : []; return { @@ -398,12 +398,12 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch public override async signAndBroadcast( signerAddress: string, messages: readonly EncodeObject[], - fee: number | StdFee | "auto", + fee: number | StdFee | 'auto', memo?: string, ): Promise { let usedFee: StdFee; - if (fee === "auto" || typeof fee === "number") { - const gasAdjustment = typeof fee === "number" ? fee : this.gasAdjustment; + if (fee === 'auto' || typeof fee === 'number') { + const gasAdjustment = typeof fee === 'number' ? fee : this.gasAdjustment; usedFee = await this.calculateFee(signerAddress, messages, memo, gasAdjustment); } else { usedFee = fee; @@ -458,7 +458,7 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch messages .filter(({ typeUrl }) => flatFeeRequiredTypes.includes(typeUrl)) .map(async ({ value }) => { - const contractAddress = _.get(value, "contract") as string; + const contractAddress = _.get(value, 'contract') as string; const { flatFee } = await _getContractPremium(contractAddress); return flatFee; }), @@ -474,7 +474,7 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch private async assertSignAndBroadcast( signerAddress: string, messages: readonly EncodeObject[], - fee: StdFee | "auto" | number, + fee: StdFee | 'auto' | number, memo?: string, ): Promise { const response = await this.signAndBroadcast(signerAddress, messages, fee, memo); @@ -501,7 +501,7 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch public override async withdrawRewards( delegatorAddress: string, validatorAddress: string, - fee: number | StdFee | "auto", + fee: number | StdFee | 'auto', memo?: string, ): Promise { return await super.withdrawRewards(delegatorAddress, validatorAddress, fee, memo); @@ -537,7 +537,7 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch account => account.address === signerAddress, ); if (!accountFromSigner) { - throw new Error("Failed to retrieve account from signer"); + throw new Error('Failed to retrieve account from signer'); } const pubkey = encodeSecp256k1Pubkey(accountFromSigner.pubkey); const { sequence } = await this.getSequence(signerAddress); From 9ae2455fe24e699f38e2dceb4b6ed30c3b8ee30c Mon Sep 17 00:00:00 2001 From: eliasmpw Date: Wed, 11 Dec 2024 16:52:32 +0100 Subject: [PATCH 3/5] test: add unit tests --- packages/arch3-core/src/archwayclient.ts | 1 - .../arch3-core/src/modules/authz/tx.spec.ts | 81 +++++++++++ packages/arch3-core/src/modules/authz/tx.ts | 135 +++++++++--------- .../arch3-core/src/signingarchwayclient.ts | 2 +- 4 files changed, 152 insertions(+), 67 deletions(-) create mode 100644 packages/arch3-core/src/modules/authz/tx.spec.ts diff --git a/packages/arch3-core/src/archwayclient.ts b/packages/arch3-core/src/archwayclient.ts index a91ff47b..66cdbc47 100644 --- a/packages/arch3-core/src/archwayclient.ts +++ b/packages/arch3-core/src/archwayclient.ts @@ -3,7 +3,6 @@ import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'; import { EncodeObject } from '@cosmjs/proto-signing'; import { HttpEndpoint, - RpcClient, HttpBatchClient, HttpBatchClientOptions, CometClient, diff --git a/packages/arch3-core/src/modules/authz/tx.spec.ts b/packages/arch3-core/src/modules/authz/tx.spec.ts new file mode 100644 index 00000000..4dc3b76e --- /dev/null +++ b/packages/arch3-core/src/modules/authz/tx.spec.ts @@ -0,0 +1,81 @@ +import { GenericAuthorization } from 'cosmjs-types/cosmos/authz/v1beta1/authz'; +import { MsgGrant, MsgRevoke } from 'cosmjs-types/cosmos/authz/v1beta1/tx'; +import { SendAuthorization } from 'cosmjs-types/cosmos/bank/v1beta1/authz'; +import { MsgWithdrawDelegatorReward } from 'cosmjs-types/cosmos/distribution/v1beta1/tx'; + +import { createAuthzAminoConverters } from '.'; + +const aminoConverters = createAuthzAminoConverters(); + +const archwayd = { + chainId: 'local-1', + endpoint: 'http://localhost:26657', + prefix: 'archway', + denom: process.env.DENOM || 'aarch', +}; + +const granterAddress = 'archway1ecak50zhujddqd639xw4ejghnyrrc6jlwnlgwt'; +const granteeAddress = 'archway1vrzhff4gnnye4qt37stn9v6xker333cdew6xdt'; + +describe('Amino converter for /cosmos.authz.v1beta1.MsgGrant', () => { + describe('Grants generic authorization with /cosmos.authz.v1beta1.GenericAuthorization', () => { + it('encodes and decodes back to the same value', () => { + const message = { + granter: granterAddress, + grantee: granteeAddress, + grant: { + authorization: { + typeUrl: GenericAuthorization.typeUrl, + value: GenericAuthorization.encode( + GenericAuthorization.fromPartial({ + msg: MsgWithdrawDelegatorReward.typeUrl, + }), + ).finish(), + }, + expiration: undefined, + }, + }; + const toAmino = aminoConverters[MsgGrant.typeUrl].toAmino(message); + const result = aminoConverters[MsgGrant.typeUrl].fromAmino(toAmino); + + expect(message).toStrictEqual(result); + }); + }); + describe('Grants generic authorization with /cosmos.authz.v1beta1.SendAuthorization', () => { + it('encodes and decodes back to the same value', () => { + const message = { + granter: granterAddress, + grantee: granteeAddress, + grant: { + authorization: { + typeUrl: SendAuthorization.typeUrl, + value: SendAuthorization.encode( + SendAuthorization.fromPartial({ + spendLimit: [{ denom: archwayd.denom, amount: '1' }], + }), + ).finish(), + }, + expiration: undefined, + }, + }; + const toAmino = aminoConverters[MsgGrant.typeUrl].toAmino(message); + const result = aminoConverters[MsgGrant.typeUrl].fromAmino(toAmino); + + expect(message).toStrictEqual(result); + }); + }); +}); + +describe('Amino converter for /cosmos.authz.v1beta1.MsgRevoke', () => { + it('encodes and decodes back to the same value', () => { + const message = { + granter: granterAddress, + grantee: granteeAddress, + msgTypeUrl: MsgWithdrawDelegatorReward.typeUrl, + }; + const toAmino = aminoConverters[MsgRevoke.typeUrl].toAmino(message); + const result = aminoConverters[MsgRevoke.typeUrl].fromAmino(toAmino); + + expect(message).toStrictEqual(result); + }); +}); diff --git a/packages/arch3-core/src/modules/authz/tx.ts b/packages/arch3-core/src/modules/authz/tx.ts index da3e90b3..02828d71 100644 --- a/packages/arch3-core/src/modules/authz/tx.ts +++ b/packages/arch3-core/src/modules/authz/tx.ts @@ -1,44 +1,48 @@ -import { AminoConverters } from "@cosmjs/stargate"; -import { GenericAuthorization } from "cosmjs-types/cosmos/authz/v1beta1/authz"; -import { MsgGrant, MsgRevoke } from "cosmjs-types/cosmos/authz/v1beta1/tx"; -import { SendAuthorization } from "cosmjs-types/cosmos/bank/v1beta1/authz"; -import { Timestamp } from "cosmjs-types/google/protobuf/timestamp"; -import { fromJsonTimestamp, fromTimestamp } from "cosmjs-types/helpers"; +import { AminoConverters } from '@cosmjs/stargate'; +import { GenericAuthorization } from 'cosmjs-types/cosmos/authz/v1beta1/authz'; +import { MsgGrant, MsgRevoke } from 'cosmjs-types/cosmos/authz/v1beta1/tx'; +import { SendAuthorization } from 'cosmjs-types/cosmos/bank/v1beta1/authz'; +import { Timestamp } from 'cosmjs-types/google/protobuf/timestamp'; +import { fromJsonTimestamp, fromTimestamp } from 'cosmjs-types/helpers'; -export function createAuthzAminoConverters(): AminoConverters { +/** + * Creates the Amino converters for the Authz tx messages + * + * @returns to be used in a client + */ +export const createAuthzAminoConverters = (): AminoConverters => { return { [MsgGrant.typeUrl]: { - aminoType: "cosmos-sdk/MsgGrant", + aminoType: 'cosmos-sdk/MsgGrant', toAmino: ({ granter, grantee, grant }) => { if (!grant || !grant.authorization) { throw new Error(`Unsupported grant type: '${grant?.authorization?.typeUrl}'`); } let authorizationValue; switch (grant?.authorization?.typeUrl) { - case GenericAuthorization.typeUrl: { - const generic = GenericAuthorization.decode(grant.authorization.value); - authorizationValue = { - type: "cosmos-sdk/GenericAuthorization", - value: { - msg: generic.msg, - }, - }; - break; - } - case SendAuthorization.typeUrl: { - const spend = SendAuthorization.decode(grant.authorization.value); - authorizationValue = { - type: "cosmos-sdk/SendAuthorization", - value: { - spend_limit: spend.spendLimit, - }, - }; - break; - } - default: - throw new Error(`Unsupported grant type: '${grant.authorization.typeUrl}'`); + case GenericAuthorization.typeUrl: { + const generic = GenericAuthorization.decode(grant.authorization.value); + authorizationValue = { + type: 'cosmos-sdk/GenericAuthorization', + value: { + msg: generic.msg, + }, + }; + break; + } + case SendAuthorization.typeUrl: { + const spend = SendAuthorization.decode(grant.authorization.value); + authorizationValue = { + type: 'cosmos-sdk/SendAuthorization', + value: { + spend_limit: spend.spendLimit, + }, + }; + break; + } + default: + throw new Error(`Unsupported grant type: '${grant.authorization.typeUrl}'`); } - const expiration = grant.expiration?.seconds; return { granter, @@ -47,8 +51,8 @@ export function createAuthzAminoConverters(): AminoConverters { authorization: authorizationValue, expiration: grant.expiration ? fromTimestamp(grant.expiration) - .toISOString() - .replace(/\.\d{3}Z$/, "Z") + .toISOString() + .replace(/\.\d{3}Z$/, 'Z') : undefined, }, }; @@ -57,28 +61,28 @@ export function createAuthzAminoConverters(): AminoConverters { const authorizationType = grant?.authorization?.type; let authorizationValue; switch (authorizationType) { - case "cosmos-sdk/GenericAuthorization": { - authorizationValue = { - typeUrl: GenericAuthorization.typeUrl, - value: GenericAuthorization.encode({ - msg: grant.authorization.value.msg, - }).finish(), - }; - break; - } - case "cosmos-sdk/SendAuthorization": { - authorizationValue = { - typeUrl: SendAuthorization.typeUrl, - value: SendAuthorization.encode( - SendAuthorization.fromPartial({ - spendLimit: grant.authorization.value.spend_limit, - }), - ).finish(), - }; - break; - } - default: - throw new Error(`Unsupported grant type: '${grant?.authorization?.type}'`); + case 'cosmos-sdk/GenericAuthorization': { + authorizationValue = { + typeUrl: GenericAuthorization.typeUrl, + value: GenericAuthorization.encode({ + msg: grant.authorization.value.msg, + }).finish(), + }; + break; + } + case 'cosmos-sdk/SendAuthorization': { + authorizationValue = { + typeUrl: SendAuthorization.typeUrl, + value: SendAuthorization.encode( + SendAuthorization.fromPartial({ + spendLimit: grant.authorization.value.spend_limit, + }), + ).finish(), + }; + break; + } + default: + throw new Error(`Unsupported grant type: '${grant?.authorization?.type}'`); } return MsgGrant.fromPartial({ granter, @@ -91,18 +95,19 @@ export function createAuthzAminoConverters(): AminoConverters { }, }, [MsgRevoke.typeUrl]: { - aminoType: "cosmos-sdk/MsgRevoke", - toAmino: ({ granter, grantee, msgTypeUrl }) => ({ + aminoType: 'cosmos-sdk/MsgRevoke', + toAmino: ({ granter, grantee, msgTypeUrl }) => { + return { + granter, + grantee, + msg_type_url: msgTypeUrl, + }; + }, + fromAmino: ({ granter, grantee, msg_type_url }) => MsgRevoke.fromPartial({ granter, grantee, - msg_type_url: msgTypeUrl, + msgTypeUrl: msg_type_url, }), - fromAmino: ({ granter, grantee, msg_type_url }) => - MsgRevoke.fromPartial({ - granter, - grantee, - msgTypeUrl: msg_type_url, - }), }, }; -} +}; diff --git a/packages/arch3-core/src/signingarchwayclient.ts b/packages/arch3-core/src/signingarchwayclient.ts index b20f54b4..2b0a3ad5 100644 --- a/packages/arch3-core/src/signingarchwayclient.ts +++ b/packages/arch3-core/src/signingarchwayclient.ts @@ -19,7 +19,7 @@ import { logs, StdFee, } from '@cosmjs/stargate'; -import { CometClient, HttpBatchClient, HttpBatchClientOptions, RpcClient, connectComet } from '@cosmjs/tendermint-rpc'; +import { CometClient, HttpBatchClient, HttpBatchClientOptions, connectComet } from '@cosmjs/tendermint-rpc'; import { assertDefined } from '@cosmjs/utils'; import { SimulateResponse } from 'cosmjs-types/cosmos/tx/v1beta1/service'; import _ from 'lodash'; From 8334f53240926086de8c96d86ac8311de3a1b8ef Mon Sep 17 00:00:00 2001 From: eliasmpw Date: Wed, 11 Dec 2024 18:33:47 +0100 Subject: [PATCH 4/5] fix: lint --- packages/arch3-core/src/archwayclient.ts | 5 ++- .../arch3-core/src/modules/authz/tx.spec.ts | 2 +- packages/arch3-core/src/modules/authz/tx.ts | 36 ++++++++++++++----- .../arch3-core/src/signingarchwayclient.ts | 8 ++--- packages/arch3-core/src/utils.ts | 2 ++ 5 files changed, 36 insertions(+), 17 deletions(-) diff --git a/packages/arch3-core/src/archwayclient.ts b/packages/arch3-core/src/archwayclient.ts index 66cdbc47..bc7f34d8 100644 --- a/packages/arch3-core/src/archwayclient.ts +++ b/packages/arch3-core/src/archwayclient.ts @@ -3,7 +3,6 @@ import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'; import { EncodeObject } from '@cosmjs/proto-signing'; import { HttpEndpoint, - HttpBatchClient, HttpBatchClientOptions, CometClient, connectComet, @@ -47,11 +46,11 @@ export class ArchwayClient extends CosmWasmClient implements IArchwayQueryClient } /** - * Creates an instance by connecting to the given Tendermint/CometBFT RPC endpoint using an {@link HttpBatchClient} to batch + * Creates an instance by connecting to the given Tendermint/CometBFT RPC endpoint using an HttpBatchClient to batch * multiple requests and reduce queries to the server. * * @param endpoint - String URL of the RPC endpoint to connect or an {@link HttpEndpoint} object. - * @param options - Optional configuration to control how the {@link HttpBatchClient} will batch requests. + * @param options - Optional configuration to control how the HttpBatchClient will batch requests. * @returns An {@link ArchwayClient} connected to the endpoint. * * @remarks This factory method doesn't support WebSocket endpoints. diff --git a/packages/arch3-core/src/modules/authz/tx.spec.ts b/packages/arch3-core/src/modules/authz/tx.spec.ts index 4dc3b76e..4cceb536 100644 --- a/packages/arch3-core/src/modules/authz/tx.spec.ts +++ b/packages/arch3-core/src/modules/authz/tx.spec.ts @@ -41,7 +41,7 @@ describe('Amino converter for /cosmos.authz.v1beta1.MsgGrant', () => { expect(message).toStrictEqual(result); }); }); - describe('Grants generic authorization with /cosmos.authz.v1beta1.SendAuthorization', () => { + describe('Grants send authorization with /cosmos.authz.v1beta1.SendAuthorization', () => { it('encodes and decodes back to the same value', () => { const message = { granter: granterAddress, diff --git a/packages/arch3-core/src/modules/authz/tx.ts b/packages/arch3-core/src/modules/authz/tx.ts index 02828d71..854d562d 100644 --- a/packages/arch3-core/src/modules/authz/tx.ts +++ b/packages/arch3-core/src/modules/authz/tx.ts @@ -1,4 +1,4 @@ -import { AminoConverters } from '@cosmjs/stargate'; +import { AminoConverters, Coin } from '@cosmjs/stargate'; import { GenericAuthorization } from 'cosmjs-types/cosmos/authz/v1beta1/authz'; import { MsgGrant, MsgRevoke } from 'cosmjs-types/cosmos/authz/v1beta1/tx'; import { SendAuthorization } from 'cosmjs-types/cosmos/bank/v1beta1/authz'; @@ -14,11 +14,11 @@ export const createAuthzAminoConverters = (): AminoConverters => { return { [MsgGrant.typeUrl]: { aminoType: 'cosmos-sdk/MsgGrant', - toAmino: ({ granter, grantee, grant }) => { + toAmino: ({ granter, grantee, grant }: MsgGrant) => { if (!grant || !grant.authorization) { throw new Error(`Unsupported grant type: '${grant?.authorization?.typeUrl}'`); } - let authorizationValue; + let authorizationValue: { type: string; value: any }; switch (grant?.authorization?.typeUrl) { case GenericAuthorization.typeUrl: { const generic = GenericAuthorization.decode(grant.authorization.value); @@ -35,7 +35,10 @@ export const createAuthzAminoConverters = (): AminoConverters => { authorizationValue = { type: 'cosmos-sdk/SendAuthorization', value: { + // eslint-disable-next-line camelcase, @typescript-eslint/naming-convention spend_limit: spend.spendLimit, + // eslint-disable-next-line camelcase, @typescript-eslint/naming-convention + allow_list: spend.allowList, }, }; break; @@ -57,15 +60,26 @@ export const createAuthzAminoConverters = (): AminoConverters => { }, }; }, - fromAmino: ({ granter, grantee, grant }) => { + fromAmino: ({ + granter, + grantee, + grant, + }: { + granter: string; + grantee: string; + grant: { + authorization: { type: string; value: Record }; + expiration: { seconds: any; nanos: any } | undefined; + }; + }) => { const authorizationType = grant?.authorization?.type; - let authorizationValue; + let authorizationValue: { typeUrl: string; value: any }; switch (authorizationType) { case 'cosmos-sdk/GenericAuthorization': { authorizationValue = { typeUrl: GenericAuthorization.typeUrl, value: GenericAuthorization.encode({ - msg: grant.authorization.value.msg, + msg: grant.authorization.value.msg as string, }).finish(), }; break; @@ -75,7 +89,8 @@ export const createAuthzAminoConverters = (): AminoConverters => { typeUrl: SendAuthorization.typeUrl, value: SendAuthorization.encode( SendAuthorization.fromPartial({ - spendLimit: grant.authorization.value.spend_limit, + spendLimit: grant.authorization.value.spend_limit as Coin[], + allowList: grant.authorization.value.allow_list as string[] | undefined, }), ).finish(), }; @@ -96,16 +111,19 @@ export const createAuthzAminoConverters = (): AminoConverters => { }, [MsgRevoke.typeUrl]: { aminoType: 'cosmos-sdk/MsgRevoke', - toAmino: ({ granter, grantee, msgTypeUrl }) => { + toAmino: ({ granter, grantee, msgTypeUrl }: MsgRevoke) => { return { granter, grantee, + // eslint-disable-next-line camelcase, @typescript-eslint/naming-convention msg_type_url: msgTypeUrl, }; }, - fromAmino: ({ granter, grantee, msg_type_url }) => MsgRevoke.fromPartial({ + // eslint-disable-next-line camelcase, @typescript-eslint/naming-convention + fromAmino: ({ granter, grantee, msg_type_url }: { granter: string; grantee: string; msg_type_url: string }) => MsgRevoke.fromPartial({ granter, grantee, + // eslint-disable-next-line camelcase msgTypeUrl: msg_type_url, }), }, diff --git a/packages/arch3-core/src/signingarchwayclient.ts b/packages/arch3-core/src/signingarchwayclient.ts index 2b0a3ad5..c4b46be2 100644 --- a/packages/arch3-core/src/signingarchwayclient.ts +++ b/packages/arch3-core/src/signingarchwayclient.ts @@ -19,7 +19,7 @@ import { logs, StdFee, } from '@cosmjs/stargate'; -import { CometClient, HttpBatchClient, HttpBatchClientOptions, connectComet } from '@cosmjs/tendermint-rpc'; +import { CometClient, HttpBatchClientOptions, connectComet } from '@cosmjs/tendermint-rpc'; import { assertDefined } from '@cosmjs/utils'; import { SimulateResponse } from 'cosmjs-types/cosmos/tx/v1beta1/service'; import _ from 'lodash'; @@ -156,7 +156,7 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch /** * Creates an instance by connecting to the given Tendermint RPC endpoint. * - * @param endpoint - String URL of the RPC endpoint to connect or an `HttpEndpoint` object. + * @param endpoint - String URL of the RPC endpoint to connect or an {@link HttpEndpoint} object. * @param signer - The transaction signer configuration. * @param options - Options for the signing client. * @returns A {@link SigningArchwayClient} connected to the endpoint. @@ -171,13 +171,13 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch } /** - * Creates an instance by connecting to the given Tendermint RPC endpoint using an {@link HttpBatchClient} to batch + * Creates an instance by connecting to the given Tendermint RPC endpoint using an HttpBatchClient to batch * multiple requests and reduce queries to the server. * * @param endpoint - String URL of the RPC endpoint to connect or an {@link HttpEndpoint} object. * @param signer - The transaction signer configuration. * @param options - Options for the signing client. - * @param batchClientOptions - Optional configuration to control how the {@link HttpBatchClient} will batch requests. + * @param batchClientOptions - Optional configuration to control how the HttpBatchClient will batch requests. * @returns A {@link SigningArchwayClient} connected to the endpoint. * * @remarks This factory method doesn't support WebSocket endpoints. diff --git a/packages/arch3-core/src/utils.ts b/packages/arch3-core/src/utils.ts index 1fb3678c..61f69d0e 100644 --- a/packages/arch3-core/src/utils.ts +++ b/packages/arch3-core/src/utils.ts @@ -14,6 +14,8 @@ import { * * @param endpoint - String URL of the RPC endpoint to connect or an {@link HttpEndpoint} object. * @param options - Optional configuration to control how the {@link HttpBatchClient} will batch requests. + * + * @returns A connected {@link CometClient} */ export async function connectCometWithBatchClient( endpoint: string | HttpEndpoint, From a1725a2b6e6438ff5d742b80dabe27feaeacfebb Mon Sep 17 00:00:00 2001 From: eliasmpw Date: Wed, 11 Dec 2024 19:33:40 +0100 Subject: [PATCH 5/5] test: add allowList to tested fields --- packages/arch3-core/src/modules/authz/tx.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/arch3-core/src/modules/authz/tx.spec.ts b/packages/arch3-core/src/modules/authz/tx.spec.ts index 4cceb536..9485c255 100644 --- a/packages/arch3-core/src/modules/authz/tx.spec.ts +++ b/packages/arch3-core/src/modules/authz/tx.spec.ts @@ -52,6 +52,7 @@ describe('Amino converter for /cosmos.authz.v1beta1.MsgGrant', () => { value: SendAuthorization.encode( SendAuthorization.fromPartial({ spendLimit: [{ denom: archwayd.denom, amount: '1' }], + allowList: [granteeAddress], }), ).finish(), },