ERC-721: How to get all token ids? - blockchain

I want to iterate over all token ids of a ethereum ERC-721 contract.
Some contracts have counting ids (0, 1, 2, 3, ...) which is easy, but some have random ids, e.g. https://etherscan.io/token/0xf87e31492faf9a91b02ee0deaad50d51d56d5d4d#inventory
Sadly etherscan only shows the last 10000 token ids used, but I want to iterate over all 79490.
Is there a way to accomplish this? For me, everything is fine. Setup my own ethereum node, using some API.

You can loop through all Transfer() events emitted by the collection contract.
You're looking for transfers from address 0x0 (minted tokens). And excluding from the list transfers to address 0x0 (destroyed tokens).
One way to achieve this is by using the Web3 Contract getPastEvents() function (docs).
const myContract = new web3.eth.Contract(abiJson, contractAddress);
myContract.getPastEvents('Transfer', {
filter: {
_from: '0x0000000000000000000000000000000000000000'
},
fromBlock: 0
}).then((events) => {
for (let event of events) {
console.log(event.returnValues._tokenId);
}
});

There's no easy way to do it with an Ethereum node in a contract-agnostic way...the ERC-721 does not specify any interface methods that allow querying for all token ID, so unless the contract you're looking at uses sequential token ids, there's no way to guess all token ids from a simple node query.
Unless you want to iterate over the whole transaction history of the contract to get the ids of every minted NFT (you'd need an archive node for that, as a full node would not have the full transaction history) you should use an API from services that index all NFT activity.
You could use this API from CovalentHQ:
https://www.covalenthq.com/docs/api/#/0/Class-A/Get-NFT-Token-IDs-for-contract/lng=en
Or this one from Moralis:
https://docs.moralis.io/moralis-server/web3-sdk/token#getalltokenids

I needed the same with Ethers instead of Web3, here i the code snippet for ethers.js:
const getTransferEvents = async () => {
const provider = new ethers.providers.Web3Provider(window.ethereum)
const contract = new ethers.Contract("address", "abi", provider);
const events = await contract.queryFilter('Transfer', 0);
console.log(events);
};

Related

Is there a simple API to transfer ERC-1155(Polygon Mainnet) tokens to other users?

Sorry I am new to blockchain development, so pardon my silly basic question.
I have created a bunch of ERC-1155 using Polygon main net. I have all the address and ids of the token. Now I want to transfer them to other users from my backend(nodejs) api.
What I have tried till now:
Used opensea-js with following code but getting alchemy error.
import { OpenSeaPort, Network } from 'opensea-js'
import Web3 from "web3"
// This example provider won't let you make transactions, only read-only calls:
const provider = new Web3.providers.HttpProvider('https://polygon-mainnet.g.alchemy.com/v2/**************************')
const seaport = new OpenSeaPort(provider, {
networkName: Network.Main,
})
const transactionHash = await seaport.transfer({
asset: {
tokenId: '**************************************',
tokenAddress:'**********************************',
schemaName: "ERC1155"
},
fromAddress: '***********************************', // Must own the asset
toAddress: '*************************************',
quantity: 1,
})
console.log(transactionHash);
Its giving error "Unsupported method: eth_sendTransaction"
I then searched about this error and alchemy has some complex solution for this. But I believe this is a very simple task and there must be a simpler solution which I could not find.

How to check if NFT is in a specific wallet

I need to check if a specific NFT is in a specific wallet, there is an API or a way to do it programmatically?
Thanks a lot.
Assuming the NFT is published onchain and its collection contract implements the ERC-721 standard, you can call the ownerOf() function (defined in the standard) on the collection contract.
Example using web3js:
const collection = new web3.eth.Contract(abiJson, collectionAddress);
const owner = await collection.methods.ownerOf(tokenId).call();
return owner == desiredAddress;
For the ERC-1155 standard, you can use the balanceOf() function.
const balance = await collection.methods.balanceOf(owner, ,tokenId).call();
return balance > 0;

Unable to call safeTransferFrom method from a JS api file

I have deployed my ERC721 contract on polygon testnet from owner(ox43b....81). I have created a small node server to interact with my contract through web3. I m using truffle config
//truffle-config.js
polygon-testnet: {
provider: new HDWalletProvider(privateKey, https://rpc-mumbai.maticvigil.com),
network_id: 80001,
gas: 6000000,
gasPrice: 10000000000,
confirmations: 2,
timeoutBlocks: 200,
skipDryRun: true
}
//index.js file
function test(){
const owner = await myContract.methods.owner().call()
console.log('contract owner', owner) //ox43b....81
//miniting token
const mint = await myContract.methods
.safeMint(owner,1001, 'google.com')
.send({ from: owner, gas: 100000 }) // is this the write way of calling ??
//token transfer
const transfer = await myContract.methods
.safeTransferFrom(acc1, acc2, 1003)
.send({ from: acc1, gas: 1000000 }) // is this the write way of calling ??
}
bcz i m getting err in both function calling: Error: Returned error: unknown account.
NOTE: I am not calling my contract func from truffle console. I m calling through a JS api file index.js
I know there is another way by using web3.eth.accounts.signTransaction() to mint & transfer the token.
Q.1 Is signing is reqd? Can't we access the contract method directly as i did above ?
Q.2 By using web3.eth.accounts.signTransaction() method i can able to mint the tokens successfully but i am facing issue in token transfer (using safeTransferFrom()). There are two cases:
case 1: i am transferring the token which is created/minted by contract owner(ox43b....81) to any other individual user address.
const fromAddress = 'ox43b....81' //owner of the contract
const toAddress = '0x26....7D'
const tokenId = 10001 // created by owner(ox43b....81)
const tx = {
from: ownerAddress, // ox43b....81 --> owner who created contract
to: contractAddress,// 0xfe.....24
nonce: nonce, // nonce with the no of transactions from our owner address
gas: 1000000, // fee estimate to complete the transaction
data: await myContract.methods
.safeTransferFrom(fromAddress, toAddress, tokenId)
.encodeABI()
}
// do signing stuff & send it
so in this case i m able to transfer the token successfully.
case 2: i am transferring the token which is created/minted by another(not contract owner) user(0x26....7D) to any other individual user address / or to owner.
const fromAddress = '0x26....7D' (not the owner of contract)
const toAnyAddress = '0x5e....5a' // any other user
const tokenId = 10002 // created by user '0x26....7D' (not the owner of contract)
const nonce = await web3.eth.getTransactionCount(fromAddress, 'latest')
const tx = {
from: fromAddress, // 0x26....7D -> is this right? or i put contract owner adr?
to: contractAddress,// 0xfe.....24
nonce: nonce,
gas: 1000000, // fee estimate to complete the transaction
data: await this.myContract.methods
.safeTransferFrom(fromAddress, toAnyAddress, tokenId)
.encodeABI()
}
// do signing stuff & send it
so in this case i m getting err - Fail with error 'ERC721: transfer caller is not owner nor approved'.
I need solution for my case 2. Is there something related with locked/unlocked accounts? How can i fix it?
Please help! Thanks in advance.
Q.1 Is signing is reqd? Can't we access the contract method directly as i did above ?
The .send() method of the web3 Contract instance performs/requests the signature in the background. So it's always signed - just not with an explicit "sign" keyword in the code in this case.
Q.2 There are two cases:
This is not related to the way you sign the transaction.
The second case fails because the token holder has not approved the spender (i.e. the transaction sender) to spend their tokens.
You need to invoke the approve(<spender>, <tokenId>) function from the token holder address first, to be able to successfully use the transferFrom().
// assuming `acc1` is holder of the token ID `1003`
// approve the `owner` to spend the `acc1`'s token
await myContract.methods.approve(owner, 1003).send({from: acc1});
// transfer token `1003` held by `acc1` ... and send the transaction from the `owner` address
await myContract.methods.transferFrom(acc1, recipient, 1003).send({from: owner});

How to get TRC20 transactions to an address

I am using tron web to query transactions of an address but it does not return transactions sent to that address where token transferred is TRC20.
This does not work.
I want to get the transactions on an address and get both TRX, trc10 and trc20 transactions.
What am I doing wrong or how to do that?
Here is my code block:
tronWeb.setDefaultBlock("latest");
var result = await tronGrid.account.getTransactions(address, {
only_confirmed: true,
only_to: true,
limit: 10
});
console.log(JSON.stringify(result));
})();
After a lot of research, I found out one can easily query contract events at intervals to get transactions on that contract address and you can then filter it for the address you are watching since you can't get a webhook or websocket with your trongrid/tronweb implementation.
Here is a sample file I used to achieve this and it works great for monitoring many address even with different contract addresses.
Note: In my own implementation, this node file is called from another file and other logistics are handled in the other file, but below you see how I queried the transfer events emitted by the specified contract
const TronWeb = require("tronweb");
const TronGrid = require("trongrid");
const tronWeb = new TronWeb({
fullHost: "https://api.trongrid.io"
});
const tronGrid = new TronGrid(tronWeb);
const argv = require("minimist")(process.argv.slice(2));
var contractAddress = argv.address;
var min_timestamp = Number(argv.last_timestamp) + 1; //this is stored for the last time i ran the query
(async function() {
tronWeb.setDefaultBlock("latest");
tronWeb.setAddress("ANY TRON ADDRESS"); // maybe being the one making the query not necessarily the addresses for which you need the transactions
var result = await tronGrid.contract.getEvents(contractAddress, {
only_confirmed: true,
event_name: "Transfer",
limit: 100,
min_timestamp: min_timestamp,
order_by: "timestamp,asc"
});
result.data = result.data.map(tx => {
tx.result.to_address = tronWeb.address.fromHex(tx.result.to); // this makes it easy for me to check the address at the other end
return tx;
});
console.log(JSON.stringify(result));
})();
You are free to customize the config data passed to the tronGrid.contract.getEvents method. Depending on how frequently transactions come on the contract you are monitoring you should DYOR to know at what interval is great for you and what limit value you should pass.
Refer to https://developers.tron.network/docs/trongridjs for details.
I found a API that can take TRC20 transactions, but I haven't found an implementation in webtron.
https://api.shasta.trongrid.io/v1/accounts/address/transactions
Related document:
https://developers.tron.network/reference#transaction-information-by-account-address

Calling a Smart Contract method using Web3 1.0

Presently, I have a smart contract successfully deployed to the Rinkeby testnet, I'm having trouble accessing the method in question using web3 version 1.0.
Here's my web3 code, which instantiates a contract instance and calls a contract method:
const contractInstance = new web3.eth.Contract(abiDefinition, contractAddress);
var value = web3.utils.toWei('1', 'ether')
var sentTransaction = contractInstance.methods.initiateScoreRetrieval().send({value: value, from: fromAddress})
console.log('event sent, now set listeners')
sentTransaction.on('confirmation', function(confirmationNumber, receipt){
console.log('method confirmation', confirmationNumber, receipt)
})
sentTransaction.on('error', console.error);
And here is my smart contract, or rather a version of it stripped down to the relevant bits:
contract myContract {
address private txInitiator;
uint256 private amount;
function initiateScoreRetrieval() public payable returns(bool) {
require(msg.value >= coralFeeInEth);
amount = msg.value;
txInitiator = msg.sender;
return true;
}
}
I am not able to get to the console.log that is setting the event listeners on the web3 side, and I am not getting an error of any kind thrown. I'm certainly not getting the consoles from the actual event listeners. I am guessing something is wrong with the way I'm sending the transaction, but I think I am correctly following the pattern documented below: https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html#methods-mymethod-send
Does anyone have any insight on how to use web3 1.0 to make contract method calls correctly? Am I doing something wrong with how I'm passing options, etc.?
Thanks!
I believe you forgot to specify your HttpProvider for your web3, thus you're not connecting to live Rinkeby network, and by default web3 is running on you local host, which is why even if you provide the right contract address, there's nothing there.
To connect to live network, I would strongly encourage you to use Infura Node by ConsenSys.
const Web3 = require("web3");
const web3 = new Web3(new Web3.providers.HttpProvider("https://rinkeby.infura.io"));
Then by now, everything should work perfectly fine.
First, you need to generate your transaction ABI using encodeABI(), here is an example:
let tx_builder = contractInstance.methods.myMethod(arg1, arg2, ...);
let encoded_tx = tx_builder.encodeABI();
let transactionObject = {
gas: amountOfGas,
data: encoded_tx,
from: from_address,
to: contract_address
};
Then you have to sign the transaction using signTransaction() using private key of sender. Later you can sendSignedTransaction()
web3.eth.accounts.signTransaction(transactionObject, private_key, function (error, signedTx) {
if (error) {
console.log(error);
// handle error
} else {
web3.eth.sendSignedTransaction(signedTx.rawTransaction)
.on('receipt', function (receipt) {
//do something
});
}