diff --git a/__tests__/cairo1v2_typed.test.ts b/__tests__/cairo1v2_typed.test.ts index ac01daf1e..a45bbd34e 100644 --- a/__tests__/cairo1v2_typed.test.ts +++ b/__tests__/cairo1v2_typed.test.ts @@ -27,7 +27,8 @@ import { types, } from '../src'; import { hexToDecimalString } from '../src/utils/num'; -import { encodeShortString, isString } from '../src/utils/shortString'; +import { encodeShortString } from '../src/utils/shortString'; +import { isString } from '../src/utils/typed'; import { TEST_TX_VERSION, compiledC1Account, diff --git a/__tests__/config/schema.ts b/__tests__/config/schema.ts index e985a3f35..e27f4f343 100644 --- a/__tests__/config/schema.ts +++ b/__tests__/config/schema.ts @@ -11,7 +11,7 @@ import componentSchemas from '../schemas/component.json'; import libSchemas from '../schemas/lib.json'; import providerSchemas from '../schemas/provider.json'; import rpcSchemas from '../schemas/rpc.json'; -import { isBigInt } from '../../src/utils/num'; +import { isBigInt } from '../../src/utils/typed'; const matcherSchemas = [accountSchemas, libSchemas, providerSchemas, rpcSchemas]; const starknetSchemas = [ diff --git a/__tests__/utils/assert.test.ts b/__tests__/utils/assert.test.ts new file mode 100644 index 000000000..5e4bb4c7d --- /dev/null +++ b/__tests__/utils/assert.test.ts @@ -0,0 +1,15 @@ +import assert from '../../src/utils/assert'; + +describe('assert', () => { + test('should throw an error if condition is not true', () => { + expect(() => assert(false)).toThrow(new Error('Assertion failure')); + }); + + test('should throw an error with a specific message', () => { + expect(() => assert(false, 'Error message')).toThrow(new Error('Error message')); + }); + + test('should not throw an error if condition is true', () => { + expect(() => assert(true)).toBeTruthy(); + }); +}); diff --git a/__tests__/utils/CairoTypes/CairoFelt.test.ts b/__tests__/utils/cairoDataTypes/CairoFelt.test.ts similarity index 100% rename from __tests__/utils/CairoTypes/CairoFelt.test.ts rename to __tests__/utils/cairoDataTypes/CairoFelt.test.ts diff --git a/__tests__/utils/CairoTypes/CairoUint256.test.ts b/__tests__/utils/cairoDataTypes/CairoUint256.test.ts similarity index 100% rename from __tests__/utils/CairoTypes/CairoUint256.test.ts rename to __tests__/utils/cairoDataTypes/CairoUint256.test.ts diff --git a/__tests__/utils/CairoTypes/CairoUint512.test.ts b/__tests__/utils/cairoDataTypes/CairoUint512.test.ts similarity index 100% rename from __tests__/utils/CairoTypes/CairoUint512.test.ts rename to __tests__/utils/cairoDataTypes/CairoUint512.test.ts diff --git a/__tests__/utils/num.test.ts b/__tests__/utils/num.test.ts index 81f0e7bb1..f918f4408 100644 --- a/__tests__/utils/num.test.ts +++ b/__tests__/utils/num.test.ts @@ -1,7 +1,6 @@ import { isHex, toBigInt, - isBigInt, toHex, hexToDecimalString, cleanHex, @@ -15,8 +14,6 @@ import { toCairoBool, hexToBytes, addPercent, - isNumber, - isBoolean, } from '../../src/utils/num'; import { num } from '../../src'; @@ -49,23 +46,6 @@ describe('toBigInt', () => { }); }); -describe('isBigInt', () => { - test('should return true for big integers', () => { - expect(isBigInt(BigInt(10))).toBe(true); - expect(isBigInt(BigInt('9007199254740991'))).toBe(true); - }); - - test('should return false for non-big integers', () => { - expect(isBigInt(10)).toBe(false); - expect(isBigInt('10')).toBe(false); - expect(isBigInt(undefined)).toBe(false); - expect(isBigInt(null)).toBe(false); - expect(isBigInt({})).toBe(false); - expect(isBigInt([])).toBe(false); - expect(isBigInt(true)).toBe(false); - }); -}); - describe('toHex', () => { test('should properly convert to hex-string', () => { expect(toHex(100)).toBe('0x64'); @@ -177,39 +157,6 @@ describe('addPercent', () => { }); }); -describe('isNumber', () => { - test('should correctly determine if value is a number', () => { - expect(isNumber(0)).toBe(true); - expect(isNumber(123)).toBe(true); - expect(isNumber(-123)).toBe(true); - - expect(isNumber(123n)).toBe(false); - expect(isNumber('')).toBe(false); - expect(isNumber('123')).toBe(false); - expect(isNumber(true)).toBe(false); - expect(isNumber(false)).toBe(false); - expect(isNumber(null)).toBe(false); - expect(isBoolean([])).toBe(false); - expect(isBoolean({})).toBe(false); - }); -}); - -describe('isBoolean', () => { - test('should correctly determine if value is a boolean', () => { - expect(isBoolean(true)).toBe(true); - expect(isBoolean(false)).toBe(true); - - expect(isBoolean(0)).toBe(false); - expect(isBoolean(1)).toBe(false); - expect(isBoolean('')).toBe(false); - expect(isBoolean('true')).toBe(false); - expect(isBoolean('false')).toBe(false); - expect(isBoolean(null)).toBe(false); - expect(isBoolean([])).toBe(false); - expect(isBoolean({})).toBe(false); - }); -}); - describe('stringToSha256ToArrayBuff4', () => { test('should correctly hash&encode an utf8 string', () => { const buff = num.stringToSha256ToArrayBuff4('LedgerW'); diff --git a/__tests__/utils/shortString.test.ts b/__tests__/utils/shortString.test.ts index de894d709..41cb739d6 100644 --- a/__tests__/utils/shortString.test.ts +++ b/__tests__/utils/shortString.test.ts @@ -5,7 +5,6 @@ import { encodeShortString, isDecimalString, isShortString, - isString, } from '../../src/utils/shortString'; describe('shortString', () => { @@ -110,22 +109,6 @@ describe('shortString', () => { ).toBe(''); }); -describe('isString', () => { - test('should return true for strings', () => { - expect(isString('test')).toBe(true); - expect(isString('')).toBe(true); - }); - - test('should return false for non-string values', () => { - expect(isString(10)).toBe(false); - expect(isString({})).toBe(false); - expect(isString(null)).toBe(false); - expect(isString(undefined)).toBe(false); - expect(isString([])).toBe(false); - expect(isString(true)).toBe(false); - }); -}); - describe('isShortString', () => { test('should return true for short strings', () => { const shortStr = '1234567890123456789012345678901'; diff --git a/__tests__/utils/typed.test.ts b/__tests__/utils/typed.test.ts new file mode 100644 index 000000000..27b037759 --- /dev/null +++ b/__tests__/utils/typed.test.ts @@ -0,0 +1,100 @@ +import { + isUndefined, + isBigInt, + isBoolean, + isNumber, + isString, + isObject, +} from '../../src/utils/typed'; + +describe('isUndefined', () => { + test('should return true if value is undefined', () => { + expect(isUndefined(undefined)).toBe(true); + }); + + test('should return false if value is not undefined', () => { + const value = 'existing value'; + expect(isUndefined(value)).toBe(false); + }); +}); + +describe('isNumber', () => { + test('should correctly determine if value is a number', () => { + expect(isNumber(0)).toBe(true); + expect(isNumber(123)).toBe(true); + expect(isNumber(-123)).toBe(true); + + expect(isNumber(123n)).toBe(false); + expect(isNumber('')).toBe(false); + expect(isNumber('123')).toBe(false); + expect(isNumber(true)).toBe(false); + expect(isNumber(false)).toBe(false); + expect(isNumber(null)).toBe(false); + expect(isBoolean([])).toBe(false); + expect(isBoolean({})).toBe(false); + }); +}); + +describe('isBoolean', () => { + test('should correctly determine if value is a boolean', () => { + expect(isBoolean(true)).toBe(true); + expect(isBoolean(false)).toBe(true); + + expect(isBoolean(0)).toBe(false); + expect(isBoolean(1)).toBe(false); + expect(isBoolean('')).toBe(false); + expect(isBoolean('true')).toBe(false); + expect(isBoolean('false')).toBe(false); + expect(isBoolean(null)).toBe(false); + expect(isBoolean([])).toBe(false); + expect(isBoolean({})).toBe(false); + }); +}); + +describe('isBigInt', () => { + test('should return true for big integers', () => { + expect(isBigInt(BigInt(10))).toBe(true); + expect(isBigInt(BigInt('9007199254740991'))).toBe(true); + }); + + test('should return false for non-big integers', () => { + expect(isBigInt(10)).toBe(false); + expect(isBigInt('10')).toBe(false); + expect(isBigInt(undefined)).toBe(false); + expect(isBigInt(null)).toBe(false); + expect(isBigInt({})).toBe(false); + expect(isBigInt([])).toBe(false); + expect(isBigInt(true)).toBe(false); + }); +}); + +describe('isString', () => { + test('should return true for strings', () => { + expect(isString('test')).toBe(true); + expect(isString('')).toBe(true); + }); + + test('should return false for non-string values', () => { + expect(isString(10)).toBe(false); + expect(isString({})).toBe(false); + expect(isString(null)).toBe(false); + expect(isString(undefined)).toBe(false); + expect(isString([])).toBe(false); + expect(isString(true)).toBe(false); + }); +}); + +describe('isObject', () => { + test('should return true if value is object', () => { + expect(isObject({ test: 'test' })).toEqual(true); + expect(isObject({})).toEqual(true); + }); + + test('should return false if value is not object', () => { + expect(isObject(10)).toBe(false); + expect(isObject(null)).toBe(false); + expect(isObject(undefined)).toBe(false); + expect(isObject([])).toBe(false); + expect(isObject(true)).toBe(false); + }); +}); diff --git a/package-lock.json b/package-lock.json index aca534791..e40fe1cbf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,8 +19,7 @@ "lossless-json": "^4.0.1", "pako": "^2.0.4", "starknet-types-07": "npm:@starknet-io/types-js@^0.7.7", - "ts-mixer": "^6.0.3", - "url-join": "^4.0.1" + "ts-mixer": "^6.0.3" }, "devDependencies": { "@babel/plugin-transform-modules-commonjs": "^7.18.2", @@ -37,7 +36,6 @@ "@types/jest": "^29.5.0", "@types/jest-json-schema": "^6.1.1", "@types/pako": "^2.0.0", - "@types/url-join": "^4.0.1", "@typescript-eslint/eslint-plugin": "^7.4.0", "@typescript-eslint/parser": "^7.4.0", "ajv": "^8.12.0", @@ -5460,13 +5458,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/url-join": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/url-join/-/url-join-4.0.3.tgz", - "integrity": "sha512-3l1qMm3wqO0iyC5gkADzT95UVW7C/XXcdvUcShOideKF0ddgVRErEQQJXBd2kvQm+aSgqhBGHGB38TgMeT57Ww==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", @@ -20019,12 +20010,6 @@ "punycode": "^2.1.0" } }, - "node_modules/url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "license": "MIT" - }, "node_modules/url-parse": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", diff --git a/package.json b/package.json index 569c8de92..52395c6c5 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,6 @@ "@types/jest": "^29.5.0", "@types/jest-json-schema": "^6.1.1", "@types/pako": "^2.0.0", - "@types/url-join": "^4.0.1", "@typescript-eslint/eslint-plugin": "^7.4.0", "@typescript-eslint/parser": "^7.4.0", "ajv": "^8.12.0", @@ -105,8 +104,7 @@ "lossless-json": "^4.0.1", "pako": "^2.0.4", "starknet-types-07": "npm:@starknet-io/types-js@^0.7.7", - "ts-mixer": "^6.0.3", - "url-join": "^4.0.1" + "ts-mixer": "^6.0.3" }, "lint-staged": { "*.ts": "eslint --cache --fix", diff --git a/src/account/default.ts b/src/account/default.ts index aa9154748..ad15db5e8 100644 --- a/src/account/default.ts +++ b/src/account/default.ts @@ -1,6 +1,3 @@ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -import type { SPEC } from 'starknet-types-07'; - import { OutsideExecutionCallerAny, SNIP9_V1_INTERFACE_ID, @@ -30,6 +27,7 @@ import { DeployContractUDCResponse, DeployTransactionReceiptResponse, EstimateFee, + UniversalSuggestedFee, EstimateFeeAction, EstimateFeeBulk, Invocation, @@ -47,7 +45,7 @@ import { UniversalDeployerContractPayload, UniversalDetails, } from '../types'; -import { ETransactionVersion, ETransactionVersion3, ResourceBounds } from '../types/api'; +import { ETransactionVersion, ETransactionVersion3, type ResourceBounds } from '../types/api'; import { OutsideExecutionVersion, type OutsideExecution, @@ -58,6 +56,7 @@ import { CallData } from '../utils/calldata'; import { extractContractHashes, isSierra } from '../utils/contract'; import { parseUDCEvent } from '../utils/events'; import { calculateContractAddressFromHash } from '../utils/hash'; +import { isUndefined, isString } from '../utils/typed'; import { isHex, toBigInt, toCairoBool, toHex } from '../utils/num'; import { buildExecuteFromOutsideCallData, @@ -65,7 +64,6 @@ import { getTypedData, } from '../utils/outsideExecution'; import { parseContract } from '../utils/provider'; -import { isString } from '../utils/shortString'; import { supportsInterface } from '../utils/src5'; import { estimateFeeToBounds, @@ -678,7 +676,7 @@ export class Account extends Provider implements AccountInterface { * const call1: Call = { contractAddress: ethAddress, entrypoint: 'transfer', calldata: { * recipient: recipientAccount.address, amount: cairo.uint256(100) } }; * const outsideTransaction1: OutsideTransaction = await signerAccount.getOutsideTransaction(callOptions, call3); - * // result = { + * // result = { * // outsideExecution: { * // caller: '0x64b48806902a367c8598f4f95c305e8c1a1acba5f082d294a43793113115691', * // nonce: '0x28a612590dbc36927933c8ee0f357eee639c8b22b3d3aa86949eed3ada4ac55', @@ -784,9 +782,10 @@ export class Account extends Provider implements AccountInterface { version: ETransactionVersion, { type, payload }: EstimateFeeAction, details: UniversalDetails - ) { + ): Promise { let maxFee: BigNumberish = 0; let resourceBounds: ResourceBounds = estimateFeeToBounds(ZERO); + if (version === ETransactionVersion.V3) { resourceBounds = details.resourceBounds ?? @@ -803,28 +802,25 @@ export class Account extends Provider implements AccountInterface { }; } - public async getSuggestedFee({ type, payload }: EstimateFeeAction, details: UniversalDetails) { - let feeEstimate: EstimateFee; - + public async getSuggestedFee( + { type, payload }: EstimateFeeAction, + details: UniversalDetails + ): Promise { switch (type) { case TransactionType.INVOKE: - feeEstimate = await this.estimateInvokeFee(payload, details); - break; + return this.estimateInvokeFee(payload, details); case TransactionType.DECLARE: - feeEstimate = await this.estimateDeclareFee(payload, details); - break; + return this.estimateDeclareFee(payload, details); case TransactionType.DEPLOY_ACCOUNT: - feeEstimate = await this.estimateAccountDeployFee(payload, details); - break; + return this.estimateAccountDeployFee(payload, details); case TransactionType.DEPLOY: - feeEstimate = await this.estimateDeployFee(payload, details); - break; + return this.estimateDeployFee(payload, details); default: - feeEstimate = { + return { gas_consumed: 0n, gas_price: 0n, overall_fee: ZERO, @@ -834,10 +830,7 @@ export class Account extends Provider implements AccountInterface { data_gas_consumed: 0n, data_gas_price: 0n, }; - break; } - - return feeEstimate; } public async buildInvocation( @@ -863,7 +856,7 @@ export class Account extends Provider implements AccountInterface { const compressedCompiledContract = parseContract(contract); if ( - typeof compiledClassHash === 'undefined' && + isUndefined(compiledClassHash) && (details.version === ETransactionVersion3.F3 || details.version === ETransactionVersion3.V3) ) { throw Error('V3 Transaction work with Cairo1 Contracts and require compiledClassHash'); diff --git a/src/account/interface.ts b/src/account/interface.ts index d86405a2c..42ab2ab27 100644 --- a/src/account/interface.ts +++ b/src/account/interface.ts @@ -119,13 +119,13 @@ export abstract class AccountInterface extends ProviderInterface { /** * Estimate Fee for executing a UDC DEPLOY transaction on starknet * This is different from the normal DEPLOY transaction as it goes through the Universal Deployer Contract (UDC) - + * @param deployContractPayload array or singular * - classHash: computed class hash of compiled contract * - salt: address salt * - unique: bool if true ensure unique salt * - constructorCalldata: constructor calldata - * + * * @param estimateFeeDetails - * - blockIdentifier? * - nonce? diff --git a/src/index.ts b/src/index.ts index 6bf0681ec..4804e4b9f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,7 +40,6 @@ export * from './utils/responseParser'; export * from './utils/cairoDataTypes/uint256'; export * from './utils/cairoDataTypes/uint512'; export * from './utils/address'; -export * from './utils/url'; export * from './utils/calldata'; export * from './utils/calldata/enum'; export * from './utils/contract'; diff --git a/src/types/account.ts b/src/types/account.ts index fec5c0754..2a6022844 100644 --- a/src/types/account.ts +++ b/src/types/account.ts @@ -14,6 +14,11 @@ import { DeclareTransactionReceiptResponse, EstimateFeeResponse } from './provid export interface EstimateFee extends EstimateFeeResponse {} +export type UniversalSuggestedFee = { + maxFee: BigNumberish; + resourceBounds: ResourceBounds; +}; + export type EstimateFeeBulk = Array; // TODO: This is too wide generic with optional params diff --git a/src/types/lib/contract/abi.ts b/src/types/lib/contract/abi.ts index 5e0bee453..97cd01b55 100644 --- a/src/types/lib/contract/abi.ts +++ b/src/types/lib/contract/abi.ts @@ -4,7 +4,7 @@ import type { ENUM_EVENT, EVENT_FIELD, STRUCT_EVENT } from 'starknet-types-07'; export type Abi = ReadonlyArray; // Basic elements -export type AbiEntry = { name: string; type: 'felt' | 'felt*' | string }; +export type AbiEntry = { name: string; type: 'felt' | 'felt*' | 'event' | string }; export type EventEntry = { name: string; type: 'felt' | 'felt*' | string; kind: 'key' | 'data' }; diff --git a/src/types/lib/index.ts b/src/types/lib/index.ts index 9bbdcb138..69be02c71 100644 --- a/src/types/lib/index.ts +++ b/src/types/lib/index.ts @@ -2,8 +2,8 @@ import { StarknetChainId } from '../../constants'; import { weierstrass } from '../../utils/ec'; import { EDataAvailabilityMode, ResourceBounds } from '../api'; import { CairoEnum } from '../cairoEnum'; -import { ValuesType } from '../helpers/valuesType'; import { CompiledContract, CompiledSierraCasm, ContractClass } from './contract'; +import { ValuesType } from '../helpers/valuesType'; export type WeierstrassSignatureType = weierstrass.SignatureType; export type ArraySignatureType = string[]; diff --git a/src/utils/assert.ts b/src/utils/assert.ts index 56323961f..5b6ea51c3 100644 --- a/src/utils/assert.ts +++ b/src/utils/assert.ts @@ -1,6 +1,6 @@ /** * Asserts that the given condition is true, otherwise throws an error with an optional message. - * @param {any} condition - The condition to check. + * @param {boolean} 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 diff --git a/src/utils/cairoDataTypes/felt.ts b/src/utils/cairoDataTypes/felt.ts index 480f7eba1..6bba632a5 100644 --- a/src/utils/cairoDataTypes/felt.ts +++ b/src/utils/cairoDataTypes/felt.ts @@ -1,7 +1,8 @@ // TODO Convert to CairoFelt base on CairoUint256 and implement it in the codebase in the backward compatible manner -import { BigNumberish, isBigInt, isBoolean, isHex, isStringWholeNumber } from '../num'; -import { encodeShortString, isShortString, isString, isText } from '../shortString'; +import { BigNumberish, isHex, isStringWholeNumber } from '../num'; +import { encodeShortString, isShortString, isText } from '../shortString'; +import { isBoolean, isString, isBigInt } from '../typed'; /** * Create felt Cairo type (cairo type helper) diff --git a/src/utils/calldata/enum/CairoCustomEnum.ts b/src/utils/calldata/enum/CairoCustomEnum.ts index c1c8fd7a7..fb00a4214 100644 --- a/src/utils/calldata/enum/CairoCustomEnum.ts +++ b/src/utils/calldata/enum/CairoCustomEnum.ts @@ -1,3 +1,5 @@ +import { isUndefined } from '../../typed'; + export type CairoEnumRaw = { [key: string]: any; }; @@ -31,9 +33,7 @@ export class CairoCustomEnum { if (variantsList.length === 0) { throw new Error('This Enum must have at least 1 variant'); } - const nbActiveVariants = variantsList.filter( - (content) => typeof content !== 'undefined' - ).length; + const nbActiveVariants = variantsList.filter((content) => !isUndefined(content)).length; if (nbActiveVariants !== 1) { throw new Error('This Enum must have exactly one active variant'); } @@ -46,11 +46,8 @@ export class CairoCustomEnum { */ public unwrap(): any { const variants = Object.entries(this.variant); - const activeVariant = variants.find((item) => typeof item[1] !== 'undefined'); - if (typeof activeVariant === 'undefined') { - return undefined; - } - return activeVariant[1]; + const activeVariant = variants.find((item) => !isUndefined(item[1])); + return isUndefined(activeVariant) ? undefined : activeVariant[1]; } /** @@ -59,10 +56,7 @@ export class CairoCustomEnum { */ public activeVariant(): string { const variants = Object.entries(this.variant); - const activeVariant = variants.find((item) => typeof item[1] !== 'undefined'); - if (typeof activeVariant === 'undefined') { - return ''; - } - return activeVariant[0]; + const activeVariant = variants.find((item) => !isUndefined(item[1])); + return isUndefined(activeVariant) ? '' : activeVariant[0]; } } diff --git a/src/utils/calldata/enum/CairoOption.ts b/src/utils/calldata/enum/CairoOption.ts index 53e83ba6a..97919e742 100644 --- a/src/utils/calldata/enum/CairoOption.ts +++ b/src/utils/calldata/enum/CairoOption.ts @@ -1,4 +1,5 @@ import { ValuesType } from '../../../types/helpers/valuesType'; +import { isUndefined } from '../../typed'; export const CairoOptionVariant = { Some: 0, @@ -27,7 +28,7 @@ export class CairoOption { throw new Error('Wrong variant : should be CairoOptionVariant.Some or .None.'); } if (variant === CairoOptionVariant.Some) { - if (typeof someContent === 'undefined') { + if (isUndefined(someContent)) { throw new Error( 'The creation of a Cairo Option with "Some" variant needs a content as input.' ); @@ -46,10 +47,7 @@ export class CairoOption { * If None, returns 'undefined'. */ public unwrap(): T | undefined { - if (this.None) { - return undefined; - } - return this.Some; + return this.None ? undefined : this.Some; } /** @@ -57,7 +55,7 @@ export class CairoOption { * @returns true if the valid variant is 'isSome'. */ public isSome(): boolean { - return !(typeof this.Some === 'undefined'); + return !isUndefined(this.Some); } /** diff --git a/src/utils/calldata/enum/CairoResult.ts b/src/utils/calldata/enum/CairoResult.ts index c09a0b46a..654c746c7 100644 --- a/src/utils/calldata/enum/CairoResult.ts +++ b/src/utils/calldata/enum/CairoResult.ts @@ -1,4 +1,5 @@ import { ValuesType } from '../../../types/helpers/valuesType'; +import { isUndefined } from '../../typed'; export const CairoResultVariant = { Ok: 0, @@ -40,10 +41,10 @@ export class CairoResult { * @returns the content of the valid variant of a Cairo Result. */ public unwrap(): T | U { - if (typeof this.Ok !== 'undefined') { + if (!isUndefined(this.Ok)) { return this.Ok; } - if (typeof this.Err !== 'undefined') { + if (!isUndefined(this.Err)) { return this.Err; } throw new Error('Both Result.Ok and .Err are undefined. Not authorized.'); @@ -54,7 +55,7 @@ export class CairoResult { * @returns true if the valid variant is 'Ok'. */ public isOk(): boolean { - return !(typeof this.Ok === 'undefined'); + return !isUndefined(this.Ok); } /** @@ -62,6 +63,6 @@ export class CairoResult { * @returns true if the valid variant is 'isErr'. */ public isErr(): boolean { - return !(typeof this.Err === 'undefined'); + return !isUndefined(this.Err); } } diff --git a/src/utils/calldata/formatter.ts b/src/utils/calldata/formatter.ts index 260299d0c..b1ea5eda0 100644 --- a/src/utils/calldata/formatter.ts +++ b/src/utils/calldata/formatter.ts @@ -1,4 +1,4 @@ -import { isBigInt } from '../num'; +import { isBigInt } from '../typed'; import { decodeShortString } from '../shortString'; const guard = { diff --git a/src/utils/calldata/index.ts b/src/utils/calldata/index.ts index 53336b88c..4be129fc9 100644 --- a/src/utils/calldata/index.ts +++ b/src/utils/calldata/index.ts @@ -15,7 +15,8 @@ import { ValidateType, } from '../../types'; import assert from '../assert'; -import { isBigInt, toHex } from '../num'; +import { toHex } from '../num'; +import { isBigInt } from '../typed'; import { getSelectorFromName } from '../hash/selector'; import { isLongText } from '../shortString'; import { byteArrayFromString } from './byteArray'; diff --git a/src/utils/calldata/propertyOrder.ts b/src/utils/calldata/propertyOrder.ts index 4619700fa..a6d046cdc 100644 --- a/src/utils/calldata/propertyOrder.ts +++ b/src/utils/calldata/propertyOrder.ts @@ -24,8 +24,7 @@ import { CairoResultVariant, } from './enum'; import extractTupleMemberTypes from './tuple'; - -import { isString } from '../shortString'; +import { isUndefined, isString } from '../typed'; function errorU256(key: string) { return Error( @@ -184,7 +183,7 @@ export default function orderPropsByAbi( const unorderedCustomEnum = unorderedObject2 as CairoCustomEnum; const variants = Object.entries(unorderedCustomEnum.variant); const newEntries = variants.map((variant) => { - if (typeof variant[1] === 'undefined') { + if (isUndefined(variant[1])) { return variant; } const variantType: string = abiObject.type.substring( diff --git a/src/utils/calldata/requestParser.ts b/src/utils/calldata/requestParser.ts index eb6e3ace5..096fb8be1 100644 --- a/src/utils/calldata/requestParser.ts +++ b/src/utils/calldata/requestParser.ts @@ -13,7 +13,8 @@ import { CairoUint256 } from '../cairoDataTypes/uint256'; import { CairoUint512 } from '../cairoDataTypes/uint512'; import { addHexPrefix, removeHexPrefix } from '../encode'; import { toHex } from '../num'; -import { encodeShortString, isString, isText, splitLongString } from '../shortString'; +import { encodeShortString, isText, splitLongString } from '../shortString'; +import { isUndefined, isString } from '../typed'; import { byteArrayFromString } from './byteArray'; import { felt, @@ -81,7 +82,7 @@ function parseTuple(element: object, typeStr: string): Tupled[] { if (elements.length !== memberTypes.length) { throw Error( `ParseTuple: provided and expected abi tuple size do not match. - provided: ${elements} + provided: ${elements} expected: ${memberTypes}` ); } @@ -185,7 +186,7 @@ function parseCalldataValue( const myOption = element as CairoOption; if (myOption.isSome()) { const listTypeVariant = variants.find((variant) => variant.name === 'Some'); - if (typeof listTypeVariant === 'undefined') { + if (isUndefined(listTypeVariant)) { throw Error(`Error in abi : Option has no 'Some' variant.`); } const typeVariantSome = listTypeVariant.type; @@ -210,7 +211,7 @@ function parseCalldataValue( const myResult = element as CairoResult; if (myResult.isOk()) { const listTypeVariant = variants.find((variant) => variant.name === 'Ok'); - if (typeof listTypeVariant === 'undefined') { + if (isUndefined(listTypeVariant)) { throw Error(`Error in abi : Result has no 'Ok' variant.`); } const typeVariantOk = listTypeVariant.type; @@ -230,7 +231,7 @@ function parseCalldataValue( } // is Result::Err const listTypeVariant = variants.find((variant) => variant.name === 'Err'); - if (typeof listTypeVariant === 'undefined') { + if (isUndefined(listTypeVariant)) { throw Error(`Error in abi : Result has no 'Err' variant.`); } const typeVariantErr = listTypeVariant.type; @@ -247,7 +248,7 @@ function parseCalldataValue( const myEnum = element as CairoCustomEnum; const activeVariant: string = myEnum.activeVariant(); const listTypeVariant = variants.find((variant) => variant.name === activeVariant); - if (typeof listTypeVariant === 'undefined') { + if (isUndefined(listTypeVariant)) { throw Error(`Not find in abi : Enum has no '${activeVariant}' variant.`); } const typeActiveVariant = listTypeVariant.type; diff --git a/src/utils/calldata/validate.ts b/src/utils/calldata/validate.ts index 129d71ed1..45339d753 100644 --- a/src/utils/calldata/validate.ts +++ b/src/utils/calldata/validate.ts @@ -14,8 +14,9 @@ import { import assert from '../assert'; import { CairoUint256 } from '../cairoDataTypes/uint256'; import { CairoUint512 } from '../cairoDataTypes/uint512'; -import { isBigInt, isBoolean, isHex, isNumber, toBigInt } from '../num'; -import { isLongText, isString } from '../shortString'; +import { isHex, toBigInt } from '../num'; +import { isLongText } from '../shortString'; +import { isBoolean, isNumber, isString, isBigInt } from '../typed'; import { getArrayType, isLen, diff --git a/src/utils/contract.ts b/src/utils/contract.ts index b93c8c230..0ec3adf7c 100644 --- a/src/utils/contract.ts +++ b/src/utils/contract.ts @@ -10,8 +10,7 @@ import { CompleteDeclareContractPayload, DeclareContractPayload } from '../types import { computeCompiledClassHash, computeContractClassHash } from './hash'; import { parse } from './json'; import { decompressProgram } from './stark'; - -import { isString } from './shortString'; +import { isString } from './typed'; /** * Checks if a given contract is in Sierra (Safe Intermediate Representation) format. diff --git a/src/utils/encode.ts b/src/utils/encode.ts index 6b9878a40..081712114 100644 --- a/src/utils/encode.ts +++ b/src/utils/encode.ts @@ -1,6 +1,5 @@ import { base64 } from '@scure/base'; -/* eslint-disable no-param-reassign */ export const IS_BROWSER = typeof window !== 'undefined'; const STRING_ZERO = '0'; @@ -106,7 +105,7 @@ export function btoaUniversal(b: ArrayBuffer): string { * // result = "48656c6c6f" * ``` */ -export function buf2hex(buffer: Uint8Array) { +export function buf2hex(buffer: Uint8Array): string { return buffer.reduce((r, x) => r + x.toString(16).padStart(2, '0'), ''); } @@ -163,7 +162,12 @@ export function addHexPrefix(hex: string): string { * // result = '00000hello' * ``` */ -function padString(str: string, length: number, left: boolean, padding = STRING_ZERO): string { +function padString( + str: string, + length: number, + left: boolean, + padding: string = STRING_ZERO +): string { const diff = length - str.length; let result = str; if (diff > 0) { @@ -183,7 +187,6 @@ function padString(str: string, length: number, left: boolean, padding = STRING_ * @param {number} length The target length for the padded string. * @param {string} [padding='0'] The string to use for padding. Defaults to '0'. * @returns {string} The padded string. - * * @example * ```typescript * const myString = '1A3F'; @@ -191,7 +194,7 @@ function padString(str: string, length: number, left: boolean, padding = STRING_ * // result: '0000001A3F' * ``` */ -export function padLeft(str: string, length: number, padding = STRING_ZERO): string { +export function padLeft(str: string, length: number, padding: string = STRING_ZERO): string { return padString(str, length, true, padding); } @@ -215,7 +218,7 @@ export function padLeft(str: string, length: number, padding = STRING_ZERO): str * * ``` */ -export function calcByteLength(str: string, byteSize = 8): number { +export function calcByteLength(str: string, byteSize: number = 8): number { const { length } = str; const remainder = length % byteSize; return remainder ? ((length - remainder) / byteSize) * byteSize + byteSize : length; @@ -242,7 +245,11 @@ export function calcByteLength(str: string, byteSize = 8): number { * // result: '00000123' (padded to 8 characters) * ``` */ -export function sanitizeBytes(str: string, byteSize = 8, padding = STRING_ZERO): string { +export function sanitizeBytes( + str: string, + byteSize: number = 8, + padding: string = STRING_ZERO +): string { return padLeft(str, calcByteLength(str, byteSize), padding); } @@ -251,8 +258,8 @@ export function sanitizeBytes(str: string, byteSize = 8, padding = STRING_ZERO): * and then re-adding the '0x' prefix. * * *[no internal usage]* - * @param hex hex-string - * @returns format: hex-string + * @param {string} hex hex-string + * @returns {string} format: hex-string * * @example * ```typescript @@ -262,12 +269,9 @@ export function sanitizeBytes(str: string, byteSize = 8, padding = STRING_ZERO): * ``` */ export function sanitizeHex(hex: string): string { - hex = removeHexPrefix(hex); - hex = sanitizeBytes(hex, 2); - if (hex) { - hex = addHexPrefix(hex); - } - return hex; + const hexWithoutPrefix = removeHexPrefix(hex); + const sanitizedHex = sanitizeBytes(hexWithoutPrefix, 2); + return sanitizedHex ? addHexPrefix(sanitizedHex) : sanitizedHex; } /** @@ -285,7 +289,7 @@ export function sanitizeHex(hex: string): string { * // result: 'PASCAL_CASE_EXAMPLE' * ``` */ -export const pascalToSnake = (text: string) => +export const pascalToSnake = (text: string): string => /[a-z]/.test(text) ? text .split(/(?=[A-Z])/) diff --git a/src/utils/events/index.ts b/src/utils/events/index.ts index 07ef25a44..c6dec7620 100644 --- a/src/utils/events/index.ts +++ b/src/utils/events/index.ts @@ -14,6 +14,7 @@ import { type CairoEventVariant, type InvokeTransactionReceiptResponse, type AbiEntry, + DeployContractUDCResponse, } from '../../types'; import assert from '../assert'; import { isCairo1Abi } from '../calldata/cairo'; @@ -21,6 +22,7 @@ import responseParser from '../calldata/responseParser'; import { starkCurve } from '../ec'; import { addHexPrefix, utf8ToArray } from '../encode'; import { cleanHex } from '../num'; +import { isUndefined, isObject } from '../typed'; /** * Check if an ABI entry is related to events. @@ -51,7 +53,7 @@ export function isAbiEvent(object: AbiEntry): boolean { } * ``` */ -function getCairo0AbiEvents(abi: Abi) { +function getCairo0AbiEvents(abi: Abi): AbiEvents { return abi .filter((abiEntry) => abiEntry.type === 'event') .reduce((acc, abiEntry) => { @@ -75,11 +77,10 @@ function getCairo0AbiEvents(abi: Abi) { * ```typescript * const result = events.getCairo1AbiEvents(abi1); * // result = { - * // '0x22ea134d4126804c60797e633195f8c9aa5fd6d1567e299f4961d0e96f373ee': + * // '0x22ea134d4126804c60797e633195f8c9aa5fd6d1567e299f4961d0e96f373ee': * // { '0x34e55c1cd55f1338241b50d352f0e91c7e4ffad0e4271d64eb347589ebdfd16': { * // kind: 'struct', type: 'event', * // name: 'ka::ExComponent::ex_logic_component::Mint', - * // members: [{ * // name: 'spender', * // type: 'core::starknet::contract_address::ContractAddress', @@ -88,7 +89,7 @@ function getCairo0AbiEvents(abi: Abi) { * // ... * ``` */ -function getCairo1AbiEvents(abi: Abi) { +function getCairo1AbiEvents(abi: Abi): AbiEvents { const abiEventsStructs = abi.filter((obj) => isAbiEvent(obj) && obj.kind === 'struct'); const abiEventsEnums = abi.filter((obj) => isAbiEvent(obj) && obj.kind === 'enum'); const abiEventsData: AbiEvents = abiEventsStructs.reduce((acc: CairoEvent, event: CairoEvent) => { @@ -99,20 +100,24 @@ function getCairo1AbiEvents(abi: Abi) { // eslint-disable-next-line no-constant-condition while (true) { const eventEnum = abiEventsEnums.find((eventE) => eventE.variants.some(findName)); - if (typeof eventEnum === 'undefined') break; + if (isUndefined(eventEnum)) break; const variant = eventEnum.variants.find(findName); nameList.unshift(variant.name); if (variant.kind === 'flat') flat = true; name = eventEnum.name; } + if (nameList.length === 0) { throw new Error('inconsistency in ABI events definition.'); } + if (flat) nameList = [nameList[nameList.length - 1]]; + const final = nameList.pop(); let result: AbiEvents = { [addHexPrefix(starkCurve.keccak(utf8ToArray(final!)).toString(16))]: event, }; + while (nameList.length > 0) { result = { [addHexPrefix(starkCurve.keccak(utf8ToArray(nameList.pop()!)).toString(16))]: result, @@ -134,11 +139,10 @@ function getCairo1AbiEvents(abi: Abi) { * ```typescript * const result = events.getAbiEvents(abi); * // result = { - * // '0x22ea134d4126804c60797e633195f8c9aa5fd6d1567e299f4961d0e96f373ee': + * // '0x22ea134d4126804c60797e633195f8c9aa5fd6d1567e299f4961d0e96f373ee': * // { '0x34e55c1cd55f1338241b50d352f0e91c7e4ffad0e4271d64eb347589ebdfd16': { * // kind: 'struct', type: 'event', * // name: 'ka::ExComponent::ex_logic_component::Mint', - * // members: [{ * // name: 'spender', * // type: 'core::starknet::contract_address::ContractAddress', @@ -151,20 +155,6 @@ export function getAbiEvents(abi: Abi): AbiEvents { return isCairo1Abi(abi) ? getCairo1AbiEvents(abi) : getCairo0AbiEvents(abi); } -/** - * Checks if a given value is an object (Object or Array) - * @param {any} item the tested item - * @returns {boolean} - * @example - * ```typescript - * const result = events.isObject({event: "pending"}); - * // result = true - * ``` - */ -export function isObject(item: any): boolean { - return item && typeof item === 'object' && !Array.isArray(item); -} - /** * internal function to deep merge 2 event description objects */ @@ -262,9 +252,10 @@ export function parseEvents( * 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 - */ -export function parseUDCEvent(txReceipt: InvokeTransactionReceiptResponse) { + * @returns {DeployContractUDCResponse} */ +export function parseUDCEvent( + txReceipt: InvokeTransactionReceiptResponse +): DeployContractUDCResponse { if (!txReceipt.events) { throw new Error('UDC emitted event is empty'); } diff --git a/src/utils/fetchPonyfill.ts b/src/utils/fetchPonyfill.ts index 35db800e9..02888b3a6 100644 --- a/src/utils/fetchPonyfill.ts +++ b/src/utils/fetchPonyfill.ts @@ -2,7 +2,9 @@ // @ts-ignore import makeFetchCookie from 'fetch-cookie'; import isomorphicFetch from 'isomorphic-fetch'; +import { IS_BROWSER } from './encode'; +import { isUndefined } from './typed'; -export default (typeof window !== 'undefined' && window.fetch) || // use buildin fetch in browser if available - (typeof global !== 'undefined' && makeFetchCookie(global.fetch)) || // use buildin fetch in node, react-native and service worker if available +export default (IS_BROWSER && window.fetch) || // use built-in fetch in browser if available + (!isUndefined(global) && makeFetchCookie(global.fetch)) || // use built-in fetch in node, react-native and service worker if available isomorphicFetch; // ponyfill fetch in node and browsers that don't have it diff --git a/src/utils/hash/classHash.ts b/src/utils/hash/classHash.ts index 70aae9c56..44ce8749e 100644 --- a/src/utils/hash/classHash.ts +++ b/src/utils/hash/classHash.ts @@ -22,7 +22,8 @@ import { starkCurve } from '../ec'; import { addHexPrefix, utf8ToArray } from '../encode'; import { parse, stringify } from '../json'; import { toHex } from '../num'; -import { encodeShortString, isString } from '../shortString'; +import { encodeShortString } from '../shortString'; +import { isString } from '../typed'; export function computePedersenHash(a: BigNumberish, b: BigNumberish): string { return starkCurve.pedersen(BigInt(a), BigInt(b)); diff --git a/src/utils/hash/selector.ts b/src/utils/hash/selector.ts index 87d4dd918..b9fd1ce71 100644 --- a/src/utils/hash/selector.ts +++ b/src/utils/hash/selector.ts @@ -4,7 +4,8 @@ import { bytesToHex } from '@noble/curves/abstract/utils'; import { MASK_250 } from '../../constants'; import { BigNumberish } from '../../types'; import { addHexPrefix, removeHexPrefix, utf8ToArray } from '../encode'; -import { hexToBytes, isBigInt, isHex, isNumber, isStringWholeNumber, toHex } from '../num'; +import { hexToBytes, isHex, isStringWholeNumber, toHex } from '../num'; +import { isBigInt, isNumber } from '../typed'; /** * Calculate the hex-string Starknet Keccak hash for a given BigNumberish diff --git a/src/utils/num.ts b/src/utils/num.ts index d9a097f11..d5e3649c1 100644 --- a/src/utils/num.ts +++ b/src/utils/num.ts @@ -4,6 +4,7 @@ import { BigNumberish } from '../types'; import assert from './assert'; import { addHexPrefix, buf2hex, removeHexPrefix } from './encode'; import { MASK_31 } from '../constants'; +import { isNumber, isBigInt, isString } from './typed'; /** @deprecated prefer importing from 'types' over 'num' */ export type { BigNumberish }; @@ -44,24 +45,6 @@ export function toBigInt(value: BigNumberish): bigint { return BigInt(value); } -/** - * Test if value is bigint - * - * @param value value to test - * @returns {boolean} true if value is bigint, false otherwise - * @example - * ```typescript - * isBigInt(10n); // true - * isBigInt(BigInt('10')); // true - * isBigInt(10); // false - * isBigInt('10'); // false - * isBigInt(null); // false - * ``` - */ -export function isBigInt(value: any): value is bigint { - return typeof value === 'bigint'; -} - /** * Convert BigNumberish to hex-string * @@ -324,7 +307,7 @@ export function hexToBytes(str: string): Uint8Array { * * @param number value to be modified * @param percent integer as percent ex. 50 for 50% - * @returns {BigInt} modified value + * @returns {bigint} modified value * @example * ```typescript * addPercent(100, 50); // 150n @@ -335,49 +318,11 @@ export function hexToBytes(str: string): Uint8Array { * addPercent(200, -150); // -100n * ``` */ -export function addPercent(number: BigNumberish, percent: number) { +export function addPercent(number: BigNumberish, percent: number): bigint { const bigIntNum = BigInt(number); return bigIntNum + (bigIntNum * BigInt(percent)) / 100n; } -/** - * Check if a value is a number. - * - * @param {unknown} value - The value to check. - * @returns {boolean} Returns true if the value is a number, otherwise returns false. - * @example - * ```typescript - * const result = isNumber(123); - * // result = true - * - * const result2 = isNumber("123"); - * // result2 = false - * ``` - * @return {boolean} Returns true if the value is a number, otherwise returns false. - */ -export function isNumber(value: unknown): value is number { - return typeof value === 'number'; -} - -/** - * Checks if a given value is of boolean type. - * - * @param {unknown} value - The value to check. - * @returns {boolean} - True if the value is of boolean type, false otherwise. - * @example - * ```typescript - * const result = isBoolean(true); - * // result = true - * - * const result2 = isBoolean(false); - * // result2 = false - * ``` - * @return {boolean} - True if the value is of boolean type, false otherwise. - */ -export function isBoolean(value: unknown): value is boolean { - return typeof value === 'boolean'; -} - /** * Calculate the sha256 hash of an utf8 string, then encode the * result in an uint8Array of 4 elements. @@ -413,6 +358,6 @@ export function isBigNumberish(input: unknown): input is BigNumberish { return ( isNumber(input) || isBigInt(input) || - (typeof input === 'string' && (isHex(input) || isStringWholeNumber(input))) + (isString(input) && (isHex(input) || isStringWholeNumber(input))) ); } diff --git a/src/utils/provider.ts b/src/utils/provider.ts index 19630e4ef..5623bfc25 100644 --- a/src/utils/provider.ts +++ b/src/utils/provider.ts @@ -18,8 +18,9 @@ import { ETransactionVersion } from '../types/api'; import { isSierra } from './contract'; import { formatSpaces } from './hash'; import { parse, stringify } from './json'; -import { isBigInt, isHex, isNumber, toHex } from './num'; -import { isDecimalString, isString } from './shortString'; +import { isHex, toHex } from './num'; +import { isDecimalString } from './shortString'; +import { isBigInt, isNumber, isString } from './typed'; import { compressProgram } from './stark'; import type { GetTransactionReceiptResponse } from './transactionReceipt'; @@ -43,7 +44,7 @@ export function wait(delay: number): Promise { * Create Sierra compressed Contract Class from a given Compiled Sierra * * CompiledSierra -> SierraContractClass - * + * * @param {CompiledSierra} contract sierra code from the Cairo compiler * @returns {SierraContractClass} compressed Sierra * @example diff --git a/src/utils/responseParser/rpc.ts b/src/utils/responseParser/rpc.ts index d2f944d81..4b59d488c 100644 --- a/src/utils/responseParser/rpc.ts +++ b/src/utils/responseParser/rpc.ts @@ -17,7 +17,7 @@ import type { TransactionReceipt, } from '../../types/provider'; import { toBigInt } from '../num'; -import { isString } from '../shortString'; +import { isString } from '../typed'; import { estimateFeeToBounds, estimatedFeeToMaxFee } from '../stark'; import { ResponseParser } from './interface'; diff --git a/src/utils/shortString.ts b/src/utils/shortString.ts index 5710711f1..d37e3cf0d 100644 --- a/src/utils/shortString.ts +++ b/src/utils/shortString.ts @@ -1,6 +1,7 @@ import { TEXT_TO_FELT_MAX_LEN } from '../constants'; import { addHexPrefix, removeHexPrefix } from './encode'; import { isHex, isStringWholeNumber } from './num'; +import { isString } from './typed'; /** * Test if string contains only ASCII characters (string can be ascii text) @@ -49,20 +50,6 @@ export function isDecimalString(str: string): boolean { return /^[0-9]*$/i.test(str); } -/** - * Checks if a given value is a string. - * @param {unknown} value the value to be checked. - * @return {boolean} returns true if the value is a string, false otherwise. - * @example - * ```typescript - * const result = shortString.isString("12345"); - * // result = true - * ``` - */ -export function isString(value: unknown): value is string { - return typeof value === 'string'; -} - /** * Test if value is a pure string text, and not a hex string or number string * @param {any} val the value to test @@ -75,7 +62,7 @@ export function isString(value: unknown): value is string { * // result = false * ``` */ -export function isText(val: any) { +export function isText(val: any): boolean { return isString(val) && !isHex(val) && !isStringWholeNumber(val); } @@ -89,7 +76,7 @@ export function isText(val: any) { * // result = true * ``` */ -export const isShortText = (val: any) => isText(val) && isShortString(val); +export const isShortText = (val: any): boolean => isText(val) && isShortString(val); /** * Test if value is long text @@ -101,7 +88,7 @@ export const isShortText = (val: any) => isText(val) && isShortString(val); * // result = true * ``` */ -export const isLongText = (val: any) => isText(val) && !isShortString(val); +export const isLongText = (val: any): boolean => isText(val) && !isShortString(val); /** * Split long text (string greater than 31 characters) into short strings (string lesser or equal 31 characters) diff --git a/src/utils/stark.ts b/src/utils/stark.ts index 4add7ae6a..25f13254d 100644 --- a/src/utils/stark.ts +++ b/src/utils/stark.ts @@ -1,5 +1,3 @@ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -import type { SPEC } from 'starknet-types-07'; import { getPublicKey, getStarkKey, utils } from '@scure/starknet'; import { gzip, ungzip } from 'pako'; @@ -20,10 +18,21 @@ import { addPercent, bigNumberishArrayToDecimalStringArray, bigNumberishArrayToHexadecimalStringArray, - isBigInt, toHex, } from './num'; -import { isString } from './shortString'; +import { isUndefined, isString, isBigInt } from './typed'; + +type V3Details = Required< + Pick< + UniversalDetails, + | 'tip' + | 'paymasterData' + | 'accountDeploymentData' + | 'nonceDataAvailabilityMode' + | 'feeDataAvailabilityMode' + | 'resourceBounds' + > +>; /** * Compress compiled Cairo 0 program @@ -46,8 +55,8 @@ export function compressProgram(jsonProgram: Program | string): CompressedProgra /** * Decompress compressed compiled Cairo 0 program - * @param {CompressedProgram} base64 Compressed Cairo 0 program - * @returns {Object | CompressedProgram} Parsed decompressed compiled Cairo 0 program + * @param {CompressedProgram | CompressedProgram[]} base64 Compressed Cairo 0 program + * @returns Parsed decompressed compiled Cairo 0 program * @example * ```typescript * const contractCairo0 = json.parse(fs.readFileSync("./cairo0contract.json").toString("ascii")); @@ -72,7 +81,7 @@ export function compressProgram(jsonProgram: Program | string): CompressedProgra * // ... * ``` */ -export function decompressProgram(base64: CompressedProgram) { +export function decompressProgram(base64: CompressedProgram | CompressedProgram[]) { if (Array.isArray(base64)) return base64; const decompressed = arrayBufferToString(ungzip(atobUniversal(base64))); return parse(decompressed); @@ -214,7 +223,7 @@ export function estimateFeeToBounds( }; } - if (typeof estimate.gas_consumed === 'undefined' || typeof estimate.gas_price === 'undefined') { + if (isUndefined(estimate.gas_consumed) || isUndefined(estimate.gas_price)) { throw Error('estimateFeeToBounds: estimate is undefined'); } @@ -280,7 +289,7 @@ export function toTransactionVersion( /** * Convert Transaction version to Fee version or throw an error * @param {BigNumberish} [providedVersion] 0..3 number representing the transaction version - * @returns {ETransactionVersion} the fee estimation version corresponding to the transaction version provided + * @returns {ETransactionVersion | undefined} the fee estimation version corresponding to the transaction version provided * @throws {Error} if the transaction version is unknown * @example * ```typescript @@ -288,7 +297,7 @@ export function toTransactionVersion( * // result = "0x100000000000000000000000000000002" * ``` */ -export function toFeeVersion(providedVersion?: BigNumberish) { +export function toFeeVersion(providedVersion?: BigNumberish): ETransactionVersion | undefined { if (!providedVersion) return undefined; const version = toHex(providedVersion); @@ -303,7 +312,7 @@ export function toFeeVersion(providedVersion?: BigNumberish) { /** * Return provided or default v3 tx details * @param {UniversalDetails} details details of the transaction - * @return {} an object including the V3 transaction details. + * @return {V3Details} an object including the V3 transaction details. * @example * ```typescript * const detail: UniversalDetails = { tip: 3456n }; @@ -321,7 +330,8 @@ export function toFeeVersion(providedVersion?: BigNumberish) { * // } * ``` */ -export function v3Details(details: UniversalDetails) { + +export function v3Details(details: UniversalDetails): V3Details { return { tip: details.tip || 0, paymasterData: details.paymasterData || [], diff --git a/src/utils/typed.ts b/src/utils/typed.ts new file mode 100644 index 000000000..4498cdde5 --- /dev/null +++ b/src/utils/typed.ts @@ -0,0 +1,102 @@ +/** + * Check if a value is a undefined. + * + * @param {unknown} value - The value to check. + * @returns {boolean} Returns true if the value is a undefined, otherwise returns false. + * @example + * ```typescript + * const result = isUndefined(undefined); + * // result = true + * + * const result2 = isUndefined('existing value'); + * // result2 = false + * ``` + * @return {boolean} Returns true if the value is undefined, otherwise returns false. + */ +export const isUndefined = (value: unknown): value is undefined => { + return typeof value === 'undefined' || value === undefined; +}; + +/** + * Check if a value is a number. + * + * @param {unknown} value - The value to check. + * @returns {boolean} Returns true if the value is a number, otherwise returns false. + * @example + * ```typescript + * const result = isNumber(123); + * // result = true + * + * const result2 = isNumber("123"); + * // result2 = false + * ``` + * @return {boolean} Returns true if the value is a number, otherwise returns false. + */ +export function isNumber(value: unknown): value is number { + return typeof value === 'number'; +} + +/** + * Checks if a given value is of boolean type. + * + * @param {unknown} value - The value to check. + * @returns {boolean} - True if the value is of boolean type, false otherwise. + * @example + * ```typescript + * const result = isBoolean(true); + * // result = true + * + * const result2 = isBoolean(false); + * // result2 = false + * ``` + * @return {boolean} - True if the value is of boolean type, false otherwise. + */ +export function isBoolean(value: unknown): value is boolean { + return typeof value === 'boolean'; +} + +/** + * Test if value is bigint + * + * @param value value to test + * @returns {boolean} true if value is bigint, false otherwise + * @example + * ```typescript + * isBigInt(10n); // true + * isBigInt(BigInt('10')); // true + * isBigInt(10); // false + * isBigInt('10'); // false + * isBigInt(null); // false + * ``` + */ +export function isBigInt(value: any): value is bigint { + return typeof value === 'bigint'; +} + +/** + * Checks if a given value is a string. + * @param {unknown} value the value to be checked. + * @return {boolean} returns true if the value is a string, false otherwise. + * @example + * ```typescript + * const result = shortString.isString("12345"); + * // result = true + * ``` + */ +export function isString(value: unknown): value is string { + return typeof value === 'string'; +} + +/** + * Checks if a given value is an object (Object or Array) + * @param {unknown} item the tested item + * @returns {boolean} + * @example + * ```typescript + * const result = events.isObject({event: "pending"}); + * // result = true + * ``` + */ +export function isObject(item: unknown | undefined): boolean { + return !!item && typeof item === 'object' && !Array.isArray(item); +} diff --git a/src/utils/typedData.ts b/src/utils/typedData.ts index 07c1e19f8..c83f05aed 100644 --- a/src/utils/typedData.ts +++ b/src/utils/typedData.ts @@ -21,7 +21,8 @@ import { } from './hash'; import { MerkleTree } from './merkle'; import { isBigNumberish, isHex, toHex } from './num'; -import { encodeShortString, isString } from './shortString'; +import { encodeShortString } from './shortString'; +import { isString } from './typed'; /** @deprecated prefer importing from 'types' over 'typedData' */ export * from '../types/typedData'; @@ -464,7 +465,7 @@ export function encodeData( type: string, data: T['message'], revision: Revision = Revision.LEGACY -) { +): [string[], string[]] { const targetType = types[type] ?? revisionConfiguration[revision].presetTypes[type]; const [returnTypes, values] = targetType.reduce<[string[], string[]]>( ([ts, vs], field) => { @@ -519,7 +520,7 @@ export function getStructHash( type: string, data: T['message'], revision: Revision = Revision.LEGACY -) { +): string { return revisionConfiguration[revision].hashMethod(encodeData(types, type, data, revision)[1]); } diff --git a/src/utils/url.ts b/src/utils/url.ts deleted file mode 100644 index a5d934e10..000000000 --- a/src/utils/url.ts +++ /dev/null @@ -1,79 +0,0 @@ -import urljoin from 'url-join'; - -/** - * Inspired from https://github.com/segmentio/is-url - */ - -/** - * RegExps. - * A URL must match #1 and then at least one of #2/#3. - * Use two levels of REs to avoid REDOS. - */ -const protocolAndDomainRE = /^(?:\w+:)?\/\/(\S+)$/; - -const localhostDomainRE = /^localhost[:?\d]*(?:[^:?\d]\S*)?$/; -const nonLocalhostDomainRE = /^[^\s.]+\.\S{2,}$/; - -/** - * @deprecated - * - * Loosely validate a URL `string`. - * - * @param {string} s - The URL to check for - * @return {boolean} `true` if url is valid, `false` otherwise - * @example - * ```typescript - * const s = "https://starknetjs.com/docs"; - * const result = isUrl(s); - * // result == true - */ -export function isUrl(s?: string): boolean { - if (!s) { - return false; - } - - if (typeof s !== 'string') { - return false; - } - - const match = s.match(protocolAndDomainRE); - if (!match) { - return false; - } - - const everythingAfterProtocol = match[1]; - if (!everythingAfterProtocol) { - return false; - } - - if ( - localhostDomainRE.test(everythingAfterProtocol) || - nonLocalhostDomainRE.test(everythingAfterProtocol) - ) { - return true; - } - - return false; -} - -/** - * @deprecated - * - * Builds a URL using the provided base URL, default path, and optional URL or path. - * - * @param {string} baseUrl - The base URL of the URL being built. - * @param {string} defaultPath - The default path to use if no URL or path is provided. - * @param {string} [urlOrPath] - The optional URL or path to append to the base URL. - * @return {string} The built URL. - * @example - * ```typescript - * const baseUrl = "https://starknetjs.com"; - * const defaultPath = "/"; - * const urlOrPath = "/docs"; - * const result = buildUrl(baseUrl, defaultPath, urlOrPath); - * - * result = "https://starknetjs.com/docs" - */ -export function buildUrl(baseUrl: string, defaultPath: string, urlOrPath?: string) { - return isUrl(urlOrPath) ? urlOrPath! : urljoin(baseUrl, urlOrPath ?? defaultPath); -} diff --git a/src/wallet/account.ts b/src/wallet/account.ts index 874bab45b..8a1bccffc 100644 --- a/src/wallet/account.ts +++ b/src/wallet/account.ts @@ -1,12 +1,10 @@ -import { - type AccountChangeEventHandler, - type AddStarknetChainParameters, - type NetworkChangeEventHandler, - type WatchAssetParameters, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - type SPEC, +import type { + Signature, + AccountChangeEventHandler, + AddStarknetChainParameters, + NetworkChangeEventHandler, + WatchAssetParameters, } from 'starknet-types-07'; - import { Account, AccountInterface } from '../account'; import { ProviderInterface } from '../provider'; import { @@ -82,11 +80,11 @@ export class WalletAccount extends Account implements AccountInterface { /** * WALLET EVENTS */ - public onAccountChange(callback: AccountChangeEventHandler) { + public onAccountChange(callback: AccountChangeEventHandler): void { onAccountChange(this.walletProvider, callback); } - public onNetworkChanged(callback: NetworkChangeEventHandler) { + public onNetworkChanged(callback: NetworkChangeEventHandler): void { onNetworkChanged(this.walletProvider, callback); } @@ -168,7 +166,7 @@ export class WalletAccount extends Account implements AccountInterface { }; } - override signMessage(typedData: TypedData) { + override signMessage(typedData: TypedData): Promise { return signMessage(this.walletProvider, typedData); } diff --git a/src/wallet/connect.ts b/src/wallet/connect.ts index 4a57d1181..7ec623534 100644 --- a/src/wallet/connect.ts +++ b/src/wallet/connect.ts @@ -8,8 +8,13 @@ import { type ChainId, type StarknetWindowObject, type TypedData, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - type SPEC, + type Permission, + type Address, + AddInvokeTransactionResult, + AddDeclareTransactionResult, + AccountDeploymentData, + Signature, + SpecVersion, } from 'starknet-types-07'; /** @@ -17,12 +22,13 @@ import { * @param {boolean} [silent_mode=false] false: request user interaction allowance. true: return only pre-allowed * @returns allowed accounts addresses */ -export function requestAccounts(swo: StarknetWindowObject, silent_mode = false) { +export function requestAccounts( + swo: StarknetWindowObject, + silent_mode: boolean = false +): Promise { return swo.request({ type: 'wallet_requestAccounts', - params: { - silent_mode, - }, + params: { silent_mode }, }); } @@ -30,7 +36,7 @@ export function requestAccounts(swo: StarknetWindowObject, silent_mode = false) * Request Permission for wallet account * @returns allowed accounts addresses */ -export function getPermissions(swo: StarknetWindowObject) { +export function getPermissions(swo: StarknetWindowObject): Promise { return swo.request({ type: 'wallet_getPermissions' }); } @@ -39,11 +45,11 @@ export function getPermissions(swo: StarknetWindowObject) { * @param asset WatchAssetParameters * @returns boolean */ -export function watchAsset(swo: StarknetWindowObject, asset: WatchAssetParameters) { - return swo.request({ - type: 'wallet_watchAsset', - params: asset, - }); +export function watchAsset( + swo: StarknetWindowObject, + asset: WatchAssetParameters +): Promise { + return swo.request({ type: 'wallet_watchAsset', params: asset }); } /** @@ -51,12 +57,12 @@ export function watchAsset(swo: StarknetWindowObject, asset: WatchAssetParameter * @param chain AddStarknetChainParameters * @returns boolean */ -export function addStarknetChain(swo: StarknetWindowObject, chain: AddStarknetChainParameters) { +export function addStarknetChain( + swo: StarknetWindowObject, + chain: AddStarknetChainParameters +): Promise { // TODO: This should set custom RPC endpoint ? - return swo.request({ - type: 'wallet_addStarknetChain', - params: chain, - }); + return swo.request({ type: 'wallet_addStarknetChain', params: chain }); } /** @@ -64,12 +70,10 @@ export function addStarknetChain(swo: StarknetWindowObject, chain: AddStarknetCh * @param chainId StarknetChainId * @returns boolean */ -export function switchStarknetChain(swo: StarknetWindowObject, chainId: ChainId) { +export function switchStarknetChain(swo: StarknetWindowObject, chainId: ChainId): Promise { return swo.request({ type: 'wallet_switchStarknetChain', - params: { - chainId, - }, + params: { chainId }, }); } @@ -77,7 +81,7 @@ export function switchStarknetChain(swo: StarknetWindowObject, chainId: ChainId) * Request the current chain ID from the wallet. * @returns The current Starknet chain ID. */ -export function requestChainId(swo: StarknetWindowObject) { +export function requestChainId(swo: StarknetWindowObject): Promise { return swo.request({ type: 'wallet_requestChainId' }); } @@ -85,7 +89,7 @@ export function requestChainId(swo: StarknetWindowObject) { * Get deployment data for a contract. * @returns The deployment data result. */ -export function deploymentData(swo: StarknetWindowObject) { +export function deploymentData(swo: StarknetWindowObject): Promise { return swo.request({ type: 'wallet_deploymentData' }); // TODO: test } @@ -97,11 +101,8 @@ export function deploymentData(swo: StarknetWindowObject) { export function addInvokeTransaction( swo: StarknetWindowObject, params: AddInvokeTransactionParameters -) { - return swo.request({ - type: 'wallet_addInvokeTransaction', - params, - }); +): Promise { + return swo.request({ type: 'wallet_addInvokeTransaction', params }); } /** @@ -112,11 +113,8 @@ export function addInvokeTransaction( export function addDeclareTransaction( swo: StarknetWindowObject, params: AddDeclareTransactionParameters -) { - return swo.request({ - type: 'wallet_addDeclareTransaction', - params, - }); +): Promise { + return swo.request({ type: 'wallet_addDeclareTransaction', params }); } /** @@ -125,18 +123,15 @@ export function addDeclareTransaction( * @param typedData The typed data to sign. * @returns An array of signatures as strings. */ -export function signMessage(swo: StarknetWindowObject, typedData: TypedData) { - return swo.request({ - type: 'wallet_signTypedData', - params: typedData, - }); +export function signMessage(swo: StarknetWindowObject, typedData: TypedData): Promise { + return swo.request({ type: 'wallet_signTypedData', params: typedData }); } /** * Get the list of supported specifications. * @returns An array of supported specification strings. */ -export function supportedSpecs(swo: StarknetWindowObject) { +export function supportedSpecs(swo: StarknetWindowObject): Promise { return swo.request({ type: 'wallet_supportedSpecs' }); }