top of page

Bank Solidity Smart Contract for Ethereum Blockchain

Here we will create a bank smart contract using truffle to generate the skeleton of our smart contract project.

Requirements

For this exercise, the root requirements and Node and its pacakage manager npm. To install them follow these intructions: https://www.npmjs.com/get-npm. We will then use the Truffle smart contract development framework to bootstrap the development process and take care of deployment and testing.

To install Truffle, once we have Node and npm:

npm install -g truffle

Smart Contract Development

To bootstrap the development process, we will use Truffle to generate the skeleton of our smart contract project. To do that, create a directory for your project, move there and execute Truffle initialization as folows:

mkdir simple_bank

cd simple_bank

truffle init

​This generates a smart project structure.In our case, it is an empty project without an initial example contract like MetaCoin.sol

Therefore, first we create a new file in the "contracts/" folder called "SimpleBank.sol" with the following content:

pragma solidity ^0.5.8;

contract SimpleBank {
    uint8 private clientCount;
    mapping (address => uint) private balances;
    address public owner;

 

    event LogDepositMade(address indexed accountAddress, uint amount);

​

    constructor() public payable {
        require(msg.value == 30 ether, "30 ether initial funding required");
        owner = msg.sender;
        clientCount = 0;
    }

 

    function enroll() public returns (uint) {
        if (clientCount < 3) {
            clientCount++;
            balances[msg.sender] = 10 ether;
        }
        return balances[msg.sender];
    }

 

    function deposit() public payable returns (uint) {
        balances[msg.sender] += msg.value;
        emit LogDepositMade(msg.sender, msg.value);
        return balances[msg.sender];
    }

 

    function withdraw(uint withdrawAmount) public returns (uint remainingBal) {
 

        if (withdrawAmount <= balances[msg.sender]) {
            balances[msg.sender] -= withdrawAmount;
            msg.sender.transfer(withdrawAmount);
        }
        return balances[msg.sender];
    }

 

    function balance() public view returns (uint) {
        return balances[msg.sender];
    }

 

    function depositsBalance() public view returns (uint) {
        return address(this).balance;
    }
}

Deployment

To instruct Truffle to deploy the previous contract, the following script "2_deploy_contracts.js" should be added to the "migrations/" folder. The deployment should include sending 30 ether (equivalent to 30000000000000000000 wei) to the constructor, which is marked as "payable" to receive this initial balance in the contract.

var SimpleBank = artifacts.require("./SimpleBank.sol");

module.exports = function(deployer) {
  deployer.deploy(SimpleBank, { value: 30000000000000000000 });
};

We will then deploy the contract to a local development network also provided by truffle. The command to start this testing network is:

truffle develop

This will start Truffle Develop at http://127.0.0.1:9545 together with 10 sample accounts.

Then, compile the contracts in a different terminal (truffle develop keeps running the network):

truffle compile

If there are no errors, the contracts can be deployed to the local development network. To configure this network, the file "truffle-config.js" should contain, at least:

module.exports = {
  networks: {
      development: {
      host: "127.0.0.1",
      port: 9545,
      network_id: "*",
    },
  },
}

Then, to trigger the deployment of the smart contract:

rewardsbox.gif

truffle migrate

Testing

The previously deployed contract can be now tested to check if it works as expected. We will also use Truffle for testing.

Tests can be written also in Solidity or using JavaScript. In our case we use JavaScript and the following code is in the file "simpleBank.test.js" in the "test/" folder:

var SimpleBank = artifacts.require("./SimpleBank.sol");

const ether = 10**18; // 1 ether = 1000000000000000000 wei
const reward = 10 * ether;
const initialDepositsBalance = 30 * ether;

contract("SimpleBank - basic initialization", function(accounts) {
  const alice = accounts[1];
  const bob = accounts[2];
  const charlie = accounts[3];
  const dave = accounts[4];

  it("should reward 3 first clients with 10 balance", async () => {
    const bank = await SimpleBank.deployed();

    await bank.enroll({from: alice});
    const aliceBalance = await bank.balance({from: alice});
    assert.equal(aliceBalance, reward, "initial balance is incorrect");

    await bank.enroll({from: bob});
    const bobBalance = await bank.balance({from: bob});
    assert.equal(bobBalance, reward, "initial balance is incorrect");

    await bank.enroll({from: charlie});
    const charlieBalance = await bank.balance({from: charlie});
    assert.equal(charlieBalance, reward, "initial balance is incorrect");

    await bank.enroll({from: dave});
    const daveBalance = await bank.balance({from: dave});
    assert.equal(daveBalance, 0, "initial balance is incorrect");

    const depositsBalance = await bank.depositsBalance();
    assert.equal(depositsBalance, initialDepositsBalance, "initial balance is incorrect");
  });

  it("should deposit correct amount", async () => {
    const bank = await SimpleBank.deployed();
    const deposit = 2 * ether;

    const receipt = await bank.deposit({from: alice, value: web3.utils.toBN(deposit)});

    const balance = await bank.balance({from: alice});
    assert.equal(balance, reward + deposit,
        "deposit amount incorrect, check deposit method");
    const depositsBalance = await bank.depositsBalance();
    assert.equal(depositsBalance, initialDepositsBalance + deposit,
        "bank deposits balance should be increased");

    const expectedEventResult = {accountAddress: alice, amount: deposit};
    assert.equal(receipt.logs[0].args.accountAddress, expectedEventResult.accountAddress,
        "LogDepositMade event accountAddress property not emitted");
    assert.equal(receipt.logs[0].args.amount, expectedEventResult.amount,
        "LogDepositMade event amount property not emitted");
  });
});

contract("SimpleBank - proper withdrawal", function(accounts) {
  const alice = accounts[1];

  it("should withdraw correct amount", async () => {
    const bank = await SimpleBank.deployed();
    const deposit = 5 * ether;

    await bank.deposit({from: alice, value: web3.utils.toBN(deposit)});
    await bank.withdraw(web3.utils.toBN(deposit), {from: alice});

    const balance = await bank.balance({from: alice});
    assert.equal(balance, deposit - deposit, "withdraw amount incorrect");
  });
});

contract("SimpleBank - incorrect withdrawal", function(accounts) {
  const alice = accounts[1];

  it("should keep balance unchanged if withdraw greater than balance", async() => {
    const bank = await SimpleBank.deployed();
    const deposit = 3 * ether;

    await bank.deposit({from: alice, value: web3.utils.toBN(deposit)});
    await bank.withdraw(web3.utils.toBN(deposit + 1*ether), {from: alice});

    const balance = await bank.balance({from: alice});
    assert.equal(balance, deposit, "balance should be kept intact");
  });
});

contract("SimpleBank - fallback works", function(accounts) {
  const alice = accounts[1];

  it("should revert ether sent to this contract through fallback", async() => {
    const bank = await SimpleBank.deployed();
    const deposit = 3 * ether;

    try {
      await bank.send(web3.utils.toBN(deposit), {from: alice});
    } catch(e) {
      assert(e, "Error: VM Exception while processing transaction: revert");
    }

    const depositsBalance = await bank.depositsBalance();
    assert.equal(depositsBalance, initialDepositsBalance, "balance should be kept intact");
  });
});

truffle test

To run the previous test, using Truffle, do:

If everything is fine, this should be the expected output:

Screenshot (60).png
giphy.gif

Interacting with the Contract

To test the contract interactively in a testing environment, without requiring us to spend any ether, we can use the Remix online IDE tool. No need to install anything, just browse to: https://remix.ethereum.org

Just, copy and paste the content of SimpleBank.sol in Remix. There shouldn't be any compilation error so we can switch to the "Deploy" tab to deploy it in a "JavaScript VM" simulated Ethereum blockchain from the first of the available accounts, which is preloaded with 100 ether. Before deploying, we should set the value of the deployment transaction to 30 ether so we provide the required initial funding for rewards, as shown in the screenshot:

Screenshot (62).png

After deployment, we can observe that the amount of ether in the deployment account has been reduced to less that 70 ether, due to the 30 ether transferred to the bank contract plus the transaction gas cost. 

Now, we can start interaction with the bank contract. After selecting the second account, we can enroll it by clicking the "enroll" button in the deployed contract. This will reward the new client with 10 ether, though it will be stored in the contract so the balance of the account should be slightly below the initial 100 ether due to the cost of the enrollment transaction, as shown in the figure:

Screenshot (63).png

You can continue experimenting with Remix and interacting with the contract. For instance getting the balance of all the deposits in the contract, which should be right now 70 ether, or enrolling other accounts.

After some experimentation you might encounter some strange behaviours. For instance, the second account can enroll again and, if all rewards havent been already taken, his balance will reset to 10 ether...

Thats All....

giphy (1).gif
CONTACT ME

Joyston Menezes

computer science engineer

​

Phone:

+91 9480966920

​

Email:

joystonmj7@gmail.com

​

  • Black LinkedIn Icon
  • Black Facebook Icon
  • Black Twitter Icon
  • Black Instagram Icon

Thanks for submitting!

© 2020 All Right reserved developed by Joyston Menezes

bottom of page