Calling a Smart Contract | Developers

Calling a Smart Contract

Estimated reading time: More than 10 minutes

Introduction

In this section, we will guide you through the creation of an application that will call a smart contract to read and write.

Prerequisites

Before starting, make sure you have gone through the prerequisites.

Send Ether token to your Ledger Nano ethereum account

To send some ethers on the Ropsten network, go to one of the ropsten faucet websites:

The Ropsten network is not visible on Ledger Live, you can then check the transaction passed on ropsten.etherscan.io.

If the Ropsten and Dimension faucets don’t work or the queue is too long, please use another faucet of your choice to receive testnet Ether.

Ropsten Ethereum Network

Go to the Ropsten Ethereum Faucet website put your Wallet Public Key on the input and click on “Send me test Ether”

Ropsten Ethereum Faucet
Fig. 1: Ropsten Ethereum Faucet

Dimensions Network

Go to the Dimensions Network website put your Wallet Public Key on the input, do the captcha and click on “Send me test Ether”

Ropsten Ethereum Faucet
Fig. 2: Ropsten Ethereum Faucet

Web App Bluetooth (only Nano X)

The Ledger Nano S and S Plus do not have the Bluetooth functionality. This tutorial will only work with a Ledger Nano X.

Please be aware that the Bluetooth implementation is only supported by a few browsers. You can check the browser support for the Web Bluetooth transport.

Project Initialization

The app is build with React, which is a frontend Javascript framework.

First, open a terminal and create a new project. For this tutorial the project will be named “e2e-tutorial-contract”. Run:

npx create-react-app e2e-tutorial-contract
cd e2e-tutorial-contract

Open the folder in an editor. The React app initialization creates a “src” folder where you will find all the code.

Run:

touch ./src/ConnectLedger.js
touch ./src/SmartContract.js
touch ./src/ethereum.js
touch ./src/polyfill.js

Your folder must look like this.

Folder of the Application
Fig. 1: Folder of the Application

To implement the Ledger connexion you will only modify “App.js”, “index.js”, “ConnectLedger.js”,”SmartContract.js”, and ethereum.js”

Code Implementation

App.js

In App.js copy-paste the following code:

import React, { useState } from 'react';
import ConnectLedger from './ConnectLedger.js';
import SmartContract from './SmartContract.js';

function App() {
  const [transport, setTransport] = useState(undefined);
  const [eth, setEth] = useState(undefined);
  const [address, setAddress] = useState(undefined);

  const saveInfo = (info) => {
    setAddress(info.address);
    setEth(info.eth);
    setTransport(info.transport);
  }

  return (
    <div className='container'>
      {
      !transport ?
      <ConnectLedger onTransport={(info) => saveInfo(info)}></ConnectLedger> :
      <SmartContract address={address} eth={eth}></SmartContract>
      }
    </div>
  );
}

export default App;

polyfill.js

In App.js copy-paste the following code:

global.Buffer = require("buffer").Buffer;

index.js

in the index.js add these line

import './polyfill'
import 'bootstrap/dist/css/bootstrap.min.css';

ConnectLedger.js

In ConnectLedger.js, copy-paste the following code:

import React from 'react';

import TransportWebBLE from "@ledgerhq/hw-transport-web-ble";
import Eth from "@ledgerhq/hw-app-eth";


function ConnectLedger({onTransport}) {

  const connectLedger = async() => {
    const transport = await TransportWebBLE.create();
    const eth = new Eth(transport);
    const {address} = await eth.getAddress("44'/60'/0'/0/0", false);
    onTransport({address,eth,transport})
  }


  return (
    <div className='container'>
      <div className='row'>
        <div className='col-sm-4 mt-5 mx-auto'>
          <button onClick={connectLedger}>Connect Your Ledger</button>
        </div>
      </div>
    </div>
  );
}

export default ConnectLedger;

SmartContract.js

In “SmartContract.js”, copy-paste the following code:

import React, { useState } from 'react';
import getBlockchain from './ethereum.js';
import { ethers } from 'ethers';

function SmartContract({eth,address}) {
  const [simpleStorage, setSimpleStorage] = useState(undefined);
  const [data, setData] = useState(undefined);
  const [provider, setProvider] = useState(undefined);
  const [url, setUrl] = useState(undefined);

  const smartContractRead = async() => {
    const provider = new ethers.providers.JsonRpcProvider('https://ropsten.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161');
    const { simpleStorage } = await getBlockchain(provider);
    console.log(simpleStorage);
    const data = await simpleStorage.readData();
    setProvider(provider);
    setSimpleStorage(simpleStorage);
    setData(data);
  };

  const updateData = async e => {
    e.preventDefault();
    const dataInput = e.target.elements[0].value;
    console.log(simpleStorage);
    const { data } = await simpleStorage.populateTransaction['updateData(uint256)'](dataInput);

    const unsignedTx = {
      to: simpleStorage.address,
      gasPrice: (await provider.getGasPrice())._hex,
      gasLimit: ethers.utils.hexlify(100000),
      nonce: await provider.getTransactionCount(address, "latest"),
      chainId: 3,
      data: data,
    }
    console.log(unsignedTx);
    const serializedTx = ethers.utils.serializeTransaction(unsignedTx).slice(2);

    console.log(serializedTx);
    const signature = await eth.signTransaction(
      "44'/60'/0'/0/0",
      serializedTx
    );

    console.log(signature);
    //Parse the signature
    signature.r = "0x"+signature.r;
    signature.s = "0x"+signature.s;
    signature.v = parseInt("0x"+signature.v);
    signature.from = address;
    console.log(signature);

    //Serialize the same transaction as before, but adding the signature on it
    const signedTx = ethers.utils.serializeTransaction(unsignedTx, signature);
    console.log(signedTx);

    const hash = (await provider.sendTransaction(signedTx)).hash;
    console.log(hash);
    setUrl("https://ropsten.etherscan.io/tx/" + hash);
  };


  return (
    <div className='container'>
      <div className='row'>
        <div className='col-sm-4'>
          <h2>Data:</h2>
          <p>{data ? data.toString() : "..." }</p>
          <button onClick={() => smartContractRead()}>Get Data</button>
        </div>

        <div className='col-sm-4'>
          <h2>Change data</h2>
          <form className="form-inline" onSubmit={e => updateData(e)}>
            <input 
              type="text" 
              className="form-control" 
              placeholder="data"
            />
            <button 
              type="submit" 
              className="btn btn-primary"
            >
              Submit
            </button>
          </form>
        </div>
        <div className="mt-5 mx-auto d-flex flex-column">
          <p>
            Ropsten Etherscan :
          </p>
          <p><a href={url} target="_blank" rel="noreferrer">{url}</a></p>
        </div>
      </div>
    </div>
  );
}

export default SmartContract;

ethereum.js

In “ethereum.js”, copy-paste the following code:

import { Contract } from 'ethers';

const getBlockchain = (provider) =>
  new Promise( async (resolve, reject) => {
    if(provider) {
      const simpleStorage = new Contract(
        "0x989c810f64ac577683d49a318adfd98b8d482472",
        [
          {
            "inputs": [],
            "name": "data",
            "outputs": [
              {
                "internalType": "uint256",
                "name": "",
                "type": "uint256"
              }
            ],
            "stateMutability": "view",
            "type": "function"
          },
          {
            "inputs": [],
            "name": "readData",
            "outputs": [
              {
                "internalType": "uint256",
                "name": "",
                "type": "uint256"
              }
            ],
            "stateMutability": "view",
            "type": "function"
          },
          {
            "inputs": [
              {
                "internalType": "uint256",
                "name": "_data",
                "type": "uint256"
              }
            ],
            "name": "updateData",
            "outputs": [],
            "stateMutability": "nonpayable",
            "type": "function"
          }
        ],
        provider
      );
      resolve({simpleStorage});
      return;
    }
    reject('Provider not recognized');
  });

export default getBlockchain;

Dependencies Installation

Run:

npm install --save bootstrap
npm install --save ethers
npm install --save @ledgerhq/hw-app-eth
npm install --save @ledgerhq/hw-transport-web-ble
npm install --save buffer
Package What does it do?
bootstrap It allows you to use the Bootstrap CSS framework.
ethers It provides you with all the methods to interact with the Ethereum blockchain
@ledgerhq/hw-app-eth It will help you ask your Nano to access the ethereum address.
@ledgerhq/hw-transport-web-ble It provides you with all the methods to interact with your Ledger Nano X with a Bluetooth connexion.
buffer The goal is to provide an API that is 100% identical to node's Buffer API.

Package.json Dependencies

Now that the dependencies are installed you can find them in the “package.js”. This is how your “package.json” shoud look like:

{
  "name": "e2e-tutorial",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@ledgerhq/hw-app-eth": "^6.26.0",
    "@ledgerhq/hw-transport-web-ble": "^6.24.0",
    "@testing-library/jest-dom": "^5.16.2",
    "@testing-library/react": "^12.1.3",
    "@testing-library/user-event": "^13.5.0",
    "bootstrap": "^5.1.3",
    "buffer": "^6.0.3",
    "ethers": "^5.5.4",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-scripts": "5.0.0",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}


Web App Test

Start the Development Server

Run:

npm run start
Warning
All the browser do not support the Bluetooth please look at the browser support .

Now the application is up and running. Open the browser and go to localhost:3000, it will display :

Application Running on Browser
Fig. 2: Application Running on Browser

Don’t click on the button yet.

Launch Ethereum App

Before clicking on the button, unlock your Nano X and run the Ethereum application. The steps are described below.

Ledger Enter Code Pin
Fig. 3: Ledger Enter Code Pin

Ledger Application
Fig. 4: Ledger Application

Ledger Run Application
Fig. 5: Ledger Run Application

Connect Your Nano to the Application

Now you can click on the button and a popup open. Choose your Ledger Nano X and click connexion

Connect the Ledger with Bluetooth
Fig. 6: Connect the Ledger with Bluetooth

Read the data of a Smart Contract

Now you can click on the button “Get Data” to read the data of the smart contract. Then the data will be displayed on the screen.

Get data from a smart contract
Fig. 7: Get data from a smart contract

Update the data of a Smart Contract

Now instead of reading data, we will overwrite the data by calling a function of the smart contract which is “UpdateData”.

Change data from a smart contract
Fig. 8: Change data from a smart contract

Verify the Address on your Nano

For security reasons, the address will also be displayed on your Ledger Nano X to verify and confirm the address.

Nano Review Screen Nano Amount Screen
Fig. 9: Nano Review Screen \ Fig. 10: Nano Amount Screen

Nano Address Screen Nano Network Screen
Fig. 11: Nano Address Screen \ Fig. 12: Nano Network Screen

Nano Max Fees Screen Nano Accept and Send Screen
Fig. 13: Nano Max Fees Screen \ Fig. 14: Nano Accept and Send Screen

Warning
For the Smart Contract call you need to allow blind signing because the smart contract that is called in the tutorial is not yet verified and reviewed by Ledger. But if the smart contract you are calling is accepted by Ledger do not enable blind signing. Moreover, we do not recommend enabling blind signing in other situations as the main purpose to sign with Ledger is the 'Sign what you see' system. And by enabling blind signing it can not ensure that what you see is what you get.

Review the Transaction on Ropsten Etherscan

By updating the data a transaction is created to change this data, it can be verified on Ropsten Etherscan.

Ropsten Etherscan
Fig. 15: Ropsten Etherscan

Wait till the status passes to Success.

Ropsten Etherscan
Fig. 16: Ropsten Etherscan

Verify the update of data

Finally, to verify if data was updated, open the web application and click on “Get data”.

Verify the data
Fig. 17: Verify the data

Verify the data
Fig. 18: Verify the data

Congratulations, you have successfully built your first application connected with Ledger!


Did you find this page helpful?


How would you improve this page for developers?



Getting Started
Theme Features
Customization