๐Ÿ”ฎ Develop your own ether transaction smart contract

๐Ÿ”ฎ Develop your own ether transaction smart contract

ยท

8 min read

Overview

Welcome. I'm here to share my learnings.

Today we are going to develop, deploy a smart contract by understanding the code through breakdowns. It's totally okay if you are beginner. You can start your journey right here ๐Ÿš€.

I'm a soothsayer. I leave my sayings at the end of blog and they are much important


Building Contract EherBank

You proposed a web3 solution for existing banking system and we are working on it.

Let's Develop a Bank contract EtherBank which allows users to deposit and withdraw ether.

As always, first think about to the requirements and functionalities to be implemented and outline them

Outline

Our store must be capable of tracking balances
โ””โ”€โ”€โ”€ To store the amount of ether with a particular person (wallet) we can use 
    - mapping(address => uint256)

Functions
โ””โ”€โ”€โ”€ deposit
|   - retrieve the account balance of caller address
|   - add the funds to the account balance
|   - update the account balance with new balance
โ””โ”€โ”€โ”€ withdraw
|   - check if account has sufficient funds to withdraw
|   - Initiate the transaction
|   - update the account balance to 0 on successful transaction.

Code

1  pragma solidity 0.8.11;
2
3  import "@openzeppelin/contracts/utils/Address.sol";
4  import "hardhat/console.sol";
5
6  contract EtherBank {
7     using Address for address payable;
8
9     mapping(address => uint) public balances;
10
11    function deposit() external payable {
12       balances[msg.sender] += msg.value;
13    }
14
15    function withdraw() external {
16        uint256 balance = balances[msg.sender];
17        require(balance > 0, "Withdrawl amount exceeds available balance.");
18        
19       console.log("");
20        console.log("EtherBank balance: ", address(this).balance);
21        console.log("Attacker balance: ", balance);
22        console.log("");
23
24        payable(msg.sender).sendValue(balance);
25        balances[msg.sender] = 0;
26    }
27
28    function getBalance() external view returns (uint) {
29        return address(this).balance;
30    }
31 }

Breakdown

Compiler version

pragma solidity 0.8.11;

Import statements

import "@openzeppelin/contracts/utils/Address.sol";
import "hardhat/console.sol";

OpenZeppelin Contracts helps you minimize risk by using battle-tested libraries of smart contracts for Ethereum and other blockchains.

Libraries A library in Solidity is a different type of smart contract that contains reusable code

A library Address.sol is used to use secure standard functions related to the address type. more

Hardhat is an Ethereum development environment. We can get solidity stack traces, logging features and much more.

We are able to use console.log by importing hardhat's console.sol library, so we can log to console output.

Contract and state variables

A contract named EtherBank is defined with a state variable that maps a user address to their balances.

contract EtherBank {
     ...
     mapping(address => uint) public balances;
     ...
}

Mapping acts like a hash table or dictionary in any other language. These are used to store the data in the form of key-value pairs

AccountBalance
15 eth
28 eth

Payable addresses and functions

using Address for address payable;

It specifies payable addresses can now use all the functions provided by Address.sol library.

address vs address payable

You can use .transfer(..) and .send(..) on address payable, but not on address.

You can use a low-level .call(..) on both address and address payable, even if you attach value.

Prerequisites (functionalities)

Global Variables

The msg global variables in particular are special global variables that contain properties which allow access to the blockchain. msg.sender is always the address where the current (external) function call came from msg.value contains the amount of wei (ether / 1e18) sent in the transaction

Payable functions

In solidity a function that can send and receive Ether is given with payable modifier

External functions

External functions are part of the contract interface. They can be called from other contracts and via transactions. An external function f() cannot be called internally

1. Deposit

    function deposit() external payable {
        balances[msg.sender] += msg.value;
    }

The code is self explanatory.
It need to be a payable function as it receiving ether.
msg.sender is the one who invokes deposit function.
msg.value is the amount provided to deposit by the user.
And we added new funds to the old and maintaining the track of it in balances

2. Withdraw

    function withdraw() external {
        uint256 balance = balances[msg.sender];
        require(balance > 0, "Withdraw amount exceeds available balance.")

        payable(msg.sender).sendValue(balance);
        balances[msg.sender] = 0;    }

Require statement

To withdraw amount one should have balance > 0.
The check can be implemented using require() statements in solidity.
If condition met, it permits for next instruction else it reverts all the changes to initial stage.

Type conversion

msg.sender is of type address. To use transfer/send we should convert it into address payable. It is done by payable(address).

.sendValue() comes from the library Address.sol that we imported from OpenZeppelin utils

Transfer eth to owner

The sendValue() function when executed invokes the external receive()/fallback() function that was deployed in users contract. (read further)

We transfered the amount to account user. So we can then set his balance to 0 in our transaction state.

3. GetBalance

    function getBalance() external view returns (uint) {
        return address(this).balance;
    }

This is a utility function to get the total amount holed by the contract. this refers to the contract address and .balance returns the amount of eth in the address given.

Deploy

Let's deploy it, so that any user can interact with our EthBank contract.

Deploying contract states that the contract was mined by miners and verified by validators and the contract settles as one of the transaction in a block of transactions.

I use remix ide

Upon deployment it provides us contract address 0xd9145CCE52D386f254917e481eB44e9943F39138. I've fed eth to my contract with 3 users and in total 12 ether

image.png


Building User Contract

User is capable of depositing and withdrawing eth.

Prerequisites

Fallback function

Every Ethereum smart contract byte code contains the so-called default fallback function which has the following default implementation.

contract EveryContract {
    function () public {}
}

This default fallback function can contain arbitrary code if the developer overrides the default implementation. If it is overridden as payable, the smart contract can accept ether. The function is executed whenever ether is transferred to the contract

Transferring ether

Aside from calling payable methods, Solidity supports three ways of transferring ether between wallets and smart contracts.

These supported methods of transferring ether are send(), transfer() and call.value().

The methods differ by how much gas they pass to the transfer for executing other methods (in case the recipient is a smart contract), and by how they handle exceptions.

address.send()address.transfer()address.call.value()
Adjustable gasnonoyes
Gas limit23002300all/settable
Behaviout on errorreturn falseThrow exceptionreturn false

In short, whenever transferring methods are executed the external fallback functions are executed behind. fallback functions can be overridden.

Code

1  // SPDX-License-Identifier: MIT
2  pragma solidity 0.8.17;  
3  import "hardhat/console.sol";
4  import "./EtherBank.sol";
5
6  contract User {
7      EtherBank public immutable etherBank;
8
9      constructor(address etherBankAddress) {
10        etherBank = EtherBank(etherBankAddress);
11    }
12
13    function transact() external payable {
14        etherBank.deposit{value: msg.value}();
15        etherBank.withdraw();
16    }
17
18    fallback() external payable {}
19
20    receive() external payable {
21       console.log("received payment");
27    }
28
29    function getBalance() external view returns (uint) {
30        return address(this).balance;
31    }
32 }

Breakdown:

Using EtherBank in another contract

import "./EtherBank.sol";

As we have developed the code locally we can import it like any other solidity file. (we can also use interfaces to achieve this)

User Contract

contract User {
    EtherBank public immutable etherBank;

    constructor(address etherBankAddress) {
        etherBank = EtherBank(etherBankAddress);
    }
    ...
}

Contract user is defined with etherBank state variable (similar to class variable) which stores address of EtherBank deployed contract and acts as an instance so the functionalities can be used using it (similar to objects in any other oop language).

Constructor is a special function that was called only once (i.e., called during deployment) and sets the etherBank as an instance of EtherBank contract. We pass the contract address to the constructor as parameter. In my case it is 0xd9145CCE52D386f254917e481eB44e9943F39138

Now we can use etherBank to call any of the EtherBank contract functions.

Transaction logic

It shows how we deposit and withdraw eth.

    function transact() external payable {
        etherBank.deposit{value: msg.value}();
        etherBank.withdraw();
    }

etherBank can call deposit and withdraw functions.

deposit must be provided with some value we sent it through .deposit{value: msg.value}(); it updates our balance with given amount.

withdraw when called executes the instructions and when payable(msg.sender).sendValue(balance); triggers it invokes the fallback/receive function.

fallback/receive functions

.sendValue(balance) invokes receive function

    fallback() external payable {}

    receive() external payable {
        console.log("received eth");
    }

we can confirm withdrawal by console output "received eth"

Deploy:

Observe the image. When I choose transact with 2 ether as deposit value it added to the EtherBank and then the withdrawal function executed which invokes receive() (confirm it by console output "received eth")

image.png

Congratulations ๐Ÿ˜Œโœจ. You have developed a Ethereum Transaction organization. You became most popular with 10million eth balance.

Always better together๐Ÿฅน๐Ÿ‘‰ twitter

"Believe in yourself. You are enough" - caroline ghosn


Saying ๐Ÿ‘‡

๐Ÿ˜ฒ Flash news: A famous Ethereum Transaction organization lost 3.6m eth

ย