-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathContract_template.hs
More file actions
427 lines (342 loc) · 14.4 KB
/
Contract_template.hs
File metadata and controls
427 lines (342 loc) · 14.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# OPTIONS_GHC -fno-warn-unused-imports #-}
{-# OPTIONS_GHC -fwarn-incomplete-patterns #-}
{-# OPTIONS_GHC -Wall #-}
-- A module starts with its export declarations of symbols declared in this file.
-- module MyModule (myExport1, myExport2) where
module Contract_template where
-- Followed by a set of imports of symbols from other files
-- import OtherModule (myImport1, myImport2)
-- Import all symbols into the local namespace.
-- import Data.List
-- Import select symbols into the local namespace:
-- import Data.List (nub, sort)
-- We can import one or more constructors explicitly:
-- import Text.Read (Lexeme(Ident, Symbol))
-- All constructors for a given type can also be imported:
-- import Text.Read (Lexeme(..))
-- We can also import types and classes defined in the module:
-- import Text.Read (Read, ReadS)
-- Import into the global namespace masking a symbol:
-- import Data.List hiding (nub)
-- Import symbols qualified under Data.Map namespace into the local namespace.
-- import qualified Data.Map
-- A second form does not create an alias. Instead,
-- the prefix becomes the module name. We can
-- write a simple function to check if a string is all
-- upper case:
-- import qualified Char
-- allUpper str = all Char.isUpper str
-- Import symbols qualified and reassigned to a custom namespace (M, in the example below):
-- import qualified Data.Map as M
import Control.Monad hiding (fmap)
import Data.Aeson (ToJSON, FromJSON)
import Data.Map as Map
import Data.Text (Text)
import Data.Void (Void)
import GHC.Generics (Generic)
import Plutus.Contract
import qualified PlutusTx
import PlutusTx.Prelude hiding (unless)
import Ledger hiding (singleton)
import Ledger.Constraints (TxConstraints)
import qualified Ledger.Constraints as Constraints
import qualified Ledger.Typed.Scripts as Scripts
import Ledger.Ada as Ada
import Playground.Contract (printJson, printSchemas, ensureKnownCurrencies, stage, ToSchema)
import Playground.TH (mkKnownCurrencies, mkSchemaDefinitions)
import Playground.Types (KnownCurrency (..))
import Prelude (IO)
import qualified Prelude as P
import Text.Printf (printf)
-- data types
-- data Point = Point Int Int
-- data Point2 = Point2 { x :: Int, y :: Int }
data VestingDatum = VestingDatum
{ beneficiary1 :: PaymentPubKeyHash
, beneficiary2 :: PaymentPubKeyHash
, deadline :: POSIXTime
} deriving P.Show
PlutusTx.unstableMakeIsData ''VestingDatum
{-# INLINABLE mkValidator #-}
-- This should validate if either beneficiary1 has signed the transaction and the current slot is before or at the deadline
-- or if beneficiary2 has signed the transaction and the deadline has passed.
mkValidator :: VestingDatum -> () -> ScriptContext -> Bool
-- mkValidator dat () ctx
-- | (unPaymentPubKeyHash (beneficiary1 dat) `elem` sigs) && (to (deadline dat) `contains` range) = True
-- | (unPaymentPubKeyHash (beneficiary2 dat) `elem` sigs) && (from (1 + deadline dat) `contains` range) = True
-- | otherwise = False
-- where
-- info :: TxInfo
-- info = scriptContextTxInfo ctx
-- sigs :: [PubKeyHash]
-- sigs = txInfoSignatories info
-- range :: POSIXTimeRange
-- range = txInfoValidRange info
-- needs {-# LANGUAGE NamedFieldPuns #-} for b1, b1, deadline
mkValidator VestingDatum{beneficiary1, beneficiary2, deadline} _ ScriptContext{scriptContextTxInfo=txInfo} =
let signedBy1 = txInfo `txSignedBy` unPaymentPubKeyHash beneficiary1
signedBy2 = txInfo `txSignedBy` unPaymentPubKeyHash beneficiary2
vr = txInfoValidRange txInfo
in if from deadline `contains` vr
then traceIfFalse "tx unsigned by beneficiary 1" signedBy1
else traceIfFalse "tx unsigned by beneficiary 2" signedBy2
data Vesting
instance Scripts.ValidatorTypes Vesting where
type instance DatumType Vesting = VestingDatum
type instance RedeemerType Vesting = ()
typedValidator :: Scripts.TypedValidator Vesting
typedValidator = Scripts.mkTypedValidator @Vesting
$$(PlutusTx.compile [|| mkValidator ||])
$$(PlutusTx.compile [|| wrap ||])
where
wrap = Scripts.wrapValidator @VestingDatum @()
validator :: Validator
validator = Scripts.validatorScript typedValidator
scrAddress :: Ledger.Address
scrAddress = scriptAddress validator
data GiveParams = GiveParams
{ gpBeneficiary :: !PaymentPubKeyHash
, gpDeadline :: !POSIXTime
, gpAmount :: !Integer
} deriving (Generic, ToJSON, FromJSON, ToSchema)
type VestingSchema =
Endpoint "give" GiveParams
.\/ Endpoint "grab" ()
give :: AsContractError e => GiveParams -> Contract w s e ()
give gp = do
pkh <- ownPaymentPubKeyHash
let dat = VestingDatum
{ beneficiary1 = gpBeneficiary gp
, beneficiary2 = pkh
, deadline = gpDeadline gp
}
tx = Constraints.mustPayToTheScript dat $ Ada.lovelaceValueOf $ gpAmount gp
ledgerTx <- submitTxConstraints typedValidator tx
void $ awaitTxConfirmed $ getCardanoTxId ledgerTx
logInfo @P.String $ printf "made a gift of %d lovelace to %s with deadline %s"
(gpAmount gp)
(P.show $ gpBeneficiary gp)
(P.show $ gpDeadline gp)
grab :: forall w s e. AsContractError e => Contract w s e ()
grab = do
now <- currentTime
pkh <- ownPaymentPubKeyHash
utxos <- utxosAt scrAddress
let utxos1 = Map.filter (isSuitable $ \dat -> beneficiary1 dat == pkh && now <= deadline dat) utxos
utxos2 = Map.filter (isSuitable $ \dat -> beneficiary2 dat == pkh && now > deadline dat) utxos
logInfo @P.String $ printf "found %d gift(s) to grab" (Map.size utxos1 P.+ Map.size utxos2)
unless (Map.null utxos1) $ do
let orefs = fst <$> Map.toList utxos1
lookups = Constraints.unspentOutputs utxos1 P.<>
Constraints.otherScript validator
tx :: TxConstraints Void Void
tx = mconcat [Constraints.mustSpendScriptOutput oref unitRedeemer | oref <- orefs] P.<>
Constraints.mustValidateIn (to now)
void $ submitTxConstraintsWith @Void lookups tx
unless (Map.null utxos2) $ do
let orefs = fst <$> Map.toList utxos2
lookups = Constraints.unspentOutputs utxos2 P.<>
Constraints.otherScript validator
tx :: TxConstraints Void Void
tx = mconcat [Constraints.mustSpendScriptOutput oref $ unitRedeemer | oref <- orefs] P.<>
Constraints.mustValidateIn (from now)
void $ submitTxConstraintsWith @Void lookups tx
where
isSuitable :: (VestingDatum -> Bool) -> ChainIndexTxOut -> Bool
isSuitable p o = case _ciTxOutDatum o of
Left _ -> False
Right (Datum d) -> maybe False p $ PlutusTx.fromBuiltinData d
endpoints :: Contract () VestingSchema Text ()
endpoints = awaitPromise (give' `select` grab') >> endpoints
where
give' = endpoint @"give" give
grab' = endpoint @"grab" $ const grab
mkSchemaDefinitions ''VestingSchema
mkKnownCurrencies []
-- todo: emulator trace
-- todo: customised em config
-- http://dev.stephendiehl.com/hask/index.html
-- data Suit = Clubs | Diamonds | Hearts | Spades
-- data Color = Red | Black
-- data Value
-- = Two
-- | Three
-- | Four
-- | Five
-- | Six
-- | Seven
-- | Eight
-- | Nine
-- | Ten
-- | Jack
-- | Queen
-- | King
-- | Ace
-- deriving (Eq, Ord)
-- data Card = Card
-- { suit :: Suit
-- , color :: Color
-- , value :: Value
-- }
-- queenDiamonds :: Card
-- queenDiamonds = Card Diamonds Red Queen
-- -- Alternatively
-- queenDiamonds :: Card
-- queenDiamonds = Card { suit = Diamonds, color = Red, value = Queen }
-- -- The convention for preventing these kind of values in Haskell is to limit the export of constructors in a module and only provide a limited set of functions which the module exports, which can enforce these constraints. These are smart constructors
-- module Cards (Card, diamond, spade, heart, club) where
-- diamond :: Value -> Card
-- diamond = Card Diamonds Red
-- spade :: Value -> Card
-- spade = Card Spades Black
-- heart :: Value -> Card
-- heart = Card Hearts Red
-- club :: Value -> Card
-- club = Card Clubs Black
-- -- traverse a list
-- addOne :: [Int] -> [Int]
-- addOne (x : xs) = (x+1) : (addOne xs)
-- addOne [] = []
-- guards
-- absolute :: Int -> Int
-- absolute n
-- | n < 0 = (-n)
-- | otherwise = n
-- Guards can also occur in pattern case expressions.
-- absoluteJust :: Maybe Int -> Maybe Int
-- absoluteJust n = case n of
-- Nothing -> Nothing
-- Just n
-- | n < 0 -> Just (-n)
-- | otherwise -> Just n
-- absolute :: Int -> Int
-- absolute n =
-- if (n < 0)
-- then (-n)
-- else n
-- If statements are just syntactic sugar for case expressions over boolean values. The following example is equivalent to the above example.
-- absolute :: Int -> Int
-- absolute n = case (n < 0) of
-- True -> (-n)
-- False -> n
-- function application operator $.
-- This function is right associative and takes the entire expression on the right hand side of the operator and applies it to a function on the left.
-- infixr 0 $
-- ($) :: (a -> b) -> a -> b
-- Function Composition .
-- operation which takes two functions and produces another function with the result of the first argument function applied to the result of the second function.
-- example :: [Integer] -> [Integer]
-- example =
-- sort -- sort is in import Data.List
-- . filter (<100)
-- . map (*10)
-- List Comprehensions
-- Generators
-- Let bindings
-- Guards
-- [n*x | x <- [1,2,3,4,5], let n = 3, odd x]
-- cartesian product
-- [(x,y) | x <- [1,2,3], y <- [10,20,30]]
-- [ e1.. ] enumFrom e1
-- [ e1,e2.. ] enumFromThen e1 e2
-- [ e1..e3 ] enumFromTo e1 e3
-- [ e1,e2..e3 ] enumFromThenTo e1 e2 e3
-- fizzbuzz :: [String]
-- fizzbuzz = [fb x| x <- [1..100]]
-- where fb y
-- | y `mod` 15 == 0 = "FizzBuzz"
-- | y `mod` 3 == 0 = "Fizz"
-- | y `mod` 5 == 0 = "Buzz"
-- | otherwise = show y
-- The undefined function is extremely practical for debugging or to accommodate writing incomplete programs.
-- undefined :: a
-- mean :: Num a => Vector a -> a
-- mean nums = (total / count) where -- Partially defined function
-- total = undefined
-- count = undefined
-- addThreeNums :: Num a => a -> a -> a -> a
-- addThreeNums n m j = undefined -- No function body declared at all
-- f :: a -> Complicated Type
-- f = undefined -- Write tomorrow, typecheck today!
-- -- Arbitrarily complicated types
-- -- welcome!
-- Haddock
-- methods uses -- | to delineate the beginning of a comment:
-- -- | Documentation for f
-- f :: a -> a
-- f = ...
-- Multiline comments are also possible:
-- -- | Multiline documentation for the function
-- -- f with multiple arguments.
-- fmap :: Functor f
-- => (a -> b) -- ^ function
-- -> f a -- ^ input
-- -> f b -- ^ output
-- -- ^ is used to comment Constructors or Record fields:
-- data T a b
-- = A a -- ^ Documentation for A
-- | B b -- ^ Documentation for B
-- data R a b = R
-- { f1 :: a -- ^ Documentation for the field f1
-- , f2 :: b -- ^ Documentation for the field f2
-- }
-- Elements within a module (i.e. values, types, classes) can be hyperlinked by enclosing the identifier in single quotes:
-- data T a b
-- = A a -- ^ Documentation for 'A'
-- | B b -- ^ Documentation for 'B'
-- Modules themselves can be referenced by enclosing them in double quotes:
-- -- | Here we use the "Data.Text" library and import
-- -- the 'Data.Text.pack' function.
-- haddock also allows the user to include blocks of code within the generated documentation. Two methods of demarcating the code blocks exist in haddock. For example, enclosing a code snippet in @ symbols marks it as a code block:
-- -- | An example of a code block.
-- --
-- -- @
-- -- f x = f (f x)
-- -- @
-- Similarly, it is possible to use bird tracks (>) in a comment line to set off a code block.
-- -- | A similar code block example that uses bird tracks (i.e. '>')
-- -- > f x = f (f x)
-- Snippets of interactive shell sessions can also be included in haddock documentation. In order to denote the beginning of code intended to be run in a REPL, the >>> symbol is used:
-- -- | Example of an interactive shell session embedded within documentation
-- --
-- -- >>> factorial 5
-- -- 120
-- Headers for specific blocks can be added by prefacing the comment in the module block with a *:
-- module Foo (
-- -- * My Header
-- example1,
-- example2
-- )
-- Sections can also be delineated by $ blocks that pertain to references in the body of the module:
-- module Foo (
-- -- $section1
-- example1,
-- example2
-- )
-- -- $section1
-- -- Here is the documentation section that describes the symbols
-- -- 'example1' and 'example2'.
-- Links can be added with the following syntax:
-- <url text>
-- Images can also be included, so long as the path is either absolute or relative to the directory in which haddock is run.
-- <<diagram.png title>>
-- haddock options can also be specified with pragmas in the source, either at the module or project level.
-- {-# OPTIONS_HADDOCK show-extensions, ignore-exports #-}
-- Option Description
-- ignore-exports Ignores the export list and includes all signatures in scope.
-- not-home Module will not be considered in the root documentation.
-- show-extensions Annotates the documentation with the language extensions used.
-- hide Forces the module to be hidden from Haddock.
-- prune Omits definitions with no annotations.