feat: Add Hardhat config, MockERC20 and tests for Smart Contract
This commit is contained in:
12
contracts/MockERC20.sol
Normal file
12
contracts/MockERC20.sol
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.8.20;
|
||||||
|
|
||||||
|
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||||
|
|
||||||
|
contract MockERC20 is ERC20 {
|
||||||
|
constructor(string memory name, string memory symbol) ERC20(name, symbol) {}
|
||||||
|
|
||||||
|
function mint(address to, uint256 amount) public {
|
||||||
|
_mint(to, amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
hardhat.config.ts
Normal file
22
hardhat.config.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { HardhatUserConfig } from "hardhat/config";
|
||||||
|
import "@nomicfoundation/hardhat-toolbox";
|
||||||
|
|
||||||
|
const config: HardhatUserConfig = {
|
||||||
|
solidity: {
|
||||||
|
version: "0.8.20",
|
||||||
|
settings: {
|
||||||
|
optimizer: {
|
||||||
|
enabled: true,
|
||||||
|
runs: 200,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
paths: {
|
||||||
|
sources: "./contracts",
|
||||||
|
tests: "./test",
|
||||||
|
cache: "./cache",
|
||||||
|
artifacts: "./artifacts"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
6255
package-lock.json
generated
6255
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,6 @@
|
|||||||
"@supabase/supabase-js": "^2.90.1",
|
"@supabase/supabase-js": "^2.90.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"ethers": "^6.13.5",
|
|
||||||
"lucide-react": "^0.562.0",
|
"lucide-react": "^0.562.0",
|
||||||
"next": "16.1.1",
|
"next": "16.1.1",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
@@ -25,12 +24,20 @@
|
|||||||
"tailwind-merge": "^3.4.0"
|
"tailwind-merge": "^3.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@nomicfoundation/hardhat-ethers": "^4.0.6",
|
||||||
|
"@nomicfoundation/hardhat-toolbox": "^6.1.2",
|
||||||
|
"@openzeppelin/contracts": "^5.6.1",
|
||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
|
"@types/chai": "^5.2.3",
|
||||||
|
"@types/mocha": "^10.0.10",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^19",
|
"@types/react": "^19",
|
||||||
"@types/react-dom": "^19",
|
"@types/react-dom": "^19",
|
||||||
|
"chai": "^6.2.2",
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "16.1.1",
|
"eslint-config-next": "16.1.1",
|
||||||
|
"ethers": "^6.16.0",
|
||||||
|
"hardhat": "^3.1.12",
|
||||||
"tailwindcss": "^4",
|
"tailwindcss": "^4",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
}
|
}
|
||||||
|
|||||||
84
test/AyrisSplitter.test.ts
Normal file
84
test/AyrisSplitter.test.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
import { ethers } from "hardhat";
|
||||||
|
import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers";
|
||||||
|
|
||||||
|
describe("AyrisSplitter", function () {
|
||||||
|
async function deploySplitterFixture() {
|
||||||
|
const [owner, platformAccount, merchantAccount, customerAccount] = await ethers.getSigners();
|
||||||
|
|
||||||
|
const Splitter = await ethers.getContractFactory("AyrisSplitter");
|
||||||
|
const splitter = await Splitter.deploy(platformAccount.address);
|
||||||
|
|
||||||
|
const MockERC20 = await ethers.getContractFactory("MockERC20");
|
||||||
|
const mockToken = await MockERC20.deploy("Tether USD", "USDT");
|
||||||
|
|
||||||
|
// Mint 1000 USDT to customer
|
||||||
|
await mockToken.mint(customerAccount.address, ethers.parseUnits("1000", 18));
|
||||||
|
|
||||||
|
return { splitter, mockToken, owner, platformAccount, merchantAccount, customerAccount };
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Deployment", function () {
|
||||||
|
it("Should set the right platform address", async function () {
|
||||||
|
const { splitter, platformAccount } = await loadFixture(deploySplitterFixture);
|
||||||
|
expect(await splitter.platformAddress()).to.equal(platformAccount.address);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Native Payments (ETH/MATIC)", function () {
|
||||||
|
it("Should split native currency exactly 99% to merchant and 1% to platform", async function () {
|
||||||
|
const { splitter, merchantAccount, platformAccount, customerAccount } = await loadFixture(deploySplitterFixture);
|
||||||
|
|
||||||
|
const paymentAmount = ethers.parseEther("1.0"); // 1 ETH/MATIC
|
||||||
|
|
||||||
|
const initialMerchantBalance = await ethers.provider.getBalance(merchantAccount.address);
|
||||||
|
const initialPlatformBalance = await ethers.provider.getBalance(platformAccount.address);
|
||||||
|
|
||||||
|
// Customer sends 1 ETH to contract using payNative
|
||||||
|
await splitter.connect(customerAccount).payNative(merchantAccount.address, { value: paymentAmount });
|
||||||
|
|
||||||
|
const finalMerchantBalance = await ethers.provider.getBalance(merchantAccount.address);
|
||||||
|
const finalPlatformBalance = await ethers.provider.getBalance(platformAccount.address);
|
||||||
|
|
||||||
|
// 1 ETH * 1% = 0.01 ETH
|
||||||
|
const expectedPlatformShare = ethers.parseEther("0.01");
|
||||||
|
// 1 ETH * 99% = 0.99 ETH
|
||||||
|
const expectedMerchantShare = ethers.parseEther("0.99");
|
||||||
|
|
||||||
|
expect(finalMerchantBalance - initialMerchantBalance).to.equal(expectedMerchantShare);
|
||||||
|
expect(finalPlatformBalance - initialPlatformBalance).to.equal(expectedPlatformShare);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Token Payments (USDT/USDC)", function () {
|
||||||
|
it("Should split ERC20 tokens exactly 99% to merchant and 1% to platform", async function () {
|
||||||
|
const { splitter, mockToken, merchantAccount, platformAccount, customerAccount } = await loadFixture(deploySplitterFixture);
|
||||||
|
|
||||||
|
const paymentAmount = ethers.parseUnits("100", 18); // 100 USDT
|
||||||
|
|
||||||
|
// Customer must approve the splitter contract first
|
||||||
|
await mockToken.connect(customerAccount).approve(await splitter.getAddress(), paymentAmount);
|
||||||
|
|
||||||
|
const initialMerchantBalance = await mockToken.balanceOf(merchantAccount.address);
|
||||||
|
const initialPlatformBalance = await mockToken.balanceOf(platformAccount.address);
|
||||||
|
|
||||||
|
// Customer executes payment
|
||||||
|
await splitter.connect(customerAccount).payToken(
|
||||||
|
await mockToken.getAddress(),
|
||||||
|
paymentAmount,
|
||||||
|
merchantAccount.address
|
||||||
|
);
|
||||||
|
|
||||||
|
const finalMerchantBalance = await mockToken.balanceOf(merchantAccount.address);
|
||||||
|
const finalPlatformBalance = await mockToken.balanceOf(platformAccount.address);
|
||||||
|
|
||||||
|
// 100 USDT * 1% = 1 USDT
|
||||||
|
const expectedPlatformShare = ethers.parseUnits("1", 18);
|
||||||
|
// 100 USDT * 99% = 99 USDT
|
||||||
|
const expectedMerchantShare = ethers.parseUnits("99", 18);
|
||||||
|
|
||||||
|
expect(finalMerchantBalance - initialMerchantBalance).to.equal(expectedMerchantShare);
|
||||||
|
expect(finalPlatformBalance - initialPlatformBalance).to.equal(expectedPlatformShare);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user