Skip to content

Commit 1fd9baa

Browse files
committed
rewrite calldataEncoder
1 parent 91c69c7 commit 1fd9baa

File tree

3 files changed

+102
-74
lines changed

3 files changed

+102
-74
lines changed

src/utils/calldata/calldataDecoder.ts

+88-66
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,7 @@ import {
55
BigNumberish,
66
ByteArray,
77
CairoEnum,
8-
Calldata,
9-
MultiType,
108
ParsedStruct,
11-
RawArgs,
12-
RawArgsArray,
13-
StructAbi,
149
} from '../../types';
1510
import { CairoUint256 } from '../cairoDataTypes/uint256';
1611
import { CairoUint512 } from '../cairoDataTypes/uint512';
@@ -19,30 +14,30 @@ import { decodeShortString } from '../shortString';
1914
import { stringFromByteArray } from './byteArray';
2015
import { addHexPrefix, removeHexPrefix } from '../encode';
2116
import {
22-
isTypeFelt,
2317
getArrayType,
2418
isTypeArray,
2519
isTypeBytes31,
2620
isTypeEnum,
2721
isTypeBool,
2822
isLen,
23+
isCairo1Type,
2924
isTypeByteArray,
3025
isTypeSecp256k1Point,
3126
isTypeOption,
3227
isTypeResult,
33-
isTypeStruct,
28+
isTypeEthAddress,
3429
isTypeTuple,
3530
} from './cairo';
3631
import {
3732
CairoCustomEnum,
33+
CairoEnumRaw,
3834
CairoOption,
3935
CairoOptionVariant,
4036
CairoResult,
4137
CairoResultVariant,
4238
} from './enum';
4339
import extractTupleMemberTypes from './tuple';
4440
import assert from '../assert';
45-
import { call } from 'abi-wan-kanabi';
4641

4742
/**
4843
* Decode base types from calldata
@@ -79,11 +74,11 @@ function decodeBaseTypes(type: string, it: Iterator<string>):
7974

8075
return new CairoUint512(limb0, limb1, limb2, limb3).toBigInt();
8176

82-
case type === 'core::starknet::eth_address::EthAddress':
77+
case isTypeEthAddress(type):
8378
temp = it.next().value;
8479
return BigInt(temp);
8580

86-
case type === 'core::bytes_31::bytes31':
81+
case isTypeBytes31(type):
8782
temp = it.next().value;
8883
return decodeShortString(temp);
8984

@@ -102,34 +97,6 @@ function decodeBaseTypes(type: string, it: Iterator<string>):
10297
}
10398
}
10499

105-
/**
106-
* Get the expected calldata length for a given enum variant.
107-
* @param variantIndexCalldata The calldata for the variant index.
108-
* @param enumName The name of the enum.
109-
* @param enums The ABI enums.
110-
* @returns The expected calldata length.
111-
*/
112-
function getExpectedCalldataLengthForEnum(
113-
variantIndexCalldata: string,
114-
enumName: string,
115-
enums: AbiEnums
116-
): number {
117-
const enumDefinition = enums[enumName];
118-
assert(enumDefinition, `Enum with name ${enumName} not found.`);
119-
120-
const variantIndex = parseInt(variantIndexCalldata, 10);
121-
const variant = enumDefinition.variants[variantIndex];
122-
123-
switch (enumName) {
124-
case 'CairoOption':
125-
return variant.name === 'None' ? 1 : 2; // "None" requires only the index, "Some" requires additional data.
126-
case 'CairoResult':
127-
return 2; // Both "Ok" and "Err" require additional data.
128-
default:
129-
return 1; // Assuming other enums don't have associated data by default.
130-
}
131-
}
132-
133100
/**
134101
* Decodes calldata based on the provided type, using an iterator over the calldata.
135102
* @param calldataIterator Iterator over the encoded calldata strings.
@@ -144,10 +111,11 @@ function decodeCalldataValue(
144111
structs: AbiStructs,
145112
enums: AbiEnums
146113
):
147-
| Boolean
114+
| Boolean
148115
| ParsedStruct
149116
| BigNumberish
150117
| BigNumberish[]
118+
| any[]
151119
| CairoOption<any>
152120
| CairoResult<any, any>
153121
| CairoEnum
@@ -163,6 +131,7 @@ function decodeCalldataValue(
163131
const high = calldataIterator.next().value;
164132
return new CairoUint256(low, high).toBigInt();
165133
}
134+
166135
// type uint512 struct
167136
if (CairoUint512.isAbiType(element.type)) {
168137
const limb0 = calldataIterator.next().value;
@@ -171,6 +140,7 @@ function decodeCalldataValue(
171140
const limb3 = calldataIterator.next().value;
172141
return new CairoUint512(limb0, limb1, limb2, limb3).toBigInt();
173142
}
143+
174144
// type C1 ByteArray struct, representing a LongString
175145
if (isTypeByteArray(element.type)) {
176146
const parsedBytes31Arr: BigNumberish[] = [];
@@ -188,9 +158,75 @@ function decodeCalldataValue(
188158
return stringFromByteArray(myByteArray);
189159
}
190160

191-
// type Bytes31 string
192-
if (isTypeBytes31(element.type)) {
193-
return decodeShortString(calldataIterator.next().value);
161+
// type struct
162+
if (structs && element.type in structs && structs[element.type]) {
163+
if (isTypeEthAddress(element.type)) {
164+
return decodeBaseTypes(element.type, calldataIterator);
165+
}
166+
return structs[element.type].members.reduce((acc, el) => {
167+
acc[el.name] = decodeCalldataValue(calldataIterator, el, structs, enums);
168+
return acc;
169+
}, {} as any);
170+
}
171+
172+
// type Enum (only CustomEnum)
173+
if (enums && element.type in enums && enums[element.type]) {
174+
const variantNum: number = Number(calldataIterator.next().value); // get variant number
175+
const rawEnum = enums[element.type].variants.reduce((acc, variant, num) => {
176+
if (num === variantNum) {
177+
acc[variant.name] = decodeCalldataValue(
178+
calldataIterator,
179+
{ name: '', type: variant.type },
180+
structs,
181+
enums
182+
);
183+
return acc;
184+
}
185+
acc[variant.name] = undefined;
186+
return acc;
187+
}, {} as CairoEnumRaw);
188+
// Option
189+
if (isTypeOption(element.type)) {
190+
const content = variantNum === CairoOptionVariant.Some ? rawEnum.Some : undefined;
191+
return new CairoOption<Object>(variantNum, content);
192+
}
193+
// Result
194+
if (isTypeResult(element.type)) {
195+
let content: Object;
196+
if (variantNum === CairoResultVariant.Ok) {
197+
content = rawEnum.Ok;
198+
} else {
199+
content = rawEnum.Err;
200+
}
201+
return new CairoResult<Object, Object>(variantNum, content);
202+
}
203+
// Cairo custom Enum
204+
const customEnum = new CairoCustomEnum(rawEnum);
205+
return customEnum;
206+
}
207+
208+
// type tuple
209+
if (isTypeTuple(element.type)) {
210+
const memberTypes = extractTupleMemberTypes(element.type);
211+
return memberTypes.reduce((acc, it: any, idx) => {
212+
const name = it?.name ? it.name : idx;
213+
const type = it?.type ? it.type : it;
214+
const el = { name, type };
215+
acc[name] = decodeCalldataValue(calldataIterator, el, structs, enums);
216+
return acc;
217+
}, {} as any);
218+
}
219+
220+
// type c1 array
221+
if (isTypeArray(element.type)) {
222+
// eslint-disable-next-line no-case-declarations
223+
const parsedDataArr = [];
224+
const el: AbiEntry = { name: '', type: getArrayType(element.type) };
225+
const len = BigInt(calldataIterator.next().value); // get length
226+
while (parsedDataArr.length < len) {
227+
parsedDataArr.push(decodeCalldataValue(calldataIterator, el, structs, enums));
228+
}
229+
return parsedDataArr;
194230
}
195231

196232
// base type
@@ -218,33 +254,19 @@ export default function decodeCalldataField(
218254
let temp = calldataIterator.next().value;
219255
return BigInt(temp);
220256

221-
case isTypeArray(type): {
222-
const elementType = getArrayType(type);
223-
const elements: any[] = [];
224-
let elementResult = calldataIterator.next();
225-
while (!elementResult.done) {
226-
elements.push(decodeCalldataValue(elementResult.value, elementType, structs, enums));
227-
elementResult = calldataIterator.next();
228-
}
229-
return elements;
230-
}
257+
case (structs && type in structs) || isTypeTuple(type):
258+
return decodeCalldataValue(calldataIterator, input, structs, enums);
231259

232-
case isTypeStruct(type, structs):
233-
case isTypeTuple(type):
234-
const structOrTupleResult: RawArgs = {};
235-
const memberTypes = structs[type]?.members || extractTupleMemberTypes(type);
236-
memberTypes.forEach(member => {
237-
structOrTupleResult[member.name] = decodeCalldataValue(calldataIterator.next().value, member.type, structs, enums);
238-
});
239-
return structOrTupleResult;
260+
case enums && isTypeEnum(type, enums):
261+
return decodeCalldataValue(calldataIterator, input, structs, enums);
240262

241-
case isTypeFelt(type):
242-
case CairoUint256.isAbiType(type):
243-
case isTypeEnum(type, enums):
244-
case isTypeBytes31(type):
245-
return decodeCalldataValue(calldataIterator.next().value, type, structs, enums);
263+
case isTypeArray(type):
264+
// C1 Array
265+
if (isCairo1Type(type)) {
266+
return decodeCalldataValue(calldataIterator, input, structs, enums);
267+
}
246268

247269
default:
248-
throw new Error(`Unsupported or unrecognized type: ${type}`);
270+
return decodeBaseTypes(type, calldataIterator);
249271
}
250272
}

src/utils/calldata/requestParser.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ import {
2222
isTypeBytes31,
2323
isTypeEnum,
2424
isTypeOption,
25+
isTypeEthAddress,
2526
isTypeResult,
2627
isTypeSecp256k1Point,
2728
isTypeStruct,
2829
isTypeTuple,
2930
uint256,
31+
isTypeByteArray,
3032
} from './cairo';
3133
import {
3234
CairoCustomEnum,
@@ -147,10 +149,10 @@ function parseCalldataValue(
147149
if (CairoUint512.isAbiType(type)) {
148150
return new CairoUint512(element as any).toApiRequest();
149151
}
150-
if (type === 'core::starknet::eth_address::EthAddress')
152+
if (isTypeEthAddress(type))
151153
return parseBaseTypes(type, element as BigNumberish);
152154

153-
if (type === 'core::byte_array::ByteArray') return parseByteArray(element as string);
155+
if (isTypeByteArray(type)) return parseByteArray(element as string);
154156

155157
const { members } = structs[type];
156158
const subElement = element as any;
@@ -297,7 +299,7 @@ export function parseCalldataField(
297299
}
298300
return parseCalldataValue(value, input.type, structs, enums);
299301

300-
case type === 'core::starknet::eth_address::EthAddress':
302+
case isTypeEthAddress(type):
301303
return parseBaseTypes(type, value);
302304
// Struct or Tuple
303305
case isTypeStruct(type, structs) ||

src/utils/calldata/responseParser.ts

+9-5
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@ import {
2323
isTypeArray,
2424
isTypeBool,
2525
isTypeByteArray,
26+
isTypeBytes31,
2627
isTypeEnum,
28+
isTypeOption,
29+
isTypeResult,
30+
isTypeEthAddress,
2731
isTypeSecp256k1Point,
2832
isTypeTuple,
2933
} from './cairo';
@@ -59,10 +63,10 @@ function parseBaseTypes(type: string, it: Iterator<string>) {
5963
const limb2 = it.next().value;
6064
const limb3 = it.next().value;
6165
return new CairoUint512(limb0, limb1, limb2, limb3).toBigInt();
62-
case type === 'core::starknet::eth_address::EthAddress':
66+
case isTypeEthAddress(type):
6367
temp = it.next().value;
6468
return BigInt(temp);
65-
case type === 'core::bytes_31::bytes31':
69+
case isTypeBytes31(type):
6670
temp = it.next().value;
6771
return decodeShortString(temp);
6872
case isTypeSecp256k1Point(type):
@@ -141,7 +145,7 @@ function parseResponseValue(
141145

142146
// type struct
143147
if (structs && element.type in structs && structs[element.type]) {
144-
if (element.type === 'core::starknet::eth_address::EthAddress') {
148+
if (isTypeEthAddress(element.type)) {
145149
return parseBaseTypes(element.type, responseIterator);
146150
}
147151
return structs[element.type].members.reduce((acc, el) => {
@@ -167,12 +171,12 @@ function parseResponseValue(
167171
return acc;
168172
}, {} as CairoEnumRaw);
169173
// Option
170-
if (element.type.startsWith('core::option::Option')) {
174+
if (isTypeOption(element.type)) {
171175
const content = variantNum === CairoOptionVariant.Some ? rawEnum.Some : undefined;
172176
return new CairoOption<Object>(variantNum, content);
173177
}
174178
// Result
175-
if (element.type.startsWith('core::result::Result')) {
179+
if (isTypeResult(element.type)) {
176180
let content: Object;
177181
if (variantNum === CairoResultVariant.Ok) {
178182
content = rawEnum.Ok;

0 commit comments

Comments
 (0)