Add in a Kyc Mockup¶
KYC, or "know your customer", is necessary for many different applications nowadays. In it's simplest form, it's just a whitelist, where, based on some criteria, someone get's the permission to do something.
Let's add a simple whitelist, or KYC functionality.
The KYC Smart Contract¶
First, we're going to add a KYC Smart Contract which handles the white-listing. In contracts/KycContract.sol add the following content.
pragma solidity ^0.6.0;
import "@openzeppelin/contracts/access/Ownable.sol";
contract KycContract is Ownable {
mapping(address => bool) allowed;
function setKycCompleted(address _addr) public onlyOwner {
allowed[_addr] = true;
}
function setKycRevoked(address _addr) public onlyOwner {
allowed[_addr] = false;
}
function kycCompleted(address _addr) public view returns(bool) {
return allowed[_addr];
}
}
And in our TokenSale.sol we have to check -- before the actual sale -- if the user is whitelisted. Change the contracts/MyTokenSale.sol to:
pragma solidity ^0.6.0;
import "./Crowdsale.sol";
import "./KycContract.sol";
contract MyTokenSale is Crowdsale {
KycContract kyc;
constructor(
uint256 rate, // rate in TKNbits
address payable wallet,
IERC20 token,
KycContract _kyc
)
Crowdsale(rate, wallet, token)
public
{
kyc = _kyc;
}
function _preValidatePurchase(address beneficiary, uint256 weiAmount) internal view override {
super._preValidatePurchase(beneficiary, weiAmount);
require(kyc.kycCompleted(beneficiary), "KYC not completed yet, aborting");
}
}
And now we also have to change the migration obviously, or else it won't work:
var MyToken = artifacts.require("./MyToken.sol");
var MyTokenSales = artifacts.require("./MyTokenSale.sol");
var KycContract = artifacts.require("./KycContract.sol");
require('dotenv').config({path: '../.env'});
module.exports = async function(deployer) {
let addr = await web3.eth.getAccounts();
await deployer.deploy(MyToken, process.env.INITIAL_TOKENS);
await deployer.deploy(KycContract);
await deployer.deploy(MyTokenSales, 1, addr[0], MyToken.address, KycContract.address);
let tokenInstance = await MyToken.deployed();
await tokenInstance.transfer(MyTokenSales.address, process.env.INITIAL_TOKENS);
};
Now let's change also the Unit-Tests to reflect this:
const Token = artifacts.require("MyToken");
const TokenSale = artifacts.require("MyTokenSale");
const KycContract = artifacts.require("KycContract");
const chai = require("./chaisetup.js");
const BN = web3.utils.BN;
const expect = chai.expect;
contract("TokenSale", async function(accounts) {
const [ initialHolder, recipient, anotherAccount ] = accounts;
//the rest of the code here
it("should be possible to buy one token by simply sending ether to the smart contract", async () => {
let tokenInstance = await Token.deployed();
let tokenSaleInstance = await TokenSale.deployed();
let balanceBeforeAccount = await tokenInstance.balanceOf.call(recipient);
await expect(tokenSaleInstance.sendTransaction({from: recipient, value: web3.utils.toWei("1", "wei")})).to.be.rejected;
await expect(balanceBeforeAccount).to.be.bignumber.equal(await tokenInstance.balanceOf.call(recipient));
let kycInstance = await KycContract.deployed();
await kycInstance.setKycCompleted(recipient);
await expect(tokenSaleInstance.sendTransaction({from: recipient, value: web3.utils.toWei("1", "wei")})).to.be.fulfilled;
return expect(balanceBeforeAccount + 1).to.be.bignumber.equal(await tokenInstance.balanceOf.call(recipient));
});
});