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

Add support for MsgRun transaction execution #91

Merged
Show file tree
Hide file tree
Changes from all 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
21 changes: 16 additions & 5 deletions proto/gno/vm.proto
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,33 @@ message MsgAddPackage {
string deposit = 3;
}

// MsgRun is the execute arbitrary Gno code tx message,
// denoted as "m_run"
message MsgRun {
// the bech32 address of the caller
string caller = 1;
// the amount of funds to be deposited to the package, if any ("<amount><denomination>")
string send = 2;
// the package being executed
MemPackage package = 3;
}

// MemPackage is the metadata information tied to
// package / realm deployment
message MemPackage {
// the name of the package
string name = 1;
string name = 1 [json_name = "Name"];
// the gno path of the package
string path = 2;
string path = 2 [json_name = "Path"];
// the associated package gno source
repeated MemFile files = 3;
repeated MemFile files = 3 [json_name = "Files"];
}

// MemFile is the metadata information tied to
// a single gno package / realm file
message MemFile {
// the name of the source gno file
string name = 1;
string name = 1 [json_name = "Name"];
// the content of the source gno file
string body = 2;
string body = 2 [json_name = "Body"];
}
129 changes: 120 additions & 9 deletions src/proto/gno/vm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ export interface MsgAddPackage {
deposit: string;
}

/**
* MsgRun is the execute arbitrary Gno code tx message,
* denoted as "m_run"
*/
export interface MsgRun {
/** the bech32 address of the caller */
caller: string;
/** the amount of funds to be deposited to the package, if any ("<amount><denomination>") */
send: string;
/** the package being execute */
package?: MemPackage | undefined;
}

/**
* MemPackage is the metadata information tied to
* package / realm deployment
Expand Down Expand Up @@ -291,6 +304,104 @@ export const MsgAddPackage = {
},
};

function createBaseMsgRun(): MsgRun {
return { caller: '', send: '', package: undefined };
}

export const MsgRun = {
encode(
message: MsgRun,
writer: _m0.Writer = _m0.Writer.create()
): _m0.Writer {
if (message.caller !== '') {
writer.uint32(10).string(message.caller);
}
if (message.send !== '') {
writer.uint32(18).string(message.send);
}
if (message.package !== undefined) {
MemPackage.encode(message.package, writer.uint32(26).fork()).ldelim();
}
return writer;
},

decode(input: _m0.Reader | Uint8Array, length?: number): MsgRun {
const reader =
input instanceof _m0.Reader ? input : _m0.Reader.create(input);
let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseMsgRun();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
if (tag !== 10) {
break;
}

message.caller = reader.string();
continue;
case 2:
if (tag !== 18) {
break;
}

message.send = reader.string();
continue;
case 3:
if (tag !== 26) {
break;
}

message.package = MemPackage.decode(reader, reader.uint32());
continue;
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skipType(tag & 7);
}
return message;
},

fromJSON(object: any): MsgRun {
return {
caller: isSet(object.caller) ? String(object.caller) : '',
send: isSet(object.send) ? String(object.send) : '',
package: isSet(object.package)
? MemPackage.fromJSON(object.package)
: undefined,
};
},

toJSON(message: MsgRun): unknown {
const obj: any = {};
if (message.caller !== '') {
obj.caller = message.caller;
}
if (message.send !== '') {
obj.send = message.send;
}
if (message.package !== undefined) {
obj.package = MemPackage.toJSON(message.package);
}
return obj;
},

create<I extends Exact<DeepPartial<MsgRun>, I>>(base?: I): MsgRun {
return MsgRun.fromPartial(base ?? ({} as any));
},
fromPartial<I extends Exact<DeepPartial<MsgRun>, I>>(object: I): MsgRun {
const message = createBaseMsgRun();
message.caller = object.caller ?? '';
message.send = object.send ?? '';
message.package =
object.package !== undefined && object.package !== null
? MemPackage.fromPartial(object.package)
: undefined;
return message;
},
};

function createBaseMemPackage(): MemPackage {
return { name: '', path: '', files: [] };
}
Expand Down Expand Up @@ -352,8 +463,8 @@ export const MemPackage = {

fromJSON(object: any): MemPackage {
return {
name: isSet(object.name) ? String(object.name) : '',
path: isSet(object.path) ? String(object.path) : '',
name: isSet(object.Name) ? String(object.Name) : '',
path: isSet(object.Path) ? String(object.Path) : '',
files: Array.isArray(object?.files)
? object.files.map((e: any) => MemFile.fromJSON(e))
: [],
Expand All @@ -363,13 +474,13 @@ export const MemPackage = {
toJSON(message: MemPackage): unknown {
const obj: any = {};
if (message.name !== '') {
obj.name = message.name;
obj.Name = message.name;
}
if (message.path !== '') {
obj.path = message.path;
obj.Path = message.path;
}
if (message.files?.length) {
obj.files = message.files.map((e) => MemFile.toJSON(e));
obj.Files = message.files.map((e) => MemFile.toJSON(e));
}
return obj;
},
Expand Down Expand Up @@ -439,18 +550,18 @@ export const MemFile = {

fromJSON(object: any): MemFile {
return {
name: isSet(object.name) ? String(object.name) : '',
body: isSet(object.body) ? String(object.body) : '',
name: isSet(object.Name) ? String(object.Name) : '',
body: isSet(object.Body) ? String(object.Body) : '',
};
},

toJSON(message: MemFile): unknown {
const obj: any = {};
if (message.name !== '') {
obj.name = message.name;
obj.Name = message.name;
}
if (message.body !== '') {
obj.body = message.body;
obj.Body = message.body;
}
return obj;
},
Expand Down
1 change: 1 addition & 0 deletions src/wallet/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export enum MsgEndpoint {
MSG_SEND = '/bank.MsgSend',
MSG_ADD_PKG = '/vm.m_addpkg',
MSG_CALL = '/vm.m_call',
MSG_RUN = '/vm.m_run',
}
30 changes: 24 additions & 6 deletions src/wallet/utility/utility.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Any, MsgAddPackage, MsgCall, MsgSend } from '../../proto';
import { MsgRun } from '../../proto/gno/vm';
import { MsgEndpoint } from '../endpoints';

/**
Expand Down Expand Up @@ -38,21 +39,38 @@ export const defaultTxFee = '1000000ugnot'; // 1 GNOT
export const decodeTxMessages = (messages: Any[]): any[] => {
return messages.map((m: Any) => {
switch (m.typeUrl) {
case MsgEndpoint.MSG_CALL:
case MsgEndpoint.MSG_CALL: {
const decodedMessage = MsgCall.decode(m.value);
const messageJson = MsgCall.toJSON(decodedMessage) as object;
return {
'@type': m.typeUrl,
...MsgCall.decode(m.value),
...messageJson,
};
case MsgEndpoint.MSG_SEND:
}
case MsgEndpoint.MSG_SEND: {
const decodedMessage = MsgSend.decode(m.value);
const messageJson = MsgSend.toJSON(decodedMessage) as object;
return {
'@type': m.typeUrl,
...MsgSend.decode(m.value),
...messageJson,
};
case MsgEndpoint.MSG_ADD_PKG:
}
case MsgEndpoint.MSG_ADD_PKG: {
const decodedMessage = MsgAddPackage.decode(m.value);
const messageJson = MsgAddPackage.toJSON(decodedMessage) as object;
return {
'@type': m.typeUrl,
...MsgAddPackage.decode(m.value),
...messageJson,
};
}
case MsgEndpoint.MSG_RUN: {
const decodedMessage = MsgRun.decode(m.value);
const messageJson = MsgRun.toJSON(decodedMessage) as object;
return {
'@type': m.typeUrl,
...messageJson,
};
}
default:
throw new Error(`unsupported message type ${m.typeUrl}`);
}
Expand Down
55 changes: 55 additions & 0 deletions src/wallet/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Long from 'long';
import { MemPackage, MsgAddPackage, MsgCall, MsgSend } from '../proto';
import { MsgEndpoint } from './endpoints';
import { LedgerConnector } from '@cosmjs/ledger-amino';
import { MsgRun } from '../proto/gno/vm';

/**
* GnoWallet is an extension of the TM2 wallet with
Expand Down Expand Up @@ -254,4 +255,58 @@ export class GnoWallet extends Wallet {
// Send the transaction
return this.sendTransaction(signedTx, endpoint);
};

/**
* Executes arbitrary Gno code
* @param {MemPackage} gnoPackage the gno package being executed
* @param {TransactionEndpoint} endpoint the transaction broadcast type (sync / commit)
* @param {Map<string, number>} [funds] the denomination -> value map for funds, if any
* @param {TxFee} [fee] the custom transaction fee, if any
*/
executePackage = async <K extends keyof BroadcastTransactionMap>(
gnoPackage: MemPackage,
endpoint: K,
funds?: Map<string, number>,
fee?: TxFee
): Promise<BroadcastTransactionMap[K]['result']> => {
// Convert the funds into the correct representation
const amount: string = fundsToCoins(funds);

// Fetch the wallet address
const caller: string = await this.getAddress();

// Construct the transaction fee
const txFee: TxFee = fee
? fee
: {
gasWanted: new Long(60000),
gasFee: defaultTxFee,
};

// Prepare the Msg
const runMsg: MsgRun = {
caller,
send: amount,
package: gnoPackage,
};

// Construct the transfer transaction
const tx: Tx = {
messages: [
{
typeUrl: MsgEndpoint.MSG_RUN,
value: MsgRun.encode(runMsg).finish(),
},
],
fee: txFee,
memo: '',
signatures: [], // No signature yet
};

// Sign the transaction
const signedTx: Tx = await this.signTransaction(tx, decodeTxMessages);

// Send the transaction
return this.sendTransaction(signedTx, endpoint);
};
}
Loading