BitBadges
  • Overview
    • ๐Ÿ‘‹BitBadges Overview
    • ๐Ÿ‘จโ€๐Ÿ’ปLearn the Basics
      • BitBadges Claims
      • Multi-Chain Accounts
      • Sign In with BitBadges
      • Badges
      • Address Lists
      • Attestations
      • Applications (Points)
      • Subscriptions
      • Additional Badge Concepts
        • Manager
        • Total Supplys
        • Time-Dependent Ownership
        • Transferability
        • Balances Types
      • Wallets and Sign Ins
        • Supported Wallets
        • Alternate Sign Ins / Mobile
        • Approved Transactors
    • ๐Ÿ”จGetting Started
    • ๐Ÿ’ปHow Do I Check...?
    • ๐Ÿ”How Do I Gate...?
    • ๐ŸŽจUse Cases
    • ๐Ÿ”—Official Links and Resources
    • โš–๏ธBitBadges L1 vs Others
    • ๐Ÿช™Launch Phases
    • ๐ŸŒดEcosystem
      • WordPress Plugin
      • MetaMask Snap
      • Browser Extensions
      • LinkedIn Certifications
      • Blockin
    • ๐ŸคBrand Guidelines
    • โ“FAQ
  • โŒจ๏ธFor Developers
    • ๐Ÿšดโ€โ™‚๏ธGetting Started
    • ๐Ÿ‘คHandling Addresses
    • ๐ŸงชTestnet Mode
    • ๐Ÿ“šBitBadges API
      • Getting Started
      • Full Reference
      • Typed SDK Types
      • Upgrading an API Key Tier
      • Concepts
        • Native Chain Algorithm
        • Refresh / Claim Completion Queue
        • Designing for Compatibility
        • Limits / Restrictions
        • Managing Views
        • Use via Pipedream
    • ๐Ÿ–ฑ๏ธSign In with BitBadges
      • Overview
      • Already Have Web3 Auth?
      • Alternative - P2P Verification
      • Templates and Frameworks
        • WordPress
        • Auth0
        • ExpressJS
        • Discourse
        • Supabase
        • Others
      • Setting Up an App
      • Connecting a Claim
      • Authorization URL
        • Configuration
        • Generating the URL
      • Approaches
        • QR Codes
        • Redirect Callback
      • Verification
        • Verification Flow
        • Access Tokens
        • Offline Verification
        • Security Considerations
      • Blockin Docs
    • ๐Ÿ—๏ธBitBadges Claims
      • Overview
      • Concepts
        • Standard vs On-Demand
        • Completion Methods
        • Gating Badge Distribution
        • Claim Numbers
        • Success Logic
        • Claim Links (URLs)
        • Signed In vs Select Address
        • Universal Approach - Claim Codes
        • Identify By Socials / Emails?
        • Payment Checking
        • Receiving Attestations
      • Checking Custom Criteria
      • Implementing Custom Utility
      • Leveraging AI
      • BitBadges API & Claims
        • Verifying Claim Attempts w/ the API
        • Fetching Claims
        • Auto-Complete Claims w/ BitBadges API
      • Dynamic Stores
        • Overview
        • Adding Data
      • Custom Plugins / Webhooks
        • Overview
        • Pre-Built Webhook Plugins
        • Creating a Custom Plugin
          • Implement Your Plugin
            • Getting Started
            • Hook Types and Simulations
            • Design Considerations
            • Parameters
            • Custom Inputs
            • API Handler
          • Managing Your Plugin
          • Testing Your Plugin
        • Configuration Tools
      • Integrate with Zapier
        • Overview
        • Dynamic Store Zaps
        • Automatic Claim Tutorial
        • Post-Success Zaps
        • Leveraging Zapier AI Actions / MCP
        • Automate Any Part of the Process
          • Google Forms
      • Integrate with Pipedream
        • Overview
        • Leveraging Pipedream MCP
        • Build Custom Plugins
        • Workflow Actions
          • Complete Claim
          • Get Claim Attempt Status
          • Get Claim Code by Idx
          • Add User to Dynamic Store
        • Workflow Triggers
          • Poll Claim Attempts
        • End to End Example
      • In-Site Plugins
        • Plugins Directory
        • Plugin Documentation
        • Ownership Requirements
      • Tutorials
        • In-Site Guides
        • Get Integration User IDs
          • Get Discord User ID
          • Get Discord Server ID
          • X / Twitch / GitHub IDs
        • Add Telegram Bot to Channel
    • โš’๏ธBitBadges JS / SDK
      • Overview
      • SDK Types
      • Common Snippets
        • Address Conversions
        • NumberType Conversions
        • Uint Ranges
        • Balances
        • Transfers
        • Address Lists
        • Badge Metadata
        • Approvals / Transferability
        • Off-Chain Balances
        • Timelines
    • ๐ŸŒŸBadges - Advanced
      • Overview
      • Balances / Transfers
        • ๐Ÿ“ŠBalances
        • โž•Valid Badge IDs
        • ๐Ÿช™Balance Types
        • ๐ŸคTransferability / Approvals
        • โœ…Approval Criteria
          • Overview
          • $BADGE Transfers
          • Override User Level Approvals
          • Approval Trackers
          • Tallied Approval Amounts
          • Max Number of Transfers
          • Predetermined Balances
          • Requires
          • Merkle Challenges
          • Extending the Approval (Advanced)
      • Self-Hosted Balances
        • Overview
        • Examples / Tutorials
          • Indexed
          • Non-Indexed
      • Permissions
        • Overview
        • Action Permission
        • Timed Update Permission
        • Timed Update With Badge Ids Permission
        • Badge IDs Action Permission
        • Update Approval Permission
      • Standards
      • Archived Collections
      • Metadata
      • Timelines
      • Different Time Fields
      • List IDs
      • Uint Ranges
      • Subscriptions Protocol
    • โ›“๏ธBitBadges Blockchain
      • Overview
      • Chain Details
      • REST API Docs - Node
      • Staking / Validators
      • Run a Node
        • Overview
        • Run a Mainnet Node
        • Run a Local Dev Node
        • Cosmovisor
      • Create a Smart Contract
      • ๐Ÿ”ƒCreate, Generate, and Sign Txs
        • Transaction Context
        • Generate Msg Contents
        • Signing - Cosmos
        • Signing - Ethereum
        • Signing - Solana
        • Signing - Bitcoin
        • Broadcast to a Node
        • Sign + Broadcast - bitbadges.io
      • ๐Ÿ“ฉCosmos SDK Msgs
        • x/anchor
          • MsgAddCustomData
        • x/badges
          • MsgCreateCollection
          • MsgUpdateCollection
          • MsgDeleteCollection
          • MsgCreateAddressLists
          • MsgTransferBadges
          • MsgUpdateUserApprovals
          • MsgUniversalUpdateCollection
        • x/wasmx
          • MsgStoreCodeCompat
          • MsgInstantiateContractCompat
          • MsgExecuteContractCompat
        • x/maps
          • MsgCreateMap
          • MsgUpdateMap
          • MsgDeleteMap
          • MsgSetValue
        • MsgSend
        • Cosmos Native Msgs
    • ๐Ÿง Other Concepts
      • Uint Ranges
      • Accounts (Low-Level)
      • Address Lists
      • Maps / Protocols
      • Attestations - Advanced
        • Overview
        • Creating an Attestation
        • Custom Creation Links
        • Proofs vs Attestations
        • Deriving a Proof
        • Design Considerations
        • Verification / Presentations
        • Custom Schemes
          • WITNESS Proofs
Powered by GitBook
On this page
  1. For Developers
  2. Badges - Advanced

Subscriptions Protocol

The subscriptions protocol is a subset of the overall badge standard. We use the folllowing techniques to make it work:

  1. Set up a collection faucet with the durationFromTimestamp + allowOverrideTimestamp + coinTransfers to determine the price

  2. Each user sets up incoming approvals using recurringOwnershipTimes

  3. The MsgTransferBadges specifies overrideTimestamp to match the next recurring interval with the overriden timestamp.

import { AddressList, iCollectionApprovalWithDetails, iUintRange, iUserIncomingApprovalWithDetails, UintRangeArray } from 'bitbadgesjs-sdk';
import crypto from 'crypto';
import { t } from 'i18next';
import { RequiredApprovalProps } from '../../components/transfers/ApprovalSelectHelpers/utils';

export const MONTHLY_MS = 2629746000n;
export const ANNUAL_MS = 31556952000n;

export const subscriptionFaucet = ({
  payoutAddress,
  defaultApprovalToAdd,
  validBadgeIds,
  ubadgeAmount,
  intervalDuration = MONTHLY_MS
}: {
  payoutAddress: string;
  defaultApprovalToAdd: RequiredApprovalProps;
  validBadgeIds: iUintRange<bigint>[];
  ubadgeAmount: bigint;
  intervalDuration?: bigint;
}): RequiredApprovalProps => {
  const id = crypto.randomBytes(32).toString('hex');
  const toSet: RequiredApprovalProps = {
    ...defaultApprovalToAdd,
    details: {
      ...defaultApprovalToAdd.details,
      name: t('Subscription_Faucet', { ns: 'common' }),
      description: t('subscription_faucet_description', { ns: 'common' })
    },
    initiatedByListId: 'All',
    initiatedByList: AddressList.AllAddresses(),
    transferTimes: UintRangeArray.FullRanges(),
    badgeIds: validBadgeIds,
    ownershipTimes: UintRangeArray.FullRanges(),
    approvalId: id,
    approvalCriteria: {
      ...defaultApprovalToAdd.approvalCriteria,
      coinTransfers: [
        {
          to: payoutAddress,
          overrideFromWithApproverAddress: false,
          overrideToWithInitiator: false,
          coins: [
            {
              amount: ubadgeAmount,
              denom: 'ubadge'
            }
          ]
        }
      ],
      predeterminedBalances: {
        manualBalances: [],
        orderCalculationMethod: {
          useOverallNumTransfers: true,
          usePerToAddressNumTransfers: false,
          usePerFromAddressNumTransfers: false,
          usePerInitiatedByAddressNumTransfers: false,
          useMerkleChallengeLeafIndex: false,
          challengeTrackerId: ''
        },
        incrementedBalances: {
          startBalances: [{ amount: 1n, badgeIds: [{ start: 1n, end: 1n }], ownershipTimes: UintRangeArray.FullRanges() }],
          incrementBadgeIdsBy: 0n,
          incrementOwnershipTimesBy: 0n,

          //monthly in milliseconds
          durationFromTimestamp: intervalDuration ? intervalDuration : MONTHLY_MS,
          allowOverrideTimestamp: true,
          recurringOwnershipTimes: {
            startTime: 0n,
            intervalLength: 0n,
            chargePeriodLength: 0n
          }
        }
      },
      maxNumTransfers: {
        overallMaxNumTransfers: 0n,
        perToAddressMaxNumTransfers: 0n,
        perFromAddressMaxNumTransfers: 0n,
        perInitiatedByAddressMaxNumTransfers: 0n,
        amountTrackerId: id,
        resetTimeIntervals: {
          startTime: 0n,
          intervalLength: 0n
        }
      },
      approvalAmounts: {
        overallApprovalAmount: 0n,
        perFromAddressApprovalAmount: 0n,
        perToAddressApprovalAmount: 0n,
        perInitiatedByAddressApprovalAmount: 0n,
        amountTrackerId: id,
        resetTimeIntervals: {
          startTime: 0n,
          intervalLength: 0n
        }
      },
      merkleChallenges: [],
      requireToEqualsInitiatedBy: false,
      requireFromEqualsInitiatedBy: false,
      overridesFromOutgoingApprovals: true,
      overridesToIncomingApprovals: false
    }
  };

  return toSet;
};

export const userRecurringApproval = ({
  subscriptionApproval,
  firstIntervalStartTime,
  ubadgeTipAmount,
  transferTimes
}: {
  subscriptionApproval: iCollectionApprovalWithDetails<bigint>;
  firstIntervalStartTime: bigint;
  ubadgeTipAmount: bigint;
  transferTimes: UintRangeArray<bigint>;
}) => {
  const id = crypto.randomBytes(32).toString('hex');

  const badgeIds = [{ start: 1n, end: 1n }];
  const intervalLength = BigInt(subscriptionApproval.approvalCriteria?.predeterminedBalances?.incrementedBalances.durationFromTimestamp ?? 0);
  const chargePeriodLength = BigInt(Math.min(Number(intervalLength), 604800000));
  const subscriptionAmount = subscriptionApproval.approvalCriteria?.coinTransfers?.[0]?.coins?.[0]?.amount ?? 0n;

  return {
    fromList: AddressList.Reserved('Mint'),
    fromListId: 'Mint',
    initiatedByList: AddressList.AllAddresses(),
    initiatedByListId: 'All',
    transferTimes: transferTimes,
    badgeIds: badgeIds,
    ownershipTimes: UintRangeArray.FullRanges(),
    approvalId: id,
    approvalCriteria: {
      coinTransfers: [
        {
          to: '',
          overrideFromWithApproverAddress: true,
          overrideToWithInitiator: true,
          coins: [
            {
              amount: ubadgeTipAmount + subscriptionAmount,
              denom: 'ubadge'
            }
          ]
        }
      ],
      predeterminedBalances: {
        manualBalances: [],
        orderCalculationMethod: {
          useOverallNumTransfers: true,
          usePerToAddressNumTransfers: false,
          usePerFromAddressNumTransfers: false,
          usePerInitiatedByAddressNumTransfers: false,
          useMerkleChallengeLeafIndex: false,
          challengeTrackerId: ''
        },
        incrementedBalances: {
          // We override the start balances to be the badge ids from the subscription approval
          // This is to get the correct badge IDs and amounts. Times will be overridden by the recurring approval
          startBalances: [{ amount: 1n, badgeIds: badgeIds, ownershipTimes: UintRangeArray.FullRanges() }],
          incrementBadgeIdsBy: 0n,
          incrementOwnershipTimesBy: 0n,
          durationFromTimestamp: 0n,
          allowOverrideTimestamp: false,

          recurringOwnershipTimes: {
            startTime: firstIntervalStartTime,
            intervalLength: intervalLength,
            //1 week in milliseconds
            chargePeriodLength: chargePeriodLength
          }
        }
      },
      maxNumTransfers: {
        overallMaxNumTransfers: 1n,
        perToAddressMaxNumTransfers: 0n,
        perFromAddressMaxNumTransfers: 0n,
        perInitiatedByAddressMaxNumTransfers: 0n,
        amountTrackerId: id,
        resetTimeIntervals: {
          startTime: firstIntervalStartTime,
          intervalLength: intervalLength
        }
      },
      approvalAmounts: {
        overallApprovalAmount: 0n,
        perFromAddressApprovalAmount: 0n,
        perToAddressApprovalAmount: 0n,
        perInitiatedByAddressApprovalAmount: 0n,
        amountTrackerId: id,
        resetTimeIntervals: {
          startTime: 0n,
          intervalLength: 0n
        }
      },
      merkleChallenges: [],
      requireToEqualsInitiatedBy: false,
      requireFromEqualsInitiatedBy: false
    },
    details: {
      ...subscriptionApproval.details,
      name: t('Recurring_Approval', { ns: 'common' }),
      description: t('recurring_approval_description', { ns: 'common' })
    },
    version: 0n
  } as iUserIncomingApprovalWithDetails<bigint>;
};
PreviousUint RangesNextBitBadges Blockchain

Last updated 2 days ago

โŒจ๏ธ
๐ŸŒŸ