mirror of
https://github.com/alexgo-io/simple-pre-faktory.git
synced 2026-01-12 08:34:38 +08:00
name pre faktory for audit
This commit is contained in:
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
tests/** linguist-vendored
|
||||
vitest.config.js linguist-vendored
|
||||
* text=lf
|
||||
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal 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
4
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
{
|
||||
"files.eol": "\n"
|
||||
}
|
||||
19
.vscode/tasks.json
vendored
Normal file
19
.vscode/tasks.json
vendored
Normal 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
49
Clarinet.toml
Normal 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
74
README.md
Normal 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
7
Testnet.toml
Normal 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>"
|
||||
254
contracts/aibtc-dao-traits-v2.clar
Normal file
254
contracts/aibtc-dao-traits-v2.clar
Normal 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))
|
||||
;; ))
|
||||
44
contracts/faktory-dex-trait-v1-1.clar
Normal file
44
contracts/faktory-dex-trait-v1-1.clar
Normal 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))
|
||||
)
|
||||
)
|
||||
24
contracts/faktory-trait-v1.clar
Normal file
24
contracts/faktory-trait-v1.clar
Normal 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))
|
||||
)
|
||||
)
|
||||
208
contracts/name-faktory-dex.clar
Normal file
208
contracts/name-faktory-dex.clar
Normal 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
120
contracts/name-faktory.clar
Normal 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,
|
||||
})
|
||||
)
|
||||
590
contracts/name-pre-faktory.clar
Normal file
590
contracts/name-pre-faktory.clar
Normal 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
83
contracts/sbtc-token.clar
Normal 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
24
package.json
Normal 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
151
settings/Devnet.toml
Normal 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
21
tests/aibtc-dao-traits-v2.test.ts
vendored
Normal 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
21
tests/faktory-dex-trait-v1-1.test.ts
vendored
Normal 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
21
tests/faktory-trait-v1.test.ts
vendored
Normal 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
21
tests/name-faktory-dex.test.ts
vendored
Normal 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
21
tests/name-faktory.test.ts
vendored
Normal 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
21
tests/name-pre-faktory.test.ts
vendored
Normal 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
21
tests/sbtc-token.test.ts
vendored
Normal 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
26
tsconfig.json
Normal 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
42
vitest.config.js
vendored
Normal 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
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user