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: authz amino converters #126

Merged
merged 5 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
2 changes: 1 addition & 1 deletion packages/arch3-core/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
6 changes: 2 additions & 4 deletions packages/arch3-core/src/archwayclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate';
import { EncodeObject } from '@cosmjs/proto-signing';
import {
HttpEndpoint,
RpcClient,
HttpBatchClient,
HttpBatchClientOptions,
CometClient,
connectComet,
Expand Down Expand Up @@ -48,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.
Expand Down
1 change: 1 addition & 0 deletions packages/arch3-core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export { ArchwayClient } from './archwayclient';

export {
createAuthzAminoConverters,
createRewardsAminoConverters,
RewardsExtension,
RewardsMsgEncoder,
Expand Down
1 change: 1 addition & 0 deletions packages/arch3-core/src/modules/authz/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './tx';
81 changes: 81 additions & 0 deletions packages/arch3-core/src/modules/authz/tx.spec.ts
Original file line number Diff line number Diff line change
@@ -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 send 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' }],
pyncz marked this conversation as resolved.
Show resolved Hide resolved
}),
).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);
});
});
131 changes: 131 additions & 0 deletions packages/arch3-core/src/modules/authz/tx.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
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';
import { Timestamp } from 'cosmjs-types/google/protobuf/timestamp';
import { fromJsonTimestamp, fromTimestamp } from 'cosmjs-types/helpers';

/**
* 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',
toAmino: ({ granter, grantee, grant }: MsgGrant) => {
if (!grant || !grant.authorization) {
throw new Error(`Unsupported grant type: '${grant?.authorization?.typeUrl}'`);
}
let authorizationValue: { type: string; value: any };
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: {
// 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;
}
default:
throw new Error(`Unsupported grant type: '${grant.authorization.typeUrl}'`);
}

return {
granter,
grantee,
grant: {
authorization: authorizationValue,
expiration: grant.expiration
? fromTimestamp(grant.expiration)
.toISOString()
.replace(/\.\d{3}Z$/, 'Z')
: undefined,
},
};
},
fromAmino: ({
granter,
grantee,
grant,
}: {
granter: string;
grantee: string;
grant: {
authorization: { type: string; value: Record<string, unknown> };
expiration: { seconds: any; nanos: any } | undefined;
};
}) => {
const authorizationType = grant?.authorization?.type;
let authorizationValue: { typeUrl: string; value: any };
switch (authorizationType) {
case 'cosmos-sdk/GenericAuthorization': {
authorizationValue = {
typeUrl: GenericAuthorization.typeUrl,
value: GenericAuthorization.encode({
msg: grant.authorization.value.msg as string,
}).finish(),
};
break;
}
case 'cosmos-sdk/SendAuthorization': {
authorizationValue = {
typeUrl: SendAuthorization.typeUrl,
value: SendAuthorization.encode(
SendAuthorization.fromPartial({
spendLimit: grant.authorization.value.spend_limit as Coin[],
allowList: grant.authorization.value.allow_list as string[] | undefined,
}),
).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 }: MsgRevoke) => {
return {
granter,
grantee,
// eslint-disable-next-line camelcase, @typescript-eslint/naming-convention
msg_type_url: msgTypeUrl,
};
},
// 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,
}),
},
};
};
1 change: 1 addition & 0 deletions packages/arch3-core/src/modules/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './authz';
export * from './rewards';
export * from './tx';
11 changes: 6 additions & 5 deletions packages/arch3-core/src/signingarchwayclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ import {
logs,
StdFee,
} from '@cosmjs/stargate';
import { CometClient, HttpBatchClient, HttpBatchClientOptions, RpcClient, 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';

import { createRewardsAminoConverters, RewardsMsgEncoder, rewardsTypes } from './modules';
import { createAuthzAminoConverters, createRewardsAminoConverters, RewardsMsgEncoder, rewardsTypes } from './modules';
import { IArchwayQueryClient, createArchwayQueryClient } from './queryclient';
import {
BlockTracking,
Expand Down Expand Up @@ -141,6 +141,7 @@ export class SigningArchwayClient extends SigningCosmWasmClient implements IArch
...createDefaultAminoConverters(),
...createWasmAminoConverters(),
...createRewardsAminoConverters(),
...createAuthzAminoConverters(),
}),
gasAdjustment = defaultGasAdjustment,
} = options;
Expand All @@ -155,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.
Expand All @@ -170,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.
Expand Down
2 changes: 2 additions & 0 deletions packages/arch3-core/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion packages/arch3-proto/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
Loading