/**
* Template utilities for Clarity contract generation
*/
export interface ContractTemplate {
name: string;
description: string;
code: string;
placeholders: string[];
}
export const TEMPLATES: Record<string, ContractTemplate> = {
'fungible-token': {
name: 'Fungible Token',
description: 'SIP-010 compliant fungible token',
code: `;; {{TOKEN_NAME}} - SIP-010 Fungible Token
;; Generated by StacksAgent MCP
;; SIP-010 Trait Implementation
;; Note: Trait addresses are network-specific:
;; - Mainnet: SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.sip-010-trait
;; - Testnet: ST339A455EK9PAY9NP81WHK73T1JMFC3NN0321T18.sip-010-trait-ft-standard.sip-010-trait
(impl-trait '{{TRAIT_ADDRESS}}.sip-010-trait-ft-standard.sip-010-trait)
;; Constants
(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u100))
(define-constant err-not-token-owner (err u101))
(define-constant err-insufficient-balance (err u102))
;; Token definitions
(define-fungible-token {{TOKEN_SYMBOL}} u{{TOTAL_SUPPLY}})
;; Initialize token supply to contract owner
(begin
(try! (ft-mint? {{TOKEN_SYMBOL}} u{{TOTAL_SUPPLY}} contract-owner))
)
;; SIP-010 Functions
(define-public (transfer (amount uint) (sender principal) (recipient principal) (memo (optional (buff 34))))
(begin
(asserts! (is-eq tx-sender sender) err-not-token-owner)
(asserts! (> amount u0) (err u103))
(try! (ft-transfer? {{TOKEN_SYMBOL}} amount sender recipient))
(match memo to-print (print to-print) 0x)
(ok true)))
(define-read-only (get-name)
(ok "{{TOKEN_NAME}}"))
(define-read-only (get-symbol)
(ok "{{TOKEN_SYMBOL}}"))
(define-read-only (get-decimals)
(ok u{{DECIMALS}}))
(define-read-only (get-balance (who principal))
(ok (ft-get-balance {{TOKEN_SYMBOL}} who)))
(define-read-only (get-total-supply)
(ok (ft-get-supply {{TOKEN_SYMBOL}})))
(define-read-only (get-token-uri)
(ok none))
`,
placeholders: ['TOKEN_NAME', 'TOKEN_SYMBOL', 'TOTAL_SUPPLY', 'DECIMALS', 'TRAIT_ADDRESS'],
},
'non-fungible-token': {
name: 'Non-Fungible Token',
description: 'SIP-009 compliant NFT',
code: `;; {{NFT_NAME}} - SIP-009 Non-Fungible Token
;; Generated by StacksAgent MCP
;; SIP-009 Trait Implementation
;; Note: Trait addresses are network-specific:
;; - Mainnet: SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait.nft-trait
;; - Testnet: ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.nft-trait.nft-trait
(impl-trait '{{NFT_TRAIT_ADDRESS}}.nft-trait.nft-trait)
;; Constants
(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u100))
(define-constant err-not-token-owner (err u101))
(define-constant err-token-exists (err u102))
(define-constant err-token-not-found (err u103))
;; NFT definition
(define-non-fungible-token {{NFT_SYMBOL}} uint)
;; Data variables
(define-data-var last-token-id uint u0)
(define-data-var token-uri (string-utf8 256) u"{{BASE_URI}}")
;; Data maps
(define-map token-metadata uint {uri: (string-utf8 256)})
;; SIP-009 Functions
(define-public (transfer (token-id uint) (sender principal) (recipient principal))
(begin
(asserts! (is-eq tx-sender sender) err-not-token-owner)
(nft-transfer? {{NFT_SYMBOL}} token-id sender recipient)))
(define-read-only (get-last-token-id)
(ok (var-get last-token-id)))
(define-read-only (get-token-uri (token-id uint))
(ok (some (var-get token-uri))))
(define-read-only (get-owner (token-id uint))
(ok (nft-get-owner? {{NFT_SYMBOL}} token-id)))
;; Mint function
(define-public (mint (recipient principal))
(let
(
(token-id (+ (var-get last-token-id) u1))
)
(asserts! (is-eq tx-sender contract-owner) err-owner-only)
(try! (nft-mint? {{NFT_SYMBOL}} token-id recipient))
(var-set last-token-id token-id)
(ok token-id)))
;; Set token URI
(define-public (set-token-uri (new-uri (string-utf8 256)))
(begin
(asserts! (is-eq tx-sender contract-owner) err-owner-only)
(ok (var-set token-uri new-uri))))
`,
placeholders: ['NFT_NAME', 'NFT_SYMBOL', 'BASE_URI', 'NFT_TRAIT_ADDRESS'],
},
'vault': {
name: 'Simple Vault',
description: 'Basic vault for holding STX with deposit/withdraw',
code: `;; Simple Vault Contract
;; Generated by StacksAgent MCP
;; Constants
(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u100))
(define-constant err-insufficient-balance (err u101))
(define-constant err-invalid-amount (err u102))
;; Data maps
(define-map vault-balances principal uint)
;; Data variables
(define-data-var total-deposited uint u0)
;; Public functions
(define-public (deposit (amount uint))
(begin
(asserts! (> amount u0) err-invalid-amount)
(try! (stx-transfer? amount tx-sender (as-contract tx-sender)))
(map-set vault-balances tx-sender
(+ (default-to u0 (map-get? vault-balances tx-sender)) amount))
(var-set total-deposited (+ (var-get total-deposited) amount))
(ok true)))
(define-public (withdraw (amount uint))
(let
(
(balance (default-to u0 (map-get? vault-balances tx-sender)))
)
(asserts! (> amount u0) err-invalid-amount)
(asserts! (>= balance amount) err-insufficient-balance)
(try! (as-contract (stx-transfer? amount tx-sender tx-sender)))
(map-set vault-balances tx-sender (- balance amount))
(var-set total-deposited (- (var-get total-deposited) amount))
(ok true)))
;; Read-only functions
(define-read-only (get-balance (user principal))
(ok (default-to u0 (map-get? vault-balances user))))
(define-read-only (get-total-deposited)
(ok (var-get total-deposited)))
`,
placeholders: [],
},
'dao': {
name: 'Basic DAO',
description: 'Simple DAO with proposal and voting mechanism',
code: `;; Basic DAO Contract
;; Generated by StacksAgent MCP
;; Constants
(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u100))
(define-constant err-already-voted (err u101))
(define-constant err-proposal-not-found (err u102))
(define-constant err-voting-closed (err u103))
(define-constant err-not-member (err u104))
;; Data variables
(define-data-var proposal-count uint u0)
(define-data-var member-count uint u0)
;; Data maps
(define-map proposals
uint
{
proposer: principal,
title: (string-utf8 256),
description: (string-utf8 1024),
yes-votes: uint,
no-votes: uint,
end-block: uint,
executed: bool
})
(define-map votes {proposal-id: uint, voter: principal} bool)
(define-map members principal bool)
;; Membership functions
(define-public (add-member (member principal))
(begin
(asserts! (is-eq tx-sender contract-owner) err-owner-only)
(map-set members member true)
(var-set member-count (+ (var-get member-count) u1))
(ok true)))
(define-read-only (is-member (account principal))
(default-to false (map-get? members account)))
;; Proposal functions
(define-public (create-proposal (title (string-utf8 256)) (description (string-utf8 1024)) (duration uint))
(let
(
(proposal-id (+ (var-get proposal-count) u1))
)
(asserts! (is-member tx-sender) err-not-member)
(map-set proposals proposal-id {
proposer: tx-sender,
title: title,
description: description,
yes-votes: u0,
no-votes: u0,
end-block: (+ block-height duration),
executed: false
})
(var-set proposal-count proposal-id)
(ok proposal-id)))
(define-public (vote (proposal-id uint) (vote-yes bool))
(let
(
(proposal (unwrap! (map-get? proposals proposal-id) err-proposal-not-found))
(voter-key {proposal-id: proposal-id, voter: tx-sender})
)
(asserts! (is-member tx-sender) err-not-member)
(asserts! (is-none (map-get? votes voter-key)) err-already-voted)
(asserts! (< block-height (get end-block proposal)) err-voting-closed)
(if vote-yes
(map-set proposals proposal-id (merge proposal {yes-votes: (+ (get yes-votes proposal) u1)}))
(map-set proposals proposal-id (merge proposal {no-votes: (+ (get no-votes proposal) u1)})))
(map-set votes voter-key true)
(ok true)))
;; Read-only functions
(define-read-only (get-proposal (proposal-id uint))
(map-get? proposals proposal-id))
(define-read-only (get-proposal-count)
(ok (var-get proposal-count)))
(define-read-only (has-voted (proposal-id uint) (voter principal))
(is-some (map-get? votes {proposal-id: proposal-id, voter: voter})))
`,
placeholders: [],
},
'marketplace': {
name: 'NFT Marketplace',
description: 'Simple NFT marketplace with listing and buying',
code: `;; NFT Marketplace Contract
;; Generated by StacksAgent MCP
;; Constants
(define-constant contract-owner tx-sender)
(define-constant err-not-owner (err u100))
(define-constant err-listing-not-found (err u101))
(define-constant err-insufficient-payment (err u102))
(define-constant err-listing-expired (err u103))
(define-constant err-nft-transfer-failed (err u104))
;; Data variables
(define-data-var listing-count uint u0)
(define-data-var platform-fee-percent uint u250) ;; 2.5% (250 basis points)
;; Data maps
(define-map listings
uint
{
nft-contract: principal,
token-id: uint,
seller: principal,
price: uint,
expiry: uint,
active: bool
})
;; Marketplace functions
(define-public (list-nft (nft-contract principal) (token-id uint) (price uint) (duration uint))
(let
(
(listing-id (+ (var-get listing-count) u1))
)
(asserts! (> price u0) (err u105))
(map-set listings listing-id {
nft-contract: nft-contract,
token-id: token-id,
seller: tx-sender,
price: price,
expiry: (+ block-height duration),
active: true
})
(var-set listing-count listing-id)
(ok listing-id)))
(define-public (cancel-listing (listing-id uint))
(let
(
(listing (unwrap! (map-get? listings listing-id) err-listing-not-found))
)
(asserts! (is-eq tx-sender (get seller listing)) err-not-owner)
(map-set listings listing-id (merge listing {active: false}))
(ok true)))
(define-public (buy-nft (listing-id uint))
(let
(
(listing (unwrap! (map-get? listings listing-id) err-listing-not-found))
(price (get price listing))
(fee (/ (* price (var-get platform-fee-percent)) u10000))
(seller-amount (- price fee))
)
(asserts! (get active listing) err-listing-not-found)
(asserts! (< block-height (get expiry listing)) err-listing-expired)
;; Transfer payment to seller
(try! (stx-transfer? seller-amount tx-sender (get seller listing)))
;; Transfer platform fee to contract owner
(try! (stx-transfer? fee tx-sender contract-owner))
;; Mark listing as inactive
(map-set listings listing-id (merge listing {active: false}))
(ok true)))
;; Read-only functions
(define-read-only (get-listing (listing-id uint))
(map-get? listings listing-id))
(define-read-only (get-listing-count)
(ok (var-get listing-count)))
(define-read-only (get-platform-fee)
(ok (var-get platform-fee-percent)))
;; Admin functions
(define-public (set-platform-fee (new-fee uint))
(begin
(asserts! (is-eq tx-sender contract-owner) err-not-owner)
(asserts! (<= new-fee u1000) (err u106)) ;; Max 10%
(ok (var-set platform-fee-percent new-fee))))
`,
placeholders: [],
},
'custom': {
name: 'Custom Contract',
description: 'Minimal contract skeleton',
code: `;; {{CONTRACT_NAME}}
;; Generated by StacksAgent MCP
;; Constants
(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u100))
;; Data variables
;; Add your data variables here
;; Data maps
;; Add your data maps here
;; Public functions
;; Add your public functions here
;; Read-only functions
;; Add your read-only functions here
;; Private functions
;; Add your private functions here
`,
placeholders: ['CONTRACT_NAME'],
},
};
/**
* Fills template placeholders with provided values
*/
export function fillTemplate(template: ContractTemplate, values: Record<string, string>): string {
let code = template.code;
for (const placeholder of template.placeholders) {
const value = values[placeholder] || placeholder;
const regex = new RegExp(`\\{\\{${placeholder}\\}\\}`, 'g');
code = code.replace(regex, value);
}
return code;
}
/**
* Gets default placeholder values
*/
export function getDefaultPlaceholders(contractType: string): Record<string, string> {
const defaults: Record<string, Record<string, string>> = {
'fungible-token': {
TOKEN_NAME: 'My Token',
TOKEN_SYMBOL: 'MTK',
TOTAL_SUPPLY: '1000000',
DECIMALS: '6',
},
'non-fungible-token': {
NFT_NAME: 'My NFT',
NFT_SYMBOL: 'MNFT',
BASE_URI: 'https://example.com/nft/',
},
'custom': {
CONTRACT_NAME: 'my-contract',
},
};
return defaults[contractType] || {};
}