From 5cb29e70a3541e4e81398ba0b6392e8e65b6550c Mon Sep 17 00:00:00 2001 From: Philippe ROSTAN <81040730+PhilippeR26@users.noreply.github.com> Date: Mon, 17 Jul 2023 10:59:53 +0200 Subject: [PATCH] feat: cairo enums 1 --- src/types/contract.ts | 22 +++++++++++- src/types/lib/contract/abi.ts | 8 +++++ src/utils/calldata/cairo.ts | 3 +- src/utils/calldata/index.ts | 23 ++++++++++++- src/utils/calldata/responseParser.ts | 50 +++++++++++++++++++++++----- 5 files changed, 95 insertions(+), 11 deletions(-) diff --git a/src/types/contract.ts b/src/types/contract.ts index 2bbef2e57..924ec6dcb 100644 --- a/src/types/contract.ts +++ b/src/types/contract.ts @@ -2,6 +2,25 @@ import { BigNumberish, BlockIdentifier, RawArgsArray, Signature } from './lib'; export type AsyncContractFunction = (...args: ArgsOrCalldataWithOptions) => Promise; export type ContractFunction = (...args: ArgsOrCalldataWithOptions) => any; + +export interface CairoCustomEnumInterface { + [key: string]: any; + unwrap: Function; +} +export interface CairoOptionInterface { + [key: string]: any; + unwrap: Function; + isSome: Function; + isNone: Function; +} +export interface CairoResultInterface { + [key: string]: any; + unwrap: Function; + isOK: Function; + isErr: Function; +} +export type CairoEnum = CairoCustomEnumInterface | CairoOptionInterface | CairoResultInterface; + export type Result = | { [key: string]: any; @@ -9,7 +28,8 @@ export type Result = | Result[] | bigint | string - | boolean; + | boolean + | CairoEnum; /** * Compiled calldata ready to be sent diff --git a/src/types/lib/contract/abi.ts b/src/types/lib/contract/abi.ts index 586bfa094..249a7aaec 100644 --- a/src/types/lib/contract/abi.ts +++ b/src/types/lib/contract/abi.ts @@ -29,4 +29,12 @@ export type StructAbi = { type: 'struct'; }; +export type AbiEnums = { [name: string]: EnumAbi }; +export type EnumAbi = { + variants: (AbiEntry & { offset: number })[]; + name: string; + size: number; + type: 'enum'; +}; + type EventAbi = any; diff --git a/src/utils/calldata/cairo.ts b/src/utils/calldata/cairo.ts index 251d497b8..ed84fd645 100644 --- a/src/utils/calldata/cairo.ts +++ b/src/utils/calldata/cairo.ts @@ -1,4 +1,4 @@ -import { Abi, AbiStructs, BigNumberish, Uint, Uint256 } from '../../types'; +import { Abi, AbiEnums, AbiStructs, BigNumberish, Uint, Uint256 } from '../../types'; import { isBigInt, isHex, isStringWholeNumber } from '../num'; import { encodeShortString, isShortString, isText } from '../shortString'; import { UINT_128_MAX, isUint256 } from '../uint256'; @@ -10,6 +10,7 @@ export const isTypeArray = (type: string) => export const isTypeTuple = (type: string) => /^\(.*\)$/i.test(type); export const isTypeNamedTuple = (type: string) => /\(.*\)/i.test(type) && type.includes(':'); export const isTypeStruct = (type: string, structs: AbiStructs) => type in structs; +export const isTypeEnum = (type: string, enums: AbiEnums) => type in enums; export const isTypeUint = (type: string) => Object.values(Uint).includes(type as Uint); export const isTypeUint256 = (type: string) => type === 'core::integer::u256'; export const isTypeBool = (type: string) => type === 'core::bool'; diff --git a/src/utils/calldata/index.ts b/src/utils/calldata/index.ts index c32337f4e..cffb2447f 100644 --- a/src/utils/calldata/index.ts +++ b/src/utils/calldata/index.ts @@ -1,6 +1,7 @@ /* eslint-disable no-plusplus */ import { Abi, + AbiEnums, AbiStructs, Args, ArgsOrCalldata, @@ -34,8 +35,11 @@ export class CallData { protected readonly structs: AbiStructs; + protected readonly enums: AbiEnums; + constructor(abi: Abi) { this.structs = CallData.getAbiStruct(abi); + this.enums = CallData.getAbiEnum(abi); this.parser = createAbiParser(abi); this.abi = this.parser.getLegacyFormat(); } @@ -175,7 +179,7 @@ export class CallData { const parsed = outputs.flat().reduce((acc, output, idx) => { const propName = output.name ?? idx; - acc[propName] = responseParser(responseIterator, output, this.structs, acc); + acc[propName] = responseParser(responseIterator, output, this.structs, this.enums, acc); if (acc[propName] && acc[`${propName}_len`]) { delete acc[`${propName}_len`]; } @@ -215,6 +219,23 @@ export class CallData { ); } + /** + * Helper to extract enums from abi + * @param abi Abi + * @returns AbiEnums - enums from abi + */ + static getAbiEnum(abi: Abi): AbiEnums { + return abi + .filter((abiEntry) => abiEntry.type === 'enum') + .reduce( + (acc, abiEntry) => ({ + ...acc, + [abiEntry.name]: abiEntry, + }), + {} + ); + } + /** * Helper: Compile HexCalldata | RawCalldata | RawArgs * @param rawCalldata HexCalldata | RawCalldata | RawArgs diff --git a/src/utils/calldata/responseParser.ts b/src/utils/calldata/responseParser.ts index 169959422..9d0ffbb19 100644 --- a/src/utils/calldata/responseParser.ts +++ b/src/utils/calldata/responseParser.ts @@ -1,5 +1,13 @@ /* eslint-disable no-case-declarations */ -import { AbiEntry, AbiStructs, Args, BigNumberish, ParsedStruct } from '../../types'; +import { + AbiEntry, + AbiEnums, + AbiStructs, + Args, + BigNumberish, + CairoCustomEnum, + ParsedStruct, +} from '../../types'; import { uint256ToBN } from '../uint256'; import { getArrayType, @@ -7,6 +15,7 @@ import { isLen, isTypeArray, isTypeBool, + isTypeEnum, isTypeTuple, isTypeUint256, } from './cairo'; @@ -45,7 +54,8 @@ function parseBaseTypes(type: string, it: Iterator) { function parseResponseValue( responseIterator: Iterator, element: { name: string; type: string }, - structs: AbiStructs + structs: AbiStructs, + enums: AbiEnums ): BigNumberish | ParsedStruct | boolean | any[] { // type uint256 struct (c1v2) if (isTypeUint256(element.type)) { @@ -57,11 +67,15 @@ function parseResponseValue( // type struct if (element.type in structs && structs[element.type]) { return structs[element.type].members.reduce((acc, el) => { - acc[el.name] = parseResponseValue(responseIterator, el, structs); + acc[el.name] = parseResponseValue(responseIterator, el, structs, enums); return acc; }, {} as any); } + // type Enum + // if (element.type in enums && enums[element.type]) { + // } + // type tuple if (isTypeTuple(element.type)) { const memberTypes = extractTupleMemberTypes(element.type); @@ -69,7 +83,7 @@ function parseResponseValue( const name = it?.name ? it.name : idx; const type = it?.type ? it.type : it; const el = { name, type }; - acc[name] = parseResponseValue(responseIterator, el, structs); + acc[name] = parseResponseValue(responseIterator, el, structs, enums); return acc; }, {} as any); } @@ -81,7 +95,7 @@ function parseResponseValue( const el = { name: '', type: getArrayType(element.type) }; const len = BigInt(responseIterator.next().value); // get length while (parsedDataArr.length < len) { - parsedDataArr.push(parseResponseValue(responseIterator, el, structs)); + parsedDataArr.push(parseResponseValue(responseIterator, el, structs, enums)); } return parsedDataArr; } @@ -103,6 +117,7 @@ export default function responseParser( responseIterator: Iterator, output: AbiEntry, structs: AbiStructs, + enums: AbiEnums, parsedResult?: Args ): any { const { name, type } = output; @@ -114,12 +129,30 @@ export default function responseParser( return BigInt(temp); case type in structs || isTypeTuple(type): - return parseResponseValue(responseIterator, output, structs); + return parseResponseValue(responseIterator, output, structs, enums); + + case isTypeEnum(type, enums): + const variantNum: number = Number(responseIterator.next().value); // get variant number + const myEnum = enums[output.type].variants.reduce((acc, variant, num) => { + if (num === variantNum) { + acc[variant.name] = parseResponseValue( + responseIterator, + { name: '0', type: variant.type }, + structs, + enums + ); + return acc; + } + acc[variant.name] = undefined; + return acc; + }, {} as Record); + const customEnum: CairoCustomEnum = { ...myEnum, ...unw }; + return customEnum; case isTypeArray(type): // C1 Array if (isCairo1Type(type)) { - return parseResponseValue(responseIterator, output, structs); + return parseResponseValue(responseIterator, output, structs, enums); } // C0 Array // eslint-disable-next-line no-case-declarations @@ -131,7 +164,8 @@ export default function responseParser( parseResponseValue( responseIterator, { name, type: output.type.replace('*', '') }, - structs + structs, + enums ) ); }