diff --git a/package-lock.json b/package-lock.json index 8f4d4e82..85863ae3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,17 @@ { "name": "@terra-money/feather.js", - "version": "2.0.0-beta.1", + "version": "2.0.0-beta.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@terra-money/feather.js", - "version": "2.0.0-beta.1", + "version": "2.0.0-beta.5", "license": "MIT", "dependencies": { "@ethersproject/bytes": "^5.7.0", "@terra-money/legacy.proto": "npm:@terra-money/terra.proto@^0.1.7", - "@terra-money/terra.proto": "^4.0.3", + "@terra-money/terra.proto": "^4.0.4", "assert": "^2.0.0", "axios": "^0.27.2", "bech32": "^2.0.0", @@ -1999,9 +1999,9 @@ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, "node_modules/@terra-money/terra.proto": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@terra-money/terra.proto/-/terra.proto-4.0.3.tgz", - "integrity": "sha512-VSSX8ZA+RB7N1CMSMCIBXh8hoo4ZVAE7DEZ6qr+YPBhWm5Tkdwo4SVestCJouMyPNfp/Kf6ii8U8zAwedOUcNw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@terra-money/terra.proto/-/terra.proto-4.0.4.tgz", + "integrity": "sha512-Xju3ObFvMWXCDpeOXwa+WpmcbvUFOgJ4shSSfbgocnX5q3250aTaIAaycxkArUtg1QoqV4B5qoboRAplMHYDZw==", "dependencies": { "@improbable-eng/grpc-web": "^0.14.1", "browser-headers": "^0.4.1", diff --git a/package.json b/package.json index 881eda0e..7825e7e5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@terra-money/feather.js", - "version": "2.0.0-beta.1", + "version": "2.0.0-beta.5", "description": "The JavaScript SDK for Terra and Feather chains", "license": "MIT", "author": "Terraform Labs, PTE.", @@ -87,7 +87,7 @@ "dependencies": { "@ethersproject/bytes": "^5.7.0", "@terra-money/legacy.proto": "npm:@terra-money/terra.proto@^0.1.7", - "@terra-money/terra.proto": "^4.0.3", + "@terra-money/terra.proto": "^4.0.4", "assert": "^2.0.0", "axios": "^0.27.2", "bech32": "^2.0.0", diff --git a/src/client/lcd/LCDClient.ts b/src/client/lcd/LCDClient.ts index 655d744d..c39b589a 100644 --- a/src/client/lcd/LCDClient.ts +++ b/src/client/lcd/LCDClient.ts @@ -24,6 +24,8 @@ import { AllianceAPI } from './api/AllianceAPI'; import { PobAPI } from './api/PobAPI'; import { FeeshareAPI } from './api/FeeshareAPI'; import { GovV1API } from './api/GovV1API'; +import { ICAv1API } from './api/ICAv1API'; +import { ICQv1API } from './api/ICQv1API'; export interface LCDClientConfig { /** @@ -114,6 +116,8 @@ export class LCDClient { public wasm: WasmAPI; public tx: TxAPI; public ibc: IbcAPI; + public icaV1: ICAv1API; + public icqV1: ICQv1API; public ibcTransfer: IbcTransferAPI; public pob: PobAPI; public feeshare: FeeshareAPI; @@ -157,6 +161,8 @@ export class LCDClient { this.tendermint = new TendermintAPI(this); this.wasm = new WasmAPI(this); this.ibc = new IbcAPI(this); + this.icaV1 = new ICAv1API(this); + this.icqV1 = new ICQv1API(this); this.ibcTransfer = new IbcTransferAPI(this); this.tx = new TxAPI(this); this.pob = new PobAPI(this); diff --git a/src/client/lcd/api/ICAv1API.ts b/src/client/lcd/api/ICAv1API.ts new file mode 100644 index 00000000..8c2a4186 --- /dev/null +++ b/src/client/lcd/api/ICAv1API.ts @@ -0,0 +1,64 @@ +import { Params as HostParams } from '@terra-money/terra.proto/ibc/applications/interchain_accounts/host/v1/host'; +import { Params as ControllerParams } from '@terra-money/terra.proto/ibc/applications/interchain_accounts/controller/v1/controller'; +import { QueryInterchainAccountResponse } from '@terra-money/terra.proto/ibc/applications/interchain_accounts/controller/v1/query'; +import { APIParams } from '../APIRequester'; +import { LCDClient } from '../LCDClient'; +import { BaseAPI } from './BaseAPI'; +import { AccAddress } from 'core'; + +export class ICAv1API extends BaseAPI { + constructor(public lcd: LCDClient) { + super(lcd.apiRequesters, lcd.config); + } + + /** + * Query interchain account host module params + * + * @tags Query + * @name params + * @request GET:/ibc/apps/interchain_accounts/host/v1/params + */ + public async hostParams(chainId: string, params: Partial = {}) { + return this.getReqFromChainID(chainId).get<{ params: HostParams }>( + `/ibc/apps/interchain_accounts/host/v1/params`, + params + ); + } + + /** + * Query interchain account controller module params + * + * @tags Query + * @name params + * @request GET:/ibc/apps/interchain_accounts/controller/v1/params + */ + public async controllerParams( + chainId: string, + params: Partial = {} + ) { + return this.getReqFromChainID(chainId).get<{ params: ControllerParams }>( + `/ibc/apps/interchain_accounts/controller/v1/params`, + params + ); + } + + /** + * Returns the interchain account address for a given owner address on a given connection + * + * @tags Query + * @name params + * @request GET:/ibc/apps/interchain_accounts/controller/v1/owners/${ownerAddr}/connections/${connectionId} + */ + public async controllerAccountAddress( + ownerAddr: AccAddress, + connectionId: string, + params: Partial = {} + ) { + return this.getReqFromAddress( + ownerAddr + ).get( + `/ibc/apps/interchain_accounts/controller/v1/owners/${ownerAddr}/connections/${connectionId}`, + params + ); + } +} diff --git a/src/client/lcd/api/ICQv1API.ts b/src/client/lcd/api/ICQv1API.ts new file mode 100644 index 00000000..09e78e1d --- /dev/null +++ b/src/client/lcd/api/ICQv1API.ts @@ -0,0 +1,25 @@ +import { Params } from '@terra-money/terra.proto/icq/v1/icq'; +import { APIParams } from '../APIRequester'; +import { LCDClient } from '../LCDClient'; +import { BaseAPI } from './BaseAPI'; + +export class ICQv1API extends BaseAPI { + constructor(public lcd: LCDClient) { + super(lcd.apiRequesters, lcd.config); + } + + /** + * Query all parameters associated with the icq module. + * + * @tags Query + * @name params + * @summary Query icq module params + * @request GET:/async-icq/v1/params + */ + public async params(chainId: string, params: Partial = {}) { + return this.getReqFromChainID(chainId).get<{ params: Params }>( + `/async-icq/v1/params`, + params + ); + } +} diff --git a/src/core/Msg.ts b/src/core/Msg.ts index f76dbb8d..541db48e 100644 --- a/src/core/Msg.ts +++ b/src/core/Msg.ts @@ -111,6 +111,11 @@ import { MsgRegisterFeeShare, MsgUpdateFeeShare, } from './feeshare'; +import { + ICAMsg, + MsgRegisterInterchainAccount, + MsgSendTx, +} from './ica/controller/v1/msgs'; export type Msg = | BankMsg @@ -127,6 +132,7 @@ export type Msg = | IbcClientMsg | IbcConnectionMsg | IbcChannelMsg + | ICAMsg | AllianceMsg | CustomMsg | CrisisMsg @@ -169,6 +175,7 @@ export namespace Msg { | IbcClientMsg.Data | IbcConnectionMsg.Data | IbcChannelMsg.Data + | ICAMsg.Data | AllianceMsg.Data | CustomMsg.Data | CrisisMsg.Data @@ -188,6 +195,7 @@ export namespace Msg { | VestingMsg.Proto | WasmMsg.Proto | IbcTransferMsg.Proto + | ICAMsg.Proto | IbcClientMsg.Proto | IbcConnectionMsg.Proto | IbcChannelMsg.Proto @@ -492,6 +500,7 @@ export namespace Msg { return MsgAminoCustom.fromAmino(data as any, isClassic); } } + export function fromData(data: Msg.Data, isClassic?: boolean): Msg { switch (data['@type']) { // alliance @@ -616,6 +625,12 @@ export namespace Msg { case '/ibc.applications.transfer.v1.MsgTransfer': return MsgTransfer.fromData(data, isClassic); + // ibc ica + case '/ibc.applications.interchain_accounts.controller.v1.MsgRegisterInterchainAccount': + return MsgRegisterInterchainAccount.fromData(data, isClassic); + case '/ibc.applications.interchain_accounts.controller.v1.MsgSendTx': + return MsgSendTx.fromData(data, isClassic); + // ibc-client case '/ibc.core.client.v1.MsgCreateClient': return MsgCreateClient.fromData(data, isClassic); @@ -814,6 +829,12 @@ export namespace Msg { case '/ibc.applications.transfer.v1.MsgTransfer': return MsgTransfer.unpackAny(proto, isClassic); + // ibc ica + case '/ibc.applications.interchain_accounts.controller.v1.MsgRegisterInterchainAccount': + return MsgRegisterInterchainAccount.unpackAny(proto, isClassic); + case '/ibc.applications.interchain_accounts.controller.v1.MsgSendTx': + return MsgSendTx.unpackAny(proto, isClassic); + // ibc-client case '/ibc.core.client.v1.MsgCreateClient': return MsgCreateClient.unpackAny(proto, isClassic); diff --git a/src/core/ica/controller/v1/CosmosTx.ts b/src/core/ica/controller/v1/CosmosTx.ts new file mode 100644 index 00000000..66de526d --- /dev/null +++ b/src/core/ica/controller/v1/CosmosTx.ts @@ -0,0 +1,91 @@ +import { Msg } from '../../../Msg'; +import { JSONSerializable } from '../../../../util/json'; +import { CosmosTx as CosmosTx_pb } from '@terra-money/terra.proto/ibc/applications/interchain_accounts/v1/packet'; +import { Any } from '@terra-money/terra.proto/google/protobuf/any'; + +/** + * CosmosTx represents the encoded transaction + */ +export class CosmosTx extends JSONSerializable< + {}, + CosmosTx.Data, + CosmosTx.Proto +> { + /** + * @param messages all proto messages that are part of the transaction + */ + constructor(public messages: Msg[]) { + super(); + } + + public static fromAmino(data: CosmosTx.Amino, _?: boolean): CosmosTx { + _; + data; + throw new Error('Amino not supported on CosmosTx'); + } + + public toAmino(_?: boolean): CosmosTx.Amino { + _; + throw new Error('Amino not supported on CosmosTx'); + } + + public static fromData(data: CosmosTx.Data, _?: boolean): CosmosTx { + _; + console.log('CosmosTx#fromData', data); + const parsedData = Buffer.from(data, 'base64'); + const { messages } = CosmosTx_pb.decode(parsedData); + return new CosmosTx(messages.map(msg => Msg.fromProto(msg))); + } + + public toData(_?: boolean): CosmosTx.Data { + _; + console.log('CosmosTx#toData', this.messages); + const { messages } = this; + + const ct = CosmosTx_pb.encode({ + messages: messages.map(msg => msg.packAny()) as Any[], + }); + + return Buffer.from(ct.finish()).toString('base64'); + } + + public static fromProto(proto: CosmosTx.Proto, _?: boolean): CosmosTx { + _; + console.log('CosmosTx#fromProto', proto); + return new CosmosTx(proto.messages.map(msg => Msg.fromProto(msg))); + } + + public toProto(_?: boolean): CosmosTx.Proto { + _; + console.log('CosmosTx#toProto', this.messages); + const { messages } = this; + + const ct = CosmosTx_pb.encode({ + messages: messages.map(msg => msg.packAny()) as Any[], + }); + + return Buffer.from(ct.finish()).toString('base64') as any; + } + + public packAny(isClassic?: boolean): Any { + console.log('CosmosTx#packAny', this.messages); + return Any.fromPartial({ + value: this.toProto(isClassic) as any, + }); + } + + public static unpackAny(msgAny: Any, isClassic?: boolean): CosmosTx { + console.log('CosmosTx#unpackAny', msgAny); + return CosmosTx.fromProto(CosmosTx_pb.decode(msgAny.value), isClassic); + } +} + +export namespace CosmosTx { + export interface Amino { + value: {}; + } + type Base64String = string; + export type Data = Base64String; + + export type Proto = CosmosTx_pb; +} diff --git a/src/core/ica/controller/v1/InterchainAccountPacketData.ts b/src/core/ica/controller/v1/InterchainAccountPacketData.ts new file mode 100644 index 00000000..22a39685 --- /dev/null +++ b/src/core/ica/controller/v1/InterchainAccountPacketData.ts @@ -0,0 +1,121 @@ +import { JSONSerializable } from '../../../../util/json'; +import { + InterchainAccountPacketData as InterchainAccountPacketData_pb, + Type, +} from '@terra-money/terra.proto/ibc/applications/interchain_accounts/v1/packet'; +import { CosmosTx } from './CosmosTx'; +import { Any } from '@terra-money/terra.proto/google/protobuf/any'; + +/** + * PacketData represents the encoded data to be executed on + * host chain by the intechain account. + */ +export class InterchainAccountPacketData extends JSONSerializable< + {}, + InterchainAccountPacketData.Data, + InterchainAccountPacketData.Proto +> { + /** + * @param data to be executed on host chain + * @param memo for the transaction to be executed on host chain + * @param type by default is **TYPE_EXECUTE_TX** which means that the Msg is converted from proto TYPE_UNSPECIFIED, TYPE_EXECUTE_TX or UNRECOGNIZED + */ + constructor( + public data: CosmosTx, + public memo: string = '', + public type: Type = Type.TYPE_EXECUTE_TX + ) { + super(); + } + + public static fromAmino( + data: InterchainAccountPacketData.Amino, + _?: boolean + ): InterchainAccountPacketData { + _; + data; + throw new Error('Amino not supported on InterchainAccountPacketData'); + } + + public toAmino(_?: boolean): InterchainAccountPacketData.Amino { + _; + throw new Error('Amino not supported on InterchainAccountPacketData'); + } + + public static fromData( + packetData: InterchainAccountPacketData.Data, + _?: boolean + ): InterchainAccountPacketData { + _; + const { data, memo, type } = packetData; + + return new InterchainAccountPacketData(CosmosTx.fromData(data), memo, type); + } + + public toData(_?: boolean): InterchainAccountPacketData.Data { + _; + const { data, memo, type } = this; + return { + data: data.toData(), + memo, + type, + }; + } + + public static fromProto( + proto: InterchainAccountPacketData.Proto, + _?: boolean + ): InterchainAccountPacketData { + _; + + return new InterchainAccountPacketData( + CosmosTx.unpackAny(proto.data as any), + proto.memo, + proto.type + ); + } + + public toProto(_?: boolean): InterchainAccountPacketData.Proto { + _; + const { data, memo, type } = this; + + return InterchainAccountPacketData_pb.fromPartial({ + data: data.toProto() as any, + memo, + type, + }); + } + + public packAny(isClassic?: boolean): Any { + return Any.fromPartial({ + value: InterchainAccountPacketData_pb.encode( + this.toProto(isClassic) + ).finish(), + }); + } + + public static unpackAny( + msgAny: Any, + isClassic?: boolean + ): InterchainAccountPacketData { + return InterchainAccountPacketData.fromProto( + InterchainAccountPacketData_pb.decode(msgAny.value), + isClassic + ); + } +} + +export namespace InterchainAccountPacketData { + export interface Amino { + value: {}; + } + + type Base64String = string; + export interface Data { + type: Type; + data: Base64String; + memo: string; + } + + export type Proto = InterchainAccountPacketData_pb; +} diff --git a/src/core/ica/controller/v1/msgs/MsgRegisterInterchainAccount.ts b/src/core/ica/controller/v1/msgs/MsgRegisterInterchainAccount.ts new file mode 100644 index 00000000..9e873696 --- /dev/null +++ b/src/core/ica/controller/v1/msgs/MsgRegisterInterchainAccount.ts @@ -0,0 +1,119 @@ +import { AccAddress } from '../../../../bech32'; +import { Any } from '@terra-money/legacy.proto/google/protobuf/any'; +import { JSONSerializable } from '../../../../../util/json'; +import { MsgRegisterInterchainAccount as MsgRegisterInterchainAccount_pb } from '@terra-money/terra.proto/ibc/applications/interchain_accounts/controller/v1/tx'; + +/** + * A basic message for sending [[Coins]] between Terra accounts. + */ +export class MsgRegisterInterchainAccount extends JSONSerializable< + MsgRegisterInterchainAccount.Amino, + MsgRegisterInterchainAccount.Data, + MsgRegisterInterchainAccount.Proto +> { + /** + * @param owner sender's address + * @param connectionId ibc connection id + * @param version of the interchain account + */ + constructor( + public owner: AccAddress, + public connectionId: string, + public version: string + ) { + super(); + } + + public static fromAmino( + data: MsgRegisterInterchainAccount.Amino, + _?: boolean + ): MsgRegisterInterchainAccount { + _; + data; + throw new Error('Amino not supported on MsgRegisterInterchainAccount'); + } + + public toAmino(_?: boolean): MsgRegisterInterchainAccount.Amino { + _; + throw new Error('Amino not supported on MsgRegisterInterchainAccount'); + } + + public static fromData( + data: MsgRegisterInterchainAccount.Data, + _?: boolean + ): MsgRegisterInterchainAccount { + _; + const { owner, connection_id, version } = data; + + return new MsgRegisterInterchainAccount(owner, connection_id, version); + } + + public toData(_?: boolean): MsgRegisterInterchainAccount.Data { + _; + const { owner, connectionId, version } = this; + return { + '@type': + '/ibc.applications.interchain_accounts.controller.v1.MsgRegisterInterchainAccount', + owner: owner, + connection_id: connectionId, + version: version, + }; + } + + public static fromProto( + proto: MsgRegisterInterchainAccount.Proto, + _?: boolean + ): MsgRegisterInterchainAccount { + _; + return new MsgRegisterInterchainAccount( + proto.owner, + proto.connectionId, + proto.version + ); + } + + public toProto(_?: boolean): MsgRegisterInterchainAccount.Proto { + _; + const { owner, connectionId, version } = this; + return MsgRegisterInterchainAccount_pb.fromPartial({ + owner, + connectionId, + version, + }); + } + + public packAny(isClassic?: boolean): Any { + return Any.fromPartial({ + typeUrl: + '/ibc.applications.interchain_accounts.controller.v1.MsgRegisterInterchainAccount', + value: MsgRegisterInterchainAccount_pb.encode( + this.toProto(isClassic) + ).finish(), + }); + } + + public static unpackAny( + msgAny: Any, + isClassic?: boolean + ): MsgRegisterInterchainAccount { + return MsgRegisterInterchainAccount.fromProto( + MsgRegisterInterchainAccount_pb.decode(msgAny.value), + isClassic + ); + } +} + +export namespace MsgRegisterInterchainAccount { + export interface Amino { + value: {}; + } + + export interface Data { + '@type': '/ibc.applications.interchain_accounts.controller.v1.MsgRegisterInterchainAccount'; + owner: AccAddress; + connection_id: string; + version: string; + } + + export type Proto = MsgRegisterInterchainAccount_pb; +} diff --git a/src/core/ica/controller/v1/msgs/MsgSendTx.ts b/src/core/ica/controller/v1/msgs/MsgSendTx.ts new file mode 100644 index 00000000..1d439237 --- /dev/null +++ b/src/core/ica/controller/v1/msgs/MsgSendTx.ts @@ -0,0 +1,118 @@ +import { AccAddress } from '../../../../bech32'; +import { Any } from '@terra-money/legacy.proto/google/protobuf/any'; +import { JSONSerializable } from '../../../../../util/json'; +import { MsgSendTx as MsgSendTx_pb } from '@terra-money/terra.proto/ibc/applications/interchain_accounts/controller/v1/tx'; +import Long from 'long'; +import { InterchainAccountPacketData } from '../InterchainAccountPacketData'; + +/** + * Transaction message to wrap the packet data and execute actions on host chain. + */ +export class MsgSendTx extends JSONSerializable< + {}, + MsgSendTx.Data, + MsgSendTx.Proto +> { + /** + * @param owner sender's address + * @param connectionId ibc connection id + * @param version of the interchain account + */ + constructor( + public owner: AccAddress, + public connectionId: string, + public relativeTimeout: Long, + public packetData?: InterchainAccountPacketData + ) { + super(); + } + + public static fromAmino(data: MsgSendTx.Amino, _?: boolean): MsgSendTx { + _; + data; + throw new Error('Amino not supported on MsgSendTx'); + } + + public toAmino(_?: boolean): MsgSendTx.Amino { + _; + throw new Error('Amino not supported on MsgSendTx'); + } + + public static fromData(data: MsgSendTx.Data, _?: boolean): MsgSendTx { + _; + const { owner, connection_id, relative_timeout, packet_data } = data; + + return new MsgSendTx( + owner, + connection_id, + Long.fromString(relative_timeout.toString()), + packet_data + ? InterchainAccountPacketData.fromData(packet_data) + : undefined + ); + } + + public toData(_?: boolean): MsgSendTx.Data { + _; + const { owner, connectionId, relativeTimeout, packetData } = this; + return { + '@type': '/ibc.applications.interchain_accounts.controller.v1.MsgSendTx', + owner: owner, + connection_id: connectionId, + relative_timeout: relativeTimeout.toString(), + packet_data: packetData ? packetData.toData() : undefined, + }; + } + + public static fromProto(proto: MsgSendTx.Proto, _?: boolean): MsgSendTx { + _; + + return new MsgSendTx( + proto.owner, + proto.connectionId, + proto.relativeTimeout, + proto.packetData + ? InterchainAccountPacketData.fromProto(proto.packetData) + : undefined + ); + } + + public toProto(_?: boolean): MsgSendTx.Proto { + _; + const { owner, connectionId, relativeTimeout, packetData } = this; + + return MsgSendTx_pb.fromPartial({ + owner, + connectionId, + relativeTimeout, + packetData: packetData?.toProto(), + }); + } + + public packAny(isClassic?: boolean): Any { + return Any.fromPartial({ + typeUrl: '/ibc.applications.interchain_accounts.controller.v1.MsgSendTx', + value: MsgSendTx_pb.encode(this.toProto(isClassic)).finish(), + }); + } + + public static unpackAny(msgAny: Any, isClassic?: boolean): MsgSendTx { + return MsgSendTx.fromProto(MsgSendTx_pb.decode(msgAny.value), isClassic); + } +} + +export namespace MsgSendTx { + export interface Amino { + value: {}; + } + + export interface Data { + '@type': '/ibc.applications.interchain_accounts.controller.v1.MsgSendTx'; + owner: AccAddress; + connection_id: string; + relative_timeout: string; + packet_data?: InterchainAccountPacketData.Data; + } + + export type Proto = MsgSendTx_pb; +} diff --git a/src/core/ica/controller/v1/msgs/index.ts b/src/core/ica/controller/v1/msgs/index.ts new file mode 100644 index 00000000..fc637551 --- /dev/null +++ b/src/core/ica/controller/v1/msgs/index.ts @@ -0,0 +1,12 @@ +import { MsgRegisterInterchainAccount } from './MsgRegisterInterchainAccount'; +import { MsgSendTx } from './MsgSendTx'; + +export * from './MsgRegisterInterchainAccount'; +export * from './MsgSendTx'; + +export type ICAMsg = MsgRegisterInterchainAccount | MsgSendTx; +export namespace ICAMsg { + export type Amino = MsgRegisterInterchainAccount.Amino | MsgSendTx.Amino; + export type Data = MsgRegisterInterchainAccount.Data | MsgSendTx.Data; + export type Proto = MsgRegisterInterchainAccount.Proto | MsgSendTx.Proto; +}