diff --git a/sushiswap-farms/schema.graphql b/sushiswap-farms/schema.graphql index f30f6b45..b2dffe3b 100644 --- a/sushiswap-farms/schema.graphql +++ b/sushiswap-farms/schema.graphql @@ -163,6 +163,9 @@ type PositionSnapshot @entity { outputTokenBalance: BigInt! inputTokenBalances: [String!]! # TokenBalance string format rewardTokenBalances: [String!]! # TokenBalance string format + outputTokenBalanceFormula: String + inputTokenBalanceFormula: [String!] + rewardTokenBalanceFormula: [String!] transferredTo: [String!]! # TokenBalance string format } @@ -224,6 +227,12 @@ type SushiFarmSnapshot @entity { " total amount of LP tokens provided to farm " totalSupply: BigInt! + " last block number where sushi distribution occurs. " + lastRewardBlock: BigInt! + + " accumulated amount of sushi per share" + accSushiPerShare: BigInt! + " snapshot timestamp " timestamp: BigInt! @@ -257,6 +266,35 @@ type UserInfo @entity { rewardDebt: BigInt! } +type UserInfoSnapshot @entity { + " UserInfo.id-trasactionHash-logIndex " + id: ID! + + " UserInfo for which the snapshot is taken " + userInfo: UserInfo! + + " LP token amount the user has provided " + amount: BigInt! + + " amount of SUSHI entitled to the user " + rewardDebt: BigInt! + + " snapshot timestamp " + timestamp: BigInt! + + " block number in which snapshot is taken " + blockNumber: BigInt! + + " transaction in which snapshot is taken " + transactionHash: String! + + " index of transaction in block " + transactionIndexInBlock: BigInt! + + " snapshot log index " + logIndex: BigInt! +} + type FarmDeposit @entity { " transactionHash-logIndex " id: ID! @@ -358,6 +396,8 @@ type MasterChef @entity { " bonus muliplier for early sushi makers " bonusMultiplier: BigInt + + precision: BigInt! } type Rewarder @entity { diff --git a/sushiswap-farms/src/library/common.ts b/sushiswap-farms/src/library/common.ts index 1e168849..82fd0afa 100644 --- a/sushiswap-farms/src/library/common.ts +++ b/sushiswap-farms/src/library/common.ts @@ -173,6 +173,10 @@ export function updateMarket( inputTokenBalances: TokenBalance[], outputTokenTotalSupply: BigInt ): MarketSnapshot { + market.inputTokenTotalBalances = inputTokenBalances.map((tb) => tb.toString()); + market.outputTokenTotalSupply = outputTokenTotalSupply; + market.save(); + let transactionHash = event.transaction.hash.toHexString(); let id = transactionHash.concat("-").concat(event.logIndex.toHexString()); let marketSnapshot = MarketSnapshot.load(id); @@ -191,10 +195,6 @@ export function updateMarket( marketSnapshot.logIndex = event.logIndex; marketSnapshot.save(); - market.inputTokenTotalBalances = inputTokenBalances.map((tb) => tb.toString()); - market.outputTokenTotalSupply = outputTokenTotalSupply; - market.save(); - return marketSnapshot as MarketSnapshot; } @@ -332,6 +332,18 @@ export class TokenBalance { } } +export class TokenBalanceFormula { + output: string; + input: string[]; + reward: string[]; + + constructor(output: string, input: string[], reward: string[]) { + this.output = output; + this.input = input; + this.reward = reward; + } +} + /** * Create snapshot of user's position at certain block * @@ -339,7 +351,11 @@ export class TokenBalance { * @param {Transaction} transaction Transaction which triggered the change in position * @return {*} {PositionSnapshot} PositionSnapshot entity */ -function createPositionSnapshot(position: Position, transaction: Transaction): PositionSnapshot { +function createPositionSnapshot( + position: Position, + transaction: Transaction, + tokenBalanceFormula: TokenBalanceFormula | null +): PositionSnapshot { let newCounter = position.historyCounter.plus(BigInt.fromI32(1)); let newSnapshot = new PositionSnapshot(position.id.concat("-").concat(newCounter.toString())); newSnapshot.position = position.id; @@ -347,6 +363,9 @@ function createPositionSnapshot(position: Position, transaction: Transaction): P newSnapshot.outputTokenBalance = position.outputTokenBalance; newSnapshot.inputTokenBalances = position.inputTokenBalances; newSnapshot.rewardTokenBalances = position.rewardTokenBalances; + newSnapshot.outputTokenBalanceFormula = tokenBalanceFormula.output; + newSnapshot.inputTokenBalanceFormula = tokenBalanceFormula.input; + newSnapshot.rewardTokenBalanceFormula = tokenBalanceFormula.reward; newSnapshot.transferredTo = position.transferredTo; position.blockNumber = transaction.blockNumber; position.timestamp = transaction.timestamp; @@ -387,7 +406,8 @@ export function investInMarket( outputTokenBalance: BigInt, inputTokenBalances: TokenBalance[], rewardTokenBalances: TokenBalance[], - transferredFrom: string | null + transferredFrom: string | null, + tokenBalanceFormula: TokenBalanceFormula | null ): Position { // Create marketSnapshot for transaction let marketSnapshot = createMarketSnapshot(event, market); @@ -423,7 +443,6 @@ export function investInMarket( transaction.save(); let position = getOrCreateOpenPosition(event, account, market, PositionType.INVESTMENT); - let positionSnapshot = createPositionSnapshot(position, transaction); position.inputTokenBalances = inputTokenBalances.map((tb) => tb.toString()); position.outputTokenBalance = outputTokenBalance; @@ -435,6 +454,7 @@ export function investInMarket( } position.save(); + let positionSnapshot = createPositionSnapshot(position, transaction, tokenBalanceFormula); return position; } @@ -468,7 +488,8 @@ export function redeemFromMarket( outputTokenBalance: BigInt, inputTokenBalances: TokenBalance[], rewardTokenBalances: TokenBalance[], - transferredTo: string | null + transferredTo: string | null, + tokenBalanceFormula: TokenBalanceFormula | null ): Position { // Create marketSnapshot for transaction let marketSnapshot = createMarketSnapshot(event, market); @@ -504,7 +525,6 @@ export function redeemFromMarket( transaction.save(); let position = getOrCreateOpenPosition(event, account, market, PositionType.INVESTMENT); - let postionSnapshot = createPositionSnapshot(position, transaction); // No change in investment amount as no new investment has been made position.inputTokenBalances = inputTokenBalances.map((tb) => tb.toString()); @@ -527,6 +547,7 @@ export function redeemFromMarket( } position.save(); + let postionSnapshot = createPositionSnapshot(position, transaction, tokenBalanceFormula); return position; } @@ -556,7 +577,8 @@ export function borrowFromMarket( rewardTokenAmounts: TokenBalance[], outputTokenBalance: BigInt, inputTokenBalances: TokenBalance[], - rewardTokenBalances: TokenBalance[] + rewardTokenBalances: TokenBalance[], + tokenBalanceFormula: TokenBalanceFormula | null ): Position { // Create marketSnapshot for transaction let marketSnapshot = createMarketSnapshot(event, market); @@ -587,7 +609,6 @@ export function borrowFromMarket( transaction.save(); let position = getOrCreateOpenPosition(event, account, market, PositionType.DEBT); - let positionSnapshot = createPositionSnapshot(position, transaction); position.inputTokenBalances = inputTokenBalances.map((tb) => tb.toString()); position.outputTokenBalance = outputTokenBalance; @@ -599,6 +620,7 @@ export function borrowFromMarket( } position.save(); + let positionSnapshot = createPositionSnapshot(position, transaction, tokenBalanceFormula); return position; } @@ -629,7 +651,8 @@ export function repayToMarket( rewardTokenAmounts: TokenBalance[], outputTokenBalance: BigInt, inputTokenBalances: TokenBalance[], - rewardTokenBalances: TokenBalance[] + rewardTokenBalances: TokenBalance[], + tokenBalanceFormula: TokenBalanceFormula | null ): Position { // Create marketSnapshot for transaction let marketSnapshot = createMarketSnapshot(event, market); @@ -660,7 +683,6 @@ export function repayToMarket( transaction.save(); let position = getOrCreateOpenPosition(event, account, market, PositionType.DEBT); - let postionSnapshot = createPositionSnapshot(position, transaction); // Loan amount is not changed on repayment position.inputTokenBalances = inputTokenBalances.map((tb) => tb.toString()); @@ -673,6 +695,7 @@ export function repayToMarket( } position.save(); + let postionSnapshot = createPositionSnapshot(position, transaction, tokenBalanceFormula); return position; } diff --git a/sushiswap-farms/src/library/masterChefUtils.ts b/sushiswap-farms/src/library/masterChefUtils.ts index d1a074cc..900544aa 100644 --- a/sushiswap-farms/src/library/masterChefUtils.ts +++ b/sushiswap-farms/src/library/masterChefUtils.ts @@ -1,6 +1,6 @@ -import { BigInt } from "@graphprotocol/graph-ts"; +import { BigInt, ethereum } from "@graphprotocol/graph-ts"; -import { UserInfo } from "../../generated/schema"; +import { SushiFarm, SushiFarmSnapshot, UserInfo, UserInfoSnapshot } from "../../generated/schema"; /** * Create UserInfo entity which tracks how many LP tokens user provided and how many Sushi rewards he claimed @@ -23,3 +23,44 @@ export function getOrCreateUserInfo(user: string, farmId: string): UserInfo { return userInfo; } + +export function updateUserInfo(event: ethereum.Event, userInfo: UserInfo, amount: BigInt, rewardDebt: BigInt): UserInfo { + userInfo.amount = amount; + userInfo.rewardDebt = rewardDebt; + userInfo.save(); + + let id = userInfo.id + "-" + event.transaction.hash.toHexString() + "-" + event.logIndex.toHexString(); + let userInfoSnapshot = new UserInfoSnapshot(id); + + userInfoSnapshot.userInfo = userInfo.id; + userInfoSnapshot.amount = userInfo.amount; + userInfoSnapshot.rewardDebt = userInfo.rewardDebt; + userInfoSnapshot.timestamp = event.block.timestamp; + userInfoSnapshot.transactionHash = event.transaction.hash.toHexString(); + userInfoSnapshot.transactionIndexInBlock = event.transaction.index; + userInfoSnapshot.blockNumber = event.block.number; + userInfoSnapshot.logIndex = event.logIndex; + + userInfoSnapshot.save(); + + return userInfo; +} + +export function createFarmSnapshot(event: ethereum.Event, farm: SushiFarm): SushiFarmSnapshot { + let snapshotId = event.transaction.hash.toHexString() + "-" + event.logIndex.toHexString(); + let farmSnapshot = new SushiFarmSnapshot(snapshotId); + farmSnapshot.farmPid = farm.farmPid; + farmSnapshot.sushiFarm = farm.id; + farmSnapshot.allocPoint = farm.allocPoint; + farmSnapshot.totalSupply = farm.totalSupply; + farmSnapshot.accSushiPerShare = farm.accSushiPerShare; + farmSnapshot.lastRewardBlock = farm.lastRewardBlock; + farmSnapshot.timestamp = event.block.timestamp; + farmSnapshot.transactionHash = event.transaction.hash.toHexString(); + farmSnapshot.transactionIndexInBlock = event.transaction.index; + farmSnapshot.blockNumber = event.block.number; + farmSnapshot.logIndex = event.logIndex; + farmSnapshot.save(); + + return farmSnapshot; +} diff --git a/sushiswap-farms/src/mappings/masterChef.ts b/sushiswap-farms/src/mappings/masterChef.ts index 003e6f0e..d8db304a 100644 --- a/sushiswap-farms/src/mappings/masterChef.ts +++ b/sushiswap-farms/src/mappings/masterChef.ts @@ -29,9 +29,10 @@ import { investInMarket, redeemFromMarket, TokenBalance, + TokenBalanceFormula, } from "../library/common"; -import { getOrCreateUserInfo } from "../library/masterChefUtils"; +import { createFarmSnapshot, getOrCreateUserInfo, updateUserInfo } from "../library/masterChefUtils"; import { ProtocolName, ProtocolType } from "../library/constants"; @@ -67,12 +68,13 @@ export function handleAdd(call: AddCall): void { masterChef.sushiPerBlock = masterChefContract.sushiPerBlock(); masterChef.bonusEndBlock = masterChefContract.bonusEndBlock(); masterChef.bonusMultiplier = masterChefContract.BONUS_MULTIPLIER(); + masterChef.precision = ACC_SUSHI_PRECISION; masterChef.save(); } // update all farms reward variables if (call.inputs._withUpdate) { - massUpdateFarms(masterChef as MasterChefEntity, call.block); + massUpdateFarms(masterChef as MasterChefEntity, call); } // create SushiFarm entity @@ -100,7 +102,7 @@ export function handleDeposit(event: Deposit): void { let amount = event.params.amount; // update farm/pool reward variables - updateFarm(sushiFarm, event.block); + updateFarmEvent(sushiFarm, event); // save new deposit entity let deposit = new FarmDeposit( @@ -120,15 +122,16 @@ export function handleDeposit(event: Deposit): void { .minus(userInfo.rewardDebt); // increase user's balance of provided LP tokens and amount of rewards entitled to user - userInfo.amount = userInfo.amount.plus(amount); - userInfo.rewardDebt = userInfo.amount.times(sushiFarm.accSushiPerShare).div(ACC_SUSHI_PRECISION); - userInfo.save(); + let newUserInfoAmount = userInfo.amount.plus(amount); + let newUserInfoRewardDebt = userInfo.amount.times(sushiFarm.accSushiPerShare).div(ACC_SUSHI_PRECISION); + userInfo = updateUserInfo(event, userInfo, newUserInfoAmount, newUserInfoRewardDebt); ////// update market LP supply // update sushifarm sushiFarm.totalSupply = sushiFarm.totalSupply.plus(amount); sushiFarm.save(); + createFarmSnapshot(event, sushiFarm); // update market LP supply let market = Market.load(sushiFarm.id) as Market; @@ -165,6 +168,9 @@ export function handleDeposit(event: Deposit): void { new TokenBalance(rewardTokens[0], user.id, BigInt.fromI32(0)), ]; + let rewardTokenBalanceFormula = "(userInfoSnapshots.amount * sushiFarmSnapshots.accSushiPerShare / masterChef.precision) - userInfoSnapshots.rewardDebt|" + "userInfoSnapshots.userInfo:" + userInfo.id + "|sushiFarmSnapshots.sushiFarm:"+sushiFarm.id + "|masterChef.id:"+ masterChef.id; + let tokenBalanceFormula = new TokenBalanceFormula(null, null, [rewardTokenBalanceFormula]); + investInMarket( event, user, @@ -175,7 +181,8 @@ export function handleDeposit(event: Deposit): void { outputTokenBalance, inputTokenBalances, rewardTokenBalances, - null + null, + tokenBalanceFormula ); } @@ -192,7 +199,7 @@ export function handleWithdraw(event: Withdraw): void { let amount = event.params.amount; // update farm/pool reward variables - updateFarm(sushiFarm, event.block); + updateFarmEvent(sushiFarm, event); // save new withdrawal entity let withdrawal = new FarmWithdrawal( @@ -212,15 +219,16 @@ export function handleWithdraw(event: Withdraw): void { .minus(userInfo.rewardDebt); // decrease user's balance of provided LP tokens and amount of rewards entitled to user - userInfo.amount = userInfo.amount.minus(amount); - userInfo.rewardDebt = userInfo.amount.times(sushiFarm.accSushiPerShare).div(ACC_SUSHI_PRECISION); - userInfo.save(); + let newUserInfoAmount = userInfo.amount.minus(amount); + let newUserInfoRewardDebt = userInfo.amount.times(sushiFarm.accSushiPerShare).div(ACC_SUSHI_PRECISION); + userInfo = updateUserInfo(event, userInfo, newUserInfoAmount, newUserInfoRewardDebt); ////// update market LP supply // update sushifarm sushiFarm.totalSupply = sushiFarm.totalSupply.minus(amount); sushiFarm.save(); + createFarmSnapshot(event, sushiFarm); // update market let market = Market.load(sushiFarm.id) as Market; @@ -257,6 +265,9 @@ export function handleWithdraw(event: Withdraw): void { new TokenBalance(rewardTokens[0], user.id, BigInt.fromI32(0)), ]; + let rewardTokenBalanceFormula = "(UserInfo.amount * SushiFarm.accSushiPerShare / MasterChef.precision) - UserInfo.rewardDebt|" + "UserInfo:" + userInfo.id + "|SushiFarm:"+sushiFarm.id + "|MasterChef:"+ masterChef.id; + let tokenBalanceFormula = new TokenBalanceFormula(null, null, [rewardTokenBalanceFormula]); + redeemFromMarket( event, user, @@ -267,7 +278,8 @@ export function handleWithdraw(event: Withdraw): void { outputTokenBalance, inputTokenBalances, rewardTokenBalances, - null + null, + tokenBalanceFormula ); } @@ -295,9 +307,7 @@ export function handleEmergencyWithdraw(event: EmergencyWithdraw): void { // LP token balance and claimable rewards are resetted to 0 in EmergencyWithdraw let userInfo = getOrCreateUserInfo(user.id, sushiFarm.id); - userInfo.amount = BigInt.fromI32(0); - userInfo.rewardDebt = BigInt.fromI32(0); - userInfo.save(); + userInfo = updateUserInfo(event, userInfo, BigInt.fromI32(0), BigInt.fromI32(0)); ////// update market LP supply @@ -305,6 +315,8 @@ export function handleEmergencyWithdraw(event: EmergencyWithdraw): void { sushiFarm.totalSupply = sushiFarm.totalSupply.minus(amount); sushiFarm.save(); + createFarmSnapshot(event, sushiFarm); + // update market let market = Market.load(sushiFarm.id) as Market; updateMarket( @@ -338,6 +350,9 @@ export function handleEmergencyWithdraw(event: EmergencyWithdraw): void { new TokenBalance(rewardTokens[0], user.id, BigInt.fromI32(0)), ]; + let rewardTokenBalanceFormula = "(UserInfo.amount * SushiFarm.accSushiPerShare / MasterChef.precision) - UserInfo.rewardDebt|" + "UserInfo:" + userInfo.id + "|SushiFarm:"+sushiFarm.id + "|MasterChef:"+ masterChef.id; + let tokenBalanceFormula = new TokenBalanceFormula(null, null, [rewardTokenBalanceFormula]); + redeemFromMarket( event, user, @@ -348,7 +363,8 @@ export function handleEmergencyWithdraw(event: EmergencyWithdraw): void { outputTokenBalance, inputTokenBalances, rewardTokenBalances, - null + null, + tokenBalanceFormula ); } @@ -359,7 +375,7 @@ export function handleEmergencyWithdraw(event: EmergencyWithdraw): void { export function handleUpdatePool(call: UpdatePoolCall): void { let masterChef = call.to.toHexString(); let sushiFarm = SushiFarm.load(masterChef + "-" + call.inputs._pid.toString()) as SushiFarm; - updateFarm(sushiFarm, call.block); + updateFarm(sushiFarm, call); } /** @@ -373,7 +389,7 @@ export function handleSet(call: SetCall): void { // update all farms if (call.inputs._withUpdate) { - massUpdateFarms(masterChefEntity, call.block); + massUpdateFarms(masterChefEntity, call); } // update totalalloc of MasterChef @@ -385,6 +401,12 @@ export function handleSet(call: SetCall): void { // update sushifarm sushiFarm.allocPoint = call.inputs._allocPoint; sushiFarm.save(); + let event = new ethereum.Event(); + event.block = call.block; + event.transaction = call.transaction; + // Workaround to keep API of farmSnapshot same + event.logIndex = call.transaction.index; + createFarmSnapshot(event, sushiFarm); } /** @@ -433,7 +455,7 @@ export function handleMigrate(call: MigrateCall): void { */ export function handleMassUpdatePools(call: MassUpdatePoolsCall): void { let masterChef = MasterChefEntity.load(call.to.toHexString()) as MasterChefEntity; - massUpdateFarms(masterChef, call.block); + massUpdateFarms(masterChef, call); } /** @@ -441,11 +463,24 @@ export function handleMassUpdatePools(call: MassUpdatePoolsCall): void { * * Implementation loosely copied from MasterChef's `updatePool` function. * @param sushiFarm - * @param event + * @param call * @returns */ -function updateFarm(sushiFarm: SushiFarm, block: ethereum.Block): void { +function updateFarm(sushiFarm: SushiFarm, call: ethereum.Call): void { let masterChef = MasterChefEntity.load(sushiFarm.masterChef) as MasterChefEntity; + let block = call.block + let event = new ethereum.Event(); + event.block = block; + event.transaction = call.transaction; + // Workaround to keep API of farmSnapshot same + event.logIndex = call.transaction.index; + + updateFarmEvent(sushiFarm, event); +} + +function updateFarmEvent(sushiFarm: SushiFarm, event: ethereum.Event): void { + let masterChef = MasterChefEntity.load(sushiFarm.masterChef) as MasterChefEntity; + let block = event.block if (block.number.le(sushiFarm.lastRewardBlock)) { return; @@ -454,6 +489,7 @@ function updateFarm(sushiFarm: SushiFarm, block: ethereum.Block): void { if (sushiFarm.totalSupply == BigInt.fromI32(0)) { sushiFarm.lastRewardBlock = block.number; sushiFarm.save(); + createFarmSnapshot(event, sushiFarm); return; } @@ -468,6 +504,8 @@ function updateFarm(sushiFarm: SushiFarm, block: ethereum.Block): void { ); sushiFarm.lastRewardBlock = block.number; sushiFarm.save(); + + createFarmSnapshot(event, sushiFarm); } /** @@ -494,13 +532,13 @@ function getMultiplier(masterChef: MasterChefEntity, from: BigInt, to: BigInt): /** * Update reward variables for all pools * @param masterChef - * @param block + * @param call */ -function massUpdateFarms(masterChef: MasterChefEntity, block: ethereum.Block): void { +function massUpdateFarms(masterChef: MasterChefEntity, call: ethereum.Call): void { let length = masterChef.numberOfFarms.toI32(); for (let pid: i32 = 0; pid < length; ++pid) { let sushiFarm = SushiFarm.load(masterChef.id + "-" + pid.toString()) as SushiFarm; - updateFarm(sushiFarm, block); + updateFarm(sushiFarm, call); } } @@ -558,6 +596,7 @@ function getOrCreateSushiFarm( } sushiFarm.save(); + createFarmSnapshot(event, sushiFarm); // create market representing the farm let marketId = sushiFarm.id; diff --git a/sushiswap-farms/src/mappings/masterChefV2.ts b/sushiswap-farms/src/mappings/masterChefV2.ts index 12bad794..f338ec1c 100644 --- a/sushiswap-farms/src/mappings/masterChefV2.ts +++ b/sushiswap-farms/src/mappings/masterChefV2.ts @@ -18,7 +18,6 @@ import { IRewarder } from "../../generated/MasterChefV2/IRewarder"; import { SushiFarm, - SushiFarmSnapshot, FarmDeposit, FarmWithdrawal, UserInfo, @@ -40,9 +39,10 @@ import { redeemFromMarket, TokenBalance, ADDRESS_ZERO, + TokenBalanceFormula, } from "../library/common"; -import { getOrCreateUserInfo } from "../library/masterChefUtils"; +import { createFarmSnapshot, getOrCreateUserInfo, updateUserInfo } from "../library/masterChefUtils"; import { RewardToken } from "../../generated/templates"; @@ -77,6 +77,7 @@ export function handleLogPoolAddition(event: LogPoolAddition): void { masterChef.numberOfFarms = BigInt.fromI32(0); masterChef.totalAllocPoint = BigInt.fromI32(0); masterChef.sushiPerBlock = masterChefContract.sushiPerBlock(); + masterChef.precision = ACC_SUSHI_PRECISION; masterChef.save(); } @@ -104,6 +105,8 @@ export function handleLogPoolAddition(event: LogPoolAddition): void { sushiFarm.accSushiPerShare = BigInt.fromI32(0); sushiFarm.save(); + createFarmSnapshot(event, sushiFarm); + // numberOfFarms++ masterChef.numberOfFarms = masterChef.numberOfFarms.plus(BigInt.fromI32(1)); masterChef.totalAllocPoint = masterChef.totalAllocPoint.plus(sushiFarm.allocPoint); @@ -160,11 +163,11 @@ export function handleDeposit(event: Deposit): void { // increase user's balance of provided LP tokens and amount of rewards entitled to user let userInfo = getOrCreateUserInfo(deposit.depositReceiver, sushiFarm.id); - userInfo.amount = userInfo.amount.plus(amount); - userInfo.rewardDebt = userInfo.rewardDebt.plus( + let newUserInfoAmount = userInfo.amount.plus(amount); + let newUserInfoRewardDebt = userInfo.rewardDebt.plus( amount.times(sushiFarm.accSushiPerShare).div(ACC_SUSHI_PRECISION) ); - userInfo.save(); + userInfo = updateUserInfo(event, userInfo, newUserInfoAmount, newUserInfoRewardDebt); ////// update user's position @@ -194,6 +197,9 @@ export function handleDeposit(event: Deposit): void { let rewardTokenBalances: TokenBalance[] = []; collectRewardTokenBalances(sushiFarm, receiver, rewardTokenBalances, market); + let rewardTokenBalanceFormula = "(userInfoSnapshots.amount * sushiFarmSnapshots.accSushiPerShare / masterChef.precision) - userInfoSnapshots.rewardDebt|" + "userInfoSnapshots.userInfo:" + userInfo.id + "|sushiFarmSnapshots.sushiFarm:"+sushiFarm.id + "|masterChef.id:"+ masterChef; + let tokenBalanceFormula = new TokenBalanceFormula(null, null, [rewardTokenBalanceFormula]); + investInMarket( event, receiver, @@ -204,7 +210,8 @@ export function handleDeposit(event: Deposit): void { outputTokenBalance, inputTokenBalances, rewardTokenBalances, - null + null, + tokenBalanceFormula ); } @@ -242,21 +249,23 @@ export function handleWithdraw(event: Withdraw): void { // if there are preceding reward transfers then this event is part of WithdrawAndHarvest function, // otherwise it is part of just a Withdraw function + let newUserInfoAmount = userInfo.amount; + let newUserInfoRewardDebt = userInfo.rewardDebt; if (isThereUnprocessedRewardTransfer(market, event)) { let accSushi = userInfo.amount.times(sushiFarm.accSushiPerShare).div(ACC_SUSHI_PRECISION); - userInfo.rewardDebt = accSushi.minus( + newUserInfoRewardDebt = accSushi.minus( amount.times(sushiFarm.accSushiPerShare).div(ACC_SUSHI_PRECISION) ); - userInfo.amount = userInfo.amount.minus(amount); + newUserInfoAmount = userInfo.amount.minus(amount); } else { // decrease user's balance of provided LP tokens and amount of rewards entitled to user - userInfo.amount = userInfo.amount.minus(amount); - userInfo.rewardDebt = userInfo.rewardDebt.minus( + newUserInfoAmount = userInfo.amount.minus(amount); + newUserInfoRewardDebt = userInfo.rewardDebt.minus( amount.times(sushiFarm.accSushiPerShare).div(ACC_SUSHI_PRECISION) ); } - userInfo.save(); + userInfo = updateUserInfo(event, userInfo, newUserInfoAmount, newUserInfoRewardDebt); ////// update user's position @@ -285,6 +294,9 @@ export function handleWithdraw(event: Withdraw): void { let rewardTokenBalances: TokenBalance[] = []; collectRewardTokenBalances(sushiFarm, user, rewardTokenBalances, market); + let rewardTokenBalanceFormula = "(userInfoSnapshots.amount * sushiFarmSnapshots.accSushiPerShare / masterChef.precision) - userInfoSnapshots.rewardDebt|" + "userInfoSnapshots.userInfo:" + userInfo.id + "|sushiFarmSnapshots.sushiFarm:"+sushiFarm.id + "|masterChef.id:"+ masterChef; + let tokenBalanceFormula = new TokenBalanceFormula(null, null, [rewardTokenBalanceFormula]); + redeemFromMarket( event, user, @@ -295,7 +307,8 @@ export function handleWithdraw(event: Withdraw): void { outputTokenBalance, inputTokenBalances, rewardTokenBalances, - null + null, + tokenBalanceFormula ); } @@ -330,9 +343,7 @@ export function handleEmergencyWithdraw(event: EmergencyWithdraw): void { // LP token balance and claimable rewards are resetted to 0 in EmergencyWithdraw let userInfo = getOrCreateUserInfo(withdrawal.withdrawer, sushiFarm.id); - userInfo.amount = BigInt.fromI32(0); - userInfo.rewardDebt = BigInt.fromI32(0); - userInfo.save(); + userInfo = updateUserInfo(event, userInfo, BigInt.fromI32(0), BigInt.fromI32(0)); ////// update user's position @@ -362,6 +373,9 @@ export function handleEmergencyWithdraw(event: EmergencyWithdraw): void { let rewardTokenBalances: TokenBalance[] = []; collectRewardTokenBalances(sushiFarm, user, rewardTokenBalances, market); + let rewardTokenBalanceFormula = "(userInfoSnapshots.amount * sushiFarmSnapshots.accSushiPerShare / masterChef.precision) - userInfoSnapshots.rewardDebt|" + "userInfoSnapshots.userInfo:" + userInfo.id + "|sushiFarmSnapshots.sushiFarm:"+sushiFarm.id + "|masterChef.id:"+ masterChef; + let tokenBalanceFormula = new TokenBalanceFormula(null, null, [rewardTokenBalanceFormula]); + redeemFromMarket( event, user, @@ -372,7 +386,8 @@ export function handleEmergencyWithdraw(event: EmergencyWithdraw): void { outputTokenBalance, inputTokenBalances, rewardTokenBalances, - null + null, + tokenBalanceFormula ); } @@ -402,8 +417,8 @@ export function handleHarvest(event: Harvest): void { // updated user's rewardDebt which tracks total amount of claimed Sushi tokens let userInfo = getOrCreateUserInfo(harvester.id, sushiFarm.id); - userInfo.rewardDebt = userInfo.amount.times(sushiFarm.accSushiPerShare).div(ACC_SUSHI_PRECISION); - userInfo.save(); + let newUserInfoRewardDebt = userInfo.amount.times(sushiFarm.accSushiPerShare).div(ACC_SUSHI_PRECISION); + userInfo = updateUserInfo(event, userInfo, userInfo.amount, newUserInfoRewardDebt); ////// update user's position @@ -428,6 +443,9 @@ export function handleHarvest(event: Harvest): void { let rewardTokenBalances: TokenBalance[] = []; collectRewardTokenBalances(sushiFarm, harvester, rewardTokenBalances, market); + let rewardTokenBalanceFormula = "(userInfoSnapshots.amount * sushiFarmSnapshots.accSushiPerShare / masterChef.precision) - userInfoSnapshots.rewardDebt|" + "userInfoSnapshots.userInfo:" + userInfo.id + "|sushiFarmSnapshots.sushiFarm:"+sushiFarm.id + "|masterChef.id:"+ masterChef; + let tokenBalanceFormula = new TokenBalanceFormula(null, null, [rewardTokenBalanceFormula]); + redeemFromMarket( event, harvester, @@ -438,7 +456,8 @@ export function handleHarvest(event: Harvest): void { outputTokenBalance, inputTokenBalances, rewardTokenBalances, - null + null, + tokenBalanceFormula ); } @@ -450,20 +469,6 @@ export function handleLogUpdatePool(event: LogUpdatePool): void { let masterChef = event.address.toHexString(); let sushiFarm = SushiFarm.load(masterChef + "-" + event.params.pid.toString()) as SushiFarm; - // create farm snapshot - let snapshotId = event.transaction.hash.toHexString() + "-" + event.logIndex.toHexString(); - let farmSnapshot = new SushiFarmSnapshot(snapshotId); - farmSnapshot.farmPid = event.params.pid; - farmSnapshot.sushiFarm = sushiFarm.id; - farmSnapshot.allocPoint = sushiFarm.allocPoint; - farmSnapshot.totalSupply = sushiFarm.totalSupply; - farmSnapshot.timestamp = event.block.timestamp; - farmSnapshot.transactionHash = event.transaction.hash.toHexString(); - farmSnapshot.transactionIndexInBlock = event.transaction.index; - farmSnapshot.blockNumber = event.block.number; - farmSnapshot.logIndex = event.logIndex; - farmSnapshot.save(); - // update sushifarm let oldAccSushiPerShare = sushiFarm.accSushiPerShare; sushiFarm.lastRewardBlock = event.params.lastRewardBlock; @@ -471,6 +476,9 @@ export function handleLogUpdatePool(event: LogUpdatePool): void { sushiFarm.accSushiPerShare = event.params.accSushiPerShare; sushiFarm.save(); + // create farm snapshot + let farmSnapshot = createFarmSnapshot(event, sushiFarm); + // update market let market = Market.load(sushiFarm.id) as Market; updateMarket( @@ -502,6 +510,8 @@ export function handleLogSetPool(event: LogSetPool): void { sushiFarm.rewarder = event.params.rewarder.toHexString(); } sushiFarm.save(); + + let sushiFarmSnapshot = createFarmSnapshot(event, sushiFarm); } /**