Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Map.toString() throwing Exception: An unhandled exception was thrown. Key not found in Map #860

Open
lock9 opened this issue Jan 15, 2024 · 18 comments
Assignees

Comments

@lock9
Copy link
Contributor

lock9 commented Jan 15, 2024

Problem:
When I call the toString() method in a map, the VM will throw an exception. The problems happen in runtime and not compile time. I'm not sure if this is a compiler error or not (probably is).

Compile and run the following code:

Map<String, object> myNft = new Map<String, object>();
myNft["name"] = "My NFT";
myNft["description"] = "My NFT Description";
var tokenString = myNft.ToString(); // The problem is here

Result:

Invoke results:
Gas Consumed: 0.0295854 GAS
Exception: An unhandled exception was thrown. Key not found in Map
Result: [  ]

However, if I return the map and print it locally, it works.

@cschuchardt88
Copy link
Member

cschuchardt88 commented Jan 15, 2024

Use primitive types. For example string not String.

Map<string, string> myNft = new();
myNft["name"] = "My NFT";
myNft["description"] = "My NFT Description";
var tokenString = myNft;

@Jim8y
Copy link
Contributor

Jim8y commented Jan 16, 2024

I did not see the same exception.... tests pass

@Jim8y
Copy link
Contributor

Jim8y commented Jan 16, 2024

Screenshot 2024-01-16 at 12 34 35

@cschuchardt88
Copy link
Member

@Jim8y Its an ApplicationEngine Error. But It's because of non-primitive types.

@Jim8y
Copy link
Contributor

Jim8y commented Jan 16, 2024

I mean, why can't I reproduce the error.

@cschuchardt88
Copy link
Member

He said

However, if I return the map and print it locally, it works.

So try blockchain?

@Jim8y
Copy link
Contributor

Jim8y commented Jan 16, 2024

I'll wait for Rick to explain it.

@lock9
Copy link
Contributor Author

lock9 commented Jan 16, 2024

Hello.

Could it be my compiler version? What version are you using? I'm using Devpack 3.6.2

This doesn't work:

var nftProperties = new Map<string, string>();
nftProperties["key1"] = "value1";
nftProperties["key2"] = "value2";

var nftString = nftProperties.ToString();

This does work:

var nftProperties = new Map<string, string>();
nftProperties["key1"] = "value1";
nftProperties["key2"] = "value2";

var nftString = StdLib.Serialize(nftProperties);

@Jim8y
Copy link
Contributor

Jim8y commented Jan 16, 2024

I am using the master of devpack tested your code in one of the test contracts, nothing happens, build and run normally.

@lock9
Copy link
Contributor Author

lock9 commented Jan 16, 2024

I was using the analyzer branch. I switched to the master branch, rebuilt it and got the same results.
Is there any chance that the error is in Neo Express?

Here is the file:

using System;
using System.ComponentModel;
using System.Numerics;
using Neo;
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Attributes;
using Neo.SmartContract.Framework.Native;
using Neo.SmartContract.Framework.Services;


namespace NFTCollection
{
    [DisplayName("CSharpNFT")]
    [ManifestExtra("Author", "Linkd Academy")]
    [ManifestExtra("Description", "NFT Contract Example")]
    [ContractPermission("*", "onNEP11Payment")]
    public class NFTCollection : SmartContract
    {
        static readonly byte[] KeyTotalSupply = { 0x01 };

        static readonly byte[] KeyOwner = { 0x02 };

        static readonly byte[] PrefixTokenId = { 0x03 };
        static readonly byte[] PrefixToken = { 0x04 };
        static readonly byte[] PrefixAccountToken = { 0x05 };

        static readonly byte[] PrefixBalance = { 0x06 };


        public delegate void OnTransferDelegate(UInt160 from, UInt160 to, BigInteger amount, ByteString tokenId);


        [DisplayName("Transfer")]
        public static event OnTransferDelegate OnTransfer;

        [Safe]
        public static string Symbol()
        {
            return "NFTS";
        }

        [Safe]
        public static int Decimals()
        {
            return 0;
        }

        [Safe]
        public static BigInteger TotalSupply()
        {
            return (BigInteger)Storage.Get(KeyTotalSupply);
        }

        [Safe]
        public static UInt160 OwnerOf(string tokenId)
        {
            return (UInt160)Storage.Get(PrefixToken.Concat(tokenId));
        }

        public class MyNFT
        {
            public string name;
            public string description;
            public string tokenId;
            public Map<string, string> properties = new Map<string, string>();
        }



        public static MyNFT CreateNFT()
        {
            var nftName = "My NFT";
            var nftDescription = "My NFT Description";
            var nftProperties = new Map<string, string>();
            nftProperties["key1"] = "value1";
            nftProperties["key2"] = "value2";

            var nftString = nftProperties.ToString();
            // var nftString = StdLib.Serialize(nftProperties);
            // Runtime.Notify("Debug", new object[] { nftString });

            // Generate Token ID
            var serializedProperties = StdLib.Serialize(nftProperties);
            var transactionHash = (Transaction)Runtime.ScriptContainer;
            var tokenHashInput = serializedProperties.Concat(transactionHash.Hash);
            var tokenId = CryptoLib.Ripemd160(tokenHashInput);

            var myNft = new MyNFT()
            {
                name = nftName,
                description = nftDescription,
                properties = nftProperties,
                tokenId = tokenId
            };

            return myNft;
        }


        public static object TestNFT()
        {
            var transaction = (Transaction)Runtime.ScriptContainer;
            var sender = transaction.Sender;

            var nft = CreateNFT();
            var serializedNFT = StdLib.Serialize(nft);

            var tokenDetailsKey = PrefixToken.Concat(nft.tokenId);
            //Persist the NFT
            Storage.Put(tokenDetailsKey, serializedNFT);

            //Persist the owner of the NFT
            Storage.Put(PrefixTokenId.Concat(nft.tokenId), sender);

            var existingTokens = Storage.Get(PrefixAccountToken.Concat(sender));

            List<ByteString> deserializedTokens;

            if (existingTokens is null)
            {
                deserializedTokens = new ByteString[] { };
            }
            else
            {
                deserializedTokens = (ByteString[])StdLib.Deserialize(existingTokens);
            }

            //Persist the NFT in the account
            deserializedTokens.Add(nft.tokenId);
            Storage.Put(PrefixAccountToken.Concat(sender), StdLib.Serialize(deserializedTokens));

            // Update the balance of the sender
            var balance = (BigInteger)Storage.Get(PrefixBalance.Concat(sender));
            var newBalance = balance + 1;
            Storage.Put(PrefixBalance.Concat(sender), newBalance);

            //Increase the total supply
            var totalSupply = (BigInteger)Storage.Get(KeyTotalSupply);
            var newTotalSupply = totalSupply + 1;
            Storage.Put(KeyTotalSupply, newTotalSupply);

            return nft;
        }

        public static object Properties(string tokenId)
        {
            var rawNFT = Storage.Get(PrefixToken.Concat(tokenId));
            if (rawNFT is null)
            {
                throw new Exception("Token does not exist");
            }

            var nft = (MyNFT)StdLib.Deserialize(rawNFT);
            return nft.properties;
        }

        public static Iterator TokensOf(UInt160 account)
        {
            if (account is null || !account.IsValid)
                throw new Exception("Invalid Parameter.");

            var accountKey = PrefixAccountToken.Concat(account);
            var tokenIds = (Iterator)StdLib.Deserialize(Storage.Get(accountKey));
            return tokenIds;
        }


        [DisplayName("_deploy")]
        public static void Deploy(object data, bool update)
        {
            if (update) return;
            Storage.Put(KeyTotalSupply, 0);

            var tx = (Transaction)Runtime.ScriptContainer;
            Storage.Put(KeyOwner, tx.Sender);
        }

        [Safe]
        public static BigInteger BalanceOf(UInt160 account)
        {
            if (account is null || !account.IsValid)
                throw new Exception("Invalid Parameter.");

            BigInteger balance = (BigInteger)Storage.Get(PrefixBalance.Concat(account));
            return balance;
        }

        public static bool Transfer(UInt160 from, UInt160 to, String tokenId, object data)
        {
            if (from is null || !from.IsValid)
                throw new Exception("Invalid from address.");
            if (to is null || !to.IsValid)
                throw new Exception("Invalid to address.");
            if (!Runtime.CheckWitness(from))
                throw new Exception("Invalid Signature");


            var tokenOwner = (UInt160)Storage.Get(PrefixTokenId.Concat(tokenId));

            if (tokenOwner is null)
                throw new Exception("Token does not exist");

            if (tokenOwner != from)
                throw new Exception("Not Authorized");

            if (from == to)
                return true;

            Storage.Put(PrefixTokenId.Concat(tokenId), to);
            Storage.Put(PrefixBalance.Concat(from), BalanceOf(from) - 1);
            Storage.Put(PrefixBalance.Concat(to), BalanceOf(to) + 1);


            if (ContractManagement.GetContract(to) is not null)
            {
                Contract.Call(to, "onNEP11Payment", CallFlags.All, from, 1, tokenId, data);
            }

            OnTransfer(from, to, 1, tokenId);

            return true;
        }

        public static void Update(ByteString nefFile, string manifest, object data)
        {
            if (Runtime.CheckWitness((UInt160)Storage.Get(KeyOwner)))
                ContractManagement.Update(nefFile, manifest, data);
        }
    }
}

@cschuchardt88
Copy link
Member

It works fine. Remove the .ToString. This was on TestNet T5
image

public static void Test()
{
    Map<string, string> myNft = new();
    myNft["name"] = "My NFT";
    myNft["description"] = "My NFT Description";
    var tokenString = myNft;
}

@cschuchardt88
Copy link
Member

This line is wrong var nftString = nftProperties.ToString(); You can't do that.

@lock9
Copy link
Contributor Author

lock9 commented Jan 17, 2024

It seems that there is a general issue with ToString().
The following code is also causing an error:

public static string TestString()
{
      return "Jimmy".ToString();
}
Invoke results:
Gas Consumed: 0.0246507 GAS
Exception: An unhandled exception was thrown. The value 2 is out of range.
Result: [  ]

@Jim8y Jim8y self-assigned this Jan 17, 2024
@cschuchardt88
Copy link
Member

It seems that there is a general issue with ToString(). The following code is also causing an error:

public static string TestString()
{
      return "Jimmy".ToString();
}
Invoke results:
Gas Consumed: 0.0246507 GAS
Exception: An unhandled exception was thrown. The value 2 is out of range.
Result: [  ]

What don't you understand?

YOU CAN'T DO THAT!!!!!!!!!!!!

YOU CAST!!!!!!!!!!!

@Jim8y
Copy link
Contributor

Jim8y commented Jan 17, 2024

@cschuchardt88 I think what @lock9 want to say is it is normal C# operation, neo should allow it work that way. Not about how to make it work, but how it should work.

@cschuchardt88
Copy link
Member

Well than say that. After 20 times of telling him why he can't do something. He keeps saying I can't do this.

  1. It shows he doesn't read everything.
  2. It wastes our time.

@lock9
Copy link
Contributor Author

lock9 commented Jan 17, 2024

Well than say that. After 20 times of telling him why he can't do something. He keeps saying I can't do this.

  1. It shows he doesn't read everything.
  2. It wastes our time.
Screenshot 2024-01-17 at 08 42 03

@lock9
Copy link
Contributor Author

lock9 commented Jan 17, 2024

Hi @Jim8y ,
I've looked into the UT that you used. It's not checking the VM State.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants