Ethereum Unity3D Boilerplate Questions

The result should be a Json string. I cannot tell the type of “result” (the second return param so based on the name of the function I will call this a bool) For the function you provided the result should look something like this (run it and check):
{"0":["0x1234567890ABCDEF1234567890ABCDEF", "0xFEDCB09876543211234567890ABCDEF"],"1":true}

could also have a repeat of the the second response param but named result - not sure since the first response param is not named.

You can deserialize straight to object and use it like this:

object x = JsonConvert.DeserializeObject<object>(resp);
List<String> myArray = new List<string>(x["0"]);
bool isWhiteListed = bool.Parse(x["1"]);
1 Like

I’m not 100% sure how this is supposed to work but it looks like the default ACL when creating an object is “no permissions”

Try this:

// create your object:
var myObject = MoralisInterface.GetClient().Create<MyObject>();
// set ACL
myObject.ACL = new MoralisAcl(MoralisInterface.GetUser());

This allows the user to read / write the object.

OR

There are also a couple other useful properties on the MoralisAcl class to allow public access

        /// <summary>
        /// Gets or sets whether the public is allowed to read this object.
        /// </summary>
        public bool PublicReadAccess
        {
            get => GetAccess(AccessKind.Read, publicName);
            set => SetAccess(AccessKind.Read, publicName, value);
        }

        /// <summary>
        /// Gets or sets whether the public is allowed to write this object.
        /// </summary>
        public bool PublicWriteAccess
        {
            get => GetAccess(AccessKind.Write, publicName);
            set => SetAccess(AccessKind.Write, publicName, value);
        }
1 Like

Hi There,

Thanks again for this part.
Everything seemed to run, but I got a failed transaction on the end.

This is my function:
Capture d’écran 2022-03-11 à 10.45.17
It is made to buy nft with custom ERC20.
And this is the method:

public string marketABI;

    public async void BuyNFT()
    {
        BigInteger _offerId = 1;
        BigInteger price = 1000000000000000000;

        MoralisInterface.InsertContractInstance("Buy", marketABI, "mumbai", marketAddress);
        // Set gas estimate
        HexBigInteger gas = new HexBigInteger(80000);
        object[] pars = { _offerId, price };
        string result = await MoralisInterface.SendEvmTransactionAsync("Buy", "mumbai", "BuyNFT", senderAccount, gas, new HexBigInteger("0x0"), pars);

        print(result);
    }

this is the failed result on polyscan:

I can’t seem to figure out what is happening.
Thanks again !

Not sure if this is the whole issue but instead of sending the price it appears you are sending a zero value.

does this work on remix ?
send your solidity contract and the ABI and address , so i can try it out

Hi,
I am getting two errors which i cant seem to resolve.

First Error:

Setup your Moralis Server URI and Application Id before running. For more help read the Quick Start on: https://github.com/ethereum-boilerplate/ethereum-unity-boilerplate#-quick-start

This error should not be occurring as I already have given the Server URI and the App ID but this still occurs.

The second error:

EntryPointNotFoundException: ConnectWeb3

Which I have no idea why it is occurring. All i understand from error name is that it has to do with connecting to Web3.

i believe you are building for webgl, you cannot test for webgl on the unity editor , you have to build :slightly_smiling_face:
and follow the processes on the docs
https://docs.moralis.io/moralis-server/connect-the-sdk/connect-with-unity
and

1 Like

Yes it does indeed work on remix.
tx hash on mumbai scan : 0x358e8dccadf12e7bdd99e7365b26782dec2b91c388991554533f088b26e579ab

It’s a combinaison of 3 contracts.
One for minting Erc20s:
One for minting NFTs
One for listing and buying NFTs that inherits from the two (marketplace).

// SPDX-License-Identifier: UNLICENSE
pragma solidity ^0.8.1;

import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";

interface IERC_COLLECTION is IERC1155 {
  function safeMint(string memory _tokenURI, uint256 amount) external;
}
contract NFTMarketplace is Ownable, ERC1155Holder {
  uint public offerCount;
  uint public tokenCount;
  mapping (uint => _Offer) public offers;
  IERC_COLLECTION nftCollection;
  IERC20 tokenCollection;
  struct _Offer {
    uint offerId;
    uint id;
    address user;
    uint price;
    bool fulfilled;
  }

  event Offer(
    uint offerId,
    uint token_id,
    address user,
    uint price,
    bool fulfilled
  );

  event OfferFilled(uint offerId, uint id, address newOwner);
  event OfferCancelled(uint offerId, uint id, address owner);
  event ClaimFunds(address user, uint amount);

  constructor(address _nftCollection, address _tokenCollection) {
    nftCollection = IERC_COLLECTION(_nftCollection);
    tokenCollection = IERC20(_tokenCollection);
  }

  function SetPrice(string memory _tokenURI, uint256 amount, uint256 price) external onlyOwner {
    nftCollection.safeMint(_tokenURI, amount);
    tokenCount ++;
    for (uint256 i = 0; i < amount; i ++) {
      offers[offerCount] = _Offer(offerCount, tokenCount, address(this), price, false);
      offerCount ++;
    }
  }
  function BuyNFT(uint _offerId, uint256 price) public {
    _Offer storage _offer = offers[_offerId];
    uint256 token_bal = tokenCollection.balanceOf(msg.sender);
    require(_offer.offerId == _offerId, "The offer must exist");
    require(_offer.user != msg.sender, "The owner of the offer cannot fill it");
    require(!_offer.fulfilled, "An offer cannot be fulfilled twice");
    require(token_bal >= price, "An offer cannot be fulfilled twice");
    require(price == _offer.price, "The TOKEN amount should match with the NFT Price");
    nftCollection.safeTransferFrom(address(this), msg.sender, _offer.id, 1, "");
    tokenCollection.transferFrom(msg.sender, address(this), price);
    _offer.fulfilled = true;
    emit OfferFilled(_offerId, _offer.id, msg.sender);
  }

  // Fallback: reverts if Ether is sent to this smart-contract by mistake
  fallback () external {
    revert();
  }
}

Capture d’écran 2022-03-12 à 05.27.18

The address of the contract is: 0xFEeD74839Ea943354e3a422702B986d410a62774
This is the ABI:

[
	{
		"inputs": [
			{
				"internalType": "uint256",
				"name": "_offerId",
				"type": "uint256"
			},
			{
				"internalType": "uint256",
				"name": "price",
				"type": "uint256"
			}
		],
		"name": "BuyNFT",
		"outputs": [],
		"stateMutability": "nonpayable",
		"type": "function"
	},
	{
		"inputs": [
			{
				"internalType": "address",
				"name": "_nftCollection",
				"type": "address"
			},
			{
				"internalType": "address",
				"name": "_tokenCollection",
				"type": "address"
			}
		],
		"stateMutability": "nonpayable",
		"type": "constructor"
	},
	{
		"anonymous": false,
		"inputs": [
			{
				"indexed": false,
				"internalType": "address",
				"name": "user",
				"type": "address"
			},
			{
				"indexed": false,
				"internalType": "uint256",
				"name": "amount",
				"type": "uint256"
			}
		],
		"name": "ClaimFunds",
		"type": "event"
	},
	{
		"anonymous": false,
		"inputs": [
			{
				"indexed": false,
				"internalType": "uint256",
				"name": "offerId",
				"type": "uint256"
			},
			{
				"indexed": false,
				"internalType": "uint256",
				"name": "token_id",
				"type": "uint256"
			},
			{
				"indexed": false,
				"internalType": "address",
				"name": "user",
				"type": "address"
			},
			{
				"indexed": false,
				"internalType": "uint256",
				"name": "price",
				"type": "uint256"
			},
			{
				"indexed": false,
				"internalType": "bool",
				"name": "fulfilled",
				"type": "bool"
			}
		],
		"name": "Offer",
		"type": "event"
	},
	{
		"anonymous": false,
		"inputs": [
			{
				"indexed": false,
				"internalType": "uint256",
				"name": "offerId",
				"type": "uint256"
			},
			{
				"indexed": false,
				"internalType": "uint256",
				"name": "id",
				"type": "uint256"
			},
			{
				"indexed": false,
				"internalType": "address",
				"name": "owner",
				"type": "address"
			}
		],
		"name": "OfferCancelled",
		"type": "event"
	},
	{
		"anonymous": false,
		"inputs": [
			{
				"indexed": false,
				"internalType": "uint256",
				"name": "offerId",
				"type": "uint256"
			},
			{
				"indexed": false,
				"internalType": "uint256",
				"name": "id",
				"type": "uint256"
			},
			{
				"indexed": false,
				"internalType": "address",
				"name": "newOwner",
				"type": "address"
			}
		],
		"name": "OfferFilled",
		"type": "event"
	},
	{
		"inputs": [
			{
				"internalType": "address",
				"name": "",
				"type": "address"
			},
			{
				"internalType": "address",
				"name": "",
				"type": "address"
			},
			{
				"internalType": "uint256[]",
				"name": "",
				"type": "uint256[]"
			},
			{
				"internalType": "uint256[]",
				"name": "",
				"type": "uint256[]"
			},
			{
				"internalType": "bytes",
				"name": "",
				"type": "bytes"
			}
		],
		"name": "onERC1155BatchReceived",
		"outputs": [
			{
				"internalType": "bytes4",
				"name": "",
				"type": "bytes4"
			}
		],
		"stateMutability": "nonpayable",
		"type": "function"
	},
	{
		"inputs": [
			{
				"internalType": "address",
				"name": "",
				"type": "address"
			},
			{
				"internalType": "address",
				"name": "",
				"type": "address"
			},
			{
				"internalType": "uint256",
				"name": "",
				"type": "uint256"
			},
			{
				"internalType": "uint256",
				"name": "",
				"type": "uint256"
			},
			{
				"internalType": "bytes",
				"name": "",
				"type": "bytes"
			}
		],
		"name": "onERC1155Received",
		"outputs": [
			{
				"internalType": "bytes4",
				"name": "",
				"type": "bytes4"
			}
		],
		"stateMutability": "nonpayable",
		"type": "function"
	},
	{
		"anonymous": false,
		"inputs": [
			{
				"indexed": true,
				"internalType": "address",
				"name": "previousOwner",
				"type": "address"
			},
			{
				"indexed": true,
				"internalType": "address",
				"name": "newOwner",
				"type": "address"
			}
		],
		"name": "OwnershipTransferred",
		"type": "event"
	},
	{
		"inputs": [],
		"name": "renounceOwnership",
		"outputs": [],
		"stateMutability": "nonpayable",
		"type": "function"
	},
	{
		"inputs": [
			{
				"internalType": "string",
				"name": "_tokenURI",
				"type": "string"
			},
			{
				"internalType": "uint256",
				"name": "amount",
				"type": "uint256"
			},
			{
				"internalType": "uint256",
				"name": "price",
				"type": "uint256"
			}
		],
		"name": "SetPrice",
		"outputs": [],
		"stateMutability": "nonpayable",
		"type": "function"
	},
	{
		"inputs": [
			{
				"internalType": "address",
				"name": "newOwner",
				"type": "address"
			}
		],
		"name": "transferOwnership",
		"outputs": [],
		"stateMutability": "nonpayable",
		"type": "function"
	},
	{
		"stateMutability": "nonpayable",
		"type": "fallback"
	},
	{
		"inputs": [],
		"name": "offerCount",
		"outputs": [
			{
				"internalType": "uint256",
				"name": "",
				"type": "uint256"
			}
		],
		"stateMutability": "view",
		"type": "function"
	},
	{
		"inputs": [
			{
				"internalType": "uint256",
				"name": "",
				"type": "uint256"
			}
		],
		"name": "offers",
		"outputs": [
			{
				"internalType": "uint256",
				"name": "offerId",
				"type": "uint256"
			},
			{
				"internalType": "uint256",
				"name": "id",
				"type": "uint256"
			},
			{
				"internalType": "address",
				"name": "user",
				"type": "address"
			},
			{
				"internalType": "uint256",
				"name": "price",
				"type": "uint256"
			},
			{
				"internalType": "bool",
				"name": "fulfilled",
				"type": "bool"
			}
		],
		"stateMutability": "view",
		"type": "function"
	},
	{
		"inputs": [],
		"name": "owner",
		"outputs": [
			{
				"internalType": "address",
				"name": "",
				"type": "address"
			}
		],
		"stateMutability": "view",
		"type": "function"
	},
	{
		"inputs": [
			{
				"internalType": "bytes4",
				"name": "interfaceId",
				"type": "bytes4"
			}
		],
		"name": "supportsInterface",
		"outputs": [
			{
				"internalType": "bool",
				"name": "",
				"type": "bool"
			}
		],
		"stateMutability": "view",
		"type": "function"
	},
	{
		"inputs": [],
		"name": "tokenCount",
		"outputs": [
			{
				"internalType": "uint256",
				"name": "",
				"type": "uint256"
			}
		],
		"stateMutability": "view",
		"type": "function"
	}
]

Player (ownerAddress) should increase allowance to market( spenderAddress) to be able ton buy from it.
This would be the last step to achieve buying an erc1155 with custom erc20s in-game.
Very excited about it !
can’t thank you enough.

public string marketABI;

    public async void BuyNFT()
    {
       // BigInteger _offerId = 1;
        HexBigInteger _offerId = new HexBigInteger(1);
        HexBigInteger price = new HexBigInteger(1000000000000000000);
      //  BigInteger price = 1000000000000000000;

        MoralisInterface.InsertContractInstance("Buy", marketABI, "mumbai", marketAddress);
        // Set gas estimate
        HexBigInteger gas = new HexBigInteger(80000);
        object[] pars = { _offerId, price };
        string result = await MoralisInterface.SendEvmTransactionAsync("Buy", "mumbai", "BuyNFT", senderAccount, gas, new HexBigInteger("0x0"), pars);

        print(result);
    }

Try this and see if it works :thinking: or if there is an error, if this doesn’t work,
you may need to check if you can pass _offerId as just an int :thinking: instead of HexBigInteger
and you can paste the updated response if you tried those two things and it still doesn’t work

1 Like

Tried to pass parameters HexBigInteger or int but returns this error:

Evm Transaction failed: An error occurred encoding abi value. Order: ‘1’, Type: ‘uint256’, Value: ‘1’. Ensure the value is valid for the abi type.

it does the same with price parameter. It does however work with BigInteger but transactions fail onchain.

Hello,

I have the same issue when implementing a Unity project for Android & iOS (the issue appeared on Android, didn’t test it on iOS or PC):

NftOwnerCollection balance = await MoralisInterface.GetClient().Web3Api.Account.GetNFTs(address.ToLower(), ChainList.eth);

The error is “ApiException: Error calling GetNFTs: Object reference not set to an instance of an object”, and as far as I could see it is thrown whenever the API returned status code is 0. What does 0 means as Http status code? Or in this code?

Also, what could be the problem? I’ve seen another guy posted the same thing but no answer was received. I have tried to solve it during the last 5 days but no success, that’s why I’ve decided to write it here.

Can you take a look, please? If you need further info, please contact me. Thanks

Details :
E Unity : ApiException: Error calling GetNFTsForContract: Object reference not set to an instance of an object
E Unity : at Moralis.Web3Api.CloudApi.AccountApi.GetNFTsForContract (System.String address, System.String tokenAddress, Moralis.Web3Api.Models.ChainList chain, System.String format, System.Nullable1[T] offset, System.Nullable1[T] limit) [0x00321] in F:\NewGame\Assets\MoralisWeb3ApiSdk\Moralis\Moralis.Web3Api\CloudApi\AccountApi.cs:446
Unity : at SwordNFT.MoralisTest () [0x00179] in F:\NewGame\Assets\Scripts\SwordNFT.cs:262
E Unity : at System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.b__7_0 (System.Object state) [0x00000] in <57faacfd2e7144baaa50d26ab4b3b712>:0
E Unity : at UnityEngine.UnitySynchronizationContext+WorkRequest.Invoke () [0x00002] in /Users/bokken/buildslave/unity/build/Runtime/Export/Scripting/UnitySynchronizationContext.cs:153
E Unity : at UnityEngine.UnitySynchronizationContext.Exec () [0x00056] in /Users/bokken/buildslave/unity/build/Runtime/Export/Scripting/UnitySynchroniz

David @0xprof ,

I have the exact same issue. I have a function called createMarketItem() - it creates an ERC721 token. I have deployed my contract to the rinkeby testnet. When this function is called from remix, it works correctly. I represented ‘value’ in wei. Here is the successful transaction hash: 0x28d16265963104c2b83327593ceca93dc987948a59d9d90132156e01afb66ba1

// function to create a market item. minting
    function createMarketItem(address nftContract, uint256 tokenId, uint256 price) public payable nonReentrant{
        require(price > 0, "Price must be at least 1 wei"); // check and make sure price is not zero!
        require(msg.value == listingPrice, "Price must be equal to listing price"); 

        _itemIds.increment();
        uint256 itemId = _itemIds.current();

        idToMarketItem[itemId] = MarketItem(
            itemId,
            nftContract,
            tokenId,
            payable(msg.sender),
            payable(address(0)),
            price,
            false
        );

        IERC721(nftContract).transferFrom(msg.sender, address(this), tokenId); 

        emit MarketItemCreated(
            itemId,
            nftContract,
            tokenId,
            msg.sender,
            address(0),
            price,
            false
        );
    }

However, when I call the same function from within Unity, I get an error due to this line in the code above: require(msg.value == listingPrice, “Price must be equal to listing price”); . It checks to see if the msg.value is equal to listing price. I have set listingPrice = 0.01 ether somewhere else in the same contract. Refer below on how I am calling the function from within Unity

MoralisInterface.InsertContractInstance("MarketplaceTest", MarketTest.ABI, MarketTest.CHAIN, MarketTest.CONTRACT_ADDRESS);
            Nethereum.Hex.HexTypes.HexBigInteger gas = new Nethereum.Hex.HexTypes.HexBigInteger(80000);
            
            **BigInteger result = new BigInteger(1000000000000000);**

**            Nethereum.Hex.HexTypes.HexBigInteger amount = new Nethereum.Hex.HexTypes.HexBigInteger(result);**

           
            string address = "0x7c8a8f3cd440f23157088086a9dae2ba42c31bfa";
            string price = "1000000000000000";
            object[] pars = { address, price, bi.ToString("x") };
            TransactionInput input = new TransactionInput(null, addr, gas, amount);
            string resp = await MoralisInterface.SendEvmTransactionAsync("MarketplaceTest", "rinkeby", 
"createMarketItem", input, pars);
            print(resp);

When I call the function it throws an error : “Price must be equal to listing price” . This error was visible in metamask activity and etherscan. The error is no longer visible on etherscan. This is the failed hash : 0x642042ad86a84b88372817b2790accea40e0092ccffd19f82957a8a44df91cdc.

This is my contract address : 0xFea152266F7BB57cd6a523e798161E37AaE3DEbb
Below is my abi :

"name": "createMarketItem",
		"outputs": [],
		"stateMutability": "payable",
		"type": "function"
	},
	{
		"inputs": [
			{
				"internalType": "address",
				"name": "nftContract",
				"type": "address"
			},
			{
				"internalType": "uint256",
				"name": "itemId",
				"type": "uint256"
			}
		],
		"name": "createMarketSale",
		"outputs": [],
		"stateMutability": "payable",
		"type": "function"
	}, 

We are stuck. Can you please help?

This is the error you are getting, it isnt from unity or the sdk, it seems you are using msg.value
createMarketItem(address nftContract, uint256 tokenId, uint256 price) and you also getting price as an argument :thinking:,

string resp = await MoralisInterface.SendEvmTransactionAsync("MarketplaceTest", "rinkeby", "createMarketItem" input, gas, new HexBigInteger("0x0"), pars);

fix up your contract call to be like this, new HexBigInteger("0x0") this in the above is msg.value which you will need, so put the the correct value there new HexBigInteger(your value)

After a couple of hours spent to investigate, I found out that this line “RestClient.Execute(request);” returns, as a result, a status Code of 0 with exception: “Object reference not set to an instance of an object”.

This applies to all CLoudApi calls (I’ve tested on GetNativeBalance, GetNFTs, GetNFTsForContract). If I extract de parameters, base URL and body and put them in Postman, the call returns the info, it’s successful. The only problem is on Unity.

As I cannot look into de RestClient.Execute method to see exactly why it returns 0, I cannot investigate more. Please help

1 Like

I have tried this before and it did not work. Refer to the line of code below. Gave me the same error.

string resp = await MoralisInterface.SendEvmTransactionAsync("MarketplaceTest", "rinkeby", "createMarketItem", addr, gas, new Nethereum.Hex.HexTypes.HexBigInteger(10000000000000000), pars);

When the authentication message appears in Metamask asking if I would like to approve the transaction, it shows 0.00001 eth instead of 0.01 eth. So it means the wrong value is being sent to Metamask, hence triggering the error due to the line require(msg.value == listingPrice, “Price must be equal to listing price”);. Refer to Metamask message below

Any suggestions?

Had that issue, but for me it was because I was misshandling the conversion between int / BigInteger / HexBigInteger / string lol

Although still struggling with this :confused:

Later update:

If running from Editor, it works.
If running from Android physical device (Samsung S9 with Android 10), it returns status code 0 with the error above.

Hope it helps, thanks

1 Like

This object and function is from RestSharp if that helps with the investigation. This performs a straight https call under the hood.

Have you been able to run the demo on Android and does the mug show for an account that has not claimed it yet? I know this does not solve your problem but I have been able to run various Web3API calls successfully on my Android device.

For the Web3Api interface via cloud functions, the calling account must be authenticated.

Are there any errors in your Moralis Server Logs after a failed call?

Are there any Android errors indicating network permissions are not present?

Because the error is happening at the network call, the above information seems to indicate a network connectivity issue.

Are you comfortable posting the complete function that the Web3Api call is made from?