From 888d1b9c2912d25bb2ece55811866162930386f7 Mon Sep 17 00:00:00 2001 From: ukorvl Date: Wed, 22 May 2024 02:02:38 +0300 Subject: [PATCH] add deserialization prototypes and give commonjs build cjs extension #1 --- .changeset/brave-dancers-end.md | 2 +- .changeset/red-poems-roll.md | 5 + .changeset/wise-shrimps-look.md | 5 + README.md | 8 +- biome.json | 2 +- package.json | 17 ++- src/clients/PublicClient.ts | 34 +++--- src/clients/WalletClient.ts | 92 +++++++++------- src/clients/index.ts | 2 +- src/clients/types/ClientConfigs.ts | 2 +- src/clients/types/ISendMessageOptions.ts | 14 +++ src/clients/types/ISendTransactionOptions.ts | 14 --- src/clients/types/ISignMessageOptions.ts | 14 +++ src/encoding/fromSsz.ts | 68 ++++++++++++ src/encoding/{sszSchemas.ts => ssz.ts} | 14 +-- src/encoding/toSsz.ts | 106 ++++++++++--------- src/signers/LocalKeySigner.test.ts | 8 +- src/signers/LocalKeySigner.ts | 4 +- src/signers/generatePrivateKey.test.ts | 0 src/signers/generatePrivateKey.ts | 13 +++ src/signers/index.ts | 1 + src/types/IMessage.ts | 19 ++++ src/types/ISignedMessage.ts | 6 ++ src/types/ISignedTransaction.ts | 6 -- src/types/ITransaction.ts | 19 ---- src/types/index.ts | 2 +- src/utils/assert.ts | 17 ++- src/utils/index.ts | 2 +- src/utils/{transaction.ts => message.ts} | 0 29 files changed, 314 insertions(+), 182 deletions(-) create mode 100644 .changeset/red-poems-roll.md create mode 100644 .changeset/wise-shrimps-look.md create mode 100644 src/clients/types/ISendMessageOptions.ts delete mode 100644 src/clients/types/ISendTransactionOptions.ts create mode 100644 src/clients/types/ISignMessageOptions.ts rename src/encoding/{sszSchemas.ts => ssz.ts} (65%) create mode 100644 src/signers/generatePrivateKey.test.ts create mode 100644 src/signers/generatePrivateKey.ts create mode 100644 src/types/IMessage.ts create mode 100644 src/types/ISignedMessage.ts delete mode 100644 src/types/ISignedTransaction.ts delete mode 100644 src/types/ITransaction.ts rename src/utils/{transaction.ts => message.ts} (100%) diff --git a/.changeset/brave-dancers-end.md b/.changeset/brave-dancers-end.md index 8e4319d9..581713e3 100644 --- a/.changeset/brave-dancers-end.md +++ b/.changeset/brave-dancers-end.md @@ -2,4 +2,4 @@ "niljs": patch --- -Added methods to serialize transactions and transactions with signatures +Added methods to serialize messages and messages with signatures diff --git a/.changeset/red-poems-roll.md b/.changeset/red-poems-roll.md new file mode 100644 index 00000000..33260285 --- /dev/null +++ b/.changeset/red-poems-roll.md @@ -0,0 +1,5 @@ +--- +"niljs": patch +--- + +Rename transaction to message diff --git a/.changeset/wise-shrimps-look.md b/.changeset/wise-shrimps-look.md new file mode 100644 index 00000000..18f4fd50 --- /dev/null +++ b/.changeset/wise-shrimps-look.md @@ -0,0 +1,5 @@ +--- +"niljs": patch +--- + +Add cjs extension to commonjs build diff --git a/README.md b/README.md index 2e86539d..36bcde14 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ publicClient.getBalance("your_address").then((balance) => { }); ``` -The second one is the `WalletClient` class, which is used to interact with the Nil network with a private key. It is used to send transactions to the network. +The second one is the `WalletClient` class, which is used to interact with the Nil network with a private key. It is used to send messages to the network. ```typescript import { WalletClient } from "niljs"; @@ -51,7 +51,7 @@ const endpoint = "https://localhost:8259"; const walletClient = new WalletClient({ endpoint }); walletClient - .sendTransaction({ + .sendMessage({ from: "your_address", to: "recipient_address", amount: 100, @@ -61,7 +61,7 @@ walletClient }); ``` -Initialize the Signer with the private key of the account you want to use to sign transactions. +Initialize the Signer with the private key of the account you want to use to sign messages. ```typescript import { Signer } from "niljs"; @@ -72,7 +72,7 @@ const signer = new Signer({ privateKey }); signer.sign(new Uint8Array(32)); ``` -You can also sign transactions automatically by passing the Signer instance to the WalletClient. +You can also sign messages automatically by passing the Signer instance to the WalletClient. ```typescript import { WalletClient } from "niljs"; diff --git a/biome.json b/biome.json index e5002ef4..bd997c32 100644 --- a/biome.json +++ b/biome.json @@ -20,6 +20,6 @@ "lineWidth": 80 }, "files": { - "ignore": ["node_modules", "dist"] + "ignore": ["node_modules", "dist", "package.json"] } } diff --git a/package.json b/package.json index c1edf9e2..3264a042 100644 --- a/package.json +++ b/package.json @@ -11,9 +11,11 @@ "node": ">=18.0.0" }, "type": "module", - "main": "dist/niljs.cjs.js", + "main": "dist/niljs.cjs.cjs", "module": "dist/niljs.esm.js", - "files": ["dist"], + "files": [ + "dist" + ], "types": "dist/niljs.d.ts", "exports": { ".": { @@ -65,10 +67,17 @@ "pre-commit": "npm run lint", "pre-push": "npm run format", "commit-msg": "npx --no-install commitlint --edit $1", - "preserveUnused": ["commit-msg"] + "preserveUnused": [ + "commit-msg" + ] }, "dependencies": { "@chainsafe/ssz": "^0.16.0", "tiny-invariant": "^1.3.3" - } + }, + "keywords": [ + "nil", + "blockchain", + "client" + ] } diff --git a/src/clients/PublicClient.ts b/src/clients/PublicClient.ts index 14ae3c80..3afe20e0 100644 --- a/src/clients/PublicClient.ts +++ b/src/clients/PublicClient.ts @@ -63,9 +63,9 @@ class PublicClient extends BaseClient { } /** - * getBlockTransactionCountByNumber returns the transaction count by the block number. + * getBlockMessageCountByNumber returns the message count by the block number. * @param number - The block number. - * @returns The transaction count. + * @returns The message count. * @example import { PublicClient } from 'niljs'; * @@ -73,13 +73,11 @@ class PublicClient extends BaseClient { * endpoint: 'http://127.0.0.1:8529' * }) * - * const count = await client.getBlockTransactionCountByNumber(1); + * const count = await client.getBlockMessageCountByNumber(1); */ - public async getBlockTransactionCountByNumber( - number: number, - ): Promise { + public async getBlockMessageCountByNumber(number: number): Promise { const res = await this.rpcClient.request({ - method: "eth_getBlockTransactionCountByNumber", + method: "eth_getBlockMessageCountByNumber", params: [number], }); @@ -87,9 +85,9 @@ class PublicClient extends BaseClient { } /** - * getBlockTransactionCountByHash returns the transaction count by the block hash. + * getBlockMessageCountByHash returns the message count by the block hash. * @param hash - The block hash. - * @returns The transaction count. + * @returns The message count. * @example import { PublicClient } from 'niljs'; * @@ -97,13 +95,11 @@ class PublicClient extends BaseClient { * endpoint: 'http://127.0.0.1:8529' * }) * - * const count = await client.getBlockTransactionCountByHash(Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); + * const count = await client.getBlockMessageCountByHash(Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); */ - public async getBlockTransactionCountByHash( - hash: Uint8Array, - ): Promise { + public async getBlockMessageCountByHash(hash: Uint8Array): Promise { const res = await this.rpcClient.request({ - method: "eth_getBlockTransactionCountByHash", + method: "eth_getBlockMessageCountByHash", params: [hash], }); @@ -137,10 +133,10 @@ class PublicClient extends BaseClient { } /** - * getTransactionCount returns the transaction count of the address. + * getMessageCount returns the message count of the address. * @param address - The address. * @param blockNumberOrHash - The block number or hash. - * @returns The transaction count. + * @returns The message count. * @example import { PublicClient } from 'niljs'; * @@ -148,14 +144,14 @@ class PublicClient extends BaseClient { * endpoint: 'http://127.0.0.1:8529' * }) * - * const count = await client.getTransactionCount(Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), 'latest'); + * const count = await client.getMessageCount(Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), 'latest'); */ - public async getTransactionCount( + public async getMessageCount( address: Uint8Array, blockNumberOrHash: string, ): Promise { const res = await this.rpcClient.request({ - method: "eth_getTransactionCount", + method: "eth_getMessageCount", params: [address, blockNumberOrHash], }); diff --git a/src/clients/WalletClient.ts b/src/clients/WalletClient.ts index 3cfac92b..4833ddc5 100644 --- a/src/clients/WalletClient.ts +++ b/src/clients/WalletClient.ts @@ -1,11 +1,12 @@ import invariant from "tiny-invariant"; -import { signedTransactionToSsz, transactionToSsz } from "../encoding/toSsz.js"; +import { messageToSsz, signedMessageToSsz } from "../encoding/toSsz.js"; import type { ISigner } from "../signers/index.js"; -import type { ITransaction } from "../types/ITransaction.js"; -import { assertIsValidTransaction } from "../utils/assert.js"; +import type { IMessage } from "../types/IMessage.js"; +import { assertIsValidMessage } from "../utils/assert.js"; import { BaseClient } from "./BaseClient.js"; import type { IWalletClientConfig } from "./types/ClientConfigs.js"; -import type { ISendTransactionOptions } from "./types/ISendTransactionOptions.js"; +import type { ISendMessageOptions } from "./types/ISendMessageOptions.js"; +import type { ISignMessageOptions } from "./types/ISignMessageOptions.js"; /** * Wallet client is a class that allows you to interact with the network via JSON-RPC api. @@ -27,10 +28,11 @@ class WalletClient extends BaseClient { } /** - * sendTransaction sends a transaction to the network. - * @param transaction - The transaction to send. It can be a raw transaction or a base transaction object. - * If the transaction is a raw transaction, it will be signed with the signer, that is passed in the constructor. - * @returns The hash of the transaction. + * sendMessage sends a message to the network. + * @param message - The message to send. It can be a raw message or a base message object. + * If the message is a raw message, it will be signed with the signer, that is passed in the constructor. + * @param options - The options to send a message. + * @returns The hash of the message. * @example * import { WalletClient } from 'niljs'; * @@ -38,41 +40,58 @@ class WalletClient extends BaseClient { * endpoint: 'http://127.0.0.1:8529' * }) * - * const transaction = Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); - * const hash = await client.sendTransaction(transaction); + * const message = Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + * const hash = await client.sendMessage(message); */ - public async sendTransaction( - transaction: ITransaction, - { shouldValidate = true } = {} as ISendTransactionOptions, + public async sendMessage( + message: IMessage, + { shouldValidate = true } = {} as ISendMessageOptions, ): Promise { - shouldValidate && assertIsValidTransaction(transaction); + shouldValidate && assertIsValidMessage(message); + + const signedMessage = this.signMessage(message, { + shouldValidate: false, + }); + + return await this.sendRawMessage(signedMessage); + } + + /** + * signMessage signs a message with the signer. + * @param message - The message to sign. + * @param options - The options to sign a message. + * @returns The signed message as Uint8Array. + */ + public signMessage( + message: IMessage, + { shouldValidate = true } = {} as ISignMessageOptions, + ): Uint8Array { + shouldValidate && assertIsValidMessage(message); invariant( this.signer !== undefined, - "Signer is required to sign a transaction. Please provide a signer in the constructor or use sendRawTransaction method.", + "Signer is required to sign a message. Please provide a signer in the constructor or use sendRawMessage method.", ); - const serializedTransaction = transactionToSsz(transaction); + const serializedMessage = messageToSsz(message); invariant( - serializedTransaction !== undefined, - "Serialized transaction is required to sign a transaction.", + serializedMessage !== undefined, + "Serialized message is required to sign a message.", ); - const signature = this.signer.sign(serializedTransaction); + const signature = this.signer.sign(serializedMessage); - const signedTransaction = signedTransactionToSsz({ - ...transaction, + return signedMessageToSsz({ + ...message, ...signature, }); - - return await this.sendRawTransaction(signedTransaction); } /** - * sendRawTransaction sends a raw transaction to the network. - * @param transaction - The transaction to send. - * @returns The hash of the transaction. + * sendRawMessage sends a raw message to the network. + * @param message - The message to send. + * @returns The hash of the message. * @example * import { WalletClient } from 'niljs'; * @@ -80,15 +99,13 @@ class WalletClient extends BaseClient { * endpoint: 'http://127.0.0.1:8529' * }) * - * const transaction = Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); - * const hash = await client.sendRawTransaction(transaction); + * const message = Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + * const hash = await client.sendRawMessage(message); */ - public async sendRawTransaction( - transaction: Uint8Array, - ): Promise { + public async sendRawMessage(message: Uint8Array): Promise { const res = await this.rpcClient.request({ - method: "eth_sendRawTransaction", - params: [transaction], + method: "eth_sendRawMessage", + params: [message], }); return res.hash; @@ -97,7 +114,7 @@ class WalletClient extends BaseClient { /** * deployContract deploys a contract to the network. * @param contract - The contract to deploy. - * @returns The hash of the transaction. + * @returns The hash of the message. * @example import { WalletClient } from 'niljs'; * @@ -109,10 +126,13 @@ class WalletClient extends BaseClient { * const hash = await client.deployContract(contract); */ public async deployContract(contract: Uint8Array): Promise { - const hash = await this.sendRawTransaction(contract); + const hash = await this.sendRawMessage(contract); // there will be a method to get receipt by hash // receipt - result of smart conract calling - // we should wait to receipts to be sure that transaction is included in the block + // we should wait to receipts to be sure that message is included in the block + + // ! compiling smart contract to the bytecode shall not be included in this library + // it can be done by hardhat return hash; } } diff --git a/src/clients/index.ts b/src/clients/index.ts index 0bacc02b..1c078306 100644 --- a/src/clients/index.ts +++ b/src/clients/index.ts @@ -1,4 +1,4 @@ export * from "./WalletClient.js"; export * from "./PublicClient.js"; export * from "./types/ClientConfigs.js"; -export * from "./types/ISendTransactionOptions.js"; +export * from "./types/ISendMessageOptions.js"; diff --git a/src/clients/types/ClientConfigs.ts b/src/clients/types/ClientConfigs.ts index 19151ec4..3cada5dc 100644 --- a/src/clients/types/ClientConfigs.ts +++ b/src/clients/types/ClientConfigs.ts @@ -15,7 +15,7 @@ type IPublicClientConfig = IClientBaseConfig; type IWalletClientConfig = IClientBaseConfig & { /** - * The instance of signer is used to sign transactions and messages. + * The instance of signer is used to sign messages and messages. * If not included in the config, messages should be signed before passing to the client. * @example * import { Signer } from 'niljs'; diff --git a/src/clients/types/ISendMessageOptions.ts b/src/clients/types/ISendMessageOptions.ts new file mode 100644 index 00000000..4a8a2034 --- /dev/null +++ b/src/clients/types/ISendMessageOptions.ts @@ -0,0 +1,14 @@ +/** + * Options for sending a message. + */ +type ISendMessageOptions = { + /** + * If true, the message will be validated before sending. + * If the message is invalid, an error will be thrown. + * If false, the message will not be validated before sending. + * @default true + */ + shouldValidate?: boolean; +}; + +export type { ISendMessageOptions }; diff --git a/src/clients/types/ISendTransactionOptions.ts b/src/clients/types/ISendTransactionOptions.ts deleted file mode 100644 index dae0700c..00000000 --- a/src/clients/types/ISendTransactionOptions.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Options for sending a transaction. - */ -type ISendTransactionOptions = { - /** - * If true, the transaction will be validated before sending. - * If the transaction is invalid, an error will be thrown. - * If false, the transaction will not be validated before sending. - * @default true - */ - shouldValidate?: boolean; -}; - -export type { ISendTransactionOptions }; diff --git a/src/clients/types/ISignMessageOptions.ts b/src/clients/types/ISignMessageOptions.ts new file mode 100644 index 00000000..5fe651ef --- /dev/null +++ b/src/clients/types/ISignMessageOptions.ts @@ -0,0 +1,14 @@ +/** + * Options for signing a message. + */ +type ISignMessageOptions = { + /** + * If true, the message will be validated before signing. + * If the message is invalid, an error will be thrown. + * If false, the message will not be validated before sending. + * @default true + */ + shouldValidate?: boolean; +}; + +export type { ISignMessageOptions }; diff --git a/src/encoding/fromSsz.ts b/src/encoding/fromSsz.ts index e69de29b..4e28ad4e 100644 --- a/src/encoding/fromSsz.ts +++ b/src/encoding/fromSsz.ts @@ -0,0 +1,68 @@ +import { + type IMessage, + type ISignature, + bytesToString, + toHex, +} from "../index.js"; +import type { ISignedMessage } from "../types/ISignedMessage.js"; +import { + SszMessageSchema, + SszSignatureSchema, + SszSignedMessageSchema, +} from "./ssz.js"; + +/** + * Convert SSZ encoded message to a message object. + * @param ssz - SSZ encoded message + * @returns IMessage - Message object + */ +const sszToMessage = (ssz: Uint8Array): IMessage => { + const { from, to, data, ...rest } = SszMessageSchema.deserialize(ssz); + + return { + ...rest, + from: bytesToString(from), + to: bytesToString(to), + data: bytesToString(data), + signature: null, + }; +}; + +/** + * Convert SSZ encoded signature to a signature object. + * @param ssz - SSZ encoded signature + * @returns ISignature - Signature object + */ +const sszToSignature = (ssz: Uint8Array): ISignature => { + const { r, s, v, yParity } = SszSignatureSchema.deserialize(ssz); + + return { + r: toHex(r), + s: toHex(s), + v: v ? v : undefined, + yParity, + }; +}; + +/** + * Convert SSZ encoded signed message to a signed message object. + * @param ssz - SSZ encoded signed message + * @returns ISignedMessage - Signed message object + */ +const sszToSignedMessage = (ssz: Uint8Array): ISignedMessage => { + const { from, to, data, v, r, s, ...rest } = + SszSignedMessageSchema.deserialize(ssz); + + return { + ...rest, + from: bytesToString(from), + to: bytesToString(to), + data: bytesToString(data), + signature: null, + v: v ? v : undefined, + r: toHex(r), + s: toHex(s), + }; +}; + +export { sszToMessage, sszToSignature, sszToSignedMessage }; diff --git a/src/encoding/sszSchemas.ts b/src/encoding/ssz.ts similarity index 65% rename from src/encoding/sszSchemas.ts rename to src/encoding/ssz.ts index 1dd4ec91..2bae9acc 100644 --- a/src/encoding/sszSchemas.ts +++ b/src/encoding/ssz.ts @@ -12,9 +12,9 @@ const Uint32 = new UintNumberType(4); const UintBn64 = new UintBigintType(8); /** - * SSZ schema for a transaction object. It includes all the fields of a transaction object. + * SSZ schema for a message object. It includes all the fields of a message object. */ -const SszTransactionSchema = new ContainerType({ +const SszMessageSchema = new ContainerType({ index: Uint32, shardId: Uint32, from: Bytes32, @@ -36,15 +36,15 @@ const SszSignatureSchema = new ContainerType({ r: Bytes32, s: Bytes32, v: new OptionalType(UintBn64), - yParity: UintBn64, + yParity: Uint32, }); /** - * SSZ schema for a signed transaction object. It includes all the fields of a signed transaction object. + * SSZ schema for a signed message object. It includes all the fields of a signed message object. */ -const SszSignedTransactionSchema = new ContainerType({ - ...SszTransactionSchema.fields, +const SszSignedMessageSchema = new ContainerType({ + ...SszMessageSchema.fields, ...SszSignatureSchema.fields, }); -export { SszTransactionSchema, SszSignedTransactionSchema }; +export { SszMessageSchema, SszSignedMessageSchema, SszSignatureSchema }; diff --git a/src/encoding/toSsz.ts b/src/encoding/toSsz.ts index 5340834b..d3b07950 100644 --- a/src/encoding/toSsz.ts +++ b/src/encoding/toSsz.ts @@ -1,87 +1,89 @@ import {} from "@chainsafe/ssz"; -import type { ISignedTransaction } from "../types/ISignedTransaction.js"; -import type { ITransaction } from "../types/ITransaction.js"; -import { hexToBytes } from "./fromHex.js"; +import type { ISignature } from "../index.js"; +import type { IMessage } from "../types/IMessage.js"; +import type { ISignedMessage } from "../types/ISignedMessage.js"; import { - SszSignedTransactionSchema, - SszTransactionSchema, -} from "./sszSchemas.js"; + SszMessageSchema, + SszSignatureSchema, + SszSignedMessageSchema, +} from "./ssz.js"; import { toBytes } from "./toBytes.js"; /** - * Process a transaction object to convert all string fields to bytes and BigInt fields to BigInt. - * @param transaction - Transaction object - * @returns ITransaction - Processed transaction object ready to be SSZ encoded. + * Process a message object to convert all string fields to bytes and BigInt fields to BigInt. + * @param message - Message object + * @returns IMessage - Processed message object ready to be SSZ encoded. */ -const processTransaction = ({ - from, - to, - value, - data, - signature, - maxPriorityFeePerGas, - gasPrice, - maxFeePerGas, - ...rest -}: ITransaction) => ({ +const prepareMessage = ({ from, to, data, signature, ...rest }: IMessage) => ({ ...rest, from: toBytes(from), to: toBytes(to), - value: BigInt(value), data: toBytes(data), signature: signature ? toBytes(signature) : null, - maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas), - gasPrice: BigInt(gasPrice), - maxFeePerGas: BigInt(maxFeePerGas), }); /** - * Process a signed transaction object to convert all string fields to bytes and BigInt fields to BigInt. - * @param signedTransaction - Signed transaction object - * @returns ISignedTransaction - Processed signed transaction object ready to be SSZ encoded. + * Process a signature object to convert all string fields to bytes and BigInt fields to BigInt. + * @param signature - Signature object + * @returns ISignature - Processed signature object ready to be SSZ encoded. */ -const processSignedTransaction = ({ +const prepareSignature = ({ r, s, v, yParity }: ISignature) => ({ + r: toBytes(r), + s: toBytes(s), + v: v ? v : null, + yParity, +}); + +/** + * Process a signed message object to convert all string fields to bytes and BigInt fields to BigInt. + * @param signedMessage - Signed message object + * @returns ISignedMessage - Processed signed message object ready to be SSZ encoded. + */ +const processSignedMessage = ({ v, r, s, yParity, ...rest -}: ISignedTransaction) => ({ - ...rest, - v: v ? BigInt(v) : null, - r: hexToBytes(r), - s: hexToBytes(s), - yParity: BigInt(yParity), - ...processTransaction(rest), +}: ISignedMessage) => ({ + ...prepareMessage(rest), + ...prepareSignature({ r, s, v, yParity }), }); /** - * Convert a transaction object to SSZ encoded Uint8Array. - * @param transaction - Transaction object - * @returns Uint8Array - SSZ encoded transaction + * Convert a message object to SSZ encoded Uint8Array. + * @param message - Message object + * @returns Uint8Array - SSZ encoded message */ -const transactionToSsz = (transaction: ITransaction): Uint8Array => { - const serialized = SszTransactionSchema.serialize( - processTransaction(transaction), - ); +const messageToSsz = (message: IMessage): Uint8Array => { + const serialized = SszMessageSchema.serialize(prepareMessage(message)); + + return serialized; +}; + +/** + * Convert a signature object to SSZ encoded Uint8Array. + * @param signature - Signature object + * @returns Uint8Array - SSZ encoded signature + */ +const signatureToSsz = (signature: ISignature): Uint8Array => { + const serialized = SszSignatureSchema.serialize(prepareSignature(signature)); return serialized; }; /** - * Convert a signed transaction object to SSZ encoded Uint8Array. - * @param transaction - Transaction object with signature - * @returns Uint8Array - SSZ encoded signed transaction + * Convert a signed message object to SSZ encoded Uint8Array. + * @param message - Message object with signature + * @returns Uint8Array - SSZ encoded signed message * @example - * const serializedTx = signedTransactionToSsz(signedTransaction); + * const serializedTx = signedMessageToSsz(signedMessage); */ -const signedTransactionToSsz = ( - transaction: ISignedTransaction, -): Uint8Array => { - const serialized = SszSignedTransactionSchema.serialize( - processSignedTransaction(transaction), +const signedMessageToSsz = (message: ISignedMessage): Uint8Array => { + const serialized = SszSignedMessageSchema.serialize( + processSignedMessage(message), ); return serialized; }; -export { transactionToSsz, signedTransactionToSsz }; +export { messageToSsz, signedMessageToSsz, signatureToSsz }; diff --git a/src/signers/LocalKeySigner.test.ts b/src/signers/LocalKeySigner.test.ts index 8e896e8a..f2395f7d 100644 --- a/src/signers/LocalKeySigner.test.ts +++ b/src/signers/LocalKeySigner.test.ts @@ -24,14 +24,14 @@ test("LocalKeySigner should throw error if invalid private key is provided", asy test("Signature should be valid", async () => { const privateKey = accounts[0].privateKey; const signer = new LocalKeySigner({ privateKey }); - const transaction = Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + const message = Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); /** - * Sign the transaction with the private key and verify the signature with the public key. + * Sign the message with the private key and verify the signature with the public key. */ - const signature = signer.sign(transaction); + const signature = signer.sign(message); const publicKey = ec.keyFromPublic(accounts[0].publicKey, "hex"); - const verified = publicKey.verify(transaction, signature); + const verified = publicKey.verify(message, signature); expect(verified).toBe(true); }); diff --git a/src/signers/LocalKeySigner.ts b/src/signers/LocalKeySigner.ts index b4495821..721a14e3 100644 --- a/src/signers/LocalKeySigner.ts +++ b/src/signers/LocalKeySigner.ts @@ -11,9 +11,9 @@ import type { ISigner } from "./types/ISigner.js"; * It is an abstraction of signing the data with the private key. * It uses the secp256k1 curve implementation by @noble/curves/secp256k1 library under the hood. * @example - * import { LocalKeySigner } from 'niljs'; + * import { LocalKeySigner, generatePrivateKey } from 'niljs'; * - * const privateKey = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + * const privateKey = genratePrivateKey(); * const signer = new LocalKeySigner({ privateKey }); */ class LocalKeySigner implements ISigner { diff --git a/src/signers/generatePrivateKey.test.ts b/src/signers/generatePrivateKey.test.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/signers/generatePrivateKey.ts b/src/signers/generatePrivateKey.ts new file mode 100644 index 00000000..a0bbdcf6 --- /dev/null +++ b/src/signers/generatePrivateKey.ts @@ -0,0 +1,13 @@ +import type { Hex } from "@noble/curves/abstract/utils"; +import { secp256k1 } from "@noble/curves/secp256k1"; +import { toHex } from "../index.js"; + +/** + * Generate a new private key. + * @returns Hex - Private key + * @example + * const privateKey = generatePrivateKey(); + */ +const generatePrivateKey = (): Hex => toHex(secp256k1.utils.randomPrivateKey()); + +export { generatePrivateKey }; diff --git a/src/signers/index.ts b/src/signers/index.ts index b1620bb7..7787b47b 100644 --- a/src/signers/index.ts +++ b/src/signers/index.ts @@ -5,3 +5,4 @@ export * from "./types/ILocalKeySignerConfig.js"; export * from "./types/IPrivateKey.js"; export * from "./types/ISigner.js"; export * from "./types/ISignature.js"; +export * from "./generatePrivateKey.js"; diff --git a/src/types/IMessage.ts b/src/types/IMessage.ts new file mode 100644 index 00000000..d5c100f0 --- /dev/null +++ b/src/types/IMessage.ts @@ -0,0 +1,19 @@ +/** + * The interface for the message object. This object is used to represent a message in the network. + */ +interface IMessage { + index: number; + shardId: number; + from: string; + to: string; + value: bigint; + data: string; + seqno: number; + signature: string | null; + maxPriorityFeePerGas: bigint; + gasPrice: bigint; + maxFeePerGas: bigint; + chainId: number; +} + +export type { IMessage }; diff --git a/src/types/ISignedMessage.ts b/src/types/ISignedMessage.ts new file mode 100644 index 00000000..65c63b99 --- /dev/null +++ b/src/types/ISignedMessage.ts @@ -0,0 +1,6 @@ +import type { ISignature } from "../signers/index.js"; +import type { IMessage } from "./IMessage.js"; + +type ISignedMessage = IMessage & ISignature; + +export type { ISignedMessage }; diff --git a/src/types/ISignedTransaction.ts b/src/types/ISignedTransaction.ts deleted file mode 100644 index 6a441d58..00000000 --- a/src/types/ISignedTransaction.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { ISignature } from "../signers/index.js"; -import type { ITransaction } from "./ITransaction.js"; - -type ISignedTransaction = ITransaction & ISignature; - -export type { ISignedTransaction }; diff --git a/src/types/ITransaction.ts b/src/types/ITransaction.ts deleted file mode 100644 index 6394f5f8..00000000 --- a/src/types/ITransaction.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * The transaction type. - */ -interface ITransaction { - index: number; - shardId: number; - from: string; - to: string; - value: number; - data: string; - seqno: number; - signature: string | null; - maxPriorityFeePerGas: number; - gasPrice: number; - maxFeePerGas: number; - chainId: number; -} - -export type { ITransaction }; diff --git a/src/types/index.ts b/src/types/index.ts index 5c687896..7f85463c 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,5 +1,5 @@ export * from "../clients/types/ClientConfigs.js"; -export * from "./ITransaction.js"; +export * from "./IMessage.js"; export * from "../signers/types/ISigner.js"; export * from "../signers/types/IPrivateKey.js"; export * from "./IBlock.js"; diff --git a/src/utils/assert.ts b/src/utils/assert.ts index 39b11850..e4d34faf 100644 --- a/src/utils/assert.ts +++ b/src/utils/assert.ts @@ -1,9 +1,9 @@ import type { Hex } from "@noble/curves/abstract/utils"; import invariant from "tiny-invariant"; import type { IPrivateKey } from "../signers/index.js"; -import type { ITransaction } from "../types/ITransaction.js"; +import type { IMessage } from "../types/IMessage.js"; import { isHexString } from "./hex.js"; -import { isAddress } from "./transaction.js"; +import { isAddress } from "./message.js"; /** * Checks if the value is a string. @@ -48,13 +48,12 @@ const assertIsValidPrivateKey = ( }; /** - * Checks if the value is a valid transaction. If the value is a valid transaction, it returns nothing. - * @throws Will throw an error if the value is not a valid transaction. - * @param transaction - The transaction to validate. + * Checks if the value is a valid message. If the value is a valid message, it returns nothing. + * @throws Will throw an error if the value is not a valid message. + * @param message - The message to validate. */ -const assertIsValidTransaction = (transaction: ITransaction) => { - const { chainId, maxPriorityFeePerGas, gasPrice, maxFeePerGas, to } = - transaction; +const assertIsValidMessage = (message: IMessage) => { + const { chainId, maxPriorityFeePerGas, gasPrice, maxFeePerGas, to } = message; invariant( typeof to === "string" && isAddress(to), `Expected a valid 'to' address but got ${to}`, @@ -81,5 +80,5 @@ export { assertIsBuffer, assertIsHexString, assertIsValidPrivateKey, - assertIsValidTransaction, + assertIsValidMessage, }; diff --git a/src/utils/index.ts b/src/utils/index.ts index 8bb9654a..9a5cc367 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,3 +1,3 @@ export * from "./assert.js"; export * from "./hex.js"; -export * from "./transaction.js"; +export * from "./message.js"; diff --git a/src/utils/transaction.ts b/src/utils/message.ts similarity index 100% rename from src/utils/transaction.ts rename to src/utils/message.ts