Skip to content

Commit

Permalink
feat: cairo enums 1
Browse files Browse the repository at this point in the history
  • Loading branch information
PhilippeR26 committed Jul 17, 2023
1 parent 045af8e commit 5cb29e7
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 11 deletions.
22 changes: 21 additions & 1 deletion src/types/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,34 @@ import { BigNumberish, BlockIdentifier, RawArgsArray, Signature } from './lib';

export type AsyncContractFunction<T = any> = (...args: ArgsOrCalldataWithOptions) => Promise<T>;
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;
}
| Result[]
| bigint
| string
| boolean;
| boolean
| CairoEnum;

/**
* Compiled calldata ready to be sent
Expand Down
8 changes: 8 additions & 0 deletions src/types/lib/contract/abi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
3 changes: 2 additions & 1 deletion src/utils/calldata/cairo.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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';
Expand Down
23 changes: 22 additions & 1 deletion src/utils/calldata/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable no-plusplus */
import {
Abi,
AbiEnums,
AbiStructs,
Args,
ArgsOrCalldata,
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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`];
}
Expand Down Expand Up @@ -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
Expand Down
50 changes: 42 additions & 8 deletions src/utils/calldata/responseParser.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
/* 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,
isCairo1Type,
isLen,
isTypeArray,
isTypeBool,
isTypeEnum,
isTypeTuple,
isTypeUint256,
} from './cairo';
Expand Down Expand Up @@ -45,7 +54,8 @@ function parseBaseTypes(type: string, it: Iterator<string>) {
function parseResponseValue(
responseIterator: Iterator<string>,
element: { name: string; type: string },
structs: AbiStructs
structs: AbiStructs,
enums: AbiEnums
): BigNumberish | ParsedStruct | boolean | any[] {
// type uint256 struct (c1v2)
if (isTypeUint256(element.type)) {
Expand All @@ -57,19 +67,23 @@ 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);
return memberTypes.reduce((acc, it: any, idx) => {
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);
}
Expand All @@ -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;
}
Expand All @@ -103,6 +117,7 @@ export default function responseParser(
responseIterator: Iterator<string>,
output: AbiEntry,
structs: AbiStructs,
enums: AbiEnums,
parsedResult?: Args
): any {
const { name, type } = output;
Expand All @@ -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<string, any>);
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
Expand All @@ -131,7 +164,8 @@ export default function responseParser(
parseResponseValue(
responseIterator,
{ name, type: output.type.replace('*', '') },
structs
structs,
enums
)
);
}
Expand Down

0 comments on commit 5cb29e7

Please sign in to comment.