Restricting a sender from withdrawing more than available balance - ethereum

I am working on a simple bank smart contract example but I am having trouble getting the contract to restrict a sender from withdrawing more than the remaining balance. Here is my function within the contract:
function withdraw(uint withdrawAmount) public returns (uint) {
assert(balances[msg.sender] >= withdrawAmount);
balances[owner] -= withdrawAmount;
emit LogWithdrawal(msg.sender, withdrawAmount, balances[msg.sender]);
return balances[msg.sender];
}
Here is the .js test:
it("should not be able to withdraw more than has been deposited", async() => {
await instance.enroll({from: alice})
await instance.deposit({from: alice, value: deposit})
await catchRevert(instance.withdraw(deposit + 1, {from: alice}))
})
I was thinking maybe assert(.....) but that didn't work so any assistance would be appreciated.

First of all, you should use require instead of assert in this case. That being said your code seems ok, so make sure you are keeping track of the user balances correctly.

Your code is not consistent, you check the balance for msg.sender, but withdraw from owner.
It should be the correct version:
function withdraw(uint withdrawAmount) public returns (uint) {
require(balances[msg.sender] >= withdrawAmount);
balances[msg.sender] -= withdrawAmount;
emit LogWithdrawal(msg.sender, withdrawAmount, balances[msg.sender]);
return balances[msg.sender];
}

Related

I would like to automatically send some text (like a key) if I receive a payment of 10 Ether

I have a Rinkeby Metamask account that I would like to get students to pay me 10 Ether, to show that they have understood the following principles as part of a challenge:
Creation of an online wallet that deals with ethereum
Ability to get enough free ethereum to complete the payment
Finally pay the total in one payment
Once this payment has gone through I would like to give them a text string say "Well_Done_101" that would prove they have completed the challenge.
I understand that smart contracts are available but I'm not sure how this would work as I would want the text string to be invisible until the payment has been completed.
This is some code that I tried earlier:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract payment_for_art {
// default value is `false`, don't need to explicitly state it
bool isPaid;
function winner() public view returns (string memory) {
return flag;
}
function hidden() private view returns (string memory) {
string memory flag_true;
flag_true = 'Well_done_101';
return flag;
}
function invest() external payable {
// to prevent multiple payments
// reverts if the condition is not met
require(isPaid == false);
if(msg.value < 10 ether) {
revert('Pay me the full amount');
}
if(isPaid = true) { // flag that the payment has been done
//return ('Well_Done_101');
return flag_true;
}
}
function balance_of() external view returns(uint) {
return address(this).balance;
}
}
Any ideas or comments would be greatly appreciated.
I think what you're searching for is something like this.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract payment_for_art {
// Mappings of studentNames to flag; Its like a key => value list data type.
// Public storage variables can be accessed by anyone (solidity creates a getter implicitly), but can't be modified directly.
mapping (string => string) public buyers;
// Recieves the payment, and also the students name (your students must call this function with their name as a parameter).
function invest(string memory studentName) external payable {
// check if "studentname" isn't empty.
require(bytes(studentName).length != 0, "You forgot to specify your student name.");
// check if the payment is 10 eth.
require(msg.value == 10 ether, "You are either paying too much, or too little.");
// check if the student already bought, by checking if his flag is set in the mapping.
require(bytes(buyers[studentName]).length == 0, "You already bought the art.");
// set flag for student.
// While regular string literals can only contain ASCII,
// Unicode literals – prefixed with the keyword unicode – can contain any valid UTF-8 sequence (this allows you to use emogis and whatnot).
// They also support the very same escape sequences as regular string literals.
buyers[studentName] = unicode"Well_Done_101 😃";
}
function balance_of() external view returns(uint) {
return address(this).balance;
}
}
And to check if they bought, just call the mapping and search for your students name.
Example made using web3:
payment_for_artContract.methods.invest("Daniel Jackson")
.send({ from: account, value: weiValue})
.on('transactionHash', (hash) => {
const studentsWhoBought = await payment_for_artContract.methods.buyers().call();
// prints "Well_Done_101 😃";
console.log(studentsWhoBought["Daniel Jackson"]);
console.log(studentsWhoBought);
})
.on('error', (err) => {
console.error(err);
})
If you want to actually get a value from it when its called, you'll need to use events (triggers that clients can subscribe to) and have web3 subscribe to said event.
For example:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract payment_for_art {
// Mappings of studentNames to flag; Its like a key => value list data type.
// Public storage variables can be accessed by anyone (solidity creates a getter implicitly), but can't be modified directly.
mapping (string => string) public buyers;
// Event of buying.
event Bought(string studentName, address studentAddress, string flag);
// Recieves the payment, and also the students name (your students must call this function with their name as a parameter).
function invest(string memory studentName) external payable {
// check if "studentname" isn't empty.
require(bytes(studentName).length != 0, "You forgot to specify your student name.");
// check if the payment is 10 eth.
require(msg.value == 10 ether, "You are either paying too much, or too little.");
// check if the student already bought, by checking if his flag is set in the mapping.
require(bytes(buyers[studentName]).length == 0, "You already bought the art.");
// set flag for student.
// While regular string literals can only contain ASCII,
// Unicode literals – prefixed with the keyword unicode – can contain any valid UTF-8 sequence (this allows you to use emogis and whatnot).
// They also support the very same escape sequences as regular string literals.
buyers[studentName] = unicode"Well_Done_101 😃";
emit Bought(studentName, msg.sender, unicode"Well_Done_101 😃");
}
function balance_of() external view returns(uint) {
return address(this).balance;
}
}
And then in web3:
payment_for_artContract.methods.invest("Daniel Jackson")
.send({ from: account, value: weiValue})
.on('transactionHash', (hash) => {
console.log("transaction mined");
})
.on('error', (err) => {
console.error(err);
});
const options = {
fromBlock: 0, // Number || "earliest" || "pending" || "latest"
toBlock: 'latest'
};
payment_for_artContract.events.Bought(options)
// prints the student's name who bought, address of the student, and "Well_Done_101 😃";
.on('data', event => console.log(event))
.on('changed', changed => console.log(changed))
.on('error', err => throw err)
.on('connected', str => console.log(str));
I would stick with the first method, since its easier, yet the second method is how it should be properly done.
Try that, and let me know if thats what you were searching for :)

Estimating gas of an ERC20 transfer

I'd like to estimate the gas of a simple ERC20 transfer between two addresses. The web3.js docs on estimateGas are admittedly confusing:
// using the callback
myContract.methods.myMethod(123).estimateGas({gas: 5000000}, function(error, gasAmount){
if(gasAmount == 5000000)
console.log('Method ran out of gas');
});
The myMethod(123) is what I find confusing. What is that for? The following is what I'm currently thinking, but I'm getting TypeError: contract.methods.send is not a function. What should I substitute for myMethod(123)?
try {
await contract.methods
.send("0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe")
.estimateGas({ gas: 60000 }, (error, gasAmount) => {
return gasAmount;
});
} catch (err) {
console.log(err);
}
send() refers to a contract method called send. You do not have send in your Solidity Contract source code.
Instead, try contract.methods.myMethod.send
A bit late, but you need to look for the function in the smartcontract you want to call and replace myMethod(123) for the function in the contract you are calling.
For example, if you are working with the pancakeswap contract and check in the code the function function deposit(uint256 _pid, uint256 _amount) public {...} (line 1674), you will need to do this:
const estimatedGas = await contract.methods.deposit(123, 0).estimateGas({from: "0x345})
You need to pass the args to deposit in order to make it work.

Solidity: Events triggered but state not updated after delegatecall

I would like to use delegatecall to deposit ETH into WETH and batch this process together with other actions (see code below). When running truffle tests, it seems like the events are triggered correctly but the state does not seem to be updated.
Same behavior on the Rinkeby network. The weird thing is that the transaction completes completely successfully. Here is an example of transaction: https://rinkeby.etherscan.io/tx/0x93c724174e2b70f2257544ebc0e0b9da6e31a7a752872da7683711bd4d4bd92b
Solidity code:
pragma solidity 0.4.24;
import "../interfaces/ERC20.sol";
import "./WETH9.sol";
contract SetupAccount {
address public exchangeAddress;
address public wethAddress;
constructor (
address _exchangeAddress,
address _wethAddress
) public {
exchangeAddress = _exchangeAddress;
wethAddress = _wethAddress;
}
function setup(
address[] _tokenAddresses,
uint256[] _values
) public payable {
for (uint i = 0; i < _tokenAddresses.length; i++) {
_tokenAddresses[i].delegatecall(abi.encodeWithSignature("approve(address,uint256)", exchangeAddress, _values[i]));
}
if (msg.value != 0) {
wethAddress.delegatecall(abi.encodeWithSignature("deposit()"));
}
}
Failing truffle test:
describe('setupAccount', async () => {
beforeEach(async () => {
weth = await WETH.new()
exchange = await Exchange.new(rewardAccount)
bnb = await BNB.new(user1, 1000)
dai = await DAI.new(user1, 1000)
omg = await OMG.new(user1, 1000)
setupAccount = await SetupAccount.new(exchange.address, weth.address)
})
it('setupAccount should deposit weth and approve tokens', async () => {
await setupAccount.setup(
[bnb.address, dai.address, omg.address],
[1000, 1000, 1000],
{ from: user1, value: 10 ** 18 }
)
let wethBalance = await weth.balanceOf(user1)
wethBalance.should.be.bignumber.equal(10 ** 18)
bnbAllowance.should.be.bignumber.equal(1000)
daiAllowance.should.be.bignumber.equal(1000)
omgAllowance.should.be.bignumber.equal(1000)
})
})
Events emitted during test:
Events emitted during test:
---------------------------
Deposit(dst: <indexed>, wad: 1000000000000000000)
---------------------------
Test result:
1) Contract: SetupAccount
setupAccount
setupAccount should deposit weth and approve tokens:
AssertionError: expected '0' to equal '1000000000000000000'
+ expected - actual
-0
+1000000000000000000
I'm confused as to why this doesn't work. Thank's in advance.
msg.value is preserved correctly through a delegatecall. The following code demonstrates this, as evidenced by the emitted event showing the correct value.
pragma solidity >0.4.99 <0.6;
contract Target {
event Received(uint256 amount);
function deposit() external payable {
emit Received(msg.value);
}
}
contract Delegater {
function deposit() external payable {
(bool success,) = address(new Target()).delegatecall(abi.encodeWithSignature("deposit()"));
require(success);
}
}

web3 balanceOf always 0

I'm trying to get the balance of an address on my smart contract using web3, but the balance is always 0. Using metamask on Rinkeby since my contract is deployed on rinkeby. https://rinkeby.etherscan.io/address/0x8e3a88be716ce7c8119c36558ec97bc634592255
You can verify the wallet has a balance by putting it in the balanceOf function on etherScan. Use the address 0x8b54A82a12bD5A7bA33B4842cA677E55f78a8612
let provider = web3.currentProvider;
web3 = new Web3(provider);
let abi = 'too long of a string to post here';
let MyContract = web3.eth.contract(JSON.parse(abi));
let myContractInstance = MyContract.at('0x8e3a88be716ce7c8119c36558ec97bc634592255');
let address = '0x8b54A82a12bD5A7bA33B4842cA677E55f78a8612';
function balanceOf(address) {
if (!address) {
return;
}
this.myContractInstance.balanceOf.call(address, function(error, balance) {
if (error) {
return;
}
alert(balance.c[0] + ' RHC');
});
}
balanceOf(address);
Here is the getBalance function on my contract
function balanceOf(address _owner) public view returns (uint256 balance) {
return balances[_owner];
}
Website implemented on http://robinhoodcoin.net/metamask.html
Code https://github.com/robinhoodcoin/robinhoodcoin.github.io/blob/master/metamask.html
EDIT
when I change the provider to be the following:
var web3 = new Web3(new Web3.providers.HttpProvider('https://rinkeby.infura.io/'));
I am able to get the balance. SO there is something up with using metamask as the provider.
The line at https://github.com/robinhoodcoin/robinhoodcoin.github.io/blob/master/metamask.html#L118 has a typo. It reads:
self.myContractInstance = self.MyContract.at(self.address);
but the address is stored at self.contractAddress, so it should read:
self.myContractInstance = self.MyContract.at(self.contractAddress);
After making that fix, the page works fine for me with MetaMask.

How are the ethers sent to smart contract in the IBM example

I was reading the IBM Example about smartSponsor, and there is this following code:
personal.unlockAccount(thesponsor,"password");
ss.pledge("Good luck with the run!", {from: thesponsor, value: 10000000, gas: 3000000});
While the function pledge is:
function pledge(bytes32 _message) {
if (msg.value == 0 || complete || refunded) throw;
pledges[numPledges] = Pledge(msg.value, msg.sender, _message);
numPledges++;
}
struct Pledge {
uint amount;
address eth_address;
bytes32 message;
}
I was looking for "send" or "transfer" functions. But I could not find any. Therefore I was confused how were the ethers sent from the sponsor to the smart contract?
Update:
How did the sender send the ethers? I was expecting something like .transfer(uint256 amount) or .send(uint256 amount). But it seems that there is no such function call?
They are in magical variable msg. The function pledge uses this variable in the following line:
pledges[numPledges] = Pledge(msg.value, msg.sender, _message);