Savings Challenge Mechanics

We use game-design elements to reward users who hit their savings target (i.e. winners) more so than users who do not. This happens transparently on a public blockchain.

🕹️ Game mechanics

For each game (also referred to as challenge or pool), a new savings challenge smart contract is deployed by the HaloFi team. Each challenge runs for a fixed amount of time. There are always one or more deposit rounds - where you deposit a specific digital asset into the savings challenge smart contract - followed by a single waiting round.

To win, players need to complete all deposits. Aka: they need to make a deposit into the challenge smart contract every round, prior to the round’s deadline. At the end of the challenge, all players can withdraw their initial deposit (minus any impermanent loss), irrespective whether they won or not.

Winners earn a slice of the challenge's rewards. This includes any interest generated through decentralized finance platforms as well as additional incentives and sponsorship (see Yield Strategies and HaloFi Challenges). The rewards are proportional to how much and when a user deposited. Typically, winners earn more than when they would save by themselves.

The rewards are proportional to how much and when a user deposited. Typically, winners earn more than when they would save by themselves.

⚙️ Technical

Deposit tokens

On a technical level, deposits take place in ERC-20 tokens. These can be digital stablecoins (such as DAI, USDC or cUSD) whose value is soft-pegged to fiat currency (e.g. the US Dollar) and which can be transferred to our saving challenge smart contract, using blockchain technology. Additionally, we support most of the other ERC-20 tokens (including most volatile tokens) that are available on the Celo and Polygon blockchains.

Note that all users in a specific challenge, are required to deposit the same ERC-20 token which is defined by inboundToken.

Deposit amounts

We support both challenges with a fixed deposit amount (identical for each user), as well as challenges with flexible deposits (unique for each users). When flexible deposits are enabled (flexibleSegmentPayment = true), everyone can decide how much they want to deposit into a HaloFi challenge. Once a users joins the challenge with a specific deposit amount, all next deposits in that challenge will have to be of the same amount. This is defined by depositAmount. For challenges with flexible (custom) despoit amounts, there can be a maximum deposit limit per player, defined by maxFlexibleSegmentPaymentAmount.

Challenge duration

The duration of both the deposit and waiting round are determined at time of challenge creation. This means each challenge has a predictable fixed end date. Deposit rounds can have a different duration (segmentLength) than the final waiting round (waitingRoundSegmentLength), which enables different with interesting dynamics.

pageChallenge Types

Rewarding early depositors (Rewards calculation)

Reward distributions are taking into account the total time (and amount) a user has funds deposited into a savings challenge. Thereby increasing fairness to all depositors. The sooner a player deposits in each segment, the bigger their share of the challenge's generated interest and rewards in that segment - assuming they are considered winners at the end of the challenge. Typically, a winning user (who deposits during the very first block of a deposit segment) can earn up to a factor of 2 more the rewards compared to another user (who deposits during the very last block of a deposit segment). On the smart contract level, this is tracked using cumulativePlayerIndexSum and playerSharePercentage. This is updated for every new deposit for each segment. For the waiting round, rewards are calculated according to the total amount of funds a user has deposited (since each user starts this round at the same time).

See to learn more about how rewards are generated.

pageYield Strategies

Smart contract code

Smart contract overview

In order to make the contracts modular, the contracts are divided into two types:

  1. The challenge contract that holds all the core game/pool logic (Pool.Sol) It is the main contract through which players are able to make deposits into the underlying yield strategy contract and withdraw funds.

  2. The yield strategy contracts that hold the logic to integrate with the external protocols. The strategy contract is owned by the challenge contract. Thereby only the challenge contract can directly interact with the strategy contract, not players or any other external actors.

There are multiple yield strategy contracts available, but each challenge contract can only be coupled to one strategy at a time. This is determined at the time the challenge contract is deployed. Hence, the yield source becomes fixed at the time of pool deployment.

Flow of Funds

Public functions

Commonly used functions by users

The initial deposit happens by calling the joinGame()function on our challenge smart contract (Pool.sol). Subsequent deposit happen via the makeDeposit()function. withdraw() - Allows player to withdraw their funds after the game ends. Losing players pay no fee and receive their initial deposit back (minus any IL if applicable). Winning players get their initial deposit back (minus any IL or performance fee, if applicable) and a share of the earned rewards, based on the cumulativePlayerIndexSum.

earlyWithdraw() - Allows a player to withdraw their funds before the game ends. An early withdrawal fee is charged, as defined in the constructor. IL is also taken into consideration (if applicable for the used yield strategy), prior to the user receiving the remaining portion of his funds. This function can be called any time prior to the end of the game, and results in the player exiting the savings challenge. If the game is still in the first segment, a user can still re-join the challenge.

Admin functions

Our challenge smart contract contain multiple administrative functions, that are only accessible to the admin (owner) of the contract.

General admin functions

adminFeeWithdraw() - Allows the admin to withdraw the performance fee, if applicable. This function can be called only once, and only by the contract's admin and only after the game has ended.

lowerEarlyWithdrawFee() - Allows the admin to set a lower early withdrawal fee by specifying newEarlyWithdrawFee.

setIncentiveToken - To add explanation

// To add remaining admin functions //

Who is the admin?

Upon initialization, the admin is the HaloFi-owned address (EOA) that deployed the contract to the blockchain. Soon after deployment, the ownership typically gets transferred to a HaloFi-controlled Safe multisig wallet. Example of such a wallet for Celo and Polygon.

Last updated