> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chainstack.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Solana: Subscriptions and allowances

> Build native onchain recurring payments and delegated spending on Solana in Python — the Subscription Authority PDA model, the three delegation types, and a full create-plan, subscribe, and collect lifecycle against a Chainstack node.

**TLDR:**

* On June 2, 2026, the Solana Foundation shipped **Subscriptions & Allowances** — an audited, open-source reference program (program ID `De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44`) that makes recurring billing and delegated spending native to Solana, live on mainnet and devnet.
* The trick it solves: an SPL token account can have **only one delegate**. The program routes every arrangement through a per-(user, mint) **Subscription Authority** PDA that holds the single `u64::MAX` approval, then gates each transfer through individual delegation PDAs — so one token account can power unlimited simultaneous subscriptions and allowances.
* It exposes three models: **fixed delegations** (allowances), **recurring delegations**, and **subscription plans**.
* There is no Python SDK — the official TypeScript client is even Codama-generated — so we build the instructions by hand in Python with `solders` and run the full create-plan, subscribe, and collect lifecycle against a Chainstack Solana devnet node.

## What shipped

On June 2, 2026, the Solana Foundation released [Subscriptions & Allowances](https://solana.com/news/subscriptions-and-allowances): a single shared program that any team can build on for recurring billing and delegated spending, instead of stitching together off-chain billing infrastructure.

It is a Solana Foundation reference program — repo [`solana-program/subscriptions`](https://github.com/solana-program/subscriptions) — written in [Pinocchio](/docs/solana-pinocchio-vs-quasar) (a lightweight `no_std` framework), with IDL and clients generated by Codama, audited by Cantina, and deployed under a Squads multisig upgrade authority. Design partners at launch include Helius (API-tier billing), Confirmo, Dynamic, Majority, Mesh, and Meow.

This guide explains the one idea that makes the whole thing work, disambiguates it from the two adjacent primitives people confuse it with, and then walks the full lifecycle in Python against a Chainstack node.

## The one-delegate problem

Solana's SPL Token program already lets you hand spending authority to someone else. The `Approve` instruction records a **delegate** and an **allowance** on your token account: the delegate can move up to that many tokens out of your account without you signing each transfer. `Revoke` clears it. This is the primitive behind every "approve then pull" flow on Solana.

It has one hard limit, stated plainly in the program's architecture notes:

> Solana's SPL Token delegate model allows only **one delegate per token account**.

Approving a new delegate silently overwrites the previous one. So a single USDC account can authorize exactly one service at a time. Want to subscribe to two newsletters and give an AI agent a budget, all from the same balance? You can't — the third approval wipes the first two. This constraint has been an open request since 2022 ([solana-program-library issue #3858](https://github.com/solana-labs/solana-program-library/issues/3858)).

The Subscriptions program fixes it with one level of indirection.

## Not to be confused with

Before the mechanics, clear up three things that sound alike:

| Primitive                          | What it is                                                                                                                    | Limit                               |
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
| **Approve delegate** (`spl-token`) | The raw SPL Token instruction that sets one delegate + allowance on a token account                                           | One delegate per account            |
| **Spend permissions**              | Solana's product framing of that same approve-delegate primitive for builders                                                 | Same — one delegate per account     |
| **Subscriptions program**          | A program *built on top of* approve-delegate that multiplexes many policy-controlled delegations behind a single PDA delegate | Unlimited simultaneous arrangements |

"Spend permissions" on Solana is not a separate mechanism — it is the approve-delegate primitive described from a payments angle. The Subscriptions program is the thing that removes the one-delegate ceiling.

## How it works

For each `(user, mint)` pair, the program derives a **Subscription Authority** (SA) PDA:

```text theme={"system"}
seeds = ["SubscriptionAuthority", user_pubkey, mint_pubkey]
```

On initialization, the program calls SPL `Approve` so the SA PDA becomes the user's **single delegate, with a `u64::MAX` allowance**. From the token program's point of view there is exactly one delegate — no conflict with the one-delegate rule.

The SA itself can't move anything. It only transfers when a separate **delegation PDA** authorizes a specific amount to a specific party. Those delegation PDAs are where all the real limits live:

```text theme={"system"}
delegation (fixed/recurring) = ["delegation", subscription_authority, delegator, delegatee, nonce]
subscription                  = ["subscription", plan_pda, subscriber]
```

So the SPL allowance is a blunt "yes, everything" switch, and the program is the policy engine in front of it. You can create as many delegation PDAs as you like against the one SA — each with its own cap, cadence, expiry, and authorized puller. That is how a single token account supports unlimited concurrent subscriptions and allowances while staying exactly as safe as a normal approve-delegate (the SA can never move funds without an active delegation that permits it).

<Note>
  Each delegation PDA records the SA's `init_id` (the slot at which the SA was created). Every transfer checks it against the live SA. Closing and re-creating the SA in a later slot changes `init_id`, which invalidates old delegations — a coarse kill switch. To stop one specific delegation immediately, revoke that delegation PDA.
</Note>

## The three models

One program, three delegation types:

| Model                            | Who creates it  | Cap behavior                                                             | Who can pull                             |
| -------------------------------- | --------------- | ------------------------------------------------------------------------ | ---------------------------------------- |
| **Fixed delegation** (allowance) | The token owner | One total `amount`, decremented per transfer; optional expiry            | The delegatee only                       |
| **Recurring delegation**         | The token owner | `amount_per_period` that resets each period; skipped periods don't stack | The delegatee only                       |
| **Subscription plan**            | The merchant    | Plan-defined `amount` per `period_hours`, resets each period             | Plan owner + up to 4 whitelisted pullers |

Fixed and recurring delegations are pull arrangements the **user** sets up for a specific delegatee — ideal for allowances and payroll. Subscription plans flip the direction: a **merchant** publishes immutable terms once, and any number of users subscribe to the same plan, each getting their own subscription PDA. The merchant (or a whitelisted puller) collects each cycle.

Subscription plan terms (`amount`, `period_hours`, and an on-chain `created_at` fingerprint) are immutable and are snapshotted into each subscriber's PDA at subscribe time. A merchant can't change the price out from under existing subscribers — and can't delete-and-recreate a plan at the same address with new terms, because the snapshot would no longer match (you'd get a `PlanTermsMismatch`).

## Token-2022 and stablecoins

The program works with both SPL Token and Token-2022 mints, but Token-2022 extensions that would make delegated transfers unsafe are a problem.

<Warning>
  The program's README and the official docs state that seven extensions are rejected at SA initialization: `ConfidentialTransfer`, `NonTransferable`, `PermanentDelegate`, `TransferHook`, `TransferFee`, `MintCloseAuthority`, and `Pausable`. That is the **design intent**, and it's the right mental model for what mints are safe.

  The shipped code is narrower: the runtime check (`validate_mint_extensions`) currently rejects only a mint with a **configured `TransferHook`** (an inert, permanently-unconfigured hook is allowed). The `ConfidentialTransfer` error code is even marked "reserved for backwards compatibility." So don't rely on the program to bounce a fee-bearing or permanent-delegate mint for you — treat the seven-item list as the set of mints you should not use, and pick your mint deliberately.
</Warning>

In practice:

* **USDC works** — it's a plain mint with no problematic extensions. (Solana's announcement copy says the program works "including confidential transfers"; that contradicts both the README and the code, so don't build on it.)
* **PYUSD does not** — PayPal's Solana mint carries Token-2022 extensions (permanent delegate / transfer fee) that the design rules out for delegated pulls.

We'll use a plain SPL mint for the walkthrough.

## Prerequisites

* Python 3.10+
* `pip install solana solders` (the `solana` package ships the `spl.token` helpers we use for mint setup)
* A Chainstack Solana **devnet** node endpoint
* Some devnet SOL from the [Chainstack faucet](https://faucet.chainstack.com/solana-testnet-faucet) for fees and rent

<Check>
  ### Run Solana mainnet and devnet nodes on Chainstack

  [Start for free](https://console.chainstack.com/) and get your app to production levels immediately. No credit card required. You can sign up with your GitHub, X, Google, or Microsoft account.
</Check>

To get a devnet endpoint: in the [Chainstack console](https://console.chainstack.com/), deploy a Solana devnet node and copy its HTTPS endpoint. We'll refer to it as `CHAINSTACK_DEVNET` below.

## Build it in Python

There is no Python SDK for this program, and the official TypeScript client is Codama-generated (and gitignored in the repo) — so the cleanest path in Python is to build the instructions ourselves. The program makes this pleasant: every instruction is a **single leading discriminator byte** followed by a fixed, C-packed, little-endian payload — no Borsh length prefixes, no 8-byte Anchor hashes.

<Steps>
  <Step title="Constants and helpers">
    The program IDs, PDA derivation, ATA derivation, and a small instruction-data encoder.

    ```python theme={"system"}
    import time
    import struct

    from solana.rpc.api import Client
    from solders.keypair import Keypair
    from solders.pubkey import Pubkey
    from solders.instruction import Instruction, AccountMeta
    from solders.message import MessageV0
    from solders.transaction import VersionedTransaction
    from solders.system_program import transfer, TransferParams
    from spl.token.client import Token
    from spl.token.constants import TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID
    from solana.rpc.commitment import Confirmed

    CHAINSTACK_DEVNET = "YOUR_CHAINSTACK_SOLANA_DEVNET_ENDPOINT"

    PROGRAM_ID = Pubkey.from_string("De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44")
    SYS_PROGRAM = Pubkey.from_string("11111111111111111111111111111111")

    # The program's "anchor:event" CPI authority is a constant PDA.
    EVENT_AUTHORITY = Pubkey.find_program_address([b"event_authority"], PROGRAM_ID)[0]

    # Pin the client to "confirmed" so reads and confirmations don't wait on finalization.
    client = Client(CHAINSTACK_DEVNET, commitment=Confirmed)


    # --- PDA derivation (seeds taken verbatim from the program) ---

    def subscription_authority_pda(user: Pubkey, mint: Pubkey) -> tuple[Pubkey, int]:
        return Pubkey.find_program_address(
            [b"SubscriptionAuthority", bytes(user), bytes(mint)], PROGRAM_ID
        )


    def plan_pda(owner: Pubkey, plan_id: int) -> tuple[Pubkey, int]:
        return Pubkey.find_program_address(
            [b"plan", bytes(owner), plan_id.to_bytes(8, "little")], PROGRAM_ID
        )


    def subscription_pda(plan: Pubkey, subscriber: Pubkey) -> tuple[Pubkey, int]:
        return Pubkey.find_program_address(
            [b"subscription", bytes(plan), bytes(subscriber)], PROGRAM_ID
        )


    def ata(owner: Pubkey, mint: Pubkey) -> Pubkey:
        return Pubkey.find_program_address(
            [bytes(owner), bytes(TOKEN_PROGRAM_ID), bytes(mint)],
            ASSOCIATED_TOKEN_PROGRAM_ID,
        )[0]


    def meta(pubkey: Pubkey, signer: bool, writable: bool) -> AccountMeta:
        return AccountMeta(pubkey=pubkey, is_signer=signer, is_writable=writable)


    def send(ixs: list[Instruction], payer: Keypair, signers: list[Keypair]) -> str:
        blockhash = client.get_latest_blockhash().value.blockhash
        msg = MessageV0.try_compile(payer.pubkey(), ixs, [], blockhash)
        tx = VersionedTransaction(msg, signers)
        sig = client.send_raw_transaction(bytes(tx)).value
        client.confirm_transaction(sig, commitment=Confirmed)
        return str(sig)
    ```
  </Step>

  <Step title="Instruction builders">
    Each builder returns a `solders.Instruction`. The account order and discriminators come straight from the program's instruction handlers.

    ```python theme={"system"}
    # Instruction discriminators (single leading byte).
    INIT_SUBSCRIPTION_AUTHORITY = 0
    CREATE_PLAN = 7
    SUBSCRIBE = 11
    TRANSFER_SUBSCRIPTION = 10


    def ix_init_subscription_authority(user: Pubkey, mint: Pubkey) -> Instruction:
        sa, _ = subscription_authority_pda(user, mint)
        accounts = [
            meta(user, signer=True, writable=True),
            meta(sa, signer=False, writable=True),
            meta(mint, signer=False, writable=False),
            meta(ata(user, mint), signer=False, writable=True),
            meta(SYS_PROGRAM, signer=False, writable=False),
            meta(TOKEN_PROGRAM_ID, signer=False, writable=False),
        ]
        return Instruction(PROGRAM_ID, bytes([INIT_SUBSCRIPTION_AUTHORITY]), accounts)


    def ix_create_plan(
        merchant: Pubkey,
        mint: Pubkey,
        plan_id: int,
        amount: int,
        period_hours: int,
        end_ts: int = 0,
        metadata_uri: str = "",
    ) -> Instruction:
        plan, _ = plan_pda(merchant, plan_id)

        # PlanData (456 bytes): plan_id u64, mint, terms{amount u64, period_hours u64,
        # created_at i64}, end_ts i64, destinations[4], pullers[4], metadata_uri[128].
        # created_at is sent as 0 and overwritten on-chain with the current clock.
        data = bytearray([CREATE_PLAN])
        data += struct.pack("<Q", plan_id)
        data += bytes(mint)
        data += struct.pack("<QQq", amount, period_hours, 0)   # terms
        data += struct.pack("<q", end_ts)
        data += bytes(32 * 4)                                  # destinations: any
        data += bytes(32 * 4)                                  # pullers: owner only
        uri = metadata_uri.encode("utf-8")[:128]
        data += uri + bytes(128 - len(uri))                    # fixed 128-byte string

        accounts = [
            meta(merchant, signer=True, writable=True),
            meta(plan, signer=False, writable=True),
            meta(mint, signer=False, writable=False),
            meta(SYS_PROGRAM, signer=False, writable=False),
            meta(TOKEN_PROGRAM_ID, signer=False, writable=False),
        ]
        return Instruction(PROGRAM_ID, bytes(data), accounts)


    def ix_subscribe(subscriber: Pubkey, merchant: Pubkey, mint: Pubkey, plan_id: int) -> Instruction:
        plan, plan_bump = plan_pda(merchant, plan_id)
        sub, _ = subscription_pda(plan, subscriber)
        sa, _ = subscription_authority_pda(subscriber, mint)

        # The subscriber consents to the exact live plan terms. Read them from chain
        # so a stale signed transaction can't bind the user to different terms.
        amount, period_hours, created_at = read_plan_terms(plan)
        init_id = read_sa_init_id(sa)

        # SubscribeData (73 bytes): plan_id u64, plan_bump u8, expected_mint,
        # expected_amount u64, expected_period_hours u64, expected_created_at i64,
        # expected_sa_init_id i64.
        data = bytearray([SUBSCRIBE])
        data += struct.pack("<QB", plan_id, plan_bump)
        data += bytes(mint)
        data += struct.pack("<QQqq", amount, period_hours, created_at, init_id)

        accounts = [
            meta(subscriber, signer=True, writable=True),
            meta(merchant, signer=False, writable=False),
            meta(plan, signer=False, writable=False),
            meta(sub, signer=False, writable=True),
            meta(sa, signer=False, writable=False),
            meta(SYS_PROGRAM, signer=False, writable=False),
            meta(EVENT_AUTHORITY, signer=False, writable=False),
            meta(PROGRAM_ID, signer=False, writable=False),   # self program
        ]
        return Instruction(PROGRAM_ID, bytes(data), accounts)


    def ix_transfer_subscription(
        caller: Pubkey, subscriber: Pubkey, merchant: Pubkey, mint: Pubkey,
        plan_id: int, amount: int, receiver: Pubkey,
    ) -> Instruction:
        plan, _ = plan_pda(merchant, plan_id)
        sub, _ = subscription_pda(plan, subscriber)
        sa, _ = subscription_authority_pda(subscriber, mint)

        # TransferData (72 bytes): amount u64, delegator (the subscriber), mint.
        data = bytearray([TRANSFER_SUBSCRIPTION])
        data += struct.pack("<Q", amount)
        data += bytes(subscriber)
        data += bytes(mint)

        accounts = [
            meta(sub, signer=False, writable=True),
            meta(plan, signer=False, writable=False),
            meta(sa, signer=False, writable=False),
            meta(ata(subscriber, mint), signer=False, writable=True),
            meta(ata(receiver, mint), signer=False, writable=True),
            meta(caller, signer=True, writable=False),
            meta(mint, signer=False, writable=False),
            meta(TOKEN_PROGRAM_ID, signer=False, writable=False),
            meta(EVENT_AUTHORITY, signer=False, writable=False),
            meta(PROGRAM_ID, signer=False, writable=False),   # self program
        ]
        return Instruction(PROGRAM_ID, bytes(data), accounts)
    ```

    The two read helpers decode the raw account bytes. Both accounts are `#[repr(C, packed)]`, so the offsets are exact:

    ```python theme={"system"}
    def read_sa_init_id(sa: Pubkey) -> int:
        # SubscriptionAuthority: disc(1) user(32) mint(32) payer(32) bump(1) init_id(i64).
        data = client.get_account_info(sa).value.data
        return int.from_bytes(data[98:106], "little", signed=True)


    def read_plan_terms(plan: Pubkey) -> tuple[int, int, int]:
        # Plan: disc(1) owner(32) bump(1) status(1) then PlanData at offset 35.
        # PlanData: plan_id(35:43) mint(43:75) amount(75:83) period_hours(83:91)
        #           created_at(91:99) ...
        data = client.get_account_info(plan).value.data
        amount = int.from_bytes(data[75:83], "little")
        period_hours = int.from_bytes(data[83:91], "little")
        created_at = int.from_bytes(data[91:99], "little", signed=True)
        return amount, period_hours, created_at
    ```
  </Step>

  <Step title="Fund the wallets">
    We need two wallets — a merchant and a subscriber — and some devnet SOL for fees and rent. Managed devnet nodes have `requestAirdrop` disabled, so we fund the merchant from the [Chainstack faucet](https://faucet.chainstack.com/solana-testnet-faucet) and then send the subscriber a little SOL from the merchant.

    ```python theme={"system"}
    merchant = Keypair()
    subscriber = Keypair()

    print("Fund this merchant address from the Chainstack faucet, then continue:")
    print(merchant.pubkey())
    ```

    Paste the merchant address into the [Chainstack Solana faucet](https://faucet.chainstack.com/solana-testnet-faucet) to receive 1 devnet SOL, then run the rest:

    ```python theme={"system"}
    # Wait until the merchant is funded.
    while client.get_balance(merchant.pubkey()).value == 0:
        time.sleep(2)

    # Send the subscriber 0.2 SOL for rent and fees.
    send(
        [transfer(TransferParams(
            from_pubkey=merchant.pubkey(),
            to_pubkey=subscriber.pubkey(),
            lamports=200_000_000,
        ))],
        payer=merchant,
        signers=[merchant],
    )
    print("subscriber funded:", client.get_balance(subscriber.pubkey()).value / 1e9, "SOL")
    ```
  </Step>

  <Step title="Create a mint and give the subscriber a balance">
    A fresh 6-decimal SPL mint, an ATA for each wallet, and 100 test tokens for the subscriber to be billed against. We use a plain SPL mint so there are no Token-2022 extensions to worry about.

    ```python theme={"system"}
    # Create a 6-decimal mint with the merchant as mint authority.
    token = Token.create_mint(
        conn=client,
        payer=merchant,
        mint_authority=merchant.pubkey(),
        decimals=6,
        program_id=TOKEN_PROGRAM_ID,
    )
    MINT = token.pubkey

    # Create ATAs and mint 100 test tokens to the subscriber.
    token.create_associated_token_account(subscriber.pubkey())
    token.create_associated_token_account(merchant.pubkey())
    token.mint_to(
        dest=ata(subscriber.pubkey(), MINT),
        mint_authority=merchant,
        amount=100_000_000,  # 100.0 tokens at 6 decimals
    )
    print("mint:", MINT)
    ```
  </Step>

  <Step title="Initialize the subscriber's Subscription Authority">
    This creates the SA PDA and approves it as the `u64::MAX` delegate on the subscriber's token account. The subscriber must sign — a sponsor can pay rent but cannot approve on someone else's account.

    ```python theme={"system"}
    sig = send(
        [ix_init_subscription_authority(subscriber.pubkey(), MINT)],
        payer=subscriber,
        signers=[subscriber],
    )
    print("init SA:", sig)
    ```
  </Step>

  <Step title="Merchant publishes a plan">
    A plan billing 1 token per day. `period_hours` is the billing period, bounded to 1–8760 (up to one year); we pass `24`.

    ```python theme={"system"}
    PLAN_ID = 1
    sig = send(
        [ix_create_plan(
            merchant=merchant.pubkey(),
            mint=MINT,
            plan_id=PLAN_ID,
            amount=1_000_000,     # 1.0 token per period
            period_hours=24,      # daily
            metadata_uri="https://example.com/plan.json",
        )],
        payer=merchant,
        signers=[merchant],
    )
    print("create plan:", sig)
    ```
  </Step>

  <Step title="Subscriber subscribes">
    `ix_subscribe` reads the live plan terms and the SA `init_id` from chain and binds them into the instruction as consent fields. The program rejects the subscribe if the live plan disagrees.

    ```python theme={"system"}
    sig = send(
        [ix_subscribe(subscriber.pubkey(), merchant.pubkey(), MINT, PLAN_ID)],
        payer=subscriber,
        signers=[subscriber],
    )
    print("subscribe:", sig)
    ```
  </Step>

  <Step title="Merchant collects the first payment">
    The merchant pulls one period's amount from the subscriber into the merchant's token account. The program enforces that the caller is the plan owner (or a whitelisted puller), the receiver is allowed, and the per-period cap isn't exceeded.

    ```python theme={"system"}
    sig = send(
        [ix_transfer_subscription(
            caller=merchant.pubkey(),
            subscriber=subscriber.pubkey(),
            merchant=merchant.pubkey(),
            mint=MINT,
            plan_id=PLAN_ID,
            amount=1_000_000,           # this period's charge
            receiver=merchant.pubkey(),
        )],
        payer=merchant,
        signers=[merchant],
    )
    print("collect:", sig)

    bal = client.get_token_account_balance(ata(merchant.pubkey(), MINT)).value
    print("merchant balance:", bal.ui_amount_string)
    ```

    A second collect inside the same 24-hour period fails with `AmountExceedsPeriodLimit` — the cap resets only when the period rolls over. Skipped periods don't accumulate.
  </Step>
</Steps>

That's the full lifecycle: one SA approval, one published plan, any number of subscribers, and a pull each cycle — all onchain, no billing backend.

## What's next

This guide covered the user-facing lifecycle. Two follow-ups build on it:

* Indexing subscription events with Yellowstone gRPC — the program emits create, cancel, and pull events as Anchor-compatible inner-instruction CPIs. Streaming them through a Chainstack Geyser endpoint gives you live revenue, churn, and failed-collection dashboards without polling.
* Capped onchain budgets for AI agents — the fixed-delegation model is the natural way to hand an autonomous agent a spending allowance, and it pairs with x402 for per-call agentic commerce.

## Conclusion

Subscriptions & Allowances turns recurring revenue from custom infrastructure into a shared, audited onchain primitive. The whole design rests on one idea — route every arrangement through a single Subscription Authority delegate and enforce the real limits in per-delegation PDAs — which is what lifts the one-delegate-per-account ceiling that blocked this for years.

Because there's no Python SDK, building the instructions by hand is also the best way to understand exactly what the program checks on every pull. Point the script at a Chainstack devnet node, run it top to bottom, and you have a working subscription in a few seconds.

### About the author

<CardGroup>
  <Card title="Ake">
    <img src="https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max&auto=format&n=UN3rP7zhB69idvnC&q=85&s=792a24ab1b4682406fa589c0ecd88e5d" alt="Ake" style={{width: '80px', height: '80px', borderRadius: '50%', objectFit: 'cover', display: 'block', margin: '0 auto'}} noZoom width="400" height="400" data-path="images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg" />

    <Icon icon="code" iconType="solid" /> Director of Developer Experience @ Chainstack
    <br /><Icon icon="screwdriver-wrench" iconType="solid" /> Talk to me all things Web3
    <br />20 years in technology | 8+ years in Web3 full time years experience

    <div style={{display: "flex", justifyContent: "center", gap: "12px"}}>
      <a href="https://github.com/akegaviar/" style={{textDecoration: "none", borderBottom: "none"}}>
        <Icon icon="github" iconType="brands" />
      </a>

      <a href="https://twitter.com/akegaviar" style={{textDecoration: "none", borderBottom: "none"}}>
        <Icon icon="twitter" iconType="brands" />
      </a>

      <a href="https://www.linkedin.com/in/ake/" style={{textDecoration: "none", borderBottom: "none"}}>
        <Icon icon="linkedin" iconType="brands" />
      </a>
    </div>
  </Card>
</CardGroup>
