starknet.js
starknet.js copied to clipboard
feat: Implement Calldata Decoding to JSC Types
Motivation and Resolution
Resolves #952
Currently involves calldata decoder, ie, calldata -> JSC types
Checklist:
- [x] Performed a self-review of the code
- [x] Rebased to the last commit of the target branch (or merged it into my branch)
- [x] Linked the issues which this PR resolves
- [x] Documented the changes in code (API docs will be generated automatically)
- [x] Updated the tests
- [x] All tests are passing
@tabaktoni @ivpavici I've tried to implement one-half of the issue and could really use your review on this one (no rush though, ik the weekend has started 😅)
Good point splitting it into two subtasks is more manageable. Will deal with it this week.
Good point splitting it into two subtasks is more manageable. Will deal with it this week.
Are the current changes a step in the right direction? If so, then I'll start to work on the API encoder part as well :D
In general, this is a good direction. We need to have encoding < - > decoding tests for this. So take some call data encode it and then decode, you should get back the initial objects sent to the calldata.
I think you would focus for now only on this part of the task as there are multiple data types to support this flow. Some of them are extracted to singular classes like uin256 and decoding call data will be part of the class, but at the moment it is not so important as we can do it in future steps.
Priority is to have test type by type and mix types for this flow.
In general, this is a good direction. We need to have encoding < - > decoding tests for this. So take some call data encode it and then decode, you should get back the initial objects sent to the calldata.
I think you would focus for now only on this part of the task as there are multiple data types to support this flow. Some of them are extracted to singular classes like uin256 and decoding call data will be part of the class, but at the moment it is not so important as we can do it in future steps.
Priority is to have test type by type and mix types for this flow.
Hi, sorry I was busy last week and couldn't get to this task, I'll quickly get to addressing these, and implementing the rest of the solution :)
@tabaktoni I've written an initial test. Does this approach seem correct to you? If so, I'll begin to write more testcases https://github.com/starknet-io/starknet.js/blob/745eb9a801e49b71e14dc87fba66ed709acf58fc/tests/utils/calldataDecode.test.ts#L8
Not sure what was the goal with Iterator, but in general I imagine test to be like:
import contractX....
const data = {
list: [1, 3n],
balance: "0x34"
};
const cd = new CallData(contractX.abi);
const compiledCalldata = cd.compile('methodY', testData)
const decompiledCalldata = cd.decompile(compiledCalldata)
expect(data).toEqual(decompiledCalldata)
You can use any of predefined contracts abi we already have in mocks for example: https://github.com/starknet-io/starknet.js/blob/dceaf85f38273d0497156526e227ee98af988618/tests/cairo1v2.test.ts#L560 this can be data and use
const contractCallData: CallData = new CallData(compiledComplexSierra.abi);
I am a bit in trouble to review this PR. My DAPP for encoding/decoding is using Starknet.js v6.8.0, and do not needs any new functionality. So external tools have already all what is necessary to encode/decode calldata and custom types. Is the purpose of this PR to have something more user friendly?
For example, if I want to decode the encoded parameters of a function, I just write 4 lines :
const encodedCalldata=["4674576345645645","2000","0"];
const fnName="transfer";
const myCallData=new CallData(abi);
const { inputs } = myCallData.parser.getLegacyFormat().find((abiItem:AbiEntry) => abiItem.name === fnName) as FunctionAbi;
const inputsTypes=inputs.map((inp:any)=>{return inp.type as string});
const result=myCallData.decodeParameters(inputsTypes,encodedCalldata);
console.log(result);
I am a bit in trouble to review this PR. My DAPP for encoding/decoding is using Starknet.js v6.8.0, and do not needs any new functionality. So external tools have already all what is necessary to encode/decode calldata and custom types. Is the purpose of this PR to have something more user friendly?
For example, if I want to decode the encoded parameters of a function, I just write 4 lines :
const encodedCalldata=["4674576345645645","2000","0"]; const fnName="transfer"; const myCallData=new CallData(abi); const { inputs } = myCallData.parser.getLegacyFormat().find((abiItem:AbiEntry) => abiItem.name === fnName) as FunctionAbi; const inputsTypes=inputs.map((inp:any)=>{return inp.type as string}); const result=myCallData.decodeParameters(inputsTypes,encodedCalldata); console.log(result);
Hey, @PhilippeR26, Can you share a link for this code? I'd love to take a look as well.
As for the PR, from the issue, according to me, while you already have a great way to accomplish this, having a proper functionality setup within the repo itself would be both more user-friendly and also more sustainable. This will allow a seamless transition between the blockchain and JSC types without having to rely on external tools/write custom logic yourself for each new use case.
Plus, it improves flexibility and customization for handling complex data structures. Additionally, it'll help in validation, error handling, and testing, enhancing the reliability and maintainability of the repo.
https://github.com/PhilippeR26/starknet-encode-decode/blob/046970f58ff07ceb0cf852d4f1cf8638eee7db70/src/app/components/client/EncodeDecode/DecodeFunctionCalldata.tsx#L76-L78
Encoding of a custom type is working but isn't user friendly. It should be improved, to be as nice as decodeParameters.
https://github.com/PhilippeR26/starknet-encode-decode/blob/046970f58ff07ceb0cf852d4f1cf8638eee7db70/src/app/components/client/EncodeDecode/DecodeFunctionCalldata.tsx#L76-L78
Encoding of a custom type is working but isn't user friendly. It should be improved, to be as nice as
decodeParameters.
Thanks for this! I'm working on improving the structure of the code I wrote above. It wasn't compatible with the existing format; so I'm rewriting it.
The PR isn't quite read for review at the moment, making lots of breaking changes, and I'll later squash all the temp commits into a final one.
So please feel free to not review for some time 😅
And if you want to encode a function response, you can use the current code this way :
const params = [cairo.tuple(300,new CairoOption<BigNumberish>(CairoOptionVariant.Some,50));];
const selectedType = "(core::felt252, core::option::Option::<core::integer::u8>)";
const iter = params[Symbol.iterator]();
const structs = CallData.getAbiStruct(abi);
const enums = CallData.getAbiEnum(abi);
const abiExtract = abi.find((abiItem) => abiItem.name === selectedType);
const inputAbi: AbiEntry = abiExtract ?
{ name: abiExtract.type, type: abiExtract.name } :
{ name: "literal", type: responseType };
const result = parseCalldataField(iter, inputAbi, structs, enums);
