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.
Related
I'm working on a uni project based on blockchain, and I have to audit our system, check known attacks, ect.
This the the document that I check, principaly, since i start to work on smart contracts issues first :
Known-attack ethereum smart contract
I have trouble understanding the example used in the "Dos With (unexpected) revert attack" part. I share the code :
// INSECURE
contract Auction {
address currentLeader;
uint highestBid;
function bid() payable {
require(msg.value > highestBid);
require(currentLeader.send(highestBid)); // Refund the old leader, if it fails then revert
currentLeader = msg.sender;
highestBid = msg.value;
}}
They say that an attacker could force the call of bid to revert everytime so no-one is able to bid, which would make the attacker win the auction by default.
But.. How would he do that, that's the part I don't get. Do we agree that at least this piece of contract is the "valid one", and isn't a payload ? If the payload is a contract, can anyone provide an exemple/explanation ?
I'll add that, if here I quote a solidity contract, we work with Vyper, but from what I read before, this is still a kind of issue that i'll find there too.
Thanks in advance !
If send() target address is a smart contract it will execute the fallback function.
If the currentLeader points to a smart contract that has a fallback function that has been intentionally made to revert on failed send, the bid() won't work for any participants until currentLeader has been changed.
More information here.
This is not a "DoS" attack but simply gotcha in Solidity programming.
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.
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 .
I am using testrpc and web3.
I used the idiom below to ensure that only a previously defined user should be able to do something:
function doSomethingProtected() {
if ( msg.sender != authorizedUser )
throw;
flagSomething = true;
}
When calling the function on an instantiated contract with web3 as follows:
myContract.doSomethingProtected( { from: "0x..." } );
it worked. At first I was pleased but then I realized the web3 API had not required me to provide any passphrase for a private key or such like.
Can anyone with the simple knowledge of someones public key/address call this function?
The use of this idiom in the examples led me to believe a benefit of the Ethereum contracts was that it ensured msg.sender was cryptographically assured.
The reason is that you are using testRPC, which doesn't lock it's accounts, so you don't need a password.
If you were to do this with geth, you would need to unlock the account before sending from it.
Without the private key, that function will throw an error, so you are correct in using that authorization method.
It's difficult to be certain without seeing more of your code, but it seems likely that you were calling the contract on your local node, rather than sending a transaction. Transactions can only be signed by someone with the account's private key, meaning you can rely on msg.sender to be accurate, but messages executed on your local node won't enforce that. Any changes they make are rolled back and not applied to state, though, so it doesn't matter what your local call does.
In general, there are two ways to call a function from web3.js: Using a transaction or just using a "call". Only in transactions you can actually modify the blockchain content (reading is always possible). Transactions always require a valid signature and thus access to a private key.
The reason you were not asked for a password might be that you already unlocked the account. Furthermore, users other than the authorized user can call the function, only the changes will be thrown away.
My guess is that your account was already unlocked when calling the function. I don't remember the exact period that your account is unlocked after unlocking it in web3. I might be wrong though. Would have added this as a comment, but I am not allowed right now.
When I publish a new contract using the output of a compiler, I get a javascript object "somecontract" which I can call methods on like so: "somecontract.doSomething();"
But how do I interact with a contract that was published to the blockchain at some point in the past?
If you already have the contract address skip to the code snippet below. If not, you’ll need to find it.
Look up the account the contract was published from on etherchain.org
Find the transaction you used to create the contract. It’ll be one of the “new contract” rows.
Finally, inside geth, get the transaction receipt, and the contract address will be the “contractAddress” property of the receipt.
To get the abi, recompile your contract (usually here https://chriseth.github.io/browser-solidity/). Now that you have the abi and the contract address, you’re ready to regain access to the contract.
var abi = <cut and paste the contract abi here>;
var contract = web3.eth.contract(abi).at(<contract address>);
contract.functionname();
contract.functionname.sendTransaction(...);
This is a sample abi from https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/05_greeter.sol
[{"constant":false,"inputs":[],"name":"kill","outputs":[],"type":"function"},
{"constant":true,"inputs":[],"name":"getBlockNumber","outputs":[{"name":"","type":"uint256"}],"type":"function"},
{"constant":false,"inputs":[{"name":"_newgreeting","type":"string"}],"name":"setGreeting","outputs":[],"type":"function"},
{"constant":true,"inputs":[],"name":"greet","outputs":[{"name":"","type":"string"}],"type":"function"},{"inputs":[{"name":"_greeting","type":"string"}],"type":"constructor"}]