Skip to content

Commit fea9368

Browse files
committed
feat(calldata): implement decodeTuple function
1 parent 6057a66 commit fea9368

File tree

1 file changed

+138
-0
lines changed

1 file changed

+138
-0
lines changed

src/utils/calldata/calldataDecoder.ts

+138
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
ByteArray,
77
CairoEnum,
88
ParsedStruct,
9+
StructAbi,
910
Tupled,
1011
} from '../../types';
1112
import { CairoUint256 } from '../cairoDataTypes/uint256';
@@ -24,6 +25,7 @@ import {
2425
} from './cairo';
2526
import {
2627
CairoCustomEnum,
28+
CairoEnumRaw,
2729
CairoOption,
2830
CairoOptionVariant,
2931
CairoResult,
@@ -32,6 +34,7 @@ import {
3234
import extractTupleMemberTypes from './tuple';
3335

3436
import { decodeShortString } from '../shortString';
37+
import { byteArray } from '.';
3538

3639
/**
3740
* Parse base types
@@ -76,3 +79,138 @@ function decodeBaseType(type: string, calldata: string | string[]): BigNumberish
7679
return BigInt(calldata);
7780
}
7881
}
82+
83+
/**
84+
* Decode a tuple from calldata.
85+
* @param calldata The calldata array.
86+
* @param typeStr The type string representing the tuple structure.
87+
* @param structs The ABI structs.
88+
* @param enums The ABI enums.
89+
* @returns An array of decoded tuple elements.
90+
*/
91+
function decodeTuple(calldata: string[], typeStr: string, structs: AbiStructs, enums: AbiEnums) {
92+
// Parse typeStr to understand the tuple structure, e.g., "('felt', 'struct', 'enum')"
93+
const types: string[] = extractTupleMemberTypes(typeStr).map((type: string | object) => String(type));
94+
95+
// Assuming we now have an array of types, ['felt', 'YourStructName', 'YourEnumName'], etc.
96+
const decodedElements = [];
97+
let calldataIndex = 0;
98+
99+
for (const type of types) {
100+
switch (true) {
101+
case isTypeStruct(type, structs):
102+
let structRes = decodeStruct(calldata.slice(calldataIndex, calldataIndex + structs[type].size), type, structs, enums);
103+
decodedElements.push(structRes);
104+
calldataIndex += structs[type].size; // Assuming size is defined for structs.
105+
break;
106+
case isTypeEnum(type, enums):
107+
// Determine the expected calldata consumption for the current enum.
108+
const expectedCalldataLength = getExpectedCalldataLengthForEnum(calldata[calldataIndex], type, enums);
109+
const enumSlice = calldata.slice(calldataIndex, calldataIndex + expectedCalldataLength);
110+
const enumRes = decodeEnum(enumSlice, type, enums);
111+
decodedElements.push(enumRes);
112+
calldataIndex += expectedCalldataLength; // Move past the consumed calldata.
113+
break;
114+
case isTypeArray(type):
115+
const arrayType = getArrayType(type);
116+
const arrayRes = decodeCalldataValue([calldata[calldataIndex]], arrayType, structs, enums);
117+
decodedElements.push(arrayRes);
118+
calldataIndex += 1;
119+
break;
120+
default:
121+
const result = decodeBaseType(type, calldata[calldataIndex]);
122+
decodedElements.push(result);
123+
calldataIndex += 1;
124+
}
125+
}
126+
127+
return decodedElements;
128+
}
129+
130+
function decodeByteArray(calldata: string[]): ByteArray {
131+
// Implementation here...
132+
return byteArrayFromString(calldata.join(''));
133+
}
134+
135+
function decodeCalldataValue(calldata: string | string[], type: string, structs: AbiStructs, enums: AbiEnums): any {
136+
// Implementation here...
137+
}
138+
139+
function decodeCalldataField(calldataIterator: Iterator<string>, input: AbiEntry, structs: AbiStructs, enums: AbiEnums): any {
140+
// Implementation here...
141+
}
142+
143+
function decodeStruct(calldataSegment: string[], structName: string, structs: AbiStructs, enums: AbiEnums): ParsedStruct {
144+
const structAbi: StructAbi = structs[structName];
145+
if (!structAbi) {
146+
throw new Error(`Struct with name ${structName} not found.`);
147+
}
148+
149+
let index = 0;
150+
const result: ParsedStruct = {};
151+
152+
for (const field of structAbi.members) {
153+
const fieldType = field.type;
154+
const fieldCalldata = calldataSegment.slice(index, index + 1); // This is simplified; complex types might span multiple elements.
155+
result[field.name] = decodeCalldataValue(fieldCalldata[0], fieldType, structs, enums);
156+
index++;
157+
}
158+
159+
return result;
160+
}
161+
162+
function decodeEnum(calldataValues: string[], enumName: string, enums: AbiEnums): CairoEnum {
163+
const enumDefinition = enums[enumName];
164+
if (!enumDefinition) {
165+
throw new Error(`Enum with name ${enumName} not found.`);
166+
}
167+
168+
const variantIndex = parseInt(calldataValues[0], 10);
169+
if (variantIndex < 0 || variantIndex >= enumDefinition.variants.length) {
170+
throw new Error(`Variant index ${variantIndex} out of range for enum ${enumName}.`);
171+
}
172+
173+
const variant = enumDefinition.variants[variantIndex];
174+
175+
// Determine the enum type and decode accordingly
176+
switch (enumName) {
177+
case "CairoOption":
178+
switch (variant.name) {
179+
case "None":
180+
return new CairoOption(CairoOptionVariant.None);
181+
default: // "Some"
182+
const someValue = calldataValues[1]; // Placeholder for actual decoding logic.
183+
// The decoding of someValue needs to be based on the expected data type.
184+
return new CairoOption(CairoOptionVariant.Some, someValue); // Assuming someValue decoding logic is implemented elsewhere.
185+
}
186+
case "CairoResult":
187+
const resultValue = calldataValues[1]; // Placeholder for actual decoding logic.
188+
189+
switch (variant.name) {
190+
case "Ok":
191+
return new CairoResult(CairoResultVariant.Ok , resultValue); // Needs proper decoding based on expected type.
192+
default: // "Err"
193+
return new CairoResult(CairoResultVariant.Err , resultValue); // Needs proper decoding based on expected type.
194+
}
195+
196+
default: // Handling CairoCustomEnum or simple enum types without associated data.
197+
return new CairoCustomEnum({ activeVariant: variant.name, variant: variant.name });
198+
}
199+
}
200+
201+
function getExpectedCalldataLengthForEnum(variantIndexCalldata: string, enumName: string, enums: AbiEnums): number {
202+
const enumDefinition = enums[enumName];
203+
if (!enumDefinition) throw new Error(`Enum with name ${enumName} not found.`);
204+
205+
const variantIndex = parseInt(variantIndexCalldata, 10);
206+
const variant = enumDefinition.variants[variantIndex];
207+
208+
switch (enumName) {
209+
case "CairoOption":
210+
return variant.name === "None" ? 1 : 2; // "None" requires only the index, "Some" requires additional data.
211+
case "CairoResult":
212+
return 2; // Both "Ok" and "Err" require additional data.
213+
default:
214+
return 1; // Assuming other enums don't have associated data by default.
215+
}
216+
}

0 commit comments

Comments
 (0)