I know a user can send ETH to the contract/function manually, but is there way to request a specific amount directly in the code so that it is added to the gas fee, for example, the way dxsale.app does it when creating a presale (see screenshot below) - it adds the 0.1 ETH cost of presale to the 0.0072 gas fee for a total of 0.1072.
Can this be done in an ERC20 contract? I know I can receive ETH in a payable function, e.g.
function deposit() payable public {
require(msg.value > 0, "You need to send some Ether");
}
but can I specify the amount I want to receive (for a flat fee), so that it is added to the tx cost?
Thank you
TLDR: The contract function is executed after the transaction has been sent. So it's not possible to to set the transaction params from the contract.
is there way to request a specific amount directly in the code
Not in the Solidity code. You can only throw an exception if the sent amount is not an expected value.
function deposit() payable public {
require(msg.value == 0.1 ether, "You need to send 0.1 Ether");
}
You can specify the amount when you're creating the transaction request. For example MetaMask uses the Ethereum Provider API, so you can predefine the amount that MetaMask will show.
const params = [
{
from: '0xb60e8dd61c5d32be8058bb8eb970870f07233155',
to: '0xd46e8dd67c5d32be8058bb8eb970870f07244567',
value: '0x9184e72a', // 2441406250
},
];
ethereum
.request({
method: 'eth_sendTransaction',
params,
});
But the user can override this option in their wallet, or they can always submit a transaction using a different way (not requested with the custom value).
The value (both in the params JS object and the Solidity msg.value global variable) always excludes the gas fees.
So if you're sending 0.1 ETH, the gas fees are added on top of that and the total cost might be 0.10012345. The params.value will contain 0.1 ETH (hex number in wei), as well as the msg.value will contain 0.1 ETH (decimal number in wei).
Related
I've been working on different ways to perform reentrancy attacks and there is one case which I have still not seen any working example on the internet. In the book Mastering Ethereum, the reentrancy attack is explained with a contract where the function withdraw(uint amount) takes the input amount. The version on Solidity has been updated a lot since then and whenever I try to perform a similar attack, it does not work. It works whenever the function withdraw() takes no arguments and it also works when using older versions.
Could anyone provide an example of a reentrancy attack where the target contract takes the withdraw amount as input?
Thank you!
Let's say you have 1 ether in the contract, and the contract has a total of 10 ether. You're trying to steal all 10 ether with re-entrancy, but that necessarily means the variable tracking your balance must underflow to the equivalent of uint256(-9) ether -- you're trying to withdraw 1 ether 10 times.. This will cause a revert in Solidity 0.8.0 or higher, since it has built in under/overflow protection. If you want it to work in 0.8.0, you have to wrap the balance reduction line with unchecked.
This code is still vulnerable to re-entrancy in 0.8.0, but only because it sets the balance to zero, and can't underflow
mapping(address => uint256) public balance;
function deposit() external payable {
balance[msg.sender] += msg.value;
}
function withdraw() external {
msg.sender.call{value: balance[msg.sender]}(""); // re-entrancy
balance[msg.sender] == 0; // cannot underflow
}
function withdrawV2(uint256 value) external {
require(value <= balance[msg.sender], "you don't have that much"); // not that this does anything...
msg.sender.call{value: balance[msg.sender]}("");
unchecked { // now it can underflow
balance[msg.sender] -= value;
}
}
I'm creating a smart contract that allows people to pay for a monthly subscription
I got stacked in this :
how to transfer plan.amount from the user wallet to the smart contract?
function subscribe(uint planId) external {
Plan storage plan = plans[planId];
require(plan.merchant != address(0), 'address not valid');
bool sent = payable(address(this)).send(plan.amount);
require(sent, "tx failed");
emit PaymentSent(
msg.sender,
plan.merchant,
plan.amount, // the monthly amount for Subscription
planId,
block.timestamp
);
subscriptions[msg.sender][planId] = Subscription(
msg.sender,
block.timestamp,
block.timestamp + 4 weeks // next payement
);
emit SubscriptionCreated(msg.sender, planId, block.timestamp);
}
The subscribe() function needs to use the payable modifier in order to accept ETH. Then you can validate how much the user has sent while invoking your function with the msg.value global variable.
It's not possible to request a specific amount from the contract, as the contract code is executed after the user has sent the transaction. You always need to validate how much has the user sent along with the transaction invoking the function.
function subscribe(uint planId) external payable {
// revert if the sent value is not expected
require(msg.value == 1 ether, "You need to send 1 ETH");
}
However you can control the predefined value on the UI, while creating the transaction request to their MetaMask or other wallet.
await window.ethereum.request(
method: 'eth_sendTransaction',
[
from: userAddress,
to: yourContract,
data: <invoking the subscribe() function>,
value: <1 ETH in wei, in hex>
]
);
Docs: https://docs.metamask.io/guide/ethereum-provider.html#ethereum-request-args
I have a smart contract named "Manager" with a "claimRewards" method, that will be called by users and should transfer to them some ERC20 tokens. The ERC20 is another smart contract named "rdk".
function claimRewards() public {
uint256 howMany = calculateRewards(msg.sender);
if (howMany > 0) {
// does the contract wallet have tokens enough?
uint256 walletBalance = rdk.balanceOf(address(this));
emit Log(walletBalance, howMany);
require (walletBalance >= howMany, "empty wallet");
// transfer tokens from contract wallet to player
rdk.transferFrom(address(this), msg.sender, howMany);
}
}
The ERC20 tokens are in the smart contract wallet. The event "Log" shows these values:
Manager.Log(
balance: 101000 (type: uint256),
howMany: 9 (type: uint256)
)
So there are 101000 tokens on the smart contract wallet, and it has to transfer 9 to msg.sender
Before calling the method "claimRewards" I approve the smart contract to spend 100000 of those tokens:
it("Should transfer tokens from operator for rewards ", async function () {
await token.approve(manager.address, 100000, { from: accounts[1] });
await token.transfer(manager.address, 100000, { from: accounts[1] });
});
But when running my tests on Truffle, I got the error:
Error: Returned error: VM Exception while processing transaction: revert ERC20: transfer amount exceeds allowance -- Reason given: ERC20: transfer amount exceeds allowance.
I cannot understand if the smart contract "Manager" is the owner of 100000 RDK tokens, and even has been approved for spending 100000 RDK tokens, why is showing that error when transferring 9 of those tokens to the caller of the method.
Anyone could help me with this issue?
await token.approve(manager.address, 100000, { from: accounts[1] });
This JS snippet approves the Manager to spend accounts[1]'s tokens.
rdk.transferFrom(address(this), msg.sender, howMany);
But this Solidity snippet checks for approval from the Manager address (effectively the msg.sender in the rdk contract) to the same Manager address (1st argument of address(this)).
If you want to transfer tokens from the Manager contract, you can simply invoke the token's transfer() function in the Manager contract.
// transfer `howMany` of `rdk` tokens
// from the `Manager` address
// to the user invoking the `claimRewards()` function
rdk.transfer(msg.sender, howMany);
This will not check the allowance but only the sender (Manager) balance.
I have a contract defined in solidity and I want to make it so that when a specific function is called, the overall cost of the contract increases by 1 ether. I am a little fuzzy on how to use ether in practice. Would I just use a normal int for this? Where does the keyword ether come into play?
As you probably know, 1 ether == 1000000000000000000 (or 10^18) wei.
You can access the transaction value in a global variable msg.value that returns amount of wei sent with the transaction.
So you can make a simple validation that checks whether the transaction calling your function has a value of 1 ETH.
function myFunc() external payable {
require(msg.value == 1 ether, 'Need to send 1 ETH');
}
It's the same as comparing to 10^18 wei
function myFunc() external payable {
require(msg.value == 1000000000000000000, 'Need to send 1 ETH');
}
function myFunc() external payable {
require(msg.value == 1e18, 'Need to send 1 ETH');
}
There's also a short paragraph on the Solidity docs that shows more examples: https://docs.soliditylang.org/en/v0.8.2/units-and-global-variables.html#ether-units
I'm writing a smart contract in solidity, and I need to send ether from my coinbase (eth.coinbase) to my friend coinbase (address = 0x0123).
If I try to use an address.send(value) the function doesn't decrease my account and doesn't increase my friend coin base.
I only can send ether in geth with "eth.sendTransaction(VALUE, {from:eth.coinbase, to:address})"
so I want to know if its possible to call an eth method in contract or a different way to send ether in smart contract
function withdraw() returns (bool) {
address x = 0x0123;
uint amount = 100 ether;
if (x.send(amount))
return true;
else
return false;
}
address.send does not propagate exception that's why you don't see any issue. Make sure you have enough Eth in your contract.
Have a look on this documentation that will explain how to set up your smart contract: https://developer.ibm.com/clouddataservices/2016/05/19/block-chain-technology-smart-contracts-and-ethereum/
You can use following function. just change variable name with yours.
function Transfer(uint amount,address reciever){
// check sender balance is less than of amount which he wants to send.
if(balance[msg.sender] < amount){
return;
}
// decrease sender's balance.
balance[msg.sender] = balance[msg.sender] - amount;
// increase reciever's balance.
balance[reciever] = balance[reciever] + amount;
// event
// transaction(msg.sender,reciever,amount);
}