starknet.js icon indicating copy to clipboard operation
starknet.js copied to clipboard

feat: Implement Calldata Decoding to JSC Types

Open AryanGodara opened this issue 1 year ago • 12 comments

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

AryanGodara avatar Mar 15 '24 13:03 AryanGodara

@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 😅)

AryanGodara avatar Mar 15 '24 19:03 AryanGodara

Good point splitting it into two subtasks is more manageable. Will deal with it this week.

tabaktoni avatar Mar 18 '24 08:03 tabaktoni

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

AryanGodara avatar Mar 20 '24 10:03 AryanGodara

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.

tabaktoni avatar Mar 22 '24 14:03 tabaktoni

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 :)

AryanGodara avatar Mar 26 '24 10:03 AryanGodara

@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

AryanGodara avatar Mar 26 '24 12:03 AryanGodara

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);

tabaktoni avatar Apr 23 '24 14:04 tabaktoni

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);

PhilippeR26 avatar Apr 26 '24 14:04 PhilippeR26

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.

AryanGodara avatar Apr 27 '24 19:04 AryanGodara

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. image

PhilippeR26 avatar Apr 27 '24 20:04 PhilippeR26

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. image

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 😅

AryanGodara avatar Apr 27 '24 22:04 AryanGodara

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);

PhilippeR26 avatar Apr 30 '24 12:04 PhilippeR26