From 1f74c9e7746fd6baa380ce9764f6add7fca2ab80 Mon Sep 17 00:00:00 2001 From: Chef Huan Date: Sun, 9 Jun 2024 15:00:44 +0700 Subject: [PATCH] chore: Add predictionV3 AI subgraph --- config/bsc.js | 4 + config/chapel.js | 4 + .../v3-ai-generic/abis/PredictionV3.json | 1103 +++++++++++++++++ .../v3-ai-generic/mappings/index.ts | 473 +++++++ .../prediction/v3-ai-generic/package.json | 18 + .../prediction/v3-ai-generic/schema.graphql | 119 ++ .../v3-ai-generic/subgraph.template.yaml | 45 + .../prediction/v3-ai-generic/subgraph.yaml | 45 + 8 files changed, 1811 insertions(+) create mode 100644 subgraphs/prediction/v3-ai-generic/abis/PredictionV3.json create mode 100644 subgraphs/prediction/v3-ai-generic/mappings/index.ts create mode 100644 subgraphs/prediction/v3-ai-generic/package.json create mode 100644 subgraphs/prediction/v3-ai-generic/schema.graphql create mode 100644 subgraphs/prediction/v3-ai-generic/subgraph.template.yaml create mode 100644 subgraphs/prediction/v3-ai-generic/subgraph.yaml diff --git a/config/bsc.js b/config/bsc.js index a50b595c..3012f25d 100644 --- a/config/bsc.js +++ b/config/bsc.js @@ -36,4 +36,8 @@ module.exports = { startBlock: 10333825, address: "0x18B2A687610328590Bc8F2e5fEdDe3b582A49cdA", }, + predictionV3AI: { + startBlock: 10333825, //TODO update me + address: "0x505B6e8DA1c31f4033ef5b70cba426E00538Fb3c", //TODO update me + }, }; diff --git a/config/chapel.js b/config/chapel.js index bf163036..5ff56558 100644 --- a/config/chapel.js +++ b/config/chapel.js @@ -29,4 +29,8 @@ module.exports = { masterChefAddress: "0x4c650fb471fe4e0f476fd3437c3411b1122c4e3b", startBlock: 28492774, }, + predictionV3AI: { + startBlock: 40576515, + address: "0x505B6e8DA1c31f4033ef5b70cba426E00538Fb3c", + }, }; diff --git a/subgraphs/prediction/v3-ai-generic/abis/PredictionV3.json b/subgraphs/prediction/v3-ai-generic/abis/PredictionV3.json new file mode 100644 index 00000000..ee2996cf --- /dev/null +++ b/subgraphs/prediction/v3-ai-generic/abis/PredictionV3.json @@ -0,0 +1,1103 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_adminAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "_operatorAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_intervalSeconds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_bufferSeconds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minBetAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_treasuryFee", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "AddressInsufficientBalance", + "type": "error" + }, + { + "inputs": [], + "name": "EnforcedPause", + "type": "error" + }, + { + "inputs": [], + "name": "ExpectedPause", + "type": "error" + }, + { + "inputs": [], + "name": "FailedInnerCall", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "SafeERC20FailedOperation", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount", + "type": "uint128" + } + ], + "name": "BetBear", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount", + "type": "uint128" + } + ], + "name": "BetBull", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Claim", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "price", + "type": "uint128" + } + ], + "name": "EndRound", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "price", + "type": "uint128" + } + ], + "name": "LockRound", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "admin", + "type": "address" + } + ], + "name": "NewAdminAddress", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "bufferSeconds", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "intervalSeconds", + "type": "uint256" + } + ], + "name": "NewBufferAndIntervalSeconds", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "minBetAmount", + "type": "uint256" + } + ], + "name": "NewMinBetAmount", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "NewOperatorAddress", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "treasuryFee", + "type": "uint256" + } + ], + "name": "NewTreasuryFee", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + } + ], + "name": "Pause", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "rewardBaseCalAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "rewardAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "treasuryAmount", + "type": "uint256" + } + ], + "name": "RewardsCalculated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "AIPrice", + "type": "uint128" + } + ], + "name": "StartRound", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "TokenRecovery", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "TreasuryClaim", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + } + ], + "name": "Unpause", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "inputs": [], + "name": "MAX_TREASURY_FEE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "adminAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + } + ], + "name": "betBear", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + } + ], + "name": "betBull", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "bufferSeconds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "epochs", + "type": "uint256[]" + } + ], + "name": "claim", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "claimTreasury", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "claimable", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "currentEpoch", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint128", + "name": "currentPrice", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "AIPrice", + "type": "uint128" + } + ], + "name": "executeRound", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "genesisLockOnce", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint128", + "name": "currentPrice", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "AIPrice", + "type": "uint128" + } + ], + "name": "genesisLockRound", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "genesisStartOnce", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint128", + "name": "AIPrice", + "type": "uint128" + } + ], + "name": "genesisStartRound", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "uint256", + "name": "cursor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "size", + "type": "uint256" + } + ], + "name": "getUserRounds", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "components": [ + { + "internalType": "enum PancakeAIPrediction.Position", + "name": "position", + "type": "uint8" + }, + { + "internalType": "uint128", + "name": "amount", + "type": "uint128" + }, + { + "internalType": "bool", + "name": "claimed", + "type": "bool" + } + ], + "internalType": "struct PancakeAIPrediction.BetInfo[]", + "name": "", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "getUserRoundsLength", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "intervalSeconds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "ledger", + "outputs": [ + { + "internalType": "enum PancakeAIPrediction.Position", + "name": "position", + "type": "uint8" + }, + { + "internalType": "uint128", + "name": "amount", + "type": "uint128" + }, + { + "internalType": "bool", + "name": "claimed", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "minBetAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "operatorAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "recoverToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "refundable", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "rounds", + "outputs": [ + { + "internalType": "uint32", + "name": "startTimestamp", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "lockTimestamp", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "closeTimestamp", + "type": "uint32" + }, + { + "internalType": "uint128", + "name": "AIPrice", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "lockPrice", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "closePrice", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "totalAmount", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "bullAmount", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "bearAmount", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "rewardBaseCalAmount", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "rewardAmount", + "type": "uint128" + }, + { + "internalType": "bool", + "name": "oracleCalled", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_adminAddress", + "type": "address" + } + ], + "name": "setAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_bufferSeconds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_intervalSeconds", + "type": "uint256" + } + ], + "name": "setBufferAndIntervalSeconds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_minBetAmount", + "type": "uint256" + } + ], + "name": "setMinBetAmount", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_operatorAddress", + "type": "address" + } + ], + "name": "setOperator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_treasuryFee", + "type": "uint256" + } + ], + "name": "setTreasuryFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "treasuryAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "treasuryFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "userRounds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/subgraphs/prediction/v3-ai-generic/mappings/index.ts b/subgraphs/prediction/v3-ai-generic/mappings/index.ts new file mode 100644 index 00000000..968d0341 --- /dev/null +++ b/subgraphs/prediction/v3-ai-generic/mappings/index.ts @@ -0,0 +1,473 @@ +/* eslint-disable prefer-const */ +import { BigDecimal, BigInt, Bytes, log } from "@graphprotocol/graph-ts"; +import { concat } from "@graphprotocol/graph-ts/helper-functions"; +import { Bet, Market, Round, User } from "../generated/schema"; +import { + BetBear, + BetBull, + Claim, + EndRound, + LockRound, + Pause, + RewardsCalculated, + StartRound, + Unpause, +} from "../generated/PredictionV3AI/PredictionV3AI"; + +let ZERO_BI = BigInt.fromI32(0); +let ONE_BI = BigInt.fromI32(1); +let ZERO_BD = BigDecimal.fromString("0"); +let HUNDRED_BD = BigDecimal.fromString("100"); +let EIGHT_BD = BigDecimal.fromString("1e8"); +let EIGHTEEN_BD = BigDecimal.fromString("1e18"); + +/** + * PAUSE + */ + +export function handlePause(event: Pause): void { + let market = Market.load("1"); + if (market === null) { + market = new Market("1"); + market.epoch = event.params.epoch.toString(); + market.paused = true; + market.totalUsers = ZERO_BI; + market.totalBets = ZERO_BI; + market.totalBetsBull = ZERO_BI; + market.totalBetsBear = ZERO_BI; + market.totalAmount = ZERO_BD; + market.totalBullAmount = ZERO_BD; + market.totalBearAmount = ZERO_BD; + market.totalTreasuryAmount = ZERO_BD; + market.totalBetsClaimed = ZERO_BI; + market.totalClaimedAmount = ZERO_BD; + market.winRate = HUNDRED_BD; + market.averageAmount = ZERO_BD; + market.netAmount = ZERO_BD; + market.save(); + } + market.epoch = event.params.epoch.toString(); + market.paused = true; + market.save(); + + // Pause event was called, cancelling rounds. + let round = Round.load(event.params.epoch.toString()); + if (round === null) { + round = new Round(event.params.epoch.toString()); + round.epoch = event.params.epoch; + round.previous = event.params.epoch.equals(ZERO_BI) ? null : event.params.epoch.minus(ONE_BI).toString(); + round.startAt = event.block.timestamp; + round.startBlock = event.block.number; + round.startHash = event.transaction.hash; + round.totalBets = ZERO_BI; + round.totalAmount = ZERO_BD; + round.bullBets = ZERO_BI; + round.bullAmount = ZERO_BD; + round.bearBets = ZERO_BI; + round.bearAmount = ZERO_BD; + round.save(); + } + round.failed = true; + round.save(); + + // Also fail the previous round because it will not complete. + let previousRound = Round.load(round.previous); + if (previousRound !== null) { + previousRound.failed = true; + previousRound.save(); + } +} + +export function handleUnpause(event: Unpause): void { + let market = Market.load("1"); + if (market === null) { + market = new Market("1"); + market.epoch = event.params.epoch.toString(); + market.paused = false; + market.totalUsers = ZERO_BI; + market.totalBets = ZERO_BI; + market.totalBetsBull = ZERO_BI; + market.totalBetsBear = ZERO_BI; + market.totalAmount = ZERO_BD; + market.totalBullAmount = ZERO_BD; + market.totalBearAmount = ZERO_BD; + market.totalTreasuryAmount = ZERO_BD; + market.totalBetsClaimed = ZERO_BI; + market.totalClaimedAmount = ZERO_BD; + market.winRate = HUNDRED_BD; + market.averageAmount = ZERO_BD; + market.netAmount = ZERO_BD; + market.save(); + } + market.epoch = event.params.epoch.toString(); + market.paused = false; + market.save(); +} + +/** + * ROUND + */ + +export function handleStartRound(event: StartRound): void { + let market = Market.load("1"); + if (market === null) { + market = new Market("1"); + market.epoch = event.params.epoch.toString(); + market.paused = false; + market.totalUsers = ZERO_BI; + market.totalBets = ZERO_BI; + market.totalBetsBull = ZERO_BI; + market.totalBetsBear = ZERO_BI; + market.totalAmount = ZERO_BD; + market.totalBullAmount = ZERO_BD; + market.totalBearAmount = ZERO_BD; + market.totalTreasuryAmount = ZERO_BD; + market.totalBetsClaimed = ZERO_BI; + market.totalClaimedAmount = ZERO_BD; + market.winRate = HUNDRED_BD; + market.averageAmount = ZERO_BD; + market.netAmount = ZERO_BD; + market.save(); + } + market.epoch = event.params.epoch.toString(); + market.save(); + + let round = Round.load(event.params.epoch.toString()); + if (round === null) { + round = new Round(event.params.epoch.toString()); + round.epoch = event.params.epoch; + round.previous = event.params.epoch.equals(ZERO_BI) ? null : event.params.epoch.minus(ONE_BI).toString(); + round.startAt = event.block.timestamp; + round.startBlock = event.block.number; + round.startHash = event.transaction.hash; + round.totalBets = ZERO_BI; + round.totalAmount = ZERO_BD; + round.bullBets = ZERO_BI; + round.bullAmount = ZERO_BD; + round.bearBets = ZERO_BI; + round.bearAmount = ZERO_BD; + round.save(); + } +} + +export function handleLockRound(event: LockRound): void { + let round = Round.load(event.params.epoch.toString()); + if (round === null) { + log.warning("Tried to lock round without an existing round (epoch: {}).", [event.params.epoch.toString()]); + round = new Round(event.params.epoch.toString()); + round.epoch = event.params.epoch; + round.previous = event.params.epoch.equals(ZERO_BI) ? null : event.params.epoch.minus(ONE_BI).toString(); + round.startAt = event.block.timestamp; + round.startBlock = event.block.number; + round.startHash = event.transaction.hash; + round.totalBets = ZERO_BI; + round.totalAmount = ZERO_BD; + round.bullBets = ZERO_BI; + round.bullAmount = ZERO_BD; + round.bearBets = ZERO_BI; + round.bearAmount = ZERO_BD; + round.save(); + } + round.lockAt = event.block.timestamp; + round.lockBlock = event.block.number; + round.lockHash = event.transaction.hash; + round.lockPrice = event.params.price.divDecimal(EIGHT_BD); + // round.lockRoundId = event.params.roundId; // Not evicted in the new version of event + round.save(); +} + +export function handleEndRound(event: EndRound): void { + let round = Round.load(event.params.epoch.toString()); + if (round === null) { + log.warning("Tried to end round without an existing round (epoch: {}).", [event.params.epoch.toString()]); + round = new Round(event.params.epoch.toString()); + round.epoch = event.params.epoch; + round.previous = event.params.epoch.equals(ZERO_BI) ? null : event.params.epoch.minus(ONE_BI).toString(); + round.startAt = event.block.timestamp; + round.startBlock = event.block.number; + round.startHash = event.transaction.hash; + round.totalBets = ZERO_BI; + round.totalAmount = ZERO_BD; + round.bullBets = ZERO_BI; + round.bullAmount = ZERO_BD; + round.bearBets = ZERO_BI; + round.bearAmount = ZERO_BD; + round.save(); + } + round.closeAt = event.block.timestamp; + round.closeBlock = event.block.number; + round.closeHash = event.transaction.hash; + if (event.params.price) { + round.closePrice = event.params.price.divDecimal(EIGHT_BD); + } + // round.closeRoundId = event.params.roundId; // Not evicted in the new version of event + + // Get round result based on lock/close price. + if (round.closePrice) { + if (round.closePrice.equals(round.lockPrice as BigDecimal)) { + round.position = "House"; + + let market = Market.load("1"); + if (market === null) { + log.error("Tried to query market after end round was called for a round (epoch: {})", [ + event.params.epoch.toString(), + ]); + } else { + market.totalTreasuryAmount = market.totalTreasuryAmount.plus(round.totalAmount); + market.netAmount = market.netAmount.plus(round.totalAmount); + market.save(); + } + } else if (round.closePrice.gt(round.lockPrice as BigDecimal)) { + round.position = "Bull"; + } else if (round.closePrice.lt(round.lockPrice as BigDecimal)) { + round.position = "Bear"; + } else { + round.position = null; + } + + round.failed = false; + } + + round.save(); +} + +export function handleBetBull(event: BetBull): void { + let market = Market.load("1"); + if (market === null) { + log.error("Tried to query market with bet (Bull)", []); + return; + } + market.totalBets = market.totalBets.plus(ONE_BI); + market.totalBetsBull = market.totalBetsBull.plus(ONE_BI); + market.totalAmount = market.totalAmount.plus(event.params.amount.divDecimal(EIGHTEEN_BD)); + market.totalBullAmount = market.totalBullAmount.plus(event.params.amount.divDecimal(EIGHTEEN_BD)); + market.winRate = market.totalBetsClaimed.divDecimal(market.totalBets.toBigDecimal()).times(HUNDRED_BD); + market.averageAmount = market.totalAmount.div(market.totalBets.toBigDecimal()); + market.netAmount = market.netAmount.minus(event.params.amount.divDecimal(EIGHTEEN_BD)); + market.save(); + + let round = Round.load(event.params.epoch.toString()); + if (round === null) { + log.warning("Tried to bet (bull) without an existing round (epoch: {}).", [event.params.epoch.toString()]); + round = new Round(event.params.epoch.toString()); + round.epoch = event.params.epoch; + round.previous = event.params.epoch.equals(ZERO_BI) ? null : event.params.epoch.minus(ONE_BI).toString(); + round.startAt = event.block.timestamp; + round.startBlock = event.block.number; + round.startHash = event.transaction.hash; + round.totalBets = ZERO_BI; + round.totalAmount = ZERO_BD; + round.bullBets = ZERO_BI; + round.bullAmount = ZERO_BD; + round.bearBets = ZERO_BI; + round.bearAmount = ZERO_BD; + round.save(); + } + round.totalBets = round.totalBets.plus(ONE_BI); + round.totalAmount = round.totalAmount.plus(event.params.amount.divDecimal(EIGHTEEN_BD)); + round.bullBets = round.bullBets.plus(ONE_BI); + round.bullAmount = round.bullAmount.plus(event.params.amount.divDecimal(EIGHTEEN_BD)); + round.save(); + + // Fail safe condition in case the user has not been created yet. + let user = User.load(event.params.sender.toHex()); + if (user === null) { + user = new User(event.params.sender.toHex()); + user.createdAt = event.block.timestamp; + user.updatedAt = event.block.timestamp; + user.block = event.block.number; + user.totalBets = ZERO_BI; + user.totalBetsBull = ZERO_BI; + user.totalBetsBear = ZERO_BI; + user.totalAmount = ZERO_BD; + user.totalBullAmount = ZERO_BD; + user.totalBearAmount = ZERO_BD; + user.totalBetsClaimed = ZERO_BI; + user.totalClaimedAmount = ZERO_BD; + user.winRate = HUNDRED_BD; + user.averageAmount = ZERO_BD; + user.netAmount = ZERO_BD; + + market.totalUsers = market.totalUsers.plus(ONE_BI); + market.save(); + } + user.updatedAt = event.block.timestamp; + user.totalBets = user.totalBets.plus(ONE_BI); + user.totalBetsBull = user.totalBetsBull.plus(ONE_BI); + user.totalAmount = user.totalAmount.plus(event.params.amount.divDecimal(EIGHTEEN_BD)); + user.totalBullAmount = user.totalBullAmount.plus(event.params.amount.divDecimal(EIGHTEEN_BD)); + if (user.totalBets.gt(ZERO_BI)) { + user.winRate = user.totalBetsClaimed.divDecimal(user.totalBets.toBigDecimal()).times(HUNDRED_BD); + user.averageAmount = user.totalAmount.div(user.totalBets.toBigDecimal()); + } + user.netAmount = user.netAmount.minus(event.params.amount.divDecimal(EIGHTEEN_BD)); + user.save(); + + let betId = concat(event.params.sender, Bytes.fromI32(event.params.epoch.toI32())).toHex(); + let bet = new Bet(betId); + bet.round = round.id; + bet.user = user.id; + bet.hash = event.transaction.hash; + bet.amount = event.params.amount.divDecimal(EIGHTEEN_BD); + bet.position = "Bull"; + bet.claimed = false; + bet.createdAt = event.block.timestamp; + bet.updatedAt = event.block.timestamp; + bet.block = event.block.number; + bet.save(); +} + +export function handleBetBear(event: BetBear): void { + let market = Market.load("1"); + if (market === null) { + log.error("Tried to query market with bet (Bear)", []); + return; + } + market.totalBets = market.totalBets.plus(ONE_BI); + market.totalBetsBear = market.totalBetsBear.plus(ONE_BI); + market.totalAmount = market.totalAmount.plus(event.params.amount.divDecimal(EIGHTEEN_BD)); + market.totalBearAmount = market.totalBearAmount.plus(event.params.amount.divDecimal(EIGHTEEN_BD)); + market.winRate = market.totalBetsClaimed.divDecimal(market.totalBets.toBigDecimal()).times(HUNDRED_BD); + market.averageAmount = market.totalAmount.div(market.totalBets.toBigDecimal()); + market.netAmount = market.netAmount.minus(event.params.amount.divDecimal(EIGHTEEN_BD)); + market.save(); + + let round = Round.load(event.params.epoch.toString()); + if (round === null) { + log.warning("Tried to bet (bear) without an existing round (epoch: {}).", [event.params.epoch.toString()]); + round = new Round(event.params.epoch.toString()); + round.epoch = event.params.epoch; + round.previous = event.params.epoch.equals(ZERO_BI) ? null : event.params.epoch.minus(ONE_BI).toString(); + round.startAt = event.block.timestamp; + round.startBlock = event.block.number; + round.startHash = event.transaction.hash; + round.totalBets = ZERO_BI; + round.totalAmount = ZERO_BD; + round.bullBets = ZERO_BI; + round.bullAmount = ZERO_BD; + round.bearBets = ZERO_BI; + round.bearAmount = ZERO_BD; + round.save(); + } + round.totalBets = round.totalBets.plus(ONE_BI); + round.totalAmount = round.totalAmount.plus(event.params.amount.divDecimal(EIGHTEEN_BD)); + round.bearBets = round.bearBets.plus(ONE_BI); + round.bearAmount = round.bearAmount.plus(event.params.amount.divDecimal(EIGHTEEN_BD)); + round.save(); + + // Fail safe condition in case the user has not been created yet. + let user = User.load(event.params.sender.toHex()); + if (user === null) { + user = new User(event.params.sender.toHex()); + user.createdAt = event.block.timestamp; + user.updatedAt = event.block.timestamp; + user.block = event.block.number; + user.totalBets = ZERO_BI; + user.totalBetsBull = ZERO_BI; + user.totalBetsBear = ZERO_BI; + user.totalAmount = ZERO_BD; + user.totalBullAmount = ZERO_BD; + user.totalBearAmount = ZERO_BD; + user.totalBetsClaimed = ZERO_BI; + user.totalClaimedAmount = ZERO_BD; + user.winRate = HUNDRED_BD; + user.averageAmount = ZERO_BD; + user.netAmount = ZERO_BD; + + market.totalUsers = market.totalUsers.plus(ONE_BI); + market.save(); + } + user.updatedAt = event.block.timestamp; + user.totalBets = user.totalBets.plus(ONE_BI); + user.totalBetsBear = user.totalBetsBear.plus(ONE_BI); + user.totalAmount = user.totalAmount.plus(event.params.amount.divDecimal(EIGHTEEN_BD)); + user.totalBearAmount = user.totalBearAmount.plus(event.params.amount.divDecimal(EIGHTEEN_BD)); + if (user.totalBets.gt(ZERO_BI)) { + user.winRate = user.totalBetsClaimed.divDecimal(user.totalBets.toBigDecimal()).times(HUNDRED_BD); + user.averageAmount = user.totalAmount.div(user.totalBets.toBigDecimal()); + } + user.netAmount = user.netAmount.minus(event.params.amount.divDecimal(EIGHTEEN_BD)); + user.save(); + + let betId = concat(event.params.sender, Bytes.fromI32(event.params.epoch.toI32())).toHex(); + let bet = new Bet(betId); + bet.round = round.id; + bet.user = user.id; + bet.hash = event.transaction.hash; + bet.amount = event.params.amount.divDecimal(EIGHTEEN_BD); + bet.position = "Bear"; + bet.claimed = false; + bet.createdAt = event.block.timestamp; + bet.updatedAt = event.block.timestamp; + bet.block = event.block.number; + bet.save(); +} + +export function handleClaim(event: Claim): void { + let betId = concat(event.params.sender, Bytes.fromI32(event.params.epoch.toI32())).toHex(); + let bet = Bet.load(betId); + if (bet === null) { + log.warning("Tried to query bet without an existing ID (betId: {})", [betId]); + } else { + bet.claimed = true; + bet.claimedAt = event.block.timestamp; + bet.claimedBlock = event.block.number; + bet.claimedHash = event.transaction.hash; + bet.claimedAmount = event.params.amount.divDecimal(EIGHTEEN_BD); + if (bet.amount.gt(ZERO_BD)) { + bet.claimedNetAmount = event.params.amount.divDecimal(EIGHTEEN_BD).minus(bet.amount); + } + bet.updatedAt = event.block.timestamp; + bet.save(); + } + + let user = User.load(event.params.sender.toHex()); + if (user === null) { + user = new User(event.params.sender.toHex()); + user.createdAt = event.block.timestamp; + user.updatedAt = event.block.timestamp; + user.block = event.block.number; + user.totalBets = ZERO_BI; + user.totalBetsBull = ZERO_BI; + user.totalBetsBear = ZERO_BI; + user.totalAmount = ZERO_BD; + user.totalBullAmount = ZERO_BD; + user.totalBearAmount = ZERO_BD; + user.totalBetsClaimed = ZERO_BI; + user.totalClaimedAmount = ZERO_BD; + user.winRate = HUNDRED_BD; + user.averageAmount = ZERO_BD; + user.netAmount = ZERO_BD; + user.save(); + } + user.totalBetsClaimed = user.totalBetsClaimed.plus(ONE_BI); + user.totalClaimedAmount = user.totalClaimedAmount.plus(event.params.amount.divDecimal(EIGHTEEN_BD)); + if (user.totalBets.gt(ZERO_BI)) { + user.winRate = user.totalBetsClaimed.divDecimal(user.totalBets.toBigDecimal()).times(HUNDRED_BD); + } + user.netAmount = user.netAmount.plus(event.params.amount.divDecimal(EIGHTEEN_BD)); + user.save(); + + let market = Market.load("1"); + if (market === null) { + log.error("Tried to query market after a user claimed for a round (epoch: {})", [event.params.epoch.toString()]); + return; + } + market.totalBetsClaimed = market.totalBetsClaimed.plus(ONE_BI); + market.totalClaimedAmount = market.totalClaimedAmount.plus(event.params.amount.divDecimal(EIGHTEEN_BD)); + market.winRate = market.totalBetsClaimed.divDecimal(market.totalBets.toBigDecimal()).times(HUNDRED_BD); + market.netAmount = market.netAmount.plus(event.params.amount.divDecimal(EIGHTEEN_BD)); + market.save(); +} + +export function handleRewardsCalculated(event: RewardsCalculated): void { + let market = Market.load("1"); + if (market === null) { + log.error("Tried to query market after rewards were calculated for a round (epoch: {})", [ + event.params.epoch.toString(), + ]); + return; + } + market.totalTreasuryAmount = market.totalTreasuryAmount.plus(event.params.treasuryAmount.divDecimal(EIGHTEEN_BD)); + market.save(); +} diff --git a/subgraphs/prediction/v3-ai-generic/package.json b/subgraphs/prediction/v3-ai-generic/package.json new file mode 100644 index 00000000..af4b43f2 --- /dev/null +++ b/subgraphs/prediction/v3-ai-generic/package.json @@ -0,0 +1,18 @@ +{ + "name": "prediction-v3-ai-generic", + "description": "PancakeSwap Subgraph", + "version": "1.0.0", + "repository": "git@github.com:pancakeswap/pancake-subgraph.git", + "author": "PancakeSwap", + "license": "GPL-3.0-or-later", + "scripts": { + "template": "mustache ../../../config/$NETWORK.js subgraph.template.yaml > subgraph.yaml", + "codegen": "graph codegen subgraph.yaml", + "build": "graph build subgraph.yaml", + "deploy:bsc": "graph deploy --studio --studio prediction-v3-ai-bsc", + "deploy:chapel": "graph deploy --studio huan-prediction-v3-ai-chapel", + "deploy:arbitrum-goerli": "graph deploy --studio prediction-v3-ai-arbitrum-goreli", + "deploy:arbitrum": "graph deploy --studio prediction-v3-ai-arbitrum", + "deploy:zksync-era": "graph deploy --studio prediction-v3-ai-zksync-era" + } +} diff --git a/subgraphs/prediction/v3-ai-generic/schema.graphql b/subgraphs/prediction/v3-ai-generic/schema.graphql new file mode 100644 index 00000000..559258de --- /dev/null +++ b/subgraphs/prediction/v3-ai-generic/schema.graphql @@ -0,0 +1,119 @@ +enum Position { + Bull + Bear + House +} + +type Market @entity { + id: ID! + + epoch: Round + paused: Boolean! + + # Users + totalUsers: BigInt! + + # Bets + totalBets: BigInt! + totalBetsBull: BigInt! + totalBetsBear: BigInt! + totalAmount: BigDecimal! + totalBullAmount: BigDecimal! + totalBearAmount: BigDecimal! + totalTreasuryAmount: BigDecimal! + + # Statistics + totalBetsClaimed: BigInt! + totalClaimedAmount: BigDecimal! + winRate: BigDecimal! + averageAmount: BigDecimal! + netAmount: BigDecimal! +} + +type Round @entity { + id: ID! + + epoch: BigInt! + position: Position + failed: Boolean + + previous: Round + + # Start + startAt: BigInt! + startBlock: BigInt! + startHash: Bytes! + + # Lock + lockAt: BigInt + lockBlock: BigInt + lockHash: Bytes + lockPrice: BigDecimal + lockRoundId: BigInt + + # End + closeAt: BigInt + closeBlock: BigInt + closeHash: Bytes + closePrice: BigDecimal + closeRoundId: BigInt + + # Bets + totalBets: BigInt! + totalAmount: BigDecimal! + bullBets: BigInt! + bullAmount: BigDecimal! + bearBets: BigInt! + bearAmount: BigDecimal! + bets: [Bet!]! @derivedFrom(field: "round") +} + +type User @entity { + id: ID! + + createdAt: BigInt! + updatedAt: BigInt! + + block: BigInt! + + # Bets + totalBets: BigInt! + totalBetsBull: BigInt! + totalBetsBear: BigInt! + totalAmount: BigDecimal! + totalBullAmount: BigDecimal! + totalBearAmount: BigDecimal! + bets: [Bet!]! @derivedFrom(field: "user") + + # Statistics + totalBetsClaimed: BigInt! + totalClaimedAmount: BigDecimal! + winRate: BigDecimal! + averageAmount: BigDecimal! + netAmount: BigDecimal! +} + +type Bet @entity { + id: ID! + + # epoch + round: Round! + + user: User! + hash: Bytes! + + amount: BigDecimal! + position: Position! + + claimed: Boolean! + claimedAt: BigInt + claimedBlock: BigInt + claimedHash: Bytes + claimedAmount: BigDecimal + claimedNetAmount: BigDecimal + + createdAt: BigInt! + updatedAt: BigInt! + + block: BigInt! +} diff --git a/subgraphs/prediction/v3-ai-generic/subgraph.template.yaml b/subgraphs/prediction/v3-ai-generic/subgraph.template.yaml new file mode 100644 index 00000000..9e135673 --- /dev/null +++ b/subgraphs/prediction/v3-ai-generic/subgraph.template.yaml @@ -0,0 +1,45 @@ +specVersion: 0.0.2 +description: Pancake Prediction V3 +repository: https://github.com/pancakeswap/pancakeswapswap-subgraph +schema: + file: ./schema.graphql +dataSources: + - kind: ethereum/contract + name: PredictionV3AI + network: {{ network }} + source: + address: "{{ predictionV3AI.address}}" + abi: PredictionV3AI + startBlock: {{ predictionV3AI.startBlock }} + mapping: + kind: ethereum/events + apiVersion: 0.0.4 + language: wasm/assemblyscript + file: ./mappings/index.ts + entities: + - Market + - Round + - User + - Bet + abis: + - name: PredictionV3AI + file: ./abis/PredictionV3.json + eventHandlers: + - event: StartRound(indexed uint256,uint128) + handler: handleStartRound + - event: LockRound(indexed uint256,uint128) + handler: handleLockRound + - event: EndRound(indexed uint256,uint128) + handler: handleEndRound + - event: BetBull(indexed address,indexed uint256,uint128) + handler: handleBetBull + - event: BetBear(indexed address,indexed uint256,uint128) + handler: handleBetBear + - event: Claim(indexed address,indexed uint256,uint256) + handler: handleClaim + - event: Pause(indexed uint256) + handler: handlePause + - event: Unpause(indexed uint256) + handler: handleUnpause + - event: RewardsCalculated(indexed uint256,uint256,uint256,uint256) + handler: handleRewardsCalculated diff --git a/subgraphs/prediction/v3-ai-generic/subgraph.yaml b/subgraphs/prediction/v3-ai-generic/subgraph.yaml new file mode 100644 index 00000000..04eb2df5 --- /dev/null +++ b/subgraphs/prediction/v3-ai-generic/subgraph.yaml @@ -0,0 +1,45 @@ +specVersion: 0.0.2 +description: Pancake Prediction V3 +repository: https://github.com/pancakeswap/pancakeswapswap-subgraph +schema: + file: ./schema.graphql +dataSources: + - kind: ethereum/contract + name: PredictionV3AI + network: chapel + source: + address: "0x505B6e8DA1c31f4033ef5b70cba426E00538Fb3c" + abi: PredictionV3AI + startBlock: 40576515 + mapping: + kind: ethereum/events + apiVersion: 0.0.4 + language: wasm/assemblyscript + file: ./mappings/index.ts + entities: + - Market + - Round + - User + - Bet + abis: + - name: PredictionV3AI + file: ./abis/PredictionV3.json + eventHandlers: + - event: StartRound(indexed uint256,uint128) + handler: handleStartRound + - event: LockRound(indexed uint256,uint128) + handler: handleLockRound + - event: EndRound(indexed uint256,uint128) + handler: handleEndRound + - event: BetBull(indexed address,indexed uint256,uint128) + handler: handleBetBull + - event: BetBear(indexed address,indexed uint256,uint128) + handler: handleBetBear + - event: Claim(indexed address,indexed uint256,uint256) + handler: handleClaim + - event: Pause(indexed uint256) + handler: handlePause + - event: Unpause(indexed uint256) + handler: handleUnpause + - event: RewardsCalculated(indexed uint256,uint256,uint256,uint256) + handler: handleRewardsCalculated