Nice solution @ktchaliov
Can we also see what modifications youâve made to the start of your Bank.sol file, and Bank contract header, for the inheritance?
Nice solution @ktchaliov
Can we also see what modifications youâve made to the start of your Bank.sol file, and Bank contract header, for the inheritance?
Hi @Eric_Toronto,
Thatâs a great bit of experimentation youâve done there!
Putting the Destroyed event to one side first, your solution works and, on the whole, itâs a good one.
Personally, I think it would be better to keep the contract ownership and contract destruction functionality separate, each within its own contract. You would then need to decide how to reorganise the inheritance structure, because you would now have three contracts instead of just two. Can we also see what modifications youâve made to the start of your Bank.sol file, and Bank contract header, for the inheritance?
Itâs a good idea, but unfortunately, I donât think it can be done by generating and outputting some kind of message from within the close() function, or from anywhere else within the deployed smart contract, for that matter. This is because the selfdestruct function (which actually performs the destruction of the contract) already comes built-in to the Solidity language, and its predetermined functionality apparently doesnât include the generation of a confirmation message. You are right that emitting your event immediately before selfdestruct() is called, is not an appropriate solution, because if selfdestruct() fails for some reason, then there is a risk that the event could still be emitted with incorrect information. We can only confirm an event has happened after it has actually happened, even if we are certain that the risk of the event not happening is extremely small. As blockchain developers we always need to have the mindset that our code must still do what itâs supposed to, even if the worst does happen. However, the problem here is that, if we place the emit statement at the end of the close() function (where emit statements should be), then if the selfdestruct function successfully performs its job, there will be no smart contract left in existance, and no close() function body to continue executing in order to emit the event with the confirmation message!
This highlights one of the big drawbacks of using the selfdestruct function. If a user doesnât know that the contract has been destroyed, and goes ahead and sends funds to it (e.g. by calling the deposit function), then those funds will be lost. You can see this for yourself by calling deposit() with an ether value. Remix will notify you that the transaction has been successful, and you will see the ether amount has been deducted from your external account balance. But if you add a function that gets the ether contract balance and call that (not the getBalance function), you will see that the contract has a zero balance! As a result of this, if a smart contract deployed on the mainnet is ever destroyed, all users need to be notified that they must not send any more funds to that contract address, otherwise those funds will be lost. There are more secure ways to manage this kind of situation (e.g. by using proxy contracts) but this is out of the scope of this introductory course. If you are interested, then I would highly recommend that you take the Ethereum Smart Contract Security Course, either after this course, or after youâve also done Ethereum Smart Contract Programming 201.
Hi @M.K,
Just following on from what @thecil and @mcgrane5 have said, this line of codeâŚ
govermentInstance.addTransaction(msg.sender, recipient, amount);
⌠relates to a future lesson about external contracts. Itâs crept into the solution code for this assignment by mistake, and so you can ignore it for the purposes of this assignment. When you are eventually introduced to the Government contract, the meaning of this line of code should become clear⌠or maybe youâve already got to that lesson nowâŚ
pragma solidity 0.7.5;
contract own {
address owner;
modifier onlyOwner {
require(msg.sender == owner);
_;
}
constructor(){
owner = msg.sender;
}
}
contract destryable is own {
function destroy() public onlyOwner {
selfdestruct(msg.sender);
}
}
Thank you for your comment @jon_m .
The modification is pretty much the same as the âdestroyableâ contract.
import "./owner.sol";
contract Bank is Ownable
pragma solidity 0.7.5;
contract Destroyable {
modifier _onlyOwner {
require(msg.sender == owner,"Sorry, you are not the Contract owner");
_;
}
address payable owner;
constructor () {
owner = msg.sender;
}
function close () public _onlyOwner {
selfdestruct (owner);
}
}
Nice solution @Bojo
Can we also see what modifications youâve made to the start of your Bank.sol file, and Bank contract header, for the inheritance? This is also part of the solution, because itâs the Bank contract that will be deployed and inherit the additional functionality.
Just one other thing⌠the convention is to start contract names with a capital letter (and also struct and event names). Your code will still compile and deploy perfectly well without the capital letters, but I just wanted to let you know what the generally agreed best practice is. Itâs function, variable, mapping, array and modifier names which usually start with a lower case letter.
Hi @ktchaliov,
This will only result in the Bank contract inheriting Ownable. Ownable doesnât inherit Destroyable (itâs the other way round). So, with this solution, when you deploy Bank the Destroyable functionality wonât be available. What changes do you need to make?
In case the final objective of the assignment wasnât clear⌠we want to be able to deploy Bank and have both the Ownable and Destroyable functionality available.
Nice solution @bfleming98
Personally, I think it would be better to keep the contract ownership and contract destruction functionality separate â each within its own contract. You would then need to decide how to reorganise the inheritance structure, because you would now have three contracts instead of just two.
Can we also see what modifications youâve made to the start of your Bank.sol file, and Bank contract header, for the inheritance?
Ahaa I see okay⌠I think I lost myself, cuz for some reason the destroyable.sol tab in Remix disappeared and I am also trying to understand the Government ;d but thatâs okay I guess ⌠I have to rewrite them a couple of timesâŚ
However, I have Destroyable inheriting Owner so should Bank inherit only Destroyable (as it has Owner inherited already) or Bank should separately inherent both?
Yes - I did not know we were supposed to be recreating the code on our end or do it in different files. I will resubmit Monday.
Good question @ktchaliov,
Yes, thatâs correct
This isnât necessaryâŚ
We can streamline the inheritance by having Bank just inherit Destroyable, because it will then indirectly inherit Owner/Ownable via Destroyable. This will give you a multi-level inheritance structure.
pragma solidity 0.8.1;
import "./Ownable.sol";
contract Destroyable is Ownable {
function destroy() public onlyOwner {
selfdestruct(owner);
}
}
We did not know we were supposed to be rebuilding his code. Instead we made it a Roblox ERC20 coin. We are family taking this course together. 2 boys love roblox.
What do you think?
pragma solidity 0.7.5;
contract robloxErc20 {
mapping(address => uint) balance;
address owner;
constructor(){
owner = msg.sender;
}
function _transferBalance(address Recipient, uint _amount) public returns (uint) {
require(balance[msg.sender] >= _amount,"Funds Invalid! Not Enough");
balance [msg.sender] -= _amount;
balance [Recipient] += _amount;
return balance[msg.sender];
}
function _addBalance (uint _amount) public returns (uint) {
require(msg.sender == owner,"Sorry, you are not the Contract owner");
balance[msg.sender] += _amount;
return balance[msg.sender];
}
function _getBalance () public view returns (uint) {
return balance[msg.sender];
}
}
Hi @Mickey_McClimon,
Can we see your other two contracts which are also part of this inheritance structure?
Your Destroyable contract looks good, but it relies on you having made a couple of additional changes in Ownable. Thatâs why we need to see it as part of your solution.
We also need to see what modifications youâve made to the start of your Bank.sol file, and Bank contract header, for the inheritance. And the fact that youâre using Solidity v0.8 instead of v0.7 will also require an additional modification in the Bank contract. Donât get me wrongâŚItâs great that youâre using a more up-to-date version of Solidity â it just means that I need to see the rest of your code in order to be able to check your solution properly
pragma solidity 0.8.1;
import "./Ownable.sol";
interface GovermentInterface{
function addTransaction(address from, address to, uint amount) external payable;
}
contract Bank is Ownable {
GovermentInterface govermentInstance = GovermentInterface(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4)
mapping(address => uint) balance;
event depositDone(uint amount, address indexed depositedTo);
function deposit() public payable returns (uint) {
balance[msg.sender] += msg.value;
emit depositDone(msg.value, msg.sender);
return balance[msg.sender];
}
function withdraw(uint amount) public onlyOwner returns (uint) {
require(balance[msg.sender] >= amount);
msg.sender.transfer(amount);
return balance[msg.sender];
}
function getBalance() public view returns (uint) {
return balance[msg.sender];
}
function transfer(address recipent, uint amount) public {
require(balance[msg.sender] >= amount, "Balance not sufficient");
require(msg.sender != recipent, "Dont't transfer money to yourself");
uint previousSenderBalance = balance[msg.sender];
_transer(msg.sender, recipent, amount);
govermentInstance.addTransaction{value: 1 ether}(msg.sender, recipent, amount);
assert(balance[msg.sender] == previousSenderBalance - amount);
}
function _transer(address from, address to, uint amount) private {
balance[from] -= amount;
balance[to] += amount;
}
}
I do have error codes but I am not entirely sure what is wrong with my mapping?? sorry for not posting them. maybe you could help here? Thank you!
As for using a newer version of solidity i was no able to DEPLOY my contacts when trying out 0.7.5; but maybe it was just a personal error of some kind. Should I be using the earlier version?
Hi again Micky,
Iâve just got to these posts you sent me yesterday for the Inheritance Assignment. I think the problem we have is that Iâve been reviewing both the Transfer and Inheritance Assignments, and also your External Contract code (Government contract) all at the same time. Anyway, not to worry â the end result should be the same.
So, at this point (Inheritance Assignment) where we are focussing on the inheritance structure, I needed to see the 3 contracts related via inheritance: Bank, Destroyable and Ownable (not the Government contract, as this doesnât come into play until later). So, Iâve removed the Government contract youâve posted here, so things donât get confusing. As youâve already seen, Iâve commented on your Government contract here.
As the course was written for v0.7.5 you will find it easier to follow if you use the same version. But there is nothing stopping you from using v0.8. Itâs actually better from an overall development perspective to learn the syntax of the latest versions, but then you need to be aware that you will find a few differences to what is presented in the lectures (which uses v0.7.5). You can find out what these differences are by doing your own research, and confirming these in the documentation:
https://docs.soliditylang.org/en/v0.8.4/080-breaking-changes.html
However, depending on what stage you are at in your programming journey, you may feel thatâs a challenge too far, and in that case it would be easier to use v0.7.5. It doesnât matter too much if you first learn v0.7 because, once you have learnt the basics of Solidity, it isnât too much extra work to update your knowledge with the very latest syntax. There isnât much difference between v0.7 and v0.8. It would be harder work if you learnt say v0.5 and then had to catch up with all the differences between v0.5 and v0.8.
Your updated version is written using v0.7.5. So, have you managed to compile and deploy your contracts using this version now, or are you still having problems?
1) Bank (Instead of the code youâve posted here, please see my comments on your updated version of this contract in the Inheritance & External Contracts - Discussion topic).
However, in terms of the inheritance, there is one other point to make about this contract: yours is inheriting Ownable, but what about Destroyable? Your Destroyable contract already inherits Ownable. Your updated version of this contract also doesnât inherit Destroyable. This means that when you deploy Bank, it will inherit the functionality of Ownable, but not Destroyable, making Destroyable redundant. So, you need to amend how youâve coded the inheritance in Bank. If youâre not sure how to do this, have a good look at some of the other studentsâ solutions and feedback/discussion about this, here, in this discussion topic.
2) Ownable
I still donât think Iâve seen your Ownable contractâŚthis is a vital piece in the puzzle, and if you have any issues there, then this will also be affecting things and possibly causing some of your error messages.
I was able to fix everything by using the 7.5v and it is all good. I will post all contracts here.
pragma solidity 0.7.5;
import "./Ownable.sol";
interface GovermentInterface{
function addTransaction(address from, address to, uint amount) external payable;
}
contract Bank is Ownable {
GovermentInterface govermentInstance = GovermentInterface(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4);
mapping(address => uint) balance;
event depositDone(uint amount, address indexed depositedTo);
function deposit() public payable returns (uint) {
balance[msg.sender] += msg.value;
emit depositDone(msg.value, msg.sender);
return balance[msg.sender];
}
function withdraw(uint amount) public onlyOwner returns (uint) {
require(balance[msg.sender] >= amount);
balance[msg.sender] -= amount;
msg.sender.transfer(amount);
return balance[msg.sender];
}
function getBalance() public view returns (uint) {
return balance[msg.sender];
}
function transfer(address recipent, uint amount) public {
require(balance[msg.sender] >= amount, "Balance not sufficient");
require(msg.sender != recipent, "Dont't transfer money to yourself");
uint previousSenderBalance = balance[msg.sender];
_transer(msg.sender, recipent, amount);
govermentInstance.addTransaction{value: 1 ether}(msg.sender, recipent, amount);
assert(balance[msg.sender] == previousSenderBalance - amount);
}
function _transer(address from, address to, uint amount) private {
balance[from] -= amount;
balance[to] += amount;
}
}
pragma solidity 0.7.5;
import "./Ownable.sol";
contract Destroyable is Ownable {
function destroy() public onlyOwner {
selfdestruct(owner);
}
}
pragma solidity 0.7.5;
contract Ownable {
address payable public owner;
modifier onlyOwner {
require(msg.sender == owner);
_;
}
constructor(){
owner = msg.sender;
}
}
Hey guys! @bfleming98
Thatâs awesome youâre all taking the course together as a family! I hope youâre enjoying the challenge, learning loads and having fun
Thatâs great youâre building your own project whilst following the course. Thatâs a great way to put things into practice, especially if youâre building something that means more to you and is more exciting than a Bank! Go for it!
As well as your own project, I would also try to develop the code Filip uses in the videos, as itâs been specially designed to cover, and give you practice with, all of the different fundamental features and syntax of Solidity. I think a great way to learn would be, for each section, first develop Filipâs code, as then itâs easier for you to check that youâve understood all of the key points when you watch the follow-up videos and look at the model solution code. Youâll also be able to get more detailed feedback when you submit the assignments here in the forum. Then after youâve got the hang of the new code and concepts for a section, go ahead and put it into practice and be creative and have fun with it in your own token projectâŚ
But, hey⌠the most important thing is youâre enjoying the course, having fun, and learning something⌠so do feel free to adapt how you do the course so that it works for you as a family. I think it also depends on whether you are planning to continue with the more advanced Solidity courses. If you are, then you need to make sure youâve practised and understood everything covered in this course, because the ones that follow build on this.
Your token contract is looking really good ⌠Iâve deployed it and itâs working nicely â well done!
Just for your information, I wouldnât call it an ERC20 contract, because for it to be considered an ERC-20 token, the functions in the contract need to meet specific requirements which are laid down in the ERC-20 Standard. As this involves more advanced coding, we cover this in the courses which follow this one.
You can easily build on this token contract for the requirements of this inheritance assignment:
You could also further develop your token contract, by doing the following:
I hope you find these comments helpful. Keep up the great work, guys!