Skip to content

Commit

Permalink
Merge pull request #543 from multiversx/TOOL-393-add-relayer-v-3-support
Browse files Browse the repository at this point in the history
Tool 393 add relayer v 3 support
  • Loading branch information
danielailie authored Dec 12, 2024
2 parents 9de1d6a + 684e44d commit 9bc160b
Show file tree
Hide file tree
Showing 12 changed files with 156 additions and 8 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@multiversx/sdk-core",
"version": "13.15.0",
"version": "13.16.0",
"description": "MultiversX SDK for JavaScript and TypeScript",
"author": "MultiversX",
"homepage": "https://multiversx.com",
Expand Down
5 changes: 5 additions & 0 deletions src/converters/transactionsConverter.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Address } from "../address";
import { IPlainTransactionObject, ITransaction } from "../interface";
import { IContractResultItem, ITransactionEvent, ITransactionOnNetwork } from "../interfaceOfNetwork";
import { ResultsParser } from "../smartcontracts";
Expand Down Expand Up @@ -25,9 +26,11 @@ export class TransactionsConverter {
chainID: transaction.chainID.valueOf(),
version: transaction.version,
options: transaction.options == 0 ? undefined : transaction.options,
relayer: transaction.relayer.isEmpty() ? undefined : transaction.relayer.toBech32(),
guardian: transaction.guardian ? transaction.guardian : undefined,
signature: this.toHexOrUndefined(transaction.signature),
guardianSignature: this.toHexOrUndefined(transaction.guardianSignature),
relayerSignature: this.toHexOrUndefined(transaction.relayerSignature),
};

return plainObject;
Expand All @@ -46,6 +49,7 @@ export class TransactionsConverter {
nonce: BigInt(object.nonce),
value: BigInt(object.value || ""),
receiver: object.receiver,
relayer: object.relayer ? Address.newFromBech32(object.relayer) : Address.empty(),
receiverUsername: this.bufferFromBase64(object.receiverUsername).toString(),
sender: object.sender,
senderUsername: this.bufferFromBase64(object.senderUsername).toString(),
Expand All @@ -58,6 +62,7 @@ export class TransactionsConverter {
options: Number(object.options),
signature: this.bufferFromHex(object.signature),
guardianSignature: this.bufferFromHex(object.guardianSignature),
relayerSignature: this.bufferFromHex(object.relayerSignature),
});

return transaction;
Expand Down
6 changes: 4 additions & 2 deletions src/converters/transactionsConverters.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { assert } from "chai";
import { Address } from "../address";
import {
ContractResultItem,
ContractResults,
Expand All @@ -7,8 +9,6 @@ import {
TransactionLogsOnNetwork,
TransactionOnNetwork,
} from "../networkProviders";
import { assert } from "chai";
import { Address } from "../address";
import { Transaction } from "../transaction";
import {
SmartContractCallOutcome,
Expand Down Expand Up @@ -58,7 +58,9 @@ describe("test transactions converter", async () => {
options: undefined,
guardian: undefined,
signature: undefined,
relayer: undefined,
guardianSignature: undefined,
relayerSignature: undefined,
});
});

Expand Down
5 changes: 5 additions & 0 deletions src/interface.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import BigNumber from "bignumber.js";
import { Address } from "./address";
import { ITransactionOnNetwork } from "./interfaceOfNetwork";

export interface ITransactionFetcher {
Expand All @@ -16,6 +17,7 @@ export interface IPlainTransactionObject {
receiverUsername?: string;
senderUsername?: string;
guardian?: string;
relayer?: string;
gasPrice: number;
gasLimit: number;
data?: string;
Expand All @@ -24,6 +26,7 @@ export interface IPlainTransactionObject {
options?: number;
signature?: string;
guardianSignature?: string;
relayerSignature?: string;
}

export interface ISignature {
Expand Down Expand Up @@ -102,6 +105,8 @@ export interface ITransaction {
version: number;
options: number;
guardian: string;
relayer: Address;
signature: Uint8Array;
guardianSignature: Uint8Array;
relayerSignature: Uint8Array;
}
2 changes: 2 additions & 0 deletions src/networkProviders/providers.dev.net.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,8 @@ describe("test network providers on devnet: Proxy and API", function () {
guardian: "",
guardianSignature: new Uint8Array(),
options: 0,
relayer: Address.empty(),
relayerSignature: new Uint8Array(),
};

const apiLegacyTxHash = await apiProvider.sendTransaction(transaction);
Expand Down
64 changes: 64 additions & 0 deletions src/proto/compiled.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions src/proto/serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,18 @@ export class ProtoSerializer {
protoTransaction.GuardianSignature = transaction.guardianSignature;
}

if (this.isRelayedTransaction(transaction)) {
protoTransaction.Relayer = transaction.relayer?.getPublicKey();
protoTransaction.RelayerSignature = transaction.relayerSignature;
}

return protoTransaction;
}

private isRelayedTransaction(transaction: ITransaction) {
return !transaction.relayer.isEmpty();
}

/**
* Custom serialization, compatible with mx-chain-go.
*/
Expand Down
2 changes: 2 additions & 0 deletions src/proto/transaction.proto
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ message Transaction {
uint32 Options = 13;
bytes GuardianAddr = 14;
bytes GuardianSignature = 15;
bytes Relayer = 16;
bytes RelayerSignature = 17;
}
42 changes: 41 additions & 1 deletion src/transaction.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { UserPublicKey, UserVerifier } from "./wallet";
import BigNumber from "bignumber.js";
import { assert } from "chai";
import { Address } from "./address";
Expand All @@ -10,6 +9,7 @@ import { TokenTransfer } from "./tokens";
import { Transaction } from "./transaction";
import { TransactionComputer } from "./transactionComputer";
import { TransactionPayload } from "./transactionPayload";
import { UserPublicKey, UserVerifier } from "./wallet";

describe("test transaction", async () => {
let wallets: Record<string, TestWallet>;
Expand Down Expand Up @@ -751,4 +751,44 @@ describe("test transaction", async () => {
assert.equal(isSignedByAlice, true);
assert.equal(isSignedByBob, false);
});

it("should serialize transaction with relayer", async () => {
const transaction = new Transaction({
chainID: networkConfig.ChainID,
sender: wallets.alice.address.toBech32(),
receiver: wallets.alice.address.toBech32(),
relayer: wallets.bob.address,
gasLimit: 50000n,
value: 0n,
version: 2,
nonce: 89n,
});

const serializedTransactionBytes = transactionComputer.computeBytesForSigning(transaction);
const serializedTransaction = Buffer.from(serializedTransactionBytes).toString();

assert.equal(
serializedTransaction,
`{"nonce":89,"value":"0","receiver":"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th","sender":"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th","gasPrice":1000000000,"gasLimit":50000,"chainID":"D","version":2,"relayer":"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"}`,
);
});

it("should test relayed v3", async () => {
const transaction = new Transaction({
chainID: networkConfig.ChainID,
sender: wallets.alice.address.toBech32(),
receiver: wallets.alice.address.toBech32(),
senderUsername: "alice",
receiverUsername: "bob",
gasLimit: 80000n,
value: 0n,
version: 2,
nonce: 89n,
data: Buffer.from("hello"),
});

assert.isFalse(transactionComputer.isRelayedV3Transaction(transaction));
transaction.relayer = wallets.carol.address;
assert.isTrue(transactionComputer.isRelayedV3Transaction(transaction));
});
});
18 changes: 16 additions & 2 deletions src/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
INonce,
IPlainTransactionObject,
ISignature,
ITransaction,
ITransactionOptions,
ITransactionPayload,
ITransactionValue,
Expand All @@ -20,8 +19,8 @@ import {
import { INetworkConfig } from "./interfaceOfNetwork";
import { TransactionOptions, TransactionVersion } from "./networkParams";
import { interpretSignatureAsBuffer } from "./signature";
import { TransactionPayload } from "./transactionPayload";
import { TransactionComputer } from "./transactionComputer";
import { TransactionPayload } from "./transactionPayload";

/**
* An abstraction for creating and signing transactions.
Expand Down Expand Up @@ -92,6 +91,12 @@ export class Transaction {
*/
public guardian: string;

/**
* The relayer address.
* Note: in the next major version, `sender`, `receiver` and `guardian` will also have the type `Address`, instead of `string`.
*/
public relayer: Address;

/**
* The signature.
*/
Expand All @@ -102,6 +107,11 @@ export class Transaction {
*/
public guardianSignature: Uint8Array;

/**
* The signature of the relayer.
*/
public relayerSignature: Uint8Array;

/**
* Creates a new Transaction object.
*/
Expand All @@ -110,6 +120,7 @@ export class Transaction {
value?: ITransactionValue | bigint;
sender: IAddress | string;
receiver: IAddress | string;
relayer?: Address;
senderUsername?: string;
receiverUsername?: string;
gasPrice?: IGasPrice | bigint;
Expand All @@ -121,6 +132,7 @@ export class Transaction {
guardian?: IAddress | string;
signature?: Uint8Array;
guardianSignature?: Uint8Array;
relayerSignature?: Uint8Array;
}) {
this.nonce = BigInt(options.nonce?.valueOf() || 0n);
// We still rely on "bigNumber" for value, because client code might be passing a BigNumber object as a legacy "ITransactionValue",
Expand All @@ -137,9 +149,11 @@ export class Transaction {
this.version = Number(options.version?.valueOf() || TRANSACTION_VERSION_DEFAULT);
this.options = Number(options.options?.valueOf() || TRANSACTION_OPTIONS_DEFAULT);
this.guardian = options.guardian ? this.addressAsBech32(options.guardian) : "";
this.relayer = options.relayer ? options.relayer : Address.empty();

this.signature = options.signature || Buffer.from([]);
this.guardianSignature = options.guardianSignature || Buffer.from([]);
this.relayerSignature = options.relayerSignature || Buffer.from([]);
}

private addressAsBech32(address: IAddress | string): string {
Expand Down
Loading

0 comments on commit 9bc160b

Please sign in to comment.