diff --git a/src/client/lcd/LCDClient.ts b/src/client/lcd/LCDClient.ts index 89fc47a..e055ce4 100644 --- a/src/client/lcd/LCDClient.ts +++ b/src/client/lcd/LCDClient.ts @@ -6,6 +6,7 @@ import { BankAPI, DistributionAPI, EvidenceAPI, + EvmAPI, FeeGrantAPI, GovAPI, GroupAPI, @@ -87,6 +88,7 @@ export class LCDClient { public bank: BankAPI; public distribution: DistributionAPI; public evidence: EvidenceAPI; + public evm: EvmAPI; public feeGrant: FeeGrantAPI; public gov: GovAPI; public group: GroupAPI; @@ -137,6 +139,7 @@ export class LCDClient { this.bank = new BankAPI(this.apiRequester); this.distribution = new DistributionAPI(this.apiRequester); this.evidence = new EvidenceAPI(this.apiRequester); + this.evm = new EvmAPI(this.apiRequester); this.feeGrant = new FeeGrantAPI(this.apiRequester); this.gov = new GovAPI(this.apiRequester); this.group = new GroupAPI(this.apiRequester); diff --git a/src/client/lcd/api/EvmAPI.ts b/src/client/lcd/api/EvmAPI.ts new file mode 100644 index 0000000..b895c56 --- /dev/null +++ b/src/client/lcd/api/EvmAPI.ts @@ -0,0 +1,79 @@ +import { AccAddress, EvmParams } from '../../../core'; +import { APIParams } from '../APIRequester'; +import { BaseAPI } from './BaseAPI'; + +export interface CallResponse { + response: string; + used_gas: string; + logs: { + address: AccAddress; + topics: string[]; + data: string; + }[]; + trace_output: string; +} + +export class EvmAPI extends BaseAPI { + public async code( + contractAddr: AccAddress, + params: APIParams = {} + ): Promise { + return this.c + .get<{ code: string }>(`/minievm/evm/v1/codes/${contractAddr}`, params) + .then(d => d.code); + } + + public async state( + contractAddr: AccAddress, + key: string, + params: APIParams = {} + ): Promise { + return this.c + .get<{ value: string }>( + `/minievm/evm/v1/states/${contractAddr}/${key}`, + params + ) + .then(d => d.value); + } + + public async contractAddrByDenom( + denom: string, + params: APIParams = {} + ): Promise { + return this.c + .get<{ address: AccAddress }>(`/minievm/evm/v1/contracts/by_denom`, { + ...params, + denom, + }) + .then(d => d.address); + } + + public async denom( + contractAddr: AccAddress, + params: APIParams = {} + ): Promise { + return this.c + .get<{ denom: string }>(`/minievm/evm/v1/denoms/${contractAddr}`, params) + .then(d => d.denom); + } + + public async call( + sender: AccAddress, + contractAddr: AccAddress, + input: string, + withTrace: boolean + ): Promise { + return this.c.post(`/minievm/evm/v1/call`, { + sender, + contract_addr: contractAddr, + input, + with_trace: withTrace, + }); + } + + public async parameters(params: APIParams = {}): Promise { + return this.c + .get<{ params: EvmParams.Data }>(`/minievm/evm/v1/params`, params) + .then(({ params: d }) => EvmParams.fromData(d)); + } +} diff --git a/src/client/lcd/api/index.ts b/src/client/lcd/api/index.ts index 248e793..7cc5812 100644 --- a/src/client/lcd/api/index.ts +++ b/src/client/lcd/api/index.ts @@ -4,6 +4,7 @@ export * from './AuthzAPI'; export * from './BankAPI'; export * from './DistributionAPI'; export * from './EvidenceAPI'; +export * from './EvmAPI'; export * from './FeeGrantAPI'; export * from './GovAPI'; export * from './GroupAPI'; diff --git a/src/core/Msg.ts b/src/core/Msg.ts index e4ef4ba..8cc9d3f 100644 --- a/src/core/Msg.ts +++ b/src/core/Msg.ts @@ -26,6 +26,7 @@ import { MsgDepositValidatorRewardsPool, } from './distribution'; import { EvidenceMsg, MsgSubmitEvidence } from './evidence'; +import { EvmMsg, MsgCreate, MsgCall, MsgUpdateEvmParams } from './evm'; import { FeeGrantMsg, MsgGrantAllowance, MsgRevokeAllowance } from './feegrant'; import { GovMsg, @@ -206,6 +207,7 @@ export type Msg = | CrisisMsg | DistributionMsg | EvidenceMsg + | EvmMsg | FeeGrantMsg | GovMsg | GroupMsg @@ -239,6 +241,7 @@ export namespace Msg { | CrisisMsg.Amino | DistributionMsg.Amino | EvidenceMsg.Amino + | EvmMsg.Amino | FeeGrantMsg.Amino | GovMsg.Amino | GroupMsg.Amino @@ -267,6 +270,7 @@ export namespace Msg { | CrisisMsg.Data | DistributionMsg.Data | EvidenceMsg.Data + | EvmMsg.Data | FeeGrantMsg.Data | GovMsg.Data | GroupMsg.Data @@ -299,6 +303,7 @@ export namespace Msg { | CrisisMsg.Proto | DistributionMsg.Proto | EvidenceMsg.Proto + | EvmMsg.Proto | FeeGrantMsg.Proto | GovMsg.Proto | GroupMsg.Proto @@ -381,6 +386,14 @@ export namespace Msg { case 'cosmos-sdk/MsgSubmitEvidence': return MsgSubmitEvidence.fromAmino(data); + // evm + case 'evm/MsgCreate': + return MsgCreate.fromAmino(data); + case 'evm/MsgCall': + return MsgCall.fromAmino(data); + case 'evm/MsgUpdateParams': + return MsgUpdateEvmParams.fromAmino(data); + // feegrant case 'cosmos-sdk/MsgGrantAllowance': return MsgGrantAllowance.fromAmino(data); @@ -677,6 +690,14 @@ export namespace Msg { case '/cosmos.evidence.v1beta1.MsgSubmitEvidence': return MsgSubmitEvidence.fromData(data); + // evm + case '/minievm.evm.v1.MsgCreate': + return MsgCreate.fromData(data); + case '/minievm.evm.v1.MsgCall': + return MsgCall.fromData(data); + case '/minievm.evm.v1.MsgUpdateParams': + return MsgUpdateEvmParams.fromData(data); + // feegrant case '/cosmos.feegrant.v1beta1.MsgGrantAllowance': return MsgGrantAllowance.fromData(data); @@ -1040,6 +1061,14 @@ export namespace Msg { case '/cosmos.evidence.v1beta1.MsgSubmitEvidence': return MsgSubmitEvidence.unpackAny(proto); + // evm + case '/minievm.evm.v1.MsgCreate': + return MsgCreate.unpackAny(proto); + case '/minievm.evm.v1.MsgCall': + return MsgCall.unpackAny(proto); + case '/minievm.evm.v1.MsgUpdateParams': + return MsgUpdateEvmParams.unpackAny(proto); + // feegrant case '/cosmos.feegrant.v1beta1.MsgGrantAllowance': return MsgGrantAllowance.unpackAny(proto); diff --git a/src/core/evm/EvmParams.ts b/src/core/evm/EvmParams.ts new file mode 100644 index 0000000..c9e8edf --- /dev/null +++ b/src/core/evm/EvmParams.ts @@ -0,0 +1,74 @@ +import { JSONSerializable } from '../../util/json'; +import { Params as Params_pb } from '@initia/initia.proto/minievm/evm/v1/types'; + +export class EvmParams extends JSONSerializable< + EvmParams.Amino, + EvmParams.Data, + EvmParams.Proto +> { + /** + * @param extra_eips the additional EIPs for the vm.Config + * @param allowed_publishers list of addresses with permission to distribute contracts + */ + constructor( + public extra_eips: number[], + public allowed_publishers: string[] + ) { + super(); + } + + public static fromAmino(data: EvmParams.Amino): EvmParams { + const { extra_eips, allowed_publishers } = data; + return new EvmParams(extra_eips.map(Number.parseInt), allowed_publishers); + } + + public toAmino(): EvmParams.Amino { + const { extra_eips, allowed_publishers } = this; + return { + extra_eips: extra_eips.map(eip => eip.toString()), + allowed_publishers, + }; + } + + public static fromData(data: EvmParams.Data): EvmParams { + const { extra_eips, allowed_publishers } = data; + return new EvmParams(extra_eips.map(Number.parseInt), allowed_publishers); + } + + public toData(): EvmParams.Data { + const { extra_eips, allowed_publishers } = this; + return { + extra_eips: extra_eips.map(eip => eip.toString()), + allowed_publishers, + }; + } + + public static fromProto(proto: EvmParams.Proto): EvmParams { + return new EvmParams( + proto.extraEips.map(eip => eip.toNumber()), + proto.allowedPublishers + ); + } + + public toProto(): EvmParams.Proto { + const { extra_eips, allowed_publishers } = this; + return Params_pb.fromPartial({ + extraEips: extra_eips, + allowedPublishers: allowed_publishers, + }); + } +} + +export namespace EvmParams { + export interface Amino { + extra_eips: string[]; + allowed_publishers: string[]; + } + + export interface Data { + extra_eips: string[]; + allowed_publishers: string[]; + } + + export type Proto = Params_pb; +} diff --git a/src/core/evm/index.ts b/src/core/evm/index.ts new file mode 100644 index 0000000..dca890e --- /dev/null +++ b/src/core/evm/index.ts @@ -0,0 +1,2 @@ +export * from './msgs'; +export * from './EvmParams'; diff --git a/src/core/evm/msgs/MsgCall.ts b/src/core/evm/msgs/MsgCall.ts new file mode 100644 index 0000000..fb412d5 --- /dev/null +++ b/src/core/evm/msgs/MsgCall.ts @@ -0,0 +1,102 @@ +import { JSONSerializable } from '../../../util/json'; +import { AccAddress } from '../../bech32'; +import { Any } from '@initia/initia.proto/google/protobuf/any'; +import { MsgCall as MsgCall_pb } from '@initia/initia.proto/minievm/evm/v1/tx'; + +export class MsgCall extends JSONSerializable< + MsgCall.Amino, + MsgCall.Data, + MsgCall.Proto +> { + /** + * @param sender the actor that signed the messages + * @param contract_addr the contract address to be executed, can be cosmos address or hex encoded address + * @param input hex encoded execution input bytes + */ + constructor( + public sender: AccAddress, + public contract_addr: AccAddress, + public input: string + ) { + super(); + } + + public static fromAmino(data: MsgCall.Amino): MsgCall { + const { + value: { sender, contract_addr, input }, + } = data; + + return new MsgCall(sender, contract_addr, input); + } + + public toAmino(): MsgCall.Amino { + const { sender, contract_addr, input } = this; + return { + type: 'evm/MsgCall', + value: { + sender, + contract_addr, + input, + }, + }; + } + + public static fromData(data: MsgCall.Data): MsgCall { + const { sender, contract_addr, input } = data; + return new MsgCall(sender, contract_addr, input); + } + + public toData(): MsgCall.Data { + const { sender, contract_addr, input } = this; + return { + '@type': '/minievm.evm.v1.MsgCall', + sender, + contract_addr, + input, + }; + } + + public static fromProto(data: MsgCall.Proto): MsgCall { + return new MsgCall(data.sender, data.contractAddr, data.input); + } + + public toProto(): MsgCall.Proto { + const { sender, contract_addr, input } = this; + return MsgCall_pb.fromPartial({ + sender, + contractAddr: contract_addr, + input, + }); + } + + public packAny(): Any { + return Any.fromPartial({ + typeUrl: '/minievm.evm.v1.MsgCall', + value: MsgCall_pb.encode(this.toProto()).finish(), + }); + } + + public static unpackAny(msgAny: Any): MsgCall { + return MsgCall.fromProto(MsgCall_pb.decode(msgAny.value)); + } +} + +export namespace MsgCall { + export interface Amino { + type: 'evm/MsgCall'; + value: { + sender: AccAddress; + contract_addr: AccAddress; + input: string; + }; + } + + export interface Data { + '@type': '/minievm.evm.v1.MsgCall'; + sender: AccAddress; + contract_addr: AccAddress; + input: string; + } + + export type Proto = MsgCall_pb; +} diff --git a/src/core/evm/msgs/MsgCreate.ts b/src/core/evm/msgs/MsgCreate.ts new file mode 100644 index 0000000..464360b --- /dev/null +++ b/src/core/evm/msgs/MsgCreate.ts @@ -0,0 +1,92 @@ +import { JSONSerializable } from '../../../util/json'; +import { AccAddress } from '../../bech32'; +import { Any } from '@initia/initia.proto/google/protobuf/any'; +import { MsgCreate as MsgCreate_pb } from '@initia/initia.proto/minievm/evm/v1/tx'; + +export class MsgCreate extends JSONSerializable< + MsgCreate.Amino, + MsgCreate.Data, + MsgCreate.Proto +> { + /** + * @param sender the actor that signed the messages + * @param code hex encoded raw contract bytes code + */ + constructor(public sender: AccAddress, public code: string) { + super(); + } + + public static fromAmino(data: MsgCreate.Amino): MsgCreate { + const { + value: { sender, code }, + } = data; + + return new MsgCreate(sender, code); + } + + public toAmino(): MsgCreate.Amino { + const { sender, code } = this; + return { + type: 'evm/MsgCreate', + value: { + sender, + code, + }, + }; + } + + public static fromData(data: MsgCreate.Data): MsgCreate { + const { sender, code } = data; + return new MsgCreate(sender, code); + } + + public toData(): MsgCreate.Data { + const { sender, code } = this; + return { + '@type': '/minievm.evm.v1.MsgCreate', + sender, + code, + }; + } + + public static fromProto(data: MsgCreate.Proto): MsgCreate { + return new MsgCreate(data.sender, data.code); + } + + public toProto(): MsgCreate.Proto { + const { sender, code } = this; + return MsgCreate_pb.fromPartial({ + sender, + code, + }); + } + + public packAny(): Any { + return Any.fromPartial({ + typeUrl: '/minievm.evm.v1.MsgCreate', + value: MsgCreate_pb.encode(this.toProto()).finish(), + }); + } + + public static unpackAny(msgAny: Any): MsgCreate { + return MsgCreate.fromProto(MsgCreate_pb.decode(msgAny.value)); + } +} + +export namespace MsgCreate { + export interface Amino { + type: 'evm/MsgCreate'; + value: { + sender: AccAddress; + code: string; + }; + } + + export interface Data { + '@type': '/minievm.evm.v1.MsgCreate'; + sender: AccAddress; + code: string; + } + + export type Proto = MsgCreate_pb; +} diff --git a/src/core/evm/msgs/MsgUpdateEvmParams.ts b/src/core/evm/msgs/MsgUpdateEvmParams.ts new file mode 100644 index 0000000..c29b91b --- /dev/null +++ b/src/core/evm/msgs/MsgUpdateEvmParams.ts @@ -0,0 +1,97 @@ +import { JSONSerializable } from '../../../util/json'; +import { AccAddress } from '../../bech32'; +import { EvmParams } from '../EvmParams'; +import { Any } from '@initia/initia.proto/google/protobuf/any'; +import { MsgUpdateParams as MsgUpdateParams_pb } from '@initia/initia.proto/minievm/evm/v1/tx'; + +export class MsgUpdateEvmParams extends JSONSerializable< + MsgUpdateEvmParams.Amino, + MsgUpdateEvmParams.Data, + MsgUpdateEvmParams.Proto +> { + /** + * @param authority the address that controls the module + * @param params params defines the x/evm parameters to update + */ + constructor(public authority: AccAddress, public params: EvmParams) { + super(); + } + + public static fromAmino(data: MsgUpdateEvmParams.Amino): MsgUpdateEvmParams { + const { + value: { authority, params }, + } = data; + return new MsgUpdateEvmParams(authority, EvmParams.fromAmino(params)); + } + + public toAmino(): MsgUpdateEvmParams.Amino { + const { authority, params } = this; + return { + type: 'evm/MsgUpdateParams', + value: { + authority, + params: params.toAmino(), + }, + }; + } + + public static fromData(data: MsgUpdateEvmParams.Data): MsgUpdateEvmParams { + const { authority, params } = data; + return new MsgUpdateEvmParams(authority, EvmParams.fromData(params)); + } + + public toData(): MsgUpdateEvmParams.Data { + const { authority, params } = this; + return { + '@type': '/minievm.evm.v1.MsgUpdateParams', + authority, + params: params.toData(), + }; + } + + public static fromProto(data: MsgUpdateEvmParams.Proto): MsgUpdateEvmParams { + return new MsgUpdateEvmParams( + data.authority, + EvmParams.fromProto(data.params as EvmParams.Proto) + ); + } + + public toProto(): MsgUpdateEvmParams.Proto { + const { authority, params } = this; + return MsgUpdateParams_pb.fromPartial({ + authority, + params: params.toProto(), + }); + } + + public packAny(): Any { + return Any.fromPartial({ + typeUrl: '/minievm.evm.v1.MsgUpdateParams', + value: MsgUpdateParams_pb.encode(this.toProto()).finish(), + }); + } + + public static unpackAny(msgAny: Any): MsgUpdateEvmParams { + return MsgUpdateEvmParams.fromProto( + MsgUpdateParams_pb.decode(msgAny.value) + ); + } +} + +export namespace MsgUpdateEvmParams { + export interface Amino { + type: 'evm/MsgUpdateParams'; + value: { + authority: AccAddress; + params: EvmParams.Amino; + }; + } + + export interface Data { + '@type': '/minievm.evm.v1.MsgUpdateParams'; + authority: AccAddress; + params: EvmParams.Data; + } + + export type Proto = MsgUpdateParams_pb; +} diff --git a/src/core/evm/msgs/index.ts b/src/core/evm/msgs/index.ts new file mode 100644 index 0000000..c66b364 --- /dev/null +++ b/src/core/evm/msgs/index.ts @@ -0,0 +1,23 @@ +import { MsgCreate } from './MsgCreate'; +import { MsgCall } from './MsgCall'; +import { MsgUpdateEvmParams } from './MsgUpdateEvmParams'; + +export * from './MsgCreate'; +export * from './MsgCall'; +export * from './MsgUpdateEvmParams'; + +export type EvmMsg = MsgCreate | MsgCall | MsgUpdateEvmParams; + +export namespace EvmMsg { + export type Amino = + | MsgCreate.Amino + | MsgCall.Amino + | MsgUpdateEvmParams.Amino; + + export type Data = MsgCreate.Data | MsgCall.Data | MsgUpdateEvmParams.Data; + + export type Proto = + | MsgCreate.Proto + | MsgCall.Proto + | MsgUpdateEvmParams.Proto; +} diff --git a/src/core/index.ts b/src/core/index.ts index 0d90d1a..8e60b60 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -16,6 +16,7 @@ export * from './consensus'; export * from './crisis'; export * from './distribution'; export * from './evidence'; +export * from './evm'; export * from './feegrant'; export * from './gov'; export * from './group';