Settings

Blockchain
Network
Unit
Language
Theme
Sound New Block

Transaction

7961e1da1def4a69b828d15e1f68f17de3501e780d0643088c6c36cc9188e470
Timestamp (utc)
2021-01-13 05:41:42
Fee Paid
0.00010069 BSV
(
0.00981443 BSV
-
0.00971374 BSV
)
Fee Rate
503 sat/KB
Version
1
Confirmations
329,046
Size Stats
19,998 B

3 Outputs

Total Output:
0.00971374 BSV
  • jrun cryptofightsMM{"in":0,"ref":["b3878396e872bf4dcffc6df0f4f1469a63a970182fe40c9020630f83c2c21211_o1","419f1a052198cc8464d9816e33c6ea9ec9025877b5a521df626fdab97a15fc2a_o1","e73a45bf61cd8249622cabb0ccc44da0df7b66e731b33a6ea5a22b42755ceb45_o1","8e5d26176cc5ce4f2734e2fb8abb6da01237f5c1736839adf8a82659ecbe85d6_o1","084a81d9fd6c8b96af656f9c4e673f3fcdc0994c0bd1f5bcfb8a7d03c3365ade_o1","456f45e2cc4ecce7373debce7d057173e57f6a54953d3e6144ec419b0f620dde_o1","247085e2de0e644d9ff393ba53169214fdf883895e6a1fb0c4c0b822c65055aa_o1","7c46788235187dfd3eb37da3dade340c174a814dfca7d32a046fd1462f73146e_o1","b2b0e743dafe40297dea5609ffe42516e082a04d6fef28fc03e14f4326162c97_o1","a9644e60148a55d866661bef56710b5cd868c6d697c608d6317057f5ecb25126_o1","d5ac3e5b41a47e60850f1a2e03521437f87da56fc60d73321f806f5e5ed92c37_o1","0e660dbbccc19c47e388dbd07832821da0083f020eced33c3f49d195df5427bc_o1","bc3574b008e372b4409ae7307bb8f0b34e1733cc1e78125276e2d17f89fef532_o1","507b9cffbeb94c2386e054f3d63617f7b091e0bdb1619a99c3cd04e309be45f3_o1","b61ceaab3690af8868f1b305d30b9ea37688681de59732610963091f7b8277da_o1","91e2e3cb089c8342223d40abadab69c7fdc192e146a7a33c4638515159bc0b7d_o1","b2ddb06a8acf4f6e7f594d812982ef6a924390d6e898a83140aa58606b1d2d05_o1","0914ede5c23bf9350fe3061ad9771fa26ef9f096b413fe1a1490aece5668ebad_o1","e15600b32e3d62b0d91c0f223402a8ea83525a77f2614ab2d148489bd6c2b243_o1","589e107231b17445bc648ace9e1a852d201409a3ed42280c3f5ff4033c1a28ac_o1","f4ff4bd972453ce31c95279e406b6737c19a86285a51235885025a98442884f2_o1","6137cc92426478e6aab4036389d43942774e2b4b707ffb3e5bd6edfeb5f759fb_o1","b601856681efd2f6934171e216e82d589a8be59a011336c1682b4127f688dd93_o1"],"out":["57a3bf51d74f5911103cb6d9da2c9d25337e07aafa1e19d1f9eb2c9cc5680a6e"],"del":[],"cre":["n2Bd4cWhEQK1aVjb1R7EBGV9mrw3etvSdC"],"exec":[{"op":"DEPLOY","data":["class ClientAgent extends Agent {\n async init() {\n this.jigHandlers.set(Fighter.origin, this.onFighter);\n this.jigHandlers.set(KronoCoin.origin, this.onKronoCoin);\n this.jigHandlers.set(KronoItem.origin, this.onItem);\n\n this.messageHandlers.set('BattleUpdated', this.onBattleUpdated);\n this.messageHandlers.set('BattleCompleted', this.onBattleCompleted);\n this.messageHandlers.set('ActionError', this.onActionError);\n this.messageHandlers.set('ExitQueue', this._exitQueue);\n this.messageHandlers.set('SignBattle', this.signBattle);\n this.messageHandlers.set('QueueStatus', this._queueStatus);\n\n this.eventHandlers.set('Act', this.Act);\n this.eventHandlers.set('CreateFighter', this.CreateFighter);\n this.eventHandlers.set('EnterQueue', this.EnterQueue);\n this.eventHandlers.set('ExitQueue', this.ExitQueue);\n this.eventHandlers.set('Forfeit', this.Forfeit);\n this.eventHandlers.set('GetFighters', this.GetFighters);\n this.eventHandlers.set('SimulateFighterState', this.SimulateFighterState);\n this.eventHandlers.set('SimulateFightersState', this.SimulateFightersState);\n this.eventHandlers.set('GetItems', this.GetItems);\n this.eventHandlers.set('GetPlayer', this.GetPlayer);\n this.eventHandlers.set('GetBattles', this.GetBattles);\n this.eventHandlers.set('GetBattleHistory', this.GetBattleHistory);\n this.eventHandlers.set('LevelUp', this.LevelUp);\n this.eventHandlers.set('RedeemItem', this.RedeemItem);\n\n const lock = new CoinLock(this.wallet.ownerPair.pubKey.toString());\n this.coinScript = lock.script();\n console.log('COIN SCRIPT:', this.coinScript);\n\n this._battles = new Map();\n this._fighters = new Map();\n this._items = new Map();\n this.emit('subscribe', 'QueueStatus');\n // this.wallet.setTimeout(() => this.recordVictories(), 0);\n // this.wallet.setTimeout(() => this.dispose(), 0);\n }\n\n onFighter(fighter) {\n this._fighters.set(fighter.origin, fighter.toObject());\n this.emit('client', 'FighterUpdated', this._fighters.get(fighter.origin));\n }\n\n onItem(item) {\n this._items.set(item.origin, item.toObject());\n this.emit('client', 'ItemUpdated', this._items.get(item.origin));\n }\n\n async onKronoCoin() {\n this.emit('client', 'BalanceUpdated', await this.getBalance());\n }\n\n onBattleCreated(message) {\n this.emit('client', 'BattleCreated', message.payloadObj);\n }\n\n _queueStatus(message) {\n this.emit('client', 'QueueStatus', message.payload);\n }\n\n async onBattleUpdated(message) {\n const { location, battleId, turnCount } = message.payloadObj;\n if (battleId === this.currentId) {\n delete this.queueMessage;\n } \n else {\n console.log('Simultaneous battle');\n return;\n }\n\n if(turnCount > this.turnCount) {\n this.turnCount = turnCount;\n this.emit('client', 'BattleUpdated', message.payloadObj);\n this.battle = this.wallet.loadJig(location);\n }\n }\n\n async onBattleCompleted(message) {\n const { location, battleId } = message.payloadObj;\n const battle = await this.wallet.loadJig(location);\n this._battles.set(battle.origin, battle.toObject());\n \n if (battleId === this.currentId) {\n this.emit('client', 'BattleUpdated', battle.getState());\n this.emit('client', 'BattleCompleted', this._battles.get(battle.origin));\n delete this.battle;\n delete this.currentId;\n }\n\n if(battle.owner === this.address) {\n const { fighter } = battle.victor;\n fighter.recordVictory(battle);\n await fighter.sync();\n this.onFighter(fighter);\n }\n }\n\n async recordVictories() {\n console.log('Record Victories');\n try {\n const battles = await this.wallet.loadJigIndex({\n criteria: {kind: Battle.origin},\n project: {value: false}\n });\n for(let b of battles) {\n const battle = await this.wallet.loadJig(b.location);\n await battle.victor.fighter.sync();\n battle.victor.fighter.recordVictory(battle);\n await battle.victor.fighter.sync();\n this.onFighter(battle.victor.fighter);\n }\n } catch(e) {\n console.error('Record Victories Error:', e.message);\n }\n }\n\n async signBattle(message) {\n if (!this.queueMessage) return;\n const { id, rawtx, battle } = JSON.parse(message.payload);\n const { Br, Tx} = this.bsv;\n let tx = this.bsv.Tx.fromHex(rawtx);\n const outputs = await Promise.all(tx.txIns.map(async txIn => {\n const txid = new Br(txIn.txHashBuf).readReverse().toString('hex');\n const outTx = Tx.fromHex(await this.blockchain.fetch(txid));\n return {\n location: `${txid}_o${txIn.txOutNum}`,\n script: outTx.txOuts[txIn.txOutNum].script.toString(),\n };\n }));\n console.log('OUTPUTS:', JSON.stringify(outputs, null, 2));\n const t = await this.wallet.loadTransaction(rawtx);\n \n const rules = ClientAgent.lobbies.find(lobby => lobby.origin === this.queueMessage.subject);\n if (!rules) throw new Error('Invalid Lobby');\n \n const newRawTx = await t.export({sign: true, pay: false});\n tx = this.bsv.Tx.fromHex(newRawTx);\n let sigs = tx.txIns.map(txIn => txIn.scriptVi.toNumber() && txIn.script.toString());\n console.log('SIGS AFTER:', sigs);\n\n const sigMessage = this.wallet.buildMessage({\n to: [message.from],\n subject: 'BattleSigned',\n context: [id],\n payload: JSON.stringify(sigs)\n });\n await this.blockchain.sendMessage(sigMessage, ValidatorConfig.postTo);\n this.emit('client', 'BattleCreated', battle);\n this.currentId = id;\n this.turnCount = -1;\n }\n\n async Act(request) {\n console.log('Act');\n const { actionIndex } = request;\n const {id, location} = await this.battle;\n const message = this.wallet.buildMessage({\n context: [id],\n subject: 'Act',\n payload: JSON.stringify({ location, actionIndex })\n });\n await this.blockchain.sendMessage(message);\n }\n\n async onActionError(message) {\n throw new Error(message.payload);\n }\n\n async CreateFighter(request) {\n delete request.skills;\n const message = this.wallet.buildMessage({\n to: [MintConfig.pubkey],\n subject: 'FighterRequest',\n payload: JSON.stringify({\n ...request,\n owner: this.address\n })\n });\n const resp = await this.blockchain.sendMessage(message, MintConfig.postTo);\n const fighter = await this.wallet.loadJig(resp.location);\n this.onFighter(fighter);\n return fighter;\n }\n\n async EnterQueue(request) {\n const rules = ClientAgent.lobbies[request.lobbyId-1];\n console.log('RULES:', rules && rules.origin);\n const fighter = await this.wallet.loadJig(request.fighterLocation);\n if(!fighter) throw new Error('CLIENT: Invalid Fighter');\n console.time('Fighter Sync');\n await fighter.sync({inner: false});\n console.timeEnd('Fighter Sync');\n const items = await Promise.all(request.itemLocations.map(async location => {\n if(!location) return null;\n const item = await this.wallet.loadJig(location);\n if(!item) return;\n console.time(`Item Sync: ${location}`);\n await item.sync({inner: false});\n console.timeEnd(`Item Sync: ${location}`);\n return item;\n }));\n\n const skills = request.actionIds;\n let coin = rules.fee && await this.loadCoin(rules.fee);\n\n this.queueMessage = {\n to: [QueueConfig.pubkey],\n subject: rules.origin,\n payload: JSON.stringify({\n pubkey: this.pubkey,\n owner: this.address,\n fighterLocation: fighter.location,\n itemLocations: items.map(i => i && i.location),\n skills,\n coinId: coin && coin.location\n })\n };\n await this.blockchain.sendMessage(\n this.wallet.buildMessage(this.queueMessage),\n QueueConfig.postTo\n );\n return request.lobbyId;\n }\n\n async ExitQueue() {\n const message = this.wallet.buildMessage({\n to: [QueueConfig.pubkey],\n subject: 'ExitQueue'\n });\n await this.blockchain.sendMessage(message, QueueConfig.postTo);\n this._exitQueue();\n }\n\n async _exitQueue(message) {\n delete this.queueMessage;\n this.emit('client', 'OnExitQueue', message && message.payload);\n if(message && message.payload) {\n this.emit('client', 'Error', message.payload);\n }\n }\n\n async Forfeit(location) {\n const message = this.wallet.buildMessage({\n to: [ValidatorConfig.pubkey],\n subject: 'Forfeit',\n context: [this.currentId],\n payload: JSON.stringify({ location })\n });\n await this.blockchain.sendMessage(message);\n }\n \n \n async GetBattles() {\n if(!this._battles.size) {\n const tokens = await this.blockchain.jigQuery({ \n kind: BattleToken.origin,\n owner: this.address\n });\n\n console.log('BAttles:', JSON.stringify(tokens));\n await Promise.all(tokens.map(async token => {\n const jig = await this.blockchain.loadJigData(token.location, true).catch(() => null);\n if(!jig || !jig.value.battleState) return;\n this._battles.set(jig.value.battle.origin, jig.value.battle);\n }));\n }\n \n return [...this._battles.values()]\n .sort((a, b) => a.timestamp > b.timestamp ? -1 : 1);\n }\n\n async GetFighters() {\n if(!this._fighters.size) {\n const index = await this.wallet.loadJigIndex({\n criteria: {kind: Fighter.origin}\n });\n index.forEach(f => this._fighters.set(f.origin, f.value));\n }\n return [...this._fighters.values()];\n }\n\n SimulateFighterState(message) {\n return BattleUtils.buildFighterState(message);\n }\n\n SimulateFightersState(message) {\n const fighterStates = [];\n message.forEach(f => fighterStates.push(BattleUtils.buildFighterState(f)));\n return fighterStates;\n }\n\n async GetItems() {\n if(!this._items.size) {\n const index = await this.wallet.loadJigIndex({\n criteria: {kind: KronoItem.origin},\n project: {'value.mint': false}\n });\n index.forEach(i => this._items.set(i.origin, i.value));\n }\n return [...this._items.values()];\n }\n\n async GetPlayer() {\n const [fIndex, iIndex,] = await Promise.all([\n this.wallet.loadJigIndex({criteria: {kind: Fighter.origin}, project: {value: false}}),\n this.wallet.loadJigIndex({criteria: {kind: KronoItem.origin}, project: {value: false}})\n ]);\n\n const balance = await this.getBalance();\n const config = {\n address: this.address,\n pubkey: this.pubkey,\n owner: this.address,\n handle: this.wallet.handle,\n balance,\n xpTable: {\n cumulativeXp: Fighter.LevelUpXP,\n xpPerKill: Battle.LevelXPReward\n },\n lobbiesPerLevel: Fighter.LobbiesPerLevel,\n tierPerLobby: Fighter.TierPerLobby,\n abilityScoreLevels: Fighter.AbilityScoreLevels,\n skillLevels: Fighter.SkillLevels,\n skills: Object.entries(Constants.SkillData).map(([k, v]) => {\n if(['location', 'origin', 'owner', 'nonce', 'deps', 'satoshis'].includes(k)) return;\n return {\n ...v,\n skillType: k\n };\n }).filter(x => !!x),\n fighterCount: fIndex.length,\n itemCount: iIndex.length,\n lobbies: KronoClass.deepClone(ClientAgent.lobbies)\n };\n\n return config;\n }\n\n async LevelUp(request) {\n const { fighterLocation, ability, skillType } = request;\n const fighter = await this.wallet.loadJig(fighterLocation);\n if(!fighter) throw new Error('Invalid Fighter');\n await fighter.sync({inner: false});\n\n const rawtx = await this.blockchain.sendMessage(this.wallet.buildMessage({\n subject: 'LevelUp',\n payload: JSON.stringify({\n fighterLocation: fighter.location,\n ability,\n skillType\n })\n }), MintConfig.postTo);\n\n const t = await this.wallet.loadTransaction(rawtx);\n await t.publish();\n await fighter.sync({inner: false});\n // this.onFighter(fighter);\n return fighter.toObject();\n }\n\n async RedeemItem(itemId) {\n const message = this.wallet.buildMessage({\n to: [MintConfig.pubkey],\n subject: 'RedeemItemRequest',\n payload: JSON.stringify({owner: this.address, itemId})\n });\n\n const item = await this.blockchain.sendMessage(message, MintConfig.postTo);\n this._items.set(item.origin, item);\n return item;\n }\n\n async loadCoin(amount) {\n const index = await this.blockchain.jigIndex(this.coinScript, {\n criteria: {kind: KronoCoin.origin}\n });\n const coins = [];\n let acc = 0;\n for(let coinData of index) {\n const coin = await this.wallet.loadJig(coinData.location);\n coins.push(coin);\n if((acc += coin.amount) >= amount) break;\n }\n if (acc < amount) throw new KronoError(402, 'Insufficient balance');\n const coin = coins.pop();\n console.log('COIN:', coin.location, coins.length);\n if(coins.length) {\n console.log('COMBINING COINS:', coins.length);\n coin.combine(...coins);\n await coin.sync();\n }\n return coin;\n }\n\n async getBalance() {\n const index = await this.blockchain.jigIndex(this.coinScript, {}, 'script');\n const balance = index.reduce((acc, coin) => acc + coin.value.amount, 0);\n return balance;\n }\n\n async dispose() {\n const index = await this.wallet.loadJigIndex({ project: { value: false } });\n const deprecated = index\n .filter(data => !this.constructor.whitelist.includes(data.kind))\n .slice(0, 50);\n if (!deprecated.length) return;\n for (const j of deprecated) {\n try {\n const jig = await this.wallet.loadJig(j.location);\n await jig.sync({inner: false});\n console.log('Disposing:', jig.constructor.name, jig.location);\n jig.destroy();\n await jig.sync({forward: false});\n } catch(e) {\n console.error('Dispose Error:', e.message, e.stack);\n }\n }\n }\n\n static async preDeploy() {\n ClientAgent.lobbies = [\n ClientAgent.deps.Bot1ValidatorAgent,\n ClientAgent.deps.Bot2ValidatorAgent,\n ClientAgent.deps.Bot3ValidatorAgent,\n ClientAgent.deps.Bot4ValidatorAgent,\n ClientAgent.deps.Bot5ValidatorAgent,\n ClientAgent.deps.Bot6ValidatorAgent,\n ClientAgent.deps.Tier7ValidatorAgent,\n ];\n\n ClientAgent.whitelist = [\n ClientAgent.deps.Battle.origin,\n // ClientAgent.deps.BattleToken.origin,\n ClientAgent.deps.Fighter.origin,\n ClientAgent.deps.KronoItem.origin,\n ClientAgent.deps.KronoCoin\n ];\n }\n}",{"agentId":"client","deps":{"Agent":{"$jig":0},"Battle":{"$jig":1},"BattleToken":{"$jig":2},"BattleUtils":{"$jig":3},"Bot1ValidatorAgent":{"$jig":4},"Bot2ValidatorAgent":{"$jig":5},"Bot3ValidatorAgent":{"$jig":6},"Bot4ValidatorAgent":{"$jig":7},"Bot5ValidatorAgent":{"$jig":8},"Bot6ValidatorAgent":{"$jig":9},"CoinLock":{"$jig":10},"Constants":{"$jig":11},"Fighter":{"$jig":12},"KronoClass":{"$jig":13},"KronoCoin":{"$jig":14},"KronoError":{"$jig":15},"KronoItem":{"$jig":16},"MintConfig":{"$jig":17},"QueueConfig":{"$jig":18},"Sha256":{"$jig":19},"Tier7ValidatorAgent":{"$jig":20},"ValidatorConfig":{"$jig":21},"XpTest":{"$jig":22}},"hash":"4e2079fdcd40c9e7b867e392f551e448a714a86dc4a5b4e9b466d1e2eb44aa8e","lobbies":[{"$dup":["1","deps","Bot1ValidatorAgent"]},{"$dup":["1","deps","Bot2ValidatorAgent"]},{"$dup":["1","deps","Bot3ValidatorAgent"]},{"$dup":["1","deps","Bot4ValidatorAgent"]},{"$dup":["1","deps","Bot5ValidatorAgent"]},{"$dup":["1","deps","Bot6ValidatorAgent"]},{"$dup":["1","deps","Tier7ValidatorAgent"]}],"sealed":false,"whitelist":["419f1a052198cc8464d9816e33c6ea9ec9025877b5a521df626fdab97a15fc2a_o1","bc3574b008e372b4409ae7307bb8f0b34e1733cc1e78125276e2d17f89fef532_o1","b2ddb06a8acf4f6e7f594d812982ef6a924390d6e898a83140aa58606b1d2d05_o1",{"$dup":["1","deps","KronoCoin"]}]}]}]}
    https://whatsonchain.com/tx/7961e1da1def4a69b828d15e1f68f17de3501e780d0643088c6c36cc9188e470