MU6WOCCJQWG4A5NLD3GBFATCE3SRE3QQCYXYH6WIKSGLHQOOBVRAC
JUFBTX45TKSZMB2D4CGNB73UYM5FXAV2QMKIHBSMHEQDAECYP7HQC
LAROLAYUGJ4Q5AEFV5EJMIA2ZKBNCBWHHHPCJ3CKCNIUIYUKRFVQC
X3ES7NUA42D2BF7CQDDKXM5CLMVCYA3H5YU5KXLPTGDBFPE2LNVAC
M4PWY5RUV72AEDCNC4O7UKBPHBIACR4354YTSC3SUZGWFV5UBJBQC
GCVQD44VRPQVKPZEPIC4AOIXLJIG2ZMV3QI2Y7KALUT6NVUBSGSAC
75N3UJ4JK56KXF56GASGPAWLFYGJDETVJNYTF4KXFCQM767JUU5AC
EKI57EJR65DA5FPILAHGHHAIU5ITVGHA6V3775OX7GV5XD67OWRQC
LHJ2HFXVUQ4VG25I7DADWU73G5K5WNZBDQ3SVNKFYLZ5BEYM4XCQC
B6HWAPDPXIWH7CHK5VLMWLL6EQN6NOFZEFYO47BPUY2ZO4SL7VDAC
EFSXYZPOGA5M4DN65IEIDO7JK6U34DMQELAPHOIL2UAT6HRC66BAC
F2XLL7XWGUV4TJD4X2MJADYAQHCSB4HD2TPPEYVHEKHOQIOOFISAC
WO2MINIF4TXOHWSE7JWXRZYN64XRVLYIRFMF4SMPSOXKA2V77KMQC
GLFF5ZDKWI7WKPZSAEE3IUM27LL6DFOPIL4VPODXYXV3BCSCJ6GQC
NAS4BFL43SIUAAC663R6VFBHQ2DKI45K6Y6ZKVQI7S5547HBAN7QC
5IDB3IWSB6LFW4U772Y7BH5Y3FQOQ7IFWLVXDZE5XS6SKJITFV4QC
64C6AWH66FDKU6UE6Z6JPX2J2GBM2JOPTH2GL6LHKAIUBGNGDZ5AC
O5FVTOM6YFBLEPF3S576K6IMT6ZZ5VQCSB3YVXNS4CKBITKCRZ7AC
5OI44E4EEVYOMHDWNK2WA7K4L4JWRWCUJUNN2UAUGE5VY4W7GTNAC
FXJQACESPGTLPG5ELXBU3M3OQXUZQQIR7HPIEHQ3FNUTMWVH4WBAC
ASF3UPJLCX7KIUCNJD5KAXSPDXCUEJHLL4HBRTRUBPCW73IXOCWQC
W35DDBFYF6Z4ZPCFEO5RPAONZLYCSNTXUSTS6FIUUVZHCI6Q7GHAC
IPG33FAWXGEQ2PO6OXRT2PWWXHRNMPVUKKADL6UKKN5GD2CNZ25AC
NVOCQVASZWTKQJG7GPH7KHKZZR7NUG4WLV5YY4KAIRPCJRWCZPIAC
HALRDT2F22DAQ54M334YSKD2XJCAXDXEZPDKGMOWKH7XELBAS6MAC
4R7XIYK3BP664CO3YJ2VM64ES2JYN27UTQG5KS34OTEPAIODSZLQC
5ZSKPQ3KY6T6O5S6T6HW4OHJMQXA72WKJSJJMGKGX2WMFTNZ7EGAC
Z3MK2PJ5U222DXRS22WCDHVPZ7HVAR3HOCUNXIGX6VMEPBQDF6PQC
HMDM3B557TO5RYP2IGFFC2C2VN6HYZTDQ47CJY2O37BW55DSMFZAC
RPAJLHMTUJU4AYNBOHVGHGGB4NY2NLY3BVPYN5FMWB3ZIMAUQHCQC
BROSTG5KP3NUNLSYPVQID254TE47E5RKQAKLPIY7PGWETE6JNMTAC
QMRKFEPGFBCEWAIXPEIG5ILKAJ2JH5L3TOITHR4HNJXK5FN3KXBQC
2J37EVJMX255K3XEJHTZGRPEIRMAQ62JQWOA7JU3YTZUB6PUPWVQC
UWMGUJOW5X5HQTS76T2FD7MNAJF7SESPQVU5FDIZO52V75TT2X6AC
ADMKQQGCGVSHHIMVQ4XFRDCG544SBJCYALSKZV45CQQBZ4ACUH2AC
UUR6SMCAJMA7O3ZFUCQMPZFDDIPUVQ5IHUAC5F252YVD6H3JIKPQC
LTSVBVA235BQAIU3SQURKSRHIAL33K47G4J6TSEP2K353OCHNJEAC
bidOrder :: Bid -> Bid -> Ordering
bidOrder = comparing costRatio `mappend` comparing (^. bidTime)
bidOrder ::
forall c.
IsCurrency c =>
Bid c ->
Bid c ->
Ordering
bidOrder = comparing costRatio <> comparing (^. bidTime)
secs bid = toRational $ bid ^. bidSeconds
btc bid = toRational $ bid ^. bidAmount . _Satoshi
costRatio bid = secs bid / btc bid
costRatio :: Bid c -> Rational
costRatio bid = (toRational $ bid ^. bidSeconds) / (toRational $ bid ^. bidAmount . _Units)
| -- if the last bid will exceed the raise amount, reduce it to fit
total < raiseAmount' =
let winFraction r = r % (bid ^. bidAmount . _Satoshi)
remainderSeconds (Satoshi r) =
| total < raiseAmount' =
-- if the last bid will exceed the raise amount, reduce it to fit
let winFraction r =
(r ^. _Units) % (bid ^. bidAmount . _Units)
remainderSeconds r =
adjustBid r = bid & bidSeconds .~ remainderSeconds r & bidAmount .~ r
in toList $ adjustBid <$> raiseAmount' `ssub` total
adjustBid r =
bid & bidSeconds .~ remainderSeconds r & bidAmount .~ r
in toList $ adjustBid <$> raiseAmount' `csub` total
(raiseAmount' `ssub` submittedTotal)
bidCommitment :: Satoshi -> Bid -> State Satoshi (Maybe Commitment)
bidCommitment raiseAmount' bid = do
raised <- get
case raised of
-- if the total is fully within the raise amount
x
| x <> (bid ^. bidAmount) < raiseAmount' ->
put (x <> bid ^. bidAmount)
>> (pure . Just $ Commitment bid (bid ^. bidSeconds) (bid ^. bidAmount))
-- if the last bid will exceed the raise amount, reduce it to fit
x
| x < raiseAmount' ->
let winFraction r = r % (bid ^. bidAmount . _Satoshi)
remainderSeconds (Satoshi r) =
Seconds . round $ winFraction r * fromIntegral (bid ^. bidSeconds)
in for (raiseAmount' `ssub` x) $ \remainder ->
put (x <> remainder)
*> (pure $ Commitment bid (remainderSeconds remainder) remainder)
-- otherwise,
_ -> pure Nothing
(raiseAmount' `csub` submittedTotal)
import qualified Aftok.Currency.Zcash as Zcash
import qualified Bippy.Types as Bitcoin
import Control.Lens (view)
import qualified Haskoin.Address as Bitcoin
import qualified Aftok.Currency.Bitcoin as B
import qualified Aftok.Currency.Zcash as Z
import Control.Lens (Iso')
import qualified Text.Show
BTC :: Currency Bitcoin.Address Bitcoin.Satoshi
ZEC :: Currency Zcash.Address Zcash.Zatoshi
BTC :: Currency B.Address B.Satoshi
ZEC :: Currency Z.Address Z.Zatoshi
instance Eq (Currency a c) where
BTC == BTC = True
ZEC == ZEC = True
instance Show (Currency a c) where
show = \case
BTC -> "BTC"
ZEC -> "ZEC"
scaleCurrency :: Currency a c -> c -> Rational -> Maybe c
scaleCurrency c amount factor = case c of
BTC -> (\(Bitcoin.Satoshi amt) -> Just $ Bitcoin.Satoshi ((round $ toRational amt * factor) :: Word64)) amount
ZEC -> (\amt -> Zcash.toZatoshi ((round $ toRational (view Zcash._Zatoshi amt) * factor) :: Word64)) amount
instance IsCurrency B.Satoshi where
csub = B.ssub
cscale (B.Satoshi amt) factor =
let r = toRational amt * factor
in if (r >= 0) then Just (B.Satoshi . round $ r) else Nothing
_Units = B._Satoshi
currency' = Currency' BTC
instance IsCurrency Z.Zatoshi where
csub = Z.zsub
cscale (Z.Zatoshi amt) factor =
let r = toRational amt * factor
in if (r >= 0) then Just (Z.Zatoshi . round $ r) else Nothing
_Units = Z._Zatoshi
currency' = Currency' ZEC
-- import Aftok.Currency ( Amount(..) )
-- import qualified Aftok.Currency.Bitcoin as Bitcoin
import Aftok.Currency.Bitcoin (_Satoshi)
-- import qualified Aftok.Currency.Zcash as Zcash
import Aftok.Database (Limit(..))
import Aftok.Currency (Amount (..))
import Aftok.Database (Limit (..))
[sql| INSERT INTO auctions (project_id, initiator_id, name, description, raise_amount, end_time)
VALUES (?, ?, ?, ?) RETURNING id |]
( auc ^. (projectId . _ProjectId),
auc ^. (initiator . _UserId),
[sql| INSERT INTO auctions (project_id, initiator_id, name, description, currency, raise_amount, start_time, end_time)
VALUES (?, ?, ?, ?, ?, ?, ?, ?) RETURNING id |]
( auc ^. projectId . _ProjectId,
auc ^. initiator . _UserId,
ListAuctions :: ProjectId -> RangeQuery -> Limit -> DBOp [A.Auction]
CreateAuction :: A.Auction -> DBOp A.AuctionId
FindAuction :: A.AuctionId -> DBOp (Maybe A.Auction)
CreateBid :: A.AuctionId -> A.Bid -> DBOp A.BidId
FindBids :: A.AuctionId -> DBOp [(A.BidId, A.Bid)]
ListAuctions :: ProjectId -> RangeQuery -> Limit -> DBOp [A.Auction Amount]
CreateAuction :: A.Auction Amount -> DBOp A.AuctionId
FindAuction :: A.AuctionId -> DBOp (Maybe (A.Auction Amount))
CreateBid :: A.AuctionId -> A.Bid Amount -> DBOp A.BidId
FindBids :: A.AuctionId -> DBOp [(A.BidId, A.Bid Amount)]
parseAmountJSON :: Value -> Parser Amount
parseAmountJSON = \case
Object o ->
maybeT (fail $ "Expected to find one of [\"satoshi\", \"zatoshi\"] as a key.") pure $
MaybeT (fmap (Amount BTC . review _Satoshi) <$> o .:? "satoshi")
<|> MaybeT (fmap (Amount ZEC . review _Zatoshi) <$> o .:? "zatoshi")
val -> fail $ "Value " <> show val <> " is not a JSON object."
parseRecurrence' (Object o) = parseRecurrence o
parseRecurrence' val = fail $ "Value " <> show val <> " is not a JSON object."
parseRecurrence' = \case
(Object o) -> parseRecurrence o
val -> fail $ "Value " <> show val <> " is not a JSON object."
ALTER TABLE auctions ADD COLUMN currency currency_t NOT NULL;
data AuctionCreateRequest = CA {raiseAmount :: Word64, auctionStart :: C.UTCTime, auctionEnd :: C.UTCTime}
data AuctionCreateRequest
= CA
{ name :: Text,
description :: Maybe Text,
raiseAmount :: Amount,
auctionStart :: C.UTCTime,
auctionEnd :: C.UTCTime
}
endpoints <- (,) <$> timeParam "after" <*> timeParam "before"
let ival = case endpoints of
(Just s, Just e) -> During s e
(Nothing, Just e) -> Before e
(Just s, Nothing) -> After s
(Nothing, Nothing) -> Always
limit <- fromMaybe 1 <$> decimalParam "limit"
ival <- rangeQueryParam
limit <- Limit . fromMaybe 1 <$> decimalParam "limit"