name pre faktory for audit

This commit is contained in:
Rapha
2025-03-04 23:55:34 -05:00
commit fc75f0104d
25 changed files with 1882 additions and 0 deletions

3
.gitattributes vendored Normal file
View File

@@ -0,0 +1,3 @@
tests/** linguist-vendored
vitest.config.js linguist-vendored
* text=lf

13
.gitignore vendored Normal file
View File

@@ -0,0 +1,13 @@
**/settings/Mainnet.toml
**/settings/Testnet.toml
.cache/**
history.txt
logs
*.log
npm-debug.log*
coverage
*.info
costs-reports.json
node_modules

4
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,4 @@
{
"files.eol": "\n"
}

19
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,19 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "check contracts",
"group": "test",
"type": "shell",
"command": "clarinet check"
},
{
"type": "npm",
"script": "test",
"group": "test",
"problemMatcher": [],
"label": "npm test"
}
]
}

49
Clarinet.toml Normal file
View File

@@ -0,0 +1,49 @@
[project]
name = 'pre-simplified-fees'
description = ''
authors = []
telemetry = false
cache_dir = './.cache'
requirements = []
[contracts.aibtc-dao-traits-v2]
path = 'contracts/aibtc-dao-traits-v2.clar'
clarity_version = 2
epoch = 2.5
[contracts.faktory-dex-trait-v1-1]
path = 'contracts/faktory-dex-trait-v1-1.clar'
clarity_version = 2
epoch = 2.5
[contracts.faktory-trait-v1]
path = 'contracts/faktory-trait-v1.clar'
clarity_version = 2
epoch = 2.5
[contracts.name-faktory]
path = 'contracts/name-faktory.clar'
clarity_version = 2
epoch = 2.5
[contracts.name-faktory-dex]
path = 'contracts/name-faktory-dex.clar'
clarity_version = 2
epoch = 2.5
[contracts.name-pre-faktory]
path = 'contracts/name-pre-faktory.clar'
clarity_version = 2
epoch = 2.5
[contracts.sbtc-token]
path = 'contracts/sbtc-token.clar'
clarity_version = 2
epoch = 2.5
[repl.analysis]
passes = ['check_checker']
[repl.analysis.check_checker]
strict = false
trusted_sender = false
trusted_caller = false
callee_filter = false

74
README.md Normal file
View File

@@ -0,0 +1,74 @@
You're absolutely right with that clarification. Here's the updated summary:
## Transitions and Function Availability
**Period 1:**
- `buy-up-to`: Available until Period 2 starts
- `refund`: Only available if Period 1 expires without reaching criteria and Period 2 hasn't started
**Period 2:**
- Begins automatically when 20 seats are sold to 10+ users
- `buy-single-seat`: Only available during Period 2 (100 blocks)
- `set-contract-addresses`: Only available after Period 2 starts
**Token Distribution:**
- `initialize-token-distribution`: Can only be called by the DAO token after Period 2 starts and set-contract-addresses is set properly
- `claim`: Only available after token distribution is initialized
## Key Variables
- `period-2-height`: Marks successful completion of Period 1
- `token-contract`: Marks DAO token deployment and initialization (not redundant)
## Workflow
1. Users buy seats in Period 1
2. Period 2 starts automatically when requirements met
3. Multi-sig agent sets contract addresses during Period 2
4. DAO token deploys and initializes distribution (which can only happen once)
5. Users can begin claiming tokens according to vesting schedule
Each check serves a distinct purpose in ensuring correct sequencing and authorization throughout the process. The token-contract variable is indeed not redundant - it specifically marks when the DAO token has deployed and initialized distribution, which is separate from the Period 2 transition.
## Multi-sig Security Model
At no point does the multi-sig agent control the flow of money or token distribution. The agent only facilitates the creation of the multi-sig and setting of contracts. If an error occurs in setting addresses, the agent can create a new multi-sig with the correct configuration and update the addresses accordingly without risking funds.
The design ensures transparency as anyone can verify that the multi-sig members match the first buyers from Period 1. This establishes decentralized control from day one while maintaining the technical capability to deploy the necessary infrastructure.
Recovery Path: If the token is deployed with an address that doesn't match what's set in the pre-launch contract, initialize-distribution will fail. The agent can then create a new multi-sig, update the contract addresses, and redeploy the token correctly. This ensures STX funds in the pre-launch contract are never stuck, even if deployment errors occur.
If the multi-sig creator makes a mistake in setting addresses:
1. They can deploy a new multi-sig with the correct configuration
2. Call `set-contract-addresses` again with the correct addresses
3. Deploy the token contract through the new multi-sig
4. The token contract calls `initialize-token-distribution`
Since the condition is `(asserts! (is-eq tx-sender (var-get dao-token)) ERR-NOT-AUTHORIZED)`, as long as the caller matches whatever is currently set as `dao-token`, the initialization will succeed.
This approach gives flexibility to fix mistakes without complex recovery mechanisms, while still maintaining the security of requiring the multi-sig for deployment. The only consequence is that the first incorrect multi-sig would need to be abandoned, but that's a reasonable tradeoff for the simplicity it provides.
;; Pre-launch participants are considered co-deployers of the DAO contract infrastructure through
;; the multi-sig and thus have legitimate claim to protocol fees based on:
;;
;; 1. They provide valuable service in protocol deployment and bootstrapping
;; 2. Fee distribution is transparent, immutable and programmatically defined
;; 3. Multi-sig creation formalizes their relationship with the DAO
;;
;; Fee compensation is separate from token purchase:
;; - Participants buy tokens through seat purchase
;; - Participants receive fees as compensation for their deployer role
;; - These are distinct forms of value with different legal bases
;;
;; Implementation follows standard DeFi practices:
;;
;; This creates proper incentive alignment and encourages long-term protocol engagement
;; beyond the initial vesting period.
## Fee Airdrops
We simplified the fee distribution system by replacing a complex period-based claiming mechanism with a streamlined automatic airdrop approach. Instead of tracking fees per period and requiring each user to manually claim their share, the new system accumulates all fees in a single pool and distributes them proportionally to all seat holders in one transaction. We optimized the code by directly using the existing seat-holders list, implemented a cooldown period between airdrops, and added a special "final airdrop" mode on bonding. The new system is more gas-efficient and provides a much better user experience since users automatically receive their fees without any action required.

7
Testnet.toml Normal file
View File

@@ -0,0 +1,7 @@
[network]
name = "testnet"
stacks_node_rpc_address = "https://api.testnet.hiro.so"
deployment_fee_rate = 10
[accounts.deployer]
mnemonic = "<YOUR PRIVATE TESTNET MNEMONIC HERE>"

View File

@@ -0,0 +1,254 @@
;; ;; title: aibtc-traits
;; ;; version: 2.0.0
;; ;; summary: A collection of traits for all aibtc daos.
;; ;; IMPORTS
(use-trait faktory-token .faktory-trait-v1.sip-010-trait) ;; 'STTWD9SPRQVD3P733V89SV0P8RZRZNQADG034F0A
;; (use-trait ft-trait 'STTWD9SPRQVD3P733V89SV0P8RZRZNQADG034F0A.sip-010-trait-ft-standard.sip-010-trait)
;; (use-trait nft-trait 'STTWD9SPRQVD3P733V89SV0P8RZRZNQADG034F0A.nft-trait.nft-trait)
;; ;; CORE DAO TRAITS
;; ;; a one-time action proposed by token holders
;; (define-trait proposal (
;; (execute (principal) (response bool uint))
;; ))
;; ;; a standing feature of the dao implemented in Clarity
;; (define-trait extension (
;; (callback (principal (buff 34)) (response bool uint))
;; ))
;; ;; TOKEN TRAITS
;; ;; the decentralized Bitflow trading pool following their xyk formula
;; (define-trait bitflow-pool (
;; ;; transfer funds (we're just tagging this contract)
;; ;; all functions are covered between sip-010 and bitflow-xyk
;; (transfer (uint principal principal (optional (buff 34))) (response bool uint))
;; ))
;; the decentralized exchange and initial bonding curve for a token
;; can be used to buy and sell tokens until the target is reached
;; liquidity is provided by the initial minting of tokens (20%)
;; reaching the target will trigger migration to the Bitflow pool
(define-trait faktory-dex (
;; buy tokens from the dex
;; @param ft the token contract
;; @param ustx the amount of microSTX to spend
;; @returns (response bool uint)
(buy (<faktory-token> uint) (response bool uint))
;; sell tokens to the dex
;; @param ft the token contract
;; @param amount the amount of tokens to sell
;; @returns (response bool uint)
(sell (<faktory-token> uint) (response bool uint))
))
;; the token contract for the dao, with no pre-mine or initial allocation
;; tokens are minted 80% to the dao tresaury, 20% to the initial bonding curve
(define-trait token (
;; transfer funds (limited as we're just tagging this)
(transfer (uint principal principal (optional (buff 34))) (response bool uint))
))
;; ;; EXTENSION TRAITS
;; ;; a pre-defined action that token holders can propose
;; (define-trait action (
;; ;; @param parameters serialized hex-encoded Clarity values
;; ;; @returns (response bool uint)
;; (run ((buff 2048)) (response bool uint))
;; ))
;; ;; a voting contract for whitelisted pre-defined actions
;; ;; has lower voting threshold and quorum than core proposals
;; (define-trait action-proposals (
;; ;; propose a new action
;; ;; @param action the action contract
;; ;; @param parameters encoded action parameters
;; ;; @returns (response bool uint)
;; (propose-action (<action> (buff 2048)) (response bool uint))
;; ;; vote on an existing proposal
;; ;; @param proposal the proposal id
;; ;; @param vote true for yes, false for no
;; ;; @returns (response bool uint)
;; (vote-on-proposal (uint bool) (response bool uint))
;; ;; conclude a proposal after voting period
;; ;; @param proposal the proposal id
;; ;; @param action the action contract
;; ;; @returns (response bool uint)
;; (conclude-proposal (uint <action>) (response bool uint))
;; ))
;; ;; a smart contract that can be funded and assigned to a principal
;; ;; withdrawals are based on a set amount and time period in blocks
;; (define-trait bank-account (
;; ;; set account holder
;; ;; @param principal the new account holder
;; ;; @returns (response bool uint)
;; (set-account-holder (principal) (response bool uint))
;; ;; set withdrawal period
;; ;; @param period the new withdrawal period in blocks
;; ;; @returns (response bool uint)
;; (set-withdrawal-period (uint) (response bool uint))
;; ;; set withdrawal amount
;; ;; @param amount the new withdrawal amount in microSTX
;; ;; @returns (response bool uint)
;; (set-withdrawal-amount (uint) (response bool uint))
;; ;; override last withdrawal block
;; ;; @param block the new last withdrawal block
;; ;; @returns (response bool uint)
;; (override-last-withdrawal-block (uint) (response bool uint))
;; ;; deposit STX to the bank account
;; ;; @param amount amount of microSTX to deposit
;; ;; @returns (response bool uint)
;; (deposit-stx (uint) (response bool uint))
;; ;; withdraw STX from the bank account
;; ;; @returns (response bool uint)
;; (withdraw-stx () (response bool uint))
;; ))
;; ;; an extension to manage the dao charter and mission
;; ;; allows the dao to define its mission and values on-chain
;; ;; used to guide decision-making and proposals
;; (define-trait charter (
;; ;; set the dao charter
;; ;; @param charter the new charter text
;; ;; @returns (response bool uint)
;; (set-dao-charter ((string-ascii 4096) (optional (buff 33))) (response bool uint))
;; ))
;; ;; a voting contract for core dao proposals
;; ;; has higher voting threshold and quorum than action proposals
;; ;; can run any Clarity code in the context of the dao
;; (define-trait core-proposals (
;; ;; create a new proposal
;; ;; @param proposal the proposal contract
;; ;; @returns (response bool uint)
;; (create-proposal (<proposal>) (response bool uint))
;; ;; vote on an existing proposal
;; ;; @param proposal the proposal contract
;; ;; @param vote true for yes, false for no
;; ;; @returns (response bool uint)
;; (vote-on-proposal (<proposal> bool) (response bool uint))
;; ;; conclude a proposal after voting period
;; ;; @param proposal the proposal contract
;; ;; @returns (response bool uint)
;; (conclude-proposal (<proposal>) (response bool uint))
;; ))
;; ;; a messaging contract that allows anyone to send public messages on-chain
;; ;; messages can be up to 1MB in size and are printed as events that can be monitored
;; ;; messages can verifiably indicate the sender is the dao by using a proposal
;; (define-trait messaging (
;; ;; send a message on-chain (opt from DAO)
;; ;; @param msg the message to send (up to 1MB)
;; ;; @param isFromDao whether the message is from the DAO
;; ;; @returns (response bool uint)
;; (send ((string-ascii 1048576) bool) (response bool uint))
;; ))
;; ;; an invoicing contract that allows anyone to pay invoices
;; ;; used in conjunction with the 'resources' trait
;; (define-trait invoices (
;; ;; pay an invoice by ID
;; ;; @param invoice the ID of the invoice
;; ;; @returns (response uint uint)
;; (pay-invoice (uint (optional (buff 34))) (response uint uint))
;; ;; pay an invoice by resource name
;; ;; @param name the name of the resource
;; ;; @returns (response uint uint)
;; (pay-invoice-by-resource-name ((string-utf8 50) (optional (buff 34))) (response uint uint))
;; ))
;; ;; a resource contract that allows for management of resources
;; ;; resources can be paid for by anyone, and toggled on/off
;; ;; used in conjunction with the 'invoices' trait for a payment system
;; (define-trait resources (
;; ;; set payment address for resource invoices
;; ;; @param principal the new payment address
;; ;; @returns (response bool uint)
;; (set-payment-address (principal) (response bool uint))
;; ;; adds a new resource that users can pay for
;; ;; @param name the name of the resource (unique!)
;; ;; @param price the price of the resource in microSTX
;; ;; @param description a description of the resource
;; ;; @returns (response uint uint)
;; (add-resource ((string-utf8 50) (string-utf8 255) uint (optional (string-utf8 255))) (response uint uint))
;; ;; toggles a resource on or off for payment
;; ;; @param resource the ID of the resource
;; ;; @returns (response bool uint)
;; (toggle-resource (uint) (response bool uint))
;; ;; toggles a resource on or off for payment by name
;; ;; @param name the name of the resource
;; ;; @returns (response bool uint)
;; (toggle-resource-by-name ((string-utf8 50)) (response bool uint))
;; ))
;; ;; an extension that manages the token on behalf of the dao
;; ;; allows for same functionality normally used by deployer through proposals
;; (define-trait token-owner (
;; ;; set the token URI
;; ;; @param value the new token URI
;; ;; @returns (response bool uint)
;; (set-token-uri ((string-utf8 256)) (response bool uint))
;; ;; transfer ownership of the token
;; ;; @param new-owner the new owner of the token
;; ;; @returns (response bool uint)
;; (transfer-ownership (principal) (response bool uint))
;; ))
;; ;; an extension that manages STX, SIP-009 NFTs, and SIP-010 tokens
;; ;; also supports stacking STX with Stacks Proof of Transfer
;; (define-trait treasury (
;; ;; allow an asset for deposit/withdrawal
;; ;; @param token the asset contract principal
;; ;; @param enabled whether the asset is allowed
;; ;; @returns (response bool uint)
;; (allow-asset (principal bool) (response bool uint))
;; ;; allow multiple assets for deposit/withdrawal
;; ;; @param allowList a list of asset contracts and enabled status
;; ;; @returns (response bool uint)
;; ;; TODO: removed due to conflict with contract definition (both are the same?)
;; ;; (allow-assets ((list 100 (tuple (token principal) (enabled bool)))) (response bool uint))
;; ;; deposit STX to the treasury
;; ;; @param amount amount of microSTX to deposit
;; ;; @returns (response bool uint)
;; (deposit-stx (uint) (response bool uint))
;; ;; deposit FT to the treasury
;; ;; @param ft the fungible token contract principal
;; ;; @param amount amount of tokens to deposit
;; ;; @returns (response bool uint)
;; (deposit-ft (<ft-trait> uint) (response bool uint))
;; ;; deposit NFT to the treasury
;; ;; @param nft the non-fungible token contract principal
;; ;; @param id the ID of the token to deposit
;; ;; @returns (response bool uint)
;; (deposit-nft (<nft-trait> uint) (response bool uint))
;; ;; withdraw STX from the treasury
;; ;; @param amount amount of microSTX to withdraw
;; ;; @param recipient the recipient of the STX
;; ;; @returns (response bool uint)
;; (withdraw-stx (uint principal) (response bool uint))
;; ;; withdraw FT from the treasury
;; ;; @param ft the fungible token contract principal
;; ;; @param amount amount of tokens to withdraw
;; ;; @param recipient the recipient of the tokens
;; ;; @returns (response bool uint)
;; (withdraw-ft (<ft-trait> uint principal) (response bool uint))
;; ;; withdraw NFT from the treasury
;; ;; @param nft the non-fungible token contract principal
;; ;; @param id the ID of the token to withdraw
;; ;; @param recipient the recipient of the token
;; ;; @returns (response bool uint)
;; (withdraw-nft (<nft-trait> uint principal) (response bool uint))
;; ;; delegate STX for stacking in PoX
;; ;; @param amount max amount of microSTX that can be delegated
;; ;; @param to the address to delegate to
;; ;; @returns (response bool uint)
;; (delegate-stx (uint principal) (response bool uint))
;; ;; revoke delegation of STX from stacking in PoX
;; ;; @returns (response bool uint)
;; (revoke-delegate-stx () (response bool uint))
;; ))

View File

@@ -0,0 +1,44 @@
(use-trait faktory-token .faktory-trait-v1.sip-010-trait)
(define-trait dex-trait
(
;; buy from the bonding curve dex
(buy (<faktory-token> uint) (response bool uint))
;; sell from the bonding curve dex
(sell (<faktory-token> uint) (response bool uint))
;; the status of the dex
(get-open () (response bool uint))
;; data to inform a buy
(get-in (uint) (response {
total-stx: uint,
total-stk: uint, ;; new
ft-balance: uint,
k: uint, ;; new
fee: uint,
stx-in: uint,
new-stk: uint, ;; new
new-ft: uint,
tokens-out: uint,
new-stx: uint,
stx-to-grad: uint
} uint))
;; data to inform a sell
(get-out (uint) (response {
total-stx: uint,
total-stk: uint, ;; new
ft-balance: uint,
k: uint, ;; new
new-ft: uint,
new-stk: uint, ;; new
stx-out: uint,
fee: uint,
stx-to-receiver: uint,
amount-in: uint,
new-stx: uint,
} uint))
)
)

View File

@@ -0,0 +1,24 @@
(define-trait sip-010-trait
(
;; Transfer from the caller to a new principal
(transfer (uint principal principal (optional (buff 34))) (response bool uint))
;; the human readable name of the token
(get-name () (response (string-ascii 32) uint))
;; the ticker symbol, or empty if none
(get-symbol () (response (string-ascii 32) uint))
;; the number of decimals used, e.g. 6 would mean 1_000_000 represents 1 token
(get-decimals () (response uint uint))
;; the balance of the passed principal
(get-balance (principal) (response uint uint))
;; the current total supply (which does not need to be a constant)
(get-total-supply () (response uint uint))
;; an optional URI that represents metadata of this token
(get-token-uri () (response (optional (string-utf8 256)) uint))
)
)

View File

@@ -0,0 +1,208 @@
;; 594f5fc947937dc93684965a4f7d7cd057d87c94fa9423bd256a9fc35907159f
;; aibtc.dev DAO faktory.fun DEX @version 1.0
(impl-trait .aibtc-dao-traits-v2.faktory-dex) ;; 'ST3YT0XW92E6T2FE59B2G5N2WNNFSBZ6MZKQS5D18
(impl-trait .faktory-dex-trait-v1-1.dex-trait) ;; 'STTWD9SPRQVD3P733V89SV0P8RZRZNQADG034F0A
(use-trait faktory-token .faktory-trait-v1.sip-010-trait) ;; 'STTWD9SPRQVD3P733V89SV0P8RZRZNQADG034F0A
(define-constant ERR-MARKET-CLOSED (err u1001))
(define-constant ERR-STX-NON-POSITIVE (err u1002))
(define-constant ERR-STX-BALANCE-TOO-LOW (err u1003))
(define-constant ERR-FT-NON-POSITIVE (err u1004))
(define-constant ERR-FETCHING-BUY-INFO (err u1005))
(define-constant ERR-FETCHING-SELL-INFO (err u1006))
(define-constant ERR-TOKEN-NOT-AUTH (err u401))
(define-constant ERR-UNAUTHORIZED-CALLER (err u402))
(define-constant FEE-RECEIVER 'ST1Y9QV2CY6R0NQNS8CPA5C2835QNGHMTFE94FV5R)
(define-constant G-RECEIVER 'ST3BA7GVAKQTCTX68VPAD9W8CBYG71JNMGBCAD48N)
(define-constant FAKTORY 'STTWD9SPRQVD3P733V89SV0P8RZRZNQADG034F0A)
(define-constant ORIGINATOR 'STTWD9SPRQVD3P733V89SV0P8RZRZNQADG034F0A) ;; this is creator address
(define-constant DEX-TOKEN .name-faktory)
;; token constants
(define-constant TARGET_STX u5000000)
(define-constant FAK_STX u1000000) ;; this will be 1M satoshis
(define-constant GRAD-FEE u100000)
;; data vars
(define-data-var open bool false)
(define-data-var fak-ustx uint u0)
(define-data-var ft-balance uint u0)
(define-data-var stx-balance uint u0)
(define-data-var premium uint u25)
(define-public (buy (ft <faktory-token>) (ubtc uint))
(begin
(asserts! (is-eq DEX-TOKEN (contract-of ft)) ERR-TOKEN-NOT-AUTH)
(asserts! (var-get open) ERR-MARKET-CLOSED)
(asserts! (> ubtc u0) ERR-STX-NON-POSITIVE)
(let (
(in-info (unwrap! (get-in ubtc) ERR-FETCHING-BUY-INFO))
(total-stx (get total-stx in-info))
(total-stk (get total-stk in-info))
(total-ft (get ft-balance in-info))
(k (get k in-info))
(fee (get fee in-info))
(pre-fee (/ (* fee u40) u100))
(stx-in (get stx-in in-info))
(new-stk (get new-stk in-info))
(new-ft (get new-ft in-info))
(tokens-out (get tokens-out in-info))
(new-stx (get new-stx in-info))
(ft-receiver tx-sender)
)
(try! (contract-call? .sbtc-token
transfer (- fee pre-fee) tx-sender FEE-RECEIVER none))
(try! (contract-call? .sbtc-token
transfer pre-fee tx-sender .name-pre-faktory none))
(try! (as-contract (contract-call? .name-pre-faktory create-fees-receipt pre-fee))) ;; this address could be a multi-sig
(try! (contract-call? .sbtc-token
transfer stx-in tx-sender (as-contract tx-sender) none))
(try! (as-contract (contract-call? ft transfer tokens-out tx-sender ft-receiver none)))
(if (>= new-stx TARGET_STX)
(let ((premium-amount (/ (* new-ft (var-get premium)) u100))
(amm-amount (- new-ft premium-amount))
(agent-amount (/ (* premium-amount u60) u100))
(originator-amount (- premium-amount agent-amount))
(amm-ustx (- new-stx GRAD-FEE))
(xyk-pool-uri (default-to u"https://bitflow.finance" (try! (contract-call? ft get-token-uri))))
(xyk-burn-amount (- (sqrti (* amm-ustx amm-amount)) u1)))
(try! (as-contract (contract-call? ft transfer agent-amount tx-sender FAKTORY none)))
(try! (as-contract (contract-call? ft transfer originator-amount tx-sender ORIGINATOR none)))
;; (try! (as-contract
;; (contract-call?
;; 'ST295MNE41DC74QYCPRS8N37YYMC06N6Q3VQDZ6G1.xyk-core-v-1-2
;; create-pool
;; 'STV9K21TBFAK4KNRJXF5DFP8N7W46G4V9RJ5XDY2.xyk-pool-stx-name-v-1-1
;; 'ST295MNE41DC74QYCPRS8N37YYMC06N6Q3VQDZ6G1.token-stx-v-1-2
;; ft
;; amm-ustx
;; amm-amount
;; xyk-burn-amount
;; u10 u40 u10 u40
;; 'ST27Q7Z7P5MTJN2B3M9Q406XPCDB1VFZJ3KWX3CES xyk-pool-uri true)))
(try! (as-contract (contract-call? .sbtc-token
transfer GRAD-FEE tx-sender G-RECEIVER none)))
(var-set open false)
(var-set stx-balance u0)
(var-set ft-balance u0)
(try! (as-contract (contract-call? .name-pre-faktory toggle-bonded))) ;; this address could be a multi-sig
(print {type: "buy", ft: (contract-of ft), tokens-out: tokens-out, ustx: ubtc, premium-amount: premium-amount, amm-amount: amm-amount,
amm-ustx: amm-ustx,
stx-balance: u0, ft-balance: u0,
fee: fee, grad-fee: GRAD-FEE, maker: tx-sender,
open: false})
(ok true))
(begin
(var-set stx-balance new-stx)
(var-set ft-balance new-ft)
(print {type: "buy", ft: (contract-of ft), tokens-out: tokens-out, ustx: ubtc, maker: tx-sender,
stx-balance: new-stx, ft-balance: new-ft,
fee: fee,
open: true})
(ok true))))))
(define-read-only (get-in (ubtc uint))
(let ((total-stx (var-get stx-balance))
(total-stk (+ total-stx (var-get fak-ustx)))
(total-ft (var-get ft-balance))
(k (* total-ft total-stk))
(fee (/ (* ubtc u2) u100))
(stx-in (- ubtc fee))
(new-stk (+ total-stk stx-in))
(new-ft (/ k new-stk))
(tokens-out (- total-ft new-ft))
(raw-to-grad (- TARGET_STX total-stx))
(stx-to-grad (/ (* raw-to-grad u103) u100)))
(ok {total-stx: total-stx,
total-stk: total-stk,
ft-balance: total-ft,
k: k,
fee: fee,
stx-in: stx-in,
new-stk: new-stk,
new-ft: new-ft,
tokens-out: tokens-out,
new-stx: (+ total-stx stx-in),
stx-to-grad: stx-to-grad
})))
(define-public (sell (ft <faktory-token>) (amount uint))
(begin
(asserts! (is-eq DEX-TOKEN (contract-of ft)) ERR-TOKEN-NOT-AUTH)
(asserts! (var-get open) ERR-MARKET-CLOSED)
(asserts! (> amount u0) ERR-FT-NON-POSITIVE)
(let (
(out-info (unwrap! (get-out amount) ERR-FETCHING-SELL-INFO))
(total-stx (get total-stx out-info))
(total-stk (get total-stk out-info))
(total-ft (get ft-balance out-info))
(k (get k out-info))
(new-ft (get new-ft out-info))
(new-stk (get new-stk out-info))
(stx-out (get stx-out out-info))
(fee (get fee out-info))
(pre-fee (/ (* fee u40) u100))
(stx-to-receiver (get stx-to-receiver out-info))
(new-stx (get new-stx out-info))
(stx-receiver tx-sender)
)
(asserts! (>= total-stx stx-out) ERR-STX-BALANCE-TOO-LOW)
(try! (contract-call? ft transfer amount tx-sender (as-contract tx-sender) none))
(try! (as-contract (contract-call? .sbtc-token
transfer stx-to-receiver tx-sender stx-receiver none)))
(try! (as-contract (contract-call? .sbtc-token
transfer (- fee pre-fee) tx-sender FEE-RECEIVER none)))
(try! (contract-call? .sbtc-token
transfer pre-fee tx-sender .name-pre-faktory none))
(try! (as-contract (contract-call? .name-pre-faktory create-fees-receipt pre-fee))) ;; this address could be a multi-sig
(var-set stx-balance new-stx)
(var-set ft-balance new-ft)
(print {type: "sell", ft: (contract-of ft), amount: amount, stx-to-receiver: stx-to-receiver, maker: tx-sender,
stx-balance: new-stx, ft-balance: new-ft,
fee: fee,
open: true})
(ok true))))
(define-read-only (get-out (amount uint))
(let ((total-stx (var-get stx-balance))
(total-stk (+ total-stx (var-get fak-ustx)))
(total-ft (var-get ft-balance))
(k (* total-ft total-stk))
(new-ft (+ total-ft amount))
(new-stk (/ k new-ft))
(stx-out (- (- total-stk new-stk) u1))
(fee (/ (* stx-out u2) u100))
(stx-to-receiver (- stx-out fee)))
(ok {
total-stx: total-stx,
total-stk: total-stk,
ft-balance: total-ft,
k: k,
new-ft: new-ft,
new-stk: new-stk,
stx-out: stx-out,
fee: fee,
stx-to-receiver: stx-to-receiver,
amount-in: amount,
new-stx: (- total-stx stx-out)
})))
(define-read-only (get-open)
(ok (var-get open)))
;; boot dex
(begin
(var-set fak-ustx FAK_STX)
(var-set ft-balance u20000000000000000)
(var-set stx-balance u0)
(var-set open true)
(print {
type: "faktory-dex-trait-v1-1",
dexContract: (as-contract tx-sender),
;; ammReceiver: 'ST295MNE41DC74QYCPRS8N37YYMC06N6Q3VQDZ6G1.xyk-core-v-1-2,
;; poolName: 'STV9K21TBFAK4KNRJXF5DFP8N7W46G4V9RJ5XDY2.xyk-pool-stx-name-v-1-1
})
(ok true))

120
contracts/name-faktory.clar Normal file
View File

@@ -0,0 +1,120 @@
;; 3ffc48440d8dc2bd86b328355351467275607b40f6b74499d90cbbb13a4618f7
;; NAME Powered By Faktory.fun v1.0
(impl-trait .faktory-trait-v1.sip-010-trait) ;; 'STTWD9SPRQVD3P733V89SV0P8RZRZNQADG034F0A
(impl-trait .aibtc-dao-traits-v2.token) ;; 'ST3YT0XW92E6T2FE59B2G5N2WNNFSBZ6MZKQS5D18
(define-constant ERR-NOT-AUTHORIZED u401)
(define-constant ERR-NOT-OWNER u402)
(define-fungible-token NAME MAX)
(define-constant MAX u100000000000000000)
(define-data-var contract-owner principal tx-sender) ;; 'STV9K21TBFAK4KNRJXF5DFP8N7W46G4V9RJ5XDY2.name-token-owner)
(define-data-var token-uri (optional (string-utf8 256)) (some u"https://bncytzyfafclmdxrwpgq.supabase.co/storage/v1/object/public/tokens/60360b67-5f2e-4dfb-adc4-f8bf7c9aab85.json"))
;; SIP-10 Functions
(define-public (transfer (amount uint) (sender principal) (recipient principal) (memo (optional (buff 34))))
(begin
(asserts! (is-eq tx-sender sender) (err ERR-NOT-AUTHORIZED))
(match (ft-transfer? NAME amount sender recipient)
response (begin
(print memo)
(ok response))
error (err error)
)
)
)
(define-public (set-token-uri (value (string-utf8 256)))
(begin
(asserts! (is-eq tx-sender (var-get contract-owner)) (err ERR-NOT-AUTHORIZED))
(var-set token-uri (some value))
(ok (print {
notification: "token-metadata-update",
payload: {
contract-id: (as-contract tx-sender),
token-class: "ft"
}
})
)
)
)
(define-read-only (get-balance (account principal))
(ok (ft-get-balance NAME account))
)
(define-read-only (get-name)
(ok "ai sbtc")
)
(define-read-only (get-symbol)
(ok "NAME")
)
(define-read-only (get-decimals)
(ok u8)
)
(define-read-only (get-total-supply)
(ok (ft-get-supply NAME))
)
(define-read-only (get-token-uri)
(ok (var-get token-uri))
)
(define-public (set-contract-owner (new-owner principal))
(begin
(asserts! (is-eq tx-sender (var-get contract-owner)) (err ERR-NOT-AUTHORIZED))
(print {new-owner: new-owner})
(ok (var-set contract-owner new-owner))
)
)
;; ---------------------------------------------------------
(define-public (send-many (recipients (list 200 { to: principal, amount: uint, memo: (optional (buff 34)) })))
(fold check-err (map send-token recipients) (ok true))
)
(define-private (check-err (result (response bool uint)) (prior (response bool uint)))
(match prior ok-value result err-value (err err-value))
)
(define-private (send-token (recipient { to: principal, amount: uint, memo: (optional (buff 34)) }))
(send-token-with-memo (get amount recipient) (get to recipient) (get memo recipient))
)
(define-private (send-token-with-memo (amount uint) (to principal) (memo (optional (buff 34))))
(let ((transferOk (try! (transfer amount tx-sender to memo))))
(ok transferOk)
)
)
;; ---------------------------------------------------------
(begin
;; ft distribution
;; (try! (ft-mint? NAME (/ (* MAX u80) u100) .name-treasury)) ;; 80% treasury
(try! (ft-mint? NAME (/ (* MAX u16) u100) .name-faktory-dex)) ;; 16% dex ;; Rafa put back on !not 20%
(try! (ft-mint? NAME (/ (* MAX u4) u100) .name-pre-faktory)) ;; 4% faktory ;; Rafa put back on
;; (try! (as-contract (contract-call? .name-pre-faktory initialize-token-distribution-demo))) ;; this address could be a multi-sig
(unwrap! (as-contract (contract-call? .name-pre-faktory initialize-token-distribution-demo)) (err "failed to initialize-token-distribution-demo"))
(print {
type: "faktory-trait-v1",
name: "ai sbtc",
symbol: "NAME",
token-uri: u"https://bncytzyfafclmdxrwpgq.supabase.co/storage/v1/object/public/tokens/60360b67-5f2e-4dfb-adc4-f8bf7c9aab85.json",
tokenContract: (as-contract tx-sender),
supply: MAX,
decimals: u8,
targetStx: u5000000,
tokenToDex: (/ (* MAX u16) u100),
tokenToDeployer: (/ (* MAX u4) u100),
stxToDex: u0,
stxBuyFirstFee: u0,
})
)

View File

@@ -0,0 +1,590 @@
;; 31e7dcf1490cdc5660986afa318b463f15983585fcbd009cae3f0ebad58c349b
;; aibtc.com DAO faktory.fun PRE @version 1.0
;; Pre-launch contract for token distribution
;; Dynamic allocation: 1-7 seats per user in Period 1
;; Each seat = 0.00020000 BTC, targeting 20 seats total with minimum 10 users
;; Pre-launch participants are co-deployers of the DAO contract infrastructure through
;; a multi-sig and thus have legitimate claim to protocol fees generated by the DEX contract.
;; Fee airdrops are separate from token purchase and vesting.
(use-trait faktory-token .faktory-trait-v1.sip-010-trait) ;; STTWD9SPRQVD3P733V89SV0P8RZRZNQADG034F0A
(define-constant SEATS u20)
(define-constant MIN-USERS u10)
(define-constant MAX-SEATS-PER-USER u7)
(define-constant PRICE-PER-SEAT u20000) ;; 20K sats per seat
(define-constant TOKENS-PER-SEAT u200000000000000) ;; 2M tokens per seat if supply 1B with 8 decimals
(define-constant EXPIRATION-PERIOD u2100) ;; 1 Stacks reward cycle in PoX-4
(define-constant PERIOD-2-LENGTH u100) ;; blocks for redistribution period
(define-constant FT-INITIALIZED-BALANCE u4000000000000000) ;; 40M tokens for pre-launch if supply 1B
(define-constant ACCELERATED-PERCENT u60)
;; Vesting schedule (percentages add up to 100)
(define-constant VESTING-SCHEDULE
(list
{height: u100, percent: u10, id: u0} ;; 10% at start
{height: u1000, percent: u20, id: u1} ;; 20% more
{height: u2100, percent: u30, id: u2} ;; 30% more
{height: u4200, percent: u40, id: u3})) ;; Final 40%
(define-constant MULTI-SIG-CREATOR tx-sender) ;; if a multi-sig can create a multi-sig then this is a multi-sig 2 of 5
;; Data vars
(define-data-var ft-balance uint u0)
(define-data-var stx-balance uint u0)
(define-data-var total-seats-taken uint u0)
(define-data-var total-users uint u0)
(define-data-var token-contract (optional principal) none)
(define-data-var distribution-height (optional uint) none)
(define-data-var deployment-height (optional uint) none)
(define-data-var period-2-height (optional uint) none)
(define-data-var accelerated-vesting bool false)
;; Determined after multi-sig creation
(define-data-var dao-token (optional principal) none) ;; 'STRZ4P1ABSVSZPC4HZ4GDAW834HHEHJMF65X5S6D.txt6-faktory)
(define-data-var dex-contract (optional principal) none) ;; 'STRZ4P1ABSVSZPC4HZ4GDAW834HHEHJMF65X5S6D.txt6-faktory-dex)
(define-data-var dao-multi-sig (optional principal) none) ;; 'ST3SPSJDYGHF0ARGV1TNS0HX6JEP7T1J6849A7BB4)
;; Helper vars
(define-data-var target-owner principal 'STTWD9SPRQVD3P733V89SV0P8RZRZNQADG034F0A) ;; cant-be-evil.stx in testnet? random 'SP000000000000000000002Q6VF78
;; Define a data variable to track seat holders
(define-data-var seat-holders (list 20 {owner: principal, seats: uint}) (list))
;; Track seat ownership and claims
(define-map seats-owned principal uint)
(define-map claimed-amounts principal uint)
;; Error constants
(define-constant ERR-TOO-MANY-SEATS (err u300))
(define-constant ERR-NO-SEATS-LEFT (err u301))
(define-constant ERR-NOT-SEAT-OWNER (err u302))
(define-constant ERR-NOT-SET (err u303))
(define-constant ERR-NOTHING-TO-CLAIM (err u304))
(define-constant ERR-NOT-AUTHORIZED (err u305))
(define-constant ERR-ALREADY-INITIALIZED (err u306))
(define-constant ERR-WRONG-TOKEN (err u307))
(define-constant ERR-ALREADY-EXPIRED (err u308))
(define-constant ERR-NOT-EXPIRED (err u309))
(define-constant ERR-NO-REFUND (err u310))
(define-constant ERR-CONTRACT-INSUFFICIENT-FUNDS (err u311))
(define-constant ERR-PERIOD-2-MULTIPLE-SEATS (err u312))
(define-constant ERR-INVALID-SEAT-COUNT (err u313))
(define-constant ERR-SLICE-FAILED (err u314))
(define-constant ERR-TOO-LONG (err u315))
(define-constant ERR-REMOVING-HOLDER (err u316))
(define-constant ERR-HIGHEST-ONE-SEAT (err u317))
(define-constant ERR-NOT-BONDED (err u318))
(define-constant ERR-PERIOD-2-NOT-INITIALIZED (err u319))
(define-constant ERR-PERIOD-2-ALREADY-STARTED (err u320))
(define-constant ERR-DISTRIBUTION-NOT-INITIALIZED (err u321))
(define-constant ERR-HIGHEST-HOLDER (err u322))
;; Helper functions for period management
(define-private (is-period-1-expired)
(match (var-get deployment-height)
start-height (>= burn-block-height (+ start-height EXPIRATION-PERIOD))
false))
(define-private (is-in-period-2)
(match (var-get period-2-height)
start-height (< burn-block-height (+ start-height PERIOD-2-LENGTH))
false))
;; Helper function to update seat holders list
(define-private (update-seat-holder (owner principal) (seat-count uint))
(let ((current-holders (var-get seat-holders))
(updated-list (update-or-add-holder current-holders owner seat-count)))
(var-set seat-holders updated-list)))
;; Helper to update or add a holder to the list
(define-private (update-or-add-holder
(holders (list 20 {owner: principal, seats: uint}))
(owner principal)
(seat-count uint))
(let ((position (find-holder-position holders owner)))
(if (is-some position)
;; Update existing holder - unwrap the optional result
(unwrap-panic (replace-at? holders (unwrap-panic position) {owner: owner, seats: seat-count}))
;; Add new holder
(unwrap-panic (as-max-len? (append holders {owner: owner, seats: seat-count}) u20)))))
;; Helper to find a holder's position in the list
(define-private (find-holder-position
(holders (list 20 {owner: principal, seats: uint}))
(owner principal))
(let ((result (fold check-if-owner
holders
{found: false, index: u0, current: u0})))
(var-set target-owner owner)
(if (get found result)
(some (get index result))
none)))
(define-private (check-if-owner
(entry {owner: principal, seats: uint})
(state {found: bool, index: uint, current: uint}))
(if (get found state)
;; Already found, just pass through
state
;; Check if this is the owner we're looking for
(if (is-eq (get owner entry) (var-get target-owner))
;; Found it, update state
{found: true, index: (get current state), current: (+ (get current state) u1)}
;; Not found, increment counter
{found: false, index: (get index state), current: (+ (get current state) u1)})))
(define-private (remove-seat-holder (holder principal))
(let ((position (find-holder-position (var-get seat-holders) holder))
(current-list (var-get seat-holders)))
(match position
pos (let ((before-slice (unwrap! (slice? current-list u0 pos) ERR-SLICE-FAILED))
(after-slice (unwrap! (slice? current-list (+ pos u1) (len current-list)) ERR-SLICE-FAILED))
(updated-list (unwrap! (as-max-len? (concat before-slice after-slice) u20) ERR-TOO-LONG)))
(var-set seat-holders updated-list)
(ok true))
(ok false)))) ;; If position not found, do nothing
;; Main functions
;; Buy seats in Period 1
(define-public (buy-up-to (seat-count uint))
(let (
(current-seats (var-get total-seats-taken))
(user-seats (default-to u0 (map-get? seats-owned tx-sender)))
(max-total-allowed (get-max-seats-allowed))
(max-additional-allowed (if (>= user-seats max-total-allowed)
u0
(- max-total-allowed user-seats)))
(actual-seats (if (> seat-count max-additional-allowed)
max-additional-allowed
seat-count)))
(asserts! (> actual-seats u0) ERR-INVALID-SEAT-COUNT)
(asserts! (< current-seats SEATS) ERR-NO-SEATS-LEFT)
(asserts! (is-none (var-get period-2-height)) ERR-PERIOD-2-ALREADY-STARTED)
;; Process payment
;; 'STV9K21TBFAK4KNRJXF5DFP8N7W46G4V9RJ5XDY2.sbtc-token
(match (contract-call? .sbtc-token
transfer (* PRICE-PER-SEAT actual-seats) tx-sender (as-contract tx-sender) none)
success
(begin
(if (is-eq user-seats u0)
(var-set total-users (+ (var-get total-users) u1))
true)
(map-set seats-owned tx-sender (+ user-seats actual-seats))
(var-set total-seats-taken (+ current-seats actual-seats))
(var-set stx-balance (+ (var-get stx-balance) (* PRICE-PER-SEAT actual-seats)))
(update-seat-holder tx-sender (+ user-seats actual-seats))
(if (and (>= (var-get total-users) MIN-USERS) ;; Check if we should start Period 2
(>= (var-get total-seats-taken) SEATS))
(var-set period-2-height (some burn-block-height))
true)
(print {
type: "buy-seats",
buyer: tx-sender,
seats-owned: (+ user-seats actual-seats),
total-users: (var-get total-users),
total-seats-taken: (+ current-seats actual-seats),
stx-balance: (var-get stx-balance),
seat-holders: (var-get seat-holders),
period-2-height: (var-get period-2-height) ;; perhaps these var-get can be optimized?
})
(ok true))
error (err error))))
;; Get highest seat holder for Period 2 reductions
(define-private (get-highest-seat-holder)
(let ((holders (var-get seat-holders)))
(if (> (len holders) u0)
(let ((first-holder (unwrap-panic (element-at holders u0))))
(some (get owner (fold check-highest holders first-holder))))
none)))
(define-private (check-highest
(entry {owner: principal, seats: uint})
(current-max {owner: principal, seats: uint}))
(if (>= (get seats entry) (get seats current-max))
entry
current-max))
;; Buy exactly one seat in Period 2
(define-public (buy-single-seat)
(let (
(current-seats (var-get total-seats-taken))
(highest-holder (get-highest-seat-holder))
(holder (unwrap! highest-holder ERR-HIGHEST-HOLDER))
(old-seats (default-to u0 (map-get? seats-owned holder))))
(asserts! (is-some (var-get period-2-height)) ERR-PERIOD-2-NOT-INITIALIZED)
(asserts! (is-in-period-2) ERR-ALREADY-EXPIRED)
(asserts! (< (var-get total-users) SEATS) ERR-NO-SEATS-LEFT)
(asserts! (> old-seats u1) ERR-HIGHEST-ONE-SEAT)
;; Process payment and refund highest holder
;; 'STV9K21TBFAK4KNRJXF5DFP8N7W46G4V9RJ5XDY2
(match (contract-call? .sbtc-token
transfer PRICE-PER-SEAT tx-sender holder none)
success
(begin
;; Update new buyer
(var-set total-users (+ (var-get total-users) u1))
(map-set seats-owned holder (- old-seats u1))
(map-set seats-owned tx-sender u1)
(update-seat-holder holder (- old-seats u1)) ;; Update list for holder
(update-seat-holder tx-sender u1) ;; Update list for buyer
(print {
type: "buy-single-seat",
total-users: (var-get total-users),
holder: holder,
holder-seats: (- old-seats u1),
buyer: tx-sender,
buyer-seats: u1,
seat-holders: (var-get seat-holders),
})
(ok true))
error (err error))))
;; Refund logic only for Period 1 expired and Period 2 not started
(define-public (refund)
(let (
(user-seats (default-to u0 (map-get? seats-owned tx-sender)))
(seat-owner tx-sender))
(asserts! (is-period-1-expired) ERR-NOT-EXPIRED) ;; period 1 is expired
(asserts! (is-none (var-get period-2-height)) ERR-PERIOD-2-ALREADY-STARTED)
(asserts! (> user-seats u0) ERR-NOT-SEAT-OWNER)
;; Process refund
;; 'STV9K21TBFAK4KNRJXF5DFP8N7W46G4V9RJ5XDY2
(match (as-contract (contract-call? .sbtc-token
transfer (* PRICE-PER-SEAT user-seats) tx-sender seat-owner none))
success
(let ((is-removed (unwrap! (remove-seat-holder tx-sender) ERR-REMOVING-HOLDER)))
(map-delete seats-owned tx-sender)
(var-set total-seats-taken (- (var-get total-seats-taken) user-seats))
(var-set total-users (- (var-get total-users) u1))
(var-set stx-balance (- (var-get stx-balance) (* PRICE-PER-SEAT user-seats)))
(print {
type: "refund",
user: tx-sender,
seat-holders: (var-get seat-holders),
total-seats-taken: (var-get total-seats-taken),
total-users: (var-get total-users),
stx-balance: (var-get stx-balance)
})
(ok true))
error (err error))))
;; Calculate claimable amount based on vesting schedule
(define-private (get-claimable-amount (owner principal))
(match (var-get distribution-height)
start-height
(let ((claimed (default-to u0 (map-get? claimed-amounts owner)))
(seats-owner (default-to u0 (map-get? seats-owned owner)))
(vested (fold check-claimable VESTING-SCHEDULE u0)))
(- (* vested seats-owner) claimed)) ;; double claiming is impossible
u0)) ;; If distribution not initialized, nothing is claimable
(define-private (check-claimable (entry {height: uint, percent: uint, id: uint}) (current-total uint))
(if (<= (+ (unwrap-panic (var-get distribution-height)) (get height entry)) burn-block-height)
(+ current-total (/ (* TOKENS-PER-SEAT (get percent entry)) u100))
(if (and
(var-get accelerated-vesting) ;; token graduated, accelerated vesting
(<= (get id entry) u2)) ;; we're in first 3 entries (0,1,2)
(+ current-total (/ (* TOKENS-PER-SEAT (get percent entry)) u100))
current-total)))
;; Claim vested tokens
(define-public (claim (ft <faktory-token>))
(let ((claimable (get-claimable-amount tx-sender))
(seat-owner tx-sender))
(asserts! (is-eq (var-get token-contract) (var-get dao-token)) ERR-DISTRIBUTION-NOT-INITIALIZED)
(asserts! (is-eq (contract-of ft) (unwrap-panic (var-get dao-token))) ERR-WRONG-TOKEN)
(asserts! (> (default-to u0 (map-get? seats-owned tx-sender)) u0) ERR-NOT-SEAT-OWNER)
(asserts! (> claimable u0) ERR-NOTHING-TO-CLAIM)
(asserts! (>= (var-get ft-balance) claimable) ERR-CONTRACT-INSUFFICIENT-FUNDS)
(match (as-contract (contract-call? ft transfer claimable tx-sender seat-owner none))
success
(begin
(map-set claimed-amounts tx-sender
(+ (default-to u0 (map-get? claimed-amounts tx-sender)) claimable))
(var-set ft-balance (- (var-get ft-balance) claimable)) ;; reduce ft-balance by claimable
(print {
type: "claim",
user: tx-sender,
amount-claimed: claimable,
total-claimed: (map-get? claimed-amounts tx-sender),
ft-balance: (var-get ft-balance)
})
(ok claimable))
error (err error))))
;; Read only functions
(define-read-only (get-max-seats-allowed)
(let (
(seats-remaining (- SEATS (var-get total-seats-taken))) ;; 13 seats left
(users-remaining (- MIN-USERS (var-get total-users))) ;; 9 users needed
(max-possible (+ (- seats-remaining users-remaining) u1))) ;; (13 - 9) + 1 = 5 seats possible
(if (>= max-possible MAX-SEATS-PER-USER)
MAX-SEATS-PER-USER
max-possible)))
(define-read-only (get-contract-status)
(ok
{
is-period-1-expired: (is-period-1-expired),
period-2-started: (is-some (var-get period-2-height)),
is-in-period-2: (is-in-period-2),
total-users: (var-get total-users),
total-seats-taken: (var-get total-seats-taken),
distribution-initialized: (is-some (var-get token-contract))
}))
(define-read-only (get-user-info (user principal))
(ok
{
seats-owned: (default-to u0 (map-get? seats-owned user)),
amount-claimed: (default-to u0 (map-get? claimed-amounts user)),
claimable-amount: (get-claimable-amount user)
}))
(define-read-only (get-period-2-info)
(ok
{
highest-holder: (get-highest-seat-holder),
period-2-blocks-remaining: (match (var-get period-2-height)
start (- (+ start PERIOD-2-LENGTH) burn-block-height)
u0)
}))
(define-read-only (get-remaining-seats)
(ok {remainin-seats: (- SEATS (var-get total-seats-taken))}))
(define-read-only (get-seats-owned (address principal))
(ok {seats-owned:
(> (default-to u0 (map-get? seats-owned address)) u0)}))
(define-read-only (get-claimed-amount (address principal))
(ok {claimed-amount:
(default-to u0 (map-get? claimed-amounts address))}))
(define-read-only (get-vesting-schedule)
(ok {vesting-schedule: VESTING-SCHEDULE}))
(define-read-only (get-seat-holders)
(ok {seat-holders: (var-get seat-holders)}))
;; A multi-sig creator contract addresses after creating a multi-sig whose owners are 10 buyers resulting from period 1
(define-public (set-contract-addresses (new-multi-sig principal) (new-dao-token principal) (new-dex-contract principal))
(begin
(asserts! (is-some (var-get period-2-height)) ERR-PERIOD-2-NOT-INITIALIZED)
(asserts! (is-eq tx-sender MULTI-SIG-CREATOR) ERR-NOT-AUTHORIZED)
(var-set dao-multi-sig (some new-multi-sig))
(var-set dao-token (some new-dao-token))
(var-set dex-contract (some new-dex-contract))
(print {
type: "contract-addresses-updated",
dao-multi-sig: new-multi-sig,
dao-token: new-dao-token,
dex-contract: new-dex-contract,
multi-sig-creator: MULTI-SIG-CREATOR
})
(ok true)))
;; on DAO token deployment
(define-public (initialize-token-distribution)
(begin
(asserts! (is-some (var-get period-2-height)) ERR-PERIOD-2-NOT-INITIALIZED)
(asserts! (is-eq (some tx-sender) (var-get dao-token)) ERR-NOT-AUTHORIZED)
(asserts! (is-none (var-get token-contract)) ERR-ALREADY-INITIALIZED)
(asserts! (is-some (var-get dao-multi-sig)) ERR-NOT-SET)
(asserts! (is-some (var-get dao-token)) ERR-NOT-SET)
(asserts! (is-some (var-get dex-contract)) ERR-NOT-SET)
(try! (as-contract (contract-call? .sbtc-token
transfer u250000 tx-sender (unwrap-panic (var-get dex-contract)) none))) ;; 0.00250000 BTC to DEX
(try! (as-contract (contract-call? .sbtc-token
transfer u10000 tx-sender (unwrap-panic (var-get dao-multi-sig)) none))) ;; 0.00010000 BTC to multi-sig/admin -> covers contract deployment fees
(try! (as-contract (contract-call? .sbtc-token
transfer u140000 tx-sender MULTI-SIG-CREATOR none))) ;; 0.00140000 BTC fees -> covers ordinals bot, pontis and faktory
(var-set token-contract (some tx-sender))
(var-set distribution-height (some burn-block-height))
(var-set last-airdrop-height (some burn-block-height))
(var-set ft-balance FT-INITIALIZED-BALANCE) ;; 20M tokens
(print {
type: "distribution-initialized",
token-contract: (var-get dao-token),
distribution-height: burn-block-height,
ft-balance: FT-INITIALIZED-BALANCE
})
(ok true)))
(define-public (initialize-token-distribution-demo)
(begin
;; (asserts! (is-some (var-get period-2-height)) ERR-PERIOD-2-NOT-INITIALIZED)
;; (asserts! (is-eq (some tx-sender) (var-get dao-token)) ERR-NOT-AUTHORIZED)
;; (asserts! (is-none (var-get token-contract)) ERR-ALREADY-INITIALIZED)
;; (asserts! (is-some (var-get dao-multi-sig)) ERR-NOT-SET)
;; (asserts! (is-some (var-get dao-token)) ERR-NOT-SET)
;; (asserts! (is-some (var-get dex-contract)) ERR-NOT-SET)
;; (try! (as-contract (contract-call? .sbtc-token
;; transfer u250000 tx-sender (unwrap-panic (var-get dex-contract)) none))) ;; 0.00250000 BTC to DEX
;; (try! (as-contract (contract-call? .sbtc-token
;; transfer u10000 tx-sender (unwrap-panic (var-get dao-multi-sig)) none))) ;; 0.00010000 BTC to multi-sig/admin -> covers contract deployment fees
;; (try! (as-contract (contract-call? .sbtc-token
;; transfer u140000 tx-sender MULTI-SIG-CREATOR none))) ;; 0.00140000 BTC fees -> covers ordinals bot, pontis and faktory
(var-set token-contract (some tx-sender))
(var-set distribution-height (some burn-block-height))
(var-set last-airdrop-height (some burn-block-height))
(var-set ft-balance FT-INITIALIZED-BALANCE) ;; 40M tokens
(print {
type: "distribution-initialized",
token-contract: contract-caller,
distribution-height: burn-block-height,
ft-balance: FT-INITIALIZED-BALANCE
})
(ok true)))
;;; on Bonding
(define-public (toggle-bonded)
(begin
(asserts! (is-eq contract-caller (unwrap! (var-get dex-contract) ERR-NOT-SET)) ERR-NOT-AUTHORIZED)
(var-set accelerated-vesting true)
(var-set final-airdrop-mode true)
(ok true)))
;; Simplified Fee Distribution System
;; Constants
(define-constant COOLDOWN-PERIOD u2100) ;; Longer cooldown between airdrops
;; Error constants
(define-constant ERR-NO-FEES-TO-DISTRIBUTE (err u323))
(define-constant ERR-COOLDOWN-ACTIVE (err u324))
(define-constant ERR-TOTAL-SEATS-ZERO (err u325))
;; Data vars for fee tracking
(define-data-var accumulated-fees uint u0) ;; Total fees accumulated since last airdrop
(define-data-var last-airdrop-height (optional uint) (some u0)) ;; Block height of the last airdrop
(define-data-var final-airdrop-mode bool false) ;; Toggle for final airdrop mode
;; Add this function to allow the DEX to send fees to the contract
(define-public (create-fees-receipt (amount uint))
(let ((current-fees (var-get accumulated-fees)))
;; Only the DEX contract can call this function
(asserts! (is-eq contract-caller (unwrap! (var-get dex-contract) ERR-NOT-SET)) ERR-NOT-AUTHORIZED)
;; Update accumulated fees
(var-set accumulated-fees (+ current-fees amount))
(print {
type: "fees-received",
amount: amount,
total-accumulated: (+ current-fees amount)
})
(ok true)))
;; Check if airdrop can be triggered
(define-read-only (can-trigger-airdrop)
(let ((cooldown-expired (>= burn-block-height (+ (unwrap-panic (var-get last-airdrop-height)) COOLDOWN-PERIOD)))
(has-fees (> (var-get accumulated-fees) u0))
(final-mode (var-get final-airdrop-mode)))
(or (and cooldown-expired has-fees)
(and final-mode has-fees))))
;; Main airdrop function - anyone can call
(define-public (trigger-fee-airdrop)
(let ((total-fees (var-get accumulated-fees))
(total-seats (var-get total-seats-taken))
(can-airdrop (can-trigger-airdrop)))
;; Check if airdrop can be triggered
(asserts! can-airdrop (if (> total-fees u0)
ERR-COOLDOWN-ACTIVE
ERR-NO-FEES-TO-DISTRIBUTE))
;; Must have fees to distribute and seats must exist
(asserts! (> total-fees u0) ERR-NO-FEES-TO-DISTRIBUTE)
(asserts! (> total-seats u0) ERR-TOTAL-SEATS-ZERO)
;; Distribute fees to all seat holders
(map distribute-to-holder (var-get seat-holders))
;; Reset accumulated fees and update last airdrop height
(var-set accumulated-fees u0)
(var-set last-airdrop-height (some burn-block-height))
(print {
type: "fee-airdrop",
total-distributed: total-fees,
timestamp: burn-block-height
})
(ok total-fees)))
;; Helper function to distribute fees to a single holder
(define-private (distribute-to-holder (entry {owner: principal, seats: uint}))
(let ((holder (get owner entry))
(user-seats (get seats entry))
(total-seats (var-get total-seats-taken))
(total-fees (var-get accumulated-fees))
(user-share (if (and (> user-seats u0) (> total-seats u0))
(/ (* total-fees user-seats) total-seats)
u0)))
;; Only distribute if the user's share is greater than zero
(if (> user-share u0)
(match (as-contract (contract-call? .sbtc-token
transfer user-share tx-sender holder none))
success
(begin
(print {
type: "fee-distribution",
recipient: holder,
seats: user-seats,
amount: user-share
})
true)
error false)
false)))
;; Helper to extract principal from seat-holder entry
(define-private (get-holder-principal (entry {owner: principal, seats: uint}))
(get owner entry))
;; Get all unique seat holders
(define-read-only (get-all-seat-holders)
(ok (var-get seat-holders)))
;; Get fee distribution info for UI
(define-read-only (get-fee-distribution-info)
(ok {
accumulated-fees: (var-get accumulated-fees),
last-airdrop-height: (var-get last-airdrop-height),
current-height: burn-block-height,
cooldown-period: COOLDOWN-PERIOD,
final-airdrop-mode: (var-get final-airdrop-mode),
can-trigger-now: (can-trigger-airdrop)
}))
;; Get user's expected share in the next airdrop
(define-read-only (get-user-expected-share (user principal))
(let ((user-seats (default-to u0 (map-get? seats-owned user)))
(total-seats (var-get total-seats-taken))
(total-fees (var-get accumulated-fees)))
(ok {
user: user,
user-seats: user-seats,
total-seats: total-seats,
total-accumulated-fees: total-fees,
expected-share: (if (and (> user-seats u0) (> total-seats u0) (> total-fees u0))
(/ (* total-fees user-seats) total-seats)
u0)
})))
;; boot contract
(var-set deployment-height (some burn-block-height))

83
contracts/sbtc-token.clar Normal file
View File

@@ -0,0 +1,83 @@
;; title: Mock sBTC Token
;; version: 1.0
;; summary: Mock implementation of sBTC for local testing
;; description: Implements SIP-010 trait and mimics mainnet sBTC token functionality
;; traits
;;
;; token definitions
(define-fungible-token sbtc-token)
;; constants
(define-constant ERR_NOT_OWNER (err u4))
(define-constant ERR_INVALID_SENDER (err u5))
(define-constant ERR_INVALID_RECIPIENT (err u6))
(define-constant ERR_INSUFFICIENT_BALANCE (err u7))
(define-constant token-decimals u8)
(define-constant INITIAL_SUPPLY u2100000000000000) ;; 21m sBTC with 8 decimals for testing
;; data vars
(define-data-var token-name (string-ascii 32) "sBTC")
(define-data-var token-symbol (string-ascii 10) "sBTC")
(define-data-var token-uri (optional (string-utf8 256)) (some u"https://ipfs.io/ipfs/bafkreibqnozdui4ntgoh3oo437lvhg7qrsccmbzhgumwwjf2smb3eegyqu"))
;; data maps
;;
;; public functions
(define-public (transfer (amount uint) (sender principal) (recipient principal) (memo (optional (buff 34))))
(begin
;; Authorization check
(asserts! (or (is-eq tx-sender sender) (is-eq contract-caller sender)) ERR_NOT_OWNER)
;; Sender validation
(asserts! (not (is-eq sender recipient)) ERR_INVALID_SENDER)
;; Balance check
(asserts! (<= amount (ft-get-balance sbtc-token sender)) ERR_INSUFFICIENT_BALANCE)
;; Recipient validation - ensure not burning tokens
(asserts! (not (is-eq recipient (as-contract tx-sender))) ERR_INVALID_RECIPIENT)
;; Perform transfer
(try! (ft-transfer? sbtc-token amount sender recipient))
;; Handle memo if provided
(match memo to-print (print to-print) 0x)
(ok true)
)
)
;; faucet function for testing
(define-public (faucet)
(begin
(try! (ft-mint? sbtc-token u690000000 tx-sender)) ;; Mint 6.9 sBTC to caller
(ok true)
)
)
;; read only functions
(define-read-only (get-name)
(ok (var-get token-name))
)
(define-read-only (get-symbol)
(ok (var-get token-symbol))
)
(define-read-only (get-decimals)
(ok token-decimals)
)
(define-read-only (get-balance (who principal))
(ok (ft-get-balance sbtc-token who))
)
(define-read-only (get-total-supply)
(ok (ft-get-supply sbtc-token))
)
(define-read-only (get-token-uri)
(ok (var-get token-uri))
)
;; initialize contract
(begin
;; Mint initial supply to contract deployer
(try! (ft-mint? sbtc-token INITIAL_SUPPLY tx-sender))
)

24
package.json Normal file
View File

@@ -0,0 +1,24 @@
{
"name": "pre-simplified-fees-tests",
"version": "1.0.0",
"description": "Run unit tests on this project.",
"type": "module",
"private": true,
"scripts": {
"test": "vitest run",
"test:report": "vitest run -- --coverage --costs",
"test:watch": "chokidar \"tests/**/*.ts\" \"contracts/**/*.clar\" -c \"npm run test:report\""
},
"author": "",
"license": "ISC",
"dependencies": {
"@hirosystems/clarinet-sdk": "^2.3.2",
"@stacks/transactions": "^6.12.0",
"chokidar-cli": "^3.0.0",
"typescript": "^5.3.3",
"vite": "^5.1.4",
"vitest": "^1.3.1",
"vitest-environment-clarinet": "^2.0.0"
}
}

151
settings/Devnet.toml Normal file
View File

@@ -0,0 +1,151 @@
[network]
name = "devnet"
deployment_fee_rate = 10
[accounts.deployer]
mnemonic = "twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw"
balance = 100_000_000_000_000
# secret_key: 753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601
# stx_address: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM
# btc_address: mqVnk6NPRdhntvfm4hh9vvjiRkFDUuSYsH
[accounts.wallet_1]
mnemonic = "sell invite acquire kitten bamboo drastic jelly vivid peace spawn twice guilt pave pen trash pretty park cube fragile unaware remain midnight betray rebuild"
balance = 100_000_000_000_000
# secret_key: 7287ba251d44a4d3fd9276c88ce34c5c52a038955511cccaf77e61068649c17801
# stx_address: ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5
# btc_address: mr1iPkD9N3RJZZxXRk7xF9d36gffa6exNC
[accounts.wallet_2]
mnemonic = "hold excess usual excess ring elephant install account glad dry fragile donkey gaze humble truck breeze nation gasp vacuum limb head keep delay hospital"
balance = 100_000_000_000_000
# secret_key: 530d9f61984c888536871c6573073bdfc0058896dc1adfe9a6a10dfacadc209101
# stx_address: ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG
# btc_address: muYdXKmX9bByAueDe6KFfHd5Ff1gdN9ErG
[accounts.wallet_3]
mnemonic = "cycle puppy glare enroll cost improve round trend wrist mushroom scorpion tower claim oppose clever elephant dinosaur eight problem before frozen dune wagon high"
balance = 100_000_000_000_000
# secret_key: d655b2523bcd65e34889725c73064feb17ceb796831c0e111ba1a552b0f31b3901
# stx_address: ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC
# btc_address: mvZtbibDAAA3WLpY7zXXFqRa3T4XSknBX7
[accounts.wallet_4]
mnemonic = "board list obtain sugar hour worth raven scout denial thunder horse logic fury scorpion fold genuine phrase wealth news aim below celery when cabin"
balance = 100_000_000_000_000
# secret_key: f9d7206a47f14d2870c163ebab4bf3e70d18f5d14ce1031f3902fbbc894fe4c701
# stx_address: ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND
# btc_address: mg1C76bNTutiCDV3t9nWhZs3Dc8LzUufj8
[accounts.wallet_5]
mnemonic = "hurry aunt blame peanut heavy update captain human rice crime juice adult scale device promote vast project quiz unit note reform update climb purchase"
balance = 100_000_000_000_000
# secret_key: 3eccc5dac8056590432db6a35d52b9896876a3d5cbdea53b72400bc9c2099fe801
# stx_address: ST2REHHS5J3CERCRBEPMGH7921Q6PYKAADT7JP2VB
# btc_address: mweN5WVqadScHdA81aATSdcVr4B6dNokqx
[accounts.wallet_6]
mnemonic = "area desk dutch sign gold cricket dawn toward giggle vibrant indoor bench warfare wagon number tiny universe sand talk dilemma pottery bone trap buddy"
balance = 100_000_000_000_000
# secret_key: 7036b29cb5e235e5fd9b09ae3e8eec4404e44906814d5d01cbca968a60ed4bfb01
# stx_address: ST3AM1A56AK2C1XAFJ4115ZSV26EB49BVQ10MGCS0
# btc_address: mzxXgV6e4BZSsz8zVHm3TmqbECt7mbuErt
[accounts.wallet_7]
mnemonic = "prevent gallery kind limb income control noise together echo rival record wedding sense uncover school version force bleak nuclear include danger skirt enact arrow"
balance = 100_000_000_000_000
# secret_key: b463f0df6c05d2f156393eee73f8016c5372caa0e9e29a901bb7171d90dc4f1401
# stx_address: ST3PF13W7Z0RRM42A8VZRVFQ75SV1K26RXEP8YGKJ
# btc_address: n37mwmru2oaVosgfuvzBwgV2ysCQRrLko7
[accounts.wallet_8]
mnemonic = "female adjust gallery certain visit token during great side clown fitness like hurt clip knife warm bench start reunion globe detail dream depend fortune"
balance = 100_000_000_000_000
# secret_key: 6a1a754ba863d7bab14adbbc3f8ebb090af9e871ace621d3e5ab634e1422885e01
# stx_address: ST3NBRSFKX28FQ2ZJ1MAKX58HKHSDGNV5N7R21XCP
# btc_address: n2v875jbJ4RjBnTjgbfikDfnwsDV5iUByw
[accounts.faucet]
mnemonic = "shadow private easily thought say logic fault paddle word top book during ignore notable orange flight clock image wealth health outside kitten belt reform"
balance = 100_000_000_000_000
# secret_key: de433bdfa14ec43aa1098d5be594c8ffb20a31485ff9de2923b2689471c401b801
# stx_address: STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6
# btc_address: mjSrB3wS4xab3kYqFktwBzfTdPg367ZJ2d
[devnet]
disable_stacks_explorer = false
disable_stacks_api = false
# disable_subnet_api = false
# disable_bitcoin_explorer = true
# working_dir = "tmp/devnet"
# stacks_node_events_observers = ["host.docker.internal:8002"]
# miner_mnemonic = "fragile loan twenty basic net assault jazz absorb diet talk art shock innocent float punch travel gadget embrace caught blossom hockey surround initial reduce"
# miner_derivation_path = "m/44'/5757'/0'/0/0"
# faucet_mnemonic = "shadow private easily thought say logic fault paddle word top book during ignore notable orange flight clock image wealth health outside kitten belt reform"
# faucet_derivation_path = "m/44'/5757'/0'/0/0"
# orchestrator_port = 20445
# bitcoin_node_p2p_port = 18444
# bitcoin_node_rpc_port = 18443
# bitcoin_node_username = "devnet"
# bitcoin_node_password = "devnet"
# bitcoin_controller_block_time = 30_000
# stacks_node_rpc_port = 20443
# stacks_node_p2p_port = 20444
# stacks_api_port = 3999
# stacks_api_events_port = 3700
# bitcoin_explorer_port = 8001
# stacks_explorer_port = 8000
# postgres_port = 5432
# postgres_username = "postgres"
# postgres_password = "postgres"
# postgres_database = "postgres"
# bitcoin_node_image_url = "quay.io/hirosystems/bitcoind:26.0"
# stacks_node_image_url = "quay.io/hirosystems/stacks-node:devnet-2.5"
# stacks_signer_image_url = "quay.io/hirosystems/stacks-signer:devnet-2.5"
# stacks_api_image_url = "hirosystems/stacks-blockchain-api:master"
# stacks_explorer_image_url = "hirosystems/explorer:latest"
# bitcoin_explorer_image_url = "quay.io/hirosystems/bitcoin-explorer:devnet"
# postgres_image_url = "postgres:alpine"
# enable_subnet_node = true
# subnet_node_image_url = "hirosystems/stacks-subnets:0.8.1"
# subnet_leader_mnemonic = "twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw"
# subnet_leader_derivation_path = "m/44'/5757'/0'/0/0"
# subnet_contract_id = "ST173JK7NZBA4BS05ZRATQH1K89YJMTGEH1Z5J52E.subnet-v3-0-1"
# subnet_node_rpc_port = 30443
# subnet_node_p2p_port = 30444
# subnet_events_ingestion_port = 30445
# subnet_node_events_observers = ["host.docker.internal:8002"]
# subnet_api_image_url = "hirosystems/stacks-blockchain-api:master"
# subnet_api_postgres_database = "subnet_api"
# For testing in epoch 2.1 / using Clarity2
# epoch_2_0 = 100
# epoch_2_05 = 100
# epoch_2_1 = 101
# epoch_2_2 = 102
# epoch_2_3 = 103
# epoch_2_4 = 104
# epoch_2_5 = 108
# Send some stacking orders
[[devnet.pox_stacking_orders]]
start_at_cycle = 1
duration = 12
wallet = "wallet_1"
slots = 2
btc_address = "mr1iPkD9N3RJZZxXRk7xF9d36gffa6exNC"
[[devnet.pox_stacking_orders]]
start_at_cycle = 1
duration = 12
wallet = "wallet_2"
slots = 1
btc_address = "muYdXKmX9bByAueDe6KFfHd5Ff1gdN9ErG"
[[devnet.pox_stacking_orders]]
start_at_cycle = 1
duration = 12
wallet = "wallet_3"
slots = 1
btc_address = "mvZtbibDAAA3WLpY7zXXFqRa3T4XSknBX7"

21
tests/aibtc-dao-traits-v2.test.ts vendored Normal file
View File

@@ -0,0 +1,21 @@
import { describe, expect, it } from "vitest";
const accounts = simnet.getAccounts();
const address1 = accounts.get("wallet_1")!;
/*
The test below is an example. To learn more, read the testing documentation here:
https://docs.hiro.so/clarinet/feature-guides/test-contract-with-clarinet-sdk
*/
describe("example tests", () => {
it("ensures simnet is well initalised", () => {
expect(simnet.blockHeight).toBeDefined();
});
// it("shows an example", () => {
// const { result } = simnet.callReadOnlyFn("counter", "get-counter", [], address1);
// expect(result).toBeUint(0);
// });
});

21
tests/faktory-dex-trait-v1-1.test.ts vendored Normal file
View File

@@ -0,0 +1,21 @@
import { describe, expect, it } from "vitest";
const accounts = simnet.getAccounts();
const address1 = accounts.get("wallet_1")!;
/*
The test below is an example. To learn more, read the testing documentation here:
https://docs.hiro.so/clarinet/feature-guides/test-contract-with-clarinet-sdk
*/
describe("example tests", () => {
it("ensures simnet is well initalised", () => {
expect(simnet.blockHeight).toBeDefined();
});
// it("shows an example", () => {
// const { result } = simnet.callReadOnlyFn("counter", "get-counter", [], address1);
// expect(result).toBeUint(0);
// });
});

21
tests/faktory-trait-v1.test.ts vendored Normal file
View File

@@ -0,0 +1,21 @@
import { describe, expect, it } from "vitest";
const accounts = simnet.getAccounts();
const address1 = accounts.get("wallet_1")!;
/*
The test below is an example. To learn more, read the testing documentation here:
https://docs.hiro.so/clarinet/feature-guides/test-contract-with-clarinet-sdk
*/
describe("example tests", () => {
it("ensures simnet is well initalised", () => {
expect(simnet.blockHeight).toBeDefined();
});
// it("shows an example", () => {
// const { result } = simnet.callReadOnlyFn("counter", "get-counter", [], address1);
// expect(result).toBeUint(0);
// });
});

21
tests/name-faktory-dex.test.ts vendored Normal file
View File

@@ -0,0 +1,21 @@
import { describe, expect, it } from "vitest";
const accounts = simnet.getAccounts();
const address1 = accounts.get("wallet_1")!;
/*
The test below is an example. To learn more, read the testing documentation here:
https://docs.hiro.so/clarinet/feature-guides/test-contract-with-clarinet-sdk
*/
describe("example tests", () => {
it("ensures simnet is well initalised", () => {
expect(simnet.blockHeight).toBeDefined();
});
// it("shows an example", () => {
// const { result } = simnet.callReadOnlyFn("counter", "get-counter", [], address1);
// expect(result).toBeUint(0);
// });
});

21
tests/name-faktory.test.ts vendored Normal file
View File

@@ -0,0 +1,21 @@
import { describe, expect, it } from "vitest";
const accounts = simnet.getAccounts();
const address1 = accounts.get("wallet_1")!;
/*
The test below is an example. To learn more, read the testing documentation here:
https://docs.hiro.so/clarinet/feature-guides/test-contract-with-clarinet-sdk
*/
describe("example tests", () => {
it("ensures simnet is well initalised", () => {
expect(simnet.blockHeight).toBeDefined();
});
// it("shows an example", () => {
// const { result } = simnet.callReadOnlyFn("counter", "get-counter", [], address1);
// expect(result).toBeUint(0);
// });
});

21
tests/name-pre-faktory.test.ts vendored Normal file
View File

@@ -0,0 +1,21 @@
import { describe, expect, it } from "vitest";
const accounts = simnet.getAccounts();
const address1 = accounts.get("wallet_1")!;
/*
The test below is an example. To learn more, read the testing documentation here:
https://docs.hiro.so/clarinet/feature-guides/test-contract-with-clarinet-sdk
*/
describe("example tests", () => {
it("ensures simnet is well initalised", () => {
expect(simnet.blockHeight).toBeDefined();
});
// it("shows an example", () => {
// const { result } = simnet.callReadOnlyFn("counter", "get-counter", [], address1);
// expect(result).toBeUint(0);
// });
});

21
tests/sbtc-token.test.ts vendored Normal file
View File

@@ -0,0 +1,21 @@
import { describe, expect, it } from "vitest";
const accounts = simnet.getAccounts();
const address1 = accounts.get("wallet_1")!;
/*
The test below is an example. To learn more, read the testing documentation here:
https://docs.hiro.so/clarinet/feature-guides/test-contract-with-clarinet-sdk
*/
describe("example tests", () => {
it("ensures simnet is well initalised", () => {
expect(simnet.blockHeight).toBeDefined();
});
// it("shows an example", () => {
// const { result } = simnet.callReadOnlyFn("counter", "get-counter", [], address1);
// expect(result).toBeUint(0);
// });
});

26
tsconfig.json Normal file
View File

@@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ESNext"],
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"strict": true,
"noImplicitAny": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": [
"node_modules/@hirosystems/clarinet-sdk/vitest-helpers/src",
"tests"
]
}

42
vitest.config.js vendored Normal file
View File

@@ -0,0 +1,42 @@
/// <reference types="vitest" />
import { defineConfig } from "vite";
import { vitestSetupFilePath, getClarinetVitestsArgv } from "@hirosystems/clarinet-sdk/vitest";
/*
In this file, Vitest is configured so that it works seamlessly with Clarinet and the Simnet.
The `vitest-environment-clarinet` will initialise the clarinet-sdk
and make the `simnet` object available globally in the test files.
`vitestSetupFilePath` points to a file in the `@hirosystems/clarinet-sdk` package that does two things:
- run `before` hooks to initialize the simnet and `after` hooks to collect costs and coverage reports.
- load custom vitest matchers to work with Clarity values (such as `expect(...).toBeUint()`)
The `getClarinetVitestsArgv()` will parse options passed to the command `vitest run --`
- vitest run -- --manifest ./Clarinet.toml # pass a custom path
- vitest run -- --coverage --costs # collect coverage and cost reports
*/
export default defineConfig({
test: {
environment: "clarinet", // use vitest-environment-clarinet
pool: "forks",
poolOptions: {
threads: { singleThread: true },
forks: { singleFork: true },
},
setupFiles: [
vitestSetupFilePath,
// custom setup files can be added here
],
environmentOptions: {
clarinet: {
...getClarinetVitestsArgv(),
// add or override options
},
},
},
});