Should I write a function on chain in smart contract or off chain? - ethereum

I'm confused about in what case should I put function logic on-chain in smart contract? or off-chain in frontend as ethers.js/ web3js etc.
In web2, I think no matter I put the function in frontend or backend, they have same performance.
Here is a example on-chain:
// give an address bonus token if its current balance is more than 0.
// Maximum is 100.
function bonus(address anyAddress) public {
if (balanceOf(anyAddress) >= 0 && balanceOf(anyAddress) < 100) {
_mint(anyAddress, 100 - balanceOf(anyAddress));
}
}
Obviously, I can use frontend logic to code the a function by calling the _mint() and balanceOf() function on-chain, which can perform the same result, and maybe save gas.
So, why should I put packaged function on chain?

It's often good practice to include validation logic in the contract. And for better user experience, you can duplicate the validation on the frontend.
Mind that technically skilled users can execute the contract function using other ways (from a custom script, through another contract, ...) - not just from their MetaMask wallet through your frontend app.

Related

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.

Solidity - Add Token Logo

Glad to see blockchain development support on Stack Overflow as resources as very scarce nowadays.
I'm a new kid trying to learn this ecosystem.
How can I add an image logo to a token that I've already created and deployed on remix.ethereum.org?
Should I have done this before deploying it?
Newbie question: Once deployed the same code can never modified?
I'm currently interacting with the token on BSC - seems that all BSC tokens are created as a fork of Solidity and the ETH ERC20 paradigm. (?)
This is my code
pragma solidity ^0.8.0;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol";
contract Foobar is ERC20 {
constructor(uint256 initialSupply) public ERC20("Foobar", "FOO") {
_mint(msg.sender, initialSupply);
}
}
Short answer: No, once you deploy a contract to the mainnet, you can't modify nor delete it, it'll be in the blockchain forever (unless you're using a testnet or a private blockchain network to test the contract).
Long answer:
Once a contract is deployed, it can never be modified nor deleted, the only thing you can modify is it's storage (variables that the contract uses) by calling it's defined functions, but you can't modify the logic behind it. If you messed up, you'll need to fix your contract, and re-deploy.
Take in mind that heavy tests and thoughtful inspections are advised before you deploy a contract to the mainnet and set an app into production (and even more if you'r contract will handle sensitive values), mainly because even if you screw up and re-deploy, if you had said contract work in production, its extremly difficult to fix.
For example: You make you'r own tiny bank, the contract recives eth, and it stores it a variable a mapping of addresses with a variable that keeps a count of how much eth from the total eth that the contract is holding is for each user.
contract Bank {
// mapping of address with current money value.
mapping (address user => uint money) balance;
constructor(){}
function withdraw(uint memory money) public {
// Require money parameter is not empty.
require(money != 0, "Can't withdraw 0 eth");
// Big issue here, if we send the money, and then update the balance,
// the user will be able to withdraw money even if hi's balance turns
// to 0 if he spams the withdraw function before he runs out of money.
// The good way would be the other way around, first update balance, and then send.
// Check if the user has enough money.
if (balance[msg.sender].money >= money) {
// Send money
address(msg.sender).transfer(money);
// Update balance.
balance[msg.sender].money -= money;
}
}
...
}
Now this has a critical issue, if someone withdraws money twice very fast, it skips a check iteration that checks if the user has enough money in it's balance to withdraw.
Now to fix this, you'll need to quickly withdraw all the money that the contract has and it's mapping(address user => uint money) ... mapping data, fix and re-deploy the contract, manually send all the money to the new contract, and also set the previous data that it had, and if that wasn't enough you'll need to change you'r front-end app to connect with the new contract.
I know its a long example, but when you start coding contracts from 0, you have to understand that testing and revising the contract is a must.
Once a contract is deployed, it cannot be modified nor deleted, it's there forever, alteast in the mainned, that's why you use private blockchains such as Ganache, or use a public testnet (Ropsten, Kovan, Rinkeby, Goerli).
Also about the logo, most tokens don't have their "logo" in their contract ( unless you're making a NFT [ ERC721 ] ), yet, if you still want to add a logo that anyone can access from your contract, i'd suggest to make a state variable that holds its value (string mainly).
The value can be a IPFS hash (IPFS is a decentralized file system that allows you to upload files of any kind, it will return a hash once it's uploaded, and you can access you'r file via https://HASHTOKEN.ipfs.infura-ipfs.io/, for example:
https://bafybeibctnxu7zpatp3caj2gevofs2oirdvdyo6yulxk2hfyaewxib3pj4.ipfs.infura-ipfs.io/
soo in you'r case, you can upload the logo there, grab the hash, and store it in you'r contract, then in your frontend app you'll only need to add the hash to the url.
A second way of adding you'r logo would be to turn you'r image into base64 and paste the whole thing into you'r contract, that way you have literally the image in the contract, yet, i don't recommend this last solution since base64 strings can get very large depending on how heavy is the file, and the heavier you'r contract is, the more gas it will use when you deploy it, and more expensive for the users when they try to use it.
Yet, if what you mean is to add a logo on sites such as BscScan, you can find a guide here.

Ethereum/Solidity: Do we need to implement an own "balance"-variable in Contracts?

In order to manage balances of a smart contract with Solidity, do we still need to implement the balance-keeper by ourselves?
In the following blogpost, the author is doing so:
https://medium.com/daox/three-methods-to-transfer-funds-in-ethereum-by-means-of-solidity-5719944ed6e9
contract Sender {
function send(address _receiver) payable {
_receiver.call.value(msg.value).gas(20317)();
}
}
contract Receiver {
uint public balance = 0;
function () payable {
balance += msg.value;
}
}
According to the docs, it seems to already built in: https://solidity.readthedocs.io/en/develop/units-and-global-variables.html#address-related (although it was implemented in the address-property which can be cast from this, don't know if I understand it correctly)
Can someone Experienced please clarify a bit?
PS: sorry for bad formatting of my question. Safari doesn't show the formatting-toolbar of stackoveflow anymore properly.
The two do different things. You can always check the balance of a contract via <address>.balance from a contract or eth_getBalance via JSON-RPC.
But the balance state variable in the contract you shared might be different from that number. For example, the contract address may have ether in it before deployment, or another contract might call selfdestruct(<address>) and add to this contract's ether that way. In both cases, the address's balance will reflect that, but the balance state variable in the code will not, because it only increases when the fallback function is invoked.
So it sort of depends... if you just want to know how much ether a contract holds, you should use the built-in <address>.balance, but if you want to track some other number, you'll need to do it yourself.

Few questions about smart contract I created (just start learning)

I just started learning solidity and have some questiones about the smart contract I created for pratice/fun.
Please let me know if any of my concepts are inaccurate, appreciate for all the advices and suggestiones.
Description:
the concept of this smart contract is really simple, whoever send the bigger amount of ether into the contract wins, it will pair you with whoever is before you (if no one is before you, you are player_one) and it will reset after 2 player are played (so it can be played again)
Code:
contract zero_one {
address public player_one;
address public player_two;
uint public player_one_amount;
uint public player_two_amount;
function zero_one() public{
reset();
}
function play() public payable{
//Scenario #1 Already have two player in game, dont accpet new player. do I even need this check at all? since smart contract execute serially i should never face this condition?
if(player_one != address(0) && player_two != address(0)) throw;
//Scenario #2 First player enter the game
else if(player_one == address(0) && player_two == address(0)){
player_one=msg.sender;
player_one_amount = msg.value;
}
//Scenario #3 Second player join in, execute the game
else{
player_two = msg.sender;
player_two_amount = msg.value;
//check the amount send from player_one and player two, whoever has the bigger amount win and get their money
if(player_two_amount>player_one_amount){
player_one.transfer(player_one_amount+player_two_amount);
reset();
}
else if(player_two_amount<player_one_amount){
player_two.transfer(player_one_amount+player_two_amount);
reset();
}
else{
//return fund back to both player
player_one.transfer(player_one_amount);
player_two.transfer(player_two_amount);
reset();
}
}
}
function reset() internal{
player_one = address(0);
player_two = address(0);
player_one_amount = 0;
player_two_amount = 0;
}
}
Questions
When sending ether back to users, do I need to calcualate how much
gas is going to take? or would smart contract automatically deduct
the gas from the amount i am going to send
Is it correct to set reset() as interal since i only want it to be
called within the smart contract, it shouldn't be called by anyone else
Would Scenario#1 ever happen? since from what I understand
smart contract do not have to worry about race condition and it
should never be in that state?
Is adding playable to play correct? ( since user are going to send ether with this call)
Is using throw a bad practice? (warning on remix)
transfer vs send, why is it better to use transfer?
I coded this smart contract as a practice. I can already see that if
someone want to game the system, he could just wait for someone to be player_one and check the amount player_one send(after the block is mined) and just send a bigger sum than that. Is there anyway this exploit could be stopped? Is there other security/flaws I did not see?
Thanks!
When sending ether back to users, do I need to calcualate how much gas
is going to take? or would smart contract automatically deduct the gas
from the amount i am going to send
The gas limit specified when executing a transaction needs to cover all activity end-to-end. This includes calls out to other contracts, transfers, etc. Any gas not used after the transaction is mined is returned to you.
Is it correct to set reset() as interal since i only want it to be
called within the smart contract, it shouldn't be called by anyone
else
internal is fine for what you're intending to do. But, it's more like protected access. A sub-contract can call internal methods. private provides the strictest visibility.
Would Scenario#1 ever happen? since from what I understand smart
contract do not have to worry about race condition and it should never
be in that state?
No, it should not happen. You are correct that transactions are processed serially, so you don't really have to worry about race conditions. That doesn't mean you shouldn't have this sort of protection in your code though...
Is adding playable to play correct? ( since user are going to send
ether with this call)
Yes. Any method that is expecting to receive wei and uses msg.value needs to be marked as payable.
Is using throw a bad practice? (warning on remix)
throw is deprecated. You want to use one of revert, require, assert as of 0.4.13. The one you use depends on the type of check you're doing and if gas should be refunded. See this for more details.
transfer vs send, why is it better to use transfer?
send and transfer are similar. The difference is that send limited the amount of gas sent to the call, so if the receiving contract tried to execute any logic, it would most likely run out of gas and fail. In addition, failures during send would not propagate the error and simply return false. Source
I coded this smart contract as a practice. I can already see that if
someone want to game the system, he could just wait for someone to be
player_one and check the amount player_one send(after the block is
mined) and just send a bigger sum than that. Is there anyway this
exploit could be stopped? Is there other security/flaws I did not see?
Security is a much more in depth topic. Any data sent in a transaction is visible, so you can't hide the exploit you mentioned unless you use a private blockchain. You can encrypt data sent in a transaction yourself, but I don't believe you can encrypt sending ether.
In terms of other security issues, there are several tools out there that perform security checks on contracts. I'd suggest looking into one of those. Also, read through the security considerations page on the Solidity documentation.

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 .