gmajor-encrypt / php-scale-codec

substrate php scale codec
https://packagist.org/packages/gmajor/substrate-codec-php
MIT License
7 stars 7 forks source link

An empty Option<Vec> is considered null #14

Closed leonardocustodio closed 2 years ago

leonardocustodio commented 2 years ago

Hello @gmajor-encrypt,

I noticed one issue where an Option is wrongly encoded when passing an empty array instead of null.

Consider this scenario:

 $generator = Base::create();
        Base::regCustom($generator, [
            "MultiTokensAssetId" => ["collectionId" => "Compact<u128>", "tokenId" => "Compact<u128>"],
            "CollectionMutation" => ["owner" => "Option<AccountId>", "currencies" => "Option<Vec<MultiTokensAssetId>>"],
            "MutateCollection" => [
                "callIndex" => "(u8, u8)",
                "collectionId" => "Compact<u128>",
                "mutation" => "CollectionMutation"
            ],
        ]);
        $codec = new ScaleInstance($generator);

        $response = $codec->createTypeByTypeString("MutateCollection")->encode([
            "callIndex" => [40, 2],
            "collectionId" => 2000,
            "mutation" => [
                "owner" => null,
                "currencies" => null,
            ]
        ]);

        $this->assertEquals("2802411f0000", $response);

The above is correct but shall I pass an empty Vec to currencies the encoded output is wrong:

 $generator = Base::create();
        Base::regCustom($generator, [
            "MultiTokensAssetId" => ["collectionId" => "Compact<u128>", "tokenId" => "Compact<u128>"],
            "CollectionMutation" => ["owner" => "Option<AccountId>", "currencies" => "Option<Vec<MultiTokensAssetId>>"],
            "MutateCollection" => [
                "callIndex" => "(u8, u8)",
                "collectionId" => "Compact<u128>",
                "mutation" => "CollectionMutation"
            ],
        ]);
        $codec = new ScaleInstance($generator);

        $response = $codec->createTypeByTypeString("MutateCollection")->encode([
            "callIndex" => [40, 2],
            "collectionId" => 2000,
            "mutation" => [
                "owner" => null,
                "currencies" => [],
            ]
        ]);

        $this->assertEquals("2802411f000100", $response);

The above should encode as 2802411f000100 still it encodes the same value as passing null from before 2802411f0000

Best regards, Leonardo

gmajor-encrypt commented 2 years ago

hi, @leonardocustodio I test parity_scale_codec, with this case

    let empty_vec: Vec<u8> = vec![];
    let opt_vec = Some(empty_vec);
    assert_eq!(hexify(&opt_vec.encode()),"01 00");

Empty arrays also have values, so there is no problem with the current logic

leonardocustodio commented 2 years ago

Yep, that's the thing, in php-scale-codec lib it doesn't encode to an empty array. Meaning if we try to encode an empty array instead of giving us 01 00 (an option empty array) it gives 00 (null - a null option).

If you try the second test in the question you are going to see that it fails.

gmajor-encrypt commented 2 years ago

@leonardocustodio yes, it's my fault. I have fixed it, you can try it

leonardocustodio commented 2 years ago

Yep, it worked, thanks man, you are awesome! :+1: Btw, may I suggest a new grant if you want to go for it? You could go for adding support to contract metadata/decoding/encoding/interface etc. It would be very useful for the community, something like so: https://github.com/Supercolony-net/Open-Grants-Program/blob/feature/typechain-follow-up/applications/typechain-polkadot-follow-up.md