Skip to content

Commit

Permalink
Merge branch 'feat/next' into token-management-tx-parser
Browse files Browse the repository at this point in the history
  • Loading branch information
popenta committed Feb 20, 2024
2 parents e76a3c2 + 495dcb7 commit 7aa2931
Show file tree
Hide file tree
Showing 36 changed files with 1,433 additions and 1,140 deletions.
2 changes: 1 addition & 1 deletion src/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export class Account {
/**
* The address of the account.
*/
readonly address: IAddress = new Address();
readonly address: IAddress = Address.empty();

/**
* The nonce of the account (the account sequence number).
Expand Down
14 changes: 13 additions & 1 deletion src/address.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe("test address", () => {
});

it("should create empty address", async () => {
let nobody = new Address();
const nobody = Address.empty();

assert.isEmpty(nobody.hex());
assert.isEmpty(nobody.bech32());
Expand Down Expand Up @@ -55,4 +55,16 @@ describe("test address", () => {
assert.isFalse(Address.isValid("xerd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz"));
assert.isFalse(Address.isValid("erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2"));
});

it("should check whether isSmartContract", () => {
assert.isFalse(
Address.fromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th").isSmartContract(),
);
assert.isTrue(
Address.fromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplllst77y4l").isSmartContract(),
);
assert.isTrue(
Address.fromBech32("erd1qqqqqqqqqqqqqpgqxwakt2g7u9atsnr03gqcgmhcv38pt7mkd94q6shuwt").isSmartContract(),
);
});
});
69 changes: 55 additions & 14 deletions src/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class Address {
/**
* Creates an address object, given a raw string (whether a hex pubkey or a Bech32 address), a sequence of bytes, or another Address object.
*/
public constructor(value?: Address | Buffer | string) {
public constructor(value: Address | Buffer | string) {
if (!value) {
return;
}
Expand All @@ -48,7 +48,7 @@ export class Address {
}

private static fromValidHex(value: string): Address {
let result = new Address();
let result = Address.empty();
result.valueHex = value;
return result;
}
Expand Down Expand Up @@ -91,10 +91,11 @@ export class Address {
}

/**
* Creates an empty address object
* Creates an empty address object.
* Generally speaking, this should not be used by client code (internal use only).
*/
static empty(): Address {
return new Address();
return new Address("");
}

/**
Expand All @@ -109,12 +110,12 @@ export class Address {
throw new errors.ErrAddressCannotCreate(value, err);
}

let prefix = decoded.prefix;
const prefix = decoded.prefix;
if (prefix != HRP) {
throw new errors.ErrAddressBadHrp(HRP, prefix);
}

let pubkey = Buffer.from(bech32.fromWords(decoded.words));
const pubkey = Buffer.from(bech32.fromWords(decoded.words));
if (pubkey.length != PUBKEY_LENGTH) {
throw new errors.ErrAddressCannotCreate(value);
}
Expand All @@ -138,9 +139,16 @@ export class Address {
}

/**
* Returns the hex representation of the address (pubkey)
* Use {@link toHex} instead.
*/
hex(): string {
return this.toHex();
}

/**
* Returns the hex representation of the address (pubkey)
*/
toHex(): string {
if (this.isEmpty()) {
return "";
}
Expand All @@ -149,9 +157,16 @@ export class Address {
}

/**
* Returns the bech32 representation of the address
* Use {@link toBech32} instead.
*/
bech32(): string {
return this.toBech32();
}

/**
* Returns the bech32 representation of the address
*/
toBech32(): string {
if (this.isEmpty()) {
return "";
}
Expand All @@ -162,16 +177,31 @@ export class Address {
}

/**
* Returns the pubkey as raw bytes (buffer)
* Use {@link getPublicKey} instead.
*/
pubkey(): Buffer {
return this.getPublicKey();
}

/**
* Returns the pubkey as raw bytes (buffer)
*/
getPublicKey(): Buffer {
if (this.isEmpty()) {
return Buffer.from([]);
}

return Buffer.from(this.valueHex, "hex");
}

/**
* Returns the human-readable-part of the bech32 addresses.
* The HRP is currently hardcoded to "erd".
*/
getHrp(): string {
return HRP;
}

/**
* Returns whether the address is empty.
*/
Expand All @@ -194,27 +224,38 @@ export class Address {
* Returns the bech32 representation of the address
*/
toString(): string {
return this.bech32();
return this.toBech32();
}

/**
* Converts the address to a pretty, plain JavaScript object.
*/
toJSON(): object {
return {
bech32: this.bech32(),
pubkey: this.hex(),
bech32: this.toBech32(),
pubkey: this.toHex(),
};
}

/**
* Creates the Zero address (the one that should be used when deploying smart contracts)
* Creates the Zero address (the one that should be used when deploying smart contracts).
* Generally speaking, this should not be used by client code (internal use only).
*/
static Zero(): Address {
return new Address("0".repeat(64));
}

/**
* Use {@link isSmartContract} instead.
*/
isContractAddress(): boolean {
return this.hex().startsWith(SMART_CONTRACT_HEX_PUBKEY_PREFIX);
return this.isSmartContract();
}

/**
* Returns whether the address is a smart contract address.
*/
isSmartContract(): boolean {
return this.toHex().startsWith(SMART_CONTRACT_HEX_PUBKEY_PREFIX);
}
}
11 changes: 3 additions & 8 deletions src/compatibility.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,21 @@
import { Address } from "./address";
import { Err } from "./errors";
import { IAddress } from "./interface";

/**
* For internal use only.
*/
export class Compatibility {
static areWarningsEnabled: boolean = true;

/**
* For internal use only.
*/
static guardAddressIsSetAndNonZero(address: IAddress | undefined, context: string, resolution: string) {
if (!this.areWarningsEnabled) {
return;
}

if (!address || address.bech32() == "") {
console.warn(
throw new Err(
`${context}: address should be set; ${resolution}. In the future, this will throw an exception instead of emitting a WARN.`,
);
} else if (address.bech32() == Address.Zero().bech32()) {
console.warn(
throw new Err(
`${context}: address should not be the 'zero' address (also known as the 'contracts deployment address'); ${resolution}. In the future, this will throw an exception instead of emitting a WARN.`,
);
}
Expand Down
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ export const CONTRACT_DEPLOY_ADDRESS = "erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
export const DELEGATION_MANAGER_SC_ADDRESS = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6";
export const DEFAULT_HRP = "erd";
export const ESDT_CONTRACT_ADDRESS = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u";
export const DEFAULT_MESSAGE_VERSION = 1;
export const MESSAGE_PREFIX = "\x17Elrond Signed Message:\n";
59 changes: 44 additions & 15 deletions src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,45 @@ export interface IPlainTransactionObject {
guardianSignature?: string;
}

export interface ISignature { hex(): string; }
export interface IAddress { bech32(): string; }
export interface ITransactionValue { toString(): string; }
export interface IAccountBalance { toString(): string; }
export interface INonce { valueOf(): number; }
export interface IChainID { valueOf(): string; }
export interface IGasLimit { valueOf(): number; }
export interface IGasPrice { valueOf(): number; }
export interface ITransactionVersion { valueOf(): number; }
export interface ITransactionOptions { valueOf(): number; }
export interface ISignature {
hex(): string;
}

export interface IAddress {
bech32(): string;
}

export interface ITransactionValue {
toString(): string;
}

export interface IAccountBalance {
toString(): string;
}

export interface INonce {
valueOf(): number;
}

export interface IChainID {
valueOf(): string;
}

export interface IGasLimit {
valueOf(): number;
}

export interface IGasPrice {
valueOf(): number;
}

export interface ITransactionVersion {
valueOf(): number;
}

export interface ITransactionOptions {
valueOf(): number;
}

export interface ITransactionPayload {
length(): number;
Expand All @@ -59,17 +88,17 @@ export type ITokenPayment = ITokenTransfer;
export interface ITransactionNext {
sender: string;
receiver: string;
gasLimit: BigNumber.Value;
gasLimit: bigint;
chainID: string;
nonce: BigNumber.Value;
value: BigNumber.Value;
nonce: bigint;
value: bigint;
senderUsername: string;
receiverUsername: string;
gasPrice: BigNumber.Value;
gasPrice: bigint;
data: Uint8Array;
version: number;
options: number;
guardian: string;
signature: Uint8Array;
guardianSignature: Uint8Array;
}
}
87 changes: 87 additions & 0 deletions src/message.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { assert } from "chai";
import { Message, MessageComputer } from "./message";
import { loadTestWallets, TestWallet } from "./testutils";
import { UserVerifier } from "@multiversx/sdk-wallet";
import { DEFAULT_MESSAGE_VERSION } from "./constants";

describe("test message", () => {
let alice: TestWallet;
const messageComputer = new MessageComputer();

before(async function () {
({ alice } = await loadTestWallets());
});

it("should test message compute bytes for signing", async () => {
const data = Buffer.from("test message");

const message = new Message({
data: data,
});

const serialized = messageComputer.computeBytesForSigning(message);

assert.equal(
Buffer.from(serialized).toString("hex"),
"2162d6271208429e6d3e664139e98ba7c5f1870906fb113e8903b1d3f531004d",
);
});

it("should create, sign, pack, unpack and verify message", async () => {
const data = Buffer.from("test");

const message = new Message({
data: data,
address: alice.getAddress(),
});

message.signature = await alice.signer.sign(Buffer.from(messageComputer.computeBytesForSigning(message)));

assert.equal(
Buffer.from(message.signature).toString("hex"),
"7aff43cd6e3d880a65033bf0a1b16274854fd7dfa9fe5faa7fa9a665ee851afd4c449310f5f1697d348e42d1819eaef69080e33e7652d7393521ed50d7427a0e",
);

const packedMessage = messageComputer.packMessage(message);
assert.deepEqual(packedMessage, {
address: "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th",
message: "74657374",
signature:
"7aff43cd6e3d880a65033bf0a1b16274854fd7dfa9fe5faa7fa9a665ee851afd4c449310f5f1697d348e42d1819eaef69080e33e7652d7393521ed50d7427a0e",
version: 1,
});

const unpackedMessage = messageComputer.unpackMessage(packedMessage);
assert.deepEqual(unpackedMessage.address, alice.getAddress());
assert.deepEqual(unpackedMessage.data, message.data);
assert.deepEqual(unpackedMessage.signature, message.signature);
assert.deepEqual(unpackedMessage.version, message.version);

const verifier = UserVerifier.fromAddress(alice.getAddress());
const isValid = verifier.verify(
Buffer.from(messageComputer.computeBytesForVerifying(unpackedMessage)),
Buffer.from(unpackedMessage.signature!),
);
assert.equal(isValid, true);
});

it("should unpack legacy message", async () => {
const legacyMessage = {
address: "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th",
message: "0x7468697320697320612074657374206d657373616765",
signature:
"0xb16847437049986f936dd4a0917c869730cbf29e40a0c0821ca70db33f44758c3d41bcbea446dee70dea13d50942343bb78e74979dc434bbb2b901e0f4fd1809",
version: 1,
signer: "ErdJS",
};

const message = messageComputer.unpackMessage(legacyMessage);
assert.deepEqual(message.address, alice.getAddress());
assert.deepEqual(Buffer.from(message.data).toString(), "this is a test message");
assert.deepEqual(
Buffer.from(message.signature!).toString("hex"),
"b16847437049986f936dd4a0917c869730cbf29e40a0c0821ca70db33f44758c3d41bcbea446dee70dea13d50942343bb78e74979dc434bbb2b901e0f4fd1809",
);
assert.deepEqual(message.version, DEFAULT_MESSAGE_VERSION);
});
});
Loading

0 comments on commit 7aa2931

Please sign in to comment.