CFP
Contract Reference
CrowdFi Ref

CrowdFi V1 Reference

Git Source (opens in a new tab)

Inherits: Initializable, ReentrancyGuardUpgradeable, IERC20

Author: Fabric Inc.

Each instance of a Crowdfinancing Contract represents a single campaign with a goal of raising funds for a specific purpose. The contract is deployed by the creator through the CrowdFinancingV1Factory contract. The creator specifies the recipient address, the token to use for payments, the minimum and maximum funding goals, the minimum and maximum contribution amounts, and the start and end times. The campaign is deemed successful if the minimum funding goal is met by the end time, or the maximum funding goal is met before the end time. If the campaign is successful funds can be transferred to the recipient address. If the campaign is not successful the funds can be withdrawn by the contributors.

State Variables

TRANSFER_WINDOW

If transfer doesn't occur within the TRANSFER_WINDOW, the campaign can be unlocked and put into a failed state for withdraws. This is to prevent a campaign from being locked forever if the recipient addresses are compromised.

uint256 private constant TRANSFER_WINDOW = 90 days;

MAX_DURATION_SECONDS

Max campaign duration: 90 Days

uint256 private constant MAX_DURATION_SECONDS = 90 days;

MIN_DURATION_SECONDS

Min campaign duration: 30 minutes

uint256 private constant MIN_DURATION_SECONDS = 30 minutes;

PAST_START_TOLERANCE_SECONDS

Allow a campaign to be deployed where the start time is up to one minute in the past

uint256 private constant PAST_START_TOLERANCE_SECONDS = 60;

MAX_FEE_BIPS

Maximum fee basis points (12.5%)

uint16 private constant MAX_FEE_BIPS = 1250;

MAX_BIPS

Maximum basis points

uint16 private constant MAX_BIPS = 10_000;

_state

The current state of the contract

State private _state;

_recipientAddress

The address of the recipient in the event of a successful campaign

address private _recipientAddress;

_token

The token used for funding (optional)

IERC20 private _token;

_goalMin

The minimum funding goal to meet for a successful campaign

uint256 private _goalMin;

_goalMax

The maximum funding goal. If this goal is met, funds can be transferred early

uint256 private _goalMax;

_minContribution

The minimum tokens an account can contribute

uint256 private _minContribution;

_maxContribution

The maximum tokens an account can contribute

uint256 private _maxContribution;

_startTimestamp

The start timestamp for the campaign

uint256 private _startTimestamp;

_endTimestamp

The end timestamp for the campaign

uint256 private _endTimestamp;

_contributionTotal

The total amount contributed by all accounts

uint256 private _contributionTotal;

_withdrawTotal

The total amount withdrawn by all accounts

uint256 private _withdrawTotal;

_contributions

The mapping from account to balance (contributions or transfers)

mapping(address => uint256) private _contributions;

_withdraws

The mapping from account to withdraws

mapping(address => uint256) private _withdraws;

_allowances

ERC20 allowances

mapping(address => mapping(address => uint256)) private _allowances;

_feeRecipient

The optional address of the fee recipient

address private _feeRecipient;

_feeTransferBips

The transfer fee in basis points, sent to the fee recipient upon transfer

uint16 private _feeTransferBips;

_feeYieldBips

The yield fee in basis points, used to dilute the cap table upon transfer

uint16 private _feeYieldBips;

_yieldTotal

Track the number of tokens sent via yield calls

uint256 private _yieldTotal;

_erc20

Flag indicating the contract works with ERC20 tokens rather than ETH

bool private _erc20;

Functions

erc20Only

Guard to gate ERC20 specific functions

modifier erc20Only();

ethOnly

Guard to gate ETH specific functions

modifier ethOnly();

yieldGuard

Guard to ensure yields are allowed

modifier yieldGuard(uint256 amount);

contributionGuard

Guard to ensure contributions are allowed

modifier contributionGuard(uint256 amount);

constructor

This contract is intended for use with proxies, so we prevent direct initialization. This contract will fail to function properly without a proxy

constructor();

initialize

Initialize acts as the constructor, as this contract is intended to work with proxy contracts.

function initialize(
    address recipient,
    uint256 minGoal,
    uint256 maxGoal,
    uint256 minContribution,
    uint256 maxContribution,
    uint256 startTimestamp,
    uint256 endTimestamp,
    address erc20TokenAddr,
    address feeRecipientAddr,
    uint16 feeTransferBips,
    uint16 feeYieldBips
) external initializer;

Parameters

NameTypeDescription
recipientaddressthe address of the recipient, where funds are transferred when conditions are met
minGoaluint256the minimum funding goal for the financing round
maxGoaluint256the maximum funding goal for the financing round
minContributionuint256the minimum initial contribution an account can make
maxContributionuint256the maximum contribution an account can make
startTimestampuint256the UNIX time in seconds denoting when contributions can start
endTimestampuint256the UNIX time in seconds denoting when contributions are no longer allowed
erc20TokenAddraddressthe address of the ERC20 token used for funding, or the 0 address for native token (ETH)
feeRecipientAddraddressthe address of the fee recipient, or the 0 address if no fees are collected
feeTransferBipsuint16the transfer fee in basis points, collected during the transfer call
feeYieldBipsuint16the yield fee in basis points. Dilutes the cap table for the fee recipient.

contributeERC20

Contribute ERC20 tokens into the contract

Events

  • Emits a {Contribution} event
  • Emits a {Transfer} event (ERC20)

Requirements

  • amount must be within range of min and max contribution for account
  • amount must not cause max goal to be exceeded
  • amount must be approved for transfer by the caller
  • contributions must be allowed
  • the contract must be configured to work with ERC20 tokens
function contributeERC20(uint256 amount) external erc20Only nonReentrant;

Parameters

NameTypeDescription
amountuint256the amount of ERC20 tokens to contribute

contributeEth

Contribute ETH into the contract

Events

  • Emits a {Contribution} event
  • Emits a {Transfer} event (ERC20)

Requirements

  • msg.value must be within range of min and max contribution for account
  • msg.value must not cause max goal to be exceeded
  • contributions must be allowed
  • the contract must be configured to work with ETH
function contributeEth() external payable ethOnly;

_addContribution

Add a contribution to the account and update totals

function _addContribution(address account, uint256 amount) private contributionGuard(amount);

Parameters

NameTypeDescription
accountaddressthe account to add the contribution to
amountuint256the amount of the contribution

isContributionAllowed

function isContributionAllowed() public view returns (bool);

Returns

NameTypeDescription
<none>booltrue if contributions are allowed

isTransferAllowed

function isTransferAllowed() public view returns (bool);

Returns

NameTypeDescription
<none>booltrue if the goal was met and funds can be transferred

transferBalanceToRecipient

Transfer funds to the recipient and change the state

Events

Emits a {TransferContributions} event if the target was met and funds transferred

function transferBalanceToRecipient() external;

_allocateYieldFee

Dilutes supply by allocating tokens to the fee collector, allowing for withdraws of yield

function _allocateYieldFee() private returns (uint256);

_calculateTransferFee

Calculates a fee to transfer to the fee collector

function _calculateTransferFee() private view returns (uint256);

isGoalMinMet

function isGoalMinMet() public view returns (bool);

Returns

NameTypeDescription
<none>booltrue if the minimum goal was met

isGoalMaxMet

function isGoalMaxMet() public view returns (bool);

Returns

NameTypeDescription
<none>booltrue if the maximum goal was met

unlockFailedFunds

In the event that a transfer fails due to recipient contract behavior, the campaign can be unlocked (marked as failed) to allow contributors to withdraw their funds. This can only occur if the state of the campaign is FUNDING and the transfer window has expired. Note: Recipient should invoke transferBalanceToRecipient immediately upon success to prevent this function from being callable. This is a safety mechanism to prevent permanent loss of funds.

Events

  • Emits {Fail} event
function unlockFailedFunds() external;

yieldERC20

Yield ERC20 tokens to all campaign token holders in proportion to their token balance

Requirements

  • amount must be greater than 0
  • amount must be approved for transfer for the contract

Events

  • Emits {Payout} event with amount = amount
function yieldERC20(uint256 amount) external erc20Only yieldGuard(amount) nonReentrant;

Parameters

NameTypeDescription
amountuint256the amount of tokens to payout

yieldEth

Yield ETH to all token holders in proportion to their balance

Requirements

  • msg.value must be greater than 0

Events

  • Emits {Payout} event with amount = msg.value
function yieldEth() external payable ethOnly yieldGuard(msg.value) nonReentrant;

_trackYield

Emit a Payout event and increase yield total

function _trackYield(address from, uint256 amount) private;

yieldTotal

function yieldTotal() public view returns (uint256);

Returns

NameTypeDescription
<none>uint256The total amount of tokens/wei paid back by the recipient

withdrawsOf

function withdrawsOf(address account) public view returns (uint256);

Parameters

NameTypeDescription
accountaddressthe address of a contributor or token holder

Returns

NameTypeDescription
<none>uint256The total tokens withdrawn for a given account

isWithdrawAllowed

function isWithdrawAllowed() public view returns (bool);

Returns

NameTypeDescription
<none>booltrue if the contract allows withdraws

_payoutsMadeTo

function _payoutsMadeTo(address account) private view returns (uint256);

Returns

NameTypeDescription
<none>uint256The total amount of tokens paid back to a given contributor

yieldBalanceOf

function yieldBalanceOf(address account) public view returns (uint256);

Parameters

NameTypeDescription
accountaddressthe address of a token holder

Returns

NameTypeDescription
<none>uint256The withdrawable amount of tokens for a given account, attributable to yield

yieldTotalOf

function yieldTotalOf(address account) public view returns (uint256);

Parameters

NameTypeDescription
accountaddressthe address of a contributor

Returns

NameTypeDescription
<none>uint256The total amount of tokens earned by the given account through yield

withdraw

Withdraw all available funds to the caller if withdraws are allowed and the caller has a contribution balance (campaign failed), or a yield balance (campaign succeeded)

Events

  • Emits a {Withdraw} event with amount = the amount withdrawn
  • Emits a {Transfer} event representing a token burn if the campaign failed
function withdraw() external;

_withdrawContribution

Withdraw the initial contribution for the given account

function _withdrawContribution(address account) private;

_withdrawYieldBalance

Withdraw the available yield balance for the given account

function _withdrawYieldBalance(address account) private;

_transferSafe

this contract is not compatible with tokens that rebase

Token transfer function which leverages allowance. Additionally, it accounts for tokens which take fees on transfer. Fetch the balance of this contract before and after transfer, to determine the real amount of tokens transferred.

function _transferSafe(address account, uint256 amount) private returns (uint256);

Returns

NameTypeDescription
<none>uint256The amount of tokens transferred after fees

totalSupply

Contributions mint tokens and increase the total supply

function totalSupply() external view returns (uint256);

balanceOf

function balanceOf(address account) external view returns (uint256);

transfer

function transfer(address to, uint256 amount) external returns (bool);

_transfer

See ERC20._transfer

The primary difference here is that we also need to adjust withdraws to prevent over-withdrawal of yield/contribution

function _transfer(address from, address to, uint256 amount) internal virtual;

allowance

function allowance(address owner, address spender) public view returns (uint256);

approve

function approve(address spender, uint256 amount) external returns (bool);

_spendAllowance

See ERC20._spendAllowance

function _spendAllowance(address owner, address spender, uint256 amount) internal virtual;

_approve

See ERC20._approve

function _approve(address owner, address spender, uint256 amount) internal virtual;

transferFrom

function transferFrom(address from, address to, uint256 amount) external returns (bool);

increaseAllowance

See ERC20.increaseAllowance

function increaseAllowance(address spender, uint256 addedValue) external virtual returns (bool);

decreaseAllowance

See ERC20.decreaseAllowance

function decreaseAllowance(address spender, uint256 subtractedValue) external virtual returns (bool);

contributionRangeFor

The values can be 0, indicating the account is not allowed to contribute. This method is helpful for preflight checks to ensure the amount is within the range.

function contributionRangeFor(address account) external view returns (uint256 min, uint256 max);

Returns

NameTypeDescription
minuint256The minimum contribution for the account
maxuint256The maximum contribution for the account

state

function state() public view returns (State);

Returns

NameTypeDescription
<none>StateThe current state of the campaign

minAllowedContribution

function minAllowedContribution() external view returns (uint256);

Returns

NameTypeDescription
<none>uint256The minimum allowed contribution of ERC20 tokens or WEI

maxAllowedContribution

function maxAllowedContribution() external view returns (uint256);

Returns

NameTypeDescription
<none>uint256The maximum allowed contribution of ERC20 tokens or WEI

startsAt

function startsAt() external view returns (uint256);

Returns

NameTypeDescription
<none>uint256The unix timestamp in seconds when the time window for contribution starts

isStarted

function isStarted() public view returns (bool);

Returns

NameTypeDescription
<none>booltrue if the time window for contribution has started

endsAt

function endsAt() external view returns (uint256);

Returns

NameTypeDescription
<none>uint256The unix timestamp in seconds when the contribution window ends

isEnded

function isEnded() public view returns (bool);

Returns

NameTypeDescription
<none>booltrue if the time window for contribution has closed

recipientAddress

function recipientAddress() external view returns (address);

Returns

NameTypeDescription
<none>addressThe address of the recipient

isEthDenominated

function isEthDenominated() public view returns (bool);

Returns

NameTypeDescription
<none>booltrue if the contract is ETH denominated

erc20Address

function erc20Address() external view returns (address);

Returns

NameTypeDescription
<none>addressThe address of the ERC20 Token, or 0x0 if ETH

goalMin

function goalMin() external view returns (uint256);

Returns

NameTypeDescription
<none>uint256The minimum goal amount as ERC20 tokens or WEI

goalMax

function goalMax() external view returns (uint256);

Returns

NameTypeDescription
<none>uint256The maximum goal amount as ERC20 tokens or WEI

transferFeeBips

function transferFeeBips() external view returns (uint16);

Returns

NameTypeDescription
<none>uint16The transfer fee as basis points

yieldFeeBips

function yieldFeeBips() external view returns (uint16);

Returns

NameTypeDescription
<none>uint16The yield fee as basis points

feeRecipientAddress

function feeRecipientAddress() external view returns (address);

Returns

NameTypeDescription
<none>addressThe address where the fees are transferred to, or 0x0 if no fees are collected

isUnlockAllowed

function isUnlockAllowed() public view returns (bool);

Returns

NameTypeDescription
<none>booltrue if the funds are unlockable, which means the campaign succeeded, but transfer failed to occur within the transfer window

Events

Contribution

Emitted when an account contributes funds to the contract

event Contribution(address indexed account, uint256 numTokens);

Withdraw

Emitted when an account withdraws their initial contribution or yield balance

event Withdraw(address indexed account, uint256 numTokens);

TransferContributions

Emitted when the funds are transferred to the recipient and when fees are transferred to the fee collector, if specified

event TransferContributions(address indexed account, uint256 numTokens);

Fail

Emitted when the campaign is marked as failed

event Fail();

Payout

Emitted when yieldEth or yieldERC20 are called

event Payout(address indexed account, uint256 numTokens);

Enums

State

A state enum to track the current state of the campaign

enum State {
    FUNDING,
    FAILED,
    FUNDED
}