No you donât need to. Your implementation of a multi-level inheritance structure is the best approach, because it is more streamlined. Bank only needs to explicitly inherit Destroyable, because it will implicitly inherit Ownable via Destroyable.
It will still work in exactly the same way if Bank explicitly inherits both Destroyable and Ownable, and if both destroyable.sol and ownable.sol are imported, but itâs unnecessary.
There are a few issues here, but they are easily fixed:
(1) The following two lines of code in Destroyable.sol contain misspelt keywords. These generate red compiler errors, but maybe these are just copy-and-paste errors and arenât in your code in Remix âŚ
(2) Bank canât be deployed because of the following compiling issue caused by Destroyable.sol âŚ
Imported files must have a compiler declaration that is compatible i.e. both files must be able to be compiled using the same version of Solidity.
Your Ownable.sol can only be compiled using v0.7.5. It cannot be imported into your Destroyable.sol because your Destroyable.sol requires a compiler version of v0.7.6 or above âŚ
Your Destroyable.sol also cannot be imported into your Bank.sol for the opposite reason: your Bank.sol can only be compiled using v0.7.5.
Remix should have generated red compiler errors for this. These errors wonât have highlighted a specific line of code, and so the error messages canât be viewed by hovering over the red indicator highlighting the line number. However, they will be displayed in the Solidity Compiler panel â the Solidity Compiler icon indicates this with the number of error messages highlighted in red.
(3) Once the above issues are fixed, and your code compiles successfuly, youâll be able to deploy your Bank contract. However, youâll notice that the destroy() function is not available for the owner to call. Your selfdestruct functionality is coded correctly, but itâs not inherited by Bank. Can you correct this? When you have, you will be able to deploy Bank, perform some transactions, and then get the contract owner to destroy the Bank contract (by calling the destroy function from Remix). Destroying the contract will also transfer any remaining ether balance to the ownerâs external address.
Here, a concise alternative is to call selfdestruct() with msg.sender directly, instead of using the additional receiver variable.
selfdestruct(msg.sender);
Have a look at this post for further details about the use of the additional local variable in the model solution.
Let me know if anything is unclear, or if you have any questions about how to correct your code for these issues
Your implementation of a multi-level inheritance structure is the best approach, because it is more streamlined. Bank only needs to explicitly inherit Destroyable, because it will implicitly inherit Ownable via Destroyable.
Just one commentâŚ
This works, but you are using Solidity v0.7.5, and prior to Solidity v0.8âmsg.senderâis already a payable address by default. This means that whenever you want to use msg.sender as a payable address (such as here, as the argument selfdestruct is called with), you donât have to explicitly convert it e.g.
selfdestruct(msg.sender);
However, from v.0.8âmsg.senderâis non-payable by default, and so whenever you are using v.0.8 and need to use msg.sender as a payable address, this requires the explicit conversion that you have used.
Let me know if you have any questions.
By the way, you seem to have missed out the Transfer Assignment â is that on purpose because it was too easy?
// Self destruct contract (owner only)
import "./ownable.sol";
contract Destroyable is Ownable{
// This destroys the contract forever
function closeContract() public onlyOwner {
payable(owner).transfer(address(this).balance);
assert(address(this).balance==0);
selfdestruct(payable(owner));
}
}
Third file: bank.sol
pragma solidity ^0.8.7;
import "./destroyable.sol";
contract Bank is Destroyable{
// state variables
mapping (address=>uint) balance;
// events
event depositDone(uint indexed amount, address indexed depositedTo); //deposit
event withdrawDone(uint amount, address withdrawnFrom); //withdraw
// functions
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 returns (uint){
require(amount<=balance[msg.sender], "The amount requested to withdraw exceeds the available balance");
uint balanceBefore = balance[msg.sender]; //store original balance for later safety checks
balance[msg.sender]-=amount; //update balance, BEFORE actually transfering ETH in order to avoid attacks
payable(msg.sender).transfer(amount); //transfer ether
assert(balanceBefore - amount == getBalance()); // ensure that the actual balance corresponds to the operation performed
emit withdrawDone(amount, msg.sender); //withdraw event logging
return balance[msg.sender]; //updateD balance;
}
function getBalance() public view returns (uint){
return balance[msg.sender];
}
}
The solution seems to work⌠HOWEVER
While deploying and testing the smart contract I noticed that, after calling selfDestruct, all of the functions (apart from becoming unusable as desired) accept to be sent any amount of ETH without throwing an error.
Obviously, I want to prevent users to mistakenly send ETH to a contract that has been previously destroyed, as they will never be able get them back.
Since I realized that selfDestruct essentially overrides everything with zeroes, I attempted to amend the contracts by adding:
a function that checks if the contract has been destroyed (by checking if the owner variable is equal to zero);
a modifier that require the contract to be non-destroyed in order to run the functions;
such modifier is applied to all of the functions of the Bank contract.
Here is the amended code:
First file: ownable.sol - (UNCHANGED)
Second file: destroyable.sol
// Self destruct contract (owner only)
import "./ownable.sol";
contract Destroyable is Ownable{
// This destroys the contract forever
function closeContract() public onlyOwner {
payable(owner).transfer(address(this).balance);
assert(address(this).balance==0);
selfdestruct(payable(owner));
}
// This checks if the contract has been destroyed
function isActive() view internal returns(bool){
bool result = (owner != address(0));
return result;
}
// This prevents functions to be executed on previously destroyed contracts
modifier onlyActive {
require(isActive() == true, "The contract has been deactivated, this operation will not be performed");
_;
}
}
Third file: bank.sol
pragma solidity ^0.8.7;
import "./destroyable.sol";
contract Bank is Destroyable{
// state variables
mapping (address=>uint) balance;
// events
event depositDone(uint indexed amount, address indexed depositedTo); //deposit
event withdrawDone(uint amount, address withdrawnFrom); //withdraw
// functions
function deposit() public payable onlyActive returns (uint){
balance[msg.sender] += msg.value;
emit depositDone(msg.value, msg.sender);
return balance[msg.sender];
}
function withdraw(uint amount) public onlyActive returns (uint){
require(amount<=balance[msg.sender], "The amount requested to withdraw exceeds the available balance");
uint balanceBefore = balance[msg.sender]; //store original balance for later safety checks
balance[msg.sender]-=amount; //update balance, BEFORE actually transfering ETH in order to avoid attacks
payable(msg.sender).transfer(amount); //transfer ether
assert(balanceBefore - amount == getBalance()); // ensure that the actual balance corresponds to the operation performed
emit withdrawDone(amount, msg.sender); //withdraw event logging
return balance[msg.sender]; //updateD balance;
}
function getBalance() public view onlyActive returns (uint){
return balance[msg.sender];
}
}
Yet, even after this change, the unwanted behavior persists!
Is there any way to prevent functions from destroyed contracts to accept ETH? Does my code have any mistake? If not, what is the best workaround?
Assuming that the issue might depend on the nature of the selfDestruct command itself, I attempted another workaround to get a similar functionality without using it at all.
In this case, I am adding a global state variable named âdestroyedâ (false by default) to check via a modifier if the functions are allowed to run: instead of using selfDestruct, when I want to disable the contract I simply override the value of âdestroyedâ to true which prevents all functions to be used from that time on.
// Self destruct contract (owner only)
import "./ownable.sol";
contract Destroyable is Ownable{
// This destroys the contract forever
/*function closeContract() public onlyOwner {
selfdestruct(payable(owner));
}
*/
function closeContract() public onlyOwner isActive {
payable(owner).transfer(address(this).balance);
assert(address(this).balance==0);
destroyed=true;
}
modifier isActive {
require(destroyed==false, "This contract has been destroyed and is no longer able to run");
_;
}
}
Third file: bank.sol
pragma solidity ^0.8.7;
import "./destroyable.sol";
contract Bank is Destroyable{
// state variables
mapping (address=>uint) balance;
// events
event depositDone(uint indexed amount, address indexed depositedTo); //deposit
event withdrawDone(uint amount, address withdrawnFrom); //withdraw
// functions
function deposit() public payable isActive returns (uint){
balance[msg.sender] += msg.value;
emit depositDone(msg.value, msg.sender);
return balance[msg.sender];
}
function withdraw(uint amount) public isActive returns (uint){
require(amount<=balance[msg.sender], "The amount requested to withdraw exceeds the available balance");
uint balanceBefore = balance[msg.sender]; //store original balance for later safety checks
balance[msg.sender]-=amount; //update balance, BEFORE actually transfering ETH in order to avoid attacks
payable(msg.sender).transfer(amount); //transfer ether
assert(balanceBefore - amount == getBalance()); // ensure that the actual balance corresponds to the operation performed
emit withdrawDone(amount, msg.sender); //withdraw event logging
return balance[msg.sender]; //updateD balance;
}
function getBalance() public view isActive returns (uint){
return balance[msg.sender];
}
}
Everything seems to work exactly as I expected. In addition, this also allows choosing the individual functions that are meant to become unusable (as, I guess, one might leave some view functions still accessible for some reasons), instead of destroying the full functionality of the smart contract.
Is there any reason NOT to use a global state variable to disable the contract as I just did?
(the only reason that comes to my mind is that the owner might still have the ability to re-activate the contract, but this would be solved by making the closeContract() function also permanently override the owner address with the âblackholeâ ethereum address 0x0000000000000000000000000000000000000000, so that nobody will ever be able to regain control of the smart contract⌠is that right?)
function deposit() public payable returns (uint) {
balance[msg.sender] += msg.value;
emit depositDone(msg.value, msg.sender);
return balance[msg.sender];
}
function getBalance() public view returns(uint){
return balance[msg.sender];
}
function withdraw(uint amount) public returns (uint) { //msg.sender is an address
//address payable toSend = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4 //toSend.sender.transfer d.h. msg.sender entspricht address payable
require(balance[msg.sender] >= amount);
balance[msg.sender] -= amount;
msg.sender.transfer(amount);
return balance[msg.sender];
}
function transfer(address recipient, uint amount) public {
require(balance[msg.sender] >= amount, âBalance not sufficientâ);
require(msg.sender != recipient, âDont transfer money to yourselfâ);
Your first solution is correct for an implementation of selfdestruct() using inheritance, except that there is no need to add the following two lines of code to your closeContract function in Destroyable âŚ
Your code works with these two additional lines, and from a technical point of view they are well coded. However, as well as destroying the contract, selfdestruct() automatically transfers the remaining contract balance to the address argument it is called with. This is all part of the pre-defined selfdestruct() functionality. If you remove these two lines of code, you will see that calling the closeContract() function produces exactly the same results.
Your implementation of a multi-level inheritance structure is the best approach, because it is more streamlined. Bank only needs to explicitly inherit Destroyable, because it will implicitly inherit Ownable via Destroyable
Using selfdestruct() brings with it certain clear disadvantages, the potential repercussions of which need to be fully understood before choosing to implement it. As you have correctly identified, after selfdestruct() has been executed, successful calls can still be made to the destroyed contractâs functions! 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, and as you have also discovered, those funds will be lost.
Yes ⌠the additional code youâve added will have no effect whatsoever because, even though function calls can still be made, the contract itself has actually been destroyed and removed from the blockchain. Calling selfdestruct() doesnât somehow overwrite all of the values stored in the contract state with zero values, because, from that point, the contract state ceases to exist. The zeros that are returned and, more seriously, any ether that is sent and lost, are just unwanted side-effects of making external calls to a destroyed (and now non-existant) contract.
Having said that, however, your experimentation has led you to a very interesting and well thought-out âworkaroundâ. Your 3rd solution actually ends up implementing something similar to what is called the pausable pattern. The following blog post sets out and explains the reasons why a pausable contract has many advantages over the more âprimitiveâ selfdestruct
Your variation of the pausable pattern results in a more extreme, permanent and irreversible âpauseâ, whereas, more generally, pausable contracts are able to provide a range of more nuanced solutions, whereâŚ
an authorised address can both pause and unpause a contract; and
by using two different modifiers, specific functions can be activated or deactivated depending on whether the contract is currently paused or unpaused.
In your concluding comments you do show that you have already started to think along these lines âŚ
OpenZeppelin Contracts (see their GitHub repository, and README file) provides a smart contract library which includes a âready-madeâ Pausable contract:
âŚsetting it to false on deployment with a constructor âŚ
⌠and enabling it to be set to true by an authorised address via a separate function.
However âŚ
Even though when destroyed is set to true your contract will effectively become unusable, it will remain deployed. So, I think a more suitable term than destroyed would (i) make a better name for the state variable, and (ii) be more suitable for the isActive modifierâs error message;
In order to keep the contract ownership and contract destruction/deactivation functionalities within separate contracts, it would be better to put the bool state variable in Destroyable, together with a separate constructor. This keeps the code more modular and reusable.
The contract owner address isnât able to re-activate your contract after calling closeContract(). For that to be possible, you would need to add an addtional function which would remain active after the bool state variable has been set to true e.g.
function reactivate() public onlyOwner {
require(destroyed, "The contract is already active");
destroyed = false;
}
This is correct, and would work if the owner was otherwise able to re-activate the contract; but as Iâve explained above, this is impossible with your current 3rd solution. In addition, as discussed in the blog post, and mentioned above, there are good reasons why we may not want to prevent re-activation of the contract anyway.
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, in the Smart Contract Security course, which I highly recommend you take either after this one, or after the Ethereum Smart Contract Programming 201 course. The 201 course will also introduce more advanced concepts and techniques which will also help you to more fully understand and work effectively with this material.
As always, just let me know if you have any further questions.
Youâre making great progress! Keep up the great work
Your implementation of a multi-level inheritance structure is the best approach, because it is more streamlined. Bank only needs to explicitly inherit Destroyable, because it will implicitly inherit Ownable via Destroyable.
Your inheritance structure makes the selfdestruct() functionality in Destroyable available within Bank when Bank is deployed. When close() is called, the Bank contract will be successfully destroyed and any remaining ether in the contract will be transferred to the contract ownerâs external address.
However, even though any remaining ether in the contract will only be transferred to the contract owner, at the moment, anyone can call close(), trigger selfdestruct(), destroy the contract, and trigger this transfer in the first place. The payable address passed to selfdestruct() determines where the remaining funds will be transferred to, but it doesnât restrict who can trigger selfdestruct().
So, to the extent that the contract owner can be relied upon to be a trustworthy custodian, the remaining funds are secure; but the fact that anyone can destroy the contract is still a huge problem. So thatâs why, as well as ensuring that the address passed to selfdestruct() is a secure destination (from where the remaining funds can be appropriately managed), we also need to restrict who can trigger selfdestruct(). Part of the assignment is to ensure that only the contract owner can destroy the contract. We already have an onlyOwner modifier in Ownable which is inherited by Destroyable, so you can just apply onlyOwner to close() without having to define the modifier again in Destroyable.
Please, format your code before posting it. This will make it clearer and easier to read. It will also make it easier to copy and paste if we need to deploy and test it. Follow the instructions in this FAQ:How to post code in the forum
Hi Jon, thanks for your feedback. I will check my code to restrict the selfdestruct. Also thanks for the input about formatting the code here in the forum, did not know.
Have a great day!
Ok, this took me a while. I had to rewrite the logic for the Bank, so it makes more sense.
So now when the Bank owner closes the bank each Customer receives back the money they deposit. So this way itâs not a scam Bank
I used mapping(uint => struct) customers that helped me with debugging. But on the other hand it forced looping through. Also I changed to 0.8.9 compiler, because I had some errors on 0.7.5 I couldnât resolve.
I am so glad to have finally arrived at my final answer, thank God I am really getting this, I am sorry for the deleted replies. Please verify if my code is true, I really hope to fully understand programming and to also write on my own in the future and to also make a living out of it.
So hereâs the code:
Bank Contract:
pragma solidity 0.7.5;
import "./own.sol";
import "./Destroyable.sol";
contract Bank is Own, Destroyable
Own Contract: (changed it to own, cause thereâs already an ownable JSON so I think its auto filling it)
Your implementation of a multi-level inheritance structure is the best approach, because it is more streamlined. Bank only needs to explicitly inherit Destructible because, as you say, it will implicitly inherit Ownable via DestructibleâŚ
You have a couple of issues here, which are preventing your code from working as it shouldâŚ
(1)
Your constructor in Ownable needs to assignmsg.sender to the owner state variable, not compare them as it does here âŚ
At the moment, no one can call selfdestruct(), because the owner state variable hasnât been assigned an address. So calling close() will always trigger revert and throw an error.
(2)
Prior to Solidity v0.8 msg.sender is already a payable address by default. This means that whenever you want to use msg.sender as a payable address you donât have to explicitly convert it. For example, in Ownable, your owner state variable is declared with a payable address type. With Solidity v0.7 the constructor can assign msg.sender to owner without having to explicity convert it.
However, from v.0.8 msg.sender is non-payable by default, and so whenever you are using v.0.8 and need to use msg.sender as a payable address, this requires an explicit conversion âŚ
Once you have corrected the operator in the constructor, you will get a red compiler error for this second issue.
What similar change do you have to make in your Bank contract when you use v0.8 instead of v0.7 ? (Hint: you need to make an additional modification to your solution to the Transfer Assignment)
Everything else is well coded. Your implementation of a multi-level inheritance structure is the best approach, because it is more streamlined. Bank only needs to explicitly inherit Destroyable because it will implicitly inherit Ownable via Destroyable
Let me know if anything is unclear, or if you have any questions
We can also streamline the inheritance structure by having Bank explicitly inherit Destroyable only, because it will inherit Ownable implicitly via Destroyable. This will give you a multi-level inheritance structure, as follows:
own.sol
contract Own { ... }
Destroyable.sol
import "./own.sol";
contract Destroyable is Own { ... }
Bank.sol
import "./Destroyable.sol";
contract Bank is Destroyable { ... }