Source Code

(define-constant ERR_UNAUTHORIZED (err u100))
(define-constant ERR_NOT_FOUND (err u101))
(define-constant ERR_ALREADY_EXISTS (err u102))
(define-constant ERR_INVALID_PARAMS (err u103))
(define-constant ERR_LAUNCH_ENDED (err u105))

(define-data-var contract-owner principal tx-sender)

(define-map token-launches uint {project-name: (string-utf8 100), token-contract: principal, creator: principal, total-supply: uint, price: uint, raised: uint, start-block: uint, end-block: uint, status: (string-ascii 20)})
(define-map participants {launch-id: uint, participant: principal} {contribution: uint, tokens-allocated: uint, claimed: bool})
(define-map project-kyc {launch-id: uint, participant: principal} {verified: bool, verification-hash: (buff 32)})
(define-map vesting-schedules {launch-id: uint, beneficiary: principal} {total-amount: uint, released: uint, start-block: uint, duration: uint})
(define-data-var launch-count uint u0)

(define-read-only (get-owner) (var-get contract-owner))

(define-read-only (get-token-launch (launch-id uint))
  (map-get? token-launches launch-id))

(define-read-only (get-participant (launch-id uint) (participant principal))
  (map-get? participants {launch-id: launch-id, participant: participant}))

(define-read-only (get-kyc-status (launch-id uint) (participant principal))
  (map-get? project-kyc {launch-id: launch-id, participant: participant}))

(define-read-only (get-vesting-schedule (launch-id uint) (beneficiary principal))
  (map-get? vesting-schedules {launch-id: launch-id, beneficiary: beneficiary}))

(define-public (create-launch (project-name (string-utf8 100)) (token-contract principal) (total-supply uint) (price uint) (duration uint))
  (let ((launch-id (+ (var-get launch-count) u1)))
    (asserts! (and (> total-supply u0) (> price u0) (> duration u0)) ERR_INVALID_PARAMS)
    (map-set token-launches launch-id {project-name: project-name, token-contract: token-contract, creator: tx-sender, total-supply: total-supply, price: price, raised: u0, start-block: stacks-block-height, end-block: (+ stacks-block-height duration), status: "active"})
    (var-set launch-count launch-id)
    (ok launch-id)))

(define-public (verify-participant-kyc (launch-id uint) (participant principal) (verification-hash (buff 32)))
  (let ((launch (unwrap! (map-get? token-launches launch-id) ERR_NOT_FOUND)))
    (asserts! (is-eq tx-sender (get creator launch)) ERR_UNAUTHORIZED)
    (ok (map-set project-kyc {launch-id: launch-id, participant: participant} {verified: true, verification-hash: verification-hash}))))

(define-public (participate (launch-id uint) (contribution uint))
  (let ((launch (unwrap! (map-get? token-launches launch-id) ERR_NOT_FOUND))
        (kyc (unwrap! (map-get? project-kyc {launch-id: launch-id, participant: tx-sender}) ERR_UNAUTHORIZED))
        (tokens-allocated (/ contribution (get price launch)))
        (existing-participation (default-to {contribution: u0, tokens-allocated: u0, claimed: false} 
                                             (map-get? participants {launch-id: launch-id, participant: tx-sender}))))
    (asserts! (get verified kyc) ERR_UNAUTHORIZED)
    (asserts! (is-eq (get status launch) "active") ERR_INVALID_PARAMS)
    (asserts! (<= stacks-block-height (get end-block launch)) ERR_LAUNCH_ENDED)
    (asserts! (> contribution u0) ERR_INVALID_PARAMS)
    (map-set participants {launch-id: launch-id, participant: tx-sender} 
      {contribution: (+ (get contribution existing-participation) contribution), 
       tokens-allocated: (+ (get tokens-allocated existing-participation) tokens-allocated), 
       claimed: false})
    (ok (map-set token-launches launch-id (merge launch {raised: (+ (get raised launch) contribution)})))))

(define-public (finalize-launch (launch-id uint))
  (let ((launch (unwrap! (map-get? token-launches launch-id) ERR_NOT_FOUND)))
    (asserts! (is-eq tx-sender (get creator launch)) ERR_UNAUTHORIZED)
    (asserts! (> stacks-block-height (get end-block launch)) ERR_INVALID_PARAMS)
    (ok (map-set token-launches launch-id (merge launch {status: "finalized"})))))

(define-public (create-vesting (launch-id uint) (beneficiary principal) (total-amount uint) (duration uint))
  (let ((launch (unwrap! (map-get? token-launches launch-id) ERR_NOT_FOUND)))
    (asserts! (is-eq tx-sender (get creator launch)) ERR_UNAUTHORIZED)
    (asserts! (and (> total-amount u0) (> duration u0)) ERR_INVALID_PARAMS)
    (ok (map-set vesting-schedules {launch-id: launch-id, beneficiary: beneficiary} 
         {total-amount: total-amount, released: u0, start-block: stacks-block-height, duration: duration}))))

(define-public (release-vested-tokens (launch-id uint))
  (let ((schedule (unwrap! (map-get? vesting-schedules {launch-id: launch-id, beneficiary: tx-sender}) ERR_NOT_FOUND))
        (elapsed (- stacks-block-height (get start-block schedule)))
        (releasable (if (>= elapsed (get duration schedule))
                       (get total-amount schedule)
                       (/ (* (get total-amount schedule) elapsed) (get duration schedule))))
        (unreleased (- releasable (get released schedule))))
    (asserts! (> unreleased u0) ERR_INVALID_PARAMS)
    (ok (map-set vesting-schedules {launch-id: launch-id, beneficiary: tx-sender} 
         (merge schedule {released: releasable})))))

(define-public (claim-tokens (launch-id uint))
  (let ((participation (unwrap! (map-get? participants {launch-id: launch-id, participant: tx-sender}) ERR_NOT_FOUND))
        (launch (unwrap! (map-get? token-launches launch-id) ERR_NOT_FOUND)))
    (asserts! (is-eq (get status launch) "finalized") ERR_INVALID_PARAMS)
    (asserts! (not (get claimed participation)) ERR_ALREADY_EXISTS)
    (ok (map-set participants {launch-id: launch-id, participant: tx-sender} (merge participation {claimed: true})))))

(define-public (update-launch-status (launch-id uint) (new-status (string-ascii 20)))
  (let ((launch (unwrap! (map-get? token-launches launch-id) ERR_NOT_FOUND)))
    (asserts! (is-eq tx-sender (var-get contract-owner)) ERR_UNAUTHORIZED)
    (ok (map-set token-launches launch-id (merge launch {status: new-status})))))

(define-public (transfer-ownership (new-owner principal))
  (begin
    (asserts! (is-eq tx-sender (var-get contract-owner)) ERR_UNAUTHORIZED)
    (ok (var-set contract-owner new-owner))))

Functions (14)

FunctionAccessArgs
get-ownerread-only
get-token-launchread-onlylaunch-id: uint
get-participantread-onlylaunch-id: uint, participant: principal
get-kyc-statusread-onlylaunch-id: uint, participant: principal
get-vesting-scheduleread-onlylaunch-id: uint, beneficiary: principal
create-launchpublicproject-name: (string-utf8 100
verify-participant-kycpubliclaunch-id: uint, participant: principal, verification-hash: (buff 32
participatepubliclaunch-id: uint, contribution: uint
finalize-launchpubliclaunch-id: uint
create-vestingpubliclaunch-id: uint, beneficiary: principal, total-amount: uint, duration: uint
release-vested-tokenspubliclaunch-id: uint
claim-tokenspubliclaunch-id: uint
update-launch-statuspubliclaunch-id: uint, new-status: (string-ascii 20
transfer-ownershippublicnew-owner: principal