Sending both ether and calling function on smart contract using Web3 - ethereum

So there's an ERC20 contract with an additional fallback function. I am trying to send ether to the contract as well as send tokens in same transaction. I am not sure if this is possible but I have found some examples of sending custom data to a contract. I have following code:
async getTransaction(sender: string, receiver: string, value: string, amount: string, gasPrice: number) {
let data = this.tokenContract.methods.transfer(receiver, amount).encodeABI();
let gas = await this.web3.eth.estimateGas({
value,
data,
from: sender,
to: this.CONTRACT_ADDRESS
});
let nonce = await this.getNonce(sender);
return {
value,
// data,
from: sender,
to: this.CONTRACT_ADDRESS,
gas: this.web3.utils.toHex(Math.trunc(gas)),
gasPrice: this.web3.utils.toHex(Math.trunc(gasPrice * 1e9)),
chain: this.web3.utils.toHex(1337),
nonce,
hardfork: "MUIRCLACIER"
}
}
It only works to send tokens when value is 0. If value is positive it fails at the gas estimation step with the error below. It only works if data field is removed from gas estimation and transaction, it sends the specified value successfully.
The question, is it technically possible to do both: send ether and invoke a function on the contract in a single transaction?
{
"message": "VM Exception while processing transaction: revert",
"code": -32000,
"data": {
"stack": "RuntimeError: VM Exception while processing transaction: revert\n at Function.RuntimeError.fromResults (/Applications/Ganache.app/Contents/Resources/static/node/node_modules/ganache-core/lib/utils/runtimeerror.js:94:13)\n at module.exports (/Applications/Ganache.app/Contents/Resources/static/node/node_modules/ganache-core/lib/utils/gas/guestimation.js:142:32)",
"name": "RuntimeError"
}
}
Error: Internal JSON-RPC error.
{
"message": "VM Exception while processing transaction: revert",
"code": -32000,
"data": {
"stack": "RuntimeError: VM Exception while processing transaction: revert\n at Function.RuntimeError.fromResults (/Applications/Ganache.app/Contents/Resources/static/node/node_modules/ganache-core/lib/utils/runtimeerror.js:94:13)\n at module.exports (/Applications/Ganache.app/Contents/Resources/static/node/node_modules/ganache-core/lib/utils/gas/guestimation.js:142:32)",
"name": "RuntimeError"
}
}

is it technically possible to do both: send ether and invoke a function on the contract in a single transaction?
Yes. The function receiving ETH needs to be have the payable modifier.
function foo() external payable {
// the received amount is in the global variable `msg.value`
}

Related

Error: invalid BigNumber value (argument="value", value={"value":"25000000000000000"}, code=INVALID_ARGUMENT, version=bignumber/5.5.0)

I have tried changing the values from 0.025 ether to 1 ether then also its showing the same error.
Also, I have tried with the rational number like 1/8 still not working.
LOOKED into some answers but they didn't resolve the error.
I have the same code in other project and it's working over there.
Error Which I received
Uncaught (in promise) Error: invalid BigNumber value (argument="value", value={"value":"25000000000000000"}, code=INVALID_ARGUMENT, version=bignumber/5.5.0)
Could not get the stack frames of error: TypeError: Cannot read properties of null (reading 'length')
Image of the Error
Here is my Code for the Listing Price
uint256 listingPrice = 0.025 ether ; // Here ether is denoting the MATIC
function getListingPrice() public view returns (uint256) {
return listingPrice;
}
Here is the Code for fetching the value in UI
async function putItem(url) {
const web3Modal = new Web3Modal();
const connection = await web3Modal.connect();
const provider = new ethers.providers.Web3Provider(connection);
const signer = provider.getSigner();
const { royalty } = formInput;
//NFT Contract
let contract = new ethers.Contract(nftAddress, NFT.abi, signer);
//minting the certificate
let transaction = await contract.createToken(url);
//waiting for the minting transaction to finish
let tx = await transaction.wait();
let event = tx.events[0];
let value = event.args[2];
let tokenId = value.toNumber(); //Token Id Of the NFT
console.log(tokenId)
//NFT Market Contract
contract = new ethers.Contract(nftMarketAddress, NFTMarket.abi, signer);
//fetching listing price from the contract
let listingPrice = await contract.getListingPrice();
listingPrice = listingPrice.toString();
//listing the certificate.
transaction = await contract.createMarketItem(
nftAddress,
tokenId,
{ value: (listingPrice) },
royalty,
index
);
//waiting for the transaction to complete
await transaction.wait();
console.log("completed")
//navigate back to home page
}
If any more detail required, please comment.
It looks like you're trying to send an object as the parameter { value: (listingPrice) }
This should probably be written as either an array of parameters or just the listingPrice
//listing the certificate.
transaction = await contract.createMarketItem(
nftAddress,
tokenId,
listingPrice,
royalty,
index
);
Source: https://docs.ethers.io/v5/api/contract/contract/#contract-functionsSend
For my case I needed to add .toString() to the BigNumber before passing it to the contract.
async changePayoutAmount_ether(amount_ether) {
let amount_wei = new BigNumber(amount_ether).shiftedBy(18).toString()
await this.state.pcrContract.methods.setPayoutAmount(amount_wei).send({from: this.state.account}).then(console.log)
}
Also for anyone troubleshooting, note that there are at least two BigNumber libraries: I believe this error comes from this one but be careful if you're reading docs from the ethers.js one because the syntax for the constructors is different.
I got this error as well. In my case, I forgot to update the ABI.
You can use the following module:
import converter form "ethereum-uint-converter"
And if you want to know more detail, click here.
I think this is the issue:
transaction = await contract.createMarketItem(
nftAddress,
tokenId,
{ value: (listingPrice) },
royalty,
index
);
{ value: (listingPrice) }, is supposed to be object that represents the amount of money you are sending along side the transaction and it should be the last parameter in the function. Because looks like you are creating an NFT market item and you have to pay the price of listing.
Since you are creating an nft, looks like you have a const { royalty } = formInput. I believe you wanted to send the nft price instead of { value: (listingPrice) }. so your transaction should be like this
transaction = await contract.createMarketItem(
nftAddress,
tokenId,
// I assume you want to send the nft price here from form
priceFromForm,
royalty,
index,
// this should be the last parameter
{ value: (listingPrice) }
);

Get USDC (or any non-ETH value) from transaction using web3.js

I am working with the web3.js library on a small project that subscribes to events on a smart contract for an NFT to track all sales on that project through OpenSea. This is working fine when the sale is completed using ETH, but when a sale is made using another form of payment (USDC, DAI, WETH, etc) that OpenSea offers, the value on the transaction from web3.js is coming back as 0, and it shows 0 on etherscan.io as well. Here is an example transaction representing this issue: https://etherscan.io/tx/0x17f050e3fb6d8f0bbb4d9b4e8cd477f8197a87a3a68b360a60a028d7b1037532. Any ideas on how to get the correct value, no matter what type of currency is used?
Ex of
web3.eth.getTransaction('0x17f050e3fb6d8f0bbb4d9b4e8cd477f8197a87a3a68b360a60a028d7b1037532').then((response) => {
console.log(response);
});
Response
{
accessList: [],
blockHash: '0x6140d3cb5c271fb351e0a6e9e35b32cf0607ad526152f40f2d98107a97b0212b',
blockNumber: 13545512,
chainId: '0x1',
from: '0x3F4D7b0Eba8CB40D94713023d9Dc02FdB0a5169C',
gas: 391246,
gasPrice: '157998874325',
hash: '0x17f050e3fb6d8f0bbb4d9b4e8cd477f8197a87a3a68b360a60a028d7b1037532',
input: '0xab834bab0000000000000000000000007be8076f4ea4a4ad08075c2508e481d6c946d12b00000000000000000000000074144fb8749f99382091118f7487f4a541be6d7700000000000000000000000000000000000000000000000000000000000000000000000000000000000000005b3256965e7c3cf26e11fcaf296dfc8807c01073000000000000000000000000bd3531da5cf5857e7cfaa92426877b022e612cf80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000007be8076f4ea4a4ad08075c2508e481d6c946d12b0000000000000000000000003f4d7b0eba8cb40d94713023d9dc02fdb0a5169c00000000000000000000000074144fb8749f99382091118f7487f4a541be6d770000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bd3531da5cf5857e7cfaa92426877b022e612cf80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000226000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000165a0bc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000618193b7000000000000000000000000000000000000000000000000000000006182e53b690c3f82a9b6570ef2de1af15e15942c931dba8a2bd9d5012b74b213beea4da400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000226000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000165a0bc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006182d5b70000000000000000000000000000000000000000000000000000000000000000065ae400a094fafb63173dcf4c7861ca0264920136639dbe27b5956aa6282bb00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006a0000000000000000000000000000000000000000000000000000000000000074000000000000000000000000000000000000000000000000000000000000007e0000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000001c08da96edfaed45809c68db44cabb487dba56fd196fe46d02d679f0161dee94293eaedf214e043bc4d2e3dd69cce82e21ead967d7f1cd8fc505c04781e2d144e308da96edfaed45809c68db44cabb487dba56fd196fe46d02d679f0161dee94293eaedf214e043bc4d2e3dd69cce82e21ead967d7f1cd8fc505c04781e2d144e35c5321ae45550685308a405827575e3d6b4a84aa000000000000000000000000000000000000000000000000000000000000000000000000000000000000006423b872dd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000074144fb8749f99382091118f7487f4a541be6d7700000000000000000000000000000000000000000000000000000000000008ff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006423b872dd0000000000000000000000003f4d7b0eba8cb40d94713023d9dc02fdb0a5169c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008ff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006400000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: '201893464202',
maxPriorityFeePerGas: '1500000000',
nonce: 19,
r: '0x4c8e6fbe4a49439d957e0e725f3f7e897329557a972863143ff2a34458ab190',
s: '0x25afd4caa4cdc1f93923b5f41a89bc191d0e52c27a36e51e3015ed08560fea5d',
to: '0x7Be8076f4EA4A4AD08075C2508e481d6C946D12b',
transactionIndex: 328,
type: 2,
v: '0x1',
value: '0'
}
You can get the transaction receipt containing event logs. In this case, you're looking for the Transfer event emitted by the ERC-20 contract, where the ERC-20 token sender is the same as the NFT receiver.
const run = async () => {
const receipt = await web3.eth.getTransactionReceipt('0x17f050e3fb6d8f0bbb4d9b4e8cd477f8197a87a3a68b360a60a028d7b1037532');
const nftReceiver = _getNFTReceiver(receipt.logs);
for (let log of receipt.logs) {
// keccak256 of "Transfer(address,address,uint256)"
if (log.topics[0] === '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
// ERC20 sender (1st param of the Transfer event) is the same as the NFT receiver
&& web3.eth.abi.decodeParameter('address', log.topics[1]).toLowerCase() === nftReceiver.toLowerCase()
// ERC-20 Transfer returns the value in `data`, while ERC-721 has the same signature but returns empty data
&& log.data !== '0x'
) {
console.log('ERC-20 token contract: ', log.address);
console.log('ERC-20 token amount (incl. decimals): ', web3.eth.abi.decodeParameter('uint256', log.data));
}
}
}
const _getNFTReceiver = (logs) => {
for (let log of logs) {
// keccak256 of "Transfer(address,address,uint256)"
if (log.topics[0] === '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
// ERC-20 Transfer returns the value in `data`, while ERC-721 has the same signature but returns empty data
&& log.data === '0x'
) {
// 2nd parameter of the `Transfer()` event contains the NFT receiver
return web3.eth.abi.decodeParameter('address', log.topics[2]);
}
}
return null;
}
run();
prints:
Token contract: 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Token amount (incl. decimals): 6000000000

How to interact with the deployed ERC20(openzeppelin ERC20 contract) token and transfer token from user address X to address Y?

I've created a simple Contract extending openzeppelin ERC20.
I'm trying to transfer token from one address to another.
Contract code is as below:
File name: Token.sol
pragma solidity ^0.7.0;
import "#openzeppelin/contracts/token/ERC20/ERC20.sol";
contract Token is ERC20 {
uint256 public INITIAL_SUPPLY = 100000;
constructor() ERC20("My Token", "MYT") {
_mint(msg.sender, INITIAL_SUPPLY);
}
}
Code within test file:
const { expect } = require("chai");
describe("Send token from second address in the block", function () {
it("Send 100 MYT to the third account", async function () {
const Token = await ethers.getContractFactory("Token");
const token = await Token.deploy();
// get accounts from the network
const [owner, secondAccount, thirdAccount] = await ethers.getSigners();
// send some credit to the second account
await token.transfer(secondAccount.address, 500);
// Approve token transfer
await token.approve(secondAccount.address, 200);
// Transfer credit from second account to the third account (This step is not working)
await token.transferFrom(secondAccount.address, thirdAccount.address, 100);
});
});
Error received:
Error: VM Exception while processing transaction: reverted with reason string 'ERC20: transfer amount exceeds allowance'
at Token.sub (#openzeppelin/contracts/math/SafeMath.sol:171)
at Token.transferFrom (#openzeppelin/contracts/token/ERC20/ERC20.sol:154)
at processTicksAndRejections (internal/process/task_queues.js:94:5)
at runNextTicks (internal/process/task_queues.js:63:3)
at listOnTimeout (internal/timers.js:501:9)
at processTimers (internal/timers.js:475:7)
at EthModule._estimateGasAction (node_modules/hardhat/src/internal/hardhat-network/provider/modules/eth.ts:421:9)
at HardhatNetworkProvider.request (node_modules/hardhat/src/internal/hardhat-network/provider/provider.ts:105:18)
at EthersProviderWrapper.send (node_modules/#nomiclabs/hardhat-ethers/src/internal/ethers-provider-wrapper.ts:13:20)
Thank you for the help.
If you check Approve if erc20, the function arguments expect address _spender, uint256 _value as arguments. In your case you approved secondAccount to withdraw 200 tokens.
It means secondAccount can transfer those token to his account from the token contract. To do that, you try await token.connect(secondAccount).transferFrom(token.address,secondAccount.address,100);

Switch Metamask Network to chain 1 (Ethereum Mainnet)

This past week Metamask introduced a new function called "wallet_addEthereumChain" which allows the user to automatically add a new Etheruem RPC to their wallet when prompted. This function also allows the user to change the network they are connected to, for example, if I already have Binance Smart Chain connected to metamask, calling wallet_addEthereumChain changes the active network to BSC. However, when trying this for the Ethereum chain, it gives an error that you cannot add mainnet RPC.
I have used the following code to change to change from Ethereum Mainnet to Binance Smart Chain, and it works fine:
switchToBinance: async function () {
let ethereum = window.ethereum;
const data = [{
chainId: '0x38',
chainName: 'Binance Smart Chain',
nativeCurrency:
{
name: 'BNB',
symbol: 'BNB',
decimals: 18
},
rpcUrls: ['https://bsc-dataseed.binance.org/'],
blockExplorerUrls: ['https://bscscan.com/'],
}]
/* eslint-disable */
const tx = await ethereum.request({method: 'wallet_addEthereumChain', params:data}).catch()
if (tx) {
console.log(tx)
}
},
However when I try the Exact thing, metamask throws an exception saying I cannot add a mainnet RPC:
switchToEthereum: async function () {
let ethereum = window.ethereum;
const data = [{
chainId: '0x1',
chainName: 'Ethereum',
nativeCurrency: {
name: 'Ethereum',
symbol: 'ETH',
decimals: 18,
},
rpcUrls: ['https://mainnet.infura.io/v3/undefined'],
blockExplorerUrls: ['https://etherscan.io'],
}]
/* eslint-disable */
const tx = await ethereum.request({method: 'wallet_addEthereumChain', params:data}).catch()
if (tx) {
console.log(tx)
}
},
However, the request for adding a new RPC connection, and changing the active RPC connection is the same. So is there a way to change the active Ethereum provider from a custom chain to Mainnet (chain ID-1)
as this issue comment point, for security reason wallet_addEthereumChain not support mainnet. But there a new EIP to resolve this issue, follow EIP-3326 to find release info, and this discuss to see draft progrss.
chainId: '0x38' // error because await not decimal
Number('0x38').toString(10) //56 - chain ID of BSC
Right:
chainId: `0x${Number(56).toString(16)}`
OR:
chainId: `0x86`
Simply you have to prefix 0x with the chainId.
Example:
await ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: '0x1' }],
})

Why the function of address.transfer() only can send 1ether in my contract?

Why the function of address.transfer() only can send 1ether in my contract? It will report errors when the value greater than 1 ether or less than 1 ether.
My code:
pragma solidity ^0.4.24;
contract Lottery{
uint public winn;
//抽奖者
struct lottery{
uint money; //奖金
address name; //名字(地址)
}
//庄家地址
address private bankerAddress = 0x8b3234612c7D3b805F4c9CF1Aa55Cc47B82A0769;
//得奖者数量
uint32 public count;
//得奖者集合
lottery public l;
//初始化
constructor()public {
count=0;
}
event getRandom (
uint indexed _win);
//开始抽奖的函数,返回奖金
function start() public payable{
//随机数生成
uint win= uint(keccak256(now, msg.sender, now)) % 100;
win = win % 12 +1;
winn = win;
l.money = winn;
l.name = msg.sender;
//发奖金操作
msg.sender.transfer(winn*10000000000000000);
//监听事件
getRandom(winn);
}
function () public payable{}
}
Below is javascript code:
lotteryInstance.start.sendTransaction({
from: '0x8b3234612c7D3b805F4c9CF1Aa55Cc47B82A0769',
value: 10000000000000000,
gas: 210000,
gasPrice: web3.toWei('1000', 'gwei')
});
And the error messages:
MetaMask - RPC Error: Error: Error: [ethjs-rpc] rpc error with payload {"id":7309825988666,"jsonrpc":"2.0","params":["0xf8721f85e8d4a510008303345094bcd5d351e5850774d1f720328dac1c8732d68eb7872386f26fc1000084be9a6555822d45a0a07b903c493ba6dee96a54bc74344d1c668cd3d9e8a7c757fdc5daa664ff4271a01bb6799a02054ecf1c8c5be51d6748b2c086eb330091b2e0500d43e756666f69"],"method":"eth_sendRawTransaction"} Error: VM Exception while processing transaction: revert
Uncaught (in promise) Error: Error: Error: [ethjs-rpc] rpc error with payload {"id":7309825988666,"jsonrpc":"2.0","params":["0xf8721f85e8d4a510008303345094bcd5d351e5850774d1f720328dac1c8732d68eb7872386f26fc1000084be9a6555822d45a0a07b903c493ba6dee96a54bc74344d1c668cd3d9e8a7c757fdc5daa664ff4271a01bb6799a02054ecf1c8c5be51d6748b2c086eb330091b2e0500d43e756666f69"],"method":"eth_sendRawTransaction"} Error: VM Exception while processing transaction: revert
In your JS code below:
lotteryInstance.start.sendTransaction({
from: '0x8b3234612c7D3b805F4c9CF1Aa55Cc47B82A0769',
value: 10000000000000000, // Here is the amount of ETH you give to start fuction
gas: 210000,
gasPrice: web3.toWei('1000', 'gwei')
});
The tx is failing maybe because you're giving (ETH) an amount that you may not have! Do you have value: 10000000000000000 many Ethers?
If no, then pass some amount you have, as you haven't given any conditions about the amount in your contract, you can give the smallest possible amount.
PS: The ETH your contract is receiving is not stored anywhere, it may lead to loss of Ethers.