From 4f16654a179015a6262fc6cbcb24628ee15b5fd7 Mon Sep 17 00:00:00 2001 From: Darko Sperac Date: Fri, 17 May 2024 15:32:56 +0200 Subject: [PATCH 1/6] docs: updated verious util function docs, added tests, refactoring --- __tests__/utils/address.test.ts | 23 ++++++++++++++++++++++- __tests__/utils/contract.test.ts | 31 +++++++++++++++++++++++++++++++ src/utils/address.ts | 12 ++++++++---- src/utils/assert.ts | 5 +++++ src/utils/contract.ts | 10 ++++++++-- src/utils/events.ts | 18 +++++++++++++++++- src/utils/json.ts | 8 ++++++++ 7 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 __tests__/utils/contract.test.ts diff --git a/__tests__/utils/address.test.ts b/__tests__/utils/address.test.ts index b3c346f37..cd59cff97 100644 --- a/__tests__/utils/address.test.ts +++ b/__tests__/utils/address.test.ts @@ -6,6 +6,22 @@ import { validateChecksumAddress, } from '../../src/utils/address'; +describe('addAddressPadding', () => { + test('should correctly add padding', () => { + const addr = '0x6eff1d71'; + const padded = '0x000000000000000000000000000000000000000000000000000000006eff1d71'; + + return expect(addAddressPadding(addr)).toBe(padded); + }); + + test('should add hex prefix', () => { + const addr = 'a7ee790591d9fa3efc87067d95a643f8455e0b8190eb8cb7bfd39e4fb7571fdf'; + const padded = '0xa7ee790591d9fa3efc87067d95a643f8455e0b8190eb8cb7bfd39e4fb7571fdf'; + + return expect(addAddressPadding(addr)).toBe(padded); + }); +}); + describe('validateAndParseAddress', () => { test('should pass when correct starknet address is passed', () => { const addr = '0x7ee790591d9fa3efc87067d95a643f8455e0b8190eb8cb7bfd39e4fb7571fdf'; @@ -14,11 +30,16 @@ describe('validateAndParseAddress', () => { }); test('should add 0x prefix if not provided', () => { - const addr = '0x6eff1d71068df8e6677f59a556151c56ed13e14ad431a9bef6fcb3fc5e6fa7'; + const addr = '6eff1d71068df8e6677f59a556151c56ed13e14ad431a9bef6fcb3fc5e6fa7'; return expect(validateAndParseAddress(addr)).toEqual(`${addAddressPadding(addr)}`); }); + test('should fail for invalid address', () => { + const addr = 'test'; + expect(() => validateAndParseAddress(addr)).toThrow('Cannot convert 0xtest to a BigInt'); + }); + test('should fail for out of bound address', () => { const addr = num.toHex(constants.ADDR_BOUND + 1n); expect(() => validateAndParseAddress(addr)).toThrow(/^Message not signable/); diff --git a/__tests__/utils/contract.test.ts b/__tests__/utils/contract.test.ts new file mode 100644 index 000000000..d74529b49 --- /dev/null +++ b/__tests__/utils/contract.test.ts @@ -0,0 +1,31 @@ +import { isSierra, extractContractHashes } from '../../src/utils/contract'; +import { compiledHelloSierra, compiledHelloSierraCasm, compiledErc20 } from '../config/fixtures'; + +describe('isSierra', () => { + test('should return true for a contract in Sierra format', () => { + expect(isSierra(compiledHelloSierra)).toBe(true); + }); + + test('should return false for a contract not in Sierra format', () => { + expect(isSierra(compiledErc20)).toBe(false); + }); +}); + +describe('extractContractHashes', () => { + test('should properly extract hashes from contract', () => { + const declareContractPayload = { + contract: compiledHelloSierra, + casm: compiledHelloSierraCasm, + }; + const result = extractContractHashes(declareContractPayload); + + expect(result).toHaveProperty( + 'classHash', + '0x50f3c3b9bb088969310de339fd1c1da88945f5db15bd5ea0810e4d954308734' + ); + expect(result).toHaveProperty( + 'compiledClassHash', + '0x31c736e739e4bd35116ed6cdcbb99c94e6f4fa8268d339da23e1ca80fe1de8d' + ); + }); +}); diff --git a/src/utils/address.ts b/src/utils/address.ts index fe5a012e4..905ec45ee 100644 --- a/src/utils/address.ts +++ b/src/utils/address.ts @@ -9,6 +9,7 @@ import { assertInRange, toHex } from './num'; /** * Format a hex number to '0x' and 64 characters, adding leading zeros if necessary. + * * @param {BigNumberish} address * @returns {string} Hex string : 0x followed by 64 characters. No upper case characters in the response. * @example @@ -19,13 +20,17 @@ import { assertInRange, toHex } from './num'; * ``` */ export function addAddressPadding(address: BigNumberish): string { - return addHexPrefix(removeHexPrefix(toHex(address)).padStart(64, '0')); + const hex = toHex(addHexPrefix(address.toString())); + const padded = removeHexPrefix(hex).padStart(64, '0'); + return addHexPrefix(padded); } /** * Check the validity of a Starknet address, and format it as a hex number : '0x' and 64 characters, adding leading zeros if necessary. + * * @param {BigNumberish} address * @returns {string} Hex string : 0x followed by 64 characters. No upper case characters in the response. + * @throws address argument must be a valid address inside the address range bound * @example * ```typescript * const address = "0x90591d9fa3efc87067d95a643f8455e0b8190eb8cb7bfd39e4fb7571fdf"; @@ -34,14 +39,14 @@ export function addAddressPadding(address: BigNumberish): string { * ``` */ export function validateAndParseAddress(address: BigNumberish): string { - assertInRange(address, ZERO, ADDR_BOUND - 1n, 'Starknet Address'); - const result = addAddressPadding(address); if (!result.match(/^(0x)?[0-9a-fA-F]{64}$/)) { throw new Error('Invalid Address Format'); } + assertInRange(result, ZERO, ADDR_BOUND - 1n, 'Starknet Address'); + return result; } @@ -80,7 +85,6 @@ export function getChecksumAddress(address: BigNumberish): string { * a given address to reduce the risk of errors introduced from typing an address or cut and paste issues. * * @param address string - * * @returns true if the ChecksumAddress is valid * @example * ```typescript diff --git a/src/utils/assert.ts b/src/utils/assert.ts index ef35545a3..c29ab13a9 100644 --- a/src/utils/assert.ts +++ b/src/utils/assert.ts @@ -3,6 +3,11 @@ * @param {any} condition - The condition to check. * @param {string} [message] - The optional message to include in the error. * @throws {Error} Throws an error if the condition is false. + * @example + * ```typescript + * const address = '0xa7ee790591d9fa3efc87067d95a643f8455e0b8190eb8cb7bfd39e4fb7571fdf'; + * assert(/^(0x)?[0-9a-fA-F]{64}$/.test(address)), 'Invalid address format'); + * ``` */ export default function assert(condition: boolean, message?: string): asserts condition { if (!condition) { diff --git a/src/utils/contract.ts b/src/utils/contract.ts index 1fb6a5315..f29bd1cd5 100644 --- a/src/utils/contract.ts +++ b/src/utils/contract.ts @@ -18,6 +18,10 @@ import { isString } from './shortString'; * * @param {CairoContract | string} contract - The contract to check. Can be either a CairoContract object or a string representation of the contract. * @return {boolean} - Returns true if the contract is a Sierra contract, otherwise false. + * @example + * ```typescript + * const result = isSierra(contract); + * ``` */ export function isSierra( contract: CairoContract | string @@ -30,10 +34,12 @@ export function isSierra( * Extracts contract hashes from `DeclareContractPayload`. * * @param {DeclareContractPayload} payload - The payload containing contract information. - * * @return {CompleteDeclareContractPayload} - The `CompleteDeclareContractPayload` with extracted contract hashes. - * * @throws {Error} - If extraction of compiledClassHash or classHash fails. + * @example + * ```typescript + * const result = extractContractHashes(contract); + * ``` */ export function extractContractHashes( payload: DeclareContractPayload diff --git a/src/utils/events.ts b/src/utils/events.ts index 700dd97b6..a3d9e4dbd 100644 --- a/src/utils/events.ts +++ b/src/utils/events.ts @@ -6,7 +6,23 @@ import { cleanHex } from './num'; * Parse Transaction Receipt Event from UDC invoke transaction and * create DeployContractResponse compatible response with addition of the UDC Event data * - * @returns DeployContractResponse | UDC Event Response data + * @param {InvokeTransactionReceiptResponse} txReceipt + * @return {ReturnType} Object including DeployContractResponse and UDC Event data + * @example + * ```typescript + * const deployment = await account.deploy({ + * classHash, + * constructorCalldata: [ + * encodeShortString('Token'), + * encodeShortString('ERC20'), + * account.address, + * ], + * salt, + * unique: true, + * }); + * const txReceipt = await provider.waitForTransaction(deployment.transaction_hash); + * const udcEvent = parseUDCEvent(txReceipt as any); + * ``` */ export function parseUDCEvent(txReceipt: InvokeTransactionReceiptResponse) { if (!txReceipt.events) { diff --git a/src/utils/json.ts b/src/utils/json.ts index 20d9a2801..6a500efc8 100644 --- a/src/utils/json.ts +++ b/src/utils/json.ts @@ -14,7 +14,15 @@ const parseIntAsNumberOrBigInt = (x: string) => { * * NOTE: the String() wrapping is used so the behavior conforms to JSON.parse() * which can accept simple data types but is not represented in the default typing + * * @param x JSON string + * @return {object} Parsed json object + * @example + * ```typescript + * const str = '[123, 12.3, 11223344556677889900]'; + * const result = parse(str); + * // result = [123, 12.3, 11223344556677890048n] + * ``` */ export const parse = (x: string): any => json.parse(String(x), undefined, parseIntAsNumberOrBigInt); From 5bba9507c79c69af3a9add60da417390befdcac4 Mon Sep 17 00:00:00 2001 From: Darko Sperac Date: Tue, 21 May 2024 11:16:40 +0200 Subject: [PATCH 2/6] docs: json util jsdocs improvements --- src/utils/events.ts | 2 +- src/utils/json.ts | 46 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/utils/events.ts b/src/utils/events.ts index a3d9e4dbd..34f1c780c 100644 --- a/src/utils/events.ts +++ b/src/utils/events.ts @@ -7,7 +7,7 @@ import { cleanHex } from './num'; * create DeployContractResponse compatible response with addition of the UDC Event data * * @param {InvokeTransactionReceiptResponse} txReceipt - * @return {ReturnType} Object including DeployContractResponse and UDC Event data + * @return {object} Object including DeployContractResponse and UDC Event data * @example * ```typescript * const deployment = await account.deploy({ diff --git a/src/utils/json.ts b/src/utils/json.ts index 6a500efc8..6e39e54e1 100644 --- a/src/utils/json.ts +++ b/src/utils/json.ts @@ -2,11 +2,18 @@ import * as json from 'lossless-json'; /** * Convert string to number or bigint based on size + * @param str string to parse + * @return {number | BigInt} parsed number + * @example + * ```typescript + * parseIntAsNumberOrBigInt('1.2'); // 1.2 + * parseIntAsNumberOrBigInt('11223344556677889900'); // 11223344556677890048n + * ``` */ -const parseIntAsNumberOrBigInt = (x: string) => { - if (!json.isInteger(x)) return parseFloat(x); - const v = parseInt(x, 10); - return Number.isSafeInteger(v) ? v : BigInt(x); +const parseIntAsNumberOrBigInt = (str: string) => { + if (!json.isInteger(str)) return parseFloat(str); + const num = parseInt(str, 10); + return Number.isSafeInteger(num) ? num : BigInt(str); }; /** @@ -15,7 +22,7 @@ const parseIntAsNumberOrBigInt = (x: string) => { * NOTE: the String() wrapping is used so the behavior conforms to JSON.parse() * which can accept simple data types but is not represented in the default typing * - * @param x JSON string + * @param str JSON string * @return {object} Parsed json object * @example * ```typescript @@ -24,21 +31,40 @@ const parseIntAsNumberOrBigInt = (x: string) => { * // result = [123, 12.3, 11223344556677890048n] * ``` */ -export const parse = (x: string): any => json.parse(String(x), undefined, parseIntAsNumberOrBigInt); +export const parse = (str: string): any => + json.parse(String(str), undefined, parseIntAsNumberOrBigInt); /** * Convert JSON string to JSON object with all numbers as bigint - * @param x JSON string + * @param str JSON string + * @return {object} Parsed json object + * @example + * ```typescript + * const str = '[123, 12.3, 11223344556677889900]'; + * const result = parseAlwaysAsBig(str); + * // result = [123n, 12.3, 11223344556677890048n] + * ``` */ -export const parseAlwaysAsBig = (x: string): any => - json.parse(String(x), undefined, json.parseNumberAndBigInt); +export const parseAlwaysAsBig = (str: string): any => + json.parse(String(str), undefined, json.parseNumberAndBigInt); /** * Convert JSON object to JSON string * * NOTE: the not-null assertion is used so the return type conforms to JSON.stringify() * which can also return undefined but is not represented in the default typing - * @returns JSON string + * + * @param value JSON object + * @param [replacer] Function that alters the behavior of the stringification process + * @param [space] Used to insert white space into the output JSON string + * @param [numberStringifiers] Function used to stringify numbers (returning undefined will delete the property from the object) + * @return {string} JSON string + * @example + * ```typescript + * const str = '[123, 12.3, 11223344556677889900]'; + * const result = stringify(str); + * // result = [123, 12.3, 11223344556677890048n] + * ``` */ export const stringify = ( value: unknown, From c015691b1c7369a7a39bd6c8ff69d99a932d225e Mon Sep 17 00:00:00 2001 From: Darko Sperac Date: Tue, 21 May 2024 11:32:04 +0200 Subject: [PATCH 3/6] chore: typo quickfix --- src/utils/assert.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/assert.ts b/src/utils/assert.ts index c29ab13a9..56323961f 100644 --- a/src/utils/assert.ts +++ b/src/utils/assert.ts @@ -6,7 +6,7 @@ * @example * ```typescript * const address = '0xa7ee790591d9fa3efc87067d95a643f8455e0b8190eb8cb7bfd39e4fb7571fdf'; - * assert(/^(0x)?[0-9a-fA-F]{64}$/.test(address)), 'Invalid address format'); + * assert(/^(0x)?[0-9a-fA-F]{64}$/.test(address), 'Invalid address format'); * ``` */ export default function assert(condition: boolean, message?: string): asserts condition { From 56772c7f42554386a8be007ce35e4f181dec8178 Mon Sep 17 00:00:00 2001 From: Darko Sperac Date: Tue, 21 May 2024 16:47:34 +0200 Subject: [PATCH 4/6] chore: added missing results to jsdoc examples --- src/utils/contract.ts | 7 +++++++ src/utils/events.ts | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/utils/contract.ts b/src/utils/contract.ts index f29bd1cd5..b93c8c230 100644 --- a/src/utils/contract.ts +++ b/src/utils/contract.ts @@ -21,6 +21,7 @@ import { isString } from './shortString'; * @example * ```typescript * const result = isSierra(contract); + * // result = true | false * ``` */ export function isSierra( @@ -39,6 +40,12 @@ export function isSierra( * @example * ```typescript * const result = extractContractHashes(contract); + * // result = { + * // contract: ..., + * // classHash: ..., + * // casm: ..., + * // compiledClassHash: ..., + * // } * ``` */ export function extractContractHashes( diff --git a/src/utils/events.ts b/src/utils/events.ts index 34f1c780c..3fcb4ef47 100644 --- a/src/utils/events.ts +++ b/src/utils/events.ts @@ -22,6 +22,17 @@ import { cleanHex } from './num'; * }); * const txReceipt = await provider.waitForTransaction(deployment.transaction_hash); * const udcEvent = parseUDCEvent(txReceipt as any); + * // udcEvent = { + * // transaction_hash: ..., + * // contract_address: ..., + * // address: ..., + * // deployer: ..., + * // unique: ..., + * // classHash: ..., + * // calldata_len: ..., + * // calldata: ..., + * // salt: ..., + * // } * ``` */ export function parseUDCEvent(txReceipt: InvokeTransactionReceiptResponse) { From 6e4e049847c28d8ba00f4c9b9143e2f9b5821818 Mon Sep 17 00:00:00 2001 From: Darko Sperac Date: Wed, 22 May 2024 09:35:43 +0200 Subject: [PATCH 5/6] chore: removed jsdoc from non-exported helper function --- src/utils/json.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/utils/json.ts b/src/utils/json.ts index 6e39e54e1..68de54819 100644 --- a/src/utils/json.ts +++ b/src/utils/json.ts @@ -1,14 +1,7 @@ import * as json from 'lossless-json'; /** - * Convert string to number or bigint based on size - * @param str string to parse - * @return {number | BigInt} parsed number - * @example - * ```typescript - * parseIntAsNumberOrBigInt('1.2'); // 1.2 - * parseIntAsNumberOrBigInt('11223344556677889900'); // 11223344556677890048n - * ``` + * Helper to convert string to number or bigint based on size */ const parseIntAsNumberOrBigInt = (str: string) => { if (!json.isInteger(str)) return parseFloat(str); From 1f988025abf9e5cdccb4de8698dcbf6f3bcc3998 Mon Sep 17 00:00:00 2001 From: Darko Sperac Date: Wed, 5 Jun 2024 07:47:54 +0200 Subject: [PATCH 6/6] chore: utils/json docs fixes and refactoring --- src/utils/json.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/utils/json.ts b/src/utils/json.ts index 68de54819..84a6a47e5 100644 --- a/src/utils/json.ts +++ b/src/utils/json.ts @@ -33,9 +33,9 @@ export const parse = (str: string): any => * @return {object} Parsed json object * @example * ```typescript - * const str = '[123, 12.3, 11223344556677889900]'; + * const str = '[123, 12.3, 1234567890]'; * const result = parseAlwaysAsBig(str); - * // result = [123n, 12.3, 11223344556677890048n] + * // result = [123n, 12.3, 1234567890n] * ``` */ export const parseAlwaysAsBig = (str: string): any => @@ -54,9 +54,9 @@ export const parseAlwaysAsBig = (str: string): any => * @return {string} JSON string * @example * ```typescript - * const str = '[123, 12.3, 11223344556677889900]'; - * const result = stringify(str); - * // result = [123, 12.3, 11223344556677890048n] + * const value = [123, 12.3, 1234567890]; + * const result = stringify(value); + * // result = '[123,12.3,1234567890]' * ``` */ export const stringify = (