Ethers.js, send money to a smart contract (receive function) - solidity

I have a smart contract with a receive function :
receive() external payable {
Wallets[msg.sender] += msg.value;
}
I have a front end and I want to send Ethers to this smart contract using the receive() function.
async function transfer() {
if(typeof window.ethereum !== 'undefined') {
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const contract = new ethers.Contract(WalletAddress, Wallet.abi, signer);
const transaction = await contract.send({
from: accounts[0],
value: amount
})
await transaction.wait();
alert('ok');
setAmount('');
getBalance();
}
}
Saldy, there is no "send" function, what is the function I need to use there ?
Thx a lot !

When you want to invoke the receive() function, you need to send a transaction to the contract address with empty data field. The same way as if you were sending ETH to a non-contract address.
// not defining `data` field will use the default value - empty data
const transaction = signer.sendTransaction({
from: accounts[0],
to: WalletAddress,
value: amount
});

Related

Re-send a transaction using Alchemy to speed up erc20 transaction

I am building a crypto wallet app on the ethereum chain and have set it up so that I can send transactions. Next I would like to be able to speed up transactions and understand how this is done. As far as I know you send another transaction with the same nonce but more gas. To sped up a transaction where I am sending ETH this is straightforward.
Here is the original function to send the transaction:
export const sendTransaction = async (currentWallet, toAddress, value) => {
// // Create Provider
const url = "https://eth-mainnet.g.alchemy.com/v2/" + ALCHEMY_API_KEY;
const customHttpProvider = await new ethers.providers.JsonRpcProvider(url);
// // Create wallet and connect to provider
const wallet = await new Wallet(currentWallet.pk, customHttpProvider);
const currentChainId = await getChainId();
const feeData = await customHttpProvider.getFeeData();
let gasPrice = feeData.maxFeePerGas;
const transaction = {
to: toAddress,
value: Utils.parseEther(value),
gasLimit: "21000",
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
maxFeePerGas: feeData.maxFeePerGas,
nonce: await alchemy.core.getTransactionCount(currentWallet.walletInstance.address),
type: 2,
chainId: currentChainId
// chainId: 5
}
const rawTransaction = await wallet.signTransaction(transaction);
try {
const sentTransaction = await alchemy.core.sendTransaction(rawTransaction);
return sentTransaction;
} catch (err) {
return null;
}
}
and then to speed this up I would run the same function but replace the gas and set the same nonce.
However, with erc20 tokens my function to transfer the amount specified like so:
export const sendErc20Transaction = async (token, currentWallet, toAddress, value) => {
try {
// Create Provider
const url = "https://eth-mainnet.g.alchemy.com/v2/" + ALCHEMY_API_KEY;
const customHttpProvider = await new ethers.providers.JsonRpcProvider(url);
// Create wallet and connect to provider
const wallet = await new Wallet(currentWallet.pk, customHttpProvider);
// Dynamically get ABI
const abi = await getTokenABI(token, 'erc20');
// Create Smart Contract
const currentContract = new Contract(token.contractAddress, abi, wallet);
// // Send Transaction
const transaction = await currentContract.transfer(toAddress, Utils.parseUnits(value));
return transaction;
} catch (err) {
return null;
}
}
I don't see anywhere within this function where I can specify the gas or the nonce.
How would I resend this same transaction, still sending the same token, with higher gas and the same nonce?

Testing the safeMint function

I am trying to successfully write a unit test for my safeMint function below.
Here is my current test:
const assert = require("assert");
const { accounts } = require("#openzeppelin/test-environment");
const ComNFT = artifacts.require("ComNFT");
const { expect } = require("chai");
// describe('ComNFT', () => {
// let accounts;
let comNFT;
beforeEach(async () => {
accounts = await web3.eth.getAccounts();
comNFT = await ComNFT.new({ from: accounts[0] });
//comNFT = await ComNFT.at("");
// console.log(comNFT.address);
});
it('should fail when called by a non-owner account', async () => {
try {
await web3.eth.sendTransaction({
from: accounts[1], // The non-owner account
to: comNFT.address, // The contract address
data: comNFT.methods.safeMint(accounts[1], 'token URI').encodeABI() // The function call and arguments, encoded as ABI
});
assert.fail('Expected error not thrown');
} catch (error) {
assert(error.message.includes('onlyOwner'), 'Expected "onlyOwner" error message not found');
}
});
it('should be able to mint a new token', async () => {
await web3.eth.sendTransaction({
from: accounts[0], // The owner account
to: comNFT.address, // The contract address
data: comNFT.methods.safeMint(accounts[1], 'token URI').encodeABI() // The function call and arguments, encoded as ABI
});
const tokenURI = await comNFT.tokenURI(1); // Assume the token ID is 1
assert.equal(tokenURI, 'token URI');
});
function safeMint(address to, string memory uri) public onlyOwner {
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
}
I am getting a failure message after i run npx truffle test " 1) "before each" hook for "should fail when called by a non-owner account"
0 passing (3s)
1 failing
"before each" hook for "should fail when called by a non-owner account":
TypeError: Assignment to constant variable.
at Context. (test/ComNFT.js:11:12)"
Can someone suggest a way of me successfully writing a test that calls safeMint?
The other test "itshould fail when called by a non-owner account" is also not running?
Thanks
safeMint function has onlyowner modifir, you should call safeMint function with the owner address that is accounts[0] (not accounts[1])

How can I link library and contract in one file?

I am writing smart contract in solidity with hardhat and facing error like missing links for the following libraries.
I think I should do something with sample-nft-test.js, but I don't have any idea what to do.
Does any body know how to solve the problem?
% bin/npm-install
% bin/test
SampleNft
1) "before each" hook for "should be able to return token name by token id"
0 passing (2s)
1 failing
1) SampleNft
"before each" hook for "should be able to return token name by token id":
NomicLabsHardhatPluginError: The contract SampleNft is missing links for the following libraries:
* contracts/SampleNft.sol:TokenTrait
Learn more about linking contracts at https://hardhat.org/plugins/nomiclabs-hardhat-ethers.html#library-linking
at collectLibrariesAndLink (node_modules/#nomiclabs/hardhat-ethers/src/internal/helpers.ts:258:11)
at getContractFactoryFromArtifact (node_modules/#nomiclabs/hardhat-ethers/src/internal/helpers.ts:149:32)
at getContractFactory (node_modules/#nomiclabs/hardhat-ethers/src/internal/helpers.ts:93:12)
at Context.<anonymous> (test/sample-nft-test.js:13:29)
Source codes to reproduce the error
ArakakiEth/SampleNft
sample-nft-test.js
const {
expect,
} = require("chai");
const {
ethers,
} = require("hardhat");
describe("SampleNft", () => {
beforeEach(async () => {
signers = await ethers.getSigners();
const contractFactory = await ethers.getContractFactory("SampleNft", {
signer: signers[0],
});
contract = await contractFactory.deploy();
});
it("should be able to return token name by token id", async () => {
await contract.deployed();
await contract.getName();
});
});
SampleNft.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.4;
struct Token {
string name;
}
library TokenTrait {
function getName(Token memory token) external pure returns (string memory) {
return token.name;
}
}
contract SampleNft {
using TokenTrait for Token;
uint tokenId;
mapping(uint => Token) private _tokenIdTokenMap;
constructor() {
tokenId = 0;
}
function mint() external {
Token memory token = Token("First Token");
_tokenIdTokenMap[tokenId] = token;
}
function getName() external view returns (string memory) {
Token memory token = _tokenIdTokenMap[tokenId];
return token.getName();
}
}
library TokenTrait {
function getName(Token memory token) external pure returns (string memory) {
return token.name;
}
}
You set the visibility of the function as external.
In this case, you have to deploy the library and then need to specify a link for the library in contract deployment.
The way to fix your issue is to make the function visibility internal.
The library link should be added when deploying contract.
Please replace with the following code for beforeEach part
beforeEach(async () => {
signers = await ethers.getSigners();
const Lib = await ethers.getContractFactory("TokenTrait");
const lib = await Lib.deploy();
await lib.deployed();
const contractFactory = await ethers.getContractFactory("SampleNft", {
signer: signers[0],
libraries: {
TokenTrait: lib.address,
},
});
contract = await contractFactory.deploy();
});

How to add account to web3 object?

i have private key and this is how i access account (Binance Smart Chain network):
const web3 = new Web3('https://bsc-dataseed1.binance.org:443')
const account = await web3.eth.accounts.privateKeyToAccount(pk)
So, i have account object,
{ address: '0x...', privateKey: '0x...', signTransaction: [Function: signTransaction], sign: [Function: sign], encrypt: [Function: encrypt] }
I want to send() method on BEP-20 token address:
const contract = new web3.eth.Contract(ABI, address)
const tx = await contract.methods.transfer(address, amount).send({
from: account.address
})
But i am getting error Error: Returned error: unknown account
Do i have to sign each transaction and then send it?
Maybe there is a way when provider signs transaction for me?
How to do it? How to add account object to web3.eth.accounts ?
In here:
const tx = await contract.methods.transfer(address, amount).send({
from: account.address
})
You are essentially asking the Ethereum node that you're communicating with, to sign the transaction for you.
In order for this to work, you first need to unlock the specified account on that node, by sending an appropriate request.
Alternatively (and probably more securely), you can sign the transaction yourself:
async function signAndSend(web3, account, gasPrice, transaction, value = 0) {
while (true) {
try {
const options = {
to : transaction._parent._address,
data : transaction.encodeABI(),
gas : await transaction.estimateGas({from: account.address, value: value}),
gasPrice: gasPrice,
value : value,
};
const signed = await web3.eth.accounts.signTransaction(options, account.privateKey);
const receipt = await web3.eth.sendSignedTransaction(signed.rawTransaction);
return receipt;
}
catch (error) {
console.log(error.message);
}
}
}
async function yourFunc(web3, account, gasPrice, address, amount) {
const contract = new web3.eth.Contract(ABI, address)
const receipt = await signAndSend(web3, account, gasPrice, contract.methods.transfer(address, amount));
}
BTW, I find it quite odd that you are trying to transfer tokens from your account into the Token contract.

How to send wei/eth to contract address? (using truffle javascript test)

I'm trying to send wei/eth to the address of my solidity contract which have an external payable fallback function. My truffle javascript test below doesn't result in the balance of instance.address getting any wei. Is not instance.address the smart contract address receiving wei? Can anyone spot why console.logging the balance results in 0? Or spot what I'm missing?
Thanks!
const TestContract = artifacts.require("TestContract");
contract('TestContract', async (accounts) => {
it('should send 1 ether to TestContract', async () => {
let instance = await TestContract.deployed();
instance.send({from: accounts[1], value: 1000000000000000000});
let balance = await web3.eth.getBalance(instance.address);
console.log('instance.address balance: ' + parseInt(balance));
)}
Since you are not providing the contract in your question, I'm making an assumption here that your contract looks like below.
File path:
./contracts/TestContract.sol
pragma solidity ^0.4.23;
contract TestContract {
// all logic goes here...
function() public payable {
// payable fallback to receive and store ETH
}
}
With that, if you want to send ETH from accounts[1] to the TestContract using JS, here is how to do it:
File path:
./test/TestContract.js
const tc = artifacts.require("TestContract");
contract('TestContract', async (accounts) => {
let instance;
// Runs before all tests in this block.
// Read about .new() VS .deployed() here:
// https://twitter.com/zulhhandyplast/status/1026181801239171072
before(async () => {
instance = await tc.new();
})
it('TestContract balance should starts with 0 ETH', async () => {
let balance = await web3.eth.getBalance(instance.address);
assert.equal(balance, 0);
})
it('TestContract balance should has 1 ETH after deposit', async () => {
let one_eth = web3.toWei(1, "ether");
await web3.eth.sendTransaction({from: accounts[1], to: instance.address, value: one_eth});
let balance_wei = await web3.eth.getBalance(instance.address);
let balance_ether = web3.fromWei(balance_wei.toNumber(), "ether");
assert.equal(balance_ether, 1);
})
})
See my comment in above code to learn more about the differences between .new() and .deployed() keyword in Truffle.
Full source code for my solution can be found here.
Solved! I forgot I had to send a transaction via web3 and eth like this:
web3.eth.sendTransaction({})
Thanks anyway!
You myst send to an address, not an object.
instance.send({from: accounts[1], value: 1000000000000000000});
For using Web3.js v1.4.0 in your truffle test files.
const SolidityTest = artifacts.require('SolidityTest');
contract('SolidityTest', (accounts) => {
let solidityTest;
before(async () => {
solidityTest = await SolidityTest.new();
})
it('Test something', async () => {
// Send 100 wei to the contract.
// `sendEth` is your payable method.
await solidityTest.sendEth({from: accounts[1], value: 100});
// Check account 1 balance.
let acc1Balance = await web3.eth.getBalance(accounts[1]);
// Convert the unit from wei to eth
acc1Balance = web3.utils.fromWei(acc1Balance, 'ether')
console.log('acc1 balance:', acc1Balance)
// Check the contract balance.
let contractBalance = await web3.eth.getBalance(solidityTest.address);
contractBalance = web3.utils.fromWei(contractBalance, 'ether')
console.log('contract balance:', contractBalance)
});
});
Hello you just forgot the await before the instance.send, thus making the get balance call to do not “see” the ether sent, hope it will help future readers