Skip to content

Commit

Permalink
chore: refactor various typeof comparisons with util methods (#1007)
Browse files Browse the repository at this point in the history
* chore: refactor various typeof comparisons with util methods

* chore: removed docker compose, replaced more occurences of typeOf in project, docker compose file will be added in separate PR
  • Loading branch information
ikrcatov committed Mar 20, 2024
1 parent 70e7ba6 commit 6235080
Show file tree
Hide file tree
Showing 16 changed files with 108 additions and 65 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
.env
.eslintcache
.vscode
.idea
coverage
dist
node_modules
Expand Down
4 changes: 2 additions & 2 deletions __tests__/cairo1v2_typed.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
types,
} from '../src';
import { hexToDecimalString } from '../src/utils/num';
import { encodeShortString } from '../src/utils/shortString';
import { encodeShortString, isString } from '../src/utils/shortString';
import {
TEST_TX_VERSION,
compiledC1Account,
Expand Down Expand Up @@ -337,7 +337,7 @@ describe('Cairo 1', () => {
const status = await cairo1Contract.echo_struct({
val: 'simple',
});
if (typeof status.val === 'string') {
if (isString(status.val)) {
expect(shortString.decodeShortString(status.val)).toBe('simple');
}
});
Expand Down
3 changes: 2 additions & 1 deletion __tests__/config/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import libSchemas from '../schemas/lib.json';
import providerSchemas from '../schemas/provider.json';
import rpcSchemas from '../schemas/rpc.json';
import sequencerSchemas from '../schemas/sequencer.json';
import { isBigInt } from '../../src/utils/num';

const matcherSchemas = [accountSchemas, libSchemas, providerSchemas, rpcSchemas, sequencerSchemas];
const schemas = [...matcherSchemas, componentSchemas];
Expand All @@ -16,7 +17,7 @@ const jestJsonMatchers = matchersWithOptions({ schemas }, (ajv: any) => {
keyword: 'isBigInt',
type: 'object',
validate: (_schema: any, data: any) => {
return typeof data === 'bigint' && data < 2n ** 64n && data >= 0n;
return isBigInt(data) && data < 2n ** 64n && data >= 0n;
},
errors: true,
});
Expand Down
3 changes: 2 additions & 1 deletion src/account/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import {
import { buildUDCCall, getExecuteCalldata } from '../utils/transaction';
import { getMessageHash } from '../utils/typedData';
import { AccountInterface } from './interface';
import { isString } from '../utils/shortString';

export class Account extends Provider implements AccountInterface {
public signer: SignerInterface;
Expand All @@ -76,7 +77,7 @@ export class Account extends Provider implements AccountInterface {
super(providerOrOptions);
this.address = address.toLowerCase();
this.signer =
typeof pkOrSigner === 'string' || pkOrSigner instanceof Uint8Array
isString(pkOrSigner) || pkOrSigner instanceof Uint8Array
? new Signer(pkOrSigner)
: pkOrSigner;

Expand Down
46 changes: 25 additions & 21 deletions src/utils/cairoDataTypes/felt.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,41 @@
// TODO Convert to CairoFelt base on CairoUint256 and implement it in the codebase in the backward compatible manner

import { BigNumberish, isBigInt, isHex, isStringWholeNumber } from '../num';
import { encodeShortString, isShortString, isText } from '../shortString';
import { BigNumberish, isBigInt, isBoolean, isHex, isStringWholeNumber } from '../num';
import { encodeShortString, isShortString, isString, isText } from '../shortString';

/**
* Create felt Cairo type (cairo type helper)
* @returns format: felt-string
*/
export function CairoFelt(it: BigNumberish): string {
// BN or number
if (isBigInt(it) || (typeof it === 'number' && Number.isInteger(it))) {
if (isBigInt(it) || Number.isInteger(it)) {
return it.toString();
}
// string text
if (isText(it)) {
if (!isShortString(it as string))
throw new Error(
`${it} is a long string > 31 chars, felt can store short strings, split it to array of short strings`
);
const encoded = encodeShortString(it as string);
return BigInt(encoded).toString();
}
// hex string
if (typeof it === 'string' && isHex(it)) {
// toBN().toString
return BigInt(it).toString();
}
// string number (already converted), or unhandled type
if (typeof it === 'string' && isStringWholeNumber(it)) {
return it;

// Handling strings
if (isString(it)) {
// Hex strings
if (isHex(it)) {
return BigInt(it).toString();
}
// Text strings that must be short
if (isText(it)) {
if (!isShortString(it)) {
throw new Error(
`${it} is a long string > 31 chars. Please split it into an array of short strings.`
);
}
// Assuming encodeShortString returns a hex representation of the string
return BigInt(encodeShortString(it)).toString();
}
// Whole numeric strings
if (isStringWholeNumber(it)) {
return it;
}
}
// bool to felt
if (typeof it === 'boolean') {
if (isBoolean(it)) {
return `${+it}`;
}

Expand Down
4 changes: 3 additions & 1 deletion src/utils/calldata/propertyOrder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import {
} from './enum';
import extractTupleMemberTypes from './tuple';

import { isString } from '../shortString';

function errorU256(key: string) {
return Error(
`Your object includes the property : ${key}, containing an Uint256 object without the 'low' and 'high' keys.`
Expand Down Expand Up @@ -92,7 +94,7 @@ export default function orderPropsByAbi(

function orderArray(myArray: Array<any> | string, abiParam: string): Array<any> | string {
const typeInArray = getArrayType(abiParam);
if (typeof myArray === 'string') {
if (isString(myArray)) {
return myArray; // longstring
}
return myArray.map((myElem) => orderInput(myElem, typeInArray));
Expand Down
4 changes: 2 additions & 2 deletions src/utils/calldata/requestParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
Tupled,
} from '../../types';
import { CairoUint256 } from '../cairoDataTypes/uint256';
import { encodeShortString, isText, splitLongString } from '../shortString';
import { encodeShortString, isString, isText, splitLongString } from '../shortString';
import { byteArrayFromString } from './byteArray';
import {
felt,
Expand Down Expand Up @@ -266,7 +266,7 @@ export function parseCalldataField(
if (!Array.isArray(value) && !isText(value)) {
throw Error(`ABI expected parameter ${name} to be array or long string, got ${value}`);
}
if (typeof value === 'string') {
if (isString(value)) {
// long string match cairo felt*
value = splitLongString(value);
}
Expand Down
22 changes: 11 additions & 11 deletions src/utils/calldata/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import {
} from '../../types';
import assert from '../assert';
import { CairoUint256 } from '../cairoDataTypes/uint256';
import { isHex, toBigInt } from '../num';
import { isLongText } from '../shortString';
import { isBigInt, isBoolean, isHex, isNumber, toBigInt } from '../num';
import { isLongText, isString } from '../shortString';
import {
getArrayType,
isLen,
Expand All @@ -34,10 +34,10 @@ import {

const validateFelt = (parameter: any, input: AbiEntry) => {
assert(
typeof parameter === 'string' || typeof parameter === 'number' || typeof parameter === 'bigint',
isString(parameter) || isNumber(parameter) || isBigInt(parameter),
`Validate: arg ${input.name} should be a felt typed as (String, Number or BigInt)`
);
if (typeof parameter === 'string' && !isHex(parameter)) return; // shortstring
if (isString(parameter) && !isHex(parameter)) return; // shortstring
const param = BigInt(parameter.toString(10));
assert(
// from : https://github.com/starkware-libs/starknet-specs/blob/29bab650be6b1847c92d4461d4c33008b5e50b1a/api/starknet_api_openrpc.json#L1266
Expand All @@ -47,28 +47,28 @@ const validateFelt = (parameter: any, input: AbiEntry) => {
};

const validateBytes31 = (parameter: any, input: AbiEntry) => {
assert(typeof parameter === 'string', `Validate: arg ${input.name} should be a string.`);
assert(isString(parameter), `Validate: arg ${input.name} should be a string.`);
assert(
parameter.length < 32,
`Validate: arg ${input.name} cairo typed ${input.type} should be a string of less than 32 characters.`
);
};

const validateByteArray = (parameter: any, input: AbiEntry) => {
assert(typeof parameter === 'string', `Validate: arg ${input.name} should be a string.`);
assert(isString(parameter), `Validate: arg ${input.name} should be a string.`);
};

const validateUint = (parameter: any, input: AbiEntry) => {
if (typeof parameter === 'number') {
if (isNumber(parameter)) {
assert(
parameter <= Number.MAX_SAFE_INTEGER,
`Validation: Parameter is to large to be typed as Number use (BigInt or String)`
);
}
assert(
typeof parameter === 'string' ||
typeof parameter === 'number' ||
typeof parameter === 'bigint' ||
isString(parameter) ||
isNumber(parameter) ||
isBigInt(parameter) ||
(typeof parameter === 'object' && 'low' in parameter && 'high' in parameter),
`Validate: arg ${input.name} of cairo type ${
input.type
Expand Down Expand Up @@ -142,7 +142,7 @@ const validateUint = (parameter: any, input: AbiEntry) => {

const validateBool = (parameter: any, input: AbiEntry) => {
assert(
typeof parameter === 'boolean',
isBoolean(parameter),
`Validate: arg ${input.name} of cairo type ${input.type} should be type (Boolean)`
);
};
Expand Down
4 changes: 3 additions & 1 deletion src/utils/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import { computeCompiledClassHash, computeContractClassHash } from './hash';
import { parse } from './json';
import { decompressProgram } from './stark';

import { isString } from './shortString';

export function isSierra(
contract: CairoContract | string
): contract is SierraContractClass | CompiledSierra {
const compiledContract = typeof contract === 'string' ? parse(contract) : contract;
const compiledContract = isString(contract) ? parse(contract) : contract;
return 'sierra_program' in compiledContract;
}

Expand Down
9 changes: 5 additions & 4 deletions src/utils/hash/classHash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { starkCurve } from '../ec';
import { addHexPrefix, utf8ToArray } from '../encode';
import { parse, stringify } from '../json';
import { toHex } from '../num';
import { encodeShortString } from '../shortString';
import { encodeShortString, isString } from '../shortString';

export function computePedersenHash(a: BigNumberish, b: BigNumberish): string {
return starkCurve.pedersen(BigInt(a), BigInt(b));
Expand Down Expand Up @@ -125,8 +125,9 @@ export default function computeHintedClassHash(compiledContract: LegacyCompiledC
* @returns format: hex-string
*/
export function computeLegacyContractClassHash(contract: LegacyCompiledContract | string) {
const compiledContract =
typeof contract === 'string' ? (parse(contract) as LegacyCompiledContract) : contract;
const compiledContract = isString(contract)
? (parse(contract) as LegacyCompiledContract)
: contract;

const apiVersion = toHex(API_VERSION);

Expand Down Expand Up @@ -287,7 +288,7 @@ export function computeSierraContractClassHash(sierra: CompiledSierra) {
* @returns format: hex-string
*/
export function computeContractClassHash(contract: CompiledContract | string) {
const compiledContract = typeof contract === 'string' ? parse(contract) : contract;
const compiledContract = isString(contract) ? parse(contract) : contract;

if ('sierra_program' in compiledContract) {
return computeSierraContractClassHash(compiledContract as CompiledSierra);
Expand Down
20 changes: 20 additions & 0 deletions src/utils/num.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,23 @@ export function addPercent(number: BigNumberish, percent: number) {
const bigIntNum = BigInt(number);
return bigIntNum + (bigIntNum * BigInt(percent)) / 100n;
}

/**
* Check if a value is a number.
*
* @param {unknown} value - The value to check.
* @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.
* @return {boolean} - True if the value is of boolean type, false otherwise.
*/
export function isBoolean(value: unknown): value is boolean {
return typeof value === 'boolean';
}
26 changes: 12 additions & 14 deletions src/utils/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ import { ETransactionVersion } from '../types/api';
import { isSierra } from './contract';
import { formatSpaces } from './hash';
import { parse, stringify } from './json';
import { isHex, toHex } from './num';
import { isBigInt, isHex, isNumber, toHex } from './num';
import { compressProgram } from './stark';
import { isString } from './shortString';

/**
* Helper - Async Sleep for 'delay' time
Expand Down Expand Up @@ -53,8 +54,7 @@ export function createSierraContractClass(contract: CompiledSierra): SierraContr
* (CompiledContract or string) -> ContractClass
*/
export function parseContract(contract: CompiledContract | string): ContractClass {
const parsedContract =
typeof contract === 'string' ? (parse(contract) as CompiledContract) : contract;
const parsedContract = isString(contract) ? (parse(contract) as CompiledContract) : contract;

if (!isSierra(contract)) {
return {
Expand Down Expand Up @@ -85,7 +85,7 @@ export const getDefaultNodeUrl = (networkName?: NetworkName, mute: boolean = fal
* [Reference](https://github.com/starkware-libs/cairo-lang/blob/fc97bdd8322a7df043c87c371634b26c15ed6cee/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L148-L153)
*/
export function formatHash(hashValue: BigNumberish): string {
if (typeof hashValue === 'string') return hashValue;
if (isString(hashValue)) return hashValue;
return toHex(hashValue);
}

Expand All @@ -111,19 +111,17 @@ export class Block {
tag: BlockIdentifier = null;

private setIdentifier(__identifier: BlockIdentifier) {
if (typeof __identifier === 'string' && isHex(__identifier)) {
this.hash = __identifier;
} else if (typeof __identifier === 'bigint') {
if (isString(__identifier)) {
if (isHex(__identifier)) {
this.hash = __identifier;
} else if (validBlockTags.includes(__identifier as BlockTag)) {
this.tag = __identifier;
}
} else if (isBigInt(__identifier)) {
this.hash = toHex(__identifier);
} else if (typeof __identifier === 'number') {
} else if (isNumber(__identifier)) {
this.number = __identifier;
} else if (
typeof __identifier === 'string' &&
validBlockTags.includes(__identifier as BlockTag)
) {
this.tag = __identifier;
} else {
// default
this.tag = BlockTag.pending;
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/utils/responseParser/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
import { toBigInt } from '../num';
import { estimateFeeToBounds, estimatedFeeToMaxFee } from '../stark';
import { ResponseParser } from '.';
import { isString } from '../shortString';

export class RPCResponseParser
implements
Expand Down Expand Up @@ -57,7 +58,7 @@ export class RPCResponseParser
public parseTransactionReceipt(res: TransactionReceipt): GetTransactionReceiptResponse {
// HOTFIX RPC 0.5 to align with RPC 0.6
// This case is RPC 0.5. It can be only v2 thx with FRI units
if ('actual_fee' in res && typeof res.actual_fee === 'string') {
if ('actual_fee' in res && isString(res.actual_fee)) {
return {
...(res as GetTransactionReceiptResponse),
actual_fee: {
Expand Down Expand Up @@ -113,7 +114,7 @@ export class RPCResponseParser
public parseContractClassResponse(res: ContractClassPayload): ContractClassResponse {
return {
...(res as ContractClassResponse),
abi: typeof res.abi === 'string' ? JSON.parse(res.abi) : res.abi,
abi: isString(res.abi) ? JSON.parse(res.abi) : res.abi,
};
}
}
Loading

0 comments on commit 6235080

Please sign in to comment.