Can't transfer Matic to smart contract - solidity

I'm trying to transfer Matic to my smart contract in the Mumbai test net using ethers.
I'm using the most basic contract which comes with hardhat - Greeter. sol.
The error I keep getting is(in the polygonscan-mumbai):
The client side transfer using ethers:
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const erc20Contract = new ethers.Contract("0x0000000000000000000000000000000000001010", erc20abi, signer);
const parsedAmount = ethers.utils.parseUnits(amount.toString(), 'ether');
const transferTokens = await erc20Contract.transfer(contractAddress , parsedAmount);
Greeter.sol:
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import "hardhat/console.sol";
contract Greeter {
string private greeting;
constructor(string memory _greeting) {
console.log("Deploying a Greeter with greeting:", _greeting);
greeting = _greeting;
}
function greet() public view returns (string memory) {
return greeting;
}
function setGreeting(string memory _greeting) public {
console.log("Changing greeting from '%s' to '%s'", greeting, _greeting);
greeting = _greeting;
}
}
Also when I manually try to send Matic to the smart contract using metamsk it's giving me the same error(only to contracts, not other wallets).
But if I try other tokens it works fine - am I missing something?

Your contract needs to implement either receive() or fallback() function to be able to accept native currency of the network.
Docs: https://docs.soliditylang.org/en/v0.8.13/contracts.html#special-functions
Example:
contract Greeter {
// ...
receive() external payable {
}
}

Related

How to access function of a contract instantiated with new?

I'm instantiating the contract called "Elector", applying new inside the function below, so far it works and the result is this one:
The contract is instantiated in memory with this address.
So, how do I access the getInformation() function inside this contract to use both in this main contract and in ethers in the dApp?
MAIN CONTRACT:
function updateConfirmedVotes(uint candidateId, VoteType electorVoteType) public {
_updateTotalElectoresVoted();
_pollingByCandidate[candidateId].votes.total += 1;
_pollingByCandidate[candidateId].votes.totalPercentage = _calculePercentageOfVote(_pollingByCandidate[candidateId].votes.total);
_pollingByCandidate[candidateId].electors.push(new _Elector({wallet: msg.sender, vote: electorVoteType}));
}
ELECTOR CONTRACT:
contract _Elector {
address private _wallet;
VoteType private _vote = VoteType.DID_NOT_VOTED;
constructor(address wallet, VoteType vote) {
_wallet = wallet;
_vote = vote;
}
function getInformation() external view returns (address, VoteType) {
return (_wallet, _vote);
}
}
Define a contract type variable within your Main contract, passing it pointer to the newly deployed Elector contract. Then you can invoke external/public functions defined by the contract type on the external address.
pragma solidity ^0.8;
contract Elector {
// ...
}
contract Main {
Elector elector;
function deployElector() external {
// returns pointer to the newly deployed contract
elector = new Elector();
}
function getInformationFromElector() external view returns (address, Elector.VoteType) {
// calls the external contract
return elector.getInformation();
}
}

Calling a solidity payable function with etherjs

I have a basic Solidity smart contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract TestContract {
uint256 public _blockTimestamp;
function accumulated() public payable returns (uint256) {
_blockTimestamp = block.timestamp;
return 1;
}
}
And using remix IDE I can compile it and check the value of _blockTimestamp changing after executing the accumulated function.
Now, I can deploy the contract and check the value of the public variable without any problem with etherjs:
const provider = new providers.JsonRpcProvider(getEnv('RINKEBY_NODE_URL'));
const wallet = new Wallet(getEnv('ROPSTEN_PRIVATE_KEY'), provider);
const TestContract = TestContract__factory.connect(getEnv('TEST_CONTRACT'), provider);
const _blockTimestamp = await NFTLTokenContract._blockTimestamp();
The problem is when I try to execute the function accumulated. Being a payable function I need a signer:
const provider = new providers.JsonRpcProvider(getEnv('RINKEBY_NODE_URL'));
const wallet = new Wallet(getEnv('ROPSTEN_PRIVATE_KEY'), provider);
const TestContract = TestContract__factory.connect(getEnv('TEST_CONTRACT'), provider);
const _blockTimestamp = await TestContract._blockTimestamp();
const accumulated = await TestContract.accumulated('1', wallet);
But I still get the error:
Error: sending a transaction requires a signer
(operation="sendTransaction", code=UNSUPPORTED_OPERATION,
version=contracts/5.5.0)
What am I missing?
You are trying to call a non-view function by using only a provider, you must provide a signer in order to call the function.
Try connecting to the contract using the wallet as the signer:
const TestContract = TestContract__factory.connect(getEnv('TEST_CONTRACT'), wallet);

Testing a Payable Function in Solidity

So I'm trying to test a payable function on the following smart contract here using the truffle framework:
contract FundMe {
using SafeMathChainlink for uint256;
mapping(address => uint256) public addressToAmountFunded;
address[] public funders;
address public owner;
AggregatorV3Interface public priceFeed;
constructor(address _priceFeed) public {
priceFeed = AggregatorV3Interface(_priceFeed);
owner = msg.sender;
}
function fund() public payable {
uint256 mimimumUSD = 50 * 10**18;
require(
getConversionRate(msg.value) >= mimimumUSD,
"You need to spend more ETH!"
);
addressToAmountFunded[msg.sender] += msg.value;
funders.push(msg.sender);
}
I specifically want to test the payable function, and I've seen a few things on the internet where people create other contracts with initial balances and then send their testing contract some eth. But I would just like to grab a local ganache wallet and send some eth to the contract and then test that, if someone could show me some test javascript code to wrap my head around this that would be much appreciated!
For a contract to be able to receive ETH (or any native token - BNB on Binance Smart Chain, TRX on Tron network, ...) without invoking any function, you need to define at least one of these functions receive() (docs) or fallback() (docs).
contract FundMe {
// intentionally missing the `function` keyword
receive() external payable {
// can be empty
}
// ... rest of your code
}
Then you can send a regular transaction to the contract address in truffle (docs):
const instance = await MyContract.at(contractAddress);
await instance.send(web3.toWei(1, "ether"));
Note that because receive() and fallback() are not regular functions, you cannot invoke them using the truffle autogenerated methods: myContract.functionName()
If you want to execute a payable function sending it ETH, you can use the transaction params (docs). It's always the last argument, after all of the regular function arguments.
const instance = await MyContract.at(contractAddress);
await instance.fund({
value: web3.toWei(1, "ether")
});
Note: If the fund() function had 1 argument (let's say a bool), the transaction params would be the 2nd:
await instance.fund(true, {
value: web3.toWei(1, "ether")
});

How to interact with UUPS upgradeable contract using web3?

I have an ERC20 token already deployed on the Ropsten testnet with two versions.
V1 is a simple unproxied ERC20 token and looks like this:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
import "#openzeppelin/contracts/token/ERC20/ERC20.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
contract MyToken is ERC20, Ownable {
constructor() ERC20("MyToken", "MTK") {}
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
}
I can interact with this contract using web3:
const Web3 = require('web3');
const MyToken = require('./build/contracts/MyToken.json');
const HDWalletProvider = require('#truffle/hdwallet-provider');
const provider = new HDWalletProvider(process.env.ACCOUNT_SECRET, process.env.INFURA_URL);
const web3 = new Web3(provider);
const contract = new web3.eth.Contract(MyToken.abi, process.env.CONTRACT_ADDRESS);
For example, here is a call that retrieves the owner of the contract:
await contract.methods.owner().call();
On the other hand, V2 is a UUPS upgradeable contract which looks like this:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
import "#openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "#openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "#openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "#openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
contract MyToken is Initializable, ERC20Upgradeable, OwnableUpgradeable, UUPSUpgradeable {
/// #custom:oz-upgrades-unsafe-allow constructor
constructor() initializer {}
function initialize() initializer public {
__ERC20_init("MyToken", "MTK");
__Ownable_init();
__UUPSUpgradeable_init();
}
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
function _authorizeUpgrade(address newImplementation)
internal
onlyOwner
override
{}
}
To interact with V2 using the same web3 nodejs code, I tried updating the build/abi as well as process.env.CONTRACT_ADDRESS from V1's address to V2's. However, whenever I retrieve the owner using the same code, it always returns the zero address.
I think the call should be proxied or something, but I don't know how and I can't find resources (docs/tutorials) on this.
Contracts V1 and V2 are generated from wizard.openzeppelin.com. Nothing was modified.
V2 passes the get owner, symbol, and name truffle tests.
I'm not really sure what's the best practice regarding the initializer {} modifier in constructor(). My guess is that OpenZeppelin recommends to use it with constructor in case you're also setting other variables in constructor and not using other init function.
However, the effect of your implementation is that it simply sets the initialized variable to true without executing the top-level initialize() function - effectively not setting the owner variable (and others, such as name and symbol).
I was able to perform a quick fix by removing the initializer modifier (since it's already with the initialize() function, and calling the initialize() from the constructor. Please check if there aren't any side effects to it.
// removed the modifier
// added the call to `initialize()`
constructor() {
initialize();
}
// stays the same
function initialize() initializer public {

Truffle test in solidity with sending value

duplicating my question from SA:
I have a simple contract with public function, that can receive value and do something based on that value:
pragma solidity >= 0.8.0 < 0.9.0;
contract ContractA {
uint public boughtItems = 0;
uint price = 10;
address [] buyers;
function buySomething() public payable {
require(msg.value >= price, "Sent value is lower");
boughtItems++;
buyers.push(msg.sender);
}
}
and in test folder of my Truffle project I have test contract:
pragma solidity >=0.8.0 <0.9.0;
import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/TicketsRoutes.sol";
contract TestTicketsRoutes {
ContractA instance;
address account1 = 0xD8Ce37FA3A1A61623705dac5dCb708Bb5eb9a125;
function beforeAll() public {
instance = new ContractA();
}
function testBuying() public {
//Here I need to invoke buySomething with specific value from specific address
instance.buySomething();
Assert.equal(instance.boughtItems, 1, "Routes amount is not equal");
}
}
How do I invoke function of ContractA in my TestContractA with passing value and sender?
You can use the low-level call() Solidity function to pass a value.
(bool success, bytes memory returnedData) = address(instance).call{value: 1 ether}(
abi.encode(instance.buySomething.selector)
);
But, in order to execute the buySomething() function from a different sender, you need to send it from a different address than the TestTicketsRoutes deployed address.
So you'll need to change your approach and perform the test from an off-chain script (instead of the on-chain test contract) that allows you to sign the transaction from a different sender. Since you tagged the question truffle, here's an example of executing a contract function using the Truffle JS suite (docs).
const instance = await MyContract.at(contractAddress);
const tx = await instance.buySomething({
from: senderAddress,
value: web3.toWei(1, "ether")
});