PricedRound

Git Source

Inherits: Credentialed, StateAware, BoardControlled, IPricedRound

Author: Daniel Yamagata

A contract for starting a priced round that emulates traditional priced rounds in Venture Capital financing

Priced rounds are deployed and instantiated by the Equity Financing module. They are deployed via OpenZeppelin Clones (i.e. ERC1167 Minimal Proxies).

Invariants:

  • The number of '_shares' held by this contract is greater than or equal to the number of shares released

  • The amount of the '_denominationAsset' held by this contract is greater than or equal to the amount of the denomination asset released*

State Variables

MIN_INVESTOR_ALLOCATION

uint256 public constant MIN_INVESTOR_ALLOCATION = Constants.MIN_INVESTOR_ALLOCATION;

PricedRoundStorageLocation

bytes32 private constant PricedRoundStorageLocation = 0xa7f784e00080c54058c6c75beccaf3cec18ab4699f88cfccb9370b9797cad800;

Functions

_getPricedRoundStorage

constructor

init

Opens a round and initializes the state variables

Initializer. The shares must be supported by the treasury. Otherwise, this function will revert. The 'controller' of the contract will be the 'initTreasury'

Parameters

Name
Type
Description

initArgs

DataTypes.PricedRoundInitArgs

The arguments to initialize the round with

initShares

address

The shares to use for the round. The shares will be transferred to the Token Timelock or investors when claimed depending on the unlock arguments

initTreasury

address

The treasury to send the raised funds to when the round is closed

__PricedRound_init

unlockStartDate

unlockDuration

Returns the unlock duration in seconds. Shares will be unlocked linearly over this period once claimed via the Token Timelock

unlockCliff

Returns the unlock cliff in seconds. The 'initialUnlock' will be unlocked once this cliff has passed

initialUnlockPercentage

The percentage of shares that will be unlocked initially once the 'unlockCliff' has passed

All percentages are denominated in Constants.PRECISION_FACTOR, which is 100_000

minimumRaise

The minimum amount of funds to be raised for the round for it to be able to be closed. Can be extended with increaseMinimumRaise()

Returns the value in the units of the denomination asset. Must divide by the units on a frontend to get the nominal value

targetRaise

The maximum amount of funds to be raised. Can be extended with extendRound()

Returns the value in the units of the denomination asset. Must divide by the units on a frontend to get the nominal value

shares

The shares used for the round. Transferred to either the Token Timelock or an investor when claimed depending on the unlock arguments

allottedShares

The cumulative amount of shares that have been allotted to investors who have contributed to the round. Allotted shares does not account for allocations

totalSharesForRound

The number of shares allocated to the round

totalRaised

The current total amount of funds raised in the units of the denomination asset

Must divide by the units on a frontend to get the nominal value

denominationAsset

The denomination asset of the round. Used by the investor to invest in the round.

pricePerShare

The price per share of the round in the units of the 'denominationAsset'

Must divide by the units on a frontend to get the nominal value

roundState

Returns the state of the round as a RoundState enum:

  • 0: Open

  • 1: Closed

  • 2: Filled

  • 3: Canceled

increaseMinimumRaise

Increases the minimum raise by 'minimumRaiseExtension'. The new minimum raise must be less than the target raise. This function can only be called if the round is 'Open' or 'Filled'. This function should be used cautiously: the minimum raise cannot be decreased once increased

This function will revert if the sum of 'minimumRaise' and 'minimumRaiseExtension' is greater than type(uint128).max

Parameters

Name
Type
Description

minimumRaiseExtension

uint128

The amount to increase the minimum raise by

extendRound

Cache

This function will revert if the sum of 'targetRaiseExtension' and 'targetRaise' or the sum of 'totalSharesExtensiion' and 'totalSharesForRound' are greater than uint128, which is extremely unlikely

Parameters

Name
Type
Description

targetRaiseExtension

uint128

The amount to increase 'targetRaise' by

totalSharesExtension

uint128

The amount to increase 'totalSharesForRound' by

cancelRound

Cache

A round can only be canceled only if it is 'Open' or 'Filled'

closeRound

Cache

A round can only be closed only if it is 'Open' or 'Filled'. The 'totalRaised' must be greater than or equal to the 'minimumRaise'. Otherwise, this function will revert.

setInvestorAllocation

Delete unnecessary vars that are only used when a round is 'open

Once set, an allocation should not be reset using this function. It should be increased or decreased by 'increaseInvestorAllocation' and 'decreaseInvestorAllocation' accordingly. Otherwise, the investor is capable of frontrunning any changes to their allocation via this function.

Parameters

Name
Type
Description

investor

address

The investor to set the allocation for

allocation

DataTypes.Allocation

The allocation to set, which includes the number of shares and the discount or premium

increaseInvestorShareAllocation

Increases the investor's share allocation by the specified amount. Only callable by the 'owner'

Used to avoid race conditions. Repeated use of 'setInvestorAllocation' could lead to exploitation similar to how a malicious actor can exploit 'approve' in place of 'increaseAllowance' for ERC20s

Parameters

Name
Type
Description

investor

address

The investor to increase the allocation for

amount

uint128

The amount to increase the allocation by

decreaseInvestorShareAllocation

Decreases the investor's share allocation by the specified amount. Only callable by the 'owner'

Used to avoid race conditions.

Parameters

Name
Type
Description

investor

address

The investor to decrease the allocation for

amount

uint128

The amount to decrease the allocation by

investInRound

Transfers the investor's allocation from the investor to this contract. Allots their owed shares accordingly

'expectedDiscountOrPremium' and 'expectedNumberOfShares' is used to avoid frontrunning attacks by the company. Otherwise, the owner can frontrun the investor by calling 'setInvestorAllocation' and increase their premium or number of shares unknowingly

Parameters

Name
Type
Description

expectedNumberOfShares

uint128

The number of shares that the investor is expecting to receive

expectedDiscountOrPremium

int128

The price per share that the investor is expecting to pay

claimShares

Calculate the amount of the denom asset to transfer that it will be negligible to the round. However, it is still accounted for Update state

Shares are claimable only if the round is 'Closed'

Returns

Name
Type
Description

<none>

uint256

The ERC721 token ID for the vesting shares in the Token Timelock. Returns 0 if the shares are not susceptible to vesting

revokeInvestment

Sends the caller's investment back to them, forgoing their shares in the round. The round must be 'Open' or 'Filled'

Will revert if the caller does not have an investment

recoupInvestment

Withdraws the investment of the 'investor' for a 'Canceled' round. This function is callable by anyone. Anyone can recoup an investor's investment on their behalf.

Parameters

Name
Type
Description

investor

address

The investor to recoup the investment for

calculateAdjustedPricePerShare

Returns the adjusted price per share given the 'discountOrPremium'

Parameters

Name
Type
Description

discountOrPremium

int256

The discount or premium to apply to the price per share

remainingShares

Returns the remaining shares that can be allotted to investors

remainingDenominationAsset

Returns the remaining denomination asset that can be raised

getInvestors

Returns all the investors who have contributed to the round

Returns

Name
Type
Description

<none>

address[]

The investors as an address memory array

getAllocatedAddresses

Returns all addresses that currently have an allocation

Returns

Name
Type
Description

<none>

address[]

The addresses as an address memory array

getInvestment

Returns the investment of the 'investor', which includes their investment amount and the shares they are owed

Returns

Name
Type
Description

<none>

DataTypes.Investment

The investment as a DataTypes.Investment struct

getAllocation

Returns the allocation of the 'investor', which includes the number of shares they are allocated and their discount or premium

Returns

Name
Type
Description

<none>

DataTypes.Allocation

The allocation as a DataTypes.Allocation struct

getInvestmentAmountForAllocation

Returns the implied investment amount in the denomination asset for the allocation of the 'investor'

Returns the value in the units of the denomination asset. Must divide by the units on a frontend to get the nominal value

getImpliedOutstandingValuation

Returns the implied pre-money outstanding valuation of the company in the denomination asset

Returns the value in the units of the denomination asset. Must divide by the units on a frontend to get the nominal value

Returns

Name
Type
Description

<none>

uint256

The implied valuation

getImpliedFullyDilutedValuation

Returns the implied pre-money fully diluted valuation of the company in the denomination asset

Returns the value in the units of the denomination asset. Must divide by the units on a frontend to get the nominal value

Returns

Name
Type
Description

<none>

uint256

The implied valuation

coreId

Returns the coreId of the implementation as a bytes32

The core ID is the keccak256 hash of the contract name followed by a version under the following syntax: "mezzanine.coreId.ContractName.vX" For example, the core ID of the 2nd version of the Treasury would be the following: keccak256(abi.encodePacked("mezzanine.coreId.Treasury.v2"))

version

Returns the version of the implementation as a uint256

supportsInterface

See {IERC165-supportsInterface}.

_reverseInvestment

Sends the investor's investment back to them. Updates state accordingly

Returns

Name
Type
Description

<none>

DataTypes.Investment

The investor's investment

_calculateInvestmentAmount

Update state

The 'pricePerShare' is in the denomination asset's units. We make the necessary adjustments here

_calculateAdjustedPricePerShare

'discountOrPremium' implictly converted from a int128 to a int256

_deleteAllocations

Deletes all the allocations

_validateRoundStateOpen

Validates that the round state is 'Open'

_validateRoundStateOpenOrFilled

Validates that the round state is 'Open' or 'Filled'

_validateInitArgs

_validateRaiseAmounts

Validates that the '_minimumRaise' is less than or equal to the '_targetRaise'

_validateDiscountOrPremium

Validates a 'discountOrPremium' value. A negative value is a discount, and a positive value is a premium A discount should never be below 100% (i.e. Constants.PRECISION_FACTOR), while a premium should never be above 500% (i.e. 5 * Constants.PRECISION_FACTOR)

_checkInvariants

Asserts that the cumulative shares and denomination asset deposited is greater than or equal to the cumulative shares and denomination asset released, respectively

Structs

PricedRoundStorage

Last updated