Skip to content

Commit e08c561

Browse files
Merge pull request #54 from mlabs-haskell/sam/list-nft
Add listing contract, add test, restructure slightly
2 parents 7349604 + 2aaa1a3 commit e08c561

File tree

12 files changed

+258
-50
lines changed

12 files changed

+258
-50
lines changed

index.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
export function callMarketPlaceBuy(config: Config, args: BuyNftArgs):
22
Promise<void>
3+
export function callMarketPlaceSell(config: Config, args: SellArgs):
4+
Promise<void>
35
export function callMarketPlaceListNft(config: Config):
46
Promise<Array<NftListing>>
57
/**
@@ -46,6 +48,11 @@ export type BuyNftArgs = {
4648

4749
}
4850

51+
export type SellArgs = {
52+
tokenCS: string,
53+
tokenName: string
54+
}
55+
4956
export type FetchNftArgs = Input
5057

5158
export type NftCollectionArgs = {

src/Seabug/CallContract.purs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ module Seabug.CallContract
44
, callMarketPlaceBuy
55
, callMarketPlaceFetchNft
66
, callMarketPlaceListNft
7+
, callMarketPlaceSell
78
, callMint
89
) where
910

@@ -34,6 +35,7 @@ import Contract.Value
3435
import Control.Monad.Error.Class (throwError)
3536
import Control.Promise (Promise)
3637
import Control.Promise as Promise
38+
import Data.Bifunctor (lmap)
3739
import Data.BigInt (BigInt)
3840
import Data.BigInt as BigInt
3941
import Data.Log.Level (LogLevel(..))
@@ -50,6 +52,7 @@ import Seabug.Contract.CnftMint (mintCnft)
5052
import Seabug.Contract.Common (NftResult)
5153
import Seabug.Contract.MarketPlaceFetchNft (marketPlaceFetchNft)
5254
import Seabug.Contract.MarketPlaceListNft (marketPlaceListNft)
55+
import Seabug.Contract.MarketPlaceSell (marketPlaceSell)
5356
import Seabug.Contract.Mint (mintWithCollection)
5457
import Seabug.Metadata.Share (unShare)
5558
import Seabug.Metadata.Types (SeabugMetadata(SeabugMetadata))
@@ -134,6 +137,19 @@ callMarketPlaceListNft cfg = Promise.fromAff do
134137
listnft <- runContract contractConfig (marketPlaceListNft cfg.projectId)
135138
pure $ buildNftList contractConfig.networkId <$> listnft
136139

140+
-- | Calls Seabugs marketPlaceSell and takes care of converting data types.
141+
-- | Returns a JS promise.
142+
callMarketPlaceSell
143+
:: ContractConfiguration -> SellArgs -> Effect (Promise Unit)
144+
callMarketPlaceSell cfg args = Promise.fromAff do
145+
contractConfig <- liftEither $ buildContractConfig cfg
146+
sellArgs <- liftEither $ buildSellArgs args
147+
runContract contractConfig do
148+
txHash <- marketPlaceSell sellArgs
149+
log $ "Waiting for confirmation of sell transaction: " <> show txHash
150+
awaitTxConfirmed txHash
151+
log $ "Sell transaction confirmed: " <> show txHash
152+
137153
-- | Configuation needed to call contracts from JS.
138154
type ContractConfiguration =
139155
{ serverHost :: String
@@ -202,6 +218,11 @@ type MintArgs =
202218
, price :: BigInt -- Natural
203219
}
204220

221+
type SellArgs =
222+
{ tokenCS :: String
223+
, tokenName :: String
224+
}
225+
205226
buildContractConfig
206227
:: ContractConfiguration -> Either Error (ConfigParams ())
207228
buildContractConfig cfg = do
@@ -382,6 +403,14 @@ buildMintArgs
382403
}
383404
pure (mintCnftParams /\ mintParams)
384405

406+
buildSellArgs :: SellArgs -> Either Error (CurrencySymbol /\ TokenName)
407+
buildSellArgs { tokenCS, tokenName } = lmap error do
408+
cs <- note "Failed to convert to currency symbol" $ mkCurrencySymbol =<<
409+
hexToByteArray tokenCS
410+
tn <- note "Failed to convert to token name" $ mkTokenName =<< hexToByteArray
411+
tokenName
412+
pure (cs /\ tn)
413+
385414
buildTransactionInput :: TransactionInputOut -> Either Error TransactionInput
386415
buildTransactionInput input = do
387416
transactionId <-

src/Seabug/Contract/Buy.purs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import Plutus.Types.Transaction (UtxoM)
2222
import Seabug.Contract.Util
2323
( ReturnBehaviour(..)
2424
, SeabugTxData
25-
, minAdaOnlyUTxOValue
25+
, minUTxOValue
2626
, mkChangeNftIdTxData
2727
, modify
2828
, seabugTxToMarketTx
@@ -54,15 +54,15 @@ mkBuyTxData nftData mScriptUtxos = do
5454

5555
shareToSubtract :: BigInt -> BigInt
5656
shareToSubtract v
57-
| v < minAdaOnlyUTxOValue = zero
57+
| v < minUTxOValue = zero
5858
| otherwise = v
5959

6060
filterLowValue
6161
:: BigInt
6262
-> (Value.Value -> TxConstraints Void Void)
6363
-> TxConstraints Void Void
6464
filterLowValue v t
65-
| v < minAdaOnlyUTxOValue = mempty
65+
| v < minUTxOValue = mempty
6666
| otherwise = t (Value.lovelaceValueOf v)
6767

6868
authorShare = getShare $ toBigInt nftCollection.authorShare

src/Seabug/Contract/MarketPlaceListNft.purs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import Control.Parallel (parTraverse)
1717
import Data.Array (catMaybes, mapMaybe)
1818
import Data.Map as Map
1919
import Seabug.Contract.Common (NftResult)
20-
import Seabug.Contract.Util (minAdaOnlyUTxOValue)
20+
import Seabug.Contract.Util (minUTxOValue)
2121
import Seabug.MarketPlace (marketplaceValidatorAddr)
2222
import Seabug.Metadata (getFullSeabugMetadataWithBackoff)
2323
import Seabug.Types (MarketplaceDatum(MarketplaceDatum))
@@ -51,6 +51,6 @@ marketPlaceListNft projectId = do
5151
-- I put too low. The old nfts are caught above because their
5252
-- metadata won't be parsed.
5353
guard $ (unwrap metadata.seabugMetadata # _.ownerPrice) >=
54-
(Natural.fromBigInt' minAdaOnlyUTxOValue)
54+
(Natural.fromBigInt' minUTxOValue)
5555
pure { input, output, metadata }
5656
pure $ catMaybes withMetadata
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
module Seabug.Contract.MarketPlaceSell
2+
( marketPlaceSell
3+
) where
4+
5+
import Contract.Prelude
6+
7+
import Contract.Address (getWalletAddress)
8+
import Contract.Monad (Contract, liftedE, liftedM)
9+
import Contract.PlutusData (toData)
10+
import Contract.ScriptLookups as Lookups
11+
import Contract.Transaction (TransactionHash, balanceAndSignTxE, submit)
12+
import Contract.TxConstraints as Constraints
13+
import Contract.Utxos (utxosAt)
14+
import Contract.Value
15+
( CurrencySymbol
16+
, TokenName
17+
, Value
18+
, singleton
19+
, valueOf
20+
)
21+
import Data.BigInt (fromInt)
22+
import Effect.Exception (throw)
23+
import Seabug.MarketPlace (marketplaceValidator)
24+
import Seabug.Types (MarketplaceDatum(..))
25+
26+
-- | Mint the self-governed NFT for the given collection.
27+
marketPlaceSell
28+
:: forall (r :: Row Type)
29+
. CurrencySymbol /\ TokenName
30+
-> Contract r TransactionHash
31+
marketPlaceSell (curr /\ tn) = do
32+
addr <- liftedM "Cannot get address" getWalletAddress
33+
utxos <- liftedM "Cannot get user utxos" $ utxosAt addr
34+
marketplaceValidator' <- unwrap <$> marketplaceValidator
35+
36+
let
37+
hasToken :: Value -> Boolean
38+
hasToken v = valueOf v curr tn == fromInt 1
39+
40+
callerHasToken :: Boolean
41+
callerHasToken = any (unwrap >>> _.amount >>> hasToken) $ unwrap utxos
42+
43+
nftValue :: Value
44+
nftValue = singleton curr tn one
45+
46+
lookups :: Lookups.ScriptLookups Void
47+
lookups = Lookups.unspentOutputs (unwrap utxos)
48+
49+
constraints :: Constraints.TxConstraints Void Void
50+
constraints =
51+
Constraints.mustPayToScript marketplaceValidator'.validatorHash
52+
( wrap $ toData $ MarketplaceDatum $
53+
{ getMarketplaceDatum: curr /\ tn }
54+
)
55+
nftValue
56+
57+
unless callerHasToken $ liftEffect $ throw "Missing token"
58+
59+
unbalancedTx <- liftedE $ Lookups.mkUnbalancedTx lookups constraints
60+
signedTx <- liftedE $ balanceAndSignTxE unbalancedTx
61+
transactionHash <- submit signedTx
62+
log $ "Sell transaction successfully submitted with hash: " <> show
63+
transactionHash
64+
pure transactionHash

src/Seabug/Contract/Mint.purs

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,12 @@ module Seabug.Contract.Mint
77
import Contract.Prelude
88

99
import Contract.Address
10-
( getNetworkId
11-
, ownPaymentPubKeyHash
12-
, ownStakePubKeyHash
13-
, payPubKeyHashBaseAddress
10+
( ownPaymentPubKeyHash
11+
, getWalletAddress
1412
)
1513
import Contract.AuxiliaryData (setTxMetadata)
1614
import Contract.Chain (currentSlot, currentTime)
17-
import Contract.Monad (Contract, liftContractE, liftContractM, liftedE, liftedM)
15+
import Contract.Monad (Contract, liftContractE, liftedE, liftedM)
1816
import Contract.PlutusData (toData)
1917
import Contract.ScriptLookups as Lookups
2018
import Contract.Scripts (validatorHash)
@@ -28,13 +26,16 @@ import Contract.Value
2826
, scriptCurrencySymbol
2927
, singleton
3028
)
31-
import Seabug.Contract.Util (getSeabugMetadata)
29+
import Seabug.Contract.Util
30+
( ReturnBehaviour(ToMarketPlace)
31+
, getSeabugMetadata
32+
, payBehaviour
33+
)
3234
import Seabug.Lock (mkLockScript)
3335
import Seabug.MarketPlace (marketplaceValidator)
3436
import Seabug.MintingPolicy as MintingPolicy
3537
import Seabug.Types
3638
( LockDatum(..)
37-
, MarketplaceDatum(..)
3839
, MintAct(..)
3940
, MintParams(..)
4041
, NftCollection(..)
@@ -44,23 +45,22 @@ import Seabug.Types
4445

4546
mintWithCollectionTest
4647
:: forall (r :: Row Type)
47-
. CurrencySymbol /\ TokenName
48+
. ReturnBehaviour
49+
-> CurrencySymbol /\ TokenName
4850
-> MintParams
4951
-> ( Constraints.TxConstraints Void Void
5052
-> Contract r (Constraints.TxConstraints Void Void)
5153
)
5254
-> Contract r (TransactionHash /\ (CurrencySymbol /\ TokenName) /\ NftData)
5355
mintWithCollectionTest
56+
retBehaviour
5457
(collectionNftCs /\ collectionNftTn)
5558
( MintParams
5659
{ price, lockLockup, lockLockupEnd, authorShare, daoShare }
5760
)
5861
modConstraints = do
5962
owner <- liftedM "Cannot get PaymentPubKeyHash" ownPaymentPubKeyHash
60-
ownerStake <- liftedM "Cannot get StakePubKeyHash" ownStakePubKeyHash
61-
networkId <- getNetworkId
62-
addr <- liftContractM "Cannot get user address" $
63-
payPubKeyHashBaseAddress networkId owner ownerStake
63+
addr <- liftedM "Cannot get address" getWalletAddress
6464
utxos <- liftedM "Cannot get user utxos" $ utxosAt addr
6565
marketplaceValidator' <- unwrap <$> marketplaceValidator
6666
lockingScript <- mkLockScript collectionNftCs lockLockup lockLockupEnd
@@ -95,11 +95,8 @@ mintWithCollectionTest
9595
constraints = mconcat
9696
[ Constraints.mustMintValueWithRedeemer (wrap $ toData $ MintToken nft)
9797
nftValue
98-
, Constraints.mustPayToScript marketplaceValidator'.validatorHash
99-
( wrap $ toData $ MarketplaceDatum
100-
{ getMarketplaceDatum: curr /\ tn }
101-
)
102-
nftValue
98+
, payBehaviour retBehaviour marketplaceValidator'.validatorHash
99+
(curr /\ tn)
103100
, Constraints.mustPayToScript lockingScriptHash
104101
( wrap $ toData $ LockDatum
105102
{ sgNft: curr
@@ -127,7 +124,7 @@ mintWithCollection'
127124
. CurrencySymbol /\ TokenName
128125
-> MintParams
129126
-> Contract r (TransactionHash /\ (CurrencySymbol /\ TokenName) /\ NftData)
130-
mintWithCollection' c p = mintWithCollectionTest c p pure
127+
mintWithCollection' c p = mintWithCollectionTest ToMarketPlace c p pure
131128

132129
-- | Mint the self-governed NFT for the given collection.
133130
mintWithCollection

src/Seabug/Contract/Util.purs

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
module Seabug.Contract.Util
2-
( SeabugTxData
3-
, ReturnBehaviour(..)
4-
, minAdaOnlyUTxOValue
2+
( ReturnBehaviour(..)
3+
, SeabugTxData
4+
, getSeabugMetadata
5+
, minUTxOValue
56
, mkChangeNftIdTxData
67
, modify
8+
, payBehaviour
79
, seabugTxToMarketTx
8-
, getSeabugMetadata
910
) where
1011

1112
import Contract.Prelude
@@ -61,6 +62,7 @@ import Seabug.Types
6162
, NftData(..)
6263
, NftId
6364
)
65+
import Types.Scripts (ValidatorHash)
6466
import Types.Transaction (TransactionInput)
6567

6668
type SeabugTxData =
@@ -78,6 +80,22 @@ modify fn t = wrap (fn (unwrap t))
7880

7981
data ReturnBehaviour = ToMarketPlace | ToCaller
8082

83+
payBehaviour
84+
:: ReturnBehaviour
85+
-> ValidatorHash
86+
-> (Value.CurrencySymbol /\ Value.TokenName)
87+
-> TxConstraints Void Void
88+
payBehaviour ToCaller _ _ = mempty -- Balancing will return the token to the caller
89+
payBehaviour ToMarketPlace valHash asset =
90+
mustPayToScript
91+
valHash
92+
( Datum $ toData $
93+
MarketplaceDatum { getMarketplaceDatum: asset }
94+
)
95+
( Value.singleton (fst asset) (snd asset) one <> Value.lovelaceValueOf
96+
minUTxOValue
97+
)
98+
8199
-- | Build and submit a transaction involving a given nft, specifying
82100
-- | if the nft should be sent to the current user or the marketplace.
83101
seabugTxToMarketTx
@@ -106,22 +124,11 @@ seabugTxToMarketTx name retBehaviour mkTxData nftData = do
106124
[ ScriptLookups.typedValidatorLookups $ wrap marketplaceValidator'
107125
, ScriptLookups.validator marketplaceValidator'.validator
108126
]
109-
newNftValue =
110-
Value.singleton (fst txData.newAsset) (snd txData.newAsset) one
111127

112128
constraints :: TxConstraints Void Void
113129
constraints = txData.constraints
114130
<> mustSpendScriptOutput txData.inputUtxo unitRedeemer
115-
<>
116-
case retBehaviour of
117-
ToMarketPlace ->
118-
mustPayToScript
119-
valHash
120-
( Datum $ toData $
121-
MarketplaceDatum { getMarketplaceDatum: txData.newAsset }
122-
)
123-
newNftValue
124-
ToCaller -> mempty -- Balancing will return the token to the caller
131+
<> payBehaviour retBehaviour valHash txData.newAsset
125132

126133
txDatumsRedeemerTxIns <- liftedE $ mkUnbalancedTx lookups constraints
127134
metadata <- liftContractE $ getSeabugMetadata
@@ -208,8 +215,8 @@ mkChangeNftIdTxData name act mapNft (NftData nftData) mScriptUtxos = do
208215
, newNft: newNft
209216
}
210217

211-
minAdaOnlyUTxOValue :: BigInt
212-
minAdaOnlyUTxOValue = BigInt.fromInt 2_000_000
218+
minUTxOValue :: BigInt
219+
minUTxOValue = BigInt.fromInt 2_000_000
213220

214221
-- | Set metadata on the transaction for the given NFT
215222
getSeabugMetadata

test/Contract/Buy.purs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import Seabug.Contract.Util
2727
( ReturnBehaviour(..)
2828
, SeabugTxData
2929
, getSeabugMetadata
30-
, minAdaOnlyUTxOValue
30+
, minUTxOValue
3131
, modify
3232
)
3333
import Seabug.MarketPlace (marketplaceValidatorAddr)
@@ -366,7 +366,7 @@ buyerMarketplaceUtxoAssert
366366
assertPaymentUtxo
367367
:: String -> Address -> BigInt -> PostBuyTestData -> Contract () Unit
368368
assertPaymentUtxo name addr payment { txData: { oldAsset } }
369-
| payment < minAdaOnlyUTxOValue = pure unit
369+
| payment < minUTxOValue = pure unit
370370
| otherwise =
371371
assertContract (name <> " did not have payment utxo with datum")
372372
=<< isJust

0 commit comments

Comments
 (0)