Skip to content

Commit

Permalink
Merge pull request #298 from multiversx/usernames
Browse files Browse the repository at this point in the history
Add support for "usernames" on transactions (including relayed V1)
  • Loading branch information
andreibancioiu authored Jun 13, 2023
2 parents 2c47a1d + 7786d71 commit b37a5b5
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 15 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": "12.3.0",
"version": "12.4.0",
"description": "MultiversX SDK for JavaScript and TypeScript",
"main": "out/index.js",
"types": "out/index.d.js",
Expand Down
2 changes: 2 additions & 0 deletions src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export interface IPlainTransactionObject {
value: string;
receiver: string;
sender: string;
receiverUsername?: string;
senderUsername?: string;
guardian?: string;
gasPrice: number;
gasLimit: number;
Expand Down
19 changes: 19 additions & 0 deletions src/proto/serializer.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { assert } from "chai";
import { Address } from "../address";
import { TransactionVersion } from "../networkParams";
import { Signature } from "../signature";
import { loadTestWallets, TestWallet } from "../testutils";
Expand Down Expand Up @@ -99,4 +100,22 @@ describe("serialize transactions", () => {
let buffer = serializer.serializeTransaction(transaction);
assert.equal(buffer.toString("hex"), "120200001a208049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f82a200139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1388094ebdc034080f1044a0568656c6c6f520d6c6f63616c2d746573746e657458016240dfa3e9f2fdec60dcb353bac3b3435b4a2ff251e7e98eaf8620f46c731fc70c8ba5615fd4e208b05e75fe0f7dc44b7a99567e29f94fcd91efac7e67b182cd2a04");
});

it("with usernames", async () => {
const transaction = new Transaction({
nonce: 204,
value: "1000000000000000000",
sender: Address.fromBech32("erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8"),
receiver: Address.fromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"),
senderUsername: "carol",
receiverUsername: "alice",
gasLimit: 50000,
chainID: "T"
});

transaction.applySignature(new Signature("5966dd6b98fc5ecbcd203fa38fac7059ba5c17683099071883b0ad6697386769321d851388a99cb8b81aab625aa2d7e13621432dbd8ab334c5891cd7c7755200"))

const buffer = serializer.serializeTransaction(transaction);
assert.equal(buffer.toString("hex"), "08cc011209000de0b6b3a76400001a200139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e12205616c6963652a20b2a11555ce521e4944e09ab17549d85b487dcd26c84b5017a39e31a3670889ba32056361726f6c388094ebdc0340d08603520154580162405966dd6b98fc5ecbcd203fa38fac7059ba5c17683099071883b0ad6697386769321d851388a99cb8b81aab625aa2d7e13621432dbd8ab334c5891cd7c7755200");
});
});
4 changes: 2 additions & 2 deletions src/proto/serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ export class ProtoSerializer {
Nonce: transaction.getNonce().valueOf() ? transaction.getNonce().valueOf() : undefined,
Value: this.serializeTransactionValue(transaction.getValue()),
RcvAddr: receiverPubkey,
RcvUserName: null,
RcvUserName: transaction.getReceiverUsername() ? Buffer.from(transaction.getReceiverUsername()).toString("base64") : undefined,
SndAddr: senderPubkey,
SndUserName: null,
SndUserName: transaction.getSenderUsername() ? Buffer.from(transaction.getSenderUsername()).toString("base64") : undefined,
GasPrice: transaction.getGasPrice().valueOf(),
GasLimit: transaction.getGasLimit().valueOf(),
Data: transaction.getData().length() == 0 ? null : transaction.getData().valueOf(),
Expand Down
43 changes: 40 additions & 3 deletions src/relayedTransactionV1Builder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ import * as errors from "./errors";
import { TransactionOptions, TransactionVersion } from "./networkParams";
import { RelayedTransactionV1Builder } from "./relayedTransactionV1Builder";
import { Signature } from "./signature";
import { loadTestWallets, TestWallet } from "./testutils";
import { TestWallet, loadTestWallets } from "./testutils";
import { TokenTransfer } from "./tokenTransfer";
import { Transaction } from "./transaction";
import { TransactionPayload } from "./transactionPayload";

describe("test relayed v1 transaction builder", function () {
let alice: TestWallet, bob: TestWallet, grace: TestWallet, frank: TestWallet;
let alice: TestWallet, bob: TestWallet, carol: TestWallet, grace: TestWallet, frank: TestWallet;

before(async function () {
({ alice, bob, grace, frank } = await loadTestWallets());
({ alice, bob, carol, grace, frank } = await loadTestWallets());
});

it("should throw exception if args were not set", async function () {
Expand Down Expand Up @@ -77,6 +78,42 @@ describe("test relayed v1 transaction builder", function () {
assert.equal(relayedTxV1.getSignature().toString("hex"), "c7d2c3b971f44eca676c10624d3c4319f8898af159f003e1e59f446cb75e5a294c9f0758d800e04d3daff11e67d20c4c1f85fd54aad6deb947ef391e6dd09d07");
});

it("should compute relayed v1 transaction (with usernames)", async function () {
const networkConfig = {
MinGasLimit: 50_000,
GasPerDataByte: 1_500,
GasPriceModifier: 0.01,
ChainID: "T"
};

const innerTx = new Transaction({
nonce: 208,
value: TokenTransfer.egldFromAmount(1),
sender: carol.address,
receiver: alice.address,
senderUsername: "carol",
receiverUsername: "alice",
gasLimit: 50000,
chainID: networkConfig.ChainID
});

await carol.signer.sign(innerTx);

const builder = new RelayedTransactionV1Builder();
const relayedTxV1 = builder
.setInnerTransaction(innerTx)
.setRelayerNonce(715)
.setNetworkConfig(networkConfig)
.setRelayerAddress(frank.address)
.build();

await frank.signer.sign(relayedTxV1);

assert.equal(relayedTxV1.getNonce().valueOf(), 715);
assert.equal(relayedTxV1.getData().toString(), "relayedTx@7b226e6f6e6365223a3230382c2273656e646572223a227371455656633553486b6c45344a717864556e59573068397a536249533141586f3534786f32634969626f3d222c227265636569766572223a2241546c484c76396f686e63616d433877673970645168386b77704742356a6949496f3349484b594e6165453d222c2276616c7565223a313030303030303030303030303030303030302c226761735072696365223a313030303030303030302c226761734c696d6974223a35303030302c2264617461223a22222c227369676e6174757265223a22744d616d736b6f315a574b526663594e4b5673793463797879643335764b754844576a3548706172344167734c2b4a4e585642545a574c754467384867514254476d724a6b49443133637050614c55322f38626644513d3d222c22636861696e4944223a2256413d3d222c2276657273696f6e223a312c22736e64557365724e616d65223a22593246796232773d222c22726376557365724e616d65223a22595778705932553d227d");
assert.equal(relayedTxV1.getSignature().toString("hex"), "0fbab023085551b7c497e5c52f64df802cb518ebaac93f8897e5cca25a8aff447565fa96570f7b547f7c0d0fceb2c7d12bcb5f37fa92c79725d9b2c69039f00d");
});

it("should compute guarded inner Tx - relayed v1 transaction", async function () {
const networkConfig = {
MinGasLimit: 50_000,
Expand Down
2 changes: 2 additions & 0 deletions src/relayedTransactionV1Builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ export class RelayedTransactionV1Builder {
"options": this.innerTransaction.getOptions().valueOf() == 0 ? undefined : this.innerTransaction.getOptions().valueOf(),
"guardian": this.innerTransaction.getGuardian().bech32() ? new Address(this.innerTransaction.getGuardian().bech32()).pubkey().toString("base64") : undefined,
"guardianSignature": this.innerTransaction.getGuardianSignature().toString("hex") ? this.innerTransaction.getGuardianSignature().toString("base64") : undefined,
"sndUserName": this.innerTransaction.getSenderUsername() ? Buffer.from(this.innerTransaction.getSenderUsername()).toString("base64") : undefined,
"rcvUserName": this.innerTransaction.getReceiverUsername() ? Buffer.from(this.innerTransaction.getReceiverUsername()).toString("base64") : undefined,
};

return JSON.stringify(txObject);
Expand Down
22 changes: 21 additions & 1 deletion src/transaction.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import BigNumber from "bignumber.js";
import { assert } from "chai";
import { Address } from "./address";
import { TransactionOptions, TransactionVersion } from "./networkParams";
import { loadTestWallets, TestWallet } from "./testutils";
import { TestWallet, loadTestWallets } from "./testutils";
import { TokenTransfer } from "./tokenTransfer";
import { Transaction } from "./transaction";
import { TransactionPayload } from "./transactionPayload";
Expand Down Expand Up @@ -157,6 +158,23 @@ describe("test transaction construction", async () => {
assert.isFalse(result.toString().includes("options"));
});

it("with usernames", async () => {
const transaction = new Transaction({
nonce: 204,
value: "1000000000000000000",
sender: Address.fromBech32("erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8"),
receiver: Address.fromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"),
senderUsername: "carol",
receiverUsername: "alice",
gasLimit: 50000,
chainID: "T"
});

await wallets.carol.signer.sign(transaction);
assert.equal(transaction.getSignature().toString("hex"), "5966dd6b98fc5ecbcd203fa38fac7059ba5c17683099071883b0ad6697386769321d851388a99cb8b81aab625aa2d7e13621432dbd8ab334c5891cd7c7755200");
assert.equal(transaction.getHash().toString(), "5728fadbc6c1024c4a0d5552eca44e80c182dc9077e58e31d599cf9496c96d1e");
});

it("computes correct fee", () => {
let transaction = new Transaction({
nonce: 92,
Expand Down Expand Up @@ -209,6 +227,8 @@ describe("test transaction construction", async () => {
value: "123456789000000000000000000000",
sender: sender,
receiver: wallets.bob.address,
senderUsername: "alice",
receiverUsername: "bob",
gasPrice: minGasPrice,
gasLimit: 80000,
data: new TransactionPayload("hello"),
Expand Down
48 changes: 42 additions & 6 deletions src/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ export class Transaction {
*/
private readonly receiver: IAddress;

/**
* The username of the sender.
*/
private senderUsername: string;

/**
* The username of the receiver.
*/
private receiverUsername: string;

/**
* The gas price to be used.
*/
Expand Down Expand Up @@ -97,8 +107,10 @@ export class Transaction {
public constructor({
nonce,
value,
receiver,
sender,
receiver,
senderUsername,
receiverUsername,
gasPrice,
gasLimit,
data,
Expand All @@ -109,8 +121,10 @@ export class Transaction {
}: {
nonce?: INonce;
value?: ITransactionValue;
receiver: IAddress;
sender: IAddress;
receiver: IAddress;
senderUsername?: string;
receiverUsername?: string;
gasPrice?: IGasPrice;
gasLimit: IGasLimit;
data?: ITransactionPayload;
Expand All @@ -123,6 +137,8 @@ export class Transaction {
this.value = value ? new BigNumber(value.toString()).toFixed(0) : 0;
this.sender = sender;
this.receiver = receiver;
this.senderUsername = senderUsername || "";
this.receiverUsername = receiverUsername || "";
this.gasPrice = gasPrice || TRANSACTION_MIN_GAS_PRICE;
this.gasLimit = gasLimit;
this.data = data || new TransactionPayload();
Expand Down Expand Up @@ -167,6 +183,22 @@ export class Transaction {
return this.receiver;
}

getSenderUsername(): string {
return this.senderUsername;
}

setSenderUsername(senderUsername: string) {
this.senderUsername = senderUsername;
}

getReceiverUsername(): string {
return this.receiverUsername;
}

setReceiverUsername(receiverUsername: string) {
this.receiverUsername = receiverUsername;
}

getGuardian(): IAddress {
return this.guardian;
}
Expand Down Expand Up @@ -279,12 +311,14 @@ export class Transaction {
value: this.value.toString(),
receiver: this.receiver.bech32(),
sender: this.sender.bech32(),
senderUsername: this.senderUsername ? Buffer.from(this.senderUsername).toString("base64") : undefined,
receiverUsername: this.receiverUsername ? Buffer.from(this.receiverUsername).toString("base64") : undefined,
gasPrice: this.gasPrice.valueOf(),
gasLimit: this.gasLimit.valueOf(),
data: this.data.length() == 0 ? undefined : this.data.encoded(),
chainID: this.chainID.valueOf(),
version: this.version.valueOf(),
options: this.options.valueOf() == 0 ? undefined : this.options.valueOf(),
version: this.getVersion().valueOf(),
options: this.getOptions().valueOf() == 0 ? undefined : this.getOptions().valueOf(),
guardian: this.guardian?.bech32() ? (this.guardian.bech32() == "" ? undefined : this.guardian.bech32()) : undefined,
signature: this.signature.toString("hex") ? this.signature.toString("hex") : undefined,
guardianSignature: this.guardianSignature.toString("hex") ? this.guardianSignature.toString("hex") : undefined,
Expand All @@ -305,14 +339,16 @@ export class Transaction {
nonce: Number(plainObjectTransaction.nonce),
value: new BigNumber(plainObjectTransaction.value).toFixed(0),
receiver: Address.fromString(plainObjectTransaction.receiver),
receiverUsername: plainObjectTransaction.receiverUsername ? Buffer.from(plainObjectTransaction.receiverUsername, "base64").toString() : undefined,
sender: Address.fromString(plainObjectTransaction.sender),
guardian: plainObjectTransaction.guardian == undefined ? undefined : Address.fromString(plainObjectTransaction.guardian || ""),
senderUsername: plainObjectTransaction.senderUsername ? Buffer.from(plainObjectTransaction.senderUsername, "base64").toString() : undefined,
guardian: plainObjectTransaction.guardian ? Address.fromString(plainObjectTransaction.guardian) : undefined,
gasPrice: Number(plainObjectTransaction.gasPrice),
gasLimit: Number(plainObjectTransaction.gasLimit),
data: new TransactionPayload(Buffer.from(plainObjectTransaction.data || "", "base64")),
chainID: String(plainObjectTransaction.chainID),
version: new TransactionVersion(plainObjectTransaction.version),
options: plainObjectTransaction.options == undefined ? undefined : new TransactionOptions(plainObjectTransaction.options)
options: plainObjectTransaction.options != null ? new TransactionOptions(plainObjectTransaction.options) : undefined
});

if (plainObjectTransaction.signature) {
Expand Down

0 comments on commit b37a5b5

Please sign in to comment.