Skip to content

Commit

Permalink
feat: implements transaction builder (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
jinoosss authored Jan 22, 2025
1 parent 98195ec commit 0334ccc
Show file tree
Hide file tree
Showing 10 changed files with 238 additions and 99 deletions.
5 changes: 3 additions & 2 deletions packages/sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,15 @@
"test:ci": "jest --coverage --passWithNoTests "
},
"dependencies": {
"@gnolang/gno-js-client": "^1.3.0",
"@gnolang/tm2-js-client": "^1.2.1",
"@gnolang/gno-js-client": "1.3.2",
"@gnolang/tm2-js-client": "1.2.4",
"@web3auth/base": "^8.12.4",
"@web3auth/base-provider": "^8.12.4",
"@web3auth/no-modal": "^8.12.4",
"@web3auth/openlogin-adapter": "^8.12.4"
},
"devDependencies": {
"@bufbuild/protobuf": "^2.2.3",
"@types/eslint": "^9",
"@types/jest": "^29.5.12",
"@types/node": "^22.0.0",
Expand Down
26 changes: 21 additions & 5 deletions packages/sdk/src/core/types/transaction.types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MsgAddPackage, MsgCall, MsgSend } from '@gnolang/gno-js-client';
import { MsgAddPackage, MsgCall, MsgEndpoint, MsgSend } from '@gnolang/gno-js-client';
import { MsgRun } from '@gnolang/gno-js-client/bin/proto/gno/vm';

export type TransactionMessageType = '/bank.MsgSend' | '/vm.m_call' | '/vm.m_addpkg' | '/vm.m_run';
Expand All @@ -9,11 +9,27 @@ export enum BroadcastType {
SYNC = 'SYNC',
COMMIT = 'COMMIT',
}
export interface AddPackageMessage {
type: MsgEndpoint.MSG_ADD_PKG;
value: MsgAddPackage;
}

export interface MsgCallMessage {
type: MsgEndpoint.MSG_CALL;
value: MsgCall;
}

export interface MsgSendMessage {
type: MsgEndpoint.MSG_SEND;
value: MsgSend;
}

export interface MsgRunMessage {
type: MsgEndpoint.MSG_RUN;
value: MsgRun;
}

export type TransactionMessage = {
type: TransactionMessageType;
value: TransactionMessageValue;
};
export type TransactionMessage = AddPackageMessage | MsgCallMessage | MsgSendMessage | MsgRunMessage;

export interface TransactionData {
messages: TransactionMessage[];
Expand Down
1 change: 1 addition & 0 deletions packages/sdk/src/core/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './encode.utils';
export * from './message.utils';
export * from './storage.utils';
export * from './transaction-message.utils';
export * from './transaction.utils';
100 changes: 100 additions & 0 deletions packages/sdk/src/core/utils/transaction-message.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { Any, MsgAddPackage, MsgCall, MsgEndpoint, MsgSend } from '@gnolang/gno-js-client';
import { MsgRun } from '@gnolang/gno-js-client/bin/proto/gno/vm';

import { AddPackageMessage, MsgCallMessage, MsgRunMessage, MsgSendMessage, TransactionMessage } from '../types';

export function makeAddPackageMessage(value: MsgAddPackage): AddPackageMessage {
return {
type: MsgEndpoint.MSG_ADD_PKG,
value,
};
}

export function makeMsgCallMessage(value: MsgCall): MsgCallMessage {
return {
type: MsgEndpoint.MSG_CALL,
value,
};
}

export function makeMsgSendMessage(value: MsgSend): MsgSendMessage {
return {
type: MsgEndpoint.MSG_SEND,
value,
};
}

export function makeMsgRunMessage(value: MsgRun): MsgRunMessage {
return {
type: MsgEndpoint.MSG_RUN,
value,
};
}

export function encodeTransactionMessage(message: TransactionMessage): Uint8Array {
if (isAddPackageMessage(message)) {
return MsgAddPackage.encode(message.value).finish();
}

if (isMsgCallMessage(message)) {
return MsgCall.encode(message.value).finish();
}

if (isMsgSendMessage(message)) {
return MsgSend.encode(message.value).finish();
}

if (isMsgRunMessage(message)) {
return MsgRun.encode(message.value).finish();
}

throw new Error('Unknown message type');
}

export function decodeTransactionMessage(message: Any): TransactionMessage {
if (message.typeUrl === MsgEndpoint.MSG_ADD_PKG) {
return {
type: MsgEndpoint.MSG_ADD_PKG,
value: MsgAddPackage.decode(message.value),
};
}

if (message.typeUrl === MsgEndpoint.MSG_CALL) {
return {
type: MsgEndpoint.MSG_CALL,
value: MsgCall.decode(message.value),
};
}

if (message.typeUrl === MsgEndpoint.MSG_SEND) {
return {
type: MsgEndpoint.MSG_SEND,
value: MsgSend.decode(message.value),
};
}

if (message.typeUrl === MsgEndpoint.MSG_RUN) {
return {
type: MsgEndpoint.MSG_RUN,
value: MsgRun.decode(message.value),
};
}

throw new Error('Unknown message type');
}

function isAddPackageMessage(message: TransactionMessage): message is AddPackageMessage {
return message.type === MsgEndpoint.MSG_ADD_PKG;
}

function isMsgCallMessage(message: TransactionMessage): message is MsgCallMessage {
return message.type === MsgEndpoint.MSG_CALL;
}

function isMsgSendMessage(message: TransactionMessage): message is MsgSendMessage {
return message.type === MsgEndpoint.MSG_SEND;
}

function isMsgRunMessage(message: TransactionMessage): message is MsgRunMessage {
return message.type === MsgEndpoint.MSG_RUN;
}
71 changes: 31 additions & 40 deletions packages/sdk/src/core/utils/transaction.utils.ts
Original file line number Diff line number Diff line change
@@ -1,74 +1,65 @@
import { Tx } from '@gnolang/tm2-js-client';
import { TransactionMessage } from '../types';
import { Any, defaultTxFee } from '@gnolang/gno-js-client';
import { Tx, TxFee } from '@gnolang/tm2-js-client';

export const defaultGasFee = {
amount: 1000000,
denom: 'ugnot',
};
import { TransactionMessage } from '../types';
import { encodeTransactionMessage } from './transaction-message.utils';

export const defaultGasWanted = 1_000_000;
export const defaultGasWanted = 10_000_000;

export class TransactionBuilder {
private _messages: TransactionMessage[] = [];
private _fees: { amount: string; denom: string }[] = [];
private _chainId: string = '';
private _memo: string = '';
private _accountNumber: string = '';
private _sequence: string = '';
private _gasWanted: string = '';
private _gasWanted: number = defaultGasWanted;
private _gasFee: string = defaultTxFee;

messages(...messages: TransactionMessage[]): TransactionBuilder {
this._messages = messages;
return this;
}

fees(...fees: { amount: number; denom: string }[]): TransactionBuilder {
this._fees = fees.map((fee) => ({ amount: fee.amount.toString(), denom: fee.denom }));
fee(amount: number, denom: string): TransactionBuilder {
this._gasFee = `${amount}${denom}`;
return this;
}

gasWanted(amount: number): TransactionBuilder {
this._gasWanted = amount.toString();
this._gasWanted = amount;
return this;
}

chainId(chainId: string): TransactionBuilder {
this._chainId = chainId;
memo(memo: string): TransactionBuilder {
this._memo = memo;
return this;
}

accountNumber(accountNumber: number): TransactionBuilder {
this._accountNumber = accountNumber.toString();
return this;
private get txMessages(): Any[] {
return this._messages.map((message) => {
return Any.create({
typeUrl: message.type,
value: encodeTransactionMessage(message),
});
});
}

sequence(sequence: number): TransactionBuilder {
this._sequence = sequence.toString();
return this;
}

memo(memo: string): TransactionBuilder {
this._memo = memo;
return this;
private get txFee(): TxFee {
return TxFee.create({
gasFee: this._gasFee,
gasWanted: this._gasWanted,
});
}

build(): Tx {
const txDocument = {
msgs: this._messages,
fee: {
amount: this._fees,
gas: this._gasWanted,
},
chain_id: this._chainId,
account_number: this._accountNumber,
sequence: this._sequence,
return {
messages: this.txMessages,
fee: this.txFee,
memo: this._memo,
signatures: [],
};

return Tx.fromJSON(txDocument);
}

public static create(): TransactionBuilder {
return new TransactionBuilder().gasWanted(defaultGasWanted).fees(defaultGasFee);
const builder = new TransactionBuilder();

return builder.gasWanted(defaultGasWanted);
}
}
10 changes: 4 additions & 6 deletions packages/sdk/src/providers/adena-wallet/adena-wallet.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { Tx } from '@gnolang/tm2-js-client';

import { makeResponseMessage } from '../../core';
import { WalletProvider } from '../../core/providers';
import { AccountInfo, NetworkInfo, WalletResponseFailureType, WalletResponseSuccessType } from '../../core/types';
Expand All @@ -22,8 +20,8 @@ import {
SwitchNetworkOptions,
SwitchNetworkResponse,
} from '../../core/types/methods';
import { isSuccessType, mapResponseByAdenaResponse } from './mapper.utils';
import { AdenaWallet, TransactionParams } from './types';
import { isSuccessType, mapResponseByAdenaResponse, mapTxToTransactionParams } from './mapper.utils';
import { AdenaWallet } from './types';

export class AdenaWalletProvider implements WalletProvider {
private getAdena(): AdenaWallet {
Expand Down Expand Up @@ -91,14 +89,14 @@ export class AdenaWalletProvider implements WalletProvider {

async signTransaction(options: SignTransactionOptions): Promise<SignTransactionResponse> {
const adena = this.getAdena();
const response = await adena.SignTx(Tx.toJSON(options.tx) as TransactionParams);
const response = await adena.SignTx(mapTxToTransactionParams(options.tx));

return mapResponseByAdenaResponse(response, response.data);
}

async broadcastTransaction(options: BroadcastTransactionOptions): Promise<BroadcastTransactionResponse> {
const adena = this.getAdena();
const response = await adena.DoContract(Tx.toJSON(options.tx) as TransactionParams);
const response = await adena.DoContract(mapTxToTransactionParams(options.tx));
const transactionResult = response.data;

return mapResponseByAdenaResponse(response, transactionResult);
Expand Down
34 changes: 33 additions & 1 deletion packages/sdk/src/providers/adena-wallet/mapper.utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { defaultTxFee } from '@gnolang/gno-js-client';
import { Tx } from '@gnolang/tm2-js-client';
import { decodeTransactionMessage, defaultGasWanted } from '../../core';
import {
WalletResponse,
WalletResponseFailureType,
Expand All @@ -6,7 +9,7 @@ import {
WalletResponseSuccessType,
WalletResponseType,
} from '../../core/types';
import { AdenaResponse, AdenaResponseStatus } from './types';
import { AdenaResponse, AdenaResponseStatus, TransactionParams } from './types';

export function isSuccessType(type: WalletResponseType | string): type is WalletResponseSuccessType {
const typeValue = type.toString();
Expand Down Expand Up @@ -50,3 +53,32 @@ export function mapResponseByAdenaResponse<ProviderResponseData = unknown>(
data: data as ProviderResponseData,
};
}

export function mapTxToTransactionParams(tx: Tx): TransactionParams {
const gasWanted = tx.fee?.gasWanted.toNumber() || defaultGasWanted;
const gasFee = tx.fee?.gasFee || defaultTxFee;
const gasFeeAmount = parseTokenAmount(gasFee);
const messages = tx.messages.map(decodeTransactionMessage);

return {
messages,
gasFee: gasFeeAmount,
gasWanted,
memo: tx.memo,
};
}

function parseTokenAmount(value: string): number {
const match = value.match(/^(\d+)/);
if (!match || match.length < 2) {
return 0;
}

try {
return parseInt(match[1]);
} catch (error) {
console.error('Error parsing token amount', error);
}

return 0;
}
14 changes: 2 additions & 12 deletions packages/sdk/src/providers/adena-wallet/types/transactions.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,10 @@
import { MsgAddPackage, MsgCall, MsgSend } from '@gnolang/gno-js-client';
import { MsgRun } from '@gnolang/gno-js-client/bin/proto/gno/vm';
import { BroadcastTxCommitResult } from '@gnolang/tm2-js-client';

import { AdenaResponse } from '.';

type EMessageType = '/bank.MsgSend' | '/vm.m_call' | '/vm.m_addpkg' | '/vm.m_run';

type TMessage = MsgAddPackage | MsgCall | MsgSend | MsgRun;

export type ContractMessage = {
type: EMessageType;
value: TMessage;
};
import { TransactionMessage } from '../../../core';

export type TransactionParams = {
messages: ContractMessage[];
messages: TransactionMessage[];
gasFee: number;
gasWanted: number;
memo?: string;
Expand Down
Loading

0 comments on commit 0334ccc

Please sign in to comment.