Basic Solidity Questions - ethereum

I have a few questions on solidity and ethereum.
What’s the difference between EVM and non-EVM calls?
How do you set a limit on the ether balance of a contract and what
happens when you try to send more ether to the limited contract?
How can you set the value of msg.val in a contract acct and not an
external acct?
I have checked online but cannot seem to find satisfying answers to these questions.
Thanks

What’s the difference between EVM and non-EVM calls?
EVM calls are smart contract method calls which trigger method execution and requires GAS.
non-EVM calls are reading values public values. No need to GAS.
How do you set a limit on the ether balance of a contract and what happens when you try to send more ether to the limited contract?
pragma solidity ^0.4.19;
contract yourContract{
uint256 public balanceLimit = 999;
function () payable{
if (this.balance + msg.value > balanceLimit) {
throw;
}
}
}
How can you set the value of msg.val in a contract acct and not an external acct?
msg.val => msg.value = number of wei sent with the message

Related

NFT minted through proxy contract cannot be transferred away from Crossmint

We have created a proxy contract that mints an NFT on our existing contract, as no to parameter was originally provided.
Everything works fine, however, once done and the NFT is shown on Crossmint we cannot transfer the NFT into another wallet. The following error is shown:
The successful mint transaction was this one: https://rinkeby.etherscan.io/tx/0x700cd7572303770232587ad04c65bb8b8d56f33e00ccd6d8df0980710380bd60
The proxy contract is this one:
https://rinkeby.etherscan.io/address/0xC36DB9076D0F662c9945fbd005Ea260B5259521c
Any idea what is going wrong here?
Something that might be worth looking into is your crossmint method as I think there may be an issue with how the logic is layed out in here.
One thing you might look at is the line where you attempt to transfer your token. Your from parameter is using address(this) which is actually referring to your proxy address and not your oefbContract address. I would change this to your original contract address and see if this makes a difference.
function crossmint(address to, uint8 amount) external payable {
uint256 total = oefbContract.totalSupply();
oefbContract.mintNFT{value: msg.value}(amount);
for (uint256 i = 0; i < amount; i++) {
oefbContract.transferFrom(address(this), to, total + i);
}
}

Identical contract on BNB chain fails, working on ETH

I have two contracts of identical ERC20 code, but on BNB chain the functions fail, such as allowance, and on Ethereum they are fine.
I can't seem to understand why, I've never seen this happen before. I tested on Fantom testnet as well, and it also fails.
https://ropsten.etherscan.io/address/0xe95fc92e1ae8b46e83fc970f9a5f28c9439839ff#readContract
https://testnet.bscscan.com/address/0x6f74C965Fe9A27d3A84c88c83F4700351b0c2e8d#readContract
Allowance returns uint256 : Error: Wrong response id "0" (expected: "8") in {"jsonrpc":"2.0" on BNB chain
And passes on ETH
The allowance is different for every chain.
You need to call the approve function on each chain where you want to give allowance.

ERC721 Contract: 1 ether = 1 token for network deployed on?

I am new to Solidity and looking over an ERC721 NFT contract.
I see in the code they set variables of prices to things like uint256 public MINT_PRICE = 1.5 ether;.
Does this mean that the price would be 1.5 of AVAX if were deploying this contract onto the Avalanche network whose base token is AVAX?
calculations are done in the base units for a token.
uint256 public MINT_PRICE = 1.5 avax;
People uses "ether" for placeholder
Note that gas fees are always paid in "ether" if your contract is on ethereum.

solidity: call contract function from another contract with the same msg.sender

I have a function that needs to call the transfer method on another contract.
I want the transfer method to be called from the address of the original caller and not the contract.
Is it possible?
This is the code:
function buyGameBundle(string calldata id) external nonReentrant {
structGameBundles memory currentItem = _gameBundles[id];
require(currentItem.exists == true, "bundle does not exists");
require(currentItem.totalSupply > 0, "there are no more bundles left");
if (currentItem.cost > 0) {
erc20.transfer(_feesAccount, currentItem.cost);
}
currentItem.totalSupply = currentItem.totalSupply.sub(1);
_gameBundles[id] = currentItem;
emit BuyGameBundle(_msgSender(), id, currentItem.cost);
}
erc20.transfer(_feesAccount, currentItem.cost);
Your current code performs a message call (docs). Another name for the same thing is an EVM call. It uses storage of the target contract, and the msg.sender is your contract (not the original transaction sender).
If you want the msg.sender to be the original transaction sender (the user), you need to use a delegatecall (docs). But... a delegatecall uses storage of the caller (your contract; not the called contract), so it's mostly useful for proxy contracts.
For security reasons, it's not possible to execute a function in a target contract, using the target contract storage and msg.sender of the original sender.
If it were possible, you could theoretically steal tokens from anyone who doesn't/can't verify your contract source code. Example:
usdt.transfer(attacker, usdt.balanceOf(victim));
weth.transfer(attacker, weth.balanceOf(victim));
// ...
Needing to transfer funds from someone is such a common pattern that it is built right into the ERC20 specification, and is used in almost every DeFi contract ever.
What you need to use is transferFrom() rather than transfer(). It takes a "from" address as the first parameter, and if the sending user has approved your contract to move their funds, then the call will succeed.
In your case the transfer line would change to:
erc20.transferFrom(msg.sender, _feesAccount, currentItem.cost);
The sender will need to approve your contract first.
Here are the ERC20 specifications.
https://eips.ethereum.org/EIPS/eip-20
If you are using ERC20s and want to transfer another account's tokens from within a seperate contract, the correct method to use is transferFrom. This requires an allowance to be given to the contract in which transferFrom is being called. This is done using approve or increaseAllowance (the latter is recommended).
In general, however, if you wish to call another contract's method in the context of the current contract, i.e. with the same msg.sender (amongst other things), you can use delegatecall. See https://docs.soliditylang.org/en/v0.8.11/types.html#address for more details.

How to find out if an Ethereum address is a contract?

An address in Solidity can be an account or a contract (or other things, such as a transaction). When I have a variable x, holding an address, how can I test if it is a contract or not?
(Yes, I've read the chapter on types in the doc)
Yes you can, by using some EVM assembly code to get the address' code size:
function isContract(address addr) returns (bool) {
uint size;
assembly { size := extcodesize(addr) }
return size > 0;
}
The top-voted answer with the isContract function that uses EXTCODESIZE was discovered to be hackable.
The function will return false if it is invoked from a contract's constructor (because the contract has not been deployed yet).
The code should be used very carefully, if at all, to avoid security hacks such as:
https://www.reddit.com/r/ethereum/comments/916xni/how_to_pwn_fomo3d_a_beginners_guide (archive)
To repeat:
Do not use the EXTCODESIZE check to prevent smart contracts from calling a function. This is not foolproof, it can be subverted by a constructor call, due to the fact that while the constructor is running, EXTCODESIZE for that address returns 0.
See sample code for a contract that tricks EXTCODESIZE to return 0.
Checking if a caller is a contract
If you want to make sure that an EOA is calling your contract, a simple way is require(msg.sender == tx.origin). However, preventing a contract is an anti-pattern with security and interoperability considerations.
require(msg.sender == tx.origin) will need revisiting when account abstraction is implemented.
Checking if a callee is a contract
As #Luke points out in a comment, there is no general on-chain way to find out about a callee. If you want to "call" an address, there's no general way to find out if that address is a contract, EOA, or an address that a new contract can be deployed on, or if it's a CREATE2 address.
One non-general way that works for some callees: you can have a mapping on-chain that stores addresses of known EOAs or contracts. (Just remember that for an address without any on-chain history, you can't know if it's an EOA or an address that a contract can be deployed on.)
This isn't something you can query from within a contract using Solidity, but if you were just wanting to know whether an address holds contract code or not, you can check using your geth console or similar with eg:
> eth.getCode("0xbfb2e296d9cf3e593e79981235aed29ab9984c0f")
with the hex string (here 0xbfb2e296d9cf3e593e79981235aed29ab9984c0f) as the address you wish to query. This will return the bytecode stored at that address.
You can also use a blockchain scanner to find the source code of the contract at that address, for example the ecsol library as shown on etherscan.io.
Edit: Solidity has changed since this answer was first written, #manuel-aráoz has the correct answer.
There is no way in solidity to check if an address is a contract. One of the goals of Ethereum is for humans and smart contracts to both be treated equally. This leads into a future where smart contracts interact seamlessly with humans and other contracts. It might change in the future , but for now an arbitrary address is ambiguous.
If you want to use nodejs to confirm, you can do this:
const Web3 = require('web3')
// make sure you are running geth locally
const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'))
is_contract = async function(address) {
res = await web3.eth.getCode(address)
return res.length > 5
}
is_contract('your address').then(console.log)
From openzeppeling Address.sol library, it has this function:
pragma solidity ^0.8.1;
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
isContract will return false for the following types of addresses:
an externally-owned account
a contract in construction
an address where a contract will be created
an address where a contract lived, but was destroyed
What you can do, granted you have the information at hand.
If the transactions sender address was null or unoccupied then you can tell if the address is a contract account or an EOA (externally owned account).
i.e. when sending a create contract transaction on the network then the receive address in the transaction is null/not used.
Reference from github:
https://github.com/ethereum/go-ethereum/wiki/Contracts-and-Transactions
Hope this helps.
If you are checking whether the caller is an EOA rather than a contract:
Short answer:
require(tx.origin == msg.sender);
tx.origin is a reference of the original address who initiates this serial function call, while msg.sender is the address who directly calls the target function. Which means, tx.origin must be a human, msg.sender can be a contract or human. Thus, if someone calls you from a contract, then the msg.sender is a contract address which is different from tx.origin.
I know most contracts may use #Manuel Aráoz's code, which works in most cases. But if you call a function within the constructor of a contract, extcodesize will return 0 which fails the isContract check.
NOTE: DON'T use tx.origin under other circumstances if you are not clear about what it represents because .