Professional Documents
Culture Documents
Create ERC20 Token Payment Split Smart Contract
Create ERC20 Token Payment Split Smart Contract
In almost every field of cryptocurrency, payment is a recurring topic, especially the provision of
payment to multiple pledgers. For example, DAO wants to fund multiple programs, DEX wants to
merge transaction fees to certain participants, or the team wants to distribute tokens to team
members as a monthly salary.
Smart contracts allow us to automate these types of payment functions, which limits the potential
errors caused by manual payment management and allows us to spend precious time on other
productive tasks.
Today, we will learn how to create our own ERC20 token payment splitter, which can be
incorporated into any project!
The following content requires you to be familiar with Solidity, but anyone can learn.
Project structure
We will create two contracts. The first will be an ERC20 token payment split smart contract, and the
second will be a simulated pool smart contract. The ERC20 token payment splitter smart contract
will be abstract and hold the logic and data used to manage the payer and their respective
payment parts. The simulation pool will inherit the ERC20 token payment splitter so that we can
automatically distribute payments to multiple pledgers. There are two reasons for splitting the
payment function in the two contracts:
Demonstrate the use of token payment split contracts in real-world use cases
Ensure that the token payment splitting contract is flexible enough that anyone can choose
and integrate into their own projects
https://coinyuppie.com/create-erc20-token-payment-split-smart-contract/ 1/13
10/03/2022 12:48 Create ERC20 token payment split smart contract - CoinYuppie: Bitcoin, Ethereum, Metaverse, NFT, DAO, DeFi, Dogecoin, …
After setting up the project, install Hardhat using the following command:
After installing Hardhat, enter npx Hardhat and select the option to create a basic example
project. This will include a convenient file structure to easily create, test, and deploy your own
contracts.
You can delete the Greeter.sol file in the contract folder, and delete the sample-test.js file from the
test folder.
We will also install a library of hardhat plugins, which are Hardhat plugins. They allow us to add
tools for testing and deploying smart contracts.
https://coinyuppie.com/create-erc20-token-payment-split-smart-contract/ 2/13
10/03/2022 12:48 Create ERC20 token payment split smart contract - CoinYuppie: Bitcoin, Ethereum, Metaverse, NFT, DAO, DeFi, Dogecoin, …
This token payment split smart contract will provide logic to set up and store data related to the
payee list and the share of each payee. The number of shares held by each payee is equal to the
proportion of funds they should receive (for example, if there are 4 payees and each person holds 5
shares, then each of them will receive 25% of any expenditure).
To start this contract, we will create a new file in our contract folder and name it
TokenPaymentSplitter.sol.
Note that this is an abstract contract and we will import it into the simulation pool contract
later. Making it abstract also allows us to easily import this contract into any other real project in
the future.
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
https://coinyuppie.com/create-erc20-token-payment-split-smart-contract/ 3/13
10/03/2022 12:48 Create ERC20 token payment split smart contract - CoinYuppie: Bitcoin, Ethereum, Metaverse, NFT, DAO, DeFi, Dogecoin, …
SafeERC20.sol provides the ERC20 interface, which allows us to call standard functions from any
ERC20 smart contract and wrap these calls in additional functions to provide a safer way to transfer
tokens.
_totalTokenReleased is the total amount of payment tokens that have been paid to all recipients.
_shares is the mapping between the address of the payee and the number of shares allocated to
them.
_tokenReleased is the mapping from the payee address to the number of payment tokens.
Now place a constructor that accepts three parameters. The first parameter is the array of payees
that we want to initialize in the contract deployment. The second parameter is an array of shares
for each payee. The third is the address of the ERC20 token that will be used for payment.
constructor(
https://coinyuppie.com/create-erc20-token-payment-split-smart-contract/ 4/13
10/03/2022 12:48 Create ERC20 token payment split smart contract - CoinYuppie: Bitcoin, Ethereum, Metaverse, NFT, DAO, DeFi, Dogecoin, …
address _paymentToken
) {
require(
payees.length == shares_.length,
);
_addPayee(payees[i], shares_[i]);
}
paymentToken = _paymentToken;
The constructor contains a require statement to ensure that the two arrays have the same length so
that each payee has a share allocated to them. There is another require statement to ensure that
the contract is initialized and there is at least one payee.
There is also a for loop, which assigns each payee and its share to the variables we created
above. This is done through a function called _addPayee, which we will create soon.
After the constructor is ready, add a few more functions to call and get contract variables.
return _totalShares;
return _shares[account];
return _payees[index];
require(
account != address(0),
);
https://coinyuppie.com/create-erc20-token-payment-split-smart-contract/ 5/13
10/03/2022 12:48 Create ERC20 token payment split smart contract - CoinYuppie: Bitcoin, Ethereum, Metaverse, NFT, DAO, DeFi, Dogecoin, …
require(
_shares[account] == 0,
);
_payees.push(account);
_shares[account] = shares_;
_addPayee is the function we call in the constructor to set the payee array. This function has two
parameters, the payee’s account and the number of shares associated with it. Then it checks
whether the account is a zero address, whether the share is greater than zero, and whether the
account has been registered as a payee. If all checks pass, then we add the data to the respective
variables.
require(
);
IERC20(paymentToken).safeTransfer(account, payment);
Release is a function that anyone can call. It accepts the parameters of an existing payee
account. Let’s analyze what happened in this function. First, it checks whether the account has
shares allocated to it. Then, it creates a variable called tokenTotalReceived, which adds the current
token balance of the contract to the total number of tokens previously released. Create another
variable called payment, which determines how much of the total amount of tokens received is
owed to the account, and then subtracts how much has been released to the account. Then, a
require statement checks whether the current payment amount is greater than zero (that is,
whether more tokens are currently owed). If the check passes, the tokenReleased of the account is
https://coinyuppie.com/create-erc20-token-payment-split-smart-contract/ 6/13
10/03/2022 12:48 Create ERC20 token payment split smart contract - CoinYuppie: Bitcoin, Ethereum, Metaverse, NFT, DAO, DeFi, Dogecoin, …
The function is now in place! But there is one more thing to do in this contract…event!
We will add two events to the contract. It is a good practice to add events to the top of the
contract.
After these events are included in the contract, we will emit them in the appropriate function.
///existingFunctionCode
///existingFunctionCode
Now the token payment splitting contract has been established! To understand how this works in a
real scenario, let’s create a simulated pool contract that will import the token payment splitter.
This contract will not be very complicated, because we just want to demonstrate how to integrate a
token payment splitter. This contract regularly receives specific ERC20 tokens that we want to
distribute to the payee list. This ERC20 token can be reached through different scenarios, such as
user deposits or redirection fees from another smart contract. In real life, depending on different
projects, there may be a more complex contract that contains more functions to meet the user’s
use case.
In the contract folder, create a new file named MockPool.sol. Then add the following code.
https://coinyuppie.com/create-erc20-token-payment-split-smart-contract/ 7/13
10/03/2022 12:48 Create ERC20 token payment split smart contract - CoinYuppie: Bitcoin, Ethereum, Metaverse, NFT, DAO, DeFi, Dogecoin, …
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./TokenPaymentSplitter.sol";
constructor(
require(
_token != paymentToken,
);
IERC20(_token).safeTransfer(_transferTo, balance);
}
In this contract, three things are imported. The first is OpenZeppelin’s Ownable utility, which uses
the only Owner modifier on some functions. The second is SafeERC20, which allows safe ERC20
token transfers, as will be seen in the contract. The third is our TokenPaymentSplitter contract.
In the MockPool constructor, we need TokenPaymentSplitter to provide the same three parameters,
we just pass them to our inherited contract.
Another function is added to this contract, drainTo. It actually has nothing to do with the
TokenPaymentSplitter contract. It is just a security mechanism when another ERC20 token that is
not set as a payment token is sent to the pool, and then there is a way for the contract owner to
release the token.
Test contract
Testing smart contracts is as important as creating them. The assets handled by these contracts
usually belong to other people, so as developers, we are responsible for ensuring that these assets
work the way they should, and our tests can cover almost all edge cases.
https://coinyuppie.com/create-erc20-token-payment-split-smart-contract/ 8/13
10/03/2022 12:48 Create ERC20 token payment split smart contract - CoinYuppie: Bitcoin, Ethereum, Metaverse, NFT, DAO, DeFi, Dogecoin, …
The tests that will be performed here are some examples to show that the TokenPaymentSplitter
smart contract works as expected. When working on your own project, you may want to create a
test specifically suited to your use case.
In order to support our testing, we want to include an ERC20 token. For this, we will create a new
solididity file, which will be imported into the OpenZepplin ERC20 template for our testing. In the
contract folder, create a new file named Imports.sol and include the following code:
import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol";
contract Imports {}
Now, create a file named test.js in the test folder. At the top of this file, we will import the packages
that support our tests.
Now, in order to set up the test, we will first create the necessary variables, create the beforeEach
function, which is called before each test, and create an empty describe function, which will soon
contain our test.
let deployer
let account1
let account2
let account3
let account4
let testPaymentToken
let mockPool
beforeEach(async () => {
await testPaymentToken.deployed()
})
describe('Add payees with varying amounts and distribute payments', async () => {}
https://coinyuppie.com/create-erc20-token-payment-split-smart-contract/ 9/13
10/03/2022 12:48 Create ERC20 token payment split smart contract - CoinYuppie: Bitcoin, Ethereum, Metaverse, NFT, DAO, DeFi, Dogecoin, …
With these parts in place, let’s get to the core part of these tests!
payeeAddressArray,
payeeShareArray,
testPaymentToken.address
)
await mockPool.deployed()
await mockPool
.connect(account1)
.release(account1.address)
await mockPool
.connect(account2)
.release(account2.address)
await mockPool
.connect(account3)
.release(account3.address)
await mockPool
.connect(account4)
.release(account4.address)
expect(account1TokenBalance).to.equal(25000)
expect(account2TokenBalance).to.equal(25000)
expect(account3TokenBalance).to.equal(25000)
expect(account4TokenBalance).to.equal(25000)
})
In this test, we assign the contract to 4 payees, each of whom has 10 equal shares. Then we send
100000 units of testPaymentToken to the contract and issue payment to each payee. It can be
noticed in the test that each payee is calling a function to release tokens to himself.
https://coinyuppie.com/create-erc20-token-payment-split-smart-contract/ 10/13
10/03/2022 12:48 Create ERC20 token payment split smart contract - CoinYuppie: Bitcoin, Ethereum, Metaverse, NFT, DAO, DeFi, Dogecoin, …
payeeAddressArray,
payeeShareArray,
testPaymentToken.address
)
await mockPool.deployed()
await mockPool
.connect(account1)
.release(account1.address)
await mockPool
.connect(account2)
.release(account2.address)
await mockPool
.connect(account3)
.release(account3.address)
await mockPool
.connect(account4)
.release(account4.address)
mockPool.address
)
expect(mockPoolTestPaymentTokenBalance).to.equal(1)
expect(account1TokenBalance).to.equal(30303)
expect(account2TokenBalance).to.equal(15151)
expect(account3TokenBalance).to.equal(33333)
expect(account4TokenBalance).to.equal(21212)
})
It seems that the payee can still get the money, but have you noticed anything? There is one unit of
payment token left in the contract! Since Solidity has no decimals, when it reaches the lowest unit,
it will usually be rounded, which may cause contract dust to fly, as we have seen here. But don’t
https://coinyuppie.com/create-erc20-token-payment-split-smart-contract/ 11/13
10/03/2022 12:48 Create ERC20 token payment split smart contract - CoinYuppie: Bitcoin, Ethereum, Metaverse, NFT, DAO, DeFi, Dogecoin, …
worry, because we expect payment tokens to flow into the contract in the future, so it will continue
to be distributed.
it('payment token is distributed unevenly to multiple payees with additional payment toke
payeeAddressArray = [account1.address, account2.address, account3.address, account4.ad
payeeShareArray = [10, 5, 11, 7]
payeeAddressArray,
payeeShareArray,
testPaymentToken.address
)
await mockPool.deployed()
await mockPool
.connect(account1)
.release(account1.address)
await mockPool
.connect(account2)
.release(account2.address)
await mockPool
.connect(account3)
.release(account3.address)
await mockPool
.connect(account4)
.release(account4.address)
await mockPool
.connect(account1)
.release(account1.address)
await mockPool
.connect(account2)
.release(account2.address)
mockPool.address
)
expect(mockPoolTestPaymentTokenBalance).to.equal(1)
expect(account1TokenBalance).to.equal(60606)
https://coinyuppie.com/create-erc20-token-payment-split-smart-contract/ 12/13
10/03/2022 12:48 Create ERC20 token payment split smart contract - CoinYuppie: Bitcoin, Ethereum, Metaverse, NFT, DAO, DeFi, Dogecoin, …
expect(account2TokenBalance).to.equal(30303)
expect(account3TokenBalance).to.equal(66666)
expect(account4TokenBalance).to.equal(42424)
})
Now that all the tests are ready, it’s time to run them and see if they work! In the project root
folder, use npx hardhat test to start the tests. If everything is correct, then you should see all the
green squares as shown in the image below.
As mentioned above, we need to do more testing to ensure that the entire project/agreement
works as expected, and the payment splitter is an integrated part of it. This will mean more unit
tests to cover all available functions, as well as more complex integration tests, depending on the
specific use case.
Summarize
Payments are a common aspect of many encryption protocols, and there are several ways to solve
them. Today we learned a way to manage payments, although users can even build on this contract
to meet your specific needs, such as enabling payments across multiple tokens, adding additional
payees, or removing payees , Or distribute all payments at the same time in one function call.
https://coinyuppie.com/create-erc20-token-payment-split-smart-contract/ 13/13