Is it correct to use revert this way? - solidity

I have an abstract contract and I need to validate the parameters and return an error if not valid and also return the gas fees.
I created a modifier called checkNumber and inside I validate the _number and use require and revert.
CODE
//SPDX-License-Identifier: UNLICENSED
// Solidity files have to start with this pragma.
// It will be used by the Solidity compiler to validate its version.
pragma solidity >=0.7.0 <0.9.0;
struct Information {
uint number;
string avatar;
}
abstract contract AbstractCandidate {
uint[6] private _numbers = [1, 2, 3, 4, 5, 6];
string[6] private _avatars = [
"https://raw.githubusercontent.com/thiagosaud/dApp-superior-electoral-court/main/temp/imgs/candidate-1.png",
"https://raw.githubusercontent.com/thiagosaud/dApp-superior-electoral-court/main/temp/imgs/candidate-2.png",
"https://raw.githubusercontent.com/thiagosaud/dApp-superior-electoral-court/main/temp/imgs/candidate-3.png",
"https://raw.githubusercontent.com/thiagosaud/dApp-superior-electoral-court/main/temp/imgs/candidate-4.png",
"https://raw.githubusercontent.com/thiagosaud/dApp-superior-electoral-court/main/temp/imgs/candidate-5.png",
"https://raw.githubusercontent.com/thiagosaud/dApp-superior-electoral-court/main/temp/imgs/candidate-6.png"
];
modifier checkNumber(uint _number) {
string memory _errorMessage = "Candidate number is incorret!";
require(_number < 1 || _number > _numbers.length, _errorMessage);
if (_number < 1 || _number > _numbers.length) {
revert(_errorMessage);
}
_;
}
function getInformation(uint _number) external view checkNumber(_number) returns(Information memory) {
return Information({ avatar: _avatars[_number], number: _numbers[_number] });
}
}

According to this logic you will always get _errorMessage. For example in require statement if _number 0 you wont get err, but youll get it in if statement, because 0 < 1 and it fits to your if statement condition.

Related

Failed to call solidity function with array argument

I'm trying to execute contract's function use abi encode and call function, but I'm getting an error. Don't know how to resolve this issue.
pragma solidity >=0.8.0;
contract AbiTest {
event A(uint);
event Out(bytes);
function test() public {
bytes4 func = bytes4(keccak256("callMe(uint[])"));
uint[] memory arr = new uint[](3);
arr[0] = 3;
arr[1] = 4;
(bool res, bytes memory data) = address(this).call(abi.encode(func, arr));
emit Out(data);
require(res);
}
function callMe(uint[] memory array) public {
emit A(array[0]);
emit A(array[1]);
}
}
This solution works well, if arguments sizes are known at compile time, but with dynamic sizes this doesn't work.
How can I resolve it?
I think this solution should work just fine. This is compatible with dynamic array lengths.
function test() public {
uint[] memory arr = new uint[](2);
arr[0] = 3;
arr[1] = 4;
(bool success, ) = address(this).call
(
abi.encodeWithSignature(
"callMe(uint[])",
arr
)
);
}
Need to use abi.encodePacked see Solidity doc instead of abi.encode because second one don't compress items and therefore uses 32 bytes for the function name.
pragma solidity >=0.8.0;
contract Abi2Test {
function getByte() public returns (bytes memory) {
bytes4 func = bytes4(keccak256("callMe(uint256[])"));
return abi.encode(func);
}
function getByte2() public returns (bytes memory) {
bytes4 func = bytes4(keccak256("callMe(uint256[])"));
return abi.encodePacked(func);
}
}
getByte output: 0x6600981c00000000000000000000000000000000000000000000000000000000
getByte2 output: 0x6600981c
Therefore, when you use encode with function signature additionally will be allocated 28 bytes and EVM can't correctly parse passed value.
So, to correctly call function with abi.encode and abi.encodePacked you need to pack arguments with encode at the first, and at the second group function's signature with encoded bytes.
pragma solidity >=0.8.0;
contract Abi2Test {
event A(uint256);
event Out(bytes);
event Out1(bytes);
function test() public {
bytes4 func = bytes4(keccak256("callMe(uint256[])"));
uint256[] memory arr = new uint256[](3);
arr[0] = 3;
arr[1] = 4;
(bool res, bytes memory data) = address(this).call(abi.encodePacked(func, abi.encode(arr)));
emit Out(data);
require(res);
}
function callMe(uint256[] memory array) public {
emit A(array.length);
emit Out1(msg.data);
}
}

ERC721A smart contract

I'm writing an ERC721A smart contract and getting a warning in the Remix IDE. There is the code:
function tokenOfOwnerByIndex(address owner, uint256 index) public view override returns (uint256) {
if (index >= balanceOf(owner)) revert OwnerIndexOutOfBounds();
uint256 numMintedSoFar = totalSupply();
uint256 tokenIdsIdx;
address currOwnershipAddr;
// Counter overflow is impossible as the loop breaks when uint256 i is equal to another uint256 numMintedSoFar.
unchecked {
for (uint256 i; i < numMintedSoFar; i++) {
TokenOwnership memory ownership = _ownerships[i];
if (ownership.addr != address(0)) {
currOwnershipAddr = ownership.addr;
}
if (currOwnershipAddr == owner) {
if (tokenIdsIdx == index) {
return i;
}
tokenIdsIdx++;
}
}
}
// Execution should never reach this point.
assert(false);
}
This code is from the Square Bears collection ( https://etherscan.io/address/0x2b1037def2aa4ed427627903bdef9bdd27ae1ea3#code ). I got it from a YouTube tutorial. I think the code works, but I keep getting a warning.
Warning: Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.
--> contracts/ERC721A.sol:103:94:
|
103 | function tokenOfOwnerByIndex(address owner, uint256 index) public view override returns (uint256) {
| ^^^^^^^
I assume that I have to provide a named return value or variable, but the code seems to return an iterated value (i).
Because you've told the compiler that you will return a value from the function, but you didn't.
You should return a mock value even if you don't need to.
...
// Execution should never reach this point.
assert(false);
return tokenIdsIdx; // or simply return 0;
}

Why am I getting this error "Gas estimation errored with the following message (see below)"?

My remix keep alerting "Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending? execution reverted". Can you guys help me find which part that im missing.
pragma solidity ^0.4.21;
contract GuessTheNewNumberChallenge {
function GuessTheNewNumberChallenge() public payable {
require(msg.value == 1 ether);
}
function isComplete() public view returns (bool) {
return address(this).balance == 0;
}
function guess(uint8 n) public payable {
require(msg.value == 1 ether);
uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now));
if (n == answer) {
msg.sender.transfer(2 ether);
}
}
}
contract Attack {
address vt = 0x2417929C9AE5884a754Cf1f77FA5FaBDDC9ce92A;
GuessTheNewNumberChallenge gn = GuessTheNewNumberChallenge(vt);
function attack() public {
uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now));
gn.guess(answer);
}
}
You must to specificy in Attack smart contract the GuessTheNewNumberChallenge
smart contract address for use its functions. To resolve this problem try this:
pragma solidity ^0.4.21;
contract GuessTheNewNumberChallenge {
function GuessTheNewNumberChallenge() public payable {
require(msg.value == 1 ether);
}
function isComplete() public view returns (bool) {
return address(this).balance == 0;
}
function guess(uint8 n) public payable {
require(msg.value == 1 ether);
uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now));
if (n == answer) {
msg.sender.transfer(2 ether);
}
}
}
contract Attack {
// I declare variable refers to GuessTheNewNumberChallenge
GuessTheNewNumberChallenge private gn;
// I set into constructor about this smart contract the address where GuessTheNewNumberChallenge deployed.
// And when the instance is complete, you can use the GuessTheNewNumberChallenge functions.
constructor(address gtncAddress) public {
gn = GuessTheNewNumberChallenge(gtncAddress);
}
function attack() public {
uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now));
gn.guess(answer);
}
}
I put some comments into smart contracts.

Type error: Member not found or visible in unit256

I have this crowdfunding platform that was initially using SafeMath library. And since is deprecated I'm looking for a workaround to make it compile and I'm getting a compilation error with remix in line 131:
contributions[msg.sender] = contributions[msg.sender].add(msg.value);
Here you have the whole contract code, it's a platform where you can create projects and raise funds:
// We will be using Solidity version 0.6.0
pragma solidity 0.6.0;
// Importing OpenZeppelin's SafeMath Implementation
//import 'https://github.com/OpenZeppelin/openzeppelin-solidity/contracts/math/SafeMath.sol';
//import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/mocks/SafeMathMock.sol";
//import "#openzeppelin/contracts/utils/math/SafeMath.sol";
contract Crowdfunding {
// using SafeMath for uint256;
// List of existing projects
Project[] private projects;
// Event that will be emitted whenever a new project is started
event ProjectStarted(
address contractAddress,
address projectStarter,
string projectTitle,
string projectDesc,
uint deadline,
uint goalAmount
);
/** #dev Function to start a new project.
* #param title Title of the project to be created
* #param description Brief description about the project
* #param durationInDays Project deadline in days
* #param amountToRaise Project goal in wei
*/
function startProject(
string calldata title,
string calldata description,
uint durationInDays,
uint amountToRaise
) external {
// uint raiseUntil = now.add(durationInDays.mul(1 days));
uint durationInSeconds = durationInDays * 1 days;
// Check that the result of division (inverse operation to multiplication) is the original number.
// If it's not, throw an exception, because the multiplication overflowed.
require(durationInSeconds / durationInDays == 1 days, 'Multiplication overflow');
uint raiseUntil = block.timestamp + durationInSeconds;
// Check that the result of subtraction (inverse operation to addition) is the original number.
// If it's not, throw an exception, because the addition overflowed.
require(raiseUntil - block.timestamp == durationInSeconds, 'Addition overflow');
Project newProject = new Project(msg.sender, title, description, raiseUntil, amountToRaise);
projects.push(newProject);
emit ProjectStarted(
address(newProject),
msg.sender,
title,
description,
raiseUntil,
amountToRaise
);
}
/** #dev Function to get all projects' contract addresses.
* #return A list of all projects' contract addreses
*/
function returnAllProjects() external view returns(Project[] memory){
return projects;
}
}
contract Project {
// not using SafeMath for uint256;
// Data structures
enum State {
Fundraising,
Expired,
Successful
}
// State variables
address payable public creator;
uint public amountGoal; // required to reach at least this much, else everyone gets refund
uint public completeAt;
uint256 public currentBalance;
uint public raiseBy;
string public title;
string public description;
State public state = State.Fundraising; // initialize on create
mapping (address => uint) public contributions;
// Event that will be emitted whenever funding will be received
event FundingReceived(address contributor, uint amount, uint currentTotal);
// Event that will be emitted whenever the project starter has received the funds
event CreatorPaid(address recipient);
// Modifier to check current state
modifier inState(State _state) {
require(state == _state);
_;
}
// Modifier to check if the function caller is the project creator
modifier isCreator() {
require(msg.sender == creator);
_;
}
constructor
(
address payable projectStarter,
string memory projectTitle,
string memory projectDesc,
uint fundRaisingDeadline,
uint goalAmount
) public {
creator = projectStarter;
title = projectTitle;
description = projectDesc;
amountGoal = goalAmount;
raiseBy = fundRaisingDeadline;
currentBalance = 0;
}
/** #dev Function to fund a certain project.
*/
function contribute() external inState(State.Fundraising) payable {
require(msg.sender != creator);
contributions[msg.sender] = contributions[msg.sender].add(msg.value);
currentBalance = currentBalance.add(msg.value);
emit FundingReceived(msg.sender, msg.value, currentBalance);
checkIfFundingCompleteOrExpired();
}
/** #dev Function to change the project state depending on conditions.
*/
function checkIfFundingCompleteOrExpired() public {
if (currentBalance >= amountGoal) {
state = State.Successful;
payOut();
} else if (now > raiseBy) {
state = State.Expired;
}
completeAt = now;
}
/** #dev Function to give the received funds to project starter.
*/
function payOut() internal inState(State.Successful) returns (bool) {
uint256 totalRaised = currentBalance;
currentBalance = 0;
if (creator.send(totalRaised)) {
emit CreatorPaid(creator);
return true;
} else {
currentBalance = totalRaised;
state = State.Successful;
}
return false;
}
/** #dev Function to retrieve donated amount when a project expires.
*/
function getRefund() public inState(State.Expired) returns (bool) {
require(contributions[msg.sender] > 0);
uint amountToRefund = contributions[msg.sender];
contributions[msg.sender] = 0;
if (!msg.sender.send(amountToRefund)) {
contributions[msg.sender] = amountToRefund;
return false;
} else {
currentBalance = currentBalance.sub(amountToRefund);
}
return true;
}
/** #dev Function to get specific information about the project.
* #return Returns all the project's details
*/
function getDetails() public view returns
(
address payable projectStarter,
string memory projectTitle,
string memory projectDesc,
uint256 deadline,
State currentState,
uint256 currentAmount,
uint256 goalAmount
) {
projectStarter = creator;
projectTitle = title;
projectDesc = description;
deadline = raiseBy;
currentState = state;
currentAmount = currentBalance;
goalAmount = amountGoal;
}
}
The Using X for Y expression extends the Y datatype with functions of the X library.
In case of using SafeMath for uint256, it allows to use the functions defined in SafeMath (such as add()) on uint256 variables.
Specifically, function add() in SafeMath checks whether the addition would overflow the 256bit unsigned integer. If it did overflow, it throws an exception. If it didn't overflow, it returns the result of the addition.
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
You can achieve the same result with this snippet:
uint256 contribution = contributions[msg.sender] + msg.value; // c = a + b
require(contribution >= contributions[msg.sender]); // require(c >= a)
contributions[msg.sender] = contribution; // return c
If you switch to Solidity 0.8+, this is not needed, because since version 0.8.0, Solidity performs the overflow check automatically and reverts if an overflow/underflow would occur. So it would be sufficient to use just this snippet:
// safe in Solidity 0.8+, unsafe in older versions
contributions[msg.sender] += msg.value;
Arithmetic operations revert on underflow and overflow. You can use unchecked { ... } to use the previous wrapping behaviour.
Source: docs

Solidity variable definition: (bool sent, )

In Solidity, this sentence:
(bool sent, ) = msg.sender.call{value: _amount}("");
What is the sense of that "," after sent variable?
Any link to offcial documentation?
Many thanks.
It means you do not need to define both the variables to access the return values. You can access either of these variables as follows
(,uint256 alpha) = testFunction(); //access first returned variable
(uint256 beta,) = testFunction(); //access second returned variable
function testFunction() public pure returns(uint256,uint256){
return (1,2);
}
Solidity functions can return multiple variables of different types.
If you only want to keep one variable, then you can declare a variable and then use commas:
function multiValueFunction() public returns (bool, string memory, uint[] memory, uint {
//do something
return (true, "New String", [1,2], 21)
}
function differentFunction() public {
uint numberToKeep;
(,,,numberToKeep) = multiValueFunction();
}
Expected: numberToKeep = 21
Each comma represents the place of a returned variable (that is not kept)
It just means that the function will return two variables and you just want to store the first one.
This kind of syntax (variable type) is called Tuple In Python and other languages.
In Solidity,
they're not considered a proper type but can be used as a shorthand.
See the Official Docs: Destructuring Assignments and Returning Multiple Values
example:
uint256 amount = 10 ether;
require(
amount <= address(this).balance,
"Trying to withdraw more money than the contract has."
);
(bool success, ) = (msg.sender).call{value: amount}("");
require(success, "Failed to withdraw money from contract.");
above code, Roughly, Can be translated with basic if else statements like following:
uint256 amount = 10 ether;
bool success;
if(amount <= address(this).balance) {
success = true;
} else {
success = false;
return "Trying to withdraw more money than the contract has.";
}
if(success) {
(msg.sender).call{value: amount}("")
} else {
return "Failed to withdraw money from contract.";
}
here's another contract as example,
use this link https://remix.ethereum.org/#version=soljson-v0.6.9+commit.3e3065ac.js&optimize=false&gist=83bfbd85ef79387f760154999eb4f192&runs=200&evmVersion=null to play around in remix online
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.8.0;
contract myContract {
uint index;
function myFunction() public {
( , , uint256 standardDepositAmount, , ) = returnTuple();
require(standardDepositAmount == 3);
// do something...
}
function returnTuple() public pure returns (uint, uint, uint, uint, uint) {
return (1, 2, 3, 4, 5);
}
}