Working KEVM smart contract test script

Anyone interested in a full working example of a contract compile, deploy and test script, you can use the following script.

You need to npm install the following packages: web3, solc, and request-promise-native.

After installing web3, you MUST apply the small patch included at the beginning of the script. This patch modifies web3 to not use EIP-155 which the KEVM testnet does not implement.

The first time you run the script, it will generate a random private key. Simply add the line of code to the beginning of the script and every time you run it, it will use the same address for following runs.

The second time you run it (with a private key) it will request test tokens from the IOHK test faucet several times to get the minimum required balance. This takes a while because the faucet will give an error if too many requests are issued, so the script waits 60 seconds in between requests. If you happen to get an error, just rerun the script.

After all this, the script will compile the inline solidity contract (source in the script), deploy it to the testnet, and use the setter and getter methods to demonstrate it working.

Hope this helps someone!

7 Likes
//const PRIVATEKEY = ''

// IMPORTANT: after 'npm install' you must apply the following patch to node_modules/web3-eth-accounts/src/index.js
// 179,182c179,182
// <                 transaction.data,
// <                 Bytes.fromNat(transaction.chainId || "0x1"),
// <                 "0x",
// <                 "0x"]);
// —-
// >                 transaction.data]);
// >                 //Bytes.fromNat(transaction.chainId || "0x1"),
// >                 //"0x",
// >                 //"0x"]);
// 187c187,188
// <             var signature = Account.makeSigner(Nat.toNumber(transaction.chainId || "0x1") * 2 + 35)(Hash.keccak256(rlpEncoded), privateKey);
// —-
// >             //var signature = Account.makeSigner(Nat.toNumber(transaction.chainId || "0x1") * 2 + 35)(Hash.keccak256(rlpEncoded), privateKey);
// >             var signature = Account.makeSigner(27)(Hash.keccak256(rlpEncoded), privateKey);

const Web3 = require('web3')
const crypto = require('crypto')
const request = require('request-promise-native')

const TARGET_ACCOUNT_BALANCE = 40000000000000000
const FAUCET_INTERVAL = 60000  // to prevent faucet error (too many requests in given amount of time)

process.on('unhandledRejection', err => {
  console.log(err);
});

const providerUrl = 'https://kevm-testnet.iohkdev.io:8546'
//const providerUrl = 'http://localhost:3000'

const run = async () => {
  const web3 = new Web3(providerUrl)

  // ******************************
  // Step 1 - create an account
  // To create an account, you need a random private key. This portion of the 
  // script will create a random key string that can simply be added to the top
  // of the script so it can be re-run with the same account.
  // ******************************

  try {
    var account = web3.eth.accounts.privateKeyToAccount(PRIVATEKEY)
  } catch (err) {
    const rand = crypto.randomBytes(32).toString('hex')
    console.log("No private key - generating random key.")
    console.log("- add the following line to the top of the script and re-run:")
    console.log("const PRIVATEKEY = '0x" + rand + "'");
    process.exit()
  }
  const res = web3.eth.accounts.wallet.add(account)

  // ******************************
  // Step 2 - fund the account
  // If the account balance is zero, here we request test tokens from the IOHK faucet
  // and wait until the account is funded.
  // ******************************

  console.log("Account = " + account.address)
  let balance = parseInt(await web3.eth.getBalance(account.address), 10)
  console.log("Account balance = " + balance)
  while (balance <= TARGET_ACCOUNT_BALANCE) {
    await new Promise(async (res,rej) => {
      console.log("Requesting more test tokens from faucet (waiting " + FAUCET_INTERVAL / 1000 + " seconds)")
      const url = "https://kevm-testnet.iohkdev.io:8099/faucet?address=" + account.address
      try {
        await request.post(url)
      } catch (err) {
        console.log(err.message)
        process.exit()
      }
      var funded = false
      const interval = setInterval(async () => {
        const newbalance = parseInt(await web3.eth.getBalance(account.address), 10)
        if (newbalance > balance) {
          res()
          clearInterval(interval)
        }
      }, FAUCET_INTERVAL)
    })
    balance = parseInt(await web3.eth.getBalance(account.address), 10)
    console.log("Account balance = " + balance)
  }

  // ******************************
  // Step 3 - compile the contract
  // Use the solcjs package to obtain the abi and binary for the following Solidity source
  // ******************************

  console.log("Compiling contract...")
  const solc = require('solc')
  const contractSource = `

    // Test Solidity Contract
    pragma solidity ^0.4.0;

    contract Counter {
      int private count = 0;
      function incrementCounter() public {
        count += 1;
      }
      function decrementCounter() public {
        count -= 1;
      }
      function getCount() public constant returns (int) {
        return count;
      }
    }

  `
  const output = solc.compile(contractSource, 1)
  const abi = output.contracts[':Counter'].interface
  const bin = output.contracts[':Counter'].bytecode
  //console.log("abi=" + abi)
  //console.log("bin=" + bin)

  // ******************************
  // Step 4 - deploy the contract
  // ******************************
  console.log("Deploying contract...")
  const contract = new web3.eth.Contract(JSON.parse(abi))
  const deployed = await contract.deploy({
    data: "0x" + bin
  }).send({
    from: account.address,
    gas: 5000000,
    gasPrice: 5000000000
  })

  // ******************************
  // Step 5 - test deployed contract
  // ******************************
  if (deployed.options.address !== undefined) {
    console.log("Contract address=" + deployed.options.address)
    const instance = new web3.eth.Contract(JSON.parse(abi), deployed.options.address)
    // Test setter and getter
    const beforeCount = await instance.methods.getCount().call()
    console.log("Count before=" + beforeCount)
    await instance.methods.incrementCounter().send({
      from: account.address,
      gas: 100000,
      gasPrice: 5000000000
    })
    const afterCount = await instance.methods.getCount().call()
    console.log("Count after=" + afterCount)
  }

}

try {
  run()
} catch (err) {
  console.log(err)
}
6 Likes

Thank you so much!!!

Thank you, @mjackson001, for this awesome contribution! :raised_hands:


P.S. We have discussed with the @mjackson001 that he will put the code on the github and replace the posted code with a link :slight_smile: But he first wants to create a better version of the script with already patched web3. For now - everyone can refer to this repo for readability:

I will remove this link when Mark posts his own.

1 Like

Thank you very much. This looks like a very good educational introduction

1 Like

Mark, that’s fantastic. :beers:
I cloned the repository and it runs perfectly on my Windows PC.
Since I already had nodeJS, npm and the required modules installed, I might start from the beginning with a clean Windows and write a short tutorial on how to set up the “environment” on a Windows computer (Linux users don’t need such tutorials. ;- )

Would it make sense to activate the writing of a log file by default?
Just to have some record about the created account and contract address.

1 Like

some news on this.
I’ve spend some time with @mjackson001 example script and tried to improve it a little bit.

the private key and the deployed contract address are now automatically stored in an external json file.
It’s also possible to set a certain configuration name, and so have different configuration files for different tests.
Console output has been improved and is also logged to a configuration-name specific logfile.

all together looks like this

and can be found at https://github.com/antipalos/vivliothiki/tree/master/cardano-dev/kevm/testnet-web3

Feel free to download or git-clone and run it on your own NodeJS system.

Contributions to the repository are welcome.

4 Likes

just published a little fix (missing crypto instance)

Also added a little step-by-step guide for Windows-users, allowing you to run this testnet scripts on (your) computer and deploy smart contracts.

1 Like

hello i follow the steps in github but it shows few errors

so plerase could you help me

Hi sultana
it makes me very happy to see you using our test-script.

Currently the testnet itself (one node) has some problems. So it’s not the script running on your side causing this errors. We hope to have any feedback from testnet DevOps ASAP …

EDIT: looks like the previous errors from a out-synched node was resolved but now the testnet responses have some delay our current testscript can’t handle. work and fix in progress,

Curious - is there a way to run a private testnet node?

Not that I know about.

Ruslan told me he is working on a webfrontend version, and struggled with this timeout error in the browser, until I reported that it now happens also in console.
So he presented me a theoretical solution, I believe he’s working on. …

I thought the running Mantis is exactly would be needed to have “your own” node in the testnet, like in the EmurgoHK manual: https://medium.com/emurgohk/how-to-get-on-the-cardano-kevm-testnet-835009fa2c8d

But it’s a bit weird, cuz they say to run a local Mantis, but then connect with a standard remote URL to the cluster, which works without a local node as well :man_shrugging:. I tried at some point to run the Mantis connected to the cluster and then run Mallet connected to the localhost, but couldn’t get the Mantis to sync properly, but it might be my problems with all the settings, tho.

So I kinda gave up on this option for now :slight_smile: Found better time working on it thru a browser.

1 Like

Am very much interested to do dapp in cardano is it any resources to build the dapp in cardano or is it possible to build dapp in it.
Basically i tried to find the steps or example dapp in it.

https://kevm-testnet.iohkdev.io:8099/faucet?address=” For what purpose this url has been used and where i can get this url

error

It got an error while running the code above.So help me to rectify this problem

it is either another new testnet-side error (502 bad gateway usually is a proxy webserver response when he can’t reach the backend)
Or you have requested the faucet too often.

1 Like

https://kevm-testnet.iohkdev.io:8099/faucet?address=” .I refer is from code it cannot work it cannot get balance.
what does this link refer

This is the faucet to request test tokens and load it into your testnet account/wallet

You have to do this after creating the account and before any other activity, because deployment of a smart-contract and execution of his methods requires “gas” deducted from your account balance.
As it is a testnet the faucet allows you to load this value for free. The test script mentioned in this topic does load enough tokens at the first run, by requesting the faucet multiple times and by respecting the timeout the faucet foresees. So not request it too often and request it with the account address you have created before.

1 Like

basic visualization of the Goguen KEVM Testnet activity in the past ~50 days

all values are grouped in 1000 blocks (lasting 4 hours and 11 minutes)
So we have seen 70 transactions in 4 hours as top value. (yellow)
The blue line shows the Gas used for the transaction within this blocks.

Up to now we’ve seen around 2000 transactions (deployed contracts, setter methods, …)

3 Likes