gn-contract 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.openzeppelin/bsc-testnet.json +1847 -0
- package/.prettierrc +15 -0
- package/README.md +14 -0
- package/constants/getter.ts +4 -0
- package/constants/rootAddress.ts +22 -0
- package/contracts/CROWD.sol +32 -0
- package/contracts/CUSDT.sol +32 -0
- package/contracts/NFT.sol +499 -0
- package/contracts/NFTFounder.sol +254 -0
- package/contracts/NFTGenesis.sol +337 -0
- package/contracts/Network.sol +503 -0
- package/contracts/Swap.sol +86 -0
- package/contracts/USDT.sol +32 -0
- package/contracts/lib/AccessControl.sol +35 -0
- package/contracts/lib/NFTGetter.sol +60 -0
- package/contracts/lib/NFTHelpers.sol +111 -0
- package/contracts/lib/ValhallaBlackList.sol +18 -0
- package/contracts/lib/ValhallaPool.sol +44 -0
- package/hardhat.config.ts +44 -0
- package/package.json +50 -0
- package/scripts/DeployNetwork.ts +123 -0
- package/scripts/DeploySwap.ts +57 -0
- package/scripts/DeployUSDT.ts +16 -0
- package/test/Swap.test.ts +182 -0
- package/test/lib/initializer.ts +193 -0
- package/test/nft_genesis.test.ts +399 -0
- package/test/nft_purchase.test.ts +210 -0
- package/test/rank_distribution.test.ts +142 -0
- package/test/registration.test.ts +267 -0
- package/test/registration_reward.test.ts +114 -0
- package/tsconfig.json +11 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import { ethers, upgrades } from "hardhat";
|
|
3
|
+
import { initialize, getState, getAccounts } from "./lib/initializer";
|
|
4
|
+
import { Swap } from "../typechain-types";
|
|
5
|
+
import { toBn } from "evm-bn";
|
|
6
|
+
|
|
7
|
+
describe("Swap Contract", function () {
|
|
8
|
+
let swap: Swap;
|
|
9
|
+
|
|
10
|
+
before(async () => {
|
|
11
|
+
// Initialize the environment (deploys USDT, CUSDT, CROWD, etc.)
|
|
12
|
+
await initialize();
|
|
13
|
+
const { usdt, cusdt, crowd, owner } = getState();
|
|
14
|
+
|
|
15
|
+
// Deploy Swap Contract
|
|
16
|
+
const SwapFactory = await ethers.getContractFactory("Swap", owner);
|
|
17
|
+
swap = (await upgrades.deployProxy(
|
|
18
|
+
SwapFactory,
|
|
19
|
+
[
|
|
20
|
+
usdt.address,
|
|
21
|
+
cusdt.address,
|
|
22
|
+
crowd.address,
|
|
23
|
+
owner.address, // feeReceiver
|
|
24
|
+
owner.address, // feeMarketing
|
|
25
|
+
],
|
|
26
|
+
{ kind: "uups" },
|
|
27
|
+
)) as Swap;
|
|
28
|
+
await swap.deployed();
|
|
29
|
+
|
|
30
|
+
// Fund the Swap contract with CUSDT and CROWD so users can swap
|
|
31
|
+
// Owner (admin) should have initial supply from initialize() or we can mint/transfer
|
|
32
|
+
// Check initial balances of owner
|
|
33
|
+
const ownerCusdtBal = await cusdt.balanceOf(owner.address);
|
|
34
|
+
const ownerCrowdBal = await crowd.balanceOf(owner.address);
|
|
35
|
+
|
|
36
|
+
// Transfer some liquidity to Swap contract
|
|
37
|
+
await cusdt.connect(owner).transfer(swap.address, toBn("100000", 18));
|
|
38
|
+
await crowd.connect(owner).transfer(swap.address, toBn("1000000", 18));
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should initialize with correct token addresses", async () => {
|
|
42
|
+
const { usdt, cusdt, crowd } = getState();
|
|
43
|
+
expect(await swap.usdt()).to.eq(usdt.address);
|
|
44
|
+
expect(await swap.cusdt()).to.eq(cusdt.address);
|
|
45
|
+
expect(await swap.crowd()).to.eq(crowd.address);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should swap USDT to CUSDT 1:1", async () => {
|
|
49
|
+
const { usdt, cusdt } = getState();
|
|
50
|
+
const [user] = await getAccounts(1, 1);
|
|
51
|
+
const amount = toBn("100", 18);
|
|
52
|
+
|
|
53
|
+
// Fund user with USDT
|
|
54
|
+
const { owner } = getState();
|
|
55
|
+
await usdt.connect(owner).transfer(user.address, amount);
|
|
56
|
+
|
|
57
|
+
// Approve Swap contract
|
|
58
|
+
await usdt.connect(user).approve(swap.address, amount);
|
|
59
|
+
|
|
60
|
+
const userUsdtBefore = await usdt.balanceOf(user.address);
|
|
61
|
+
const userCusdtBefore = await cusdt.balanceOf(user.address);
|
|
62
|
+
|
|
63
|
+
// Perform Swap
|
|
64
|
+
await swap.connect(user).swapToCUSDT(amount);
|
|
65
|
+
|
|
66
|
+
const userUsdtAfter = await usdt.balanceOf(user.address);
|
|
67
|
+
const userCusdtAfter = await cusdt.balanceOf(user.address);
|
|
68
|
+
|
|
69
|
+
expect(userUsdtAfter).to.eq(userUsdtBefore.sub(amount));
|
|
70
|
+
expect(userCusdtAfter).to.eq(userCusdtBefore.add(amount));
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("should fail swapToCUSDT if insufficient pool balance", async () => {
|
|
74
|
+
const { usdt } = getState();
|
|
75
|
+
const [user] = await getAccounts(2, 1);
|
|
76
|
+
// Try to swap more than available in pool (we put 100k)
|
|
77
|
+
const amount = toBn("200000", 18);
|
|
78
|
+
|
|
79
|
+
const { owner } = getState();
|
|
80
|
+
await usdt.connect(owner).transfer(user.address, amount);
|
|
81
|
+
await usdt.connect(user).approve(swap.address, amount);
|
|
82
|
+
|
|
83
|
+
await expect(swap.connect(user).swapToCUSDT(amount)).to.be.revertedWith(
|
|
84
|
+
"Insufficient Pool",
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("should swap USDT to CROWD 1:500", async () => {
|
|
89
|
+
const { usdt, crowd } = getState();
|
|
90
|
+
const [user] = await getAccounts(3, 1);
|
|
91
|
+
const amount = toBn("10", 18); // 10 USDT
|
|
92
|
+
const expectedCrowd = amount.mul(500); // 5000 CROWD
|
|
93
|
+
|
|
94
|
+
// Fund user
|
|
95
|
+
const { owner } = getState();
|
|
96
|
+
await usdt.connect(owner).transfer(user.address, amount);
|
|
97
|
+
|
|
98
|
+
// Approve
|
|
99
|
+
await usdt.connect(user).approve(swap.address, amount);
|
|
100
|
+
|
|
101
|
+
const userUsdtBefore = await usdt.balanceOf(user.address);
|
|
102
|
+
const userCrowdBefore = await crowd.balanceOf(user.address);
|
|
103
|
+
|
|
104
|
+
// Swap
|
|
105
|
+
await swap.connect(user).swapToCROWD(amount);
|
|
106
|
+
|
|
107
|
+
const userUsdtAfter = await usdt.balanceOf(user.address);
|
|
108
|
+
const userCrowdAfter = await crowd.balanceOf(user.address);
|
|
109
|
+
|
|
110
|
+
expect(userUsdtAfter).to.eq(userUsdtBefore.sub(amount));
|
|
111
|
+
expect(userCrowdAfter).to.eq(userCrowdBefore.add(expectedCrowd));
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("should fail swapToCROWD if insufficient pool balance", async () => {
|
|
115
|
+
const { usdt } = getState();
|
|
116
|
+
const [user] = await getAccounts(4, 1);
|
|
117
|
+
// Pool has 1M CROWD.
|
|
118
|
+
// Need > 1M CROWD output.
|
|
119
|
+
// 1 USDT = 500 CROWD.
|
|
120
|
+
// 2001 USDT = 1,000,500 CROWD > 1M.
|
|
121
|
+
const amount = toBn("2001", 18);
|
|
122
|
+
|
|
123
|
+
const { owner } = getState();
|
|
124
|
+
await usdt.connect(owner).transfer(user.address, amount);
|
|
125
|
+
await usdt.connect(user).approve(swap.address, amount);
|
|
126
|
+
|
|
127
|
+
await expect(swap.connect(user).swapToCROWD(amount)).to.be.revertedWith(
|
|
128
|
+
"Insufficient Pool",
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe("Owner Withdrawals", function () {
|
|
133
|
+
it("should allow owner to withdraw USDT", async () => {
|
|
134
|
+
const { usdt, owner } = getState();
|
|
135
|
+
// Swap contract likely has USDT from previous tests
|
|
136
|
+
const contractUsdtBal = await usdt.balanceOf(swap.address);
|
|
137
|
+
const withdrawAmount = contractUsdtBal;
|
|
138
|
+
|
|
139
|
+
const ownerBefore = await usdt.balanceOf(owner.address);
|
|
140
|
+
await swap.connect(owner).withdrawUSDT(withdrawAmount);
|
|
141
|
+
const ownerAfter = await usdt.balanceOf(owner.address);
|
|
142
|
+
|
|
143
|
+
expect(ownerAfter).to.eq(ownerBefore.add(withdrawAmount));
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("should allow owner to withdraw CUSDT", async () => {
|
|
147
|
+
const { cusdt, owner } = getState();
|
|
148
|
+
const amount = toBn("50", 18); // Withdraw small amount
|
|
149
|
+
|
|
150
|
+
const ownerBefore = await cusdt.balanceOf(owner.address);
|
|
151
|
+
await swap.connect(owner).withdrawCUSDT(amount);
|
|
152
|
+
const ownerAfter = await cusdt.balanceOf(owner.address);
|
|
153
|
+
|
|
154
|
+
expect(ownerAfter).to.eq(ownerBefore.add(amount));
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("should allow owner to withdraw CROWD", async () => {
|
|
158
|
+
const { crowd, owner } = getState();
|
|
159
|
+
const amount = toBn("500", 18);
|
|
160
|
+
|
|
161
|
+
const ownerBefore = await crowd.balanceOf(owner.address);
|
|
162
|
+
await swap.connect(owner).withdrawCROWD(amount);
|
|
163
|
+
const ownerAfter = await crowd.balanceOf(owner.address);
|
|
164
|
+
|
|
165
|
+
expect(ownerAfter).to.eq(ownerBefore.add(amount));
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("should NOT allow non-owner to withdraw", async () => {
|
|
169
|
+
const [user] = await getAccounts(5, 1);
|
|
170
|
+
const amount = toBn("1", 18);
|
|
171
|
+
await expect(swap.connect(user).withdrawUSDT(amount)).to.be.revertedWith(
|
|
172
|
+
"Ownable: caller is not the owner",
|
|
173
|
+
);
|
|
174
|
+
await expect(swap.connect(user).withdrawCUSDT(amount)).to.be.revertedWith(
|
|
175
|
+
"Ownable: caller is not the owner",
|
|
176
|
+
);
|
|
177
|
+
await expect(swap.connect(user).withdrawCROWD(amount)).to.be.revertedWith(
|
|
178
|
+
"Ownable: caller is not the owner",
|
|
179
|
+
);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
});
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/* eslint-disable prettier/prettier */
|
|
2
|
+
/* eslint-disable node/no-missing-import */
|
|
3
|
+
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
|
|
4
|
+
import { toBn } from "evm-bn";
|
|
5
|
+
import { ethers, upgrades, network } from "hardhat";
|
|
6
|
+
import { reserveAddress } from "../../constants/rootAddress";
|
|
7
|
+
import {
|
|
8
|
+
Network,
|
|
9
|
+
CROWD,
|
|
10
|
+
NFT,
|
|
11
|
+
NFTGenesis,
|
|
12
|
+
USDT,
|
|
13
|
+
NFTFounder,
|
|
14
|
+
CUSDT,
|
|
15
|
+
} from "../../typechain-types";
|
|
16
|
+
|
|
17
|
+
let valhalla: Network = null as any;
|
|
18
|
+
let crowd: CROWD = null as any;
|
|
19
|
+
let nft: NFT = null as any;
|
|
20
|
+
let cusdt: CUSDT = null as any;
|
|
21
|
+
let accounts: SignerWithAddress[] = [];
|
|
22
|
+
let feeReceiver: SignerWithAddress = null as any;
|
|
23
|
+
let feeReceiver2: SignerWithAddress = null as any;
|
|
24
|
+
let owner: SignerWithAddress = null as any;
|
|
25
|
+
let nftFounder: NFTFounder = null as any;
|
|
26
|
+
|
|
27
|
+
let nftGenesis: NFTGenesis = null as any;
|
|
28
|
+
let usdt: USDT = null as any;
|
|
29
|
+
|
|
30
|
+
const initiateAccounts = async () => {
|
|
31
|
+
const accounts = await ethers.getSigners();
|
|
32
|
+
return accounts;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const createSignerWithInitialBalance = async (
|
|
36
|
+
balance: string | number,
|
|
37
|
+
) => {
|
|
38
|
+
const wallet = ethers.Wallet.createRandom().connect(ethers.provider);
|
|
39
|
+
await network.provider.send("hardhat_setBalance", [
|
|
40
|
+
wallet.address,
|
|
41
|
+
"0x" + Number(toBn("" + balance).toString()).toString(16),
|
|
42
|
+
]);
|
|
43
|
+
return await ethers.getImpersonatedSigner(wallet.address);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const setBalance = async (address: string, amount: string | number) => {
|
|
47
|
+
await network.provider.send("hardhat_setBalance", [
|
|
48
|
+
address,
|
|
49
|
+
"0x" + Number(toBn("" + amount).toString()).toString(16),
|
|
50
|
+
]);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const getAccounts = async (startIndex: number, amount: number = 1) => {
|
|
54
|
+
if (startIndex + amount + 1 > accounts.length) {
|
|
55
|
+
// adjust start index in case its greater
|
|
56
|
+
// than the total length of cached accounts
|
|
57
|
+
startIndex = startIndex >= accounts.length ? accounts.length : startIndex;
|
|
58
|
+
|
|
59
|
+
for (let i = accounts.length - 1; i <= startIndex + amount; i++) {
|
|
60
|
+
const signer = await createSignerWithInitialBalance(1000);
|
|
61
|
+
accounts.push(signer);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return accounts.filter((_, idx) => {
|
|
66
|
+
return idx >= startIndex && idx < startIndex + amount;
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const initialize = async () => {
|
|
71
|
+
await initiateAccounts();
|
|
72
|
+
const _accounts = await getAccounts(0, 15);
|
|
73
|
+
|
|
74
|
+
feeReceiver = await createSignerWithInitialBalance(0);
|
|
75
|
+
feeReceiver2 = await createSignerWithInitialBalance(0);
|
|
76
|
+
|
|
77
|
+
const CROWDContract = await ethers.getContractFactory("CROWD");
|
|
78
|
+
const admin = _accounts[0];
|
|
79
|
+
crowd = await CROWDContract.connect(admin).deploy();
|
|
80
|
+
await crowd.connect(admin).deployed();
|
|
81
|
+
|
|
82
|
+
const USDTContract = await ethers.getContractFactory("USDT");
|
|
83
|
+
usdt = await USDTContract.connect(admin).deploy();
|
|
84
|
+
await usdt.connect(admin).deployed();
|
|
85
|
+
|
|
86
|
+
const CUSDTContract = await ethers.getContractFactory("CUSDT");
|
|
87
|
+
cusdt = await CUSDTContract.connect(admin).deploy();
|
|
88
|
+
await cusdt.connect(admin).deployed();
|
|
89
|
+
|
|
90
|
+
const ValhallaContract = await ethers.getContractFactory("Network");
|
|
91
|
+
valhalla = (await upgrades.deployProxy(
|
|
92
|
+
ValhallaContract,
|
|
93
|
+
[
|
|
94
|
+
admin.address,
|
|
95
|
+
[
|
|
96
|
+
_accounts[0].address,
|
|
97
|
+
_accounts[1].address,
|
|
98
|
+
_accounts[2].address,
|
|
99
|
+
_accounts[3].address,
|
|
100
|
+
_accounts[4].address,
|
|
101
|
+
_accounts[5].address,
|
|
102
|
+
_accounts[6].address,
|
|
103
|
+
_accounts[7].address,
|
|
104
|
+
_accounts[8].address,
|
|
105
|
+
_accounts[9].address,
|
|
106
|
+
_accounts[10].address,
|
|
107
|
+
_accounts[11].address,
|
|
108
|
+
_accounts[12].address,
|
|
109
|
+
_accounts[13].address,
|
|
110
|
+
_accounts[14].address,
|
|
111
|
+
],
|
|
112
|
+
feeReceiver.address,
|
|
113
|
+
feeReceiver2.address,
|
|
114
|
+
reserveAddress,
|
|
115
|
+
0,
|
|
116
|
+
toBn("50", 18), // Registration fee: 50 USDT
|
|
117
|
+
],
|
|
118
|
+
{ kind: "transparent" },
|
|
119
|
+
)) as any;
|
|
120
|
+
await valhalla.connect(admin).deployed();
|
|
121
|
+
|
|
122
|
+
const NFTContract = await ethers.getContractFactory("NFT");
|
|
123
|
+
nft = (await upgrades.deployProxy(
|
|
124
|
+
NFTContract,
|
|
125
|
+
[crowd.address, valhalla.address],
|
|
126
|
+
{ kind: "transparent" },
|
|
127
|
+
)) as any;
|
|
128
|
+
await nft.connect(admin).deployed();
|
|
129
|
+
|
|
130
|
+
const [receiverAddress] = await getAccounts(420, 2);
|
|
131
|
+
const NFTGenesiscontract = await ethers.getContractFactory("NFTGenesis");
|
|
132
|
+
nftGenesis = (await upgrades.deployProxy(
|
|
133
|
+
NFTGenesiscontract,
|
|
134
|
+
[
|
|
135
|
+
crowd.address,
|
|
136
|
+
usdt.address,
|
|
137
|
+
nft.address,
|
|
138
|
+
valhalla.address,
|
|
139
|
+
receiverAddress.address,
|
|
140
|
+
],
|
|
141
|
+
{ kind: "transparent" },
|
|
142
|
+
)) as any;
|
|
143
|
+
await nftGenesis.connect(admin).deployed();
|
|
144
|
+
|
|
145
|
+
const NFTFounderContract = await ethers.getContractFactory("NFTFounder");
|
|
146
|
+
nftFounder = (await upgrades.deployProxy(
|
|
147
|
+
NFTFounderContract,
|
|
148
|
+
[usdt.address, valhalla.address, receiverAddress.address],
|
|
149
|
+
{ kind: "transparent" },
|
|
150
|
+
)) as any;
|
|
151
|
+
await nftFounder.connect(admin).deployed();
|
|
152
|
+
|
|
153
|
+
owner = admin;
|
|
154
|
+
|
|
155
|
+
const tx = await crowd.setMinterContract(nft.address);
|
|
156
|
+
await tx.wait();
|
|
157
|
+
|
|
158
|
+
const tx2 = await valhalla.connect(admin).setNftAddress(nft.address);
|
|
159
|
+
await tx2.wait();
|
|
160
|
+
|
|
161
|
+
const tx3 = await valhalla.connect(admin).setCRWDAddress(crowd.address);
|
|
162
|
+
await tx3.wait();
|
|
163
|
+
|
|
164
|
+
const tx4 = await nft.setNFTGenesis(nftGenesis.address);
|
|
165
|
+
await tx4.wait();
|
|
166
|
+
|
|
167
|
+
// Set USDT as the registration token
|
|
168
|
+
const tx5 = await valhalla.connect(admin).setCUSDAddress(usdt.address);
|
|
169
|
+
await tx5.wait();
|
|
170
|
+
|
|
171
|
+
const tx6 = await valhalla
|
|
172
|
+
.connect(admin)
|
|
173
|
+
.setNFTFounderAddress(nftFounder.address);
|
|
174
|
+
await tx6.wait();
|
|
175
|
+
|
|
176
|
+
const receiverAddress2 = await createSignerWithInitialBalance(0);
|
|
177
|
+
|
|
178
|
+
const tx7 = await nftFounder.setReceiverAddress2(receiverAddress2.address);
|
|
179
|
+
await tx7.wait();
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
export const getState = () => ({
|
|
183
|
+
valhalla,
|
|
184
|
+
usdt,
|
|
185
|
+
crowd,
|
|
186
|
+
nft,
|
|
187
|
+
feeReceiver,
|
|
188
|
+
feeReceiver2,
|
|
189
|
+
nftGenesis,
|
|
190
|
+
owner,
|
|
191
|
+
nftFounder,
|
|
192
|
+
cusdt,
|
|
193
|
+
});
|