Source Code

;; SIP-010 Fungible Token Standard Implementation
;; A basic fungible token contract following the SIP-010 standard

;; Token trait implementation
(impl-trait 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.sip-010-trait)

;; Constants
(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u100))
(define-constant err-not-token-owner (err u101))
(define-constant err-insufficient-balance (err u102))
(define-constant err-invalid-amount (err u103))

;; Token configuration
(define-constant token-name "StackMart Tips")
(define-constant token-symbol "SMT")
(define-constant token-decimals u6)
(define-constant token-uri (some u"https://stackmart.io/token-metadata.json"))

;; Total supply (1 billion tokens with 6 decimals)
(define-constant total-supply u1000000000000000)

;; Data variables
(define-data-var token-total-supply uint total-supply)

;; Data maps
(define-map token-balances principal uint)
(define-map token-allowances {owner: principal, spender: principal} uint)

;; Initialize contract with total supply to contract owner
(map-set token-balances contract-owner total-supply)

;; SIP-010 Standard Functions

;; Get token name
(define-read-only (get-name)
  (ok token-name))

;; Get token symbol  
(define-read-only (get-symbol)
  (ok token-symbol))

;; Get token decimals
(define-read-only (get-decimals)
  (ok token-decimals))

;; Get token balance of a principal
(define-read-only (get-balance (who principal))
  (ok (default-to u0 (map-get? token-balances who))))

;; Get total supply
(define-read-only (get-total-supply)
  (ok (var-get token-total-supply)))

;; Get token URI
(define-read-only (get-token-uri)
  (ok token-uri))
;; Transfer function
(define-public (transfer (amount uint) (from principal) (to principal) (memo (optional (buff 34))))
  (begin
    (asserts! (or (is-eq from tx-sender) (is-eq from contract-caller)) err-not-token-owner)
    (asserts! (> amount u0) err-invalid-amount)
    (let ((from-balance (default-to u0 (map-get? token-balances from))))
      (asserts! (>= from-balance amount) err-insufficient-balance)
      (try! (ft-transfer? smt-token amount from to))
      (print {action: "transfer", from: from, to: to, amount: amount, memo: memo})
      (ok true))))
;; Define the fungible token
(define-fungible-token smt-token total-supply)

;; Mint function (owner only)
(define-public (mint (amount uint) (recipient principal))
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (asserts! (> amount u0) err-invalid-amount)
    (try! (ft-mint? smt-token amount recipient))
    (print {action: "mint", recipient: recipient, amount: amount})
    (ok true)))
;; Burn function
(define-public (burn (amount uint) (owner principal))
  (begin
    (asserts! (or (is-eq owner tx-sender) (is-eq owner contract-caller)) err-not-token-owner)
    (asserts! (> amount u0) err-invalid-amount)
    (try! (ft-burn? smt-token amount owner))
    (print {action: "burn", owner: owner, amount: amount})
    (ok true)))
;; Get allowance
(define-read-only (get-allowance (owner principal) (spender principal))
  (ok (default-to u0 (map-get? token-allowances {owner: owner, spender: spender}))))

;; Approve function
(define-public (approve (spender principal) (amount uint))
  (begin
    (asserts! (> amount u0) err-invalid-amount)
    (map-set token-allowances {owner: tx-sender, spender: spender} amount)
    (print {action: "approve", owner: tx-sender, spender: spender, amount: amount})
    (ok true)))
;; Transfer from function (for approved spending)
(define-public (transfer-from (amount uint) (owner principal) (recipient principal) (memo (optional (buff 34))))
  (let ((allowance (default-to u0 (map-get? token-allowances {owner: owner, spender: tx-sender}))))
    (asserts! (>= allowance amount) err-insufficient-balance)
    (asserts! (> amount u0) err-invalid-amount)
    (try! (ft-transfer? smt-token amount owner recipient))
    (map-set token-allowances {owner: owner, spender: tx-sender} (- allowance amount))
    (print {action: "transfer-from", owner: owner, recipient: recipient, amount: amount, memo: memo})
    (ok true)))
;; Increase allowance
(define-public (increase-allowance (spender principal) (amount uint))
  (let ((current-allowance (default-to u0 (map-get? token-allowances {owner: tx-sender, spender: spender}))))
    (asserts! (> amount u0) err-invalid-amount)
    (map-set token-allowances {owner: tx-sender, spender: spender} (+ current-allowance amount))
    (print {action: "increase-allowance", owner: tx-sender, spender: spender, amount: amount})
    (ok true)))
;; Decrease allowance
(define-public (decrease-allowance (spender principal) (amount uint))
  (let ((current-allowance (default-to u0 (map-get? token-allowances {owner: tx-sender, spender: spender}))))
    (asserts! (> amount u0) err-invalid-amount)
    (asserts! (>= current-allowance amount) err-insufficient-balance)
    (map-set token-allowances {owner: tx-sender, spender: spender} (- current-allowance amount))
    (print {action: "decrease-allowance", owner: tx-sender, spender: spender, amount: amount})
    (ok true)))
;; Revoke allowance (set to zero)
(define-public (revoke-allowance (spender principal))
  (begin
    (map-delete token-allowances {owner: tx-sender, spender: spender})
    (print {action: "revoke-allowance", owner: tx-sender, spender: spender})
    (ok true)))
;; Batch transfer function
(define-public (batch-transfer (transfers (list 200 {recipient: principal, amount: uint, memo: (optional (buff 34))})))
  (begin
    (asserts! (> (len transfers) u0) err-invalid-amount)
    (fold check-and-transfer transfers (ok true))))
;; Helper function for batch transfer
(define-private (check-and-transfer (transfer-data {recipient: principal, amount: uint, memo: (optional (buff 34))}) (previous-result (response bool uint)))
  (match previous-result
    success (transfer (get amount transfer-data) tx-sender (get recipient transfer-data) (get memo transfer-data))
    error (err error)))
;; Pause/unpause functionality
(define-data-var contract-paused bool false)

;; Check if contract is paused
(define-read-only (is-paused)
  (var-get contract-paused))

;; Pause contract (owner only)
(define-public (pause-contract)
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (var-set contract-paused true)
    (print {action: "pause-contract"})
    (ok true)))
;; Unpause contract (owner only)
(define-public (unpause-contract)
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (var-set contract-paused false)
    (print {action: "unpause-contract"})
    (ok true)))
;; Blacklist functionality
(define-map blacklisted-addresses principal bool)

;; Check if address is blacklisted
(define-read-only (is-blacklisted (address principal))
  (default-to false (map-get? blacklisted-addresses address)))

;; Add address to blacklist (owner only)
(define-public (blacklist-address (address principal))
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (map-set blacklisted-addresses address true)
    (print {action: "blacklist-address", address: address})
    (ok true)))
;; Remove address from blacklist (owner only)
(define-public (unblacklist-address (address principal))
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (map-delete blacklisted-addresses address)
    (print {action: "unblacklist-address", address: address})
    (ok true)))
;; Fee structure for transfers
(define-data-var transfer-fee-rate uint u100) ;; 1% = 100 basis points
(define-data-var fee-recipient principal contract-owner)

;; Get current transfer fee rate
(define-read-only (get-transfer-fee-rate)
  (var-get transfer-fee-rate))

;; Set transfer fee rate (owner only)
(define-public (set-transfer-fee-rate (new-rate uint))
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (asserts! (<= new-rate u1000) err-invalid-amount) ;; Max 10%
    (var-set transfer-fee-rate new-rate)
    (print {action: "set-transfer-fee-rate", new-rate: new-rate})
    (ok true)))
;; Set fee recipient (owner only)
(define-public (set-fee-recipient (new-recipient principal))
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (var-set fee-recipient new-recipient)
    (print {action: "set-fee-recipient", new-recipient: new-recipient})
    (ok true)))

;; Calculate transfer fee
(define-read-only (calculate-fee (amount uint))
  (/ (* amount (var-get transfer-fee-rate)) u10000))
;; Staking functionality
(define-map staked-balances principal uint)
(define-map staking-rewards principal uint)
(define-data-var total-staked uint u0)
(define-data-var reward-rate uint u500) ;; 5% APY = 500 basis points

;; Stake tokens
(define-public (stake-tokens (amount uint))
  (let ((current-balance (unwrap! (get-balance tx-sender) err-insufficient-balance))
        (current-staked (default-to u0 (map-get? staked-balances tx-sender))))
    (asserts! (> amount u0) err-invalid-amount)
    (asserts! (>= current-balance amount) err-insufficient-balance)
    ;; Note: Simplified staking - tokens remain in user's balance but are tracked as staked
    (map-set staked-balances tx-sender (+ current-staked amount))
    (var-set total-staked (+ (var-get total-staked) amount))
    (print {action: "stake-tokens", staker: tx-sender, amount: amount})
    (ok true)))
;; Unstake tokens
(define-public (unstake-tokens (amount uint))
  (let ((current-staked (default-to u0 (map-get? staked-balances tx-sender))))
    (asserts! (> amount u0) err-invalid-amount)
    (asserts! (>= current-staked amount) err-insufficient-balance)
    ;; Note: Simplified unstaking - just update the staked balance tracking
    (map-set staked-balances tx-sender (- current-staked amount))
    (var-set total-staked (- (var-get total-staked) amount))
    (print {action: "unstake-tokens", staker: tx-sender, amount: amount})
    (ok true)))

;; Get staked balance
(define-read-only (get-staked-balance (staker principal))
  (default-to u0 (map-get? staked-balances staker)))
;; Governance functionality
(define-map proposals uint {title: (string-ascii 100), description: (string-ascii 500), votes-for: uint, votes-against: uint, end-block: uint, executed: bool})
(define-map votes {proposal-id: uint, voter: principal} bool)
(define-data-var proposal-counter uint u0)
(define-data-var min-proposal-threshold uint u1000000) ;; 1M tokens to create proposal

;; Create proposal
(define-public (create-proposal (title (string-ascii 100)) (description (string-ascii 500)) (voting-period uint))
  (let ((proposal-id (+ (var-get proposal-counter) u1))
        (proposer-balance (unwrap! (get-balance tx-sender) err-insufficient-balance)))
    (asserts! (>= proposer-balance (var-get min-proposal-threshold)) err-insufficient-balance)
    (map-set proposals proposal-id {
      title: title,
      description: description,
      votes-for: u0,
      votes-against: u0,
      end-block: (+ burn-block-height voting-period),
      executed: false
    })
    (var-set proposal-counter proposal-id)
    (print {action: "create-proposal", proposal-id: proposal-id, title: title})
    (ok proposal-id)))
;; Vote on proposal
(define-public (vote-on-proposal (proposal-id uint) (vote-for bool))
  (let ((proposal (unwrap! (map-get? proposals proposal-id) (err u404)))
        (voter-balance (unwrap! (get-balance tx-sender) err-insufficient-balance))
        (has-voted (default-to false (map-get? votes {proposal-id: proposal-id, voter: tx-sender}))))
    (asserts! (not has-voted) (err u105)) ;; Already voted
    (asserts! (< burn-block-height (get end-block proposal)) (err u106)) ;; Voting ended
    (asserts! (> voter-balance u0) err-insufficient-balance)
    (map-set votes {proposal-id: proposal-id, voter: tx-sender} true)
    (if vote-for
      (map-set proposals proposal-id (merge proposal {votes-for: (+ (get votes-for proposal) voter-balance)}))
      (map-set proposals proposal-id (merge proposal {votes-against: (+ (get votes-against proposal) voter-balance)})))
    (print {action: "vote", proposal-id: proposal-id, voter: tx-sender, vote-for: vote-for, weight: voter-balance})
    (ok true)))
;; Get proposal details
(define-read-only (get-proposal (proposal-id uint))
  (map-get? proposals proposal-id))

;; Emergency functions
(define-constant err-emergency-only (err u200))
(define-data-var emergency-mode bool false)

;; Enable emergency mode (owner only)
(define-public (enable-emergency-mode)
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (var-set emergency-mode true)
    (print {action: "enable-emergency-mode"})
    (ok true)))
;; Disable emergency mode (owner only)
(define-public (disable-emergency-mode)
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (var-set emergency-mode false)
    (print {action: "disable-emergency-mode"})
    (ok true)))

;; Emergency withdraw (emergency mode only)
(define-public (emergency-withdraw (amount uint) (recipient principal))
  (begin
    (asserts! (var-get emergency-mode) err-emergency-only)
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    ;; Note: Simplified emergency withdraw - direct transfer from owner
    (try! (transfer amount tx-sender recipient none))
    (print {action: "emergency-withdraw", amount: amount, recipient: recipient})
    (ok true)))

Functions (36)

FunctionAccessArgs
get-nameread-only
get-symbolread-only
get-decimalsread-only
get-balanceread-onlywho: principal
get-total-supplyread-only
get-token-uriread-only
transferpublicamount: uint, from: principal, to: principal, memo: (optional (buff 34
mintpublicamount: uint, recipient: principal
burnpublicamount: uint, owner: principal
get-allowanceread-onlyowner: principal, spender: principal
approvepublicspender: principal, amount: uint
transfer-frompublicamount: uint, owner: principal, recipient: principal, memo: (optional (buff 34
increase-allowancepublicspender: principal, amount: uint
decrease-allowancepublicspender: principal, amount: uint
revoke-allowancepublicspender: principal
batch-transferpublictransfers: (list 200 {recipient: principal, amount: uint, memo: (optional (buff 34
check-and-transferprivatetransfer-data: {recipient: principal, amount: uint, memo: (optional (buff 34
is-pausedread-only
pause-contractpublic
unpause-contractpublic
is-blacklistedread-onlyaddress: principal
blacklist-addresspublicaddress: principal
unblacklist-addresspublicaddress: principal
get-transfer-fee-rateread-only
set-transfer-fee-ratepublicnew-rate: uint
set-fee-recipientpublicnew-recipient: principal
calculate-feeread-onlyamount: uint
stake-tokenspublicamount: uint
unstake-tokenspublicamount: uint
get-staked-balanceread-onlystaker: principal
create-proposalpublictitle: (string-ascii 100
vote-on-proposalpublicproposal-id: uint, vote-for: bool
get-proposalread-onlyproposal-id: uint
enable-emergency-modepublic
disable-emergency-modepublic
emergency-withdrawpublicamount: uint, recipient: principal