Skip to content

Commit

Permalink
Add balance task
Browse files Browse the repository at this point in the history
  • Loading branch information
eternauta1337 committed Mar 11, 2024
1 parent 8aed20a commit 345221f
Show file tree
Hide file tree
Showing 8 changed files with 250 additions and 1 deletion.
18 changes: 18 additions & 0 deletions packages/ethernaut-interact/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,24 @@ This plugin doesn't depend on any other plugins.

This plugin adds the following tasks:

### balance

```
Usage: hardhat [GLOBAL OPTIONS] interact balance [--token <SPECIAL>] [address]
OPTIONS:
--token The token address or ETH (default: "ETH")
POSITIONAL ARGUMENTS:
address The address whose balance will be queried
balance: Queries the ETH or TOKEN balance of an address
For global options help run: hardhat help
```

### contract

```
Expand Down
2 changes: 1 addition & 1 deletion packages/ethernaut-interact/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "ethernaut-interact",
"version": "1.0.3",
"description": "Tasks for sending transactions and interacting with contracts",
"description": "Tasks for interacting with a network, including sending eth, interacting with contracts, etc",
"main": "src/index.js",
"scripts": {
"build": "npm run install:test",
Expand Down
21 changes: 21 additions & 0 deletions packages/ethernaut-interact/src/internal/get-contract.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const fs = require('fs')
const path = require('path')

async function getContract(abiName, address, hre) {
return hre.ethers.getContractAt(getAbi(abiName), address)
}

function getAbi(abiName) {
const abisPath = path.join(process.cwd(), 'artifacts', 'interact', 'abis')
const filePath = path.join(abisPath, `${abiName}.json`)

let abi = JSON.parse(fs.readFileSync(filePath, 'utf8'))

if (abi.abi) abi = abi.abi

return abi
}

module.exports = {
getContract,
}
45 changes: 45 additions & 0 deletions packages/ethernaut-interact/src/tasks/balance.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const output = require('ethernaut-common/src/output')
const { types } = require('hardhat/config')
const { getContract } = require('../internal/get-contract')

require('../scopes/interact')
.task('balance', 'Queries the ETH or TOKEN balance of an address')
.addPositionalParam(
'address',
'The address whose balance will be queried',
undefined,
types.string,
)
.addParam('token', 'The token address or ETH', 'ETH', types.string)
.setAction(async ({ address, token }, hre) => {
try {
let balance
if (!token || token === 'ETH') {
balance = await getETHBalance(address, hre)
} else {
balance = await getTokenBalance(address, token, hre)
}

let str = ''
str += `Address: ${address}\n`
str += `Token: ${token}\n`
str += `Balance: ${balance}`

return output.resultBox(str)
} catch (err) {
return output.errorBox(err)
}
})

async function getETHBalance(address, hre) {
return hre.ethers.formatEther(await hre.ethers.provider.getBalance(address))
}

async function getTokenBalance(address, token, hre) {
const contract = await getContract('ERC20', token, hre)

const rawBalance = await contract.balanceOf(address)
const decimals = await contract.decimals()

return hre.ethers.formatUnits(rawBalance, decimals)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "./IERC20.sol";

contract ERC20 is IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(
address indexed owner, address indexed spender, uint256 value
);

uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
string public name;
string public symbol;
uint8 public decimals;

constructor(string memory _name, string memory _symbol, uint8 _decimals) {
name = _name;
symbol = _symbol;
decimals = _decimals;
}

function transfer(address recipient, uint256 amount)
external
returns (bool)
{
balanceOf[msg.sender] -= amount;
balanceOf[recipient] += amount;
emit Transfer(msg.sender, recipient, amount);
return true;
}

function approve(address spender, uint256 amount) external returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}

function transferFrom(address sender, address recipient, uint256 amount)
external
returns (bool)
{
allowance[sender][msg.sender] -= amount;
balanceOf[sender] -= amount;
balanceOf[recipient] += amount;
emit Transfer(sender, recipient, amount);
return true;
}

function _mint(address to, uint256 amount) internal {
balanceOf[to] += amount;
totalSupply += amount;
emit Transfer(address(0), to, amount);
}

function _burn(address from, uint256 amount) internal {
balanceOf[from] -= amount;
totalSupply -= amount;
emit Transfer(from, address(0), amount);
}

function mint(address to, uint256 amount) external {
_mint(to, amount);
}

function burn(address from, uint256 amount) external {
_burn(from, amount);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount)
external
returns (bool);
function allowance(address owner, address spender)
external
view
returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount)
external
returns (bool);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "./ERC20.sol";

contract TestToken is ERC20 {
constructor(string memory _name, string memory _symbol, uint8 _decimals) ERC20(_name, _symbol, _decimals) {
_mint(msg.sender, 10000 * 10**_decimals);
}
}
66 changes: 66 additions & 0 deletions packages/ethernaut-interact/test/tasks/balance.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
const { Terminal } = require('ethernaut-common/src/terminal')
const getBalance = require('../../src/internal/get-balance')

describe('balance', function () {
const terminal = new Terminal()

let signer
let balance

before('get signers', async function () {
const signers = await hre.ethers.getSigners()
signer = signers[0]
})

describe('when querying ETH balance', function () {
before('rec eth balance', async function () {
balance = await getBalance(signer.address)
})

before('make call', async function () {
await terminal.run(`npx hardhat interact balance ${signer.address}`)
})

it('shows the address', async function () {
terminal.has('Address: ' + signer.address)
})

it('shows the token', async function () {
terminal.has('Token: ETH')
})

it('shows the balance', async function () {
terminal.has('Balance: ' + balance)
})
})

describe('when querying a token balance', function () {
let contract

before('deploy token', async function () {
const factory = await hre.ethers.getContractFactory('TestToken')
contract = await factory.deploy('Test Token', 'TEST', 16)
})

before('rec eth balance', async function () {
const rawBalance = await contract.balanceOf(signer.address)
balance = hre.ethers.formatUnits(rawBalance, 16)
})

before('make call', async function () {
const token = await contract.getAddress()
await terminal.run(
`npx hardhat interact balance ${signer.address} --token ${token}`,
)
})

it('shows the token', async function () {
const token = await contract.getAddress()
terminal.has(`Token: ${token}`)
})

it('shows the balance', async function () {
terminal.has('Balance: ' + balance)
})
})
})

0 comments on commit 345221f

Please sign in to comment.