Yep that’s correct. A public function can be called from anyone and any contract.
Hi @DavidV,
Correct
As @jak says, it is visible from anywhere, and can theoretically be called from anywhere, although that will also depend on any constraints placed on the calling address by any require statements or modifiers.
The difference between public
and external
visibility is that, as well as being visible from external contracts and from an external service, a public
function can also be called from within the same contract (or any of that contract’s derived contracts — the contracts that inherit it).
The difference between public
and internal
visibility is that, as well as being visible from within the same contract or any of that contract’s derived contracts, a public
function can also be called from external contracts and from an external service.
Is there a way of naming assert or require statements so that you can more easily identify why an error has occurred?
Hi @leolukaz,
We can’t actually name the statements, but require() allows us to add an error message of our own choosing as a second parameter e.g.
require(msg.sender == owner, "Access restricted to contract owner");
The error message will be stated as the reason for revert being triggered, and this will enable us to identify which require statement failed.
We cannot add an error message for assert(). However, as assert() shouldn’t fail, because it’s checking an invariance, which should always be true, this isn’t really an issue.
Let me know if you have any further questions.
Hi everyone,
I’m just wondering if the following two functions mean exactly the same thing or not :
//function1
function addBalance(uint _toAdd) public returns (uint){
require(owner == msg.sender);
balance[msg.sender] += _toAdd;
return balance[msg.sender];
//function2
function addBalance(uint _toAdd) public returns (uint){
require(msg.sender == owner);
balance[msg.sender] += _toAdd;
return balance[msg.sender];
Can they be used interchangeably?
Cheers,
Built
Although is the same, the instructions logic might be the same in some cases.
function 1: owner must be equal to msg.sender.
function 2: msg.sender must be equal to owner.
The thing is, on which use case you need to confirm who first.
Carlos Z
When i let the line with “adress owner;” undefined, i cant add balance to any address and i get this error message:
transact to Firstcontract.addBalance errored: VM error: revert. revert The transaction has been reverted to the initial state. Note: The called function should be payable if you send value and the value you send should be less than your current balance. Debug the transaction to get more information.
As soon as i define “owner” with my adress, the code starts running without any problem…
Do i have a problem in the settings or where does it come from?
PS: i tripple checked it, my code is exactly the same as Filips in the course.
Hey @Leonard_Gohlke, hope you are well.
Please share your contract in the following way so we can give it a look to solve your issue
Carlos Z
Hey thecil! Here is the code:
pragma solidity 0.7.5;
contract Firstcontract {
mapping(address=>uint)balance;
address owner;
event balanceAdded( uint amount, address depositTo);
event coinsTransfered (address sentTo, uint amount, address sentFrom);
modifier onlyOwner{
require(msg.sender == owner );
_;
}
function addBalance(uint _toAdd) public onlyOwner returns (uint){
balance[msg.sender]+=_toAdd;
emit balanceAdded(_toAdd, msg.sender);
return balance[msg.sender];
}
function getBalance() public view returns (uint){
return balance[msg.sender];
}
function transfer(address recipient, uint amount) public {
require (balance[msg.sender]>=amount, "balance not sufficient");
require (msg.sender != recipient, "dont send money to yourself!");
uint previousSenderBalance = balance [msg.sender];
_transfer (msg.sender, recipient, amount);
assert (balance[msg.sender]==previousSenderBalance - amount );
emit coinsTransfered(recipient, amount, msg.sender);
}
function _transfer(address from, address to, uint amount) private {
balance[from]-=amount;
balance[to]+=amount;
}
Hey guys i just realized i forgot to add the constructor() function into my code… Now it is working.
Thank you very much for your help
@thecil I’m getting a bit confused right now here…I want to create a p2p lending smart contract but really I don’t know what I need to write in the code…it’s going to be a DeX where they can just connect to wallet…do deposit and all of that needed to be on th e code…and can you help with maybe any sketch or information I can use…?
I have a question about modifiers. I’m working on the Inheritance assignment, and in reworking my Bank contract, I am adding some new modifiers. I get errors on enoughBalance and senderNotRec modifiers, probably because the amount and toRecipient values in the modifiers are not declared. Can I pass those vars to the modifiers? Or how to go about this?
pragma solidity 0.7.5;
//SPDX-License-Identifier: UNLICENSED
//\\ -- BANK -- //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
contract Bank {
address owner;
mapping (address => uint) balance;
event depositDone(uint amount, address indexed depositedTo);
event transferDone(uint amount, address indexed sentTo, address indexed sentFrom);
modifier onlyOwner() {
require(msg.sender == owner);
_; // run the function
}
modifier enoughBalance() {
// check if balance of sender is sufficient
require(balance[msg.sender] >= amount,"Balance not sufficient");
_; // run the function
}
modifier senderNotRec() {
// check for redundancy
require(msg.sender != toRecipient, "Don't send money to yourself");
_; // run the function
}
// INIT //////////////////////////////////
constructor() {
owner = msg.sender;
}
// DEPOSIT - payable //////////////////////////////////
function deposit() public payable returns(uint) {
balance[msg.sender] += msg.value;
emit depositDone(msg.value, msg.sender);
return balance[msg.sender];
}
// GET BALANCE - read only //////////////////////////////////
function getBalance() public view returns(uint) {
return balance[msg.sender];
}
// WITHDRAW //////////////////////////////////
function withdraw(uint amount) public enoughBalance returns (uint) {
// msg.sender is an address, and address has a method to trasfer
msg.sender.transfer(amount);
// adjust balance
balance[msg.sender] -= amount;
return balance[msg.sender];
}
// TRANSFER TO //////////////////////////////////
function tranferTo(address recipient, uint amount) public enoughBalance senderNotRec {
uint previousSenderBalance = balance[msg.sender];
_transfer(msg.sender, recipient, amount);
assert(balance[msg.sender]==previousSenderBalance - amount);
emit transferDone(recipient, amount, msg.sender);
}
// _TRANSFER - private //////////////////////////////////
function _transfer(address from, address to, uint amount) private {
balance[from] -= amount;
balance[to] += amount;
}
}
Both modifiers needs to know the value of the parameter you want to compare. So you might have to do something like:
modifier enoughBalance(uint amount) {
// check if balance of sender is sufficient
require(balance[msg.sender] >= amount,"Balance not sufficient");
_; // run the function
}
Then, you just need to send the argument of the modifier on the function you want to trigger it:
function withdraw(uint amount) public enoughBalance(amount) returns (uint) {
// msg.sender is an address, and address has a method to trasfer
msg.sender.transfer(amount);
// adjust balance
balance[msg.sender] -= amount;
return balance[msg.sender];
}
Carlos Z
Got it! Thank you very much
I am confused about ‘msg.sender == owner’.
Who is the owner?
Does creating a transaction amount to deploying a contract?
Hi @Rounak_Jain,
Welcome to the forum
owner
references the owner
state variable, so its value is the value stored in that state variable, which in our case is the address that deploys the contract.
When the contract is deployed, the address that deploys it is the address displayed in the Account field in the Deploy & Run Transactions panel. You can change this address by selecting another one from the dropdown.
On deployment, the constructor assigns the deployer’s address to the owner
state variable. This will be the address referenced by owner
in the modifier’s require() statement…
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
The condition in the require() statement is comparing the owner
address with whichever address calls a function (msg.sender
) containing the onlyOwner modifier in its header.
If these two addresses are the same, the condition will evaluate to true
and the function will continue executing. In other words, a function that includes the onlyOwner modifier in it’s header will only execute if it is called by the owner
address. The address that calls a function is the address displayed in the Account field when the button with the function name is clicked in the Deploy & Run Transactions panel.
No… the contract needs to be deployed before any transactions can be executed. Contract deployment is also itself a transaction — the first transaction associated with that contract. Once the contract is deployed it remains deployed. To create a transaction, a function needs to be called which will change the contract state (orange and red buttons in Remix).
Just let me know if anything is still unclear, or if you have any further questions.
Thanks for the reply Jon.
I am an experienced javascript programmer, so the code part is clear.
I will try to rephrase my question:
I am not able to relate to the “owner” in real world scenario. If deployer is the owner, what is the practical use case of writing a contract which only interacts with his account?
Basically, in my mind, I am thinking of a contract enabling transactions between several parties. I am not able to relate the Bank contract example with such real world usage.
Thanks again.
Hi @Rounak_Jain,
At this early stage, we are only experimenting with an example, in order to introduce different Solidity concepts and syntax. The important thing about the code you’ve highlighted at this point in the course is that you understand how we can code a condition within a require statement within a modifier, which only allows a particular address to call and execute a function, and not so much which particular function, and which specific address, this modifier would be best applied to — that comes later.
You are right that it would make no practical sense, in a real-world scenario, to restrict access to the deposit function of a smart contract designed to provide a “banking service”. However, later on in the course, after introducing more concepts and syntax, you will see how we can then use this same onlyOwner modifer to restrict access to a function which destroys the contract. For obvious reasons we don’t want to allow any address to destroy the contract!
You are absolutely right to apply this type of critical thinking to the example code we are using, because, ultimately, we obviously want to write smart contracts which add value to real world scenarios. Just bear in mind, though, that, especially during this introductory course, even though we do try to make the examples as realistic as possible, some elements have a more theoretical and instructional focus in terms of introducing the syntax as efficiently as possible.
As you are already an experienced programmer, and are naturally relating the theory you are learning to potential real-world use cases, whenever you notice potential flaws or disadvantages with certain parts of our Bank contract — and there will be more, I promise you — I would encourage you to experiment with the code yourself and try to come up with your own improvements/modifications which make more practical sense to you. When you come on to the assignments, there are always alternative solutions, and as long as you don’t completely rewrite the whole contract, we do encourage you to introduce your own adaptations and extensions.
Just one final point about the onlyOwner
modifier applied to the deposit function. As we also have a transfer function which allows deposited funds to be transferred to other addresses within the contract, we could interpret this particular version of the contract as enabling a single owner to deposit funds and then distribute these funds to other addresses of their choosing. Perhaps this version would be better named contract Philanthropy
, or contract Funding
. A little later in the course we will also be adding a withdraw function, which won’t be restricted to the owner
address, so that addresses with funds can make withdrawals.
What I’m trying to say is that, even though we’ve called the contract Bank
, you sometimes need to be a bit more flexible and imaginative in how you think about potential real-world use cases than the contract name at first suggests.
I hope that makes sense. Just let me know if you have any other concerns or questions
Thanks for the clarification.