import "./Ownable.sol";
pragma solidity 0.7.5;
contract Destroyable is Ownable {
function destroy() public onlyOwner {
address payable receiver = msg.sender;
selfdestruct(receiver);
}
}
contract Bank is Destroyable {
import "./Ownable.sol";
pragma solidity 0.7.5;
contract Destroyable is Ownable {
function destroy() public onlyOwner {
address payable receiver = msg.sender;
selfdestruct(receiver);
}
}
contract Bank is Destroyable {
Contract for destroying
contract Destroyable{
function Destroy() public {
selfdestruct(msg.sender);
}
}
Ownable Contract
contract Ownable{
address owner;
modifier OnlyOwner{
require(owner == msg.sender);
_; //run the function
}
constructor(){
owner = msg.sender;
}
}
Modified bank contract (check the last few lines of code)
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;
import "./Destroyable.sol" ;
import "./Ownable.sol";
contract Bank2 is Destroyable, Ownable{
mapping(address => uint)Balance;
event DepositDone(uint Amount, address DepositedTo);
event TransactionDetails(address sender, address receiver, uint amount);
function Deposit() public payable returns(uint){
Balance[msg.sender] = Balance[msg.sender] + msg.value; //Adds ether to the contract
emit DepositDone(msg.value, msg.sender);
return Balance[msg.sender];
}
function Withdraw(uint amount) public OnlyOwner returns(uint){
require(amount <= Balance[msg.sender]);
msg.sender.transfer(amount); //transfers the Ether in the smart contract to the selected address
Balance[msg.sender] = Balance[msg.sender] - amount;
return Balance[msg.sender];
}
function transfer(address recipient, uint amount) public{
require(Balance[msg.sender] >= amount, "Balance not enough");
require(msg.sender != recipient, "You can't send funds to yourself!");
uint BalanceBefore = Balance[msg.sender];
_transfer(msg.sender, recipient, amount);
assert( Balance[msg.sender] == BalanceBefore - amount);
emit TransactionDetails(msg.sender , recipient, amount);
//calls the transfer function, and takes the parameters of msg.sender(owner of an address), recipient and an amount
}
function _transfer(address from, address to, uint _amount) private{
Balance[from] -= _amount;
Balance[to] += _amount;
//Subtracts balance from the sender and adds it to the recipient
}
function getBalance() public view returns(uint) {
return Balance[msg.sender];
}
function DestroyContract() public OnlyOwner {
Destroy();
}
}
Hi @Giupi,
Your solution meets all of the assignment objectives. Your implementation of a multi-level inheritance structure is also well coded and is the best approach: Bank only needs to explicitly inherit Destroyable, because it will implicitly inherit Ownable via Destroyable
However, you havenât taken full advantage of your inheritance structure, and youâve included a lot of code which isnât necessary. The same objectives can be met in a much more concise way âŚ
(1) By giving your destroy() function in Destroyable internal
visibility, it is inherited and available within Bank, but cannot be called externally by the contract owner. To resolve this youâve added an additional public
function to Bank which in turn calls destroy(). However, an important reason for using inheritance is to reduce code duplication, and if you change the visibility of the destroy() function from internal
to public
, it will be inherited and available to be called from both within Bank and externally from Remix. You could also give it external
visibility, which would enable it to be called externally from Remix, but not from within Bank, which for the purposes of this assignment would be enough. This removes the need for the additional destroyContract() function in Bank.
destroy() will now appear as a function-call button in the Remix panel, instead of destroyContract()
Additionally, by keeping all of the code and functionality related to contract destruction within a single, separate contract (Destroyable), you will improve the modularity and re-usability of your code (other important reasons for using inheritance) and keep your code more organised.
(2)
These lines of code in your destroyContract() function achieve the desired result, and from a technical point of view they are well coded. However âŚ
msg.sender.transfer(address(this).balance);
selfdestruct
also automatically transfers the remaining contract balance to the payable address argument it is called with (in our case, the contract ownerâs address). This is all part of the pre-defined selfdestruct
functionality, and is why there is no need to perform the extra steps that you describe here âŚSo, in summary, and as a result of all of the above points, instead of the two functions you currently have (destroy and destroyContract) all you need is the following one in DestroyableâŚ
function destroy() public onlyOwner {
selfdestruct(owner);
}
A couple of additional observations âŚ
(3)
Only functions that receive Ether from an external address (in order to add it to the contract address balance) should be marked payable
(e.g. the deposit function in Bank). Your destroyContract() function doesnât receive Ether: by calling withdraw() it deducts Ether from the contract address balance and sends it to an external address. Your destroy() function will also do the same when you change its visibility.
The method selfdestruct
in your destroy() function does require a payable address argument. This is so it can transfer the remaining Ether in the contract to this receiving address when the contract is destroyed. You have already ensured that this address argument (owner
, in your case) is payable, by defining the owner
state variable in Ownable as a payable address.
(4) In your withdraw() function, itâs important to modify the contract state by reducing the individual userâs balance in the mapping by the withdrawal amount âŚ
balance[msg.sender] -= amount;
before actually transferring the funds out of the contract to the external addressâŚ
msg.sender.transfer(amount);
This is to prevent what is known as a re-entrancy attack from occuring after the transfer, but before the userâs individual balance (effectively, the share of the total contract balance they are entitled to withdraw) is reduced to reflect this operation. Youâll learn more about this type of attack, and how the above order of operations helps to prevent it, in the courses which follow this one.
Just let me know if anything is unclear, or if you have any questions
Hi @CryptoUsic,
Your implementation of a multi-level inheritance structure is the best approach. Bank only needs to explicitly inherit Destroyable, because it will implicitly inherit Ownable via Destroyable
Your solution works if your Destroyable contract is in the same file as Bank. However, better practice is to place each contract in their own separate file, so that Destroyable, as well as Ownable, can be re-used as an independent module. That would also mean adding an import statement to Bank.sol to import Destroyable.sol.
Also, calling selfdestruct() with msg.sender
directly âŚ
selfdestruct(msg.sender);
⌠is a more concise alternative to using the additional receiver
variable âŚ
Have a look at this post for further details about the use of this additional local variable (receiver
) in the model solution for this assignment.
Just let me know if you have any questions.
Hi @Adrian1,
Your solution is almost there, except for one very serious flaw. If you deploy your Bank contract, you will notice that there are 2 functions that can be called from Remix to destroy the contract and transfer the Ether remaining in the contract to the calling address âŚ
DestroyContract() in Bank, which only allows the contract owner to destroy the contract and receive all of the remaining funds
Destroy() in Destroyable can also be called directly from Remix because it has public
visibility, and so is inherited by Bank and can also be called by an external address. Not only is this unnecessary code duplication, but also, more seriously, any address can call this function, destroying the contract and transferring all of the remaining funds to their own external address (not the contract ownerâs), because calling this function directly bypasses the onlyOwner restriction which is only applied to DestroyContract()
One easy fix would be to change the visibility of the Destroy() function in Destroyable from public
to internal
. This would still allow it to be inherited, but only available to call from within Bank. This means that only the contract owner would be able to call DestroyContract(), and DestroyContract() could then still call Destroy(), destroying the contract and transferring the remaining funds to msg.sender
(the caller of the DestroyContract function, which can only be the owner).
However, this would still leave you with unnecessary code duplication, and reducing code duplication is an important reason for using inheritance. So, if you keep Destroy() public
and add the onlyOwner modifier to this function instead, you can remove DestroyContract() from Bank altogether. Youâll also need to change your inheritance structure, so that onlyOwner is available in Destroyable and not just Bank. But this will mean that Destroy() is now the only function available to call from Remix which can trigger selfdestruct
, but this time only the contract owner will be able to perform this operation, destroy the contract and receive the remaining contract balance.
Additionally, by keeping all of the code and functionality related to contract destruction within a single, separate contract (Destroyable), you will improve the modularity and re-usability of your code (other important reasons for using inheritance) and keep your code more organised.
A couple of additional comments âŚ
Always include the pragma statements for each file in your posted solutions. Some students choose to use different Solidity versions, and so itâs always best to confirm which version youâre using by including the pragma statements.
Youâve modified the withdraw function with the onlyOwner modifier, but this means that only the contract owner will be able to withdraw funds while the Bank contract is deployed and operating normally. The contract allows multiple addresses to deposit funds (we are simulating a bank with bank account holders) and so, while the contract is still deployed, it only seems fair that all users should be able to withdraw their funds as well, donât you think? Your withdraw() function header didnât include the onlyOwner modifier before. I think during the course we were adding and removing onlyOwner from various functions to demonstrate different scenarios, and I think it might have ended up being left in the withdraw() function in the solution code for this assignment by mistake!
Just let me know if anything is unclear, if you have any questions, or if you need any help modifying your code for these points.
Hey Filip,
ownable.sol
contract Ownable {
address owner;
modifier onlyOwner {
require(owner == msg.sender);
_;
}
constructor() {
owner = msg.sender;
}
}
destroyable.sol
import "./ownable.sol";
contract Destroyable is Ownable {
function destroy() public onlyOwner {
selfdestruct(payable(owner));
}
}
bank.sol
import "./destroyable.sol";
contract Bank is Destroyable {
//...
Initially I worked out sending by creating close() function in bank.sol:
function close() public onlyOwner {
payable(owner).transfer(address(this).balance);
super.destroy();
}
But then I read a bit more and realized selfdestruct(owner) actually sends all funds to owner, so that is simpler and works good far as I tested.
I have questionâŚsince there is payable âdepositâ function in Bank contract. After contract is destroyed any sending there will not ârevertâ and will take/burn funds sentâŚthatâs sort of understandable butâŚ
âŚwould it be good to make some kind of checks/fallbacks on payables that so we know if thereâs active contract and maybe revert transaction? Or that is not possible, since contract wonât do any code when functions are called.
I understand from our frontend we can disable calls - but people still may interact with contract directly. If you can clarify that part and maybe how thatâs done.
Thanks,
Peg
Hi Jon, thanks for your response, I found it really helpful and I modified the contract according to your suggestions. Everything is a lot clearer now, Iâm looking forward to more feedback on the following assignments.
Thanks again,
Giupi.
Hi @peg ⌠welcome to the forum! I hope youâve been enjoying this course
First of all ⌠your final version is a good solution
Your implementation of a multi-level inheritance structure is the best approach. Bank only needs to explicitly inherit Destroyable, because it will implicitly inherit Ownable via Destroyable.
Yes ⌠As well as destroying the contract, selfdestruct
also automatically transfers the remaining contract balance to the payable address argument it is called with (in our case, the contract ownerâs address). This is all part of the pre-defined selfdestruct
functionality, and is why there is no need to include the extra line of code âŚ
Including your additional close() function in Bank does also achieve the desired result, and from a technical point of view itâs well coded. However, an important reason for using inheritance is to reduce code duplication. By giving the destroy() function in Destroyable public
visibility, it will be inherited and available to be called from both within Bank and externally from Remix. You could also give it external
visibility, which would enable it to be called externally from Remix, but not from within Bank, which for the purposes of this assignment would be enough. This removes the need for the additional close() function in Bank. If you deploy your Bank contract with the additional close() function included, you will notice that both close() and destroy() can be called by the contract owner from Remix. Both of these functions will produce exactly the same result, and so this also demonstrates why close() is unnecessary code duplication.
Additionally, by keeping all of the code and functionality related to contract destruction within a single, separate contract (Destroyable), you will improve the modularity and re-usability of your code (other important reasons for using inheritance). It also keeps your code more organised, and makes it more readable and easier to manage.
By the way, if we did need to call a function inherited from Destroyable from the derived contract Bank, we wouldnât need to use super
(or the Destroyable
contract name) as you have âŚ
You can call the function using just its name: â destroy();
We only need to prepend the function name with super.
or the contract name, e.g. Destroyable.destroy()
, if there are functions with the same name in different contracts within the inheritance structure (polymorphism) and we need to call an overriden function; and super
is only needed when an overriden function cannot be called using the contract name, because its contract is on a different âbranchâ of the same overall inheritance structure. For more detail on this, and a coded example, see the following section of the Solidity documentation (specifically, the bottom part with the 2nd and 3rd code examples) âŚ
https://docs.soliditylang.org/en/latest/contracts.html#inheritance
Well observed!
This is a serious disadvantage of using selfdestruct
, the potential repercussions of which need to be fully understood before choosing to implement it. As a result of this, if a smart contract deployed on the mainnet is ever destroyed, all users need to be notified and informed that they must not send any more funds to that contract address, otherwise (as you have discovered) those funds will be lost.
Obviously, this is hardly an ideal situation!
You are right that this isnât possible, because once selfdestruct() has been triggered, even though function calls can still be made from the web3 client (i.e. the frontend), the smart contract itself has been destroyed and its code removed from the blockchain; from this point the contract state ceases to exist. The zeros that are returned and, more seriously, any Ether that is sent and lost, are the unwanted side-effects of making external calls to a destroyed (and now non-existant) contract.
But, you are thinking along the right lines In practice, other types of security mechanisms, such as pausable or proxy contracts, can be implemented to deal with these kinds of emergency situations.
Instead of destroying the contract, which is permanent and irreversible, pausable contracts are able to provide a range of more nuanced solutions, whereâŚ
The following blog post sets out and explains the reasons why a pausable contract has many advantages over the more âprimitiveâ selfdestruct
âŚ
https://blog.b9lab.com/selfdestruct-is-a-bug-9c312d1bb2a5
OpenZeppelin Contracts (see their GitHub repository, and README
file) provides a smart contract library which includes a âready-madeâ Pausable
contract:
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.3.0/contracts/security/Pausable.sol
The functionality provided by OpenZeppelin in this library can be implemented by inheriting the relevant contracts.
Even though using selfdestruct() has clear disadvantages, for the purposes of this introductory course it provides a simple and straightforward way to practise implementing inheritance, which is our main objective in this assignment. Pausable contracts involve more advanced techniques and are therefore out of the scope of this introductory course. So, donât worry if you donât fully understand everything discussed in the blog post, or all of the information and code in the other links to OpenZeppelin Contracts. You will learn about pausable contracts and other smart-contract emergency and security features, such as proxy contracts, if you go on to take the Ethereum Smart Contract Security course. The 201 course will also introduce more advanced concepts and techniques which will help you to more fully understand and work effectively with this material.
Just let me know if you have any further questions ⌠and keep up the research
Wow Jon thanks a lot for this detailed walkthrough and explanation - especially that âpausedâ openzeppelin contract Iâm gonna definitely look into it - much appreciatedâŚ
Cheers!
Youâre very welcome @peg! ⌠youâre asking some great questions! When youâve got the time, I think youâll find the info in the links really interesting and helpful.
contract Ownable{
address payable public owner;
modifier onlyOwner(){
require(msg.sender == owner, "Not the owner of the contract!");
_; // Run the function
}
constructor(){
owner = msg.sender;
}
}
contract Destroyable is Ownable{
function killMyself() public onlyOwner{
selfdestruct(owner);
}
}
contract Bank is Destroyable{
Hi @tbem,
Your implementation of a multi-level inheritance structure is the best approach. Bank only needs to explicitly inherit Destroyable, because it will implicitly inherit Ownable via Destroyable
Iâm assuming you must have included a pragma
statement at the top of your file, otherwise it wonât compile and Bank canât be deployed. If so, then your solution works, but only if your 3 contracts are all in the same file, because you havenât included any import statements. However, better practice is to place each contract in their own separate file, so that Ownable and Destroyable can be re-used as independent modules to incorporate their specialised contract ownership and contract destruction functionalities. That would also mean adding an import statement to Destroyable.sol to import Ownable.sol, and to Bank.sol to import Destroyable.sol.
Let me know if you have any questions
here is my scram function
pragma solidity ^0.8.0;
import './Owneable.sol';
contract Destroyable is Owneable {
function scram() public onlyOwner {
selfdestruct(payable(owner));
}
}
contract Destroyable is Ownable{
function hakai() public onlyOwner{
selfdestruct(payable(owner));
}
}
Thatâs great @ismael_zamora
As itâs the Bank contract that we want to deploy and the contract owner to then destroy, can we also see how youâve coded your Bank contract for the inheritance?
Hi @North2L,
This is a great start, but can you post your complete solution: the full Destroyable.sol file including pragma and import statements; and the first few lines of Bank.sol up to and including the contract header?
As itâs the Bank contract that we want to deploy and the contract owner to then destroy, we need to see how youâve coded your Bank contract for the inheritance.
If you donât want to post your separate solutions to the other assignments in this course, you could post your full Bank contract for this assignment, and Iâll take a look at how youâve coded that for the other assignment tasks like the withdraw() function and the transfer event
i delete the file to clean my remix sorry
OK, Ismael, not to worry
⌠as long as youâre happy that your Bank contract was compiling and deploying successfully, and that after various different transactions had been performed, the contract owner (only) was able to destroy the contract and have the remaining contract balance sent to their external address.
Ownable file.
Destroyable file
Bank contract
Destroyable.sol:
pragma solidity 0.7.5;
import "Ownable.sol";
contract Destroyable is Ownable {
function close() internal onlyOwner {
selfdestruct(owner);
}
}
Ownable.sol:
pragma solidity 0.7.5;
contract Ownable {
address payable public owner;
modifier onlyOwner {
require(msg.sender == owner);
_;
}
constructor() {
owner = msg.sender;
}
}
Finally I did add a new function to my Bank contract. It just seemed right to have the destroyable contract have itsâ function be internal
Bank.sol:
ragma solidity 0.7.5;
import "./Ownable.sol";
import "./Destroyable.sol";
contract Bank is Ownable, Destroyable {
// lots of other stuff
function destroy() public {
close();
}
}
Looking back I didnât make the destroy function in the Bank contract onlyOwner and that could most likely save some gas for anyone that tried? I just donât feel like it was safe leaving Destroyable contractâs function of close without internal and onlyOwner modifier.
Just curious what would a normal pattern be?