DEV Community

fangjun
fangjun

Posted on • Updated on

02 Understanding Blockchain with `Ethers.js`: 4 Tasks of wallet

Ethers.js provides functions to deal with Ethereum blockchain wallet. You can find Ethers.js documents about Wallet here.

Get a better understanding with the following tasks:


Task 1: Create wallet from mnemonic and privateKey

Task 1.1 Create wallet from mnemonic

mnemonic='myth like bonus scare over problem client lizard pioneer submit female collect'

//check whether it is valid mnemonic
ethers.utils.isValidMnemonic(mnemonic)

wallet = ethers.Wallet.fromMnemonic(mnemonic)
Enter fullscreen mode Exit fullscreen mode

Note: You should never write your mnemonic and privateKey in your script like this, it's dangerous.

The mnemonic and privateKey used here are the default ones used in Ganache-cli and are public.

Task 1.2 Address, Public key and Private key

wallet.address
//'0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1'

wallet.privateKey
'0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d'

wallet.publicKey
'0x04e68acfc0253a10620dff706b0a1b1f1f5833ea3beb3bde2250d5f271f3563606672ebc45e0b7ea2e816ecb70ca03137b1c9476eec63d4632e990020b7b6fba39'
Enter fullscreen mode Exit fullscreen mode

This is the address with path "m/44'/60'/0'/0/0".

We can also get mnemonic of wallet if it is created from mnemonic.

wallet.mnemonic
{
  phrase: 'myth like bonus scare over problem client lizard pioneer submit female collect',
  path: "m/44'/60'/0'/0/0",
  locale: 'en'
}
Enter fullscreen mode Exit fullscreen mode

Task 1.3 Create wallet from privateKey

We can also create wallet from privateKey. Wallet created in this way has no mnemonic property.

privateKey='0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d'
wallet = new ethers.Wallet(privateKey)

wallet.address
//'0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1'
wallet.mnemonic
//null
Enter fullscreen mode Exit fullscreen mode

Task 2: create 20 addresses with mnemonic

We will use Ethers.js utility for HD Wallet (HDNode) to do this.

Task 2.1 Create HDNode instance from mnemonic

mnemonic='myth like bonus scare over problem client lizard pioneer submit female collect'

hdnode = ethers.utils.HDNode.fromMnemonic(mnemonic)
Enter fullscreen mode Exit fullscreen mode

Task 2.2 Get 20 addresses

basepathstr = "m/44'/60'/0'/0/"
for(i=0;i<20;i++){console.log(hdnode.derivePath(basepathstr+i.toString()).address)}
Enter fullscreen mode Exit fullscreen mode

result:

0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1
0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0
0x22d491Bde2303f2f43325b2108D26f1eAbA1e32b
......
Enter fullscreen mode Exit fullscreen mode

The first is with path: "m/44'/60'/0'/0/0"
The second is with path: "m/44'/60'/0'/0/1"
The third is with path: "m/44'/60'/0'/0/2"

Task 2.3 Get address with path

We can also get wallet address with path.

mnemonic='myth like bonus scare over problem client lizard pioneer submit female collect'

wallet = ethers.Wallet.fromMnemonic(mnemonic,"m/44'/60'/0'/0/1")
wallet.address
//'0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0'
Enter fullscreen mode Exit fullscreen mode

Task 3: Sign message with wallet

Task 3.1: Prepare wallet as a signer

mnemonic='myth like bonus scare over problem client lizard pioneer submit female collect'
wallet = ethers.Wallet.fromMnemonic(mnemonic,"m/44'/60'/0'/0/1")
Enter fullscreen mode Exit fullscreen mode

Task 3.2: sign message

message = "solidity-class"
sig = await wallet.signMessage(message)
//'0x8637dce5aeb0b9bd9ee8ce909252806ede88eebc3789436f8f569dd1e45f3f2b47e6633d4119c26917e4b2e08c422028df2209a8ca401f77d4aab9f24b6a40831b'
Enter fullscreen mode Exit fullscreen mode

The signature is result to signing: "\x19Ethereum Signed Message:\n" + message.length + message. Ethers.js docs explains:

A signed message is prefixed with "\x19Ethereum Signed Message:\n" and the length of the message, using the hashMessage method, so that it is EIP-191 compliant. If recovering the address in Solidity, this prefix will be required to create a matching hash.

Task 3.3: verify message

ethers.utils.verifyMessage(message,sig)
//'0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0'
Enter fullscreen mode Exit fullscreen mode

Task 3.4: sign message hash

We can hash the message by ourselves and sign messagehash instead. We got the same signature.

messageHash=  ethers.utils.hashMessage(message)
sig = await wallet.signMessage(messageHash)
//'0xfc40fcec036bd4501ce3bfaf991d561553db6d3601702f8abed9f733eab31a5246163711f71ad48b002ddf83c7cd93656047759ab8e2ff4420c59d5845680ca11b'
Enter fullscreen mode Exit fullscreen mode

Task 3.5 Understanding signature

Get the r,s,v of the signature which is r+s+v.

const r = sig.slice(0, 66);
const s = '0x' + sig.slice(66, 130);
const v = '0x' + sig.slice(130, 132);
Enter fullscreen mode Exit fullscreen mode

Task 4: Send transaction from wallet

Task 4.1 Create wallet

// get wallet account not in hardhat default
privateKey='0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d'
wallet = new ethers.Wallet(privateKey)

wallet.address
//'0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1'

wallet.provider
//null
Enter fullscreen mode Exit fullscreen mode

Task 4.2 send ethers from account 0 to test account

You can send Test Ethers to test wallet address for gas fee usage using MetaMask.

Or you can do it in the console as we do in previous part of this tutorial:

signer = await ethers.provider.getSigner()
send_address = await signer.getAddress()

to_address = wallet.address
nonce = await signer.getTransactionCount()
gas_price = await signer.getGasPrice()
gas_limit = ethers.utils.hexlify(21000)
value = ethers.utils.parseUnits('100.0')

tx = {
  from: send_address,
  to: to_address,
  value: value,
  nonce: nonce,
  gasLimit: gas_limit,
  gasPrice: gas_price,
}

await signer.sendTransaction(tx)

await ethers.provider.getBalance(wallet.address).then(result=>ethers.utils.formatEther(result))
//'100.0'
Enter fullscreen mode Exit fullscreen mode

Task 4.3 Try to send from wallet wrongly

Let us try to call sendTransaction from signer which is different with the send_address.

send_address = wallet.address

to_address = await signer.getAddress()
nonce = await signer.getTransactionCount()
gas_price = await signer.getGasPrice()
gas_limit = ethers.utils.hexlify(21000)
value = ethers.utils.parseUnits('1.0')

tx = {
  from: send_address,
  to: to_address,
  value: value,
  nonce: nonce,
  gasLimit: gas_limit,
  gasPrice: gas_price,
}

await signer.sendTransaction(tx)
Enter fullscreen mode Exit fullscreen mode

We got an Error: "Error: from address mismatch".

Task 4.4 Send from wallet - connect wallet to get signer

First, connect wallet to provider. The return of connect() is the wallet instance which can be a signer.

walletsigner = await wallet.connect(ethers.provider)
walletsigner.address
//'0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1'
Enter fullscreen mode Exit fullscreen mode

Task 4.4 Send ETH using wallet as a signer

Second, sendTransaction with walletsigner:

to_address = await signer.getAddress()
nonce = await walletsigner.getTransactionCount()
gas_price = await signer.getGasPrice()
gas_limit = ethers.utils.hexlify(21000)
value = ethers.utils.parseUnits('1.0')

tx = {
  from: send_address,
  to: to_address,
  value: value,
  nonce: nonce,
  gasLimit: gas_limit,
  gasPrice: gas_price,
}

await walletsigner.sendTransaction(tx)

await ethers.provider.getBalance(wallet.address).then(result=>ethers.utils.formatEther(result))
//'98.999968220292873'
Enter fullscreen mode Exit fullscreen mode

If you find this note useful, please follow my twitter @fjun99.


Discussion (0)