How to Build a Bonding Curve DEX with Clarity on Stacks

FlamesFlames
13 min read

Introduction

Welcome to Part 2 of our STX.CITY Mini tutorial series! In the previous tutorial, we built a complete SIP-010 token contract. Now we'll create a sophisticated bonding curve DEX (Decentralized Exchange) that enables automatic token trading with dynamic pricing.

By the end of this tutorial, you'll have a fully functional DEX that allows users to buy and sell tokens using a constant product bonding curve, complete with fee distribution and liquidity management.

What You'll Learn

  • Understanding bonding curve mechanics and automated market makers

  • Implementing constant product formula (x × y = k)

  • Building secure buy/sell functions with fee distribution

  • Creating initialization and state management systems

  • Testing DEX functionality with real trading scenarios

Prerequisites

  • Completed Part 1 (SIP-010 Token Contract)

  • Understanding of basic AMM concepts

  • Familiarity with Clarity programming from Part 1

Understanding Bonding Curves

What is a Bonding Curve?

A bonding curve is an automated market maker that determines token price based on supply and demand through a mathematical formula. As more tokens are bought, the price increases; as tokens are sold, the price decreases.

Our Implementation: Constant Product Formula

We'll use the constant product formula: x × y = k

Where:

  • x = STX liquidity in the pool

  • y = Token supply available for trading

  • k = Constant product (invariant)

Key Benefits

  1. No Upfront Liquidity: Tokens can be traded immediately after deployment

  2. Automatic Price Discovery: Price adjusts based on trading activity

  3. Always Available Liquidity: Users can always buy or sell tokens

  4. Fee Generation: Trading fees provide revenue to the platform and the token creator

Setting Up the DEX Contract

Let's build our DEX contract step by step.

Create a new contract we’ll call dex here:

clarinet contract new dex

1. Contract Header and Constants

;; @title Bonding Curve DEX for STX.CITY Mini Version
;; @version 1.0
;; @summary A decentralized exchange facilitating token trading using bonding curve mechanism
;; @description This DEX allows users to buy and sell tokens through a bonding curve, with automatic liquidity provision

;; Import SIP-010 trait
(use-trait sip-010-trait 'STF0V8KWBS70F0WDKTMY65B3G591NN52PR4Z71Y3.sip-010-trait-ft-standard.sip-010-trait)

;; Error constants
(define-constant ERR-UNAUTHORIZED (err u401))
(define-constant ERR-INVALID-AMOUNT (err u402))
(define-constant ERR-INSUFFICIENT-BALANCE (err u403))
(define-constant ERR-SLIPPAGE-TOO-HIGH (err u404))
(define-constant ERR-TRADING-DISABLED (err u1001))
(define-constant ERR-ALREADY-INITIALIZED (err u1002))
(define-constant ERR-NOT-INITIALIZED (err u1003))

What this does:

  • Imports the SIP-010 trait so we can interact with any compliant token

  • Defines specific error codes for different failure scenarios

  • Sets up the foundation for secure DEX operations

2. DEX Configuration Constants

;; DEX Configuration
(define-constant TARGET-STX u3000000000) ;; 3000 STX target for graduation
(define-constant VIRTUAL-STX u600000000) ;; 600 STX virtual liquidity
(define-constant FEE-PERCENTAGE u2) ;; 2% trading fee
(define-constant PLATFORM-FEE-ADDRESS 'ST1WTA0YBPC5R6GDMPPJCEDEA6Z2ZEPNMQ4C39W6M)

What this does:

  • TARGET-STX: When reached, token "graduates" to a full liquidity pool

  • VIRTUAL-STX: Initial virtual liquidity for price calculations

  • FEE-PERCENTAGE: Trading fee split between platform and token creator

  • PLATFORM-FEE-ADDRESS: Where platform fees are sent

3. State Variables

;; State variables
(define-data-var associated-token (optional principal) none)
(define-data-var initialized bool false)
(define-data-var tradable bool false)
(define-data-var virtual-stx-amount uint VIRTUAL-STX)

What this does:

  • associated-token: Which token this DEX trades

  • initialized: Whether the DEX has been set up

  • tradable: Whether trading is currently enabled

  • virtual-stx-amount: Current virtual STX for price calculations

Core DEX Functions

1. Initialization Function

;; Initialize the DEX with a token contract
(define-public (initialize (token-contract principal) (initial-token-amount uint) (initial-stx-amount uint))
    (begin
        ;; Check if already initialized
        (asserts! (not (var-get initialized)) ERR-ALREADY-INITIALIZED)

        ;; Set the associated token
        (var-set associated-token (some token-contract))

        ;; Mark as initialized and tradable
        (var-set initialized true)
        (var-set tradable true)

        ;; Set virtual STX amount
        (var-set virtual-stx-amount initial-stx-amount)

        (ok true)
    )
)

What this does:

  • Links the DEX to a specific token contract

  • Prevents double initialization

  • Enables trading functionality

  • Sets up initial virtual liquidity parameters

2. Buy Function - Converting STX to Tokens

;; Buy tokens with STX using bonding curve
(define-public (buy (token-trait <sip-010-trait>) (stx-amount uint))
    (let (
        (current-stx-balance (stx-get-balance (as-contract tx-sender)))
        (current-token-balance (unwrap-panic (contract-call? token-trait get-balance (as-contract tx-sender))))
        (virtual-stx (var-get virtual-stx-amount))

        ;; Calculate tokens to receive using constant product formula
        ;; tokens_out = (token_reserve * stx_in) / (stx_reserve + stx_in)
        (tokens-out (/ (* current-token-balance stx-amount) (+ current-stx-balance stx-amount)))

        ;; Calculate fees (2% of STX input)
        (fee-amount (/ (* stx-amount FEE-PERCENTAGE) u100))
        (platform-fee (/ fee-amount u2))
        (creator-fee (- fee-amount platform-fee))
        (net-stx-amount (- stx-amount fee-amount))
    )
        ;; Validation checks
        (asserts! (var-get tradable) ERR-TRADING-DISABLED)
        (asserts! (> stx-amount u0) ERR-INVALID-AMOUNT)
        (asserts! (> tokens-out u0) ERR-INVALID-AMOUNT)

        ;; Transfer STX from buyer to DEX
        (try! (stx-transfer? net-stx-amount tx-sender (as-contract tx-sender)))

        ;; Transfer fees
        (if (> platform-fee u0)
            (try! (stx-transfer? platform-fee tx-sender PLATFORM-FEE-ADDRESS))
            true
        )
        (if (> creator-fee u0)
            (try! (stx-transfer? creator-fee tx-sender (contract-of token-trait)))
            true
        )

        ;; Transfer tokens from DEX to buyer
        (try! (as-contract (contract-call? token-trait transfer tokens-out tx-sender (tx-sender) none)))

        (ok tokens-out)
    )
)

What this does:

  • Uses constant product formula to calculate token output

  • Implements 2% fee split between platform and token creator

  • Transfers STX from buyer to DEX contract

  • Transfers calculated tokens from DEX to buyer

  • Includes comprehensive validation and error handling

3. Sell Function - Converting Tokens to STX

;; Sell tokens for STX using bonding curve
(define-public (sell (token-trait <sip-010-trait>) (tokens-in uint))
    (let (
        (current-stx-balance (stx-get-balance (as-contract tx-sender)))
        (current-token-balance (unwrap-panic (contract-call? token-trait get-balance (as-contract tx-sender))))

        ;; Calculate STX to receive using constant product formula
        ;; stx_out = (stx_reserve * tokens_in) / (token_reserve + tokens_in)
        (stx-out (/ (* current-stx-balance tokens-in) (+ current-token-balance tokens-in)))

        ;; Calculate fees (2% of STX output)
        (fee-amount (/ (* stx-out FEE-PERCENTAGE) u100))
        (platform-fee (/ fee-amount u2))
        (creator-fee (- fee-amount platform-fee))
        (net-stx-amount (- stx-out fee-amount))
    )
        ;; Validation checks
        (asserts! (var-get tradable) ERR-TRADING-DISABLED)
        (asserts! (> tokens-in u0) ERR-INVALID-AMOUNT)
        (asserts! (> net-stx-amount u0) ERR-INVALID-AMOUNT)

        ;; Transfer tokens from seller to DEX
        (try! (contract-call? token-trait transfer tokens-in tx-sender (as-contract tx-sender) none))

        ;; Transfer STX from DEX to seller
        (try! (as-contract (stx-transfer? net-stx-amount tx-sender (tx-sender))))

        ;; Transfer fees
        (if (> platform-fee u0)
            (try! (as-contract (stx-transfer? platform-fee tx-sender PLATFORM-FEE-ADDRESS)))
            true
        )
        (if (> creator-fee u0)
            (try! (as-contract (stx-transfer? creator-fee tx-sender (contract-of token-trait))))
            true
        )

        (ok net-stx-amount)
    )
)

What this does:

  • Calculates STX output using the same constant product formula

  • Deducts 2% fees from the STX output

  • Transfers tokens from seller to DEX contract

  • Transfers net STX amount to seller

  • Distributes fees to platform and creator

Read-Only Functions for Data Access

1. State Query Functions

;; Get current STX balance in the DEX
(define-read-only (get-stx-balance)
    (stx-get-balance (as-contract tx-sender))
)

;; Get current token balance in the DEX
(define-read-only (get-token-balance)
    (match (var-get associated-token)
        token-contract (unwrap-panic (contract-call? token-contract get-balance (as-contract tx-sender)))
        u0
    )
)

;; Get initialization status
(define-read-only (get-initialized)
    (var-get initialized)
)

;; Get trading status
(define-read-only (get-tradable)
    (var-get tradable)
)

;; Get associated token contract
(define-read-only (get-associated-token)
    (var-get associated-token)
)

What this does:

  • Provides external access to DEX state information

  • Allows frontends to display current liquidity

  • Enables monitoring of DEX status and configuration

2. Trading Preview Functions

;; Calculate how many tokens you get for a given STX amount
(define-read-only (get-buyable-tokens (stx-amount uint))
    (let (
        (current-stx-balance (stx-get-balance (as-contract tx-sender)))
        (current-token-balance (get-token-balance))
    )
        (if (and (> current-stx-balance u0) (> current-token-balance u0))
            (/ (* current-token-balance stx-amount) (+ current-stx-balance stx-amount))
            u0
        )
    )
)

;; Calculate how much STX you get for a given token amount
(define-read-only (get-sellable-stx (token-amount uint))
    (let (
        (current-stx-balance (stx-get-balance (as-contract tx-sender)))
        (current-token-balance (get-token-balance))
    )
        (if (and (> current-stx-balance u0) (> current-token-balance u0))
            (/ (* current-stx-balance token-amount) (+ current-token-balance token-amount))
            u0
        )
    )
)

;; Get graduation progress (0-100)
(define-read-only (get-progress)
    (let ((current-stx (stx-get-balance (as-contract tx-sender))))
        (if (>= current-stx TARGET-STX)
            u100
            (/ (* current-stx u100) TARGET-STX)
        )
    )
)

What this does:

  • get-buyable-tokens: Preview tokens received for STX input (before fees)

  • get-sellable-stx: Preview STX received for token input (before fees)

  • get-progress: Shows how close the token is to "graduation" (0-100%)

Testing Our DEX Contract

Now let's thoroughly test our DEX to ensure it works correctly.

1. Check Contract Compilation

# Verify both contracts compile
clarinet check

You should see successful compilation for both token and DEX contracts.

2. Test with Clarinet Console

# Start interactive console
clarinet console

3. Initialize the DEX

;; First, let's check our starting balances
>> (stx-get-balance 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM)
u100000000000000

>> (contract-call? .token get-balance 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM)
(ok u100000000000000)

;; Transfer 50M tokens to the DEX for trading
>> (contract-call? .token transfer u50000000000000 tx-sender .dex none)
(ok true)

;; Initialize the DEX with token contract, token amount, and virtual STX
>> (contract-call? .dex initialize .token u50000000000000 u600000000)
(ok true)

What this shows:

  • Deployer starts with 100M tokens and 100,000 STX

  • We transfer 50M tokens to the DEX for trading liquidity

  • DEX initialization succeeds and enables trading

4. Verify DEX State

;; Check if DEX is initialized and tradable
>> (contract-call? .dex get-initialized)
(ok true)

>> (contract-call? .dex get-tradable)  
(ok true)

;; Check DEX balances
>> (contract-call? .dex get-stx-balance)
(ok u0)

>> (contract-call? .dex get-token-balance)
(ok u50000000000000)

;; Check associated token
>> (contract-call? .dex get-associated-token)
(ok (some ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token))

What this shows: DEX is properly initialized with 50M tokens ready for trading.

5. Test Token Purchase

;; Switch to a different user for testing
>> ::set_tx_sender ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5

;; Check how many tokens we can buy with 100 STX
>> (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.dex get-buyable-tokens u100000000)
(ok u2885747938752)

;; Actually buy tokens with 100 STX
>> (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.dex buy 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token u100000000)
(ok u2885747938752)

;; Check our new token balance
>> (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token get-balance tx-sender)
(ok u2885747938752)

What this shows:

  • Preview function correctly calculates ~2.89M tokens for 100 STX

  • Buy transaction succeeds and transfers tokens to buyer

  • Bonding curve pricing is working correctly

6. Test Token Sale

;; Test selling 100M tokens back to the DEX
>> (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.dex get-sellable-stx u100000000)
(ok u3597)

;; Actually sell 100M tokens
>> (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.dex sell 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token u100000000)
(ok u3531)

;; Check updated balances
>> (stx-get-balance tx-sender)
u100000003531

>> (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token get-balance tx-sender)
(ok u2785747938752)

What this shows:

  • Sell preview shows ~3,597 micro-STX for 100M tokens

  • Actual sale gives 3,531 micro-STX (after 2% fees)

  • Token balance decreases appropriately

7. Test Progress Tracking

;; Check graduation progress
>> (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.dex get-progress)
(ok u3)

;; Check current STX balance in DEX
>> (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.dex get-stx-balance)
(ok u98000000)

What this shows:

  • DEX has accumulated ~98 STX from trading

  • Progress is 3% toward the 3000 STX graduation target

Understanding the Results

Bonding Curve Mathematics

Our testing demonstrates the constant product formula in action:

  1. Initial State: 50M tokens × 600 STX = 30B constant

  2. After Buy: More STX in pool → fewer tokens → higher price for next buyer

  3. After Sell: More tokens in pool → less STX → lower price for next seller

Fee Distribution

The 2% trading fee is automatically split:

  • 1% goes to platform address for development/maintenance

  • 1% goes to token creator as revenue sharing

Price Discovery

As more trades occur:

  • Buying pressure increases token price

  • Selling pressure decreases token price

  • Price reflects real supply and demand dynamics

Key Learning Points

Automated Market Maker Concepts

  1. Constant Product Formula: Ensures liquidity is always available

  2. Slippage: Large trades have bigger price impact

  3. Virtual Liquidity: Enables immediate trading without upfront capital

  4. Price Impact: Trade size affects final execution price

Smart Contract Security

  1. Authorization Checks: Prevent unauthorized access

  2. Input Validation: Ensure valid amounts and states

  3. Atomic Operations: All-or-nothing transaction execution

  4. Fee Handling: Secure and transparent fee distribution

Clarity Programming Patterns

  1. Trait Usage: Generic interfaces for token interaction

  2. State Management: Proper initialization and state tracking

  3. Error Handling: Comprehensive error codes and validation

  4. Read-Only Functions: Safe data access without state changes

Advanced Features Implemented

1. Dynamic Fee Calculation

;; Fee calculation with safety checks
(define-private (calculate-fees (amount uint))
    (let (
        (fee-amount (/ (* amount FEE-PERCENTAGE) u100))
        (platform-fee (/ fee-amount u2))
        (creator-fee (- fee-amount platform-fee))
    )
        {
            total-fee: fee-amount,
            platform-fee: platform-fee,
            creator-fee: creator-fee,
            net-amount: (- amount fee-amount)
        }
    )
)

2. Graduation System

When STX balance reaches TARGET-STX (3000 STX), the token has "graduated" and could be migrated to a full liquidity pool on other DEXs.

3. Virtual Liquidity

The virtual STX amount creates initial price stability and prevents extreme price swings on the first trades.

Next Steps

Congratulations! You've built a sophisticated bonding curve DEX with:

  • Constant product AMM with automatic pricing

  • Fee distribution system for sustainability

  • Graduation mechanics for successful tokens

  • Comprehensive state management and error handling

  • Real-time data access for frontend integration

In Part 3 of this series, we'll build a modern Next.js frontend that provides a beautiful user interface for deploying tokens and trading through your DEX contracts.

What's Coming Next

  • Next.js 15 with App Router setup

  • Stacks wallet integration with @stacks/connect

  • Real-time trading interface with live price updates

  • Mobile-responsive design with shadcn/ui components

Complete DEX Contract Code

Here's the complete DEX contract for reference:

;; @title Bonding Curve DEX for STX.CITY Mini Version
;; @version 1.0
;; @summary A decentralized exchange facilitating token trading using bonding curve mechanism
;; @description This DEX allows users to buy and sell tokens through a bonding curve, with automatic liquidity provision

;; Import SIP-010 trait
(use-trait sip-010-trait 'STF0V8KWBS70F0WDKTMY65B3G591NN52PR4Z71Y3.sip-010-trait-ft-standard.sip-010-trait)

;; Error constants
(define-constant ERR-UNAUTHORIZED (err u401))
(define-constant ERR-INVALID-AMOUNT (err u402))
(define-constant ERR-INSUFFICIENT-BALANCE (err u403))
(define-constant ERR-SLIPPAGE-TOO-HIGH (err u404))
(define-constant ERR-TRADING-DISABLED (err u1001))
(define-constant ERR-ALREADY-INITIALIZED (err u1002))
(define-constant ERR-NOT-INITIALIZED (err u1003))

;; DEX Configuration
(define-constant TARGET-STX u3000000000) ;; 3000 STX target for graduation
(define-constant VIRTUAL-STX u600000000) ;; 600 STX virtual liquidity
(define-constant FEE-PERCENTAGE u2) ;; 2% trading fee
(define-constant PLATFORM-FEE-ADDRESS 'ST1WTA0YBPC5R6GDMPPJCEDEA6Z2ZEPNMQ4C39W6M)

;; State variables
(define-data-var associated-token (optional principal) none)
(define-data-var initialized bool false)
(define-data-var tradable bool false)
(define-data-var virtual-stx-amount uint VIRTUAL-STX)

;; Initialize the DEX with a token contract
(define-public (initialize (token-contract principal) (initial-token-amount uint) (initial-stx-amount uint))
    (begin
        (asserts! (not (var-get initialized)) ERR-ALREADY-INITIALIZED)
        (var-set associated-token (some token-contract))
        (var-set initialized true)
        (var-set tradable true)
        (var-set virtual-stx-amount initial-stx-amount)
        (ok true)
    )
)

;; Buy tokens with STX using bonding curve
(define-public (buy (token-trait <sip-010-trait>) (stx-amount uint))
    (let (
        (current-stx-balance (stx-get-balance (as-contract tx-sender)))
        (current-token-balance (unwrap-panic (contract-call? token-trait get-balance (as-contract tx-sender))))
        (tokens-out (/ (* current-token-balance stx-amount) (+ current-stx-balance stx-amount)))
        (fee-amount (/ (* stx-amount FEE-PERCENTAGE) u100))
        (platform-fee (/ fee-amount u2))
        (creator-fee (- fee-amount platform-fee))
        (net-stx-amount (- stx-amount fee-amount))
    )
        (asserts! (var-get tradable) ERR-TRADING-DISABLED)
        (asserts! (> stx-amount u0) ERR-INVALID-AMOUNT)
        (asserts! (> tokens-out u0) ERR-INVALID-AMOUNT)

        (try! (stx-transfer? net-stx-amount tx-sender (as-contract tx-sender)))

        (if (> platform-fee u0)
            (try! (stx-transfer? platform-fee tx-sender PLATFORM-FEE-ADDRESS))
            true
        )
        (if (> creator-fee u0)
            (try! (stx-transfer? creator-fee tx-sender (contract-of token-trait)))
            true
        )

        (try! (as-contract (contract-call? token-trait transfer tokens-out tx-sender (tx-sender) none)))
        (ok tokens-out)
    )
)

;; Sell tokens for STX using bonding curve
(define-public (sell (token-trait <sip-010-trait>) (tokens-in uint))
    (let (
        (current-stx-balance (stx-get-balance (as-contract tx-sender)))
        (current-token-balance (unwrap-panic (contract-call? token-trait get-balance (as-contract tx-sender))))
        (stx-out (/ (* current-stx-balance tokens-in) (+ current-token-balance tokens-in)))
        (fee-amount (/ (* stx-out FEE-PERCENTAGE) u100))
        (platform-fee (/ fee-amount u2))
        (creator-fee (- fee-amount platform-fee))
        (net-stx-amount (- stx-out fee-amount))
    )
        (asserts! (var-get tradable) ERR-TRADING-DISABLED)
        (asserts! (> tokens-in u0) ERR-INVALID-AMOUNT)
        (asserts! (> net-stx-amount u0) ERR-INVALID-AMOUNT)

        (try! (contract-call? token-trait transfer tokens-in tx-sender (as-contract tx-sender) none))
        (try! (as-contract (stx-transfer? net-stx-amount tx-sender (tx-sender))))

        (if (> platform-fee u0)
            (try! (as-contract (stx-transfer? platform-fee tx-sender PLATFORM-FEE-ADDRESS)))
            true
        )
        (if (> creator-fee u0)
            (try! (as-contract (stx-transfer? creator-fee tx-sender (contract-of token-trait))))
            true
        )

        (ok net-stx-amount)
    )
)

;; Read-only functions
(define-read-only (get-stx-balance)
    (stx-get-balance (as-contract tx-sender))
)

(define-read-only (get-token-balance)
    (match (var-get associated-token)
        token-contract (unwrap-panic (contract-call? token-contract get-balance (as-contract tx-sender)))
        u0
    )
)

(define-read-only (get-initialized)
    (var-get initialized)
)

(define-read-only (get-tradable)
    (var-get tradable)
)

(define-read-only (get-associated-token)
    (var-get associated-token)
)

(define-read-only (get-buyable-tokens (stx-amount uint))
    (let (
        (current-stx-balance (stx-get-balance (as-contract tx-sender)))
        (current-token-balance (get-token-balance))
    )
        (if (and (> current-stx-balance u0) (> current-token-balance u0))
            (/ (* current-token-balance stx-amount) (+ current-stx-balance stx-amount))
            u0
        )
    )
)

(define-read-only (get-sellable-stx (token-amount uint))
    (let (
        (current-stx-balance (stx-get-balance (as-contract tx-sender)))
        (current-token-balance (get-token-balance))
    )
        (if (and (> current-stx-balance u0) (> current-token-balance u0))
            (/ (* current-stx-balance token-amount) (+ current-token-balance token-amount))
            u0
        )
    )
)

(define-read-only (get-progress)
    (let ((current-stx (stx-get-balance (as-contract tx-sender))))
        (if (>= current-stx TARGET-STX)
            u100
            (/ (* current-stx u100) TARGET-STX)
        )
    )
)

(define-read-only (get-virtual-stx-amount)
    (var-get virtual-stx-amount)
)

This DEX contract provides the foundation for a complete token trading ecosystem. In Part 3, we'll create a beautiful frontend that makes these powerful features accessible to everyday users through a modern web interface!

0
Subscribe to my newsletter

Read articles from Flames directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Flames
Flames