tx · H9qFYX4sxTwqDZkznQnxJXrcMMYpw2ScPK4mqhQcRHmC 3MzXbNgiTerYhuAzCE2av22AVY2K44yH5BS: -0.01000000 Waves 2019.06.24 16:03 [556200] smart account 3MzXbNgiTerYhuAzCE2av22AVY2K44yH5BS > SELF 0.00000000 Waves
{ "type": 13, "id": "H9qFYX4sxTwqDZkznQnxJXrcMMYpw2ScPK4mqhQcRHmC", "fee": 1000000, "feeAssetId": null, "timestamp": 1561381434303, "version": 1, "sender": "3MzXbNgiTerYhuAzCE2av22AVY2K44yH5BS", "senderPublicKey": "7EqipifubA35bBBSY9nMcXgDyvbjK2D3NT4RE7txJh4v", "proofs": [ "2h1yoa6RrJeFHRabgTn3VLbwWaPeAYK8xpPiDmhn1bEYNDF7DAZFTcN2J28jD61cE5ozMLNq4GpXV63WrmL3rwST" ], "script": "base64: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Y3Rpb25Mb3RBc3NldElkCQEAAAARQGV4dHJOYXRpdmUoMTA1MykAAAACBQAAAAR0aGlzCQABLAAAAAIFAAAACWF1Y3Rpb25JZAIAAAAMX2xvdF9hc3NldElkBAAAABBhdWN0aW9uTG90QW1vdW50CQEAAAARQGV4dHJOYXRpdmUoMTA1MCkAAAACBQAAAAR0aGlzCQABLAAAAAIFAAAACWF1Y3Rpb25JZAIAAAALX2xvdF9hbW91bnQEAAAAE2F1Y3Rpb25QcmljZUFzc2V0SWQJAQAAABFAZXh0ck5hdGl2ZSgxMDUzKQAAAAIFAAAABHRoaXMJAAEsAAAAAgUAAAAJYXVjdGlvbklkAgAAAA1fcHJpY2VBc3NldElkBAAAABBhdWN0aW9uV2luQW1vdW50CQEAAAARQGV4dHJOYXRpdmUoMTA1MCkAAAACBQAAAAR0aGlzCQABLAAAAAIFAAAACWF1Y3Rpb25JZAIAAAAKX3dpbkFtb3VudAQAAAAPYXVjdGlvbkxvdEFzc2V0AwkAAAAAAAACBQAAABFhdWN0aW9uTG90QXNzZXRJZAIAAAAFV0FWRVMFAAAABHVuaXQJAAJZAAAAAQUAAAARYXVjdGlvbkxvdEFzc2V0SWQEAAAAEWF1Y3Rpb25QcmljZUFzc2V0AwMJAAAAAAAAAgUAAAATYXVjdGlvblByaWNlQXNzZXRJZAIAAAAFV0FWRVMGCQAAAAAAAAIFAAAAE2F1Y3Rpb25QcmljZUFzc2V0SWQCAAAAAAUAAAAEdW5pdAkAAlkAAAABBQAAABNhdWN0aW9uUHJpY2VBc3NldElkBAAAABFhdWN0aW9uV2lubmVyQWRkcgkBAAAAHEBleHRyVXNlcihhZGRyZXNzRnJvbVN0cmluZykAAAABCQEAAAAFdmFsdWUAAAABBQAAAA1hdWN0aW9uV2lubmVyBAAAABRhdWN0aW9uT3JnYW5pemVyQWRkcgkBAAAAHEBleHRyVXNlcihhZGRyZXNzRnJvbVN0cmluZykAAAABCQEAAAAFdmFsdWUAAAABBQAAABBhdWN0aW9uT3JnYW5pemVyBAAAAAliZXRBbW91bnQJAAQaAAAAAgUAAAAEdGhpcwkAASwAAAACCQABLAAAAAIFAAAACWF1Y3Rpb25JZAIAAAAIX2JpZGRlcl8FAAAAEGNhbGxl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", "chainId": 84, "height": 556200, "spentComplexity": 0 } View: original | compacted Prev: none Next: none Full:
Old | New | Differences | |
---|---|---|---|
1 | - | # no script | |
1 | + | {-# STDLIB_VERSION 3 #-} | |
2 | + | {-# SCRIPT_TYPE ACCOUNT #-} | |
3 | + | {-# CONTENT_TYPE DAPP #-} | |
4 | + | let maxAuctionDuration = (1440 * 30) | |
5 | + | ||
6 | + | @Callable(i) | |
7 | + | func startAuction (duration,startPrice,priceAssetId) = { | |
8 | + | let auctionId = toBase58String(i.transactionId) | |
9 | + | let auctionEndHeight = (lastBlock.height + duration) | |
10 | + | let pmt = extract(i.payment) | |
11 | + | if ((duration > maxAuctionDuration)) | |
12 | + | then throw(("Duration is too long. Must be less than " + toString(maxAuctionDuration))) | |
13 | + | else WriteSet([DataEntry(auctionId, auctionEndHeight), DataEntry((auctionId + "_organizer"), toBase58String(i.caller.bytes)), DataEntry((auctionId + "_lot_assetId"), if (isDefined(pmt.assetId)) | |
14 | + | then toBase58String(value(pmt.assetId)) | |
15 | + | else "WAVES"), DataEntry((auctionId + "_lot_amount"), pmt.amount), DataEntry((auctionId + "_startPrice"), startPrice), DataEntry((auctionId + "_priceAssetId"), priceAssetId)]) | |
16 | + | } | |
17 | + | ||
18 | + | ||
19 | + | ||
20 | + | @Callable(i) | |
21 | + | func bid (auctionId) = { | |
22 | + | let pmt = extract(i.payment) | |
23 | + | let pmtAssetIdStr = if (isDefined(pmt.assetId)) | |
24 | + | then toBase58String(value(pmt.assetId)) | |
25 | + | else "WAVES" | |
26 | + | let callerAddressStr = toBase58String(i.caller.bytes) | |
27 | + | let auctionEndHeight = getIntegerValue(this, auctionId) | |
28 | + | let auctionPriceAssetId = getStringValue(this, (auctionId + "_priceAssetId")) | |
29 | + | let auctionWinAmount = getInteger(this, (auctionId + "_winAmount")) | |
30 | + | if ((lastBlock.height >= auctionEndHeight)) | |
31 | + | then throw("Auction already finished") | |
32 | + | else if ((auctionPriceAssetId != pmtAssetIdStr)) | |
33 | + | then throw((("Bid must be in asset '" + auctionPriceAssetId) + "'")) | |
34 | + | else if (if (isDefined(auctionWinAmount)) | |
35 | + | then (value(auctionWinAmount) >= pmt.amount) | |
36 | + | else false) | |
37 | + | then throw(("Bid must be more then " + toString(pmt.amount))) | |
38 | + | else WriteSet([DataEntry(((auctionId + "_bidder_") + callerAddressStr), pmt.amount), DataEntry((auctionId + "_winner"), callerAddressStr), DataEntry((auctionId + "_winAmount"), pmt.amount)]) | |
39 | + | } | |
40 | + | ||
41 | + | ||
42 | + | ||
43 | + | @Callable(i) | |
44 | + | func withdraw (auctionId) = { | |
45 | + | let pmt = extract(i.payment) | |
46 | + | let pmtAssetIdStr = if (isDefined(pmt.assetId)) | |
47 | + | then toBase58String(value(pmt.assetId)) | |
48 | + | else "WAVES" | |
49 | + | let callerAddressStr = toBase58String(i.caller.bytes) | |
50 | + | let auctionEndHeight = getIntegerValue(this, auctionId) | |
51 | + | let auctionOrganizer = getStringValue(this, (auctionId + "_organizer")) | |
52 | + | let auctionWinner = getString(this, (auctionId + "_winner")) | |
53 | + | let auctionLotAssetId = getStringValue(this, (auctionId + "_lot_assetId")) | |
54 | + | let auctionLotAmount = getIntegerValue(this, (auctionId + "_lot_amount")) | |
55 | + | let auctionPriceAssetId = getStringValue(this, (auctionId + "_priceAssetId")) | |
56 | + | let auctionWinAmount = getIntegerValue(this, (auctionId + "_winAmount")) | |
57 | + | let auctionLotAsset = if ((auctionLotAssetId == "WAVES")) | |
58 | + | then unit | |
59 | + | else fromBase58String(auctionLotAssetId) | |
60 | + | let auctionPriceAsset = if (if ((auctionPriceAssetId == "WAVES")) | |
61 | + | then true | |
62 | + | else (auctionPriceAssetId == "")) | |
63 | + | then unit | |
64 | + | else fromBase58String(auctionPriceAssetId) | |
65 | + | let auctionWinnerAddr = addressFromStringValue(value(auctionWinner)) | |
66 | + | let auctionOrganizerAddr = addressFromStringValue(value(auctionOrganizer)) | |
67 | + | let betAmount = getInteger(this, ((auctionId + "_bidder_") + callerAddressStr)) | |
68 | + | if ((auctionEndHeight > lastBlock.height)) | |
69 | + | then throw("Auction is not finished yet") | |
70 | + | else if (!(isDefined(auctionWinner))) | |
71 | + | then if ((callerAddressStr == auctionOrganizer)) | |
72 | + | then if (!(isDefined(getString(this, (auctionId + "_lot_passed"))))) | |
73 | + | then throw("You have already got your lot back") | |
74 | + | else ScriptResult(WriteSet([DataEntry((auctionId + "_lot_passed"), toBase58String(i.caller.bytes))]), TransferSet([ScriptTransfer(i.caller, auctionLotAmount, auctionLotAsset)])) | |
75 | + | else throw("You haven't participate in this auction") | |
76 | + | else if (if ((callerAddressStr == auctionOrganizer)) | |
77 | + | then true | |
78 | + | else (callerAddressStr == auctionWinner)) | |
79 | + | then if (isDefined(getString(this, (auctionId + "_lot_passed")))) | |
80 | + | then throw("Lot is already passed to the winner, and organizer got his reward") | |
81 | + | else ScriptResult(WriteSet([DataEntry((auctionId + "_lot_passed"), toBase58String(auctionWinnerAddr.bytes))]), TransferSet([ScriptTransfer(auctionWinnerAddr, auctionLotAmount, auctionLotAsset), ScriptTransfer(auctionOrganizerAddr, auctionWinAmount, auctionPriceAsset)])) | |
82 | + | else if (!(isDefined(betAmount))) | |
83 | + | then throw("You didn't bid anything") | |
84 | + | else if (isDefined(getBoolean(this, (((auctionId + "_bidder_") + callerAddressStr) + "_return")))) | |
85 | + | then throw("You have already got your bid back") | |
86 | + | else ScriptResult(WriteSet([DataEntry((((auctionId + "_bidder_") + callerAddressStr) + "_return"), true)]), TransferSet([ScriptTransfer(i.caller, value(betAmount), auctionPriceAsset)])) | |
87 | + | } | |
88 | + | ||
89 | + |
github/deemru/w8io/169f3d6 26.46 ms ◑