Solidity Tutorial: Simple Tax Token

Cyrille
Coinmonks
Published in
4 min readMar 10, 2023

--

In this article, we will take a deep dive into the world of Solidity by creating a Simple Tax Token. This tutorial will help you understand the basics of Solidity programming and how to build smart contracts. By the end of this tutorial, you will have a solid understanding of Solidity and be able to apply your knowledge to create your own smart contracts.

So if you’re ready to start your journey into the exciting world of Solidity, let’s dive in!

Photo by The New York Public Library on Unsplash

Setting up the environment

To set up your development environment follow the Hardhat getting started guide. We will be using Typescript for this project.

Once you are set up, you can install the @openzeppelin/contracts library with the following command.

yarn add --dev @openzeppelin/contracts

Creating the Simple Tax Token contract

We will be creating a smart contract that implements the ERC-20 standard. Our contract includes a tax function, which automatically deducts a percentage of the token value from each transaction that takes place on the blockchain.

The tax is collected automatically and is then distributed to a specified address (fund), such as a charity or a development fund.

We will inherit contracts from the @openzeppelin/contracts library in order to improve the safety and speed of the development process.

We will only be creating two tests for this tutorial.

  1. It should set the fund address at deployment.
  2. It should transfer 5% to the fund address.
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"
import { expect } from "chai"
import { ethers } from "hardhat"

describe("SimpleVoting", function () {

async function deploy() {
const [deployer, fund, target] = await ethers.getSigners()
const Contract = await ethers.getContractFactory("SimpleTax")
const contract = await Contract.deploy(fund.address)
await contract.deployed()
return { contract, deployer, fund, target }
}

describe("deployment", function () {
it("should set the fund address", async function () {
const { contract, fund } = await loadFixture(deploy)
expect(await contract.fund()).to.eq(fund.address)
})
})

describe("transfer", function () {
it("should transfer 5% to the fund's address", async function () {
const { contract, deployer, fund, target } = await loadFixture(deploy)
const amount = ethers.utils.parseEther("100")
await expect(contract.transfer(target.address, amount)).to.changeTokenBalances(contract,
[deployer, fund, target],
[amount.mul(-1), amount.mul(5).div(100), amount.mul(95).div(100)]
)
})
})
})

In our contract, we will have to do two things.

fund

In our constructor, we will add an argument for the fund. You can see in the deploy function of our test case that we call Contract.deploy with the fund address as an argument. We will then set the fund in the contract.

If you look at the declaration of the fund in the contract, you will see that it is immutable. This simply means that it can not be changed once initialized.

_transfer

_transfer is an internal function. The external transfer function calls it. It is responsible for changing the balances. It is in this _transfer function that we calculate the amount that should be transferred to the recipient and the fund.

In solidity, there are no such things as decimals, so we can not use a fixed percentage i.e. 5/100 = 0.05, this number doesn’t exist in solidity.

In our test case, we are transferring 100 tokens. In solidity, 100 is represented by 100000000000000000000. Given the size of that number, we can divide it by 100 and multiply it by 10.

Last, but not least, we call the super._transfer function (twice). This is calling the _transfer function declared in the OpenZeppelin ERC20.sol contract. We call it once to transfer funds to the recipient, and once to transfer funds to the fund.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract SimpleTax is ERC20 {

address public immutable fund;

constructor(address fund_) ERC20("SimpleTax", "STX") {
_mint(msg.sender, 1000 * 10 ** decimals());
fund = fund_;
}

function _transfer(
address sender,
address recipient,
uint256 amount
) internal virtual override {
uint tax = (amount / 100) * 5; // 5% tax

super._transfer(sender, recipient, amount - tax);
super._transfer(sender, fund, tax);
}
}

Congratulations! We wrote a simple tax token in 25 lines of code. Not bad. The code is available on GitHub.

Production

It is important to note that in order to make this contract work correctly on a decentralized exchange, we would need to add a whitelist, in order to not tax certain transfers involving the liquidity pair. More on this in a future article.

If this article was helpful, join our Telegram group! It is a web3 space to discuss ideas, build projects and help troubleshoot code.

I’m a multidisciplinary founder, project manager, solidity + react developer, husband, and family+friends cook.

Got a project idea? Reach out to me on Twitter or join the Telegram group.

New to trading? Try crypto trading bots or copy trading on best crypto exchanges

Join Coinmonks Telegram Channel and Youtube Channel get daily Crypto News

Also, Read

--

--

Cyrille
Coinmonks

I build web2 and web3 products and teams. Depending on the project, I operate as a CTO, product manager, developer, advisor, or investor.