rocklabs-io / ic-py

Python Agent Library for the DFINITY Internet Computer
MIT License
125 stars 25 forks source link

ValueError: Variant has no data: {'principal': 'ci6uj-...-6nxa2-cae'} #71

Closed letmejustputthishere closed 1 year ago

letmejustputthishere commented 2 years ago

I have the following ogy.did candid:

type Account = variant {
  account_id : text;
  "principal" : principal;
  extensible : CandyValue;
};
type BalanceResponse = record {
  nfts : vec text;
  sales : vec EscrowRecord;
  stake : vec StakeRecord;
  multi_canister : opt vec principal;
  escrow : vec EscrowRecord;
};
type CandyValue = variant {
  Int : int;
  Nat : nat;
  Empty;
  Nat16 : nat16;
  Nat32 : nat32;
  Nat64 : nat64;
  Blob : vec nat8;
  Bool : bool;
  Int8 : int8;
  Nat8 : nat8;
  Nats : variant { thawed : vec nat; frozen : vec nat };
  Text : text;
  Bytes : variant { thawed : vec nat8; frozen : vec nat8 };
  Int16 : int16;
  Int32 : int32;
  Int64 : int64;
  Option : opt CandyValue;
  Floats : variant { thawed : vec float64; frozen : vec float64 };
  Float : float64;
  Principal : principal;
  Array : variant { thawed : vec CandyValue; frozen : vec CandyValue };
  Class : vec Property;
};
type Errors = variant {
  nyi;
  storage_configuration_error;
  escrow_withdraw_payment_failed;
  token_not_found;
  owner_not_found;
  content_not_found;
  auction_ended;
  out_of_range;
  sale_id_does_not_match;
  sale_not_found;
  item_not_owned;
  property_not_found;
  validate_trx_wrong_host;
  withdraw_too_large;
  content_not_deserializable;
  bid_too_low;
  validate_deposit_wrong_amount;
  existing_sale_found;
  asset_mismatch;
  escrow_cannot_be_removed;
  deposit_burned;
  cannot_restage_minted_token;
  cannot_find_status_in_metadata;
  receipt_data_mismatch;
  validate_deposit_failed;
  unauthorized_access;
  item_already_minted;
  no_escrow_found;
  escrow_owner_not_the_owner;
  improper_interface;
  app_id_not_found;
  token_non_transferable;
  sale_not_over;
  update_class_error;
  malformed_metadata;
  token_id_mismatch;
  id_not_found_in_metadata;
  auction_not_started;
  library_not_found;
  attempt_to_stage_system_data;
  validate_deposit_wrong_buyer;
  not_enough_storage;
  sales_withdraw_payment_failed;
};
type EscrowRecord = record {
  token : TokenSpec;
  token_id : text;
  seller : Account;
  lock_to_date : opt int;
  buyer : Account;
  amount : nat;
  sale_id : opt text;
};
type ICTokenSpec = record {
  fee : nat;
  decimals : nat;
  canister : principal;
  standard : variant { EXTFungible; DIP20; Ledger };
  symbol : text;
};
type OrigynError = record {
  "text" : text;
  error : Errors;
  number : nat32;
  flag_point : text;
};
type Principal = principal;
type Property = record {
  value : CandyValue;
  name : text;
  immutable : bool;
};
type Result_19 = variant {
  ok : BalanceResponse;
  err : OrigynError;
};
type StakeRecord = record {
  staker : Account;
  token_id : text;
  amount : nat;
};
type TokenSpec = variant {
  ic : ICTokenSpec;
  extensible : CandyValue;
};
service : {
  balance_of_nft_origyn : (
      Account,
    ) -> (Result_19) query;
}

I call the canister the following way:

        ogy = Canister(agent=agent, canister_id=canister_id,
                       candid=ogy_candid)
        result = ogy.balance_of_nft_origyn({   # type: ignore
            'principal': principal,
        })

My call fails with the following error

Traceback (most recent call last):
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/discord/ext/tasks/__init__.py", line 239, in _loop
    await self.coro(*args, **kwargs)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/glue/discord_bot/bot.py", line 47, in check_ownership
    await verify_ownership_for_guild(guild)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/glue/discord_bot/helpers.py", line 32, in verify_ownership_for_guild
    if user_has_tokens(
  File "/Users/moritz/projects/ic/glue-org/glue-bot/glue/discord_bot/helpers.py", line 66, in user_has_tokens
    result = ogy.balance_of_nft_origyn({   # type: ignore
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/ic/canister.py", line 60, in __call__
    encode(arguments),
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/ic/candid.py", line 1263, in encode
    vals += t.encodeValue(args[i])
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/ic/candid.py", line 770, in encodeValue
    raise ValueError("Variant has no data: {}".format(val))
ValueError: Variant has no data: {'principal': 'ci6uj-i7ujw-wrp3j-6nfh6-ux3y4-hym5t-ruodk-flgjh-salcu-6nxa2-cae'}

What am I doing wrong?

letmejustputthishere commented 2 years ago

ok, i solved the issue! because of this type

type Account = variant {
  account_id : text;
  "principal" : principal;
  extensible : CandyValue;
};

i have to call

        ogy = Canister(agent=agent, canister_id=canister_id,
                       candid=ogy_candid)
        result = ogy.balance_of_nft_origyn({   # type: ignore
            '"principal"': principal,
        })

(note the extra double quotes)

imo opinion this is extremely inconvenient and shouldnt be necessary to do ...

letmejustputthishere commented 2 years ago

oh, and also i can't call the canister anymore. it errors out with

ogy IC0503: Canister s32s7-zqaaa-aaaaj-afksa-cai trapped explicitly: IDL error: unexpected variant tag
letmejustputthishere commented 2 years ago

i was able to work around it like this

        types = Types.Variant({'principal': Types.Principal})  # type: ignore
        val = {'principal': principal}

        params = [
            {
                'type': types,
                'value': val
            },
        ]

        result = agent.query_raw(
            canister_id, "balance_of_nft_origyn", encode(params))

but ideally i would be able to create the Canister with the provided did file instead ...

Myse1f commented 2 years ago

I think the key '"principal"' should be 'principal'. In candid 'principal' conflict with keyword principal, so it adds a quotation. But in python, principal is not a reserved word. Now you can remove the quotes and try again.

We will fix the problem in the near future.

letmejustputthishere commented 2 years ago

removing the quotes doesnt work :(

Traceback (most recent call last):
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/discord/ext/tasks/__init__.py", line 239, in _loop
    await self.coro(*args, **kwargs)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/glue/discord_bot/bot.py", line 47, in check_ownership
    await verify_ownership_for_guild(guild)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/glue/discord_bot/helpers.py", line 32, in verify_ownership_for_guild
    if user_has_tokens(
  File "/Users/moritz/projects/ic/glue-org/glue-bot/glue/discord_bot/helpers.py", line 66, in user_has_tokens
    result = ogy.balance_of_nft_origyn({   # type: ignore
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/ic/canister.py", line 60, in __call__
    encode(arguments),
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/ic/candid.py", line 1263, in encode
    vals += t.encodeValue(args[i])
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/ic/candid.py", line 770, in encodeValue
    raise ValueError("Variant has no data: {}".format(val))
ValueError: Variant has no data: {'principal': 'ci6uj-i7ujw-wrp3j-6nfh6-ux3y4-hym5t-ruodk-flgjh-salcu-6nxa2-cae'}

@Myse1f calling the canister with only principal doesnt work, see the above error

letmejustputthishere commented 2 years ago

i was able to work around it like this

        types = Types.Variant({'principal': Types.Principal})  # type: ignore
        val = {'principal': principal}

        params = [
            {
                'type': types,
                'value': val
            },
        ]

        result = agent.query_raw(
            canister_id, "balance_of_nft_origyn", encode(params))

but ideally i would be able to create the Canister with the provided did file instead ...

@Myse1f how can I add the correct keys to the result dictonary? right now it looks like this

[{'type': 'rec_4', 'value': {'_24860': {'_1224950711': [], '_2005185548': [], '_2215341626': [], '_2839967725': [], '_3672582405': []}}}]

any way to decode the result to have the correct keys? or are the numbers like _1224950711 constant and never change, so i can rely on hardcoding them?

Myse1f commented 2 years ago

When using query_raw, you can pass the return type as last parameter.

e.g

return_type = Types.record({
    "ok": ...
    “err”: ...
})
agent.query_raw(canister_id, "balance_of_nft_origyn", encode(params),  return_type)

Besides, the number never change unless it change the key, as it is something like hash.

letmejustputthishere commented 2 years ago

awesome, many thanks! looking forward to the fix :)

letmejustputthishere commented 1 year ago

even though this issue is closed, it's not resolved on my end. both calls with '"principal"' and 'principal' still fail with the same error messages as before. i'm using the main branch in my application.

can this be reopened?

Myse1f commented 1 year ago

Fix in #77. Now it should work

letmejustputthishere commented 1 year ago

i can confirm this works by just specifying 'principal'! thank you so much