This codelab will walk you through how to create and deploy a smart contract via Remix and how to interact with it from both within Remix or a web-based tool such as MyCrypto. If you'd like more information about what is going on, I would recommend visiting https://github.com/ethereumbook/ethereumbook/blob/develop/02intro.asciidoc or viewing the associated screencast lectures available from class.
MyContract.sol
"For this lab, we will be deploying a simple contract called MyContract that takes in ETH (the cryptocurrency used for Ethereum transactions). Anyone can send ETH to this contract and anyone can destroy it. Upon destruction, all ETH from the contract is sent to its owner (the wallet that has deployed the contract).
pragma solidity 0.4.24;
contract MyContract {
address public owner;
constructor () public payable {
owner = msg.sender;
}
function getBalance() external view returns(uint) {
return address(this).balance;
}
function cashOut() external {
selfdestruct(owner);
}
function () public payable {
}
}
contract
keyword names the contract as MyContractowner
is defined to be of type address
(a 20-byte Ethereum wallet or contract address)owner
variable of the smart contract to msg.sender
(the address of the wallet that deploys the contract). This will eventually be used to ensure the ETH from the contract is returned to the contract creatorgetBalance()
function is declared with keywords external
(function may be called from outside addresses) and view
(function does not modify state). Keyword returns specifies that it will return a uint
(unsigned integer) to represent the balance of the contract.cashOut()
function is declared with keyword external
. It invokes a Solidity built-in function called selfdestruct()
with parameter owner
. This destroys the contract, removing its state, but not its history from the blockchain. It then transfers any funds the contract has to the address specified by owner
(the creator of the contract). Note that address(this)
returns the current contract's address and the balance
attribute which is built-in to Solidity to return the amount of ETH the contract currently has.()
that is both public
(accessible by all) and payable
(can receive ETH). The fallback function is called whenever a user sends a contract ETH directly via their wallet. It is also called when someone tries to call a function in the contract that does not exist. (This will turn into a security issue in one of the SI CTF levels)selfdestruct()
at your own peril (if you like keeping your $)selfdestruct()
call is protected properly.MyContract.sol
and paste it into the file. The Remix IDE (integrated development environment) uses local HTML5 storage to hold your contract code.(For CS students) A contract's ABI is a specification of its binary interface. This allows one to interact with the contract appropriately. The ABI consists of all of the variables in the contract including their types, the functions the contract implements including the parameters and their types. It also specifies any modifiers associated with the variables and functions.
Examine the different parts of MyContract's ABI, by copying it and pasting it into a text document.
owner
. Getters for public variables are automatically implemented by the Solidity compiler. They have no inputs, are not payable, and are view-only {
"constant": true,
"inputs": [],
"name": "owner",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
getBalance()
function that returns the balance of the contract as a uint256
. {
"constant": true,
"inputs": [],
"name": "getBalance",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
cashOut()
function. Note that it is non-payable in that the function can not be used to receive payment when called. (It can and does send payment) {
"constant": false,
"inputs": [],
"name": "cashOut",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"payable": true,
"stateMutability": "payable",
"type": "fallback"
}
On a public blockchain like Ethereum, all transactions are publicly accessible. To view the contract deployment transaction you have just initiated, return to Metamask.
Back in Remix, you can access and interact with the contract, by clicking on the contract under the "Deployed contracts" section. Public getters (blue) and contract functions (orange) allow you to interact with the contract
getBalance()
function and the automatically instantiated getter for the public owner
address can be clicked on to reveal their values. Click on both.We will now send the contract funds via our Metamask wallet, which will be processed by the payable fallback function.
MyContract has an external function call named cashOut()
that can be called by anyone. The function will simply destroy the contract and send the funds that it holds to the owner of the contract. While we could use MyCrypto to directly call the cashOut()
function, to show how contracts can call other contracts, we will create a second contract in Remix to call cashOut()
.
Back in Remix, use the same process for creating MyContract, to create another contract to kill it called "KillMyContract"
pragma solidity 0.4.24;
interface MyContract {
function cashOut() external payable;
}
contract KillMyContract {
address myContractAddr = <YourContractAddressForMyContract>;
// (e.g. address myContractAddr = 0x5D22a10ab401601219dc9d8A3Ffe48B6EC937954;
function kill() external {
MyContract mc = MyContract(myContractAddr);
mc.cashOut();
}
function () public payable {
}
}
Note that this code must be modified to include the contract address it is intended to call. From the previous step in the lab, copy the address of your version of MyContract that has been deployed and replace the value of
in the code with it.
Read the code to find the following in the contract code
interface
to MyContract. This specifies the function name and parameters for any function call in MyContract that is called by KillMyContract. In this case, we only wish to call one function in MyContract i.e. cashOut()
so it is the only function included in the declaration. myContractAddr
that contains the address of your deployed MyContract set above.kill()
for you to callkill()
declares a local memory variable mc
that is declared as a MyContract interface type. It is bound to the address myContractAddr.
kill()
then invokes the cashOut()
call of mc by invoking it via mc.cashOut()
Repeat the above steps from MyContract for KillMyContract
kill()
function by clicking on it and confirming the transaction through Metamask.kill()
, which will invoke cashOut()
on MyContract, which will then cause MyContract to selfdestruct()
, which will then send all of MyContract's ETH back to your wallet.We will now see what shows up on the blockchain for this transaction.
cashOut()
while the top one is the result of MyContract's invocation of selfdestruct()
to send the contract's balance over to the owner wallet. selfdestruct()
call for MyContract. call_0
transaction representing KillMyContract's call to cashOut()
while the second is the suicide_0_0
representing the selfdestruct()
call sending the remaining contract balance to the owner wallet (e.g. your wallet address)You have now funded a contract and received the funds back from it upon its self-destruction. However, these transactions are not free. To keep the network operating, miners and full-nodes must be paid for their operation. Transaction fees are an often-overlooked fact of life on any blockchain and the myriad of blockchains compete to provide similar functionality at a cheaper price while still ensuring security from tampering.
Determine the amount of ETH you have spent from the beginning of this lab.
You've completed the "Hello World" of smart contracts and their deployments on Ethereum. While simple contracts such as the ones shown here might make it seem like smart contracts with Solidity are a good idea, subsequent labs and assignments will show you otherwise. Much like we teach C at Portland State in order to show how easy it is to create security problems with it, we teach Solidity to do the same. If you can avoid using Solidity to implement smart contracts, we would recommend doing so.