Overview

TL;DR create a subscription with metadata and get a new NFT for each period the subscription is active. Anyone/cranks will be run to renew/deactivate subscriptions and they'll get a fee paid to them to incentivize. Products building on Buoyant can easily verify active status and ownership of a subscription through subscription metadata and token ownership.

Subscription Specification

A subscription in Buoyant boils down to three things: some metadata, a vault, and an NFT. The metadata includes the following:

pub struct Subscription {
    pub active: bool,

    pub mint: Option<Pubkey>,
    pub deposit_vault: Pubkey,
    pub deposit_mint: Pubkey,

    pub payee: Pubkey,
    pub amount: u64,
    pub duration: i64, // = UnixTimestamp, although will always be positive

    pub next_renew_time: i64,
    pub renewal_count: u64,
}

Subscription types are mainly differentiated by their payee, amount, and duration, and specific subscriptions vary in their active status, payment token type, ownership token mint, renewal time, and renewal count.

The vault, denoted as deposit_vault in the metadata, is a vault that a user will deposit funds into and that the program has access to transfer from. A subscription has a designated vault with a token type specified upon creation that will supply the funds to maintain an active subscription. This subscription can be modeled as a sort of "pre-pay" subscription, where you can deposit funds at any time, and recurring payments are automated.

The NFT acts as a transferable asset that represents ownership of the subscription. A subscription's metadata specifies a mint field. If someone owns the token from this mint, they own the subscription. These mints are created and controlled by the program, and only mint a single token.

Creating a Subscription

"Creating a subscription" occurs in three steps: initializing a subscription, depositing funds, and invoking the first renewal. The latter two steps are specified in the following sections.

Initializing a subscription is involves calling the Initialize instruction on the program and specifying three parameters: the payee, amount, and duration.

  • The payee is the public key of the address that the funds will be transferred to upon renewal.

  • The amount is the amount that will be paid every renewal, denoted in the most atomic unit of the token type (meaning if the mint decimals of the token are 6, then an amount of 1 would be 1 one-millionth of a token paid every renewal).

  • The duration is the number of seconds that a subscription is set to stay active before it's possible to renew or expire.

In initialize, the program will initialize an account holding this metadata, and a token account as a vault with a token type according to the accounts passed into the instruction.

Deposits and Withdraws

Deposits are normal token transfers. Our program implements a deposit function that's mainly just a wrapper for invoking a transfer on the token program.

Withdraws are essentially the same thing, but with some added checks. The only account that can withdraw funds from the subscription's vault is the current owner of the subscription.

Renewal and Expiration

There are a couple of different cases that lead to renewal or expiration. These cases boil down to different permutations of certain flags:

  • Whether the current time of transaction execution is before or after next_renew_time.

  • Whether the funds in the vault are greater than or equal to the amount specified in the metadata.

  • Whether a subscription is active.

  • Whether the person to receive the latest token is the current owner of the subscription, i.e. the account that owns the token of the type specified by the mint field in the subscription metadata.

On expire, the program will simply mark the active flag in the subscription metadata to be false and move on with its day.

On renewal, the program will:

  1. Initialize the necessary associated token accounts for the payee and caller if needed.

  2. Transfer funds from the deposit vault to the payee and caller.

  3. Create a new mint and mint one token to the current owner.

  4. Update the subscription metadata:

    1. Set active to true.

    2. Set mint to the new mint.

    3. Set next_renew_time to the current time plus duration.

    4. Increment renewal_count.

And that's it!

Last updated