Git Product home page Git Product logo

ic-py's People

Contributors

ccyanxyz avatar dfinity-skaestle avatar jim3333 avatar letmejustputthishere avatar mircial avatar myse1f avatar zkung avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ic-py's Issues

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

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?

Any abstraction available to handle interactions with the ledger canister?

I've seen code in JavaScript use a send_dfx method that will allow you to pass in text account addresses and other default values and then will wrap them up and send to the ledger canister to facilitate ICP transfers.

Is this on your roadmap at all?

I'm working on implementing an ICP transfer with the Python Agent and am trying to figure out if I should just interact with the raw canister (encoding all values to appropriate types myself) or if you had any plans to make it really easy. :)

ValueError: Not an option type

It's not possible to read the response from the ledger archive due to a bug when decoding the response.
Minimum example:

from ic.client import Client
from ic.agent import Agent
from ic.identity import Identity
from ic.common.ledger import Ledger
from ic import Canister

# Identity and Client are dependencies of Agent
iden = Identity()  # creates a random keypair
client = Client()  # creates a client to talk to the IC
# creates an agent, combination of client and identity
agent = Agent(iden, client)
# creates the ledger canister with the matching interface provided
ledger = Ledger(agent)

ledger_archive_did = open("archive.did").read()
ledger_archive = Canister(agent=agent,
                          canister_id="qjdve-lqaaa-aaaaa-aaaeq-cai",
                          candid=ledger_archive_did)

res = ledger_archive.get_blocks({"start": 10, "length": 10})

More info can be found in this thread

https://forum.dfinity.org/t/how-to-use-dfx-to-query-the-balance-of-an-account-identifier/15929/5?u=cryptoschindler

variant enum

for variant type like

 variant {
   a;
   b;
 };

use canister.xxx({"a": None}) returns AttributeError: 'NoneType' object has no attribute 'buildTypeTable'

ValueError: Missing type index for text

I have the following did file

type Canister = 
  service {
    tokens: (AccountIdentifier__1) -> (Result_1) query;
 };
type CommonError = 
  variant {
   InvalidToken: TokenIdentifier;
   Other: text;
};
type AccountIdentifier__1 = text;
type TokenIndex = nat32;
type TokenIdentifier = text;
type Result_1 = 
 variant {
   err: CommonError;
   ok: vec TokenIndex;
 };
service : () -> Canister

I setup the canister like this

# create agent
iden = Identity()
client = Client()
agent = Agent(iden, client)

ext_did = open("ext.did").read()

ext = Canister(
    agent, canister['canisterId'], candid=ext_did)

But making a call to ext.tokens(account) fails with the error mentioned above. Is this a bug or am I doing something wrong?

why is `0x` added to account ids?

when i stringify an account identifier, 0x is added. i have to manually remove because appliations on the IC don't work with the 0x prefix. can it be removed by default and added only when explicitly stated?

Candid Interface for Canisters - Cannot find field "principal"

Hi,

I'm receiving a "cannot find field 'principal' when I run the below code. It seems like the candid did is incorrect, but I've verified it multiple times. Any suggestions on troubleshooting? Thanks!

--------------------------------CODE---------------------------------
from ic.canister import Canister
from ic.client import Client
from ic.agent import Agent
from ic.candid import encode, decode, Types
from ic.identity import Identity

i1 = Identity()
client = Client(url = "https://ic0.app")
agent = Agent(i1, client)

params = []
params = encode(params)

canister_id = 'zqfso-syaaa-aaaaq-aaafq-cai'

response = agent.query_raw(canister_id,'__get_candid_interface_tmp_hack',params)
canister_did = response[0]['value']
canister = Canister(agent=agent, canister_id=canister_id, candid=canister_did)
---------------------------------------ERROR----------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\KyleLangham\PycharmProjects\ICPTransactions\SNS\test.py", line 20, in
my_canister.list_neurons({ 'of_principal': [], 'limit': 1000, 'start_page_at': []})
File "C:\Users\KyleLangham\PycharmProjects\ICPTransactions\venv\lib\site-packages\ic\canister.py", line 57, in call
res = self.agent.query_raw(
File "C:\Users\KyleLangham\PycharmProjects\ICPTransactions\venv\lib\site-packages\ic\agent.py", line 79, in query_raw
return decode(result['reply']['arg'], return_type)
File "C:\Users\KyleLangham\PycharmProjects\ICPTransactions\venv\lib\site-packages\ic\candid.py", line 1297, in decode
'value': t.decodeValue(b, types[i])
File "C:\Users\KyleLangham\PycharmProjects\ICPTransactions\venv\lib\site-packages\ic\candid.py", line 671, in decodeValue
x[expectKey] = exceptValue.decodeValue(b, v)
File "C:\Users\KyleLangham\PycharmProjects\ICPTransactions\venv\lib\site-packages\ic\candid.py", line 552, in decodeValue
rets.append(self._type.decodeValue(b, vec._type))
File "C:\Users\KyleLangham\PycharmProjects\ICPTransactions\venv\lib\site-packages\ic\candid.py", line 859, in decodeValue
return self._type.decodeValue(b, t)
File "C:\Users\KyleLangham\PycharmProjects\ICPTransactions\venv\lib\site-packages\ic\candid.py", line 671, in decodeValue
x[expectKey] = exceptValue.decodeValue(b, v)
File "C:\Users\KyleLangham\PycharmProjects\ICPTransactions\venv\lib\site-packages\ic\candid.py", line 552, in decodeValue
rets.append(self._type.decodeValue(b, vec._type))
File "C:\Users\KyleLangham\PycharmProjects\ICPTransactions\venv\lib\site-packages\ic\candid.py", line 859, in decodeValue
return self._type.decodeValue(b, t)
File "C:\Users\KyleLangham\PycharmProjects\ICPTransactions\venv\lib\site-packages\ic\candid.py", line 674, in decodeValue
raise ValueError("Cannot find field {}".format(keys[idx]))
ValueError: Cannot find field "principal"

Process finished with exit code 1

candid can't decode `opt principal`

test data

4449444c016e68010001011d779590d2cd339802981dfd935d9a3dbb085cafe6ad19b87229a016d602

use didc

>didc.exe decode 4449444c016e68010001011d779590d2cd339802981dfd935d9a3dbb085cafe6ad19b87229a016d602
(
  opt principal "expmt-gtxsw-inftj-ttabj-qhp5s-nozup-n3bbo-k7zvn-dg4he-knac3-lae",
)

use ic-py

raise "Cannot decode principal"
TypeError: exceptions must derive from BaseException

version: 0.0.8

Trying to get a simple example working

Hello! First off, I LOVE this project. I've been waiting for a Python agent for months! So good work!

Next, I pip installed and put together this small example

from ic.client import Client
from ic.identity import Identity
from ic.agent import Agent
from ic.candid import encode, decode

iden = Identity()
client = Client()
agent = Agent(iden, client)

name = agent.query_raw("hbi4x-wqaaa-aaaaj-aad7a-cai", "getRegistryCount", encode([]))

When I run that I get an error:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
/var/folders/yg/mzpfyl291vx30knxqlx43d2r0000gn/T/ipykernel_62725/2178616461.py in <module>
      8 agent = Agent(iden, client)
      9 
---> 10 name = agent.query_raw("hbi4x-wqaaa-aaaaj-aad7a-cai", "getRegistryCount", encode([]))

/opt/anaconda3/lib/python3.9/site-packages/ic/agent.py in query_raw(self, canister_id, method_name, arg)
     72         if result['status'] == 'replied':
     73             method_type = getType(method_name)
---> 74             arg = decode(method_type, result['reply']['arg'])
     75             return arg
     76         elif result['status'] == 'rejected':

/opt/anaconda3/lib/python3.9/site-packages/ic/candid.py in decode(retTypes, data)
   1147         # outputs[i.name] = i.decodeValue(b, i)
   1148         outputs.append({
-> 1149             'type': t.name,
   1150             'value': t.decodeValue(b, types[i])
   1151             })

AttributeError: 'NoneType' object has no attribute 'name'

Am I encoding empty parameters incorrectly? Or is the canister returning something incorrectly? It should just be querying (no params) to get the total number of NFTs minted, and then return that number.

Thanks, excited to hear back from you all.

Unstable result calling some Entrepot NFT canister

from ic.client import Client
from ic.identity import Identity
from ic.agent import Agent
from ic.candid import encode, decode, Types
#from struct import *

iden = Identity()
client = Client(url = "https://ic0.app")
agent = Agent(iden, client)

canister_id = '3mttv-dqaaa-aaaah-qcn6q-cai'
function_to_call = "getGenes"

params = []
params = encode(params)

res = agent.query_raw(canister_id, function_to_call, encode([]))

print(res)

This gives me either the error Traceback (most recent call last): File "..\traitchecker.py", line 17, in <module> res = agent.query_raw(canister_id, function_to_call, encode([])) File "C:\Python39\lib\site-packages\ic\agent.py", line 73, in query_raw if result['status'] == 'replied': TypeError: byte indices must be integers or slices, not str

Or the correct result (a shitload of data). Probably one out of 4 calls returns correct.

httpx.ReadTimeout errors

I'm getting a lot of httpx.ReadTimeout exceptions, is there anything that can be done to prevent this?

Traceback (most recent call last):
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/anyio/streams/tls.py", line 130, in _call_sslobject_method
    result = func(*args)
  File "/Users/moritz/.pyenv/versions/3.10.2/lib/python3.10/ssl.py", line 916, in read
    v = self._sslobj.read(len)
ssl.SSLWantReadError: The operation did not complete (read) (_ssl.c:2536)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/backends/asyncio.py", line 33, in read
    return await self._stream.receive(max_bytes=max_bytes)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/anyio/streams/tls.py", line 195, in receive
    data = await self._call_sslobject_method(self._ssl_object.read, max_bytes)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/anyio/streams/tls.py", line 137, in _call_sslobject_method
    data = await self.transport_stream.receive()
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/anyio/_backends/_asyncio.py", line 1265, in receive
    await self._protocol.read_event.wait()
  File "/Users/moritz/.pyenv/versions/3.10.2/lib/python3.10/asyncio/locks.py", line 213, in wait
    await fut
asyncio.exceptions.CancelledError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/_exceptions.py", line 8, in map_exceptions
    yield
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/backends/asyncio.py", line 31, in read
    with anyio.fail_after(timeout):
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/anyio/_core/_tasks.py", line 118, in __exit__
    raise TimeoutError
TimeoutError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpx/_transports/default.py", line 60, in map_httpcore_exceptions
    yield
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpx/_transports/default.py", line 353, in handle_async_request
    resp = await self._pool.handle_async_request(req)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/_async/connection_pool.py", line 253, in handle_async_request
    raise exc
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/_async/connection_pool.py", line 237, in handle_async_request
    response = await connection.handle_async_request(request)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/_async/connection.py", line 90, in handle_async_request
    return await self._connection.handle_async_request(request)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/_async/http11.py", line 105, in handle_async_request
    raise exc
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/_async/http11.py", line 84, in handle_async_request
    ) = await self._receive_response_headers(**kwargs)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/_async/http11.py", line 148, in _receive_response_headers
    event = await self._receive_event(timeout=timeout)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/_async/http11.py", line 177, in _receive_event
    data = await self._network_stream.read(
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/backends/asyncio.py", line 30, in read
    with map_exceptions(exc_map):
  File "/Users/moritz/.pyenv/versions/3.10.2/lib/python3.10/contextlib.py", line 153, in __exit__
    self.gen.throw(typ, value, traceback)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/_exceptions.py", line 12, in map_exceptions
    raise to_exc(exc)
httpcore.ReadTimeout

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/moritz/projects/ic/glue-org/glue-bot/glue/discord_bot/helpers.py", line 50, in verify_ownership_for_user
    tokens = await user_has_tokens(
  File "/Users/moritz/projects/ic/glue-org/glue-bot/glue/discord_bot/helpers.py", line 104, in user_has_tokens
    result = await icp_ledger.account_balance_async({'account' : account})  # type: ignore
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/ic/canister.py", line 97, in __call__
    res = await self.agent.query_raw_async(
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/ic/agent.py", line 93, in query_raw_async
    result = await self.query_endpoint_async(canister_id if effective_canister_id is None else effective_canister_id, data)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/ic/agent.py", line 46, in query_endpoint_async
    ret = await self.client.query_async(canister_id, data)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/ic/client.py", line 37, in query_async
    ret = await client.post(endpoint, data = data, headers=headers)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpx/_client.py", line 1842, in post
    return await self.request(
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpx/_client.py", line 1527, in request
    return await self.send(request, auth=auth, follow_redirects=follow_redirects)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpx/_client.py", line 1614, in send
    response = await self._send_handling_auth(
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpx/_client.py", line 1642, in _send_handling_auth
    response = await self._send_handling_redirects(
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpx/_client.py", line 1679, in _send_handling_redirects
    response = await self._send_single_request(request)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpx/_client.py", line 1716, in _send_single_request
    response = await transport.handle_async_request(request)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpx/_transports/default.py", line 352, in handle_async_request
    with map_httpcore_exceptions():
  File "/Users/moritz/.pyenv/versions/3.10.2/lib/python3.10/contextlib.py", line 153, in __exit__
    self.gen.throw(typ, value, traceback)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpx/_transports/default.py", line 77, in map_httpcore_exceptions
    raise mapped_exc(message) from exc
httpx.ReadTimeout
2022-08-24 15:50:23 INFO     discord start
2022-08-24 15:50:28 ERROR    discord error checking ownership
Traceback (most recent call last):
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/anyio/streams/tls.py", line 130, in _call_sslobject_method
    result = func(*args)
  File "/Users/moritz/.pyenv/versions/3.10.2/lib/python3.10/ssl.py", line 974, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLWantReadError: The operation did not complete (read) (_ssl.c:997)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/backends/asyncio.py", line 67, in start_tls
    ssl_stream = await anyio.streams.tls.TLSStream.wrap(
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/anyio/streams/tls.py", line 122, in wrap
    await wrapper._call_sslobject_method(ssl_object.do_handshake)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/anyio/streams/tls.py", line 137, in _call_sslobject_method
    data = await self.transport_stream.receive()
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/anyio/_backends/_asyncio.py", line 1265, in receive
    await self._protocol.read_event.wait()
  File "/Users/moritz/.pyenv/versions/3.10.2/lib/python3.10/asyncio/locks.py", line 213, in wait
    await fut
asyncio.exceptions.CancelledError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/_exceptions.py", line 8, in map_exceptions
    yield
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/backends/asyncio.py", line 76, in start_tls
    raise exc
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/backends/asyncio.py", line 66, in start_tls
    with anyio.fail_after(timeout):
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/anyio/_core/_tasks.py", line 118, in __exit__
    raise TimeoutError
TimeoutError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpx/_transports/default.py", line 60, in map_httpcore_exceptions
    yield
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpx/_transports/default.py", line 353, in handle_async_request
    resp = await self._pool.handle_async_request(req)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/_async/connection_pool.py", line 253, in handle_async_request
    raise exc
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/_async/connection_pool.py", line 237, in handle_async_request
    response = await connection.handle_async_request(request)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/_async/connection.py", line 86, in handle_async_request
    raise exc
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/_async/connection.py", line 63, in handle_async_request
    stream = await self._connect(request)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/_async/connection.py", line 150, in _connect
    stream = await stream.start_tls(**kwargs)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/backends/asyncio.py", line 64, in start_tls
    with map_exceptions(exc_map):
  File "/Users/moritz/.pyenv/versions/3.10.2/lib/python3.10/contextlib.py", line 153, in __exit__
    self.gen.throw(typ, value, traceback)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpcore/_exceptions.py", line 12, in map_exceptions
    raise to_exc(exc)
httpcore.ConnectTimeout

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/moritz/projects/ic/glue-org/glue-bot/glue/discord_bot/helpers.py", line 50, in verify_ownership_for_user
    tokens = await user_has_tokens(
  File "/Users/moritz/projects/ic/glue-org/glue-bot/glue/discord_bot/helpers.py", line 104, in user_has_tokens
    result = await icp_ledger.account_balance_async({'account' : account})  # type: ignore
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/ic/canister.py", line 97, in __call__
    res = await self.agent.query_raw_async(
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/ic/agent.py", line 93, in query_raw_async
    result = await self.query_endpoint_async(canister_id if effective_canister_id is None else effective_canister_id, data)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/ic/agent.py", line 46, in query_endpoint_async
    ret = await self.client.query_async(canister_id, data)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/ic/client.py", line 37, in query_async
    ret = await client.post(endpoint, data = data, headers=headers)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpx/_client.py", line 1842, in post
    return await self.request(
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpx/_client.py", line 1527, in request
    return await self.send(request, auth=auth, follow_redirects=follow_redirects)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpx/_client.py", line 1614, in send
    response = await self._send_handling_auth(
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpx/_client.py", line 1642, in _send_handling_auth
    response = await self._send_handling_redirects(
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpx/_client.py", line 1679, in _send_handling_redirects
    response = await self._send_single_request(request)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpx/_client.py", line 1716, in _send_single_request
    response = await transport.handle_async_request(request)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpx/_transports/default.py", line 352, in handle_async_request
    with map_httpcore_exceptions():
  File "/Users/moritz/.pyenv/versions/3.10.2/lib/python3.10/contextlib.py", line 153, in __exit__
    self.gen.throw(typ, value, traceback)
  File "/Users/moritz/projects/ic/glue-org/glue-bot/.venv/lib/python3.10/site-packages/httpx/_transports/default.py", line 77, in map_httpcore_exceptions
    raise mapped_exc(message) from exc
httpx.ConnectTimeout

Trying to call "lock" method on canister and getting typeTable error

I am trying to execute the "lock" method of an NFT canister (a marketplace method). Here is my code so far:

canister_id = 'tde7l-3qaaa-aaaah-qansa-cai'
function_to_call = 'lock'
price = int(0.01E8)
params = [
    {'type':Types.Text,'value':'kw6gv-vykor-uwiaa-aaaaa-b4adm-qaqca-aau3d-a'}, #tokenId
    {'type':Types.Nat64,'value':price}, #price
    {'type':Types.Text,'value':'485d6d59c0b58fea568b339dccd63106422c5404f684bcf6d55f4cbccd49ac6d'}, #wallet address
    {'type':Types.Vec,'value':[0]}
]
params = encode(params)

result = agent.update_raw(canister_id, function_to_call, params)

And then when I run that, I get a really quick error about the function object not having an attribute "buildTypeTable". Error below.

AttributeError                            Traceback (most recent call last)
/var/folders/yg/mzpfyl291vx30knxqlx43d2r0000gn/T/ipykernel_72643/2348097593.py in <module>
     10     {'type':Types.Vec,'value':[0]}
     11 ]
---> 12 params = encode(params)
     13 
     14 result = agent.update_raw(canister_id, function_to_call, params)

/opt/anaconda3/lib/python3.9/site-packages/ic/candid.py in encode(params)
   1101     typetable = TypeTable()
   1102     for item in argTypes:
-> 1103         item.buildTypeTable(typetable)
   1104 
   1105     pre = prefix.encode()

AttributeError: 'function' object has no attribute 'buildTypeTable'

I'm assuming the Vec encoding class hasn't been implemented yet for sending update calls? This is just a guess. I'm sure you know better than I do.

I also tried running the request without the vec option (read somewhere that it was optional), but then I run into a response decode error. So I'm back with more questions.

Thank you!

Retrieving structured data

Maybe I'm doing this wrong, but when calling a method that returns a list such as below, the response comes back as an unstructured list

record = Types.Record({
    'include_reward_status':Types.Vec(Types.Int32),
    'before_proposal':Types.Null,
    'limit':Types.Nat32,
    'exclude_topic':Types.Vec(Types.Int32),
    'include_status':Types.Vec(Types.Int32),
})
params = [
	{
        'type': record, 
        'value':{
            'include_reward_status': [], 
            'before_proposal': None, 
            'limit': 100, 
            'exclude_topic': [], 
            'include_status': [1]
        }
}]

res = agent.query_raw("rrkah-fqaaa-aaaaa-aaaaq-cai", "list_proposals", encode(params))
pprint(res)

results in

[{'type': 3871088705,
  'value': {'_3720987355': [{'_100394802': 1,
                             '_1120297033': 1644281103,
                             '_1138543385': 0,
                             '_1380048431': [1644626703],
                             '_1453869204': 0,
                             '_1659864270': 100000000,
                             '_2084260468': [{'_24641': 83739340113,
                                              '_338842564': 40551870688681598,
                                              '_4174818006': 1644301268,
                                              '_6039847': 16091166445927}],
                             '_2139208002': 1,
                             '_23515': [{'_23515': 43574}],
                             '_2756235859': 0,
                             '_3000310834': [{'_2162756390': '## Proposal to '
                                                             'Upgrade the '
                                                             'Registry '
                                                             'Canister\n'
                                                             '\n'
                                                             '### Proposer: '
                                                             'Dfinity '
                                                             'Foundation\n'
                                                             '### Git Hash: '
                                                             'ed721ffdde4b1d4d982d0c843a0b9e293380e05b\n'
                                                             '### New Wasm '
                                                             'Hash: '
                                                             '3cc60f5bee9f555258a5bfb648ff16e7441bc4920383c1127c9bd564eccfc848\n'
                                                             '### Target '
                                                             'canister: '
                                                             'rwlgt-iiaaa-aaaaa-aaaaa-cai\n'
                                                             '\n'
                                                             '---\n'
                                                             '## Release '
                                                             'Notes\n'
                                                             '\n'
                                                             '2c560f3cf2c5e36bdbf5edbd148cb28f72db24b7 '
                                                             'Merge branch '
                                                             "'egeyar/2022-02-02/Add-removed-fields-back-to-BlessReplicaVersionPayload' "

Is there a way to get this library to return a dictionary with indexed items corresponding to what I see here?

https://bmht6-iiaaa-aaaad-qabeq-cai.raw.ic0.app/principal/rrkah-fqaaa-aaaaa-aaaaq-cai

Missing 2 required positional arguments

What are the current limitations of this wonderful WIP Python Agent? I am eager to get things working but am running into a few issues with some canister calls.

This is the error I keep getting
TypeError: getType() missing 2 required positional arguments: 'table' and 't'

When I run

from ic.client import Client
from ic.identity import Identity
from ic.agent import Agent
from ic.candid import encode, decode

iden = Identity()
client = Client(url = "https://ic0.app")
agent = Agent(iden, client)

result = agent.query_raw("hbi4x-wqaaa-aaaaj-aad7a-cai", "getRegistryCount", encode([]))

It works. The getRegistryCount method works.

However, if I change the last line to:
result = agent.query_raw("hbi4x-wqaaa-aaaaj-aad7a-cai", "getRegistry", encode([]))
just get the registry rather than the count, it appears it has an issue returning a vec or something like that.

I'm assuming this is all known, I'm just exploring what I can and can't do right now. (again, eager to use it as it becomes available)

how to send records similar to dfx

Hi I have a problem using ic-py library.

I would like to mint canister tokens and send request similar to

dfx canister --network ${NETWORK} call ${CANISTER_ADDRESS} transfer '(record {
  memo = 0 : nat64;
  amount = record { e8s = 100 : nat64 };
  fee = record { e8s = 0 : nat64 };
  from_subaccount = null;
  to = '${TOKENS_TRANSFER_ACCOUNT_ID_BYTES}' : vec nat8;
  created_at_time = null;
})'

But I am not sure how to use ic-py params to fulfill my needs. It would be ideal to do something similar to

params = {
	"memo": 0,
	"amount": { "e8s": 100 },
	"fee": { "e8s": 0 },
	"from_subaccount": None,
	"to": ["${TOKENS_TRANSFER_ACCOUNT_ID_BYTES}"],
	"created_at_time": None,
}
result = agent.update_raw("${CANISTER_ADDRESS}", "transfer", encode(params))
print(result)

If you have any idea what approach could I take, please share.
Library documentation has only few simple examples and so I couldn't get it done yet

Deduplication of identical update calls

The Internet Computer Protocol has deduplication of update calls built into the protocol. This means if you send the same update call to the same canister with the same parameters only one request will process instead of all of them (the rest being deduplicated).

In some cases this is undesirable (for example a payment flow where you request a unique escrow address for each payment and you never want to return the same escrow address more than once).

The nonce and the expiration time are part of the call, so if we vary the nonce or the expiration time on the agent side, then you can prevent deduplication of calls.

Is there an easy way to vary the nonce or expiration time of an update call through ic-py? I saw in the agent creation that there is a nonce_factory parameter, but I couldn't find any documentation on what that is or how to modify it to make it work for me.

Thanks!

Authenticate with Internet Identity private key

Hello, not sure if this is possible, but figured I would ask anyway.

I can find my Internet Identity keys in local storage. If I take the first 32 bytes, then I get a nice ed25519 key that I can import with Identity("ed25519_key_I_took_from_local_storage"). This works and the identity is imported. When I create the agent with the client, I can call agent.get_principal(), but I can't get the agent.get_principal() to match up with the principal that I actually have in an app when I login with Internet Identity. Even if I change the client (to perhaps the domain of the website I was logging in to), the principal I get from agent.get_principal() stays the same.

Am I missing something here? Or am I trying to do something that isn't actually possible? Would love to know how to authenticate with an existing Internet Identity, and perhaps my strategy is flawed somehow.

Thank you!

When I get a response the keys are underscored numbers, not values

For example, in a response, instead of a nice candid value, I get an underscored prefixed number. Like this:

{
'_220804107': 'https://web.whatsapp.com/',
'_272465847': 1648359818507890360,
'_509026302': 'Japan'
}

Is there a way to automatically parse those key values as part of the Python agent? Is there some functionality with decode that I am missing? Is there a way to manually decode these key values?

Thank you!

Canister trapped explicitly: IDL error: illegal type table

I'm transferring $ghost token using ic-py.

If I pull candid from the canister and use the canister interface, I get an error: "IDL error: illegal type table"

---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
/var/folders/yg/mzpfyl291vx30knxqlx43d2r0000gn/T/ipykernel_33617/294065910.py in <module>
----> 1 my_canister.transfer(
      2     {
      3     'amount':int(0.1e8),
      4     'from':{'address':'485d6d59c0b58fea568b339dccd63106422c5404f684bcf6d55f4cbccd49ac6d'},
      5     'memo':[],

/opt/anaconda3/lib/python3.9/site-packages/ic/canister.py in __call__(self, *args, **kwargs)
     63                 )
     64         else:
---> 65             res = self.agent.update_raw(
     66                 self.canister_id,
     67                 self.name,

/opt/anaconda3/lib/python3.9/site-packages/ic/agent.py in update_raw(self, canister_id, method_name, arg, return_type, effective_canister_id, **kwargs)
    114         status, result = self.poll(eid, req_id, **kwargs)
    115         if status == 'rejected':
--> 116             raise Exception('Rejected: ' + result.decode())
    117         elif status == 'replied':
    118             return decode(result, return_type)

Exception: Rejected: Canister fjbi2-fyaaa-aaaan-qanjq-cai trapped explicitly: IDL error: illegal type table

However, if I define the type table myself, it works just fine.

canister_id = 'fjbi2-fyaaa-aaaan-qanjq-cai'

types = Types.Record({
    'amount':Types.Nat,
    'from':Types.Variant({
        'address':Types.Text,
        'principal':Types.Principal
    }),
    'memo':Types.Vec(Types.Nat8),
    'nonce':Types.Opt(Types.Nat),
    'notify':Types.Bool,
    'subaccount':Types.Opt(Types.Vec(Types.Nat8)),
    'to':Types.Variant({
        'address':Types.Text,
        'principal':Types.Principal
    }),
    'token':Types.Text
})

values = {
        'amount':int(0.1e8),
        'from':{'address':''},
        'memo':[],
        'nonce':[],
        'notify':False,
        'subaccount':[],
        'to':{'address':''},
        'token':''
}
params = [{'type':types,'value':values}]
params = encode(params)
response = agent.update_raw(canister_id,'transfer',params)

So not sure what is wrong here. Maybe it has to do with the "blob" type that is returned in the candid? I can't figure out why it won't work for me to use the candid I get back from the canister when I run __get_candid_interface_tmp_hack

Would love for you to take a look. Thanks!

Can't handle None

This is the correct candid. It was indicated in the error result. My problem is how to handle None.
I wrote params, but I am unsure how to write None in params.

record {
    quoted_resource : variant {
        Article : nat64;
        None;                # This is the point. 
        Post : nat64;
        Comment : nat64;
    };

    extension : opt variant {
        Poll : record {
        votes_per_user : nat8;
        poll_duration_in_minutes : nat16;
        options : vec text;
        };
    };
}

I used this empty string ' ' as a key for the variable name of type "Types.Null"(None) for the first, but it was not allowed. I have no idea. Could you tell me what key should be set for Types.Null? Tentatively set to 'Null', but it dose not work now.

types = Types.Record({
    'quoted_resource':Types.Variant({
        'Article':Types.Nat64,
        'Null': Types.Null,        # This is the point. 
        'Post' :Types.Nat64,
        'Comment':Types.Nat64,
    }),

    'extension':Types.Opt(Types.Variant({
        'Poll':Types.Record({
            'votes_per_user' :Types.Nat8,
            'poll_duration_in_minutes' :Types.Nat16,
            'options': Types.Vec(Types.Text),
        })
    }))
})

values = {
    'quoted_resource': {'Null':None},      # This is the point. 
    'extension': []
}

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

Import Stoic Identity via seed phrase

I know I can export PEM from plug and I can import using Identity.from_pem(private_key). But I was wondering if it was possible to import a Stoic identity via seed phrase. Something like Identity.from_seed(mnemonic). I didn't see anything like this when I looked at methods possible with Identity, so wanted to see if this was going to be supported.

Thanks!

candid interface file

Thanks for good library. I have some questions about candid interface file.
README.md

  1. Canister
    Create a canister instance with candid interface file and canister id, and call canister method with canister instance:
  1. How is creating an instance from a candid file and calling canister different from other common a query_call or update_call that does not use candid files?

  2. In the case that a Dapp has no candid file, is there any solution? If ic-py can't do it, can an agent in another language do it?

License

Which license are you using? I did not see a license file that was selected.

Error when sending cycles from cycles wallet to canister

Error

---------------------------------------------------------------------------
CBORDecodeValueError                      Traceback (most recent call last)
/var/folders/yg/mzpfyl291vx30knxqlx43d2r0000gn/T/ipykernel_27050/2334640681.py in <module>
     44             params = encode(params)
     45             print(f'topping up {collection_name}, only has {canister_cycles/1e12}T cycles, adding 15T cycles.')
---> 46             response = agent.update_raw(cycles_wallet_canister_id,'wallet_call',params)

/opt/anaconda3/lib/python3.9/site-packages/ic/agent.py in update_raw(self, canister_id, method_name, arg, return_type, effective_canister_id, **kwargs)
    112         _ = self.call_endpoint(eid, req_id, data)
    113         # print('update.req_id:', req_id.hex())
--> 114         status, result = self.poll(eid, req_id, **kwargs)
    115         if status == 'rejected':
    116             raise Exception('Rejected: ' + result.decode())

/opt/anaconda3/lib/python3.9/site-packages/ic/agent.py in poll(self, canister_id, req_id, delay, timeout)
    200         status = None
    201         for _ in wait(delay, timeout):
--> 202             status, cert = self.request_status_raw(canister_id, req_id)
    203             if status == 'replied' or status == 'done' or status  == 'rejected':
    204                 break

/opt/anaconda3/lib/python3.9/site-packages/ic/agent.py in request_status_raw(self, canister_id, req_id)
    179             ['request_status'.encode(), req_id],
    180         ]
--> 181         cert = self.read_state_raw(canister_id, paths)
    182         status = lookup(['request_status'.encode(), req_id, 'status'.encode()], cert)
    183         if (status == None):

/opt/anaconda3/lib/python3.9/site-packages/ic/agent.py in read_state_raw(self, canister_id, paths)
    154         elif ret == b'Could not parse body as read request: invalid type: byte array, expected a sequence':
    155             raise ValueError('Could not parse body as read request: invalid type: byte array, expected a sequence')
--> 156         d = cbor2.loads(ret)
    157         cert = cbor2.loads(d['certificate'])
    158         return cert

CBORDecodeValueError: unknown unsigned integer subtype 0x1c

And my code:

    canister_to_receive_cycles = collection['id']
    collection_name = collection['name']
    params = encode([])
    result = agent.query_raw(canister_to_receive_cycles, 'availableCycles', params)
    if 'availableCycles' in result:
        continue # no availableCycles method on the canister
    else:
        canister_cycles = result[0]['value']
        if canister_cycles / 1e12 > 20:
            continue # don't need to top it up
        else:
            # TOP UP TIME
            # Send cycles from cycles wallet to canister
            cycles_wallet_canister_id = 'xxxxx-xxxxx-xxxxx'
            cycles_to_send = int(15e12) # 0.3T cycles is 0.3e12

            types = Types.Record({
                'args': Types.Vec(Types.Nat8),
                'cycles': Types.Nat64,
                'method_name': Types.Text,
                'canister': Types.Principal
            })

            values = {
                'args':encode([
                    {'type':Types.Record({'canister_id':Types.Principal}),
                     'value':{'canister_id':canister_to_receive_cycles}}]),
                'cycles':cycles_to_send,
                'method_name':'deposit_cycles',
                'canister':'aaaaa-aa'
            }
            params = [{'type':types,'value':values}]
            params = encode(params)
            print(f'topping up {collection_name}, only has {canister_cycles/1e12}T cycles, adding 15T cycles.')
            response = agent.update_raw(cycles_wallet_canister_id,'wallet_call',params)

I have the latest version of ic-py (git pulled and pip installed in the git repo). Any ideas why I might be seeing this unsigned int subtype cbor error parsing the response?

Would be nice to use it locally for testing

This is not really an issue. I tried to use it locally by using: client = Client(url="http://127.0.0.1:8000"), but this does not work. I am writing Motoko and python programs, but I do not fully understand the internals of the ic. I guess it is not sufficient to just change the url of the client. Any hints?

TypeError: object of type 'NoneType' has no len()

When I run this code

from ic.client import Client
from ic.identity import Identity
from ic.agent import Agent
from ic.candid import encode, Types
import time
import schedule
import sys

# params is an array, return value is encoded bytes
# params = [{'type': Types.Nat, 'value': 10}]
# data = encode(params)

# Identity and Client are dependencies of Agent
iden = Identity()  # creates a random keypair
client = Client()  # creates a client to talk to the IC
# creates an agent, combination of client and identity
agent = Agent(iden, client)


def main():
    backup(canister_id=sys.argv[1])
    schedule.every(5).seconds.do(backup, canister_id=sys.argv[1])

    while True:
        schedule.run_pending()
        time.sleep(1)


def backup(canister_id):
    print(f"disburse completed at {time.ctime()}")
    # query the NFT canister
    # doesnt change after calling `shuffleAssets`
    result = agent.update_raw(
        canister_id, "disburse", encode([]))
        
    print(result) 

get the following error after a couple of iterations:

Traceback (most recent call last):
  File "/home/moritz/.venv/bin/backup", line 8, in <module>
    sys.exit(main())
  File "/home/moritz/.venv/lib/python3.8/site-packages/state_backup/backup.py", line 25, in main
    schedule.run_pending()
  File "/home/moritz/.venv/lib/python3.8/site-packages/schedule/__init__.py", line 780, in run_pending
    default_scheduler.run_pending()
  File "/home/moritz/.venv/lib/python3.8/site-packages/schedule/__init__.py", line 100, in run_pending
    self._run_job(job)
  File "/home/moritz/.venv/lib/python3.8/site-packages/schedule/__init__.py", line 172, in _run_job
    ret = job.run()
  File "/home/moritz/.venv/lib/python3.8/site-packages/schedule/__init__.py", line 661, in run
    ret = self.job_func()
  File "/home/moritz/.venv/lib/python3.8/site-packages/state_backup/backup.py", line 33, in backup
    result = agent.update_raw(
  File "/home/moritz/.venv/lib/python3.8/site-packages/ic/agent.py", line 92, in update_raw
    return decode(result)
  File "/home/moritz/.venv/lib/python3.8/site-packages/ic/candid.py", line 1123, in decode
    b = Pipe(data)
  File "/home/moritz/.venv/lib/python3.8/site-packages/ic/candid.py", line 45, in __init__
    self._view = buffer[0:len(buffer)]
TypeError: object of type 'NoneType' has no len()

Example for Pem Identity import?

Do you have an example for how to import a pem identity? I have exported a dfx identity from Plug Wallet, which downloaded as identity.pem. Now I am trying to import private key. Here is the error I get:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/var/folders/yg/mzpfyl291vx30knxqlx43d2r0000gn/T/ipykernel_62451/1169095691.py in <module>
      2     private_key_1 = f.read()
      3 from ic.identity import Identity
----> 4 i1 = Identity.from_pem(private_key_1)

/opt/anaconda3/lib/python3.9/site-packages/ic/identity.py in from_pem(pem)
     34     def from_pem(pem: str):
     35         key = load_pem_private_key(pem.encode(), password=None)
---> 36         privkey = key.private_bytes(encoding=Encoding.Raw, format=PrivateFormat.Raw, encryption_algorithm=NoEncryption()).hex()
     37         return Identity(privkey=privkey, type='ed25519')
     38 

/opt/anaconda3/lib/python3.9/site-packages/cryptography/hazmat/backends/openssl/ec.py in private_bytes(self, encoding, format, encryption_algorithm)
    230         encryption_algorithm: serialization.KeySerializationEncryption,
    231     ) -> bytes:
--> 232         return self._backend._private_key_bytes(
    233             encoding,
    234             format,

/opt/anaconda3/lib/python3.9/site-packages/cryptography/hazmat/backends/openssl/backend.py in _private_key_bytes(self, encoding, format, encryption_algorithm, key, evp_pkey, cdata)
   1471         # Anything that key-specific code was supposed to handle earlier,
   1472         # like Raw.
-> 1473         raise ValueError("format is invalid with this key")
   1474 
   1475     def _private_key_bytes_via_bio(self, write_bio, evp_pkey, password):

ValueError: format is invalid with this key

And then here is the code I am running.

from ic.identity import Identity

with open('/Users/bob/Downloads/identity.pem','r') as f:
    private_key_1 = f.read()

i1 = Identity.from_pem(private_key_1)

I commented this on the old issue thinking it would reopen it, but it didn't open it, so I'm commenting again here. Thanks!

differentiate between valid result and IC error

it would be great if i had a way to check wether or not a canister return a result or trapped for example.
if the canister traps, i get a string instead of the expected result. but if both return types are strings, differentiating becomes cumbersome...

ogy IC0503: Canister s32s7-zqaaa-aaaaj-afksa-cai trapped explicitly: IDL error: unexpected variant tag

"field id collision or not sorted" error

Attempting to get the details of a proposal

iden = Identity()
client = Client()
agent = Agent(iden, client)
params = [
	{'type': Types.Nat64, 'value': 43117}
]
name = agent.query_raw("rrkah-fqaaa-aaaaa-aaaaq-cai", "get_proposal_info", encode(params))

results in the error

candid.py", line 977, in readTypeTable
    raise "field id collision or not sorted"
TypeError: exceptions must derive from BaseException

I see that from debugging the IC has returned a response to my request, but the error occurs in decoding.

Does this Python agent support multiple variants in the same request?

I'm getting some type errors when using multiple variants in the same request. For example, I have

key_type : variant { platform; seed_phrase; cross_platform; unknown };
purpose : variant { authentication; recovery };

But in the error, it returns the type table, and I only have one type definition that is variant:

type table1 = variant { 1_744_417_459; 3_053_362_247; 3_484_244_626; 3_782_943_626 }

Notice how in the first case, I have two different types of variants (length 4 and length 2 with different values), but in the error, it groups them into a common type of table1, with length 4.

I would have expected two different types for the two different variants, but this is not the case.

Is this user error on my part in defining types incorrectly? Or is there something weird going on with parsing the variant types? Thank you!

provide interface according to candid provided

would be nice if i had access to the method available on a canister and their signatures when i define an actor like this:

  ogy = Canister(agent=agent, canister_id=canister_id,
                    candid=ogy_candid)

Inline candid definitions aren't recognized

I have noticed that your candid parser doesn't parse types/values in candid if they are defined inline. So I have had to pull candid files and manually edit (pull out all inline definitions to define them normally) because they aren't pick up by the candid parser.

This is an enhancement that would be awesome to see.

cant transfer

i transferred 19000 coins and 10000 fee with the update_raw function and the given status is replied and there is not any error, but the transaction is not in the network
I tried it many times and checked the balance, do you have any idea?

Struggling encoding params

Hello. Let me ask you a question.I am building a program to automate social networking posts.
However, I'm struggling to understand how to encode the params correctly for a types record.

The picture shows the correct parameters returned in Invalid record error.
Screen Shot 2022-06-23 at 18 53 40

These are the parameters I set.
Can you please tell me if there are any mistakes?

types = Types.Record({
        'quoted_resource':Types.Nat64,
        'user_tags': Types.Vec(Types.Text),
        'nft_image_url':Types.Opt(Types.Text),
        'hashtags':Types.Vec(Types.Text),
        '"text"':Types.Text,
        'image':Types.Opt(Types.Record({'canister_id':Types.Principal,'image_id':Types.Text,'timestamp':Types.Nat64,'format':Types.Text})),
    }
)

values = {
        'quoted_resource':0,
        'user_tags':[], 
        'nft_image_url':"",
        'hashtags':[],
        '"text"':"Hello.How are you doing?",
        'image':[],
}

Thanks for the great library.
Sincerely,

exceptions must derive from BaseException

I might be doing something wrong here. I'm getting this error with query_raw method. This is how i'm using it:

agent.query_raw(canisterId, "getList", encode([]))

& i'm getting response:

exceptions must derive from BaseException

It appears that underscores as keys in a record break the encode function

# Record Test
types = Types.Record({
        'to':Types.Text,
        'fee': Types.Int,
        'memo':Types.Nat64,
        'fromsubaccount':Types.Text,
        #'created_at_time':Types.Record({'timestamp_nanos':Types.Nat64}),
        'amount':Types.Nat64,
    }
)
vals = {
        'to':wallet_address_to_send_ICP,
        'fee': 10000,
        'memo':0,
        'fromsubaccount':'asdf',#convert_wallet_address_to_bytes_array(purchasing_wallet_address)],
        #'created_at_time':[],
        'amount':price,
}
params = [
    {'type': types, 'value': vals}
]
params = encode(params)

The code above works, but if you include an underscore in "from_subaccount" like so, then it gives you an error. I think the underscore is being parsed incorrectly.

Struggling encoding params

Hello, I'm struggling to understand how to encode the params correctly for a types record. I'm trying to query the list_neurons method of 'rrkah-fqaaa-aaaaa-aaaaq-cai' (canlista). I've tried multiple combinations for the encoding, but receive errors similar to below.

`from ic.client import Client
from ic.identity import Identity
from ic.agent import Agent
from ic.candid import encode, decode, Types

iden = Identity()
client = Client(url = "https://ic0.app")
agent = Agent(iden, client)
params = [{'type': Types.Record({'name':Types.Text, 'assets': Types.Nat}), 'value': {'name': 'neuron_ids','assets': 4009}},{'type': Types.Bool, 'value': True}]
params = encode(params)
can_id = 'rrkah-fqaaa-aaaaa-aaaaq-cai'
function = 'list_neurons'
response = agent.query_raw(can_id, function, params)
print(response)
`

Error:
IC0503: Canister rrkah-fqaaa-aaaaa-aaaaq-cai trapped explicitly: Panicked at 'Deserialization Failed: "Fail to decode argument 0 from table0 to record { neuron_ids : vec nat64; include_neurons_readable_by_caller : bool }"', /ic/rs/rust_canisters/dfn_core/src/endpoint.rs:34:41

validate signature

it would be nice if we could verify signatures that were created with the dfinity agent

Support async request

Currently, query_raw, update_raw is a sync function that blocks the process.

Use aiohttp to perform HTTP requests and change it to async style.

Exception thrown 'NoneType' object has no attribute 'name'

Running name = agent.query_raw("gvbup-jyaaa-aaaah-qcdwa-cai", "name", encode([])) works.
It provides the response:
[{'type': 'text', 'value': 'XTC Test'}]

Changing the method from name to symbol as follows:
name = agent.query_raw("gvbup-jyaaa-aaaah-qcdwa-cai", "symbol, encode([])) works in DFX and returns ("XTC").

In python, it generates an exception:

Exception has occurred: AttributeError
'NoneType' object has no attribute 'name'
File "C:\Users\trrei\py\Untitled-1.py", line 14, in
name = agent.query_raw("gvbup-jyaaa-aaaah-qcdwa-cai", "symbol", encode([]))

Both the name and symbol methods are text fields per the info in ic.rocks.

Exceptions must derive from BaseException error

I have a friend who is able to run this exact code, but it gives me this error. I'm running from Jupiter Notebooks through Anaconda.

This is the code:

canister_id = '3mttv-dqaaa-aaaah-qcn6q-cai'

from ic.client import Client
from ic.identity import Identity
from ic.agent import Agent
from ic.candid import encode, decode, Types

iden = Identity()
client = Client(url = "https://ic0.app")
agent = Agent(iden, client)
params = []
params = encode(params)
result = agent.query_raw(canister_id, "listings", params)

And this is the error trace. Any help would be appreciated!

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-3-1481165a8320> in <module>
      4 params = []
      5 params = encode(params)
----> 6 result = agent.query_raw(canister_id, "listings", params)

/opt/anaconda3/lib/python3.7/site-packages/ic/agent.py in query_raw(self, canister_id, method_name, arg)
     72         result = self.query_endpoint(canister_id, data)
     73         if result['status'] == 'replied':
---> 74             arg = decode(result['reply']['arg'])
     75             return arg
     76         elif result['status'] == 'rejected':

/opt/anaconda3/lib/python3.7/site-packages/ic/candid.py in decode(data, retTypes)
   1149         outputs.append({
   1150             'type': t.name,
-> 1151             'value': t.decodeValue(b, types[i])
   1152             })
   1153 

/opt/anaconda3/lib/python3.7/site-packages/ic/candid.py in decodeValue(self, b, t)
    870             raise "Recursive type uninitialized"
    871         else:
--> 872             return self._type.decodeValue(b, t)
    873 
    874     @property

/opt/anaconda3/lib/python3.7/site-packages/ic/candid.py in decodeValue(self, b, t)
    566         rets = []
    567         for _ in range(length):
--> 568             rets.append(self._type.decodeValue(b, vec._type))
    569         return rets
    570 

/opt/anaconda3/lib/python3.7/site-packages/ic/candid.py in decodeValue(self, b, t)
    870             raise "Recursive type uninitialized"
    871         else:
--> 872             return self._type.decodeValue(b, t)
    873 
    874     @property

/opt/anaconda3/lib/python3.7/site-packages/ic/candid.py in decodeValue(self, b, t)
    746                 wireType.decodeValue(b, wireType)
    747             else:
--> 748                 res.append(self._components[i].decodeValue(b, wireType))
    749         return res
    750 

/opt/anaconda3/lib/python3.7/site-packages/ic/candid.py in decodeValue(self, b, t)
    870             raise "Recursive type uninitialized"
    871         else:
--> 872             return self._type.decodeValue(b, t)
    873 
    874     @property

/opt/anaconda3/lib/python3.7/site-packages/ic/candid.py in decodeValue(self, b, t)
    685             expectKey = keys[idx]
    686             exceptValue = self._fields[expectKey]
--> 687             x[expectKey] = exceptValue.decodeValue(b, v)
    688             idx += 1
    689         if idx < len(self._fields):

/opt/anaconda3/lib/python3.7/site-packages/ic/candid.py in decodeValue(self, b, t)
    870             raise "Recursive type uninitialized"
    871         else:
--> 872             return self._type.decodeValue(b, t)
    873 
    874     @property

/opt/anaconda3/lib/python3.7/site-packages/ic/candid.py in decodeValue(self, b, t)
    610             return [self._type.decodeValue(b, opt._type)]
    611         else:
--> 612             raise "Not an option value"
    613 
    614     @property

TypeError: exceptions must derive from BaseException

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.