Skip to content

Commit

Permalink
updated contract and traits in line with developments here stacksgov/…
Browse files Browse the repository at this point in the history
…sips#52 and 51
  • Loading branch information
radicleart committed Nov 28, 2021
1 parent 871e358 commit a3a770a
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 32 deletions.
8 changes: 4 additions & 4 deletions Clarinet.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "clarinet-clarity-market"
requirements = ["nft-trait.clar", "nft-tradable-trait.clar"]
requirements = ["nft-trait.clar", "nft-operable-trait.clar"]
costs_version = 1
[contracts.appmap]
path = "contracts/appmap.clar"
Expand All @@ -10,10 +10,10 @@ depends_on = []
path = "contracts/nft-trait.clar"
depends_on = []

[contracts.nft-tradable-trait]
path = "contracts/nft-tradable-trait.clar"
[contracts.nft-operable-trait]
path = "contracts/nft-operable-trait.clar"
depends_on = []

[contracts.loopbomb]
path = "contracts/loopbomb.clar"
depends_on = ["nft-trait", "nft-tradable-trait"]
depends_on = ["nft-trait", "nft-operable-trait"]
63 changes: 43 additions & 20 deletions contracts/loopbomb.clar
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
;; Interface definitions
;; (impl-trait .nft-trait.nft-trait)
;; (impl-trait .nft-approvable-trait.nft-approvable-trait)
(impl-trait .nft-trait.nft-trait)
(impl-trait .nft-operable-trait.nft-operable-trait)

;; (impl-trait 'ST1ESYCGJB5Z5NBHS39XPC70PGC14WAQK5XXNQYDW.nft-approvable-trait.nft-approvable-trait)
;; (impl-trait 'SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait.nft-trait)
Expand Down Expand Up @@ -92,12 +92,12 @@
(maxEditions (unwrap! (get max-editions (map-get? nft-data {nft-index: nftIndex})) not-allowed))
(seriesOriginal (unwrap! (get series-original (map-get? nft-data {nft-index: nftIndex})) not-allowed))
)
(asserts! (is-approved nftIndex (unwrap! (nft-get-owner? loopbomb nftIndex) not-allowed)) not-allowed)
(asserts! (unwrap! (is-approved nftIndex (unwrap! (nft-get-owner? loopbomb nftIndex) not-allowed)) not-allowed) not-allowed)
(ok (map-set nft-data {nft-index: nftIndex} {asset-hash: ahash, meta-data-url: newMetaDataUrl, max-editions: maxEditions, edition: edition, edition-cost: editionCost, series-original: seriesOriginal}))
)
)

;; from nft-trait: Gets the owner of the 'SPecified token ID.
;; from nft-trait: Gets the owner of the 'Specified token ID.
(define-read-only (get-owner (nftIndex uint))
(ok (nft-get-owner? loopbomb nftIndex))
)
Expand Down Expand Up @@ -164,7 +164,7 @@

;; Transfers tokens to a 'SPecified principal.
(define-public (transfer (nftIndex uint) (owner principal) (recipient principal))
(if (is-approved nftIndex (unwrap! (nft-get-owner? loopbomb nftIndex) nft-not-owned-err))
(if (unwrap! (is-approved nftIndex owner) nft-not-owned-err)
(match (nft-transfer? loopbomb nftIndex owner recipient)
success (begin (ok success))
error (nft-transfer-err error))
Expand All @@ -173,7 +173,7 @@

;; Burns tokens
(define-public (burn (nftIndex uint) (owner principal))
(if (is-approved nftIndex (unwrap! (nft-get-owner? loopbomb nftIndex) nft-not-owned-err))
(if (unwrap! (is-approved nftIndex owner) nft-not-owned-err)
(match (nft-burn? loopbomb nftIndex owner)
success (begin
(ok success)
Expand All @@ -192,17 +192,35 @@
(err code)))))

;; see nft-approvable-trait
(define-public (set-approved (operator principal) (token-id uint) (approved bool))
(ok (map-set approvals {owner: tx-sender, operator: operator, nft-index: token-id} approved))
(define-public (set-approved (nftIndex uint) (operator principal) (approved bool))
(let
(
(owner (unwrap! (nft-get-owner? loopbomb nftIndex) not-allowed))
)
(begin
(if (is-eq owner contract-caller)
(ok (map-set approvals {owner: owner, operator: operator, nft-index: nftIndex} approved))
not-allowed
)
)
)
)

(define-read-only (is-approved (nftIndex uint) (owner principal))
(or
(is-eq owner tx-sender)
(is-eq owner contract-caller)
(default-to false (map-get? approvals {owner: owner, operator: tx-sender, nft-index: nftIndex}))
(default-to false (map-get? approvals {owner: owner, operator: contract-caller, nft-index: nftIndex}))
)
(define-read-only (is-approved (nftIndex uint) (address principal))
(let
(
(owner (unwrap! (nft-get-owner? loopbomb nftIndex) not-allowed))
)
(begin
(if (or
(is-eq owner tx-sender)
(is-eq owner contract-caller)
(default-to false (map-get? approvals {owner: owner, operator: tx-sender, nft-index: nftIndex}))
(default-to false (map-get? approvals {owner: owner, operator: contract-caller, nft-index: nftIndex}))
) (ok true) nft-not-owned-err
)
)
)
)

;; public methods
Expand Down Expand Up @@ -485,12 +503,13 @@
(define-public (set-edition-cost (nftIndex uint) (maxEditions uint) (editionCost uint))
(let
(
(owner (unwrap! (nft-get-owner? loopbomb nftIndex) not-allowed))
(ahash (unwrap! (get asset-hash (map-get? nft-data {nft-index: nftIndex})) not-allowed))
(metaDataUrl (unwrap! (get meta-data-url (map-get? nft-data {nft-index: nftIndex})) not-allowed))
(edition (unwrap! (get edition (map-get? nft-data {nft-index: nftIndex})) not-allowed))
(seriesOriginal (unwrap! (get series-original (map-get? nft-data {nft-index: nftIndex})) not-allowed))
)
(asserts! (is-approved nftIndex (unwrap! (nft-get-owner? loopbomb nftIndex) nft-not-owned-err)) nft-not-owned-err)
(asserts! (unwrap! (is-approved nftIndex owner) nft-not-owned-err) nft-not-owned-err)
(asserts! (is-eq nftIndex seriesOriginal) not-originale)
(ok (map-set nft-data {nft-index: nftIndex} {asset-hash: ahash, meta-data-url: metaDataUrl, max-editions: maxEditions, edition: edition, edition-cost: editionCost, series-original: seriesOriginal}))
)
Expand All @@ -505,6 +524,7 @@
(let
(
;; keeps track of the sale cycles for this NFT.
(owner (unwrap! (nft-get-owner? loopbomb nftIndex) not-allowed))
(saleCycleIndex (unwrap! (get sale-cycle-index (map-get? nft-sale-data {nft-index: nftIndex})) amount-not-set))
(saleType (unwrap! (get sale-type (map-get? nft-sale-data {nft-index: nftIndex})) amount-not-set))
(currentBidIndex (default-to u0 (get high-bid-counter (map-get? nft-high-bid-counter {nft-index: nftIndex}))))
Expand All @@ -513,7 +533,7 @@
;; u2 means bidding is in progress and the sale data can't be changed.
(asserts! (not (and (> currentAmount u0) (is-eq saleType u2))) bidding-error)
;; owner or approval can do this.
(asserts! (is-approved nftIndex (unwrap! (nft-get-owner? loopbomb nftIndex) nft-not-owned-err)) not-allowed)
(asserts! (unwrap! (is-approved nftIndex owner) nft-not-owned-err) nft-not-owned-err)
;; Note - don't override the sale cyle index here as this is a public method and can be called ad hoc. Sale cycle is update at end of sale!
(asserts! (map-set nft-sale-data {nft-index: nftIndex} {sale-cycle-index: saleCycleIndex, sale-type: sale-type, increment-stx: increment-stx, reserve-stx: reserve-stx, amount-stx: amount-stx, bidding-end-time: bidding-end-time}) not-allowed)
(print {evt: "set-sale-data", nftIndex: nftIndex, saleType: sale-type, increment: increment-stx, reserve: reserve-stx, amount: amount-stx, biddingEndTime: bidding-end-time})
Expand All @@ -526,8 +546,9 @@
(let
(
(saleCycleIndex (unwrap! (get sale-cycle-index (map-get? nft-sale-data {nft-index: nftIndex})) amount-not-set))
(owner (unwrap! (nft-get-owner? loopbomb nftIndex) not-allowed))
)
(asserts! (is-approved nftIndex (unwrap! (nft-get-owner? loopbomb nftIndex) nft-not-owned-err)) not-allowed)
(asserts! (unwrap! (is-approved nftIndex owner) nft-not-owned-err) nft-not-owned-err)
;; Note - don't override the sale cyle index here as this is a public method and can be called ad hoc. Sale cycle is update at end of sale!
(asserts! (map-set nft-sale-data {nft-index: nftIndex} {sale-cycle-index: saleCycleIndex, sale-type: u0, increment-stx: u0, reserve-stx: u0, amount-stx: u0, bidding-end-time: u0}) not-allowed)
(print {evt: "unlist-item", nftIndex: nftIndex})
Expand All @@ -540,8 +561,9 @@
(let
(
(saleCycleIndex (unwrap! (get sale-cycle-index (map-get? nft-sale-data {nft-index: nftIndex})) amount-not-set))
(owner (unwrap! (nft-get-owner? loopbomb nftIndex) not-allowed))
)
(asserts! (is-approved nftIndex (unwrap! (nft-get-owner? loopbomb nftIndex) nft-not-owned-err)) not-allowed)
(asserts! (unwrap! (is-approved nftIndex owner) nft-not-owned-err) nft-not-owned-err)
;; (map-set approvals {owner: tx-sender, operator: operator, nft-index: token-id} true)
;; Note - don't override the sale cyle index here as this is a public method and can be called ad hoc. Sale cycle is update at end of sale!
(asserts! (map-set nft-sale-data {nft-index: nftIndex} {sale-cycle-index: saleCycleIndex, sale-type: u1, increment-stx: u0, reserve-stx: u0, amount-stx: amount, bidding-end-time: u0}) not-allowed)
Expand Down Expand Up @@ -704,10 +726,11 @@
(currentBidder (unwrap! (get-current-bidder nftIndex currentBidIndex) bidding-error))
(currentAmount (unwrap! (get-current-bid-amount nftIndex currentBidIndex) bidding-error))
(seriesOriginal (unwrap! (get series-original (map-get? nft-data {nft-index: nftIndex})) not-allowed))
(owner (unwrap! (nft-get-owner? loopbomb nftIndex) not-allowed))
)
(asserts! (or (is-eq closeType u1) (is-eq closeType u2)) failed-to-close-1)
;; only the owner or administrator can call close
(asserts! (or (is-approved nftIndex (unwrap! (nft-get-owner? loopbomb nftIndex) nft-not-owned-err)) (unwrap! (is-administrator) not-allowed)) not-allowed)
(asserts! (or (unwrap! (is-approved nftIndex owner) nft-not-owned-err) (unwrap! (is-administrator) not-allowed)) not-allowed)
;; only the administrator can call close BEFORE the end time - note we use the less accurate
;; but fool proof block time here to prevent owner/client code jerry mandering the close function
(asserts! (or (> block-time bidding-end-time) (unwrap! (is-administrator) failed-to-close-3)) failed-to-close-3)
Expand Down
17 changes: 17 additions & 0 deletions contracts/nft-operable-trait.clar
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
(define-trait nft-operable-trait
(
;; set approval for an operator to handle a specified id or amount of the asset
;; must return `(ok true)` on success, never `(ok false)`
;; @param id-or-amount; identifier of NFT or amount of FTs
;; @param operator: principal that wants top operate the asset
;; @param bool: if true operator can transfer id or up to amount
(set-approved (uint principal bool) (response bool uint))

;; read-only function to return the current status of given operator
;; if returned `(ok true)` the operator can transfer the NFT with the given id or up to the requested amount of FT
;; @param id-or-amount; identifier of NFT or amount of FTs
;; @param operator: principal that wants to operate the asset
;; @param bool: if true operator can transfer id or up to amount
(is-approved (uint principal) (response bool uint))
)
)
2 changes: 1 addition & 1 deletion contracts/template.clar
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@
(err code)))))

;; see nft-approvable-trait
(define-public (set-approved (operator principal) (token-id uint) (approved bool))
(define-public (set-approved (token-id uint) (operator principal) (approved bool))
(ok (map-set approvals {owner: tx-sender, operator: operator, nft-index: token-id} approved))
)

Expand Down
4 changes: 2 additions & 2 deletions src/loopbomb-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@ export class LoopbombClient {
return result;
}

setApproved(operator: string, nftIndex: number, approved: boolean, sender: string): Tx {
setApproved(nftIndex: number, operator: string, approved: boolean, sender: string): Tx {
return Tx.contractCall(
this.contractName,
"set-approved",
[types.principal(operator), types.uint(nftIndex), types.bool(approved)],
[types.uint(nftIndex), types.principal(operator), types.bool(approved)],
sender
);
}
Expand Down
16 changes: 11 additions & 5 deletions tests/loopbomb_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -705,17 +705,23 @@ Clarinet.test({
block.receipts[0].result
.expectErr().expectUint(ErrCode.ERR_NFT_NOT_OWNED_ERR);

// wallet 1 can still set approval - see https://github.com/stacksgov/sips/issues/40
// wallet 2 can set approval - see https://github.com/stacksgov/sips/issues/40
block = chain.mineBlock([
client.setApproved(wallet3.address, 0, true, wallet1.address),
client.setApproved(0, wallet3.address, true, wallet2.address),
]);
block.receipts[0].result
.expectOk()
.expectBool(true);

// wallet 2 sets approval for wallet 3

block = chain.mineBlock([
client.setApproved(0, wallet3.address, true, wallet3.address),
]);
block.receipts[0].result
.expectErr().expectUint(ErrCode.ERR_NOT_ALLOWED);

// wallet 2 sets approval for wallet 3
block = chain.mineBlock([
client.setApproved(wallet3.address, 0, true, wallet2.address),
client.setApproved(0, wallet3.address, true, wallet2.address),
]);
block.receipts[0].result.expectOk().expectBool(true);

Expand Down

0 comments on commit a3a770a

Please sign in to comment.