@raisefinance/raisefinance-contracts
v1.0.1
Published
Smart contracts of the https://www.raisefinance.io/ project
Downloads
4
Maintainers
Readme
The raise finance project contracts
Staking
Arbitrum rinkeby
Staking address:
0x984A8746Bacb613F74FDB9934eba00aD911f3Ed3
Verification link: https://goerli-rollup-explorer.arbitrum.io/address/0x984A8746Bacb613F74FDB9934eba00aD911f3Ed3#code
Token address:
0x00eA238b9Afbc808F03a4884CBc88f407489338D
USDC address:
0x9b6D5D0fd667E685D73E18E1b8dB8f6245df0f19
USDT address:
0x7412fc4d2a3c1c24739845b1079B5bb0097AbBE5
DAI address:
0x4B0e8DAfd97679a1e393330F2B112160a97Cee73
zkSync goerli
Staking address:
0x340ef5D99E792aa66f7B3c277e90629C7D4C73B4
Zkscan link: https://explorer.zksync.io/address/0x340ef5D99E792aa66f7B3c277e90629C7D4C73B4
Token address:
0x790c8945225bbf7340d50c89b3F2a0CF95B3eA83
USDC address:
0x9f4fA1E9C27EfB6FE11DADfC4aa5E50d9ad2D426
USDT address:
0x5077B50F1Ed22c7E24eD8F8E10A3898C38cBb3DB
DAI address:
0xCECF7296e03F7753ADc58F39c647C90c5458259A
Multicall address:
0xc3012E740fff4A3034cBDA8e2bCC7B5b5d6D93CE
This contract contains sushi-like staking implementation architecture described bellow in the architecture section. User can stake RAISE tokens or other and get a staking reward. A tier is acqired to user based on staked raise tokens.
Useful commands:
Arbitrum rinkeby
- Deploy to testnet and verify:
npx hardhat deployStaking --token-addr 0x00eA238b9Afbc808F03a4884CBc88f407489338D --raise-per-block 1.0 --network arbitrumTestnet
- Deploy token to testnet:
npx hardhat deployToken --network arbitrumTestnet
- Change owner:
npx hardhat changeStakingOwnerAddr --staking-addr 0x984A8746Bacb613F74FDB9934eba00aD911f3Ed3 --new-owner-addr 0x838aec1c2565a5D660BB7F0C540d2632A40B0d5b --network arbitrumTestnet
- Fund service:
npx hardhat fund --staking-addr 0x984A8746Bacb613F74FDB9934eba00aD911f3Ed3 --token-addr 0x00eA238b9Afbc808F03a4884CBc88f407489338D --amount 100000.0 --network arbitrumTestnet
zkSync goerli
- Deploy to testnet:
ZK=1 npx hardhat deployStaking --token-addr 0x790c8945225bbf7340d50c89b3F2a0CF95B3eA83 --raise-per-block 0.25 --network zkSyncTest
- Deploy token to testnet:
ZK=1 npx hardhat deployToken --network zkSyncTest
- Change owner:
ZK=1 npx hardhat changeStakingOwnerAddr --staking-addr 0x340ef5D99E792aa66f7B3c277e90629C7D4C73B4 --new-owner-addr 0x838aec1c2565a5D660BB7F0C540d2632A40B0d5b --network zkSyncTest
- Fund service:
ZK=1 npx hardhat fund --staking-addr 0x340ef5D99E792aa66f7B3c277e90629C7D4C73B4 --token-addr 0x790c8945225bbf7340d50c89b3F2a0CF95B3eA83 --amount 100000.0 --network zkSyncTest
- Stake:
ZK=1 npx hardhat stake --staking-addr 0x340ef5D99E792aa66f7B3c277e90629C7D4C73B4 --pool-id 0 --staking-time 0 --amount "1.0" --token-addr 0xc3012E740fff4A3034cBDA8e2bCC7B5b5d6D93CE --network zkSyncTest
Usage tips
Constructor
constructor(address raiseTokenAddr, uint256 raisePerBlock_)
Receives address of the raise token and raise per block amount (like 0xD9a724b34DAa4EC1bC1e5585D608064C5Ad628c9, 5000000000000000000)
Public functions
function createPool(uint256 allocPoints_, address tokenAddr) public onlyOwner
Can be used to create a staking pool for new token. allocPoints_ is a share of the pool in the service reward distribution.
function stake(uint256 poolId, uint256 amount, StakingTime time) public whenNotPaused
Use it to stake amount tokens for a time. Pool id determines the token to stake. If user have any pending staking reward, it is withdrawn to the user Pool id is zero if you're staking RAISE tokens.
StakingTime variants:
- 0 for a month
- 1 for three months
- 2 for six months
- 3 for a year
Emits >
event Staked(address indexed user, uint256 indexed poolId, uint256 amount, uint256 reward, StakingTime time);
>event Claimed(address indexed user, uint256 indexed poolId, uint256 reward);
If collected reward autoclaimed to user >event TierObtained(address user, Tier tier)
if tier changedfunction updatePool(uint256 poolId) public whenNotPaused
Updates the current pool accRaisePerShare / lastRewardBlock (see architecture section). Mostly for internal use.
function unstake(uint256 poolId, uint256 amount) public whenNotPaused
If called before staking deadline, user will pay a stake penalty If user have any pending staking reward, it is withdrawn to the user Pool id is zero if you're staking RAISE tokens. Emits >
event Unstaked(address indexed user, uint256 indexed poolId, uint256 amount, uint256 reward)
>event Claimed(address indexed user, uint256 indexed poolId, uint256 reward);
If collected reward autoclaimed to userfunction claim(uint256 poolId) public whenNotPaused
Withdraws pending staking reward to the user. Pool id is zero if you're staking RAISE tokens. Emits >
event Claimed(address indexed user, uint256 indexed poolId, uint256 reward);
function fund(uint256 amount) public
Use it to transfer RAISE token to the service reward pool Emits >
event Funded(address indexed user, uint256 amount);
function withdraw(uint256 amount) public onlyOwner
Use it to transfer RAISE token from the service reward pool Emits >
event Withdrawed(address indexed user, uint256 amount);
function setAllocPoints(uint256 poolId, uint256 allocPoints) public onlyOwner
Use it to change weight of the pool in the reward distribution.
function setPenaltyPercent(uint8 penaltyPercent_) public onlyOwner
Use it to set the penalty percent for early unstake
function setRaisePerBlock(uint256 newRaisePerBlock) public onlyOwner
Changes the raise per block
View functions
function getUserInfo(address user)
public view
returns (
uint256 userTickets,
Tier tier,
uint256 stake_,
uint256 deadline,
uint8 allocationBonusPercent,
uint256 stakedAt
)
Returns user tickets amount, tier, stake, stake deadline and allocation bonus percent
Tier variants:
- 0 is None
- 1 is Fan
- 2 is Merchant
- 3 is Dealer
- 4 is Broker
- 5 is Tycoon
function getUserStakeInfo(uint256 poolId, address user) public view returns (uint256 amount, uint256 deadline)
Returns stake amount and deadline for user in selected pool Pool id is 0 for RAISE staking info
function getPendingReward(uint256 poolId, address user) public view returns (uint256 pendingReward)
Returns pending RAISE tokens reward for user in a pool.
function getStakerLotteryInfos(address[] calldata registeredUsers) public view returns (StakerLotteryInfo[] memory userInfos) {
Returns registered users info for the lottery
struct StakerLotteryInfo {
uint256 tickets;
uint256 stakedAt;
address user;
uint8 allocationBonusPercent;
uint8 tier;
}
function getStakedTokenAmount(uint256 poolId) public view returns (uint256)
Returns pool balance
function getTierByStakingAmount(uint256 amount) public view returns (Tier tier)
Returns what tier will user achieve if staked amount tokens
function getPeriodDuration(StakingTime time) public pure returns (uint256 period)
Convers StakingTime the actual duration in seconds
function getAllocationBonusPercentByTime(StakingTime time) public pure returns (uint8)
Convers StakingTime the actual duration in seconds
Design solutions
The staking collects reward each block passed. The reward distrubutes between pools based on their weights - allocation points. Inside pool reward distributes between users according to user stake shares.
Let's consider distrubution inside a poll if we have only one pool and user ustaked after his deadline
Test cases
If we have more than one pool the situation would be the same but block reward per pool will be less. It be distrubuted according to the pool allocation points.
Implementation notes
Each pool stores accRaisePerShare which is updated every user stake / unstake. Let's consider a pool balance over time
If we define accRaisePerShareExpression as
where
If we multiply accRaisePerShareExpression by current user balance, the result would be
What is exactly the reward earned by the user in the total period. We store also rewardDebt for user in a pool which is the the paid reward
But when we divide period1 / balance1 we actually divide the smaller number by the larger one. So we need some kind of fractional numbers. For such reason we will multiply terms by a big number, 1e12
in our case to implement something like fixed point arithmetics. So
accRaisePerShare is
So the user reward in the total period can be calculated as totalUserReward = userBalance * accRaisePerShare / 1e12
. Pending reward can be calculated as totalUserReward - rewardDebt
. Where rewardDebt is already paid to user sum
We need to store users of a specific tier to return them for lottery distribution. So mapping(Tier => address[]) public tierStakers
stores stakers by tier. But what happens if user tier changed. We need to remove user old tier info and set the new one. So we need to know user tier info index in tierStakers
for a specific user. mapping(address => uint256) public tierStakerPositions
is used for this reason. But what if we want to remove old user info from the middle of tierStakers
. We need to shift the whole right part of the array to remove the gap which is expensive. Or we can to just write zero address to the gap and fill it with a new value later. mapping(Tier => uint256[]) public tierStakerGaps
used to save gaps from removed user information and reuse the gaps if new user gets the tier.
Sale factory
Arbitrum rinkeby
- Factory address:
0xaE8e3813849be020dBE84dC447BC3A818D0e3430
- Verification link: https://testnet.arbiscan.io/address/0xaE8e3813849be020dBE84dC447BC3A818D0e3430#code
zkSync goerli
- Factory address:
0x47ff15E1843aaC779eE88B54aDEce1b71f4761Ee
- Zkscan link: https://zksync2-testnet.zkscan.io/address/0x61e14227eF05dA292536BA907d2fefbC878F2985#code
Contains ERC1967Proxy - based factory pattern implementation. Use it to create new sales. Can be used to create ERC20 and ERC1155 sales
Useful commands:
Deploy to arbitrium and verify:
npx hardhat deploySaleFactory --network arbitrumTestnet
Deploy to zkSync:
ZK=1 npx hardhat deploySaleFactory --network zkSyncTest
Deploy project token to arbitrum:
npx hardhat deployToken --network arbitrumTestnet
Deploy pay token to arbitrum:
npx hardhat deployUSDC --network arbitrumTestnet
Deploy project token to zkSync:
ZK=1 npx hardhat deployToken --network zkSyncTest
Deploy pay token to zkSync:
ZK=1 npx hardhat deployUSDC --network zkSyncTest
Change sale factory owner on zkSync:
ZK=1 npx hardhat changeSaleFactoryOwnerAddr --factory-addr 0x47ff15E1843aaC779eE88B54aDEce1b71f4761Ee --new-owner-addr 0x838aec1c2565a5D660BB7F0C540d2632A40B0d5b --network zkSyncTest
Create ERC20 sale on zkSync:
npx hardhat createSale --factory-addr 0x47ff15E1843aaC779eE88B54aDEce1b71f4761Ee --sale-owner 0x2836eC28C32E232280F984d3980BA4e05d6BF68f --sale-type 0 --pay-token-addr 0xBF3d47F19A98D3A1b7c6aa75f88F22C3FE873d41 --project-token-addr 0x17CC238aff7E7a1b4E6Ac431861c5CAC35F0b227 --minimum-amount-to-fund 10000 --is-withdraw-vesting-enabled 0 --service-fee-percent 10 --network zkSyncTest
Create ERC20 sale on arbitrum:
npx hardhat createSale --factory-addr 0xaE8e3813849be020dBE84dC447BC3A818D0e3430 --sale-owner 0x2836eC28C32E232280F984d3980BA4e05d6BF68f --sale-type 0 --pay-token-addr 0x14609E0f716B091b82625A2be6420bb47680C04a --project-token-addr 0x0A1F75C64179a7Ffd41650b8B8567BC4846DB3E6 --minimum-amount-to-fund 10000 --is-withdraw-vesting-enabled 0 --service-fee-percent 10 --network arbitrumTestnet
Fund ERC20 sale on arbitrum:
npx hardhat fundSale --sale-addr 0xAA4A16940D602B98244a0200567CF5658DCFeE42 --token-addr 0x0A1F75C64179a7Ffd41650b8B8567BC4846DB3E6 --token-amount 1000000 --network arbitrumTestnet
Create new round on arbitrum:
npx hardhat createRound --sale-addr 0x4048DF6440bf08893Bc3247cC3BebC2E52457e3C --required-tier 3 --max-allocation 100000 --max-allocation-per-user 8000 --period-seconds 604800 --token-price 400000 --whitelist 0x838aec1c2565a5D660BB7F0C540d2632A40B0d5b --allocation-bonuses 10 --network arbitrumTestnet
Create new round on zkSync:
npx hardhat createRound --sale-addr 0x1c80c72b7D1BCd6fE3188e68ccb48EF58E244204 --required-tier 3 --max-allocation 100000 --max-allocation-per-user 8000 --period-seconds 604800 --token-price 400000 --whitelist 0x838aec1c2565a5D660BB7F0C540d2632A40B0d5b,0x2836eC28C32E232280F984d3980BA4e05d6BF68f --allocation-bonuses 10,20 --network zkSyncTest
Buy in round on zkSync:
npx hardhat buy --sale-addr 0x1c80c72b7D1BCd6fE3188e68ccb48EF58E244204 --pay-token-amount 0.1 --pay-token-addr 0x2AfE245FF33976b13C325F6321f315b081609DE1 --whitelist 0x838aec1c2565a5D660BB7F0C540d2632A40B0d5b,0x2836eC28C32E232280F984d3980BA4e05d6BF68f --allocation-bonuses 10,20 --network zkSyncTest
Stop round:
npx hardhat stopRound --sale-addr 0xcAfFb55Bf736622Ec06acB487356874D48978880 --network arbitrumTestnet
Run lottery:
ZK=1 npx hardhat lottery --staking-addr 0x370985919C756677411114b054800D4D6Cb9B01b --tier 2 --users-to-choose-num 500 --registered-users 0x2836eC28C32E232280F984d3980BA4e05d6BF68f,0x838aec1c2565a5D660BB7F0C540d2632A40B0d5b --network zkSyncTest
Usage tips
Constructor
constructor(address erc20SaleContractAddr_, address erc1155SaleContractAddr_)
Receives address of the erc20 sale contract, etc1155 sale contract, staking contract addresses
Public functions
function createSale(
address saleOwner,
SaleType saleType,
address payTokenAddr,
address projectTokenAddr,
uint256 minimumAmountToFund,
bool isWithdrawVestingEnabled,
uint8 serviceFeePercent
)
public onlyOwner
Use it to create a new sale for an integradet project or RAISE token sale saleOwner – address of the integrated project owner saleType type of the sale, ERC20 / ERC1155 payTokenAddr - address of the token in which we accept donations projectTokenAddr - address of the project project token minimumAmountToFund - minimum amount to fund in project token isWithdrawVestingEnabled – is withdraw vesting can be set by raise admin for project owner serviceFeePercent – service fee percent
SaleType variants
- 0 is ERC20
- 1 is ERC1155
function updateSaleContract(SaleType saleType, address newSaleContractAddr) public onlyOwner
Use it to change sale contract address of the desired type
View functions
function getTotalSalesNum() public view returns (uint256)
Use it to get the total amount of sales. ERC20 + ERC1155
function getSalesNum(SaleType saleTye) public view returns (uint256)
Use it to get the amount of sales of the specific sale type.
SaleType variants
- 0 is ERC20
- 1 is ERC1155
function getSales(SaleType saleTye) public view returns (address[] memory)
Use it to get the sales addresses of a specific sale type
function isCreatedByFactory(address saleAddress_) public view returns (bool)
Returns is sale created by the factory
Sale ERC20
Arbitrum rinkeby
- Actual address:
0x6cB5368d2fDcCb9Dd3254e0A941Ad74Ac4cA9A8f
- Verification link: https://testnet.arbiscan.io/address/0x6cB5368d2fDcCb9Dd3254e0A941Ad74Ac4cA9A8f#code
zkSync goerli
- Actual address:
0xAd451F0bAf2a6aA7d8b3F97a4Bd504A9E8A78594
- Zkscan link: https://explorer.zksync.io//address/0xAd451F0bAf2a6aA7d8b3F97a4Bd504A9E8A78594
This contract contains token sale implementation. Users who staked tokens and acquired tiers can participate in sale. Users get allocation bonus and chances to participate based on their tiers and staking times.
There are two main persons of the sale: sale owner and raise admin. Sale owner is an integrated project representative. He manages the sale while there are no emergency situations. If there are any problems with the project, raise admin can manage the situation by setting withdraw vesting or marking project as an unhealthy.
First sale owner can optionally set up a vesting schedule by calling.
function setVestingSchedule(uint256[] calldata claimTimes_, uint8[] calldata claimPercents_)
Example params
setVestingSchedule(
[
new Date("2022.08.20").getTime() / 1000,
new Date("2022.08.21").getTime() / 1000,
new Date("2022.08.23").getTime() / 1000,
],
[20, 30, 50]
);
It means users can withdraw 20% of his bought tokens on 2022.08.20, next 30% on 2022.08.21, and the final half on 2022.08.23
By default schedule params are ([block.timestamp], [100])
which means that users can withdraw tokens immediately after buying
Then owner can create round by calling
function createRound (
Tier requiredTier,
uint256 maxAllocation, // In project tokens
uint256 maxAllocationPerUser, // In project tokens
uint256 periodSeconds,
uint256 tokenPrice, // tokenPrice = 10.0 => 1 projectToken = 10 usdt
bytes32 userInfoRoot
)
Usage tips
Public functions
function fund(uint256 amount) public
Use it to add tokens to be sold Emits
event Funded(address indexed user, uint256 amount)
function withdraw() public onlySaleOwner
Function for sale owner to withdraw tokens that was not sold after sale ended Emits
event Withdrawed(address indexed user, uint256 amount)
function emergencyWithdraw() public onlyRaiseAdmin ifProjectUnhealthy
Use it to emergency withdraw tokens to be sold from the service. Project needs to be marked as an unhealthy first Emits
event Withdrawed(address indexed user, uint256 amount)
function createRound (
Tier requiredTier,
uint256 maxAllocation, // In project tokens
uint256 maxAllocationPerUser, // In project tokens
uint256 periodSeconds,
uint256 tokenPrice, // tokenPrice = 10.0 => 1 projectToken = 10 usdt
bool isFinal, // Is final round
bytes32 userInfoRoot
)
public onlySaleOwner
Use it to create a round Sale supports only one ongoing round at the samw time so you need to stop previous round to launch a new one. Or wait for it's deadline. Only whitelisted users can participate the round Max allocation - amount of project tokens users can buy during the round Max allocation per user - amount of project tokens one user can buy during the round
Emits
event RoundStarted(uint256 indexed id, Tier indexed requiredTier, uint256 deadline)
function stopRound(uint256 roundId) public onlySaleOwner
Use it to finish a round by id
function buy(uint256 payTokenAmount, uint8 allocationBonusPercent, bytes32[] memory proof) public whenNotPaused onlyHealthy
Users call this functions to buy project tokens for pay tokens. Users pass here their allocation bonus and merkle proof Emits
event Bought(address indexed user, uint256 amount);
function claim() public whenNotPaused
Users call this function to claim bought tokens Emits
event Claimed(address indexed user, uint256 amount);
function withdrawRaisedFunds() public onlySaleOwner onlyHealthy
Use it to witdraw the tokens paid by users Emits
event RaisedFundsWithdrawn(uint256 amount, uint256 actualAmount, uint256 fee);
function emergencyWithdrawRaisedFunds() public onlyRaiseAdmin ifProjectUnhealthy
Use it to witdraw raised funds from the service in case of emergency Emits
event RaisedFundsWithdrawnEmergency(uint256 amount);
function setVestingSchedule(uint256[] calldata claimTimes_, uint8[] calldata claimPercents_) public onlySaleOwner
Can be used to set vesting schedule Example params
setVestingSchedule( [ new Date("2022.08.20").getTime() / 1000, new Date("2022.08.21").getTime() / 1000, new Date("2022.08.23").getTime() / 1000, ], [20, 30, 50] );
This means users can withdraw 20% of his bought tokens on 2022.08.20, next 30% on 2022.08.21, and the final half on 2022.08.23 Default schedule params are
([block.timestamp], [100])
which means that users can withdraw tokens immediately after buyingfunction setWithdrawScheduleForSaleOwner(uint256[] calldata withdrawTimes, uint8[] calldata withdrawPercents) public onlyRaiseAdmin ifWithdrawVestingEnabled
Use it to set vesting schedule for owner of the project
function shiftVestingSchedule(uint256 secondsToShift) public onlySaleOwner
Use it to shift the already set schedule. Can be used to restart vesting every month / year
function shiftSaleOwnerWithdrawSchedule(uint256 secondsToShift) public onlyRaiseAdmin ifWithdrawVestingEnabled
Use it to shift the already set withdraw schedule for sale owner
function setIsUnhealthy() public onlyRaiseAdmin
Use it to mark project unhealthy
Constructor
function initialize( address raiseAdmin_, address saleOwner_, address payTokenAddr, address projectTokenAddr, uint256 minimumAmountToFund_, bool isWithdrawVestingEnabled_, uint8 serviceFeePercent_ ) public initializer
Please do not initialize sale contract directly. Use contract factory
View functions
function getOngoingRound() public view returns (Round memory)
Returns ongoing round if launched
function getOngoingRoundInfo()
public view
returns (
uint256 id,
uint256 deadline,
Tier requiredTier,
uint256 tokenPrice,
uint256 maxAllocation,
uint256 maxAllocationPerUser
)
Like previous one buy more handy for frontend I guess
function canParticipate(address user) public view returns (bool)
Is user can participate in the ongoing round
function getClaimInfo(address user)
public view
returns (
uint256 amountToClaim,
uint256[] memory claimTimes_,
uint8[] memory claimPercents_
)
Returns amount to claim for user, claim times and claim percents
function totalRaised() public view returns (uint256)
Returns amount of pay tokens users paid
Design solutions
Sale intended to hold an integrated project sale. Sale containst multiple rounds, one round per users of one tier. Admin passes whitelist and allocation bonuses for a round. During the round whiltlisted users can buy project tokens for pay tokens. Users can claim their bought tokens according to the vesting schedule. Is withdraw vesting is enabled, project owner can withdraw raised funds only according to the schedule raise admin set. If something goes wrong with the project, we mark it as an unhealthy and forbid owner to witdraw funds. Also in such case users are allowed to get their funds back by calling the refund
function. Then in the end raise admin can call emergencyWithdrawRaisedFunds
function to get the remaining funds
How claiming and vesting schedule works. User intended to buy some token up to limit. We know how much tokens user bought and witdrawed during the sale. So we can calculate the percent withdrawn. We also know what percent to give to user by looking up the vesting schedule. If percent to give is greater the percent withdrawn, we'll give the difference
Sale ERC1155
Arbitrum rinkeby
- Actual address:
0x5be4eeAc2F29477d0078C14F4D2727637529c69B
- Verification link: https://testnet.arbiscan.io/address/0x5be4eeAc2F29477d0078C14F4D2727637529c69B#code
zkSync goerli
- Actual address:
0xee51adc032327839F4D0Cb1e3EBA4BAe05ea7f82
- Zkscan link: https://zksync2-testnet.zkscan.io/address/0xee51adc032327839F4D0Cb1e3EBA4BAe05ea7f82#code
Multicall
zkSync goerli
- Actual address:
0xc3012E740fff4A3034cBDA8e2bCC7B5b5d6D93CE
- Zkscan link: https://zksync2-testnet.zkscan.io/address/0xc3012E740fff4A3034cBDA8e2bCC7B5b5d6D93CE#code
Useful commands:
- Deploy multicall on zkSync:
ZK=1 npx hardhat deployMulticall --network zkSyncTest
Tests
Lottery tests
✔ If there is only one user, lottery will choose him (224ms)
✔ If we want to choose ten users out of ten, all the users will be choosen (591ms)
✔ If we want to choose 5 users out of 15, only 5 will be choosen for fan, merchant and dealer tiers with no duplicates (450ms)
✔ If we want to choose 5 users out of 15, statistical distribution will be correct (156ms)
Sale ERC20
✔ Check only sale owner can withdraw (52ms)
✔ Test that sale owner can't withdraw until end round ended
✔ Test funding and withdrawing (146ms)
✔ Test that impossible to create a round after final one (67ms)
✔ Test that user can't create round if service is not funded (71ms)
✔ Test round creation
✔ Check that admin can't create round if there an active one
✔ Check that admin create a new round if stopped current one (41ms)
✔ Check that regular user or sale owner can't create round
✔ Test that not whitelisted users can't participate event (56ms)
✔ Test that user can't buy more than user allocation (64ms)
✔ Test buying (84ms)
✔ Test project token withdrawing (72ms)
✔ Test raised withdrawing (78ms)
✔ Test that it's impossible to withdraw twice (83ms)
✔ Test withdrawing from multiple rounds (114ms)
✔ Test user can't withdraw more than round allocation (66ms)
✔ Test user can buy tokens regarding his allocation bonus (51ms)
✔ Test user can't buy more tokens then he can regarding the allocation bonus (48ms)
✔ Test ownership transferring
✔ Test claiming in case round time is default (70ms)
✔ Test that double claim is impossible (70ms)
✔ Test claiming with custom vesting schedule (250ms)
✔ Test claiming with custom vesting schedule shifting (210ms)
✔ Test incorrect vesting params
✔ Check ongoing round (74ms)
✔ Test pausing (47ms)
✔ Test allocation bonus and whitelisting correctness (91ms)
✔ Check raise admin can't withdraw if project is healthy
✔ Check raise admin transferring
✔ Check user can't buy and owner can't withdraw profit if project is unhealthy
✔ Check admin can't set or shit withdraw vesting if it's not enabled
✔ Test withdrawing with withdraw schedule (188ms)
✔ Test withdrawing with shifted withdraw schedule (176ms)
✔ Test emergency profit withdrawing by admin (158ms)
✔ Test incorrect withdraw vesting params (40ms)
✔ Test refunding (108ms)
✔ Test refunding case if first user donated 2/3, second 1/3. Owner withdrawn 1/2 (187ms)
✔ Test service fee changing
✔ Check admin can withdraw tokens project tokens if project is unhealthy (42ms)
✔ Test usdc sale with price 100 (89ms)
✔ Test usdc sale with price 0.01 (92ms)
✔ Test token price can't be zero
✔ Test user can't buy if sum is too low (53ms)
✔ Check sale is finished after if final round is stopped (71ms)
Sale factory
✔ Test erc20 sale creation (62ms)
✔ Check that it's impossible to create a valid sale with invalid implementation
✔ Test sale amount correctness (769ms)
✔ Test sale pagination (558ms)
✔ Check is created by factory correctness (59ms)
✔ Test that only owner can create sale
✔ Test that only owner can update sale contract
Staking
✔ Test platform initialization
✔ Test period calculation
✔ Test tier by staking amount calculation (56ms)
✔ Test tier by staking amount calculation if required stake changed (75ms)
✔ Test allocation bonus calculation
✔ Test staking (79ms)
✔ Test consequential tier achieving (230ms)
✔ Check that user can't stake zero tokens
✔ Test unstaking without penalty (79ms)
✔ Test that user can't unstake twice (76ms)
✔ Test emergency unstaking without penalty (57ms)
✔ Test user can't emergency unstaking if he has no stake
✔ Test unstaking without penalty if user has fan tier (67ms)
✔ Test unstaking with penalty (91ms)
✔ Test emergency unstaking with penalty (74ms)
✔ Check that user can't unstake more than he have
✔ Test reward withdrawing after second stake (75ms)
✔ Test claiming (68ms)
✔ Check that user can't claim is service has no funds (77ms)
✔ Test ticket calculation (162ms)
✔ Test stakers info fetching (261ms)
✔ Test null address info fetching
✔ Test ticket info collection (225ms)
✔ Test case if service has no funds (146ms)
✔ Check that admin can't create two pools for the same token
✔ Check pool reward distribution (123ms)
✔ Test staking pausing (102ms)
✔ Test double unstaking (181ms)
✔ Test money withdrawing
✔ Test user can't withdraw more money than service have
✔ Test staked token amount correctness (72ms)
✔ Test staking with lower deadline (74ms)
✔ Check zero pending reward in non-zero pools
✔ Check raise per block changing
✔ Check raise per block and allocation points changing
✔ Test that only owner can create a new pool
✔ Test that only owner can withdraw
✔ Test that only owner can set allocation points
✔ Test that only owner can set prenalty percent
✔ Test that only owner can set raise per block
✔ Test that only owner can set required stake for tier
✔ Test that only owner can pause the contract
✔ Test that only owner can unpause the contract
99 passing (52s)
| File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines | |-------------------------------|----------|----------|----------|----------|----------------| | SaleERC20.sol | 100 | 97.26 | 100 | 100 | | | SaleFactory.sol | 100 | 100 | 100 | 100 | | | Staking.sol | 99.23 | 97.32 | 100 | 99.42 | 134 | | Tier.sol | 100 | 100 | 100 | 100 | |