🦺Unlocker

The unlocker handles token unlocking and distribution for TokenTable.

Events

PresetCreated

event PresetCreated(bytes32 presetId);

ActualCreated

event ActualCreated(bytes32 presetId, uint256 actualId);

TokenDeposited

event TokensDeposited(uint256 actualId, uint256 amount);

TokensClaimed

event TokensClaimed(
    uint256 actualId,
    address caller,
    address to,
    uint256 amount
);

TokensWithdrawn

event TokensWithdrawn(uint256 actualId, address by, uint256 amount);

ActualCancelled

event ActualCancelled(
    uint256 actualId,
    uint256 amountUnlockedLeftover,
    uint256 amountRefunded,
    address refundFounderAddress
);

Functions

BIPS_PRECISION

uint256 public constant BIPS_PRECISION = 10 ** 4; // down to 0.01%

deployer

TTUDeployer public deployer;

futureToken

ITTFutureTokenV2 public futureToken;

accessControlDelegate

ITTAccessControlDelegate public accessControlDelegate;

unlockingScheduleActuals

mapping(uint256 => UnlockingScheduleActual) public unlockingScheduleActuals;

amountUnlockedLeftoverForActuals

Keeps track of the unlocked amount leftover to claim when an actual is cancelled.

mapping(uint256 => UnlockingScheduleActual) public unlockingScheduleActuals;

createPreset

/**
 * @notice Creates an unlocking schedule preset template.
 * @dev Emits: PresetCreated. Throws: PresetExists, InvalidPresetFormat,
 * NotPermissioned.
 * - Only callable by the owner if no access control delegate is set. If
 * delegate is set, access by anyone other than the owner depends on the
 * return value of the delegate.
 * @param presetId The ID of the preset we are trying to create. This is
 * determined off-chain and it can be anything that doesn't exist yet.
 * @param linearStartTimestampsRelative The relative start timestamps of
 * linear periods.
 * @param linearEndTimestampRelative The relative end timestamp of the
 * entire linear unlocking schedule.
 * @param linearBips Basis points (percentage of the total amount unlocked)
 * for each linear period. This must add up to BIPS_PRECISION.
 * @param numOfUnlocksForEachLinear The number of unlocks for each linear
 * unlocking period. The minimum value is 1 (unchecked).
 */
function createPreset(
    bytes32 presetId,
    uint256[] calldata linearStartTimestampsRelative,
    uint256 linearEndTimestampRelative,
    uint256[] calldata linearBips,
    uint256[] calldata numOfUnlocksForEachLinear
) external virtual;

batchCreatePreset

function batchCreatePreset(
    bytes32[] calldata presetId,
    uint256[][] calldata linearStartTimestampsRelative,
    uint256[] calldata linearEndTimestampRelative,
    uint256[][] calldata linearBips,
    uint256[][] memory numOfUnlocksForEachLinear
) external virtual;

createActual

/**
 * @notice Creates an actual unlocking schedule based on a preset.
 * @dev Emits: ActualCreated, TokensDeposited. Throws: TimestampExpired,
 * InvalidPresetFormat, InvalidSkipAmount.
 * - A FutureToken is minted in the process w/ tokenId == actualId;
 * - If amountDepositingNow > 0, the caller must call approve() on the
 * project token first so safeTransfer() does not revert
 * - There is no minimum deposit
 * - Only callable by the owner if no access control delegate is set. If
 * delegate is set, access by anyone other than the owner depends on the
 * return value of the delegate.
 * @param recipient The address of the stakeholder. A FutureToken will be
 * minted to that address.
 * @param presetId The ID of the preset we are trying to create. This is
 * determined off-chain and it can be anything that doesn't exist yet.
 * @param startTimestampAbsolute When the unlocking schedule should start
 * in UNIX epoch timestamp (seconds). Cannot be in the past.
 * @param amountSkipped If the project is being transferred into TokenTable
 * from a different platform, we can skip over what's already been unlocked
 * to keep the progress consistent.
 * @param totalAmount The total amount of tokens to be unlocked.
 * @param amountDepositingNow You can deposit some amount of tokens when
 * creating the actual schedule for convenience. If the amount deposited is
 * insufficient when the stakeholder attempts to claim, the transaction
 * will revert.
 */
function createActual(
    address recipient,
    bytes32 presetId,
    uint256 startTimestampAbsolute,
    uint256 amountSkipped,
    uint256 totalAmount,
    uint256 amountDepositingNow
) external virtual;

batchCreateActual

function batchCreateActual(
    address[] calldata recipient,
    bytes32[] calldata presetId,
    uint256[] calldata startTimestampAbsolute,
    uint256[] calldata amountSkipped,
    uint256[] calldata totalAmount,
    uint256[] memory amountDepositingNow
) external virtual;

deposit

/**
 * @notice Makes a deposit into an actual unlocking schedule.
 * @dev Emits: TokensDeposited.
 * - The caller must call approve() on the project token first so
 * safeTransfer() does not revert.
 * - There is no minimum deposit.
 * - Only callable by the owner if no access control delegate is set. If
 * delegate is set, access by anyone other than the owner depends on the
 * return value of the delegate.
 * @param actualId The ID of the actual unlocking schedule that we are
 * intending to deposit into.
 * @param amount The amount of project tokens to be deposited.
 */
function deposit(uint256 actualId, uint256 amount) external virtual;

batchDeposit

function batchDeposit(
    uint256[] calldata actualId,
    uint256[] calldata amount
) external virtual;

withdrawDeposit

/**
 * @notice Withdraws existing locked deposit from an actual schedule.
 * @dev Emits: TokensWithdrawn.
 * - Only callable by the owner if no access control delegate is set. If
 * delegate is set, access by anyone other than the owner depends on the
 * return value of the delegate.
 * @param actualId The ID of the actual unlocking schedule that we are
 * intending to withdraw from.
 * @param amount The amount of project tokens to be withdrawn.
 */
function withdrawDeposit(
    uint256 actualId,
    uint256 amount
) external virtual;

claim

/**
 * @notice Claims claimable tokens for the specified actualId. If the
 * caller is the owner of the actualId or has permission, then the
 * tokens can be claimed to a different address (as specified in args)
 * @dev Emits: TokensClaimed.
 * - Only callable by the owner of the FutureToken if no access control
 * delegate is set. If delegate is set, access by anyone other than the
 * FutureToken owner depends on the return value of the delegate.
 * @param actualId The ID of the actual unlocking schedule that we are
 * intending to claim from.
 */
function claim(
    uint256 actualId
) external virtual;

batchClaim

function batchClaim(
    uint256[] calldata actualId
) external virtual;

claimCancelledActual

/**
 * @notice Claims claimable tokens for the specified CANCELLED actualId. If
 * the caller is the owner of the actualId or has permission, then the
 * tokens can be claimed to a different address (as specified in args)
 * @dev Emits: TokensClaimed.
 * - Only callable by the owner of the FutureToken if no access control
 * delegate is set. If delegate is set, access by anyone other than the
 * FutureToken owner depends on the return value of the delegate.
 * @param actualId The ID of the actual unlocking schedule that we are
 * intending to claim from.
 */
function claimCancelledActual(
    uint256 actualId
) external virtual;

cancel

/**
 * @notice Cancels an actual unlocking schedule effective immediately.
 * Tokens not yet claimed but already unlocked will be tallied.
 * @dev Emits: ActualCancelled.
 * - Only callable by the owner if no access control delegate is set. If
 * delegate is set, access by anyone other than the owner depends on the
 * return value of the delegate.
 * @param actualId The ID of the actual unlocking schedule that we are
 * intending to cancel.
 */
function cancel(
    uint256 actualId
) external virtual returns (uint256 amountClaimed, uint256 amountRefunded);

setAccessControlDelegate

/**
 * @notice Sets the access control delegate used to control claim behavior.
 * @dev Only callable by the owner.
 */
function setAccessControlDelegate(
    address accessControlDelegate_
) external virtual;

disableCancelFunction

/**
 * @notice Permanently disables the cancel() function.
 */
function disableCancelFunction() external virtual;

getEncodedPreset

/**
 * @notice Returns an ABI-encoded preset, as nested objects cannot be
 * returned directly in Solidity.
 * @dev To decode in JS, use:
 *  ethers.utils.defaultAbiCoder.decode(
 *      ['uint256[]', 'uint256', 'uint256[]', 'uint256[]'],
 *      encodedPreset
 *  )
 * @param presetId The ID of the preset we are trying to read.
 */
function getEncodedPreset(
    bytes32 presetId
) external view virtual returns (bytes memory);

calculateAmountClaimable

/**
 * @notice Calculates the amount of unlocked tokens that have yet to be
 * claimed in an actual unlocking schedule.
 * @dev This is the most complex part of the smart contract. Quite a bit of
 * calculations are performed here.
 * @param actualId The ID of the actual unlocking schedule that we are
 * working with.
 * @return deltaAmountClaimable Amount of tokens claimable right now.
 * @return updatedAmountClaimed New total amount of tokens claimed. This is
 * the sum of all previously claimed tokens and deltaAmountClaimable.
 */
function calculateAmountClaimable(
    uint256 actualId
)
    public
    view
    virtual
    returns (uint256 deltaAmountClaimable, uint256 updatedAmountClaimed);

Errors

0x0ef8e8dc

  • The preset that's being created has an invalid format, meaning:

    • The total amount fails to add up to BIPS_PRECISION

    • linearBips, numOfUnlocksForEachLinear, and linearStartTimestampsRelative are of different lengths

    • The last element in linearStartTimestampsRelative is larger than linearEndTimestampRelative

  • The provided presetId points to an empty preset

error InvalidPresetFormat();

0x78c0fc43

  • If the amount skipped is greater than or equal to the total amount when creating actual

error InvalidSkipAmount();

0x25c3f46e

  • If tokens deposited is less than the claimable amount

error InsufficientDeposit(
    uint256 deltaAmountClaimable,
    uint256 amountDeposited
);

0x7f63bd0f

  • If the caller fails to meet certain access control permissions

error NotPermissioned();

0x7cbb15b4

  • If the provided presetId already exists when attempting to create a new preset

error PresetExists();

0xbd88ff7b

  • If the provided presetId references a nonexistent preset

error PresetDoesNotExist();

0x26c69d1a

  • If the absolute start timestamp is in the past and the amount skipped is 0

error TimestampExpired();

Last updated