Solidity - why does fallback() get called even though address.call{value:msg.value}("") does not have data? - solidity

The following contract calls another contract using an interface method (code to change):
pragma solidity 0.8.7;
interface MyStorage {
function setStorageValue(uint256) external;
}
contract StorageFactory {
uint256 storageValue;
constructor(uint256 _storageValue) {
storage = _storageValue;
}
function initStorage(MyStorage store) public payable {
store.setStorageValue(storageValue);
address payable storeAddress = payable(address(store));
storeAddress.call{value: msg.value}("");
}
}
Following is the StorageContract (code cannot be changed):
pragma solidity 0.8.7;
contract Storage {
int _storageValue;
function setStorageValue(int storageValue) public {
_storageValue = storageValue;
}
receive() external payable {
require(_storageValue == -1 || address(this).balance <= uint(_storageValue), "Invalid storage value");
}
fallback() external {
_storageValue = -1;
}
}
I use a test to call initStorage of the first contract by passing a Storage object, where the test is meant to fail because the value is set to a large amount. But somehow, the fallback() function seems to get called, setting the value to -1. I can't figure out why. Any help is appreciated.

Due to the solidity doc:
The fallback function is executed on a call to the contract if none of the other functions match the given function signature, or if no data was supplied at all and there is no receive Ether function. The fallback function always receives data, but in order to also receive Ether it must be marked payable.
Your function getting called because there's no overloading for the function
function setStorageValue(uint256 storageValue) public
So change the storageValue from int to uint256 will help.

Related

How to limit token receiver callers to accepted token address?

I want to create a payable token
which includes a function transferAndCall(TokenReceiver to, uint256 amount, bytes4 selector).
By calling this function, you can transfer tokens to the TokenReceiver smart contract address,
and then call onTransferReceived(address from,uint tokensPaid, bytes4 selector) on the receiver,
which in turn invokes a function specified in thebytes4 selector on the receiver.
Note that this is similar to/ inspired by ERC1363.
Here is a simplified version of my receivable token:
import "#openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MeowToken is ERC20 {
constructor() ERC20("MeowToken", "MEO") {
ERC20._mint(msg.sender, 10_000_000);
}
function transferAndCall(
TokenReceiver to,
uint256 amount,
bytes4 selector
) external {
ERC20.transfer(address(to), amount);
to.onTransferReceived(msg.sender, amount, selector);
}
}
And this is a token receiver:
contract TokenReceiver {
address acceptedToken;
event PurchaseMade(address from, uint tokensPaid);
modifier acceptedTokenOnly () {
require(msg.sender == address(acceptedToken), "Should be called only via the accepted token");
_;
}
constructor(address _acceptedToken) {
acceptedToken = _acceptedToken;
}
function onTransferReceived(
address from,
uint tokensPaid,
bytes4 selector
) public acceptedTokenOnly {
(bool success,) = address(this).call(abi.encodeWithSelector(selector, from, tokensPaid));
require(success, "Function call failed");
}
function purchase(address from, uint tokensPaid) public acceptedTokenOnly {
emit PurchaseMade(from, tokensPaid);
}
}
I want to make sure that public functions on the receiver are only called via the payable token.
For this reason I added acceptedTokenOnly modifier to both of them.
However after adding the modifier my test began to fail:
it('Transfer Tokens and call Purchase', async () => {
const tokenAmount = 100;
const tx = meowToken.transferAndCall(
tokenReceiver.address,
tokenAmount,
tokenReceiver.interface.getSighash('purchase'),
);
await expect(tx)
.to.emit(tokenReceiver, 'PurchaseMade')
.withArgs(deployer.address, tokenAmount);
});
1) Transfer and call
Transfer Tokens and call Purchase:
Error: VM Exception while processing transaction: reverted with reason string 'Function call failed'
Why does this happen?
How to make sure the receiver's functions are invoked only by the accepted token?
For reference, I am developing and testing smart contracts in Hardhat and deploying on RSK.
When you're doing this:
(bool success,) = address(this).call(abi.encodeWithSelector(selector, from, tokensPaid));
you're making an external call, meaning that msg.sender will become address(this).
Now the modifier acceptedTokenOnly during function purchase will fail since msg.sender isn't the token anymore.
Suggested changing the function to this:
function purchase(address from, uint tokensPaid) public {
require(msg.sender == address(this), "wrong sender");
emit PurchaseMade(from, tokensPaid);
}
The problem is, you are using low level call method, here:
​
(bool success,) = address(this).call(abi.encodeWithSelector(selector, from, tokensPaid));
​
This changes the value of msg.sender inside onTransferReceived from the accepted token to the receiver itself.
Here is one way to achieve what you want:
​
Replace call with delegatecall.
This will solve your problem instantly.
Unlike call, the delegatecall will invoke your function on behalf of the caller smart contract:
​
function onTransferReceived(
address from,
uint tokensPaid,
bytes4 selector
) public acceptedTokenOnly {
(bool success,) = address(this).delegatecall(abi.encodeWithSelector(selector, from, tokensPaid));
require(success, "Function call failed");
}
Apart from switching from call to delegatecall, as mentioned in #Juan's answer, there is a more "manual" approach:
​
Do not use call altogether, and instead invoke the functions by name.
This can be accomplished using an if ... else control structure that compares the selector parameter with the intended function selector (purchase):
​
function onTransferReceived(
address from,
uint tokensPaid,
bytes4 selector
) public acceptedTokenOnly {
if (selector == this.purchase.selector) {
purchase(from, tokensPaid);
} else {
revert("Call of an unknown function");
}
}
​
While this is more tedious to do, it might be preferable from a security point of view.
For example, if you wish to white-list the functions that you allow to be called through
this mechanism.
Note that the approach using call/ delegatecall exposes a potential vulnerability
for arbitrary (and possibly unintended) function execution.

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")
});

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")
});

What is the type of `this` object in solidity

In the following claimPayment function that is used to claim a payment made earlier to this contract, the line bytes32 message = prefixed(keccak256(abi.encodePacked(msg.sender, amount, nonce, this))); has this as part of the signed message. This makes me wonder what is this and what the type of this is. If I'm returning this in a function, what type is used for it? Thanks.
function claimPayment(uint256 amount, uint256 nonce, bytes memory signature) public {
require(!usedNonces[nonce]);
usedNonces[nonce] = true;
// this recreates the message that was signed on the client
bytes32 message = prefixed(keccak256(abi.encodePacked(msg.sender, amount, nonce, this)));
require(recoverSigner(message, signature) == owner);
payable(msg.sender).transfer(amount);
}
this is a pointer to the current class instance, as in many other programming languages. You can for example point to public methods:
pragma solidity ^0.8.0;
contract MyContract {
function foo() external {
this.bar();
}
function bar() public {
}
}
When this is typecasted, it takes a form of the address, where the current instance is deployed.
pragma solidity ^0.8.0;
contract MyContract {
function foo() external view returns (bytes memory) {
return abi.encodePacked(this);
}
function bar() external view returns (address) {
return address(this);
}
}

Solidity: Are these sentences the same? Or do they mean different things?

In order to calling a function 'isContract', with the parameter 'to' being an address, are valid both ways? :
to.isContract()
isContract(to)
Does Solidity allow both ways?
I have found both in different codes, and I don't know if just 'isContract(to)' is the right one, or if 'to.isContract()' means another different thing.
Thanks a lot for your help.
They're not the same.
to.isContract() suggests that you have defined an interface (in your code) that defines a isContract() function, and that the contract deployed at the to address implements this interface.
pragma solidity ^0.8.0;
interface ExternalContract {
function isContract() external returns (bool);
}
contract MyContract {
function foo() external {
ExternalContract to = ExternalContract(address(0x123));
bool returnedValue = to.isContract(); // calling `to`'s function `isContract()`
}
}
isContract(to) calls an external or internal function in your contract.
pragma solidity ^0.8.0;
contract MyContract {
function foo() external {
address to = address(0x123);
bool returnedValue = isContract(to); // calling your function `isContract()`
}
function isContract(address to) internal returns (bool) {
return true;
}
}
Edit: I forgot about one more case - using a library containing the isContract() function for an address. Example OpenZeppelin implementation: definition, usage.
library AddressLibrary {
function isContract (address _address) {
return true;
}
}
contract MyContract {
using AddressLibrary for address;
function foo() external {
address to = address(0x123);
bool returnedValue to.isContract(); // calling your function `isContract(to)`
}