Skip to content
This repository was archived by the owner on Apr 4, 2025. It is now read-only.

Commit 97a9991

Browse files
authored
Merge pull request #35 from code-423n4/feature/deploy-token-sale
add TokenSale to deploy script
2 parents 5d82d35 + bb63d25 commit 97a9991

File tree

3 files changed

+103
-7
lines changed

3 files changed

+103
-7
lines changed

scripts/config.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {BigNumber as BN, constants} from 'ethers';
2-
import {ONE_DAY, ONE_YEAR} from '../test/shared/Constants';
2+
import {ONE_18, ONE_DAY, ONE_YEAR} from '../test/shared/Constants';
33

44
type Config = {
55
FREE_SUPPLY: BN;
@@ -9,9 +9,38 @@ type Config = {
99
VEST_DURATION: number;
1010
MERKLE_ROOT: string;
1111
TIMELOCK_DELAY: number;
12+
TOKEN_SALE_START: number;
13+
TOKEN_SALE_DURATION: number;
14+
TOKEN_SALE_USDC: string;
15+
TOKEN_SALE_ARENA_PRICE: BN;
16+
TOKEN_SALE_RECIPIENT: string;
17+
TOKEN_SALE_WHITELIST: typeof TOKEN_SALE_WHITELIST;
1218
EXPORT_FILENAME: string;
1319
};
1420

21+
const TOKEN_SALE_WHITELIST = [
22+
{buyer: '0x0f4Aeb1847B7F1a735f4a5Af7E8C299b793c1a9A', arenaAmount: BN.from(`10000`).mul(ONE_18)},
23+
{buyer: '0x3Ab0029e1C4515134464b267557cB80A39902699', arenaAmount: BN.from(`10001`).mul(ONE_18)},
24+
{buyer: '0x4F3F7ca7E91D869180EBbA55e4322845a8Dc6862', arenaAmount: BN.from(`10002`).mul(ONE_18)},
25+
{buyer: '0x5dcEb6f4dc5b64Af6271A5Ab3297DbE3C01dd57B', arenaAmount: BN.from(`10003`).mul(ONE_18)},
26+
{buyer: '0x62641eAE546835813B56EC7b544756A532275Dd3', arenaAmount: BN.from(`10004`).mul(ONE_18)},
27+
{buyer: '0x670f9e8B37d5816c2eB93A1D94841C66652a8E26', arenaAmount: BN.from(`10005`).mul(ONE_18)},
28+
{buyer: '0x691Cbab55CC1806d29994784Ba9d9e679c03f164', arenaAmount: BN.from(`10006`).mul(ONE_18)},
29+
{buyer: '0x697ccd97C8419EBba7347CEF03a0CD02804EbF54', arenaAmount: BN.from(`10007`).mul(ONE_18)},
30+
{buyer: '0x6c422839E7EceDb6d2A86F3F2bFd03aDd154Fc27', arenaAmount: BN.from(`10008`).mul(ONE_18)},
31+
{buyer: '0x7C0fb88c87c30eBF70340E25fe47763e53b907cF', arenaAmount: BN.from(`10009`).mul(ONE_18)},
32+
{buyer: '0x8498EAb53e03E3143d77B2303eDBdAC6C9041D33', arenaAmount: BN.from(`10010`).mul(ONE_18)},
33+
{buyer: '0x8D31BAC0870e323354eAF6F98277860772FFB2d4', arenaAmount: BN.from(`10011`).mul(ONE_18)},
34+
{buyer: '0xA432F83d8054F5F859cAcb86574baC5e07DD6529', arenaAmount: BN.from(`10012`).mul(ONE_18)},
35+
{buyer: '0xD3488b8C87416946D82CC957178B0863A1F089b2', arenaAmount: BN.from(`10013`).mul(ONE_18)},
36+
{buyer: '0xD5388291EAbe96b56069440C97046791E2F72573', arenaAmount: BN.from(`10014`).mul(ONE_18)},
37+
{buyer: '0xF20eb7eAf52712EA0Aa80467741f34E6b0dB18F8', arenaAmount: BN.from(`10015`).mul(ONE_18)},
38+
{buyer: '0xa1fA3C686C9c4E5e8407b32B67191B079a65ffD2', arenaAmount: BN.from(`10016`).mul(ONE_18)},
39+
{buyer: '0xbB79597641483Ed96BCE9fc24b4D63F720898b8A', arenaAmount: BN.from(`10017`).mul(ONE_18)},
40+
{buyer: '0xe552C6A88E71B2A5069Dec480507F54321Dc65F3', arenaAmount: BN.from(`10018`).mul(ONE_18)},
41+
{buyer: '0xf4290941dBc8b31c277E30deFF3fC59979FC6757', arenaAmount: BN.from(`10019`).mul(ONE_18)},
42+
];
43+
1544
export const allConfigs: {[key: number]: Config} = {
1645
// rinkeby
1746
4: {
@@ -22,6 +51,12 @@ export const allConfigs: {[key: number]: Config} = {
2251
VEST_DURATION: 4 * ONE_DAY,
2352
MERKLE_ROOT: '0xd97c9a423833d78e0562b8ed2d14752b54e7ef9b52314cafb197e3a339299901',
2453
TIMELOCK_DELAY: 1800, // 30 mins
54+
TOKEN_SALE_START: Math.floor(new Date(`2021-12-27T13:16:00.000Z`).getTime() / 1000),
55+
TOKEN_SALE_DURATION: 14 * ONE_DAY,
56+
TOKEN_SALE_USDC: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', // USDC 6 decimals
57+
TOKEN_SALE_ARENA_PRICE: BN.from(30_000).mul(ONE_18).div(ONE_18), // 0.03 USDC * 1e18 / 1.0 ARENA
58+
TOKEN_SALE_RECIPIENT: '0x670f9e8B37d5816c2eB93A1D94841C66652a8E26', // TODO: change this to real recipient
59+
TOKEN_SALE_WHITELIST,
2560
EXPORT_FILENAME: 'rinkebyAddresses.json',
2661
},
2762
// polygon mainnet
@@ -33,6 +68,12 @@ export const allConfigs: {[key: number]: Config} = {
3368
VEST_DURATION: 4 * ONE_YEAR,
3469
MERKLE_ROOT: '0x0', // TODO: edit value
3570
TIMELOCK_DELAY: 2 * ONE_DAY, // 2 days (same as ENS)
71+
TOKEN_SALE_START: Math.floor(new Date(`2021-12-27T13:50:00.000Z`).getTime() / 1000),
72+
TOKEN_SALE_DURATION: 14 * ONE_DAY,
73+
TOKEN_SALE_USDC: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174',
74+
TOKEN_SALE_ARENA_PRICE: BN.from(30_000).mul(ONE_18).div(ONE_18), // 0.03 USDC * 1e18 / 1.0 ARENA
75+
TOKEN_SALE_RECIPIENT: '0x670f9e8B37d5816c2eB93A1D94841C66652a8E26',
76+
TOKEN_SALE_WHITELIST,
3677
EXPORT_FILENAME: 'polygonAddresses.json',
3778
},
3879
};

scripts/deploy.ts

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {task} from 'hardhat/config';
2+
import {BigNumber as BN} from 'ethers';
23
import {expect} from 'chai';
34
import fs from 'fs';
45

@@ -11,6 +12,8 @@ import {
1112
TimelockController,
1213
ArenaGovernor__factory,
1314
ArenaGovernor,
15+
TokenSale__factory,
16+
TokenSale,
1417
} from '../typechain';
1518

1619
import {allConfigs} from './config';
@@ -20,6 +23,7 @@ let token: ArenaToken;
2023
let revokableTokenLock: RevokableTokenLock;
2124
let timelock: TimelockController;
2225
let governor: ArenaGovernor;
26+
let tokenSale: TokenSale;
2327

2428
// see OZ docs: https://docs.openzeppelin.com/contracts/4.x/api/governance#timelock-roles
2529
const ADMIN_ROLE = '0x5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5';
@@ -77,6 +81,21 @@ task('deploy', 'deploy contracts').setAction(async (taskArgs, hre) => {
7781
await governor.deployed();
7882
console.log(`governor address: ${governor.address}`);
7983

84+
console.log(`deploying tokensale...`);
85+
const TokenSaleFactory = (await hre.ethers.getContractFactory('TokenSale')) as TokenSale__factory;
86+
tokenSale = await TokenSaleFactory.deploy(
87+
config.TOKEN_SALE_USDC,
88+
token.address,
89+
config.TOKEN_SALE_START,
90+
config.TOKEN_SALE_DURATION,
91+
config.TOKEN_SALE_ARENA_PRICE,
92+
config.TOKEN_SALE_RECIPIENT,
93+
revokableTokenLock.address,
94+
config.VEST_DURATION
95+
);
96+
await tokenSale.deployed();
97+
console.log(`tokensale address: ${tokenSale.address}`);
98+
8099
// give governor proposer role
81100
// https://docs.openzeppelin.com/contracts/4.x/api/governance#timelock-proposer
82101
await timelock.grantRole(PROPOSER_ROLE, governor.address);
@@ -92,12 +111,25 @@ task('deploy', 'deploy contracts').setAction(async (taskArgs, hre) => {
92111

93112
// set revoker role in TokenLock to timelock
94113
await revokableTokenLock.setRevoker(timelock.address);
114+
// set token sale in TokenLock
115+
await revokableTokenLock.setTokenSale(tokenSale.address);
95116

96117
// transfer tokenlock admin role to timelock
97118
await revokableTokenLock.transferOwnership(timelock.address);
98119

99-
// transfer all tokens held by deployer to timelock
100-
await token.transfer(timelock.address, config.FREE_SUPPLY);
120+
// set up token sale whitelist
121+
await tokenSale.changeWhiteList(
122+
config.TOKEN_SALE_WHITELIST.map(({buyer}) => buyer),
123+
config.TOKEN_SALE_WHITELIST.map(({arenaAmount}) => arenaAmount)
124+
);
125+
// transfer token sale admin role to timelock
126+
await tokenSale.transferOwnership(timelock.address);
127+
128+
// transfer all tokens held by deployer to token sale and timelock
129+
const TOKEN_SALE_SUPPLY = config.TOKEN_SALE_WHITELIST.reduce((sum, el) => sum.add(el.arenaAmount), BN.from(`0`));
130+
console.log(`transferring ${TOKEN_SALE_SUPPLY.toString()} ARENA to TokenSale. Remaining back to Timelock`);
131+
await token.transfer(tokenSale.address, TOKEN_SALE_SUPPLY);
132+
await token.transfer(timelock.address, config.FREE_SUPPLY.sub(TOKEN_SALE_SUPPLY));
101133

102134
// transfer token admin role to timelock
103135
await token.transferOwnership(timelock.address);
@@ -109,6 +141,7 @@ task('deploy', 'deploy contracts').setAction(async (taskArgs, hre) => {
109141
tokenLock: revokableTokenLock.address,
110142
timelock: timelock.address,
111143
governor: governor.address,
144+
tokenSale: tokenSale.address,
112145
};
113146
let exportJson = JSON.stringify(addressesToExport, null, 2);
114147
fs.writeFileSync(config.EXPORT_FILENAME, exportJson);
@@ -136,6 +169,9 @@ task('deploy', 'deploy contracts').setAction(async (taskArgs, hre) => {
136169
// TokenLock revoker should be timelock
137170
expect(await revokableTokenLock.revoker()).to.be.eq(timelock.address);
138171

172+
// TokenLock token sale should be set
173+
expect(await revokableTokenLock.tokenSale()).to.be.eq(tokenSale.address);
174+
139175
// TokenLock owner should be timelock
140176
expect(await revokableTokenLock.owner()).to.be.eq(timelock.address);
141177

@@ -145,6 +181,11 @@ task('deploy', 'deploy contracts').setAction(async (taskArgs, hre) => {
145181
// check Token's tokenlock has been set
146182
expect(await token.tokenLock()).to.be.eq(revokableTokenLock.address);
147183

184+
// check TokenSale's tokenlock has been set
185+
expect(await tokenSale.tokenLock()).to.be.eq(revokableTokenLock.address);
186+
// Token's owner should be timelock
187+
expect(await tokenSale.owner()).to.be.eq(timelock.address);
188+
148189
/////////////////////////
149190
// CONFIG VERIFICATION //
150191
/////////////////////////
@@ -153,8 +194,11 @@ task('deploy', 'deploy contracts').setAction(async (taskArgs, hre) => {
153194
// check ArenaToken's token balance == AIRDROP_SUPPLY
154195
expect(await token.balanceOf(token.address)).to.be.eq(config.AIRDROP_SUPPLY);
155196

156-
// check timelock's token balance == FREE_SUPPLY
157-
expect(await token.balanceOf(timelock.address)).to.be.eq(config.FREE_SUPPLY);
197+
// check timelock's token balance == TOKEN_SALE_SUPPLY
198+
expect(await token.balanceOf(tokenSale.address)).to.be.eq(TOKEN_SALE_SUPPLY);
199+
200+
// check timelock's token balance == FREE_SUPPLY - TOKEN_SALE_SUPPLY (rest of it)
201+
expect(await token.balanceOf(timelock.address)).to.be.eq(config.FREE_SUPPLY.sub(TOKEN_SALE_SUPPLY));
158202

159203
// check timelock's minDelay
160204
expect(await timelock.getMinDelay()).to.be.eq(config.TIMELOCK_DELAY);

scripts/verify.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ import {task} from 'hardhat/config';
22
import {HardhatRuntimeEnvironment} from 'hardhat/types';
33
import fs from 'fs';
44

5-
import {ArenaToken, RevokableTokenLock, TimelockController, ArenaGovernor} from '../typechain';
5+
import {ArenaToken, RevokableTokenLock, TimelockController, ArenaGovernor, TokenSale} from '../typechain';
66

77
import {allConfigs} from './config';
88

99
let token: ArenaToken;
1010
let revokableTokenLock: RevokableTokenLock;
1111
let timelock: TimelockController;
1212
let governor: ArenaGovernor;
13+
let tokenSale: TokenSale;
1314

1415
task('verifyContracts', 'verify deployed contracts')
1516
.addParam('i', 'JSON file containing exported addresses')
@@ -21,6 +22,7 @@ task('verifyContracts', 'verify deployed contracts')
2122
revokableTokenLock = await hre.ethers.getContractAt('RevokableTokenLock', addresses['tokenLock']);
2223
timelock = await hre.ethers.getContractAt('TimelockController', addresses['timelock']);
2324
governor = await hre.ethers.getContractAt('ArenaGovernor', addresses['governor']);
25+
tokenSale = await hre.ethers.getContractAt('TokenSale', addresses['tokenSale']);
2426

2527
let config = allConfigs[networkId];
2628

@@ -32,10 +34,19 @@ task('verifyContracts', 'verify deployed contracts')
3234
new Date(config.CLAIM_END_DATE).getTime() / 1000,
3335
config.VEST_DURATION,
3436
]);
35-
3637
await verifyContract(hre, revokableTokenLock.address, [token.address, addresses['deployer']]);
3738
await verifyContract(hre, timelock.address, [config.TIMELOCK_DELAY, [], []]);
3839
await verifyContract(hre, governor.address, [token.address, timelock.address]);
40+
await verifyContract(hre, tokenSale.address, [
41+
config.TOKEN_SALE_USDC,
42+
token.address,
43+
config.TOKEN_SALE_START,
44+
config.TOKEN_SALE_DURATION,
45+
config.TOKEN_SALE_ARENA_PRICE,
46+
config.TOKEN_SALE_RECIPIENT,
47+
revokableTokenLock.address,
48+
config.VEST_DURATION,
49+
]);
3950
process.exit(0);
4051
});
4152

0 commit comments

Comments
 (0)