tx · 3kPjwHSdoq3L1w8q1y1ayWfjiH9StXL3KmK3qo5T21uJ

3MpBKNSG25gK9fufNK3tjRAnpD83mXYZog6:  -0.01400000 Waves

2019.10.05 11:56 [706226] smart account 3MpBKNSG25gK9fufNK3tjRAnpD83mXYZog6 > SELF 0.00000000 Waves

{ "type": 13, "id": "3kPjwHSdoq3L1w8q1y1ayWfjiH9StXL3KmK3qo5T21uJ", "fee": 1400000, "feeAssetId": null, "timestamp": 1570265725388, "version": 1, "sender": "3MpBKNSG25gK9fufNK3tjRAnpD83mXYZog6", "senderPublicKey": "qqthrzfmAnD7FmWuFzzm3FbACout7pi6sYqHmXc2Hyc", "proofs": [ "5nKH9WfQfBATRnhZeX5rV7PapWhH9DrN5upu68uYr8kL3tMtAZKyzEge2AoSBRQ9brrUGeUFMQ7AmMTfcFRGuZ39" ], "script": "base64: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cmVmQW1vdW50BQAAAAhjYXNoQmFjawAAAAAAAAAD6AkABEwAAAACCQEAAAAQYmFsYW5jZUluY3JlbWVudAAAAAMFAAAADXdpbm5lckFkZHJlc3MFAAAAEGJldEFzc2V0TW5lbW9uaWMFAAAAC2luY0Nhc2hCYWNrCQAETAAAAAIJAQAAABZyZWNvcmRCYWxhbmNlSW5jcmVtZW50AAAABwUAAAANd2lubmVyQWRkcmVzcwUAAAAQYmV0QXNzZXRNbmVtb25pYwUAAAANd2lubmVyQWRkcmVzcwUAAAALaW5jQ2FzaEJhY2sCAAAACGNhc2hiYWNrBQAAAAR0eGlkBQAAAAl0aW1lc3RhbXAJAARMAAAAAgkBAAAAEGJhbGFuY2VJbmNyZW1lbnQAAAADBQAAAARyZWYxBQAAABBiZXRBc3NldE1uZW1vbmljBQAAAAlpbmNMZXZlbDEJAARMAAAAAgkBAAAAFnJlY29yZEJhbGFuY2VJbmNyZW1lbnQAAAAHBQAAAARyZWYxBQAAABBiZXRBc3NldE1uZW1vbmljBQAAAA13aW5uZXJBZGRyZXNzBQAAAAlpbmNMZXZlbDECAAAAAmwxBQAAAAR0eGlkBQAAAAl0aW1lc3RhbXAEAAAAByRtYXRjaDEJAQAAAApnZXRSZWZlcmVyAAAAAQUAAAAEcmVmMQMJAAABAAAAAgUAAAAHJG1hdGNoMQIAAAAGU3RyaW5nBAAAAARyZWYyBQAAAAckbWF0Y2gxBAAAAAlpbmNMZXZlbDIJAABpAAAAAgkAAGgAAAACBQAAAAlyZWZBbW91bnQFAAAABmxldmVsMgAAAAAAAAAD6AkABEwAAAACCQEAAAAQYmFsYW5jZUluY3JlbWVudAAAAAMFAAAABHJlZjIFAAAAEGJldEFzc2V0TW5lbW9uaWMFAAAACWluY0xldmVsMgkABEwAAAACCQEAAAAWcmVjb3JkQmFsYW5jZUluY3JlbWVudAAAAAcFAAAABHJlZjIFAAAAEGJldEFzc2V0TW5lbW9uaWMFAAAADXdpbm5lckFkZHJlc3MFAAAACWluY0xldmVsMgIAAAACbDIFAAAABHR4aWQFAAAACXRpbWVzdGFtcAQAAAAHJG1hdGNoMgkBAAAACmdldFJlZmVyZXIAAAABBQAAAARyZWYyCQAETAAAAAIJAQAAABBiYWxhbmNlSW5jcmVtZW50AAAAAwUAAAANY2hhbmdlQWRkcmVzcwUAAAAQYmV0QXNzZXRNbmVtb25pYwkAAGUAAAACCQAAZQAAAAIFAAAACXJlZkFtb3VudAUAAAAJaW5jTGV2ZWwxBQAAAAlpbmNMZXZlbDIJAARMAAAAAgkBAAAAFnJlY29yZEJhbGFuY2VJbmNyZW1lbnQAAAAHBQAAAA1jaGFuZ2VBZGRyZXNzBQAAABBiZXRBc3NldE1uZW1vbmljBQAAAA13aW5uZXJBZGRyZXNzCQAAZQAAAAIJAABlAAAAAgUAAAAJcmVmQW1vdW50BQAAAAlpbmNMZXZlbDEFAAAACWluY0xldmVsMgIAAAAGY2hhbmdlBQAAAAR0eGlkBQAAAAl0aW1lc3RhbXAFAAAAA25pbAkABEwAAAACCQEAAAAQYmFsYW5jZUluY3JlbWVudAAAAAMFAAAADWNoYW5nZUFkZHJlc3MFAAAAEGJldEFzc2V0TW5lbW9uaWMJAABlAAAAAgUAAAAJcmVmQW1vdW50BQAAAAlpbmNMZXZlbDEJAARMAAAAAgkBAAAAFnJlY29yZEJhbGFuY2VJbmNyZW1lbnQAAAAHBQAAAA1jaGFuZ2VBZGRyZXNzBQAAABBiZXRBc3NldE1uZW1vbmljBQAAAA13aW5uZXJBZGRyZXNzCQAAZQAAAAIFAAAACXJlZkFtb3VudAUAAAAJaW5jTGV2ZWwxAgAAAAZjaGFuZ2UFAAAABHR4aWQFAAAACXRpbWVzdGFtcAUAAAADbmlsCQAETAAAAAIJAQAAABBiYWxhbmNlSW5jcmVtZW50AAAAAwUAAAANY2hhbmdlQWRkcmVzcwUAAAAQYmV0QXNzZXRNbmVtb25pYwUAAAAJcmVmQW1vdW50CQAETAAAAAIJAQAAABZyZWNvcmRCYWxhbmNlSW5jcmVtZW50AAAABwUAAAANY2hhbmdlQWRkcmVzcwUAAAAQYmV0QXNzZXRNbmVtb25pYwUAAAANd2lubmVyQWRkcmVzcwUAAAAJcmVmQW1vdW50AgAAAAZjaGFuZ2UFAAAABHR4aWQFAAAACXRpbWVzdGFtcAUAAAADbmlsCQEAAAALVHJhbnNmZXJTZXQAAAABCQAETAAAAAIJAQAAAA5TY3JpcHRUcmFuc2ZlcgAAAAMIBQAAAAFp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", "chainId": 84, "height": 706226, "spentComplexity": 0 } View: original | compacted Prev: ASMBngEuo9NLAjqWth2yJHqAqDq3Tmv6i9uCt6FEA6a7 Next: none Diff:
OldNewDifferences
11 {-# STDLIB_VERSION 3 #-}
22 {-# SCRIPT_TYPE ACCOUNT #-}
33 {-# CONTENT_TYPE DAPP #-}
4-let betAssetIds = [DataEntry("sportr", "AcTzTgW1QbJK4Qu6hCsUCLjpxUyD3dofv8xq2CAPbzKJ"), DataEntry("dex", "WAVES")]
4+let betAssetIds = [DataEntry("sportr", "Ft8X1v1LTa1ABafufpaCWyVj8KkaxUWE6xBhW6sNFJck"), DataEntry("dex", "WAVES")]
55
6-let dexAgent = addressFromStringValue("3NCN79gCSaKTFV6fDoEzELoxDMrAUTCekd5")
6+let betAssetMnemonics = [DataEntry("sportr", "USD"), DataEntry("dex", "WAVES")]
7+
8+let assetsDecimals = [DataEntry("USD", 2), DataEntry("WAVES", 8), DataEntry("BTC", 8), DataEntry("VST", 8)]
9+
10+let dexAgent = addressFromStringValue("3P69jzek6S2q7kGWScEEZaNxKL5TiSF7wGm")
11+
12+let changeAddress = "3PQexcLjbv9CsYbt6zKF7nfEppf2gKHCEkz"
713
814 let maxFee = 5000000
915
10-let minBet = 5000000
11-
1216 let minCreateEvent = 30000000
13-
14-let minMatchNominal = 5000000
1517
1618 let dexJudgeGraceTs = ((1000 * 60) * 5)
1719
2527
2628 let totalRef = 10
2729
28-let changeAddress = "3NAxsbHxS63azdAicVvLGsxjAQ9DsK6L4NL"
30+let minWithdraw = 1000000
2931
30-let minWithdraw = 1000000
32+func getAssetIdByName (name) = {
33+ let assetIdByName = [DataEntry("USD", base58'Ft8X1v1LTa1ABafufpaCWyVj8KkaxUWE6xBhW6sNFJck'), DataEntry("BTC", base58'8LQW8f7P5d5PZM7GtZEBgaqRPGSzS3DfPuiXrURJ4AJS'), DataEntry("VST", base58'4LHHvYGNKJUg5hj65aGD5vgScvCBmLpdRFtjokvCjSL8')]
34+ if ((name == "WAVES"))
35+ then unit
36+ else getBinaryValue(assetIdByName, name)
37+ }
38+
3139
3240 func getCallerByDelegate (i,function) = {
3341 let caller = toBase58String(i.caller.bytes)
7684 func getReferer (user) = getString(this, (user + "_referer"))
7785
7886
79-func balanceIncrement (address,inc) = {
80- let balanceKey = (address + "_balance")
87+func balanceIncrement (address,asset,inc) = {
88+ let balanceKey = (((address + "_") + asset) + "_balance")
8189 DataEntry(balanceKey, (inc + match getInteger(this, balanceKey) {
8290 case b: Int =>
8391 b
8795 }
8896
8997
90-func recordBalanceIncrement (address,owner,inc,level,txId,timestamp) = DataEntry(((("fund_" + address) + "_") + txId), ((((((owner + ":") + toString(inc)) + ":") + level) + ":") + timestamp))
98+func recordBalanceIncrement (address,asset,owner,inc,level,txId,timestamp) = DataEntry(((((((("fund_" + address) + "_") + asset) + "_") + level) + "_") + txId), ((((owner + ":") + toString(inc)) + ":") + timestamp))
9199
92100
93101 func getAssetByAdapter (adapter) = {
96104 then unit
97105 else fromBase58String(assetString)
98106 }
107+
108+
109+func getMinBetByAdapter (adapter) = getIntegerValue([DataEntry("dex", 5000000), DataEntry("sportr", 100)], adapter)
110+
111+
112+func getMinMatchByAdapter (adapter) = getIntegerValue([DataEntry("dex", 1000000), DataEntry("sportr", 1)], adapter)
99113
100114
101115 func getAssetMnemonic (asset) = if ((asset == unit))
107121 func bet (event,subevent,side,odds,adapter) = {
108122 let marketKey = ("market_" + event)
109123 let betAssetId = getAssetByAdapter(adapter)
124+ let betAssetMnemonic = getStringValue(betAssetIds, adapter)
110125 let owner = toBase58String(i.caller.bytes)
111126 let payment = extract(i.payment)
112127 if ((payment.assetId != betAssetId))
113- then throw(((("bet must be in asset " + getAssetMnemonic(betAssetId)) + " for adapter ") + adapter))
114- else if ((minBet > payment.amount))
115- then throw(("minimum allowed bet is " + toString(minBet)))
116- else if (if (if ((adapter == "dex"))
117- then (minCreateEvent > payment.amount)
118- else false)
119- then (getBoolean(this, marketKey) == unit)
120- else false)
121- then throw(("min amount to create first bet is " + toString(minCreateEvent)))
122- else if (if ((side != "for"))
123- then (side != "against")
128+ then throw(((("bet must be in asset " + betAssetMnemonic) + " for adapter ") + adapter))
129+ else {
130+ let minBet = getMinBetByAdapter(adapter)
131+ if ((minBet > payment.amount))
132+ then throw(("minimum allowed bet is " + toString(minBet)))
133+ else if (if (if ((adapter == "dex"))
134+ then (minCreateEvent > payment.amount)
124135 else false)
125- then throw("unexpected side")
126- else {
127- let betid = (((((("bet_" + event) + "_") + toBase58String(i.caller.bytes)) + "_") + take(toBase58String(i.transactionId), 5)) + "_")
128- let sequenceKey = ((("seq_" + event) + "_") + subevent)
129- let sequence = match getInteger(this, sequenceKey) {
130- case prevS: Int =>
131- (prevS + 1)
132- case _ =>
133- 1
134- }
135- repayFee(i, WriteSet([DataEntry((betid + "owner"), owner), DataEntry((betid + "event"), event), DataEntry((betid + "subevent"), subevent), DataEntry((betid + "side"), side), DataEntry((betid + "odds"), odds), DataEntry((betid + "amount"), payment.amount), DataEntry((betid + "spent"), 0), DataEntry((betid + "adapter"), adapter), DataEntry((betid + "timestamp"), lastBlock.timestamp), DataEntry((betid + "sequence"), sequence), DataEntry(marketKey, true), DataEntry(sequenceKey, sequence)]), 0, unit, unit)
136- }
136+ then (getBoolean(this, marketKey) == unit)
137+ else false)
138+ then throw(("min amount to create first bet is " + toString(minCreateEvent)))
139+ else if (if ((side != "for"))
140+ then (side != "against")
141+ else false)
142+ then throw("unexpected side")
143+ else {
144+ let betid = (((((("bet_" + event) + "_") + owner) + "_") + take(toBase58String(i.transactionId), 5)) + "_")
145+ let sequenceKey = ((("seq_" + event) + "_") + subevent)
146+ let sequence = match getInteger(this, sequenceKey) {
147+ case prevS: Int =>
148+ (prevS + 1)
149+ case _ =>
150+ 1
151+ }
152+ let commonRecords = [DataEntry((betid + "owner"), owner), DataEntry((betid + "event"), event), DataEntry((betid + "subevent"), subevent), DataEntry((betid + "side"), side), DataEntry((betid + "odds"), odds), DataEntry((betid + "amount"), payment.amount), DataEntry((betid + "spent"), 0), DataEntry((betid + "adapter"), adapter), DataEntry((betid + "timestamp"), lastBlock.timestamp), DataEntry((betid + "sequence"), sequence), DataEntry(sequenceKey, sequence)]
153+ let allRecords = if ((adapter == "dex"))
154+ then DataEntry(marketKey, true) :: commonRecords
155+ else commonRecords
156+ repayFee(i, WriteSet(allRecords), 0, unit, unit)
157+ }
158+ }
137159 }
138160
139161
165187 if ((adapter != getStringValue(this, (betAgainst + "_adapter"))))
166188 then throw("bets has different adapters")
167189 else {
190+ let minMatchNominal = getMinMatchByAdapter(adapter)
168191 let betForOwner = getStringValue(this, (betFor + "_owner"))
169192 let betAgainstOwner = getStringValue(this, (betAgainst + "_owner"))
170193 let eventid = extract(getString(this, (betFor + "_event")))
196219
197220
198221 @Callable(i)
199-func approveDefeat (betId,defeat) = if (if (isDefined(getInteger(this, (betId + "_defeat"))))
200- then (defeat == 2)
201- else false)
202- then throw("can't dispute after approve")
203- else if ((getString(this, (betId + "_owner")) != getCallerByDelegate(i, "autoapprove")))
204- then throw("must be owner or its delegate to approve")
205- else repayFee(i, WriteSet([DataEntry((betId + "_defeat"), defeat)]), 0, unit, unit)
222+func approveDefeat (betId,defeat) = {
223+ let a = extract(assetInfo(base58''))
224+ if (if (isDefined(getInteger(this, (betId + "_defeat"))))
225+ then (defeat == 2)
226+ else false)
227+ then throw("can't dispute after approve")
228+ else if ((getString(this, (betId + "_owner")) != getCallerByDelegate(i, "autoapprove")))
229+ then throw("must be owner or its delegate to approve")
230+ else repayFee(i, WriteSet([DataEntry((betId + "_defeat"), defeat)]), 0, unit, unit)
231+ }
206232
207233
208234
219245 let pair = eventS[1]
220246 let eventRate = parseIntValue(eventS[2])
221247 let eventTs = parseIntValue(eventS[3])
248+ let pairSplit = split(pair, "/")
249+ let pairDecimals1 = getIntegerValue(assetsDecimals, pairSplit[0])
250+ let pairDecimals2 = getIntegerValue(assetsDecimals, pairSplit[1])
251+ let rateCorrectionPower = (pairDecimals1 - pairDecimals2)
252+ let rateCorrection = if ((rateCorrectionPower == 0))
253+ then 1
254+ else if ((rateCorrectionPower == 1))
255+ then 10
256+ else if ((rateCorrectionPower == 2))
257+ then 100
258+ else if ((rateCorrectionPower == 3))
259+ then 1000
260+ else if ((rateCorrectionPower == 4))
261+ then 10000
262+ else if ((rateCorrectionPower == 5))
263+ then 100000
264+ else if ((rateCorrectionPower == 6))
265+ then 1000000
266+ else if ((rateCorrectionPower == 7))
267+ then 10000000
268+ else if ((rateCorrectionPower == 8))
269+ then 100000000
270+ else throw("unsupported")
222271 let judgeTs = ((eventTs * 1000) + dexJudgeGraceTs)
223272 if ((judgeTs > lastBlock.timestamp))
224273 then throw(("please wait for timestamp " + toString(judgeTs)))
227276 if (!(isDefined(rateFromAgent)))
228277 then throw("rate from agent not ready yet")
229278 else {
230- let looserBet = if ((extract(rateFromAgent) >= eventRate))
279+ let looserBet = if (((extract(rateFromAgent) * rateCorrection) > eventRate))
231280 then betAgainst
232281 else betFor
233282 repayFee(i, WriteSet([DataEntry((looserBet + "_defeat"), 1), DataEntry((matchId + "_judged"), true)]), 0, unit, unit)
240289 @Callable(i)
241290 func payWinner (matchId) = if (!(checkFee(i)))
242291 then throw("error")
243- else if (isDefined(getInteger(this, (matchId + "_paid"))))
292+ else if (isDefined(getString(this, (matchId + "_paid"))))
244293 then throw("already paid")
245294 else {
246295 let adapter = getStringValue(this, (matchId + "_adapter"))
247296 let betAssetId = getAssetByAdapter(adapter)
297+ let betAssetMnemonic = getStringValue(betAssetMnemonics, adapter)
248298 let betForId = extract(getString(this, (matchId + "_for")))
249299 let betAgainstId = extract(getString(this, (matchId + "_against")))
250300 let betForDefeated = isDefeated(betForId)
266316 case ref1: String =>
267317 let incLevel1 = ((refAmount * level1) / 1000)
268318 let incCashBack = ((refAmount * cashBack) / 1000)
269-[balanceIncrement(winnerAddress, incCashBack), recordBalanceIncrement(winnerAddress, winnerAddress, incCashBack, "cashback", txid, timestamp), balanceIncrement(ref1, incLevel1), recordBalanceIncrement(ref1, winnerAddress, incLevel1, "l1", txid, timestamp), match getReferer(ref1) {
319+[balanceIncrement(winnerAddress, betAssetMnemonic, incCashBack), recordBalanceIncrement(winnerAddress, betAssetMnemonic, winnerAddress, incCashBack, "cashback", txid, timestamp), balanceIncrement(ref1, betAssetMnemonic, incLevel1), recordBalanceIncrement(ref1, betAssetMnemonic, winnerAddress, incLevel1, "l1", txid, timestamp), match getReferer(ref1) {
270320 case ref2: String =>
271321 let incLevel2 = ((refAmount * level2) / 1000)
272-[balanceIncrement(ref2, incLevel2), recordBalanceIncrement(ref2, winnerAddress, incLevel2, "l2", txid, timestamp), match getReferer(ref2) {
273- case ref3: String =>
274- let incLevel3 = ((refAmount * level3) / 1000)
275-[balanceIncrement(ref3, incLevel3), recordBalanceIncrement(ref3, winnerAddress, incLevel3, "l3", txid, timestamp), balanceIncrement(changeAddress, (((refAmount - incLevel1) - incLevel2) - incLevel3)), recordBalanceIncrement(changeAddress, winnerAddress, (((refAmount - incLevel1) - incLevel2) - incLevel3), "change", txid, timestamp)]
322+[balanceIncrement(ref2, betAssetMnemonic, incLevel2), recordBalanceIncrement(ref2, betAssetMnemonic, winnerAddress, incLevel2, "l2", txid, timestamp), match getReferer(ref2) {
276323 case _ =>
277-[balanceIncrement(changeAddress, ((refAmount - incLevel1) - incLevel2)), recordBalanceIncrement(changeAddress, winnerAddress, ((refAmount - incLevel1) - incLevel2), "change", txid, timestamp)]
324+[balanceIncrement(changeAddress, betAssetMnemonic, ((refAmount - incLevel1) - incLevel2)), recordBalanceIncrement(changeAddress, betAssetMnemonic, winnerAddress, ((refAmount - incLevel1) - incLevel2), "change", txid, timestamp)]
278325 }]
279326 case _ =>
280-[balanceIncrement(changeAddress, (refAmount - incLevel1)), recordBalanceIncrement(changeAddress, winnerAddress, (refAmount - incLevel1), "change", txid, timestamp)]
327+[balanceIncrement(changeAddress, betAssetMnemonic, (refAmount - incLevel1)), recordBalanceIncrement(changeAddress, betAssetMnemonic, winnerAddress, (refAmount - incLevel1), "change", txid, timestamp)]
281328 }]
282329 case _ =>
283-[balanceIncrement(changeAddress, refAmount), recordBalanceIncrement(changeAddress, winnerAddress, refAmount, "change", txid, timestamp)]
330+[balanceIncrement(changeAddress, betAssetMnemonic, refAmount), recordBalanceIncrement(changeAddress, betAssetMnemonic, winnerAddress, refAmount, "change", txid, timestamp)]
284331 }]), TransferSet([ScriptTransfer(i.caller, i.fee, unit), ScriptTransfer(addressFromStringValue(winnerAddress), (amount - refAmount), betAssetId)]))
285332 }
286333 }
293340
294341
295342 @Callable(i)
296-func withdraw () = if (!(checkFee(i)))
343+func withdraw (assetName) = if (!(checkFee(i)))
297344 then throw("error")
298345 else {
299346 let owner = toBase58String(i.caller.bytes)
300347 let txid = toBase58String(i.transactionId)
301- let balanceKey = (owner + "_balance")
348+ let balanceKey = (((owner + "_") + assetName) + "_balance")
349+ let assetId = getAssetIdByName(assetName)
302350 let amount = match getInteger(this, balanceKey) {
303351 case b: Int =>
304352 b
307355 }
308356 if ((minWithdraw > amount))
309357 then throw(("Min withdraw amount is" + toString(minWithdraw)))
310- else ScriptResult(WriteSet([DataEntry(balanceKey, 0), DataEntry(((("withdraw_" + owner) + "_") + txid), ((toString(amount) + ":") + toString(lastBlock.timestamp)))]), TransferSet([ScriptTransfer(i.caller, (amount + i.fee), unit)]))
358+ else ScriptResult(WriteSet([DataEntry(balanceKey, 0), DataEntry(((("withdraw_" + owner) + "_") + txid), ((((toString(amount) + ":") + toString(lastBlock.timestamp)) + ":") + assetName))]), TransferSet([ScriptTransfer(i.caller, i.fee, unit), ScriptTransfer(i.caller, amount, assetId)]))
311359 }
312360
313361
Full:
OldNewDifferences
11 {-# STDLIB_VERSION 3 #-}
22 {-# SCRIPT_TYPE ACCOUNT #-}
33 {-# CONTENT_TYPE DAPP #-}
4-let betAssetIds = [DataEntry("sportr", "AcTzTgW1QbJK4Qu6hCsUCLjpxUyD3dofv8xq2CAPbzKJ"), DataEntry("dex", "WAVES")]
4+let betAssetIds = [DataEntry("sportr", "Ft8X1v1LTa1ABafufpaCWyVj8KkaxUWE6xBhW6sNFJck"), DataEntry("dex", "WAVES")]
55
6-let dexAgent = addressFromStringValue("3NCN79gCSaKTFV6fDoEzELoxDMrAUTCekd5")
6+let betAssetMnemonics = [DataEntry("sportr", "USD"), DataEntry("dex", "WAVES")]
7+
8+let assetsDecimals = [DataEntry("USD", 2), DataEntry("WAVES", 8), DataEntry("BTC", 8), DataEntry("VST", 8)]
9+
10+let dexAgent = addressFromStringValue("3P69jzek6S2q7kGWScEEZaNxKL5TiSF7wGm")
11+
12+let changeAddress = "3PQexcLjbv9CsYbt6zKF7nfEppf2gKHCEkz"
713
814 let maxFee = 5000000
915
10-let minBet = 5000000
11-
1216 let minCreateEvent = 30000000
13-
14-let minMatchNominal = 5000000
1517
1618 let dexJudgeGraceTs = ((1000 * 60) * 5)
1719
1820 let level1 = 250
1921
2022 let level2 = 150
2123
2224 let level3 = 100
2325
2426 let cashBack = 500
2527
2628 let totalRef = 10
2729
28-let changeAddress = "3NAxsbHxS63azdAicVvLGsxjAQ9DsK6L4NL"
30+let minWithdraw = 1000000
2931
30-let minWithdraw = 1000000
32+func getAssetIdByName (name) = {
33+ let assetIdByName = [DataEntry("USD", base58'Ft8X1v1LTa1ABafufpaCWyVj8KkaxUWE6xBhW6sNFJck'), DataEntry("BTC", base58'8LQW8f7P5d5PZM7GtZEBgaqRPGSzS3DfPuiXrURJ4AJS'), DataEntry("VST", base58'4LHHvYGNKJUg5hj65aGD5vgScvCBmLpdRFtjokvCjSL8')]
34+ if ((name == "WAVES"))
35+ then unit
36+ else getBinaryValue(assetIdByName, name)
37+ }
38+
3139
3240 func getCallerByDelegate (i,function) = {
3341 let caller = toBase58String(i.caller.bytes)
3442 let callerPrefix = ("delegate_" + caller)
3543 if ((getBoolean(this, ((callerPrefix + "_") + function)) == true))
3644 then match getString(this, (callerPrefix + "_address")) {
3745 case s: String =>
3846 s
3947 case _ =>
4048 caller
4149 }
4250 else caller
4351 }
4452
4553
4654 func checkFee (i) = if ((i.fee > maxFee))
4755 then throw("unreasonable large fee")
4856 else if ((i.feeAssetId != unit))
4957 then throw("fee must be in WAVES")
5058 else true
5159
5260
5361 func repayFee (i,ws,amount,recipient,betAssetId) = if (checkFee(i))
5462 then if (if ((amount > 0))
5563 then isDefined(recipient)
5664 else false)
5765 then ScriptResult(ws, TransferSet([ScriptTransfer(i.caller, i.fee, unit), ScriptTransfer(extract(recipient), amount, betAssetId)]))
5866 else ScriptResult(ws, TransferSet([ScriptTransfer(i.caller, i.fee, unit)]))
5967 else throw("error")
6068
6169
6270 func checkOwner (i,betid) = (getString(this, (betid + "_owner")) == toBase58String(i.caller.bytes))
6371
6472
6573 func getUnspent (betid) = (extract(getInteger(this, (betid + "_amount"))) - extract(getInteger(this, (betid + "_spent"))))
6674
6775
6876 func isDefeated (betid) = {
6977 let defeatStatus = getInteger(this, (betid + "_defeat"))
7078 if ((defeatStatus == 1))
7179 then true
7280 else (defeatStatus == 3)
7381 }
7482
7583
7684 func getReferer (user) = getString(this, (user + "_referer"))
7785
7886
79-func balanceIncrement (address,inc) = {
80- let balanceKey = (address + "_balance")
87+func balanceIncrement (address,asset,inc) = {
88+ let balanceKey = (((address + "_") + asset) + "_balance")
8189 DataEntry(balanceKey, (inc + match getInteger(this, balanceKey) {
8290 case b: Int =>
8391 b
8492 case _ =>
8593 0
8694 }))
8795 }
8896
8997
90-func recordBalanceIncrement (address,owner,inc,level,txId,timestamp) = DataEntry(((("fund_" + address) + "_") + txId), ((((((owner + ":") + toString(inc)) + ":") + level) + ":") + timestamp))
98+func recordBalanceIncrement (address,asset,owner,inc,level,txId,timestamp) = DataEntry(((((((("fund_" + address) + "_") + asset) + "_") + level) + "_") + txId), ((((owner + ":") + toString(inc)) + ":") + timestamp))
9199
92100
93101 func getAssetByAdapter (adapter) = {
94102 let assetString = getStringValue(betAssetIds, adapter)
95103 if ((assetString == "WAVES"))
96104 then unit
97105 else fromBase58String(assetString)
98106 }
107+
108+
109+func getMinBetByAdapter (adapter) = getIntegerValue([DataEntry("dex", 5000000), DataEntry("sportr", 100)], adapter)
110+
111+
112+func getMinMatchByAdapter (adapter) = getIntegerValue([DataEntry("dex", 1000000), DataEntry("sportr", 1)], adapter)
99113
100114
101115 func getAssetMnemonic (asset) = if ((asset == unit))
102116 then "WAVES"
103117 else toBase58String(extract(asset))
104118
105119
106120 @Callable(i)
107121 func bet (event,subevent,side,odds,adapter) = {
108122 let marketKey = ("market_" + event)
109123 let betAssetId = getAssetByAdapter(adapter)
124+ let betAssetMnemonic = getStringValue(betAssetIds, adapter)
110125 let owner = toBase58String(i.caller.bytes)
111126 let payment = extract(i.payment)
112127 if ((payment.assetId != betAssetId))
113- then throw(((("bet must be in asset " + getAssetMnemonic(betAssetId)) + " for adapter ") + adapter))
114- else if ((minBet > payment.amount))
115- then throw(("minimum allowed bet is " + toString(minBet)))
116- else if (if (if ((adapter == "dex"))
117- then (minCreateEvent > payment.amount)
118- else false)
119- then (getBoolean(this, marketKey) == unit)
120- else false)
121- then throw(("min amount to create first bet is " + toString(minCreateEvent)))
122- else if (if ((side != "for"))
123- then (side != "against")
128+ then throw(((("bet must be in asset " + betAssetMnemonic) + " for adapter ") + adapter))
129+ else {
130+ let minBet = getMinBetByAdapter(adapter)
131+ if ((minBet > payment.amount))
132+ then throw(("minimum allowed bet is " + toString(minBet)))
133+ else if (if (if ((adapter == "dex"))
134+ then (minCreateEvent > payment.amount)
124135 else false)
125- then throw("unexpected side")
126- else {
127- let betid = (((((("bet_" + event) + "_") + toBase58String(i.caller.bytes)) + "_") + take(toBase58String(i.transactionId), 5)) + "_")
128- let sequenceKey = ((("seq_" + event) + "_") + subevent)
129- let sequence = match getInteger(this, sequenceKey) {
130- case prevS: Int =>
131- (prevS + 1)
132- case _ =>
133- 1
134- }
135- repayFee(i, WriteSet([DataEntry((betid + "owner"), owner), DataEntry((betid + "event"), event), DataEntry((betid + "subevent"), subevent), DataEntry((betid + "side"), side), DataEntry((betid + "odds"), odds), DataEntry((betid + "amount"), payment.amount), DataEntry((betid + "spent"), 0), DataEntry((betid + "adapter"), adapter), DataEntry((betid + "timestamp"), lastBlock.timestamp), DataEntry((betid + "sequence"), sequence), DataEntry(marketKey, true), DataEntry(sequenceKey, sequence)]), 0, unit, unit)
136- }
136+ then (getBoolean(this, marketKey) == unit)
137+ else false)
138+ then throw(("min amount to create first bet is " + toString(minCreateEvent)))
139+ else if (if ((side != "for"))
140+ then (side != "against")
141+ else false)
142+ then throw("unexpected side")
143+ else {
144+ let betid = (((((("bet_" + event) + "_") + owner) + "_") + take(toBase58String(i.transactionId), 5)) + "_")
145+ let sequenceKey = ((("seq_" + event) + "_") + subevent)
146+ let sequence = match getInteger(this, sequenceKey) {
147+ case prevS: Int =>
148+ (prevS + 1)
149+ case _ =>
150+ 1
151+ }
152+ let commonRecords = [DataEntry((betid + "owner"), owner), DataEntry((betid + "event"), event), DataEntry((betid + "subevent"), subevent), DataEntry((betid + "side"), side), DataEntry((betid + "odds"), odds), DataEntry((betid + "amount"), payment.amount), DataEntry((betid + "spent"), 0), DataEntry((betid + "adapter"), adapter), DataEntry((betid + "timestamp"), lastBlock.timestamp), DataEntry((betid + "sequence"), sequence), DataEntry(sequenceKey, sequence)]
153+ let allRecords = if ((adapter == "dex"))
154+ then DataEntry(marketKey, true) :: commonRecords
155+ else commonRecords
156+ repayFee(i, WriteSet(allRecords), 0, unit, unit)
157+ }
158+ }
137159 }
138160
139161
140162
141163 @Callable(i)
142164 func cancel (betid) = if ((getString(this, (betid + "_owner")) != getCallerByDelegate(i, "autocancel")))
143165 then throw("must be owner or its delegate to cancel")
144166 else {
145167 let adapter = getStringValue(this, (betid + "_adapter"))
146168 let amount = getIntegerValue(this, (betid + "_amount"))
147169 let spent = getIntegerValue(this, (betid + "_spent"))
148170 let cancelAmount = (amount - spent)
149171 repayFee(i, WriteSet([DataEntry((betid + "_spent"), amount), DataEntry((betid + "_cancel_amount"), cancelAmount), DataEntry((betid + "_cancel_tx"), toBase58String(i.transactionId))]), cancelAmount, i.caller, getAssetByAdapter(adapter))
150172 }
151173
152174
153175
154176 @Callable(i)
155177 func betMatch (betFor,betAgainst) = if ((getString(this, (betFor + "_event")) != getString(this, (betAgainst + "_event"))))
156178 then throw("event id must match")
157179 else if ((getString(this, (betFor + "_subevent")) != getString(this, (betAgainst + "_subevent"))))
158180 then throw("subevent id must match")
159181 else if ((getString(this, (betFor + "_side")) != "for"))
160182 then throw("invalid *for* bet")
161183 else if ((getString(this, (betAgainst + "_side")) != "against"))
162184 then throw("invalid *against* bet")
163185 else {
164186 let adapter = getStringValue(this, (betFor + "_adapter"))
165187 if ((adapter != getStringValue(this, (betAgainst + "_adapter"))))
166188 then throw("bets has different adapters")
167189 else {
190+ let minMatchNominal = getMinMatchByAdapter(adapter)
168191 let betForOwner = getStringValue(this, (betFor + "_owner"))
169192 let betAgainstOwner = getStringValue(this, (betAgainst + "_owner"))
170193 let eventid = extract(getString(this, (betFor + "_event")))
171194 let matchid = (((((((("match_" + eventid) + "_") + takeRight(betForOwner, 10)) + "_") + takeRight(betAgainstOwner, 10)) + "_") + takeRight(toBase58String(i.transactionId), 10)) + "_")
172195 let odds = extract(getInteger(this, (betFor + "_odds")))
173196 let unspentFor = getUnspent(betFor)
174197 let unspentAgainst = getUnspent(betAgainst)
175198 let maxForNominal = unspentFor
176199 let maxAgainstNominal = ((unspentAgainst * 100) / (odds - 100))
177200 let matchNominal = if ((maxForNominal > maxAgainstNominal))
178201 then maxAgainstNominal
179202 else maxForNominal
180203 if ((minMatchNominal > matchNominal))
181204 then throw("match nominal too low")
182205 else {
183206 let spendFor = matchNominal
184207 let spendAgainst = ((matchNominal * (odds - 100)) / 100)
185208 let newSpentFor = (spendFor + extract(getInteger(this, (betFor + "_spent"))))
186209 let newSpentAgainst = (spendAgainst + extract(getInteger(this, (betAgainst + "_spent"))))
187210 if ((newSpentFor > extract(getInteger(this, (betFor + "_amount")))))
188211 then throw("invalid match")
189212 else if ((newSpentAgainst > extract(getInteger(this, (betAgainst + "_amount")))))
190213 then throw("invalid match")
191214 else repayFee(i, WriteSet([DataEntry((matchid + "amount"), (spendFor + spendAgainst)), DataEntry((matchid + "adapter"), adapter), DataEntry((matchid + "for"), betFor), DataEntry((matchid + "against"), betAgainst), DataEntry((betFor + "_spent"), newSpentFor), DataEntry((betAgainst + "_spent"), newSpentAgainst)]), 0, unit, unit)
192215 }
193216 }
194217 }
195218
196219
197220
198221 @Callable(i)
199-func approveDefeat (betId,defeat) = if (if (isDefined(getInteger(this, (betId + "_defeat"))))
200- then (defeat == 2)
201- else false)
202- then throw("can't dispute after approve")
203- else if ((getString(this, (betId + "_owner")) != getCallerByDelegate(i, "autoapprove")))
204- then throw("must be owner or its delegate to approve")
205- else repayFee(i, WriteSet([DataEntry((betId + "_defeat"), defeat)]), 0, unit, unit)
222+func approveDefeat (betId,defeat) = {
223+ let a = extract(assetInfo(base58''))
224+ if (if (isDefined(getInteger(this, (betId + "_defeat"))))
225+ then (defeat == 2)
226+ else false)
227+ then throw("can't dispute after approve")
228+ else if ((getString(this, (betId + "_owner")) != getCallerByDelegate(i, "autoapprove")))
229+ then throw("must be owner or its delegate to approve")
230+ else repayFee(i, WriteSet([DataEntry((betId + "_defeat"), defeat)]), 0, unit, unit)
231+ }
206232
207233
208234
209235 @Callable(i)
210236 func judgeDexMatch (matchId) = if ((getStringValue(this, (matchId + "_adapter")) != "dex"))
211237 then throw("method is only for dex matches")
212238 else if ((getBoolean(this, (matchId + "_judged")) == true))
213239 then throw("already judged")
214240 else {
215241 let betFor = getStringValue(this, (matchId + "_for"))
216242 let betAgainst = getStringValue(this, (matchId + "_against"))
217243 let event = getStringValue(this, (betFor + "_event"))
218244 let eventS = split(event, ":")
219245 let pair = eventS[1]
220246 let eventRate = parseIntValue(eventS[2])
221247 let eventTs = parseIntValue(eventS[3])
248+ let pairSplit = split(pair, "/")
249+ let pairDecimals1 = getIntegerValue(assetsDecimals, pairSplit[0])
250+ let pairDecimals2 = getIntegerValue(assetsDecimals, pairSplit[1])
251+ let rateCorrectionPower = (pairDecimals1 - pairDecimals2)
252+ let rateCorrection = if ((rateCorrectionPower == 0))
253+ then 1
254+ else if ((rateCorrectionPower == 1))
255+ then 10
256+ else if ((rateCorrectionPower == 2))
257+ then 100
258+ else if ((rateCorrectionPower == 3))
259+ then 1000
260+ else if ((rateCorrectionPower == 4))
261+ then 10000
262+ else if ((rateCorrectionPower == 5))
263+ then 100000
264+ else if ((rateCorrectionPower == 6))
265+ then 1000000
266+ else if ((rateCorrectionPower == 7))
267+ then 10000000
268+ else if ((rateCorrectionPower == 8))
269+ then 100000000
270+ else throw("unsupported")
222271 let judgeTs = ((eventTs * 1000) + dexJudgeGraceTs)
223272 if ((judgeTs > lastBlock.timestamp))
224273 then throw(("please wait for timestamp " + toString(judgeTs)))
225274 else {
226275 let rateFromAgent = getInteger(dexAgent, (event + "_rate"))
227276 if (!(isDefined(rateFromAgent)))
228277 then throw("rate from agent not ready yet")
229278 else {
230- let looserBet = if ((extract(rateFromAgent) >= eventRate))
279+ let looserBet = if (((extract(rateFromAgent) * rateCorrection) > eventRate))
231280 then betAgainst
232281 else betFor
233282 repayFee(i, WriteSet([DataEntry((looserBet + "_defeat"), 1), DataEntry((matchId + "_judged"), true)]), 0, unit, unit)
234283 }
235284 }
236285 }
237286
238287
239288
240289 @Callable(i)
241290 func payWinner (matchId) = if (!(checkFee(i)))
242291 then throw("error")
243- else if (isDefined(getInteger(this, (matchId + "_paid"))))
292+ else if (isDefined(getString(this, (matchId + "_paid"))))
244293 then throw("already paid")
245294 else {
246295 let adapter = getStringValue(this, (matchId + "_adapter"))
247296 let betAssetId = getAssetByAdapter(adapter)
297+ let betAssetMnemonic = getStringValue(betAssetMnemonics, adapter)
248298 let betForId = extract(getString(this, (matchId + "_for")))
249299 let betAgainstId = extract(getString(this, (matchId + "_against")))
250300 let betForDefeated = isDefeated(betForId)
251301 let betAgainstDefeated = isDefeated(betAgainstId)
252302 if (if (!(betForDefeated))
253303 then !(betAgainstDefeated)
254304 else false)
255305 then throw("no side approved defeat")
256306 else {
257307 let winnerId = if (betForDefeated)
258308 then betAgainstId
259309 else betForId
260310 let winnerAddress = getStringValue(this, (winnerId + "_owner"))
261311 let amount = extract(getInteger(this, (matchId + "_amount")))
262312 let txid = toBase58String(i.transactionId)
263313 let timestamp = toString(lastBlock.timestamp)
264314 let refAmount = ((amount * totalRef) / 1000)
265315 ScriptResult(WriteSet([DataEntry((matchId + "_paid"), toBase58String(i.transactionId)), match getReferer(winnerAddress) {
266316 case ref1: String =>
267317 let incLevel1 = ((refAmount * level1) / 1000)
268318 let incCashBack = ((refAmount * cashBack) / 1000)
269-[balanceIncrement(winnerAddress, incCashBack), recordBalanceIncrement(winnerAddress, winnerAddress, incCashBack, "cashback", txid, timestamp), balanceIncrement(ref1, incLevel1), recordBalanceIncrement(ref1, winnerAddress, incLevel1, "l1", txid, timestamp), match getReferer(ref1) {
319+[balanceIncrement(winnerAddress, betAssetMnemonic, incCashBack), recordBalanceIncrement(winnerAddress, betAssetMnemonic, winnerAddress, incCashBack, "cashback", txid, timestamp), balanceIncrement(ref1, betAssetMnemonic, incLevel1), recordBalanceIncrement(ref1, betAssetMnemonic, winnerAddress, incLevel1, "l1", txid, timestamp), match getReferer(ref1) {
270320 case ref2: String =>
271321 let incLevel2 = ((refAmount * level2) / 1000)
272-[balanceIncrement(ref2, incLevel2), recordBalanceIncrement(ref2, winnerAddress, incLevel2, "l2", txid, timestamp), match getReferer(ref2) {
273- case ref3: String =>
274- let incLevel3 = ((refAmount * level3) / 1000)
275-[balanceIncrement(ref3, incLevel3), recordBalanceIncrement(ref3, winnerAddress, incLevel3, "l3", txid, timestamp), balanceIncrement(changeAddress, (((refAmount - incLevel1) - incLevel2) - incLevel3)), recordBalanceIncrement(changeAddress, winnerAddress, (((refAmount - incLevel1) - incLevel2) - incLevel3), "change", txid, timestamp)]
322+[balanceIncrement(ref2, betAssetMnemonic, incLevel2), recordBalanceIncrement(ref2, betAssetMnemonic, winnerAddress, incLevel2, "l2", txid, timestamp), match getReferer(ref2) {
276323 case _ =>
277-[balanceIncrement(changeAddress, ((refAmount - incLevel1) - incLevel2)), recordBalanceIncrement(changeAddress, winnerAddress, ((refAmount - incLevel1) - incLevel2), "change", txid, timestamp)]
324+[balanceIncrement(changeAddress, betAssetMnemonic, ((refAmount - incLevel1) - incLevel2)), recordBalanceIncrement(changeAddress, betAssetMnemonic, winnerAddress, ((refAmount - incLevel1) - incLevel2), "change", txid, timestamp)]
278325 }]
279326 case _ =>
280-[balanceIncrement(changeAddress, (refAmount - incLevel1)), recordBalanceIncrement(changeAddress, winnerAddress, (refAmount - incLevel1), "change", txid, timestamp)]
327+[balanceIncrement(changeAddress, betAssetMnemonic, (refAmount - incLevel1)), recordBalanceIncrement(changeAddress, betAssetMnemonic, winnerAddress, (refAmount - incLevel1), "change", txid, timestamp)]
281328 }]
282329 case _ =>
283-[balanceIncrement(changeAddress, refAmount), recordBalanceIncrement(changeAddress, winnerAddress, refAmount, "change", txid, timestamp)]
330+[balanceIncrement(changeAddress, betAssetMnemonic, refAmount), recordBalanceIncrement(changeAddress, betAssetMnemonic, winnerAddress, refAmount, "change", txid, timestamp)]
284331 }]), TransferSet([ScriptTransfer(i.caller, i.fee, unit), ScriptTransfer(addressFromStringValue(winnerAddress), (amount - refAmount), betAssetId)]))
285332 }
286333 }
287334
288335
289336
290337 @Callable(i)
291338 func delegate (delegateTo,enableAutoApprove,enableAutoCancel) = repayFee(i, WriteSet([DataEntry((("delegate_" + delegateTo) + "_address"), toBase58String(i.caller.bytes)), DataEntry((("delegate_" + delegateTo) + "_autoapprove"), enableAutoApprove), DataEntry((("delegate_" + delegateTo) + "_autocancel"), enableAutoCancel)]), 0, unit, unit)
292339
293340
294341
295342 @Callable(i)
296-func withdraw () = if (!(checkFee(i)))
343+func withdraw (assetName) = if (!(checkFee(i)))
297344 then throw("error")
298345 else {
299346 let owner = toBase58String(i.caller.bytes)
300347 let txid = toBase58String(i.transactionId)
301- let balanceKey = (owner + "_balance")
348+ let balanceKey = (((owner + "_") + assetName) + "_balance")
349+ let assetId = getAssetIdByName(assetName)
302350 let amount = match getInteger(this, balanceKey) {
303351 case b: Int =>
304352 b
305353 case _ =>
306354 0
307355 }
308356 if ((minWithdraw > amount))
309357 then throw(("Min withdraw amount is" + toString(minWithdraw)))
310- else ScriptResult(WriteSet([DataEntry(balanceKey, 0), DataEntry(((("withdraw_" + owner) + "_") + txid), ((toString(amount) + ":") + toString(lastBlock.timestamp)))]), TransferSet([ScriptTransfer(i.caller, (amount + i.fee), unit)]))
358+ else ScriptResult(WriteSet([DataEntry(balanceKey, 0), DataEntry(((("withdraw_" + owner) + "_") + txid), ((((toString(amount) + ":") + toString(lastBlock.timestamp)) + ":") + assetName))]), TransferSet([ScriptTransfer(i.caller, i.fee, unit), ScriptTransfer(i.caller, amount, assetId)]))
311359 }
312360
313361
314362
315363 @Callable(i)
316364 func register (referer,salt) = if (!(checkFee(i)))
317365 then throw("error")
318366 else if ((take(toBase58String(i.transactionId), 3) != "123"))
319367 then throw("proof of work failed")
320368 else {
321369 let owner = toBase58String(i.caller.bytes)
322370 if (isDefined(getReferer(owner)))
323371 then throw("Already registered")
324372 else ScriptResult(WriteSet([DataEntry((owner + "_referer"), referer), DataEntry(((referer + "_referral_") + owner), owner)]), TransferSet([ScriptTransfer(i.caller, i.fee, unit)]))
325373 }
326374
327375

github/deemru/w8io/873ac7e 
77.09 ms