HomeLoveStartLearnPlayConverseResourcesBlog
    HomeGuidingLearning Curve

    Table of contents

    • The Learning Curve
    • Simple Summary
    • Abstract
    • Motivation
    • Specification
      • Kernel Factory
        • Methods
        • Events
      • Learning Curve
    • Rationale
    The Learning Curve

    Any good spec starts with a picture:

    Learning Curve Explainer
    Simple Summary

    A generalised template for online educational courses.

    Abstract

    The following specifies a set of contracts capable of supporting free online educational environments for learners, while ensuring that educators are rewarded for the content they create. This standard allows for the creation of any kind of course and only expects educators to ensure learners can claim their fee back after some period of time.

    Motivation

    Student debt and underpaid teachers are two pivotal problems of our age. While an abundance of information exists online, especially in the open source world, we cannot simply make all resources free, as that ensures teachers become even more undervalued than they currently are. There must be some way of using programmable money to both erase student debt, while ensuring educators receive due recompense for the critical role they play in society.

    These contracts present exactly one such solution. An epistemic community of learners who choose to mint (increasingly valuable) LEARN tokens at the end of their course, rather than just claim their original fee back, is the emergent result.

    Specification

    Kernel Factory

    This contract contains the logic for creating courses, registering learners, and tracking whose money has gone where and how much of the initial fee can be redeemed at any given time.

    Methods

    constructor

    params:
    address _stable - the kind of token fees are denominated in;
    address _learningCurve - the address of the contract containing the logic for minting & burning;
    address _vault - the yearn vault where fees are kept during study.

    The Factory is constructed to be aware of only these three items essential to its correct functioning.

    createCourse

    params:
    uint256 _fee - the amount of DAI to be locked for the duration of study;
    uint256 _checkpoints - the number of points at which learners can redeem part of their fee;
    uint256 _checkpointBlockSpacing - checkpoints can be defined in blocks as exact time is not required;
    string calldata _url - a simple "metadata" field to link any course to its frontend, useful for validation & security, as well as future frontend displays;
    address _creator - the address to receive any yield generated when learners on this course choose not to mint LEARN.

    Anyone can create a course. There are no privileged roles here. The only expectation is that you define clearly when learners may redeem whatever it costs to take your course. You are encouraged to allow them to do so incrementally, using a few checkpoints, so that they can redeem portions of their fee as they go, as this likely makes their studies even more sustainable.

    batchDeposit

    A function which can be called by anyone to take the current funds in the Factory from fees and move them into a yearn vault.

    register

    params: uint256 _courseId

    This method allows a learner to register for a course of their choice. It checks the fee associated with that course, accepts tokens amounting to that fee and registers the learner. The tokens are kept in the contract and assigned to the current batch. Once there are enough tokens in the batch to justify the gas costs of calling

    batchDeposit()
    , anyone may do so and add the current batch to the yearn vault.

    verify

    params:
    address learner
    uint256 _courseId

    All course are deployed with a given number of checkpoints allowing learners to receive a portion of their fees back at various stages in the course. This is a helper function that checks where a learner is in a course and is used by both

    redeem()
    and
    mint()
    to figure out the proper amount to be redeemed. It is public, so anyone may check it at any time.

    _verify

    params:
    address learner
    uint256 _courseId

    The internal function that does the actual calculations and checks for the public function above. It uses

    block.number
    because being accurate even to the second here is not required. We generally expect courses to run over periods of months, so working on block number approximations is more than adequate enough for our needs.

    redeem

    params: uint256 _courseId

    If a learner is redeeming rather than minting, it means they are simply requesting their initial fee back (whether they have completed the course or not). In this case, the method checks what proportion of

    fee
    (set when the course is deployed) must be returned and sends it back to the learner. Whatever yield they earned is allocated to the course creator, which they can choose to redeem at any point with another method described below.

    This contract contains no sense of "completing" a course based on some kind of examination or assessment, as we do not feel this is the direction in which modern education ought to trend. This method simply checks the period elapsed since

    blockRegistered
    . If this contains more blocks than
    checkpoints * checkpointBlockSpacing
    , then the full
    fee
    can be returned and the yield sent to the course creator.

    mint

    params: uint256 _courseId

    Handles learner minting new LEARN and checks via

    verify()
    what proportion of the fee to send to the Learning Curve. If
    mint
    is chosen, the yield earned is still assigned to the course creator to balance incentives for educators as best as possible. The total fee is returned directly to the learner in the form of LEARN tokens, and we feel that the shape and design of the Learning Curve is enough to incentivize many learners to choose this option. However, if they don't, this is not really a concern, as we see this as a long-term project and will be satisfied if the supply of LEARN grows very slowly.

    withdrawYieldRewards

    Transfers the amount of DAI that an address is eligible to withdraw. Addresses become eligible if they are set as the

    creator
    a specific course and a learner on that course decides to redeem their fee rather than mint LEARN, which means that any yield the fee earned is returned to the course creator.

    determineEligibleAmount

    params: uint256 _courseId

    returns: uint256 eligibleShares, bool deployed

    Fetches and updates the amount of funds that a learner is eligible for at this checkpoint in their course. It does this by looking at two things:

    eligibleShares
    that represent the share of collateral in the yearn vault which the learner can rightfully claim; and any "undeployed" DAI from their initial staked fees which has not yet been sent to a yearn vault.

    getCurrentBatchTotal

    returns: uint256

    A helper get function which returns the total number of batches in our yearn vault.

    getBlockRegistered

    params:
    address learner
    uint256 _courseId

    returns: uint256

    A helper get function which returns the block a given learner registered in.

    getCurrentBatchId

    returns: uint256

    A helper get function which returns the current

    batchId
    .

    getNextCourseId

    returns: uint256

    A helper get function which returns the current

    courseId
    .

    getLearnerCourseEligibleFunds

    params:
    address learner
    uint256 _courseId

    returns: uint256

    A helper get function which returns the amount of funds a learner is eligible to claim back from a given course, at any given time.

    getLearnerCourseFundsRemaining

    params:
    address learner
    uint256 _courseId

    returns: uint256

    A helper get function which returns the amount of the initial fee a learner staked which remains in the course at any given time.

    getCourseUrl

    params: uint256 _courseId

    returns: string memory

    A helper get function which returns the URL associated with the

    courseId
    stored in the contract, mostly for security and independent verification/auditing purposes.

    getYieldRewards

    params: address redeemer

    returns: uint256

    A helper get function which returns the current yieldRewards mapped to a given

    creator
    address, should any educator wish to claim some of their earnings at any point.

    Events

    CourseCreated

    uint256 indexed courseId,
    uint256 checkpoints,
    uint256 fee,
    uint256 checkpointBlockSpacing,
    uint256 url,
    uint256 creator

    Likely the most important event in the long run, especially if we want to build a catalogue of courses, the URLs that they exist at, and the creator addresses which are receiving any yield. Tracking these events allows us to build a coherent and clear front-end for discovering the various courses using this standard.

    LearnerRegistered

    uint256 indexed courseId,
    address learner

    Keep track of the addresses of learners and the courses they've signed up for.

    FeeRedeemed

    uint256 courseId,
    address learner,
    uint256 amount

    useful for understanding which learners have redeemed how much for each course. This allows for two important things: understanding which courses learners are

    redeeming
    on rather than
    minting
    ; and tracking how many learners (and how much of their original fees) are inactive in either the Vault or the Factory.

    LearnMintedFromCourse

    uint256 courseId,
    address learner,
    uint256 stableConverted,
    uint256 learnMinted

    Tracked separately from

    LearnMinted
    events in the Learning Curve so it's easy to see how much of the total supply of LEARN comes directly from learners going through the courses on offer.

    BatchDeposited

    uint256 batchId,
    uint256 batchAmount,
    uint256 batchYieldAmount

    Tracked to make it easy to see how much DAI is in each batch in the vault, and how many yDAI are associated with it for redemption or minting purposes.

    CheckpointUpdated

    uint256 courseId,
    uint256 checkpointReached,
    address learner

    Helpful in understanding where different learners are at different points in the course. Only emitted when a learner actually claims some amount of their intial fee back at a given checkpoint.

    YieldRewardRedeemed

    address redeemer,
    uint256 YieldRewarded

    Helpful to keep track of how much yield has been claimed/sent back to the course creator at any given point.

    Learning Curve

    This contract contains the logic for minting and burning LEARN based on the collateral that is sent to it. Importantly, it is open to anyone, which is what allows secondary markets for LEARN to form easily. The more collateral locked, the less LEARN is minted, using an exponentially decaying function. This ensures that the price of LEARN - defined as the number in existence divided by the underlying collateral in this contract - increases linearly. Please see this spreadsheet for the source of the graphical depiction below.

    Learn Curves

    Methods

    initialise

    This is called only once, upon deployment, and is necessary for the maths to work. If we do not start at 1, then we end up with a DIV(0) error. The tokens are minted to the Learning Curve address itself, and so are unusable, as this seems most fair.

    mint

    params: uint256 _wad - the amount of DAI collateral to be swapped for LEARN

    This method allows anyone to mint LEARN tokens dependent on the amount of DAI they send. It is calculated according to the exponential decay above, which can be modelled with a natural logarithm. We use this library to help us calculate it, as exponents on chain are challenging.

    mintForAddress

    params: address learner; uint256 _wad

    This is the same as a normal mint, except that an address is passed in which the minted LEARN is sent to. This is necessary to allow for mints directly from a Course, where we want to learner to receive LEARN, not the Kernel Factory. It can also be used to mint LEARN to an address other than the one contributing collateral, which could - for instance - prove useful for donations.

    burn

    params: uint256 _burnAmount - how much underlying DAI the burner wishes to receive back.

    This function takes in some amount of LEARN to be burned, calculates how much underlying DAI collateral this corresponds to using the inverse operation of the natural logarithm decay function for minting - i.e. the natural exponent

    e
    - burns the LEARN and returns the DAI to the sender.

    e_calc

    params: uint256 x - some number to be used in the calculation of exponents for

    burn
    method calls.

    This function takes in some number

    x
    , divides it by the global constant
    k
    and returns the value of
    e
    raised to the power of that number. This is used for the reverse operation of minting LEARN, here called
    burn
    .

    doLn

    params: uint256 x

    An internal, pure helper function that uses the PRBMathUD60x18 contract to return the natural logarithm of any number

    x
    we pass to it.

    getPredictedBurn

    params: uint256 _burnAmount - DAI to receive

    A helper function wich calculates the amount of underlying DAI collateral to return for some

    _burnAmount
    of LEARN tokens passed into it.

    getMintableForReserveAmount

    params: uint256 reserveAmount - DAI to lock

    A helper function to check if the learner has enough DAI to get the desired LEARN tokens and ensure a successful

    mint
    transaction.

    Events

    LearnMinted

    address indexed learner,
    uint256 amountMinted,
    uint256 daiDeposited

    It is likely helpful to distinguish between

    LearnMintedFromCourse
    and
    LearnMinted
    by anyone interacting directly with the LearningCurve as secondary markets become more established, so that we can tell how much LEARN in existence comes from learning, and how much comes from speculation on the value of the resulting epistemic community.

    LearnBurned

    address indexed learner,
    uint256 amountBurned,
    uint256 daiReturned

    The same reasoning applies as above: it is helpful to track LEARN created and destroyed by learners going through courses, and by other people interacting directly with these contracts. We have nothing against speculation, as such people take real risks and provide the necessary liquidity for more efficient markets to form.

    Considerations

    "k" constant

    Where does this magic constant come from? We were inspired by Uniswap's constant product formula and the simplicity it represents. While our setup is slightly different, we need some constant which defines the slope of the linearly increasing price function for LEARN. We do not think it possible, in principle, to model this with complete certainty. All we can do is ask increasingly precise questions about the kinds of outcome we might wish to see, especially in terms of how many LEARN will be minted by the course fee, if the learner chooses that option.

    We set

    k = 10000
    because this means that 1M DAI must be locked as collateral before your course fee for the initial KERNEL course mints just 1 LEARN, an important psychological fact if nothing else.

    Rationale

    There are no special accounts or privileged roles in these contracts. There are no fees collected by a specific account. That is because we are very serious about building a general template for online education that cannot be co-opted and that both makes learning free, while also ensuring there are rewards available for educators.

    We are sure that there are even better ways to achieve this, and so have tried to outline in detail all our assumptions in order that this may be the beginning of a fruitful conversation about how programmable money might solve incentive problems surrounding the transfer of knowledge.

    The state of Ethereum will be around for a long, long time to come and so we are not here to get rich quickly, we are here to play with this vastly expanded temporal boundary. We have not written this for ourselves, but as a work of love and devotion to those yet to come.

    Education is the original gift and is especially interesting in gift-giving economies, because knowledge is given within a context that still demands effort and attention in order to be learned. Valuable knowledge may present itself for free, but it must be given attention and reflection in order to become personally meaningful. It is this paradox which makes educational gifts the seed around which a community crafting new value models can flourish.

    As Buddha said: “a single flower blooms, and throughout the world it is spring”.

    Previous
    Translations