Comment on page

Game Mechanics

for GoodGhosting v2
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, a new savings pool smart contract is deployed by the GoodGhosting team. Each game (also referred to as pool) runs for a fixed amount of time. There are always one or more deposit rounds - where you deposit a specific asset into the savings pool - followed by a single waiting round.
To win, players need to complete all deposits. Aka: they need to make a deposit into the pool smart contract every round, prior to the round’s deadline. At the end of the game, all players can withdraw their initial deposit (minus any impermanent loss), irrespective whether they won or not.
Winners earn a slice of the pool’s rewards. This includes any interest generated through decentralized finance platforms as well as additional incentives and sponsorship (see Yield strategies). 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 pool 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 pool, are required to deposit the same ERC-20 token which is defined by inboundToken.
Deposit amounts
We support both pools with a fixed deposit amount (identical for each user), as well as pools 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 GoodGhosting pool. Once a users joins the pool with a specific deposit amount, all next deposits in that pool will have to be of the same amount. This is defined by depositAmount. For flexible pools there can be a maximum deposit limit per player, defined by maxFlexibleSegmentPaymentAmount.
Game duration
The duration of both the deposit and waiting round are determined at time of pool creation. This means each pool has a predictable fixed end date. Deposit rounds can have a different duration (segmentLength) than the final waiting round (waitingRoundSegmentLength), which enables different Pool Types with interesting dynamics.

Rewarding early depositors (Rewards calculation)

Reward distributions are taking into account the total time (and amount) a user has funds deposited into a savings pool. Thereby increasing fairness to all depositors. The sooner a player deposits in each segment, the bigger their share of the pool's generated interest and rewards in that segment - assuming they are considered winners at the end of the pool. Typically, a winning ulinkser (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 Yield strategiesto learn more about how rewards are generated.

Smart contract code

Smart contract overview

In order to make the contracts modular, the contracts are divided into two types:
  1. 1.
    The pool 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. 2.
    The yield strategy contracts that hold the logic to integrate with the external protocols. The strategy contract is owned by the pool contract. Thereby only the pool contract can directly interact with the strategy contract, not players or any other external actors.
There are multiple yield strategy contracts available, but each pool contract can only be coupled to one strategy at a time. This is determined at the time the pool contract is deployed. Hence, the yield source becomes fixed at the time of pool deployment.

Flow of funds

Flow of user funds in GodGhosting v2

Public functions

Commonly used functions by users
The initial deposit happens by calling the joinGame()function on our pool 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 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 pool. If the game is still in the first segment, a user can still re-join the pool.

Admin functions

Our pool smart contract contain multiple administrative functions, that are only accessible to the admin (owner) of the contract.
General admin functions
Can be called during or after a challenge:
lowerEarlyWithdrawFee() - Allows the admin to set a lower early withdrawal fee by specifying newEarlyWithdrawFee.
Can be called only before a challenge starts
setAdminFee(): Allows the admin to adjust the admin fee for managing the pool.
setEarlyWithdrawFee(): Allows the admin to set the early withdrawal fee, useful to discourage early withdrawals or adjust the penalty.
setPoolToken(): Allows the admin to set the challenge deposit token, useful when a different token is desired for participation.
setStrategy(): Allows the admin to set the investment strategy, useful to adjust the strategy based on market conditions. setIncentiveToken() - Allows the admin to change the incentive token, useful when a different token is desired for rewards.
Can only be called after a challenge ends adminFeeWithdraw() - Allows the admin to withdraw the performance fee, if applicable. This function can be called only by the contract's admin and only after the challenge has ended.
Who is the admin?
Upon smart contract deployment, the admin is the HaloFi-owned address that deployed the contract to the blockchain. Upon initialization, the ownership typically gets transferred to a HaloFi-controlled Gnosis Safe multisig wallet. Example of such a wallet for Celo, Polygon and Base.