Hi @Joey,
First of all, apologies for the delay in giving you some feedback on your solution to this assignment, and in answering your specific question.
No⊠your solution is correct 
Well done for using the error message generated by the complier to resolve the issue with the owner
address not being payable
. But note that it would have referred to the address argument passed to selfdestruct() needing to be payable
, and not the function. The address argument passed to selfdestruct() needs to be a payable address, in order for the contractâs remaining ether balance to be transferred to that address when the contract is destroyed. To receive ether an address must be payable. There are several ways we can solve this:
(1) By doing what you have done, and declaring the owner
state variable in Ownable with a payable address type, instead of with the default, non-payable address type.
(2) If you only need this address to be payable for the purposes of executing selfdestruct(), you can leave the owner
state variable as non-payable, and explicitly convert the address to payable locally, within the function where you actually need to use it as payable. You can even do this directly within the selfdestruct() function call itself, as follows:
function close() public onlyOwner {
selfdestruct(payable(owner));
}
(3) We can also use msg.sender
to reference the contract ownerâs address, because this is the only address that it can ever represent (as a result of the onlyOwner modifier restricting who can call the close function in the first place). Prior to Solidity v.0.8, msg.sender
is payable
by default, and so you wouldnât need to explicitly convert itâŠ
selfdestruct(msg.sender);
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 e.g.
selfdestruct(payable(msg.sender));
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.
And just one other observationâŠ
Normally we always start the names we give to contracts, structs, events and interfaces with a capital letter. Our code will still compile if we donât do this, but it is easier to read and manage code when this and other conventions are applied consistently.
Just let me know if anything is still unclear, or if you have any further questions 