-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
219 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.10; | ||
|
||
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; | ||
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; | ||
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; | ||
import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; | ||
import {ERC20PermitUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol"; | ||
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; | ||
|
||
/* solhint-disable custom-errors */ | ||
|
||
contract PundiAIFX is | ||
Initializable, | ||
ERC20Upgradeable, | ||
ERC20PermitUpgradeable, | ||
AccessControlUpgradeable, | ||
UUPSUpgradeable | ||
{ | ||
bytes32 public constant OWNER_ROLE = keccak256("OWNER_ROLE"); | ||
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); | ||
|
||
IERC20Upgradeable public fxToken; | ||
|
||
/** | ||
* @notice Mints a specified amount of tokens to the recipient's account. | ||
* @dev This function can only be called by an account with the ADMIN_ROLE. | ||
* @param _to The recipient's account. | ||
* @param _amount The amount of tokens to be minted. | ||
*/ | ||
function mint(address _to, uint256 _amount) external onlyRole(ADMIN_ROLE) { | ||
_mint(_to, _amount); | ||
} | ||
|
||
/** | ||
* @notice Burns a specified amount of tokens from the sender's account. | ||
* @dev This function can only be called by an account with the ADMIN_ROLE. | ||
* @param _to The recipient's account. | ||
* @param _amount The amount of tokens to be burned | ||
*/ | ||
function burn(address _to, uint256 _amount) external onlyRole(ADMIN_ROLE) { | ||
_burn(_to, _amount); | ||
} | ||
|
||
/** | ||
* @notice Swaps a specified amount of tokens for a smaller amount of new tokens. | ||
* @dev The function calculates the swap amount as 1% of the input amount. | ||
* It then transfers the original tokens from the sender to the contract | ||
* and mints new tokens to the sender. | ||
* @param _amount The amount of tokens to be swapped. | ||
* @return bool Returns true if the swap is successful. | ||
*/ | ||
function swap(uint256 _amount) external returns (bool) { | ||
uint256 _swapAmount = _amount / 100; | ||
require(_swapAmount > 0, "swap amount is too small"); | ||
|
||
require( | ||
fxToken.transferFrom( | ||
_msgSender(), | ||
address(this), | ||
_swapAmount * 100 | ||
), | ||
"transferFrom FX failed" | ||
); | ||
_mint(_msgSender(), _swapAmount); | ||
return true; | ||
} | ||
|
||
// solhint-disable no-empty-blocks | ||
function _authorizeUpgrade( | ||
address | ||
) internal override onlyRole(OWNER_ROLE) {} | ||
// solhint-disable no-empty-blocks | ||
|
||
function initialize(address _fxToken) public virtual initializer { | ||
fxToken = IERC20Upgradeable(_fxToken); | ||
|
||
__ERC20_init("Pundi AIFX Token", "PUNDIAI"); | ||
__ERC20Permit_init("Pundi AIFX Token"); | ||
__AccessControl_init(); | ||
__UUPSUpgradeable_init(); | ||
|
||
_grantRole(DEFAULT_ADMIN_ROLE, _msgSender()); | ||
_grantRole(OWNER_ROLE, _msgSender()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import { ethers } from "hardhat"; | ||
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; | ||
import { expect } from "chai"; | ||
import { ERC20TokenTest, PundiAIFX } from "../typechain-types"; | ||
import { it } from "mocha"; | ||
|
||
describe("pundiaifx tests", function () { | ||
let deploy: HardhatEthersSigner; | ||
let user1: HardhatEthersSigner; | ||
let fxToken: ERC20TokenTest; | ||
let pundiAIFX: PundiAIFX; | ||
let totalSupply = "100000000"; | ||
|
||
beforeEach(async function () { | ||
const signers = await ethers.getSigners(); | ||
deploy = signers[0]; | ||
user1 = signers[1]; | ||
|
||
const erc20TokenFactory = await ethers.getContractFactory("ERC20TokenTest"); | ||
fxToken = await erc20TokenFactory | ||
.connect(deploy) | ||
.deploy("FX Token", "FX", "18", ethers.parseEther(totalSupply)); | ||
|
||
const pundiAIFXFactory = await ethers.getContractFactory("PundiAIFX"); | ||
const pundiAIFXDeploy = await pundiAIFXFactory.deploy(); | ||
|
||
const erc1967ProxyFactory = await ethers.getContractFactory("ERC1967Proxy"); | ||
const erc1967Proxy = await erc1967ProxyFactory | ||
.connect(deploy) | ||
.deploy(await pundiAIFXDeploy.getAddress(), "0x"); | ||
|
||
pundiAIFX = await ethers.getContractAt( | ||
"PundiAIFX", | ||
await erc1967Proxy.getAddress() | ||
); | ||
await pundiAIFX.connect(deploy).initialize(await fxToken.getAddress()); | ||
|
||
await fxToken | ||
.connect(deploy) | ||
.transfer(user1.address, ethers.parseEther(totalSupply)); | ||
await fxToken | ||
.connect(user1) | ||
.approve(await pundiAIFX.getAddress(), ethers.parseEther(totalSupply)); | ||
}); | ||
|
||
it("Pundi AIFX", async function () { | ||
expect(await pundiAIFX.name()).to.equal("Pundi AIFX Token"); | ||
expect(await pundiAIFX.symbol()).to.equal("PUNDIAI"); | ||
expect(await pundiAIFX.decimals()).to.equal(18); | ||
expect(await pundiAIFX.totalSupply()).to.equal(0); | ||
}); | ||
|
||
it("mint, burn and transfer AIFX", async function () { | ||
expect(await pundiAIFX.balanceOf(user1.address)).to.equal(0); | ||
expect(await pundiAIFX.totalSupply()).to.equal(0); | ||
|
||
await pundiAIFX.grantRole(await pundiAIFX.ADMIN_ROLE(), deploy.address); | ||
await pundiAIFX.mint(deploy.address, "1"); | ||
|
||
await pundiAIFX.approve(user1.address, "1"); | ||
await pundiAIFX | ||
.connect(user1) | ||
.transferFrom(deploy.address, user1.address, "1"); | ||
|
||
expect(await pundiAIFX.balanceOf(user1.address)).to.equal(1); | ||
expect(await pundiAIFX.totalSupply()).to.equal(1); | ||
|
||
await pundiAIFX.connect(user1).transfer(deploy.address, "1"); | ||
|
||
expect(await pundiAIFX.balanceOf(deploy.address)).to.equal(1); | ||
|
||
await pundiAIFX.burn(deploy.address, "1"); | ||
|
||
expect(await pundiAIFX.totalSupply()).to.equal(0); | ||
}); | ||
|
||
it("check role", async function () { | ||
expect(await pundiAIFX.getRoleAdmin(await pundiAIFX.OWNER_ROLE())).to.equal( | ||
await pundiAIFX.DEFAULT_ADMIN_ROLE() | ||
); | ||
expect(await pundiAIFX.getRoleAdmin(await pundiAIFX.ADMIN_ROLE())).to.equal( | ||
await pundiAIFX.DEFAULT_ADMIN_ROLE() | ||
); | ||
|
||
expect( | ||
await pundiAIFX.hasRole( | ||
await pundiAIFX.DEFAULT_ADMIN_ROLE(), | ||
deploy.address | ||
) | ||
).to.equal(true); | ||
expect( | ||
await pundiAIFX.hasRole(await pundiAIFX.OWNER_ROLE(), deploy.address) | ||
).to.equal(true); | ||
|
||
expect( | ||
await pundiAIFX.hasRole(await pundiAIFX.ADMIN_ROLE(), user1.address) | ||
).to.equal(false); | ||
|
||
await pundiAIFX.grantRole(await pundiAIFX.ADMIN_ROLE(), user1.address); | ||
|
||
expect( | ||
await pundiAIFX.hasRole(await pundiAIFX.ADMIN_ROLE(), user1.address) | ||
).to.equal(true); | ||
|
||
await pundiAIFX.revokeRole(await pundiAIFX.ADMIN_ROLE(), user1.address); | ||
|
||
expect( | ||
await pundiAIFX.hasRole(await pundiAIFX.ADMIN_ROLE(), user1.address) | ||
).to.equal(false); | ||
}); | ||
|
||
it("swap FX", async function () { | ||
expect(await fxToken.balanceOf(user1.address)).to.equal( | ||
ethers.parseEther(totalSupply) | ||
); | ||
expect(await pundiAIFX.balanceOf(user1.address)).to.equal(0); | ||
expect(await fxToken.balanceOf(await pundiAIFX.getAddress())).to.equal(0); | ||
expect(await pundiAIFX.totalSupply()).to.equal(0); | ||
|
||
await pundiAIFX.connect(user1).swap(ethers.parseEther("100")); | ||
|
||
expect(await fxToken.balanceOf(user1.address)).to.equal( | ||
ethers.parseEther((Number(totalSupply) - 100).toString()) | ||
); | ||
expect(await pundiAIFX.balanceOf(user1.address)).to.equal( | ||
ethers.parseEther("1") | ||
); | ||
expect(await fxToken.balanceOf(await pundiAIFX.getAddress())).to.equal( | ||
ethers.parseEther("100") | ||
); | ||
expect(await pundiAIFX.totalSupply()).to.equal(ethers.parseEther("1")); | ||
}); | ||
}); |