Launchpad Sale Contract

IFAllocationSale.sol

This contract is responsible for conducting a token sale based on allocation that is obtained through staking over time via IFAllocationMaster.

Events

event Fund(address indexed sender, uint256 amount);
event SetMinTotalPayment(uint256 indexed minTotalPayment);
event SetSaleTokenAllocationOverride(
    uint256 indexed saleTokenAllocationOverride
);
event SetCasher(address indexed casher);
event SetWhitelistSetter(address indexed whitelistSetter);
event SetWhitelist(bytes32 indexed whitelistRootHash);
event SetWithdrawDelay(uint24 indexed withdrawDelay);
event Purchase(address indexed sender, uint256 indexed paymentAmount);
event Withdraw(address indexed sender, uint256 indexed amount);
event Cash(
    address indexed sender,
    uint256 paymentTokenBalance,
    uint256 saleTokenBalance
);
event EmergencyTokenRetrieve(address indexed sender, uint256 amount);

Public variables

saleAmount

uint256 public saleAmount;

Amount of sale tokens to sell.

This is updated only by depositing sale tokens into this contract via fund.

paymentReceived

mapping(address => uint256) public paymentReceived;

Tracks amount of payment received by each address.

The amount of tokens a user has purchased can be determined with the following calculation:

paymentReceived[user] / salePrice

hasWithdrawn

mapping(address => bool) public hasWithdrawn;

Tracks whether user has already successfully withdrawn.

purchaserCount

uint32 public purchaserCount;

Counter of unique purchasers.

withdrawerCount

uint32 public withdrawerCount;

Counter of unique withdrawers (doesn't count casher).

salePrice

uint256 public salePrice;

Sale price is in units of paymentToken/saleToken with 10^18 decimals (we define this constant as SALE_PRICE_DECIMALS = 10^18).

For example, if selling ABC token for 10 IFUSD each, then sale price will be 10 * 10^18 = 10_000_000_000_000_000_000.

Note: this example assumes that ABC token and IFUSD have the same number of decimals.

If decimals differ, then salePrice must accommodate any differences in decimals between sale and payment tokens. In other words, if payment token has A decimals and sale token has B decimals, then the price must be adjusted by multiplying by 10**(A-B). The following examples illustrates this:

If A was 18 but B was only 12, then the salePrice should be adjusted by multiplying by 1,000,000. If A was 12 and B was 18, then salePrice should be adjusted by dividing by 1,000,000.

funder

address public funder;

The contract funder which adds sale tokens to be sold.

casher (optional)

address public casher;

Optional casher settable by owner.

The owner can cash the sale contract, and if an optional casher is specified, then both owner and casher can cash the sale contract.

whitelistSetter (optional)

address public whitelistSetter;

Optional whitelist setter settable by owner.

The owner can set the whitelist, and if an optional whitelistSetter is specified, then both owner and whitelistSetter can set the whitelist.

paymentToken

ERC20 public paymentToken;

The payment token.

saleToken

ERC20 public saleToken;

The sale token.

allocationMaster

IFAllocationMaster public allocationMaster;

The allocation master.

trackId

uint24 public trackId;

The track on which this sale is conducted.

allocSnapshotBlock

uint80 public allocSnapshotBlock;

The snapshot block to read stake weights from for determining allocations.

startBlock

uint256 public startBlock;

Start block is when sale is active (inclusive).

endBlock

uint256 public endBlock;

End block is when sale is active (inclusive).

minTotalPayment (optional)

uint256 public minTotalPayment;

Min payment for token amount.

maxTotalPayment

uint256 public maxTotalPayment;

Max payment for token amount.

This is an independent constraint from allocation. If a user's allocation determines that they can buy a maximum amount of sale tokens for X payment tokens, then the user's maximum is whichever is smaller, X or maxTotalPayment.

saleTokenAllocationOverride (optional)

uint256 public saleTokenAllocationOverride;

Flat allocation override for all participants.

Non Internal Functions

constructor

    constructor(
        uint256 _salePrice,
        address _funder,
        ERC20 _paymentToken,
        ERC20 _saleToken,
        IFAllocationMaster _allocationMaster,
        uint24 _trackId,
        uint80 _allocSnapshotBlock,
        uint256 _startBlock,
        uint256 _endBlock,
        uint256 _maxTotalPayment
    ) {
        salePrice = _salePrice;
        funder = _funder;
        paymentToken = _paymentToken;
        saleToken = _saleToken;
        allocationMaster = _allocationMaster;
        trackId = _trackId;
        allocSnapshotBlock = _allocSnapshotBlock;
        startBlock = _startBlock;
        endBlock = _endBlock;
        maxTotalPayment = _maxTotalPayment;
    }

fund

function fund(uint256 amount) external onlyFunder

Function for funding sale with sale token (called by project team).

Note that if sale tokens are sent to this contract outside of calling fund, they will not be used in the sale. The variable saleAmount tracks how many sale tokens to sell, and it only increases by calling fund. The purchase / withdraw functions use saleAmount in the denominator when computing the appropriate fractions of tokens purchased / withdrawn to participants.

However, if sale tokens are accidentally sent directly to the contract, the project team can withdraw those tokens using cash at the end of the sale.

Can only be called before sale begins.

setMinTotalPayment

function setMinTotalPayment(uint256 _minTotalPayment) external onlyOwner

Function for owner to set an optional, minimum total payment.

Can only be called before sale begins.

setSaleTokenAllocationOverride

function setSaleTokenAllocationOverride(
        uint256 _saleTokenAllocationOverride
    ) external onlyOwner

Function for owner to set an optional, sale token allocation override.

Can only be called before sale begins.

setCasher

function setCasher(address _casher) external onlyOwner

Function for owner to set an optional, separate casher.

Can only be called before sale begins.

setWhitelistSetter

function setWhitelistSetter(address _whitelistSetter) external onlyOwner

Function for owner to set an optional, separate whitelist setter.

Can only be called before sale begins.

setWhitelist

function setWhitelist(bytes32 _whitelistRootHash)
        external
        onlyWhitelistSetterOrOwner

Function for owner or whitelist setter to set the whitelist.

Can be called any time, even during sale.

setWithdrawDelay

function setWithdrawDelay(uint24 _withdrawDelay) external onlyOwner

Function for owner to set a withdraw delay.

Can be called at any time, even during sale.

checkWhitelist

function checkWhitelist(address user, bytes32[] calldata merkleProof)
    public
    view
    returns (bool)

Function for checking merkle proof against provided user address.

getTotalPaymentAllocation

function getTotalPaymentAllocation(address user)
    public
    view
    returns (uint256)

Function to get the total allocation of a user in allocation sale.

Allocation is calculated via the override if set, and otherwise allocation is calculated by the allocation master data.

getMaxPayment

function getMaxPayment(address user) public view returns (uint256)

Function to get the max remaining amount of allocation for a user (in terms of payment token).

It is whichever is smaller:

  1. user's payment allocation, which is determined by:

    1. the allocation master

    2. the allocation override

  2. maxTotalPayment

purchase

function purchase(uint256 paymentAmount) external

Function for making purchase in allocation sale when there is no whitelist set.

Can only be called during sale period.

whitelistedPurchase

function whitelistedPurchase(
        uint256 paymentAmount,
        bytes32[] calldata merkleProof
    ) external

Function for making purchase in allocation sale when there is a whitelist set.

Can only be called during sale period.

withdraw

function withdraw() external

Function for withdrawing purchased sale token.

Can be called only once and only after sale end.

withdrawGiveaway

function withdrawGiveaway(bytes32[] calldata merkleProof)
        external

Function for withdrawing (redeeming) sale tokens from a zero cost "giveaway" sale.

Can be called only once and only after sale end.

cash

function cash() external onlyCasherOrOwner

Function for funder to cash in payment token and unpurchased sale token.

Can be called only once and only after sale end.

emergencyTokenRetrieve

function emergencyTokenRetrieve(address token) external onlyOwner

Retrieve tokens erroneously sent in to this address.

Can withdraw any token EXCEPT for sale or payment token.

Other useful notes

Computing remaining

There isn't a view function that returns the remaining sale amount, but this can be easily calculated:

  1. Get public variable totalPaymentReceived.

  2. Get public variable salePrice.

  3. Calculate the current amount of sale tokens purchased (say saleTokensPurchased) with paymentReceived*SALE_PRICE_DECIMALS/salePrice.

    1. Keep in mind that salePrice is in units of paymentToken/saleToken with SALE_PRICE_DECIMALS decimals.

  4. Finally, get the amount of remaining sale tokens with saleAmount-saleTokensPurchased.

Last updated