mirror of
https://github.com/alexgo-io/alex-v1.git
synced 2026-01-12 06:23:41 +08:00
fix: bridge apower helper v1 01 (#708)
* feat: swap-helper-bridged-v1-1 (#686) * feat: add deps * feat: flash-loan susdt-xusd (#687) * Feat/flash loan susdt xusd (#688) * feat: flash-loan susdt-xusd * flashloan user added * conflict fixed * feat: launchpad v1-03 (#689) * chore: ordinals-bluewheel (#690) * feat: launchpad v1-03 * chore: ordinals-bluewheel * feat: event-claim-helper * feat: event claim helper (#692) * TODO: test suite * feat: event claim helper (#693) * working * working; * TODO: test suite * test working * working * feat: event claim helper (#694) * Update Clarinet.json * feat: non-breaking dev into main (#635) * feat: stable-swap-pool anchored to xusd * minor refactoring; test added * tests added * minor refactoring * chore: diasable non-prod contract tests * ci updated * ci updated * ci updated * feat: wip ci * feat: use curl for clarity manager * feat: fix path issue * curl added to ci * clean up ci * requirement changed to mainnet contract * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * Revert "feat: stable-swap-pool" * Revert "Revert "feat: stable-swap-pool"" (#633) * chore: short intro comments added to stable-swap-pool * fix: oracle returns proper price (#634) * tests updated to clarinet 1.0.0 requirements * Revert "tests updated to clarinet 1.0.0 requirements" This reverts commit3b95b93cd8. * Revert "Revert "tests updated to clarinet 1.0.0 requirements"" This reverts commit6677cd4d8f. * feat: merge dev * ci updated * fix: ci (#636) Co-authored-by: Zitao Xiong <caoer115@gmail.com> * working * working; * TODO: test suite * test working * working * claim-for-claimer * set-temp-timestamp * set-temp-timestamp * test added --------- Co-authored-by: Zitao Xiong <caoer115@gmail.com> * feat: event claim helper v1 01 (#695) * Update Clarinet.json * feat: non-breaking dev into main (#635) * feat: stable-swap-pool anchored to xusd * minor refactoring; test added * tests added * minor refactoring * chore: diasable non-prod contract tests * ci updated * ci updated * ci updated * feat: wip ci * feat: use curl for clarity manager * feat: fix path issue * curl added to ci * clean up ci * requirement changed to mainnet contract * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * auto-alex test updated * Revert "feat: stable-swap-pool" * Revert "Revert "feat: stable-swap-pool"" (#633) * chore: short intro comments added to stable-swap-pool * fix: oracle returns proper price (#634) * tests updated to clarinet 1.0.0 requirements * Revert "tests updated to clarinet 1.0.0 requirements" This reverts commit3b95b93cd8. * Revert "Revert "tests updated to clarinet 1.0.0 requirements"" This reverts commit6677cd4d8f. * feat: merge dev * ci updated * fix: ci (#636) Co-authored-by: Zitao Xiong <caoer115@gmail.com> * working * working; * TODO: test suite * test working * working * claim-for-claimer * set-temp-timestamp * set-temp-timestamp * test added * even-claim-helper-v1-01 * test --------- Co-authored-by: Zitao Xiong <caoer115@gmail.com> * feat: bridge tokens (#697) * feat: add contracts from bridge * fix: even claim helper test (#698) * fix: event-claim-helper test * fix: event-claim-helper test * fix: event-claim-helper test * fix: timestamp * chore: amm-swap-pool comments --------- Co-authored-by: fiftyeightandeight <alexd@alexgo.io> * feat: launchpad with liquidity lock (#700) * initial coding done * launchpad-liquidity-lock => liquidity-launchpad * feat: brc20-chax (#702) * chore: launchpad-with-pool-lock (#703) * feat: brc20-chax * liquidity-launchpad => launchpad-with-pool-lock * feat: bridged-apower-helper (#704) * feat: bridged-apower-helper * ci * feat: bridge-apower-helper-v1-01 (#705) * remove bridge-apower-helper-v1-01 * feat: missing file (#706) * feat: add clarinet for brc20-chax * clarinet.toml --------- Co-authored-by: fiftyeightandeight <443225+fiftyeightandeight@users.noreply.github.com> * fix: bridge-apower-helper-v1-01 --------- Co-authored-by: Kyle Fang <zhigang1992@gmail.com> Co-authored-by: Zitao Xiong <caoer115@gmail.com>
This commit is contained in:
committed by
GitHub
parent
2f03e34f36
commit
a7b3f4aa4d
4
.github/workflows/ci-clarinet.yaml
vendored
4
.github/workflows/ci-clarinet.yaml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
run: |
|
||||
cd clarity && ./scripts/clarinet_manager.sh install
|
||||
- name: "Check contract"
|
||||
uses: docker://hirosystems/clarinet:1.5.4
|
||||
uses: docker://hirosystems/clarinet:1.7.1
|
||||
with:
|
||||
entrypoint: "bash"
|
||||
args: -c "cd clarity && clarinet check"
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
run: |
|
||||
echo "ci_env=$(bash <(curl -s https://codecov.io/env))" >> $GITHUB_ENV
|
||||
- name: "Execute test suite"
|
||||
uses: docker://hirosystems/clarinet:1.5.4
|
||||
uses: docker://hirosystems/clarinet:1.7.1
|
||||
with:
|
||||
entrypoint: "bash"
|
||||
#args: -c "cd clarity && clarinet test --coverage && curl -Os https://uploader.codecov.io/latest/linux/codecov && chmod +x codecov && ./codecov -t ${{ secrets.CODECOV_TOKEN }} -f coverage.lcov"
|
||||
|
||||
@@ -1143,6 +1143,10 @@ depends_on = [ "auto-alex-v2", "token-apower", "brc20-db20" ]
|
||||
path = "contracts/token/brc20-db20.clar"
|
||||
depends_on = []
|
||||
|
||||
[contracts.brc20-chax]
|
||||
path = "contracts/token/brc20-chax.clar"
|
||||
depends_on = []
|
||||
|
||||
[contracts.flash-loan-user-amm-to-fixed-susdt-xusd]
|
||||
path = "contracts/flash-loan-user-amm-to-fixed-susdt-xusd.clar"
|
||||
|
||||
@@ -1163,7 +1167,17 @@ path = "contracts/helpers/event-claim-helper-v1-01.clar"
|
||||
clarity_version = 2
|
||||
epoch = "2.1"
|
||||
|
||||
[contracts.liquidity-launchpad]
|
||||
path = "contracts/pool/liquidity-launchpad.clar"
|
||||
[contracts.launchpad-with-pool-lock]
|
||||
path = "contracts/pool/launchpad-with-pool-lock.clar"
|
||||
|
||||
[contracts.bridge-apower-helper]
|
||||
path = "contracts/helpers/bridge-apower-helper.clar"
|
||||
clarity_version = 2
|
||||
epoch = "2.4"
|
||||
|
||||
[contracts.bridge-apower-helper-v1-01]
|
||||
path = "contracts/helpers/bridge-apower-helper-v1-01.clar"
|
||||
clarity_version = 2
|
||||
epoch = "2.4"
|
||||
|
||||
|
||||
|
||||
111
clarity/contracts/helpers/bridge-apower-helper-v1-01.clar
Normal file
111
clarity/contracts/helpers/bridge-apower-helper-v1-01.clar
Normal file
@@ -0,0 +1,111 @@
|
||||
(impl-trait .trait-ownable.ownable-trait)
|
||||
(use-trait ft-trait .trait-sip-010.sip-010-trait)
|
||||
|
||||
(define-constant ERR-NOT-AUTHORIZED (err u1000))
|
||||
(define-constant ERR-INVALID-TIMESTAMP (err u1001))
|
||||
(define-constant ERR-UNKNOWN-EVENT-ID (err u1002))
|
||||
(define-constant ERR-GET-BLOCK-INFO (err u1003))
|
||||
(define-constant ERR-TOKEN-MISMATCH (err u1004))
|
||||
|
||||
(define-constant ONE_8 u100000000)
|
||||
(define-constant MAX_UINT u340282366920938463463374607431768211455)
|
||||
|
||||
;; (define-constant apower .token-apower)
|
||||
|
||||
(define-data-var contract-owner principal tx-sender)
|
||||
|
||||
(define-data-var event-nonce uint u0)
|
||||
(define-map events uint { bridged-token: principal, apower-per-bridged: uint, apower-cap: uint, start-timestamp: uint, end-timestamp: uint })
|
||||
(define-map claimed { event-id: uint, user: principal } uint)
|
||||
|
||||
;; governance functions
|
||||
|
||||
(define-public (set-contract-owner (owner principal))
|
||||
(begin
|
||||
(try! (check-is-owner))
|
||||
(ok (var-set contract-owner owner))
|
||||
)
|
||||
)
|
||||
|
||||
(define-public (create-event (bridged-token principal) (apower-per-bridged uint) (start-timestamp uint) (end-timestamp uint) (apower-cap (optional uint)))
|
||||
(let
|
||||
(
|
||||
(event-id (+ (var-get event-nonce) u1))
|
||||
)
|
||||
(try! (check-is-owner))
|
||||
(asserts! (< start-timestamp end-timestamp) ERR-INVALID-TIMESTAMP)
|
||||
(map-set events event-id { bridged-token: bridged-token, apower-per-bridged: apower-per-bridged, apower-cap: (match apower-cap value value MAX_UINT), start-timestamp: start-timestamp, end-timestamp: end-timestamp })
|
||||
(var-set event-nonce event-id)
|
||||
(ok event-id)
|
||||
)
|
||||
)
|
||||
|
||||
;; read-only functions
|
||||
|
||||
(define-read-only (block-timestamp)
|
||||
(ok (unwrap! (get-block-info? time (- block-height u1)) ERR-GET-BLOCK-INFO)))
|
||||
|
||||
(define-read-only (get-contract-owner)
|
||||
(ok (var-get contract-owner)))
|
||||
|
||||
(define-read-only (get-event-details-or-fail (event-id uint))
|
||||
(ok (unwrap! (map-get? events event-id) ERR-UNKNOWN-EVENT-ID)))
|
||||
|
||||
(define-read-only (get-claimed-or-default (event-id uint) (user principal))
|
||||
(default-to u0 (map-get? claimed { event-id: event-id, user: user })))
|
||||
|
||||
;; external functions
|
||||
|
||||
(define-public (transfer-to-wrap
|
||||
(event-id uint)
|
||||
(order
|
||||
{
|
||||
to: uint,
|
||||
token: uint,
|
||||
amount-in-fixed: uint,
|
||||
chain-id: uint,
|
||||
salt: (buff 256)
|
||||
}
|
||||
)
|
||||
(token-trait <ft-trait>)
|
||||
(signature-packs (list 100 { signer: principal, order-hash: (buff 32), signature: (buff 65)})))
|
||||
(let
|
||||
(
|
||||
(current-timestamp (try! (block-timestamp)))
|
||||
(event-details (try! (get-event-details-or-fail event-id)))
|
||||
(recipient (try! (contract-call? .bridge-endpoint-v1-02 user-from-id-or-fail (get to order))))
|
||||
(apower-claimed (get-claimed-or-default event-id recipient))
|
||||
(apower-excess (- (get apower-cap event-details) apower-claimed))
|
||||
(apower-to-mint (min apower-excess (mul-down (get amount-in-fixed order) (get apower-per-bridged event-details))))
|
||||
)
|
||||
(asserts! (is-eq (contract-of token-trait) (get bridged-token event-details)) ERR-TOKEN-MISMATCH)
|
||||
;; ;; if timestamp invalid, instead of reverting, it skips and process wrap.
|
||||
(and
|
||||
(>= current-timestamp (get start-timestamp event-details))
|
||||
(<= current-timestamp (get end-timestamp event-details))
|
||||
(> apower-to-mint u0)
|
||||
(as-contract (try! (contract-call? .token-apower mint-fixed apower-to-mint recipient))))
|
||||
(map-set claimed { event-id: event-id, user: recipient } (+ apower-claimed apower-to-mint))
|
||||
(contract-call? .bridge-endpoint-v1-02 transfer-to-wrap order token-trait signature-packs)
|
||||
)
|
||||
)
|
||||
|
||||
;; internal functions
|
||||
|
||||
(define-private (check-is-owner)
|
||||
(ok (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED)))
|
||||
|
||||
(define-private (mul-down (a uint) (b uint))
|
||||
(/ (* a b) ONE_8))
|
||||
|
||||
(define-private (div-down (a uint) (b uint))
|
||||
(if (is-eq a u0)
|
||||
u0
|
||||
(/ (* a ONE_8) b)
|
||||
))
|
||||
|
||||
(define-private (max (a uint) (b uint))
|
||||
(if (<= a b) b a))
|
||||
|
||||
(define-private (min (a uint) (b uint))
|
||||
(if (<= a b) a b))
|
||||
111
clarity/contracts/helpers/bridge-apower-helper.clar
Normal file
111
clarity/contracts/helpers/bridge-apower-helper.clar
Normal file
@@ -0,0 +1,111 @@
|
||||
(impl-trait .trait-ownable.ownable-trait)
|
||||
(use-trait ft-trait .trait-sip-010.sip-010-trait)
|
||||
|
||||
(define-constant ERR-NOT-AUTHORIZED (err u1000))
|
||||
(define-constant ERR-INVALID-TIMESTAMP (err u1001))
|
||||
(define-constant ERR-UNKNOWN-EVENT-ID (err u1002))
|
||||
(define-constant ERR-GET-BLOCK-INFO (err u1003))
|
||||
(define-constant ERR-TOKEN-MISMATCH (err u1004))
|
||||
|
||||
(define-constant ONE_8 u100000000)
|
||||
(define-constant MAX_UINT u340282366920938463463374607431768211455)
|
||||
|
||||
(define-constant apower .token-apower)
|
||||
|
||||
(define-data-var contract-owner principal tx-sender)
|
||||
|
||||
(define-data-var event-nonce uint u0)
|
||||
(define-map events uint { bridged-token: principal, apower-per-bridged: uint, apower-cap: uint, start-timestamp: uint, end-timestamp: uint })
|
||||
(define-map claimed { event-id: uint, user: principal } uint)
|
||||
|
||||
;; governance functions
|
||||
|
||||
(define-public (set-contract-owner (owner principal))
|
||||
(begin
|
||||
(try! (check-is-owner))
|
||||
(ok (var-set contract-owner owner))
|
||||
)
|
||||
)
|
||||
|
||||
(define-public (create-event (bridged-token principal) (apower-per-bridged uint) (start-timestamp uint) (end-timestamp uint) (apower-cap (optional uint)))
|
||||
(let
|
||||
(
|
||||
(event-id (+ (var-get event-nonce) u1))
|
||||
)
|
||||
(try! (check-is-owner))
|
||||
(asserts! (< start-timestamp end-timestamp) ERR-INVALID-TIMESTAMP)
|
||||
(map-set events event-id { bridged-token: bridged-token, apower-per-bridged: apower-per-bridged, apower-cap: (match apower-cap value value MAX_UINT), start-timestamp: start-timestamp, end-timestamp: end-timestamp })
|
||||
(var-set event-nonce event-id)
|
||||
(ok event-id)
|
||||
)
|
||||
)
|
||||
|
||||
;; read-only functions
|
||||
|
||||
(define-read-only (block-timestamp)
|
||||
(ok (unwrap! (get-block-info? time (- block-height u1)) ERR-GET-BLOCK-INFO)))
|
||||
|
||||
(define-read-only (get-contract-owner)
|
||||
(ok (var-get contract-owner)))
|
||||
|
||||
(define-read-only (get-event-details-or-fail (event-id uint))
|
||||
(ok (unwrap! (map-get? events event-id) ERR-UNKNOWN-EVENT-ID)))
|
||||
|
||||
(define-read-only (get-claimed-or-default (event-id uint) (user principal))
|
||||
(default-to u0 (map-get? claimed { event-id: event-id, user: user })))
|
||||
|
||||
;; external functions
|
||||
|
||||
(define-public (transfer-to-wrap
|
||||
(event-id uint)
|
||||
(order
|
||||
{
|
||||
to: uint,
|
||||
token: uint,
|
||||
amount-in-fixed: uint,
|
||||
chain-id: uint,
|
||||
salt: (buff 256)
|
||||
}
|
||||
)
|
||||
(token-trait <ft-trait>)
|
||||
(signature-packs (list 100 { signer: principal, order-hash: (buff 32), signature: (buff 65)})))
|
||||
(let
|
||||
(
|
||||
(current-timestamp (try! (block-timestamp)))
|
||||
(event-details (try! (get-event-details-or-fail event-id)))
|
||||
(recipient (try! (contract-call? .bridge-endpoint-v1-02 user-from-id-or-fail (get to order))))
|
||||
(apower-claimed (get-claimed-or-default event-id recipient))
|
||||
(apower-excess (- (get apower-cap event-details) apower-claimed))
|
||||
(apower-to-mint (min apower-excess (mul-down (get amount-in-fixed order) (get apower-per-bridged event-details))))
|
||||
)
|
||||
(asserts! (is-eq (contract-of token-trait) (get bridged-token event-details)) ERR-TOKEN-MISMATCH)
|
||||
;; if timestamp invalid, instead of reverting, it skips and process wrap.
|
||||
(and
|
||||
(>= current-timestamp (get start-timestamp event-details))
|
||||
(<= current-timestamp (get end-timestamp event-details))
|
||||
(> apower-to-mint u0)
|
||||
(as-contract (try! (contract-call? apower mint-fixed apower-to-mint recipient))))
|
||||
(map-set claimed { event-id: event-id, user: recipient } (+ apower-claimed apower-to-mint))
|
||||
(contract-call? .bridge-endpoint-v1-02 transfer-to-wrap order token-trait signature-packs)
|
||||
)
|
||||
)
|
||||
|
||||
;; internal functions
|
||||
|
||||
(define-private (check-is-owner)
|
||||
(ok (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED)))
|
||||
|
||||
(define-private (mul-down (a uint) (b uint))
|
||||
(/ (* a b) ONE_8))
|
||||
|
||||
(define-private (div-down (a uint) (b uint))
|
||||
(if (is-eq a u0)
|
||||
u0
|
||||
(/ (* a ONE_8) b)
|
||||
))
|
||||
|
||||
(define-private (max (a uint) (b uint))
|
||||
(if (<= a b) b a))
|
||||
|
||||
(define-private (min (a uint) (b uint))
|
||||
(if (<= a b) a b))
|
||||
185
clarity/contracts/pool/launchpad-with-pool-lock.clar
Normal file
185
clarity/contracts/pool/launchpad-with-pool-lock.clar
Normal file
@@ -0,0 +1,185 @@
|
||||
(use-trait ft-trait .trait-sip-010.sip-010-trait)
|
||||
(use-trait sft-trait .trait-semi-fungible.semi-fungible-trait)
|
||||
|
||||
(define-constant err-unknown-launch (err u2045))
|
||||
(define-constant err-block-height-not-reached (err u2042))
|
||||
(define-constant err-not-authorized (err u1000))
|
||||
|
||||
(define-constant ONE_8 u100000000)
|
||||
|
||||
(define-data-var contract-owner principal tx-sender)
|
||||
|
||||
(define-map locked uint { launch-owner: principal, pct: uint, period: uint, pool-id: uint })
|
||||
|
||||
;; governance functions
|
||||
|
||||
(define-public (set-contract-owner (owner principal))
|
||||
(begin
|
||||
(try! (check-is-owner))
|
||||
(ok (var-set contract-owner owner))
|
||||
)
|
||||
)
|
||||
|
||||
(define-public (create-pool
|
||||
(launch-token-trait <ft-trait>)
|
||||
(payment-token-trait <ft-trait>)
|
||||
(offering
|
||||
{
|
||||
launch-owner: principal,
|
||||
launch-tokens-per-ticket: uint,
|
||||
price-per-ticket-in-fixed: uint,
|
||||
activation-threshold: uint,
|
||||
registration-start-height: uint,
|
||||
registration-end-height: uint,
|
||||
claim-end-height: uint,
|
||||
apower-per-ticket-in-fixed: (list 6 {apower-per-ticket-in-fixed: uint, tier-threshold: uint}),
|
||||
registration-max-tickets: uint,
|
||||
fee-per-ticket-in-fixed: uint
|
||||
})
|
||||
(locked-params { pct: uint, period: uint })
|
||||
)
|
||||
(let
|
||||
(
|
||||
(launch-id (try! (contract-call? .alex-launchpad-v1-3 create-pool launch-token-trait payment-token-trait (merge offering { launch-owner: (as-contract tx-sender) }))))
|
||||
)
|
||||
(try! (contract-call? .alex-vault-v1-1 set-approved-token (contract-of launch-token-trait) true))
|
||||
(try! (contract-call? .alex-vault-v1-1 set-approved-token (contract-of payment-token-trait) true))
|
||||
(map-set locked launch-id (merge locked-params { launch-owner: (get launch-owner offering), pool-id: u0 }))
|
||||
(ok launch-id)
|
||||
)
|
||||
)
|
||||
|
||||
(define-public (transfer-all-to-owner (token-trait <ft-trait>))
|
||||
(let
|
||||
(
|
||||
(balance (try! (contract-call? token-trait get-balance-fixed (as-contract tx-sender))))
|
||||
)
|
||||
(try! (check-is-owner))
|
||||
(ok (and (> balance u0) (as-contract (try! (contract-call? token-trait transfer-fixed balance tx-sender (var-get contract-owner) none)))))
|
||||
)
|
||||
)
|
||||
|
||||
(define-public (transfer-all-semi-to-owner (token-trait <sft-trait>) (token-id uint))
|
||||
(let
|
||||
(
|
||||
(balance (try! (contract-call? token-trait get-balance-fixed token-id (as-contract tx-sender))))
|
||||
)
|
||||
(try! (check-is-owner))
|
||||
(ok (and (> balance u0) (as-contract (try! (contract-call? token-trait transfer-fixed token-id balance tx-sender (var-get contract-owner))))))
|
||||
)
|
||||
)
|
||||
|
||||
;; privileged functions
|
||||
|
||||
(define-public (add-to-position (launch-id uint) (tickets uint) (launch-token-trait <ft-trait>))
|
||||
(let
|
||||
(
|
||||
(offering (unwrap! (get-launch launch-id) err-unknown-launch))
|
||||
(locked-details (unwrap! (get-locked-details launch-id) err-unknown-launch))
|
||||
)
|
||||
(asserts! (or (is-eq (get launch-owner locked-details) tx-sender) (is-ok (check-is-owner))) err-not-authorized)
|
||||
(try! (contract-call? launch-token-trait transfer-fixed (* (get launch-tokens-per-ticket offering) tickets ONE_8) tx-sender (as-contract tx-sender) none))
|
||||
(as-contract (try! (contract-call? .alex-launchpad-v1-3 add-to-position launch-id tickets launch-token-trait)))
|
||||
(contract-call? launch-token-trait transfer-fixed (mul-down (* (get launch-tokens-per-ticket offering) tickets ONE_8) (get pct locked-details)) tx-sender (as-contract tx-sender) none)
|
||||
)
|
||||
)
|
||||
|
||||
;; read-only functions
|
||||
|
||||
(define-read-only (get-launch (launch-id uint))
|
||||
(unwrap-panic (contract-call? .alex-launchpad-v1-3 get-launch launch-id))
|
||||
)
|
||||
|
||||
(define-read-only (get-locked-details (launch-id uint))
|
||||
(map-get? locked launch-id)
|
||||
)
|
||||
|
||||
(define-read-only (get-contract-owner)
|
||||
(ok (var-get contract-owner))
|
||||
)
|
||||
|
||||
;; public functions
|
||||
|
||||
(define-public (claim (launch-id uint) (input (list 200 principal)) (launch-token-trait <ft-trait>) (payment-token-trait <ft-trait>))
|
||||
(let
|
||||
(
|
||||
(offering (unwrap! (get-launch launch-id) err-unknown-launch))
|
||||
(launch-token (get launch-token offering))
|
||||
(payment-token (get payment-token offering))
|
||||
(locked-details (unwrap! (get-locked-details launch-id) err-unknown-launch))
|
||||
(launch-token-amount (* (get launch-tokens-per-ticket offering) (len input) (get pct locked-details)))
|
||||
(fee-per-ticket (mul-down (get price-per-ticket-in-fixed offering) (get fee-per-ticket-in-fixed offering)))
|
||||
(net-price-per-ticket (- (get price-per-ticket-in-fixed offering) fee-per-ticket))
|
||||
(payment-token-amount (mul-down (* net-price-per-ticket (len input)) (get pct locked-details)))
|
||||
)
|
||||
(try! (contract-call? .alex-launchpad-v1-3 claim launch-id input launch-token-trait payment-token-trait))
|
||||
(if (is-some (contract-call? .amm-swap-pool-v1-1 get-pool-exists payment-token launch-token ONE_8))
|
||||
(begin
|
||||
(as-contract (try! (contract-call? .amm-swap-pool-v1-1 add-to-position payment-token-trait launch-token-trait ONE_8 payment-token-amount (some launch-token-amount))))
|
||||
true
|
||||
)
|
||||
(let
|
||||
(
|
||||
(pool-created (as-contract (try! (contract-call? .amm-swap-pool-v1-1 create-pool payment-token-trait launch-token-trait ONE_8 tx-sender payment-token-amount launch-token-amount))))
|
||||
(pool-details (unwrap-panic (contract-call? .amm-swap-pool-v1-1 get-pool-exists payment-token launch-token ONE_8)))
|
||||
)
|
||||
|
||||
(as-contract (try! (contract-call? .amm-swap-pool-v1-1 set-fee-rate-x payment-token launch-token ONE_8 u500000)))
|
||||
(as-contract (try! (contract-call? .amm-swap-pool-v1-1 set-fee-rate-y payment-token launch-token ONE_8 u500000)))
|
||||
(as-contract (try! (contract-call? .amm-swap-pool-v1-1 set-max-in-ratio payment-token launch-token ONE_8 u3000000)))
|
||||
(as-contract (try! (contract-call? .amm-swap-pool-v1-1 set-max-out-ratio payment-token launch-token ONE_8 u3000000)))
|
||||
(as-contract (try! (contract-call? .amm-swap-pool-v1-1 set-oracle-enabled payment-token launch-token ONE_8 true)))
|
||||
(as-contract (try! (contract-call? .amm-swap-pool-v1-1 set-oracle-average payment-token launch-token ONE_8 u9900000)))
|
||||
(map-set locked launch-id (merge locked-details { pool-id: (get pool-id pool-details) }))
|
||||
)
|
||||
)
|
||||
|
||||
(ok { dx: payment-token-amount, dy: launch-token-amount })
|
||||
)
|
||||
)
|
||||
|
||||
;; redeem LP after lock period
|
||||
;; assert block-height > end of launch + locked-period
|
||||
;; redeem liquidity tokens
|
||||
;; send launch-/payment-tokens to launch-owner
|
||||
(define-public (reduce-position (launch-id uint) (launch-token-trait <ft-trait>) (payment-token-trait <ft-trait>) (percent uint))
|
||||
(let
|
||||
(
|
||||
(offering (unwrap! (get-launch launch-id) err-unknown-launch))
|
||||
(locked-details (unwrap! (get-locked-details launch-id) err-unknown-launch))
|
||||
(reduced (as-contract (try! (contract-call? .amm-swap-pool-v1-1 reduce-position payment-token-trait launch-token-trait ONE_8 percent))))
|
||||
)
|
||||
(asserts! (> block-height (+ (get registration-end-height offering) (get period locked-details))) err-block-height-not-reached)
|
||||
(as-contract (try! (contract-call? payment-token-trait transfer-fixed (get dx reduced) tx-sender (get launch-owner locked-details) none)))
|
||||
(as-contract (try! (contract-call? launch-token-trait transfer-fixed (get dy reduced) tx-sender (get launch-owner locked-details) none)))
|
||||
(ok reduced)
|
||||
)
|
||||
)
|
||||
|
||||
;; private functions
|
||||
|
||||
(define-private (check-is-owner)
|
||||
(ok (asserts! (is-eq tx-sender (var-get contract-owner)) err-not-authorized))
|
||||
)
|
||||
|
||||
;; @desc mul-down
|
||||
;; @params a
|
||||
;; @params b
|
||||
;; @returns uint
|
||||
(define-private (mul-down (a uint) (b uint))
|
||||
(/ (* a b) ONE_8)
|
||||
)
|
||||
|
||||
;; @desc div-down
|
||||
;; @params a
|
||||
;; @params b
|
||||
;; @returns uint
|
||||
(define-private (div-down (a uint) (b uint))
|
||||
(if (is-eq a u0)
|
||||
u0
|
||||
(/ (* a ONE_8) b)
|
||||
)
|
||||
)
|
||||
|
||||
;; contract initialisation
|
||||
;; (set-contract-owner .executor-dao)
|
||||
228
clarity/contracts/token/brc20-chax.clar
Normal file
228
clarity/contracts/token/brc20-chax.clar
Normal file
@@ -0,0 +1,228 @@
|
||||
(define-constant ERR-NOT-AUTHORIZED (err u1000))
|
||||
|
||||
(define-constant ONE_8 u100000000)
|
||||
|
||||
;; -- token implementation
|
||||
|
||||
(define-fungible-token brc20-chax)
|
||||
|
||||
(define-data-var contract-owner principal tx-sender)
|
||||
(define-data-var token-name (string-ascii 32) "CHAX (BRC20)")
|
||||
(define-data-var token-symbol (string-ascii 32) "CHAX")
|
||||
(define-data-var token-uri (optional (string-utf8 256)) (some u"https://cdn.alexlab.co/metadata/brc20-chax.json"))
|
||||
|
||||
(define-data-var token-decimals uint u8)
|
||||
|
||||
(define-map approved-contracts principal bool)
|
||||
|
||||
;; read-only calls
|
||||
|
||||
(define-read-only (get-contract-owner)
|
||||
(ok (var-get contract-owner))
|
||||
)
|
||||
|
||||
(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 (var-get token-decimals))
|
||||
)
|
||||
|
||||
(define-read-only (get-balance (who principal))
|
||||
(ok (ft-get-balance brc20-chax who))
|
||||
)
|
||||
|
||||
(define-read-only (get-total-supply)
|
||||
(ok (ft-get-supply brc20-chax))
|
||||
)
|
||||
|
||||
(define-read-only (get-token-uri)
|
||||
(ok (var-get token-uri))
|
||||
)
|
||||
|
||||
;; @desc fixed-to-decimals
|
||||
;; @params amount
|
||||
;; @returns uint
|
||||
(define-read-only (fixed-to-decimals (amount uint))
|
||||
(/ (* amount (pow-decimals)) ONE_8)
|
||||
)
|
||||
|
||||
;; @desc get-total-supply-fixed
|
||||
;; @params token-id
|
||||
;; @returns (response uint)
|
||||
(define-read-only (get-total-supply-fixed)
|
||||
(ok (decimals-to-fixed (unwrap-panic (get-total-supply))))
|
||||
)
|
||||
|
||||
;; @desc get-balance-fixed
|
||||
;; @params token-id
|
||||
;; @params who
|
||||
;; @returns (response uint)
|
||||
(define-read-only (get-balance-fixed (account principal))
|
||||
(ok (decimals-to-fixed (unwrap-panic (get-balance account))))
|
||||
)
|
||||
|
||||
;; governance calls
|
||||
|
||||
(define-public (set-contract-owner (owner principal))
|
||||
(begin
|
||||
(try! (check-is-owner))
|
||||
(ok (var-set contract-owner owner))
|
||||
)
|
||||
)
|
||||
|
||||
(define-public (set-name (new-name (string-ascii 32)))
|
||||
(begin
|
||||
(try! (check-is-owner))
|
||||
(ok (var-set token-name new-name))
|
||||
)
|
||||
)
|
||||
|
||||
(define-public (set-symbol (new-symbol (string-ascii 10)))
|
||||
(begin
|
||||
(try! (check-is-owner))
|
||||
(ok (var-set token-symbol new-symbol))
|
||||
)
|
||||
)
|
||||
|
||||
(define-public (set-decimals (new-decimals uint))
|
||||
(begin
|
||||
(try! (check-is-owner))
|
||||
(ok (var-set token-decimals new-decimals))
|
||||
)
|
||||
)
|
||||
|
||||
(define-public (set-token-uri (new-uri (optional (string-utf8 256))))
|
||||
(begin
|
||||
(try! (check-is-owner))
|
||||
(ok (var-set token-uri new-uri))
|
||||
)
|
||||
)
|
||||
|
||||
(define-public (add-approved-contract (new-approved-contract principal))
|
||||
(begin
|
||||
(try! (check-is-owner))
|
||||
(ok (map-set approved-contracts new-approved-contract true))
|
||||
)
|
||||
)
|
||||
|
||||
(define-public (set-approved-contract (owner principal) (approved bool))
|
||||
(begin
|
||||
(try! (check-is-owner))
|
||||
(ok (map-set approved-contracts owner approved))
|
||||
)
|
||||
)
|
||||
|
||||
;; priviliged calls
|
||||
|
||||
;; @desc mint
|
||||
;; @restricted ContractOwner/Approved Contract
|
||||
;; @params token-id
|
||||
;; @params amount
|
||||
;; @params recipient
|
||||
;; @returns (response bool)
|
||||
(define-public (mint (amount uint) (recipient principal))
|
||||
(begin
|
||||
(asserts! (or (is-ok (check-is-approved)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED)
|
||||
(ft-mint? brc20-chax amount recipient)
|
||||
)
|
||||
)
|
||||
|
||||
;; @desc burn
|
||||
;; @restricted ContractOwner/Approved Contract
|
||||
;; @params token-id
|
||||
;; @params amount
|
||||
;; @params sender
|
||||
;; @returns (response bool)
|
||||
(define-public (burn (amount uint) (sender principal))
|
||||
(begin
|
||||
(asserts! (or (is-ok (check-is-approved)) (is-ok (check-is-owner))) ERR-NOT-AUTHORIZED)
|
||||
(ft-burn? brc20-chax amount sender)
|
||||
)
|
||||
)
|
||||
|
||||
;; @desc mint-fixed
|
||||
;; @params token-id
|
||||
;; @params amount
|
||||
;; @params recipient
|
||||
;; @returns (response bool)
|
||||
(define-public (mint-fixed (amount uint) (recipient principal))
|
||||
(mint (fixed-to-decimals amount) recipient)
|
||||
)
|
||||
|
||||
;; @desc burn-fixed
|
||||
;; @params token-id
|
||||
;; @params amount
|
||||
;; @params sender
|
||||
;; @returns (response bool)
|
||||
(define-public (burn-fixed (amount uint) (sender principal))
|
||||
(burn (fixed-to-decimals amount) sender)
|
||||
)
|
||||
|
||||
(define-public (mint-fixed-many (recipients (list 200 { amount: uint, to: principal})))
|
||||
(fold mint-many-iter recipients (ok true))
|
||||
)
|
||||
|
||||
;; public calls
|
||||
|
||||
(define-public (transfer (amount uint) (sender principal) (recipient principal) (memo (optional (buff 34))))
|
||||
(begin
|
||||
(asserts! (is-eq sender tx-sender) ERR-NOT-AUTHORIZED)
|
||||
(try! (ft-transfer? brc20-chax amount sender recipient))
|
||||
(match memo to-print (print to-print) 0x)
|
||||
(ok true)
|
||||
)
|
||||
)
|
||||
|
||||
;; @desc transfer-fixed
|
||||
;; @params token-id
|
||||
;; @params amount
|
||||
;; @params sender
|
||||
;; @params recipient
|
||||
;; @returns (response bool)
|
||||
(define-public (transfer-fixed (amount uint) (sender principal) (recipient principal) (memo (optional (buff 34))))
|
||||
(transfer (fixed-to-decimals amount) sender recipient memo)
|
||||
)
|
||||
|
||||
(define-public (transfer-fixed-many (recipients (list 200 { amount: uint, to: principal})))
|
||||
(fold transfer-many-iter recipients (ok true))
|
||||
)
|
||||
|
||||
;; private calls
|
||||
|
||||
(define-private (check-is-owner)
|
||||
(ok (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED))
|
||||
)
|
||||
|
||||
(define-private (check-is-approved)
|
||||
(ok (asserts! (default-to false (map-get? approved-contracts tx-sender)) ERR-NOT-AUTHORIZED))
|
||||
)
|
||||
|
||||
;; @desc decimals-to-fixed
|
||||
;; @params amount
|
||||
;; @returns uint
|
||||
(define-private (decimals-to-fixed (amount uint))
|
||||
(/ (* amount ONE_8) (pow-decimals))
|
||||
)
|
||||
|
||||
;; @desc pow-decimals
|
||||
;; @returns uint
|
||||
(define-private (pow-decimals)
|
||||
(pow u10 (unwrap-panic (get-decimals)))
|
||||
)
|
||||
|
||||
(define-private (mint-many-iter (recipient { amount: uint, to: principal }) (previous-response (response bool uint)))
|
||||
(match previous-response prev-ok (mint-fixed (get amount recipient) (get to recipient)) prev-err previous-response)
|
||||
)
|
||||
|
||||
(define-private (transfer-many-iter (recipient { amount: uint, to: principal }) (previous-response (response bool uint)))
|
||||
(match previous-response prev-ok (transfer-fixed (get amount recipient) tx-sender (get to recipient) none) prev-err previous-response)
|
||||
)
|
||||
|
||||
;; contract initialisation
|
||||
;; (set-contract-owner .executor-dao)
|
||||
383
clarity/tests/launchpad-with-pool-lock.ts
Normal file
383
clarity/tests/launchpad-with-pool-lock.ts
Normal file
@@ -0,0 +1,383 @@
|
||||
import {
|
||||
ONE_8,
|
||||
Clarinet,
|
||||
Tx,
|
||||
types,
|
||||
assertEquals,
|
||||
prepareStandardTest,
|
||||
contractPrincipal,
|
||||
extractBounds,
|
||||
extractParameters,
|
||||
determineApower,
|
||||
} from "./models/launchpad-with-pool-lock.ts";
|
||||
import type {
|
||||
Chain,
|
||||
Account,
|
||||
StandardTestParameters,
|
||||
} from "./models/launchpad-with-pool-lock.ts";
|
||||
import {
|
||||
determineWinners,
|
||||
determineLosers,
|
||||
IdoParameters,
|
||||
IdoParticipant,
|
||||
} from "../scripts/launchpad.ts";
|
||||
|
||||
const parameters = {
|
||||
totalIdoTokens: 40000,
|
||||
idoOwner: undefined,
|
||||
ticketsForSale: 801,
|
||||
idoTokensPerTicket: 50,
|
||||
pricePerTicketInFixed: 5000000000,
|
||||
activationThreshold: 1,
|
||||
ticketRecipients: undefined,
|
||||
registrationStartHeight: 20,
|
||||
registrationEndHeight: 30,
|
||||
claimEndHeight: 40,
|
||||
apowerPerTicketInFixed: [
|
||||
{ tierThreshold: 5, apowerPerTicketInFixed: 10 * ONE_8 },
|
||||
{ tierThreshold: 10, apowerPerTicketInFixed: 50 * ONE_8 },
|
||||
{ tierThreshold: 20, apowerPerTicketInFixed: 100 * ONE_8 },
|
||||
{ tierThreshold: 30, apowerPerTicketInFixed: 150 * ONE_8 },
|
||||
{ tierThreshold: 40, apowerPerTicketInFixed: 200 * ONE_8 },
|
||||
{ tierThreshold: 50, apowerPerTicketInFixed: 250 * ONE_8 },
|
||||
],
|
||||
registrationMaxTickets: 999999999999,
|
||||
feePerTicketInFixed: 0,
|
||||
lockPct: 0.1e8,
|
||||
lockPeriod: 20
|
||||
};
|
||||
|
||||
Clarinet.test({
|
||||
name: "launchpad-with-pool-lock : example claim walk test",
|
||||
async fn(chain: Chain, accounts: Map<string, Account>) {
|
||||
const [
|
||||
deployer,
|
||||
accountA,
|
||||
accountB,
|
||||
accountC,
|
||||
accountD,
|
||||
accountE,
|
||||
accountF,
|
||||
accountG,
|
||||
] = [
|
||||
"deployer",
|
||||
"wallet_1",
|
||||
"wallet_2",
|
||||
"wallet_3",
|
||||
"wallet_4",
|
||||
"wallet_5",
|
||||
"wallet_6",
|
||||
"wallet_7",
|
||||
].map((wallet) => accounts.get(wallet)!);
|
||||
|
||||
let winners_list: number[] = [];
|
||||
|
||||
const approved_operator = accountC;
|
||||
const third = chain.mineBlock([
|
||||
Tx.contractCall(
|
||||
"alex-launchpad-v1-3",
|
||||
"add-approved-operator",
|
||||
[types.principal(approved_operator.address)],
|
||||
deployer.address
|
||||
),
|
||||
]);
|
||||
|
||||
const registrationStartHeight = 20;
|
||||
const registrationEndHeight = registrationStartHeight + 10;
|
||||
const claimEndHeight = registrationEndHeight + 100;
|
||||
|
||||
const ticketRecipients = [
|
||||
{ recipient: accountA, amount: 10 },
|
||||
{ recipient: accountB, amount: 4000 },
|
||||
{ recipient: accountC, amount: 2000 },
|
||||
{ recipient: accountD, amount: 50000 },
|
||||
{ recipient: accountE, amount: 1010 },
|
||||
{ recipient: accountF, amount: 100000 },
|
||||
{ recipient: accountG, amount: 10 },
|
||||
];
|
||||
|
||||
const params: StandardTestParameters = {
|
||||
...parameters,
|
||||
totalIdoTokens: 50000,
|
||||
idoOwner: accountA,
|
||||
ticketsForSale: 500,
|
||||
idoTokensPerTicket: 100,
|
||||
pricePerTicketInFixed: 33e8,
|
||||
activationThreshold: 500,
|
||||
ticketRecipients: ticketRecipients,
|
||||
registrationStartHeight: registrationStartHeight,
|
||||
registrationEndHeight: registrationEndHeight,
|
||||
claimEndHeight: claimEndHeight,
|
||||
};
|
||||
|
||||
const preparation = prepareStandardTest(chain, params, deployer);
|
||||
preparation.blocks.map((block) =>
|
||||
block.receipts.map(({ result }) => result.expectOk())
|
||||
);
|
||||
|
||||
const { idoId } = preparation;
|
||||
|
||||
chain.mineEmptyBlockUntil(registrationStartHeight);
|
||||
const registrations = chain.mineBlock(
|
||||
ticketRecipients.map((entry) =>
|
||||
Tx.contractCall(
|
||||
"alex-launchpad-v1-3",
|
||||
"register",
|
||||
[
|
||||
types.uint(idoId),
|
||||
types.uint(entry.amount),
|
||||
types.principal(contractPrincipal(deployer, "token-wstx")),
|
||||
],
|
||||
(entry.recipient as Account).address ||
|
||||
(entry.recipient as unknown as string)
|
||||
)
|
||||
)
|
||||
);
|
||||
// console.log(registrations.receipts);
|
||||
registrations.receipts.map(({ result }) => result.expectOk());
|
||||
assertEquals(registrations.receipts.length, ticketRecipients.length);
|
||||
for (let i = 0; i < ticketRecipients.length; i++) {
|
||||
registrations.receipts[i].events.expectSTXTransferEvent(
|
||||
((ticketRecipients[i]["amount"] *
|
||||
params["pricePerTicketInFixed"]) /
|
||||
ONE_8) *
|
||||
1e6,
|
||||
ticketRecipients[i]["recipient"].address,
|
||||
deployer.address + ".alex-launchpad-v1-3"
|
||||
);
|
||||
|
||||
registrations.receipts[i].events.expectFungibleTokenBurnEvent(
|
||||
determineApower(
|
||||
ticketRecipients[i]["amount"],
|
||||
params["apowerPerTicketInFixed"]
|
||||
),
|
||||
ticketRecipients[i]["recipient"].address,
|
||||
"apower"
|
||||
);
|
||||
}
|
||||
|
||||
const bounds = registrations.receipts.map((receipt) =>
|
||||
extractBounds(receipt.result)
|
||||
);
|
||||
|
||||
chain.mineEmptyBlockUntil(registrationEndHeight + 2);
|
||||
|
||||
const parametersFromChain = chain.callReadOnlyFn(
|
||||
"alex-launchpad-v1-3",
|
||||
"get-offering-walk-parameters",
|
||||
[types.uint(idoId)],
|
||||
deployer.address
|
||||
);
|
||||
|
||||
const idoParameters: IdoParameters = extractParameters(
|
||||
parametersFromChain.result
|
||||
);
|
||||
|
||||
const idoParticipants: IdoParticipant[] = ticketRecipients.map(
|
||||
(entry, index) => ({
|
||||
participant: entry.recipient.address,
|
||||
...bounds[index],
|
||||
})
|
||||
);
|
||||
|
||||
// console.log(idoParameters);
|
||||
// console.log(idoParticipants);
|
||||
|
||||
const dx_expected = (params.pricePerTicketInFixed - params.feePerTicketInFixed) * params.ticketsForSale * params.lockPct / ONE_8;
|
||||
const dy_expected = params.idoTokensPerTicket * params.ticketsForSale * params.lockPct;
|
||||
|
||||
let dx_actual = 0;
|
||||
let dy_actual = 0;
|
||||
// console.log("determining winners...");
|
||||
const winners = determineWinners(idoParameters, idoParticipants);
|
||||
// console.log(winners);
|
||||
let maxChunkSize = 200;
|
||||
for (
|
||||
let index = 0;
|
||||
index < winners.winners.length;
|
||||
index += maxChunkSize
|
||||
) {
|
||||
let winners_sliced = winners.winners.slice(index, index + maxChunkSize);
|
||||
// console.log(winners_sliced[0], winners_sliced[winners_sliced.length - 1]);
|
||||
const claim = chain.mineBlock([
|
||||
Tx.contractCall(
|
||||
"launchpad-with-pool-lock",
|
||||
"claim",
|
||||
[
|
||||
types.uint(idoId),
|
||||
types.list(winners_sliced.map(types.principal)),
|
||||
types.principal(contractPrincipal(deployer, "token-wban")),
|
||||
types.principal(contractPrincipal(deployer, "token-wstx")),
|
||||
],
|
||||
accountC.address
|
||||
),
|
||||
]);
|
||||
winners_list.push(winners.winners.length);
|
||||
let events = claim.receipts[0].events;
|
||||
const liquidity = claim.receipts[0].result.expectOk().expectTuple();
|
||||
const dx = parseInt(liquidity.dx.substring(1));
|
||||
const dy = parseInt(liquidity.dy.substring(1));
|
||||
dx_actual += dx;
|
||||
dy_actual += dy;
|
||||
console.log(`${index}, dx = ${dx_actual} vs ${dx_expected}, dy = ${dy_actual} vs. ${dy_expected}`);
|
||||
|
||||
// assertEquals(events.length, 1 + winners_sliced.length);
|
||||
events.expectSTXTransferEvent(
|
||||
((params["pricePerTicketInFixed"] * winners_sliced.length) /
|
||||
ONE_8) *
|
||||
1e6,
|
||||
deployer.address + ".alex-launchpad-v1-3",
|
||||
deployer.address + ".launchpad-with-pool-lock"
|
||||
);
|
||||
events.expectSTXTransferEvent(
|
||||
dx / ONE_8 * 1e6,
|
||||
deployer.address + '.launchpad-with-pool-lock',
|
||||
deployer.address + '.alex-vault-v1-1'
|
||||
);
|
||||
events.expectFungibleTokenTransferEvent(
|
||||
dy / ONE_8 * 1e6,
|
||||
deployer.address + '.launchpad-with-pool-lock',
|
||||
deployer.address + '.alex-vault-v1-1',
|
||||
"banana"
|
||||
)
|
||||
for (let j = 1; j < 1 + winners_sliced.length; j++) {
|
||||
events.expectFungibleTokenTransferEvent(
|
||||
params["idoTokensPerTicket"] * 1e6,
|
||||
deployer.address + ".alex-launchpad-v1-3",
|
||||
winners_sliced[j - 1],
|
||||
"banana"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(dx_actual, dx_expected);
|
||||
assertEquals(dy_actual, dy_expected);
|
||||
|
||||
chain.mineEmptyBlockUntil(registrationEndHeight + 19);
|
||||
const reduceFail = chain.mineBlock([
|
||||
Tx.contractCall(
|
||||
"launchpad-with-pool-lock",
|
||||
"reduce-position",
|
||||
[
|
||||
types.uint(idoId),
|
||||
types.principal(contractPrincipal(deployer, "token-wban")),
|
||||
types.principal(contractPrincipal(deployer, "token-wstx")),
|
||||
types.uint(ONE_8)
|
||||
],
|
||||
accountC.address
|
||||
),
|
||||
]);
|
||||
reduceFail.receipts[0].result.expectErr().expectUint(2042);
|
||||
|
||||
chain.mineEmptyBlockUntil(claimEndHeight);
|
||||
// console.log("determining losers...");
|
||||
const losers = determineLosers(idoParameters, idoParticipants);
|
||||
let losers_list = losers.losers.map((e) => {
|
||||
return e.recipient;
|
||||
});
|
||||
|
||||
for (let index = 0; index < idoParticipants.length; index++) {
|
||||
let participant = idoParticipants[index]["participant"];
|
||||
let won =
|
||||
winners.winners.indexOf(participant) == -1
|
||||
? 0
|
||||
: winners.winners.lastIndexOf(participant) -
|
||||
winners.winners.indexOf(participant) +
|
||||
1;
|
||||
let lost =
|
||||
losers_list.indexOf(participant) == -1
|
||||
? 0
|
||||
: losers.losers[losers_list.indexOf(participant)]["amount"];
|
||||
console.log(
|
||||
participant,
|
||||
"registered:",
|
||||
won + lost,
|
||||
"won:",
|
||||
won,
|
||||
"lost:",
|
||||
lost
|
||||
);
|
||||
assertEquals(ticketRecipients[index]["amount"], won + lost);
|
||||
}
|
||||
|
||||
maxChunkSize = 5;
|
||||
for (let index = 0; index < losers.losers.length; index += maxChunkSize) {
|
||||
let losers_sliced = losers.losers.slice(index, index + maxChunkSize);
|
||||
// console.log(losers_sliced);
|
||||
const claim = chain.mineBlock([
|
||||
Tx.contractCall(
|
||||
"alex-launchpad-v1-3",
|
||||
"refund",
|
||||
[
|
||||
types.uint(idoId),
|
||||
types.list(
|
||||
losers_sliced.map((e) => {
|
||||
return types.tuple({
|
||||
recipient: types.principal(e.recipient),
|
||||
amount: types.uint(
|
||||
e.amount * params["pricePerTicketInFixed"]
|
||||
),
|
||||
});
|
||||
})
|
||||
),
|
||||
types.principal(contractPrincipal(deployer, "token-wstx")),
|
||||
],
|
||||
accountC.address
|
||||
),
|
||||
]);
|
||||
|
||||
let events = claim.receipts[0].events;
|
||||
// console.log(index, claim.receipts[0].result);
|
||||
assertEquals(events.length, losers_sliced.length);
|
||||
|
||||
for (let j = 0; j < events.length; j++) {
|
||||
events.expectSTXTransferEvent(
|
||||
((losers_sliced[j]["amount"] *
|
||||
params["pricePerTicketInFixed"]) /
|
||||
ONE_8) *
|
||||
1e6,
|
||||
deployer.address + ".alex-launchpad-v1-3",
|
||||
losers_sliced[j]["recipient"]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const reduceOk = chain.mineBlock([
|
||||
Tx.contractCall(
|
||||
"launchpad-with-pool-lock",
|
||||
"reduce-position",
|
||||
[
|
||||
types.uint(idoId),
|
||||
types.principal(contractPrincipal(deployer, "token-wban")),
|
||||
types.principal(contractPrincipal(deployer, "token-wstx")),
|
||||
types.uint(ONE_8)
|
||||
],
|
||||
accountC.address
|
||||
),
|
||||
]);
|
||||
reduceOk.receipts[0].result.expectOk();
|
||||
const events = reduceOk.receipts[0].events;
|
||||
events.expectSTXTransferEvent(
|
||||
dx_expected / ONE_8 * 1e6,
|
||||
deployer.address + '.alex-vault-v1-1',
|
||||
deployer.address + '.launchpad-with-pool-lock'
|
||||
)
|
||||
events.expectSTXTransferEvent(
|
||||
dx_expected / ONE_8 * 1e6,
|
||||
deployer.address + '.launchpad-with-pool-lock',
|
||||
accountA.address
|
||||
)
|
||||
events.expectFungibleTokenTransferEvent(
|
||||
dy_expected / ONE_8 * 1e6,
|
||||
deployer.address + '.alex-vault-v1-1',
|
||||
deployer.address + '.launchpad-with-pool-lock',
|
||||
"banana"
|
||||
)
|
||||
events.expectFungibleTokenTransferEvent(
|
||||
dy_expected / ONE_8 * 1e6,
|
||||
deployer.address + '.launchpad-with-pool-lock',
|
||||
accountA.address,
|
||||
"banana"
|
||||
)
|
||||
},
|
||||
});
|
||||
107
clarity/tests/models/launchpad-with-pool-lock.ts
Normal file
107
clarity/tests/models/launchpad-with-pool-lock.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { Clarinet, Tx, types } from 'https://deno.land/x/clarinet@v0.34.0/index.ts';
|
||||
import type { Chain, Account } from 'https://deno.land/x/clarinet@v0.34.0/index.ts';
|
||||
import { assertEquals } from 'https://deno.land/std@0.166.0/testing/asserts.ts';
|
||||
|
||||
export { Clarinet, Tx, types, assertEquals };
|
||||
export type { Chain, Account };
|
||||
|
||||
export const ONE_8 = 100000000;
|
||||
|
||||
export type TicketAllocation = { recipient: Account | string, amount: number };
|
||||
export type StandardTestParameters = {
|
||||
totalIdoTokens: number,
|
||||
idoOwner: Account,
|
||||
ticketsForSale: number,
|
||||
idoTokensPerTicket: number,
|
||||
pricePerTicketInFixed: number,
|
||||
activationThreshold?: number,
|
||||
registrationStartHeight?: number,
|
||||
registrationEndHeight?: number,
|
||||
claimEndHeight?: number,
|
||||
ticketRecipients: TicketAllocation[],
|
||||
apowerPerTicketInFixed: any[],
|
||||
registrationMaxTickets: number,
|
||||
feePerTicketInFixed: number,
|
||||
lockPct: number,
|
||||
lockPeriod: number
|
||||
};
|
||||
|
||||
export const contractPrincipal = (address: Account | string, contractName: string) => `${(address as Account).address || address}.${contractName}`;
|
||||
|
||||
export function determineApower(tickets: number, apowerPerTicketInFixed: any[]){
|
||||
let apower = 0;
|
||||
let remaining_tickets = tickets;
|
||||
for(let i = 0; i < apowerPerTicketInFixed.length; i++){
|
||||
let tickets_to_process = (remaining_tickets < apowerPerTicketInFixed[i]['tierThreshold'] || i == apowerPerTicketInFixed.length - 1) ? remaining_tickets : apowerPerTicketInFixed[i]['tierThreshold'];
|
||||
remaining_tickets -= tickets_to_process;
|
||||
apower += apowerPerTicketInFixed[i]['apowerPerTicketInFixed'] * tickets_to_process;
|
||||
}
|
||||
return apower;
|
||||
}
|
||||
|
||||
export function prepareStandardTest(chain: Chain, parameters: StandardTestParameters, deployer: Account) {
|
||||
const {
|
||||
totalIdoTokens,
|
||||
idoOwner,
|
||||
ticketsForSale,
|
||||
idoTokensPerTicket,
|
||||
pricePerTicketInFixed,
|
||||
activationThreshold,
|
||||
registrationStartHeight,
|
||||
registrationEndHeight,
|
||||
claimEndHeight,
|
||||
ticketRecipients,
|
||||
apowerPerTicketInFixed,
|
||||
registrationMaxTickets,
|
||||
feePerTicketInFixed,
|
||||
lockPct,
|
||||
lockPeriod
|
||||
} = parameters;
|
||||
const first = chain.mineBlock([
|
||||
Tx.contractCall("alex-vault-v1-1", "set-approved-contract", [types.principal(deployer.address + '.amm-swap-pool-v1-1'), types.bool(true)], deployer.address),
|
||||
Tx.contractCall("token-banana", "mint-fixed", [types.uint(totalIdoTokens * ticketsForSale * ONE_8), types.principal(idoOwner.address)], deployer.address),
|
||||
Tx.contractCall("token-apower", "add-approved-contract", [types.principal(contractPrincipal(deployer, "alex-launchpad-v1-3"))], deployer.address),
|
||||
...ticketRecipients.map(allocation => Tx.contractCall("token-apower", "mint-fixed", [types.uint(determineApower(allocation.amount, apowerPerTicketInFixed)), types.principal((allocation.recipient as Account).address || allocation.recipient as string)], deployer.address)),
|
||||
Tx.contractCall("launchpad-with-pool-lock", "create-pool", [
|
||||
types.principal(contractPrincipal(deployer, "token-wban")),
|
||||
types.principal(contractPrincipal(deployer, "token-wstx")),
|
||||
types.tuple({
|
||||
"launch-owner": types.principal(idoOwner.address),
|
||||
"launch-tokens-per-ticket": types.uint(idoTokensPerTicket),
|
||||
"price-per-ticket-in-fixed": types.uint(pricePerTicketInFixed),
|
||||
"activation-threshold": types.uint(activationThreshold || 1),
|
||||
"registration-start-height": types.uint(registrationStartHeight || 0),
|
||||
"registration-end-height": types.uint(registrationEndHeight || 10),
|
||||
"claim-end-height": types.uint(claimEndHeight || 20),
|
||||
"apower-per-ticket-in-fixed": types.list(apowerPerTicketInFixed.map(e => { return types.tuple( { 'apower-per-ticket-in-fixed': types.uint(e.apowerPerTicketInFixed), 'tier-threshold': types.uint(e.tierThreshold) } ) })),
|
||||
"registration-max-tickets": types.uint(registrationMaxTickets),
|
||||
"fee-per-ticket-in-fixed": types.uint(feePerTicketInFixed)
|
||||
}),
|
||||
types.tuple({ pct: types.uint(lockPct), period: types.uint(lockPeriod) })
|
||||
], deployer.address),
|
||||
]);
|
||||
const idoId = parseInt(first.receipts[first.receipts.length - 1].result.expectOk().toString().substring(1));
|
||||
assertEquals(isNaN(idoId), false, "failed to get Launch ID");
|
||||
const second = chain.mineBlock([
|
||||
Tx.contractCall("launchpad-with-pool-lock", "add-to-position", [types.uint(idoId), types.uint(ticketsForSale), types.principal(contractPrincipal(deployer, "token-wban"))], idoOwner.address),
|
||||
]);
|
||||
return { idoId, blocks: [first, second] };
|
||||
}
|
||||
|
||||
export function extractBounds(registrationResponse: string) {
|
||||
const tuple = registrationResponse.expectOk().expectTuple() as any;
|
||||
return {
|
||||
start: parseInt(tuple.start.substring(1)),
|
||||
end: parseInt(tuple.end.substring(1))
|
||||
};
|
||||
}
|
||||
|
||||
export function extractParameters(parametersResponse: string) {
|
||||
const tuple = parametersResponse.expectOk().expectTuple() as any;
|
||||
return {
|
||||
maxStepSize: parseInt(tuple["max-step-size"].substring(1)),
|
||||
walkPosition: parseInt(tuple["walk-position"].substring(1)),
|
||||
ticketsForSale: parseInt(tuple["total-tickets"].substring(1)),
|
||||
activationThreshold: parseInt(tuple["activation-threshold"].substring(1))
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user