I am building a contract and web application, where users will run a function via a front-end and send funds into the contract. The amount they need to send in will be a percentage of the total ETH in the contract. This application will have many users so the total ETH in the contract will constantly be changing. How can I dynamically feed the correct amount of ETH from the front-end and to contract without it getting out of sync?
1 Answer
There's no way of "pulling" Ether from the user, so the only solution is to generate a big msg.value
and return any extra at the end of the transaction. This value can be thought as the max amount a user is willing to send, similar to a slippage check.
The solidity contract should look something like this:
function foobar() payable external {
// Here you compute the fraction of total ether in the contract.
// Careful that msg.value already entered the contract.
uint256 requiredEther = _requiredEther();
require(msg.value >= requiredEther, "msg.value too low");
// Do your stuff here.
// ...
// Return extra to msg.sender.
// This part must be at the end of the function to prevent re-entrancy exploits.
if (msg.value > requiredEther) {
(bool success, ) = payable(msg.sender).call(){value: msg.value-requiredEther};
require(success, "Reimbursement failed");
}
}
On the frontend, you can read the requiredEther
and add a small % to compute the msg.value
to send.
Another solution is using Wrapped Ether (WETH) as transfer method since it can be pulled (with transferFrom
). However users would need to do two extra transactions: one to wrap their Ether if they don't have any, and one to approve the contract.