Settings

Blockchain
Network
Unit
Language
Theme
Sound New Block

Transaction

e6eaaf8886c77c14de33e0b3fbb2c04acb1e8c6ffb98db9729f49200ff570608
Timestamp (utc)
2021-03-11 06:39:53
Fee Paid
0.00010106 BSV
(
0.00993916 BSV
-
0.00983810 BSV
)
Fee Rate
500.8 sat/KB
Version
1
Confirmations
312,487
Size Stats
20,167 B

3 Outputs

Total Output:
0.00983810 BSV
  • jrun cryptofightsMÂM{"in":0,"ref":["445f1d66f8210ab375fffefcb23881a02a41450cc83de938c60f5bd501a05ddc_o1","5611baf7a0a336c7bf4b55d113246d7dba44ffbbf279e6d76393f8dd8b4a28e1_o1","f3d35d6f7a049ba81e33309f9d6476edf7d8ffec58b4b9eb8a598d710b68b324_o1","9872c32303fd6de10a946c470e3a31b6394c02af85730eb6a2ae139f6adacc54_o1","fde32707b601adb7cb7ea928eb415bcc18d8753c3455c68dce0fe3de876e6d7b_o1","841a9a907c367110d6580e9fe958d268a7b1541fcdbee7a3757fa93be265b99f_o1","498d930f71d67c4403082fe449da080315ddcdbe2b33b6614466fa11b0050375_o1","63e0e1268d8ab021d1c578afb8eaa0828ccbba431ffffd9309d04b78ebeb6e56_o1","f94ae86b1acd5f60c356f63fa1778e8f5286100eb04d6220aae30628733efc2d_o1","5a0e8f9d40d2caec2204826628c8a5ada7264b0d8efbeca0a28ff876ab66f36f_o1","fa381fdfbdca6a30abde4b8fbd00b949bdd0821b564aa09258106ad3959b8697_o1","86de75a36af8585221451d3f9cba93065441b9f727c0f9fa8f0690d15de95a92_o1"],"out":["3e4a3a72ad17010b30a9d57c43ab756b5ba2ca9f89c5e746b19fe1371b30673a"],"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('BattleCreated', this.onBattleCreated);\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 this.messageHandlers.set('Requeue', this._enqueue);\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('GetBalance', () => 0);\n this.eventHandlers.set('GetBattle', this.GetBattle);\n this.eventHandlers.set('GetLastBattleUpdated', this.GetLastBattleUpdated);\n \n this.eventHandlers.set('LevelUp', this.LevelUp);\n this.eventHandlers.set('RedeemItem', this.RedeemItem);\n\n this.emit('subscribe', this.coinScript);\n const url = `${Config.baseUrl}/cryptofights/lobbies`;\n console.log(`Fetching lobbies: ${url}`);\n const resp = await this.lib.fetch(url);\n this.lobbies = await resp.json();\n console.log(`Lobbies: ${JSON.stringify(this.lobbies)}`);\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 const { id } = message.payloadObj;\n if(id === this.currentId) {\n delete this.queueMessage;\n }\n this.emit('client', 'BattleCreated', message.payloadObj);\n console.log(`BattleCreated: ${message.payload}`);\n }\n\n _queueStatus(message) {\n this.emit('client', 'QueueStatus', message.payload);\n }\n\n async onBattleUpdated(message) {\n console.log(`BattleUpdated: ${message.payload}`);\n const { location, battleId } = message.payloadObj;\n\n console.log('Handling BattleUpdated - BU:', battleId, ' CURR:', this.currentId);\n if(battleId === this.currentId) {\n console.log('Updating battleLocation');\n this.battleLocation = location;\n }\n this.emit('client', 'BattleUpdated', message.payloadObj);\n }\n\n async onBattleCompleted(message) {\n const battleData = message.payloadObj;\n\n this.emit('client', 'BattleCompleted', battleData);\n console.log(`BattleCompleted: ${JSON.stringify(battleData)}`);\n\n if(battleData.victor.userId === this.wallet.handle) {\n const battle = await this.wallet.loadJig(battleData.location);\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 projection: {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 console.log('signBattle');\n if (!this.queueMessage || !message.context.includes(this.queueId)){\n console.log('Invalid Queue:', this.queueMessage, this.queueId);\n return;\n } \n const { id, rawtx, paths } = message.payloadObj;\n console.log(`Client-Agent - signBattle: id = ${id}, rawtx = ${rawtx}`);\n const { Br, Tx} = this.bsv;\n let tx = this.bsv.Tx.fromHex(rawtx);\n console.log(`Client-Agent - signBattle: tx = ${tx}`);\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 console.log(`Executed wallet.loadTransaction.`); // Added debug log #1\n this.emit('setDerivations', paths);\n const newRawTx = await t.export({sign: true, pay: false});\n console.log(`Executed t.export.`);// Added debug log #4\n tx = this.bsv.Tx.fromHex(newRawTx);\n console.log(`Executed this.bsv.Tx.fromHex(newRawTx).`);// Added debug log #5\n let sigs = tx.txIns.map(txIn => txIn.scriptVi.toNumber() && txIn.script.toString());\n console.log('SIGS AFTER:', sigs);\n\n this.currentId = id;\n const sigMessage = this.wallet.buildMessage({\n to: [message.from],\n subject: 'BattleSigned',\n context: [id],\n payload: JSON.stringify({\n id,\n sigs\n })\n });\n await this.blockchain.sendMessage(sigMessage, `${Config.baseUrl}/${Config.fyxId}`);\n }\n\n async Act(request) {\n console.log('Act', JSON.stringify(request));\n const { actionIndex } = request;\n // const battle = await this.wallet.loadJig(this.battleLocation);\n // if(!battle) {\n // console.log('No Battle');\n // } else {\n // console.log('ACT BATTLE:', battle.toObject());\n // }\n // const {location} = battle;\n const message = this.wallet.buildMessage({\n subject: 'Act',\n payload: JSON.stringify({ location: this.battleLocation, actionIndex })\n });\n const battleUpdate = await this.blockchain.sendMessage(message, `${Config.baseUrl}/${Config.fyxId}`);\n const {battleId, location} = battleUpdate;\n if(battleId === this.currentId) {\n console.log('Updating battleLocation');\n this.battleLocation = location;\n }\n return battleUpdate;\n }\n\n async onActionError(message) {\n throw new Error(message.payload);\n }\n\n async CreateFighter(request) {\n const message = this.wallet.buildMessage({\n subject: 'CreateFighter',\n payload: JSON.stringify({\n ...request\n })\n });\n return this.blockchain.sendMessage(message, `${Config.baseUrl}/cryptofights`);\n }\n\n async EnterQueue(request) {\n const rules = this.lobbies[request.lobbyId-1];\n console.log(`RULES: ${JSON.stringify(rules)}`);\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 console.log('Load Coin');\n let coins = await this.selectCoins(rules.fee);\n\n console.log('Build Message');\n this.queueMessage = {\n subject: 'EnterQueue',\n payload: JSON.stringify({\n lobbyId: rules.rulesId,\n fighterLocation: fighter.location,\n itemLocations: items.map(i => i && i.location),\n skills: request.actionIds,\n coinLocations: coins.map(c => c.location)\n })\n };\n \n await this._enqueue();\n return request.lobbyId;\n }\n\n async selectCoins(amount) {\n const selections = [];\n if(!amount) return selections;\n const coins = await this.wallet.loadJigIndex({\n criteria: {kind: KronoCoin.origin},\n projection: {\n 'location': true,\n 'value.amount': true\n }\n });\n let total = 0;\n for(let coin of coins) {\n if(total >= amount) break;\n selections.push(coin);\n total += coin.value.amount;\n }\n if(total < amount) throw new KronoError(402, 'Insufficient fee');\n return selections;\n }\n\n async _enqueue() {\n if(!this.queueMessage) return;\n const message = this.wallet.buildMessage(this.queueMessage);\n this.queueId = message.id;\n console.log('_enqueue:', message.id);\n \n await this.blockchain.sendMessage(\n message,\n `${Config.baseUrl}/${Config.fyxId}`\n );\n this.wallet.setTimeout(async () => this._refreshQueue(message.id), 45000);\n }\n\n async _refreshQueue(queueId) {\n if(this.queueId !== queueId || !this.queueMessage) return;\n try {\n const refreshed = await this.blockchain.sendMessage(\n this.wallet.buildMessage({\n subject: 'RefreshQueue',\n payload: JSON.stringify({queueId})\n }),\n `${Config.baseUrl}/${Config.fyxId}`\n );\n console.log('Refreshed:', refreshed);\n if(refreshed) this.wallet.setTimeout(async () => this._refreshQueue(queueId), 45000);\n else {\n console.log('Queue expired.');\n // this.emit('client', 'OnExitQueue', 'Refresh expired');\n }\n } catch(e) {\n console.error('Refresh Queue Error:', e.message);\n }\n }\n\n async ExitQueue() {\n const message = this.wallet.buildMessage({\n subject: 'ExitQueue'\n });\n await this.blockchain.sendMessage(message, `${Config.baseUrl}/${Config.fyxId}`);\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() {\n const message = this.wallet.buildMessage({\n subject: 'Forfeit',\n payload: JSON.stringify({ location: this.battleLocation })\n });\n await this.blockchain.sendMessage(message, `${Config.baseUrl}/${Config.fyxId}`);\n }\n \n async GetBattles() {\n const tokens = await this.blockchain.jigQuery({ \n criteria: {\n kind: BattleToken.origin,\n owner: this.address\n },\n projection: {\n 'value.battle.stateHistory': false\n },\n limit: 20,\n sort: {ts: -1},\n });\n console.log('Tokens:', tokens.length);\n return tokens.map(t => t.value.battle);\n }\n\n\n async GetBattle(location) {\n const battle = await this.wallet.loadJig(location);\n return battle.toObject();\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.simulateFighterState(message);\n }\n\n SimulateFightersState(message) {\n const fighterStates = [];\n message.forEach(f => fighterStates.push(BattleUtils.simulateFighterState(f)));\n return fighterStates;\n }\n\n async GetLastBattleUpdated() {\n if(!this.battleLocation) return;\n const battle = await this.wallet.loadJig(this.battleLocation);\n return battle.getState();\n }\n\n async GetItems() {\n if(!this._items.size) {\n const index = await this.wallet.loadJigIndex({\n criteria: {kind: KronoItem.origin},\n projection: {'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 console.log('GetPlayer');\n const [fIndex, iIndex,] = await Promise.all([\n this.wallet.loadJigIndex({criteria: {kind: Fighter.origin}, projection: {value: false}}),\n this.wallet.loadJigIndex({criteria: {kind: KronoItem.origin}, projection: {value: false}})\n ]);\n\n const balance = 0;\n const config = {\n address: this.address,\n pubkey: this.pubkey,\n owner: this.address,\n userId: 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: this.lobbies\n };\n\n console.log('Player:', config);\n return config;\n }\n\n async LevelUp(request) {\n const { fighterLocation, ability, skillType } = request;\n let fighter = await this.wallet.loadJig(fighterLocation);\n if(!fighter) throw new Error('Invalid Fighter');\n await fighter.sync();\n\n const {paths, 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 }), `${Config.baseUrl}/${Config.fyxId}`);\n this.emit('setDerivations', paths);\n\n const t = await this.wallet.loadTransaction(rawtx);\n fighter = t.outputs.find(o => o instanceof Fighter);\n await t.publish();\n await fighter.sync();\n // this.onFighter(fighter);\n return fighter.toObject();\n }\n\n async RedeemItem(itemId) {\n const message = this.wallet.buildMessage({\n subject: 'RedeemItem',\n payload: JSON.stringify({owner: this.address, itemId})\n }, `${Config.baseUrl}/cryptofights`);\n\n const item = await this.blockchain.sendMessage(message, `${Config.baseUrl}/${Config.fyxId}`);\n this._items.set(item.origin, item);\n return item;\n }\n\n async dispose() {\n const index = await this.wallet.loadJigIndex({ projection: { 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.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},"Config":{"$jig":4},"Constants":{"$jig":5},"Fighter":{"$jig":6},"Group":{"$jig":7},"KronoClass":{"$jig":8},"KronoCoin":{"$jig":9},"KronoError":{"$jig":10},"KronoItem":{"$jig":11}},"hash":"a0dfb4fb35179410fe3b38b31442052b6cb4425a77daed07b7eb8c1f06794ce8","sealed":false,"whitelist":["5611baf7a0a336c7bf4b55d113246d7dba44ffbbf279e6d76393f8dd8b4a28e1_o1","498d930f71d67c4403082fe449da080315ddcdbe2b33b6614466fa11b0050375_o1","86de75a36af8585221451d3f9cba93065441b9f727c0f9fa8f0690d15de95a92_o1",{"$dup":["1","deps","KronoCoin"]}]}]}]}
    https://whatsonchain.com/tx/e6eaaf8886c77c14de33e0b3fbb2c04acb1e8c6ffb98db9729f49200ff570608