diff --git a/agents/bot-operations-agent/README.md b/agents/bot-operations-agent/README.md index 3387cea..e17f07b 100644 --- a/agents/bot-operations-agent/README.md +++ b/agents/bot-operations-agent/README.md @@ -6,13 +6,6 @@ ## Alerts -- BNBx-REWARD-CHANGE - - - Fired when Reward changes by more than 0.5 % - - Severity is set to "Medium" - - Type is set to "Info" - - metadata: lastRewardAmount, curentRewardAmount - - BNBx-DAILY-REWARDS - Fired when Daily Rewards is not executed @@ -22,35 +15,35 @@ - BNBx-START-DELEGATION - - Fired when StartDelegation is not executed for 36 hours + - Fired when StartDelegation is not executed for 48 hours - Severity is set to "Critical" - Type is set to "Info" - metadata: lastStartDelegationTime - BNBx-COMPLETE-DELEGATION - - Fired when CompleteDelegation is not executed for 12 hours past StartDelegation + - Fired when CompleteDelegation is not executed for 1 hour past StartDelegation - Severity is set to "Critical" - Type is set to "Info" - metadata: lastStartDelegationTime, lastCompleteDelegationTime - BNBx-START-UNDELEGATION - - Fired when StartUndelegation is not executed for 7 days and 1 hours + - Fired when StartUndelegation is not executed for 10 mins past its schedule time - Severity is set to "Critical" - Type is set to "Info" - metadata: lastStartUndelegationTime - BNBx-UNDELEGATION-UPDATE - - Fired when undelegationStarted is not executed for 12 hours past StartUndelegation + - Fired when undelegationStarted is not executed for 30 Mins past StartUndelegation - Severity is set to "Critical" - Type is set to "Info" - metadata: lastStartDelegationTime, lastUndelegationUpdateTime - BNBx-COMPLETE-UNDELEGATION - - Fired when CompleteUndelegation is not executed for 8 days and 12 hours past StartUndelegation + - Fired when CompleteUndelegation is not executed for 7 days and 3 hours past StartUndelegation - Severity is set to "Critical" - Type is set to "Info" - metadata: lastStartUndelegationTime, lastCompleteUndelegationTime diff --git a/agents/bot-operations-agent/package-lock.json b/agents/bot-operations-agent/package-lock.json index 29a5914..b8a4d25 100644 --- a/agents/bot-operations-agent/package-lock.json +++ b/agents/bot-operations-agent/package-lock.json @@ -1,11 +1,11 @@ { - "name": "forta-agent-starter", + "name": "bnbx-bot-operations-agent", "version": "0.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "forta-agent-starter", + "name": "bnbx-bot-operations-agent", "version": "0.0.1", "dependencies": { "forta-agent": "^0.1.9" diff --git a/agents/bot-operations-agent/package.json b/agents/bot-operations-agent/package.json index a042485..e0dcb5c 100644 --- a/agents/bot-operations-agent/package.json +++ b/agents/bot-operations-agent/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "Alerts on Off Chain Bot Delays and Failures", "chainIds": [ - 1 + 56 ], "scripts": { "build": "tsc", diff --git a/agents/bot-operations-agent/publish.log b/agents/bot-operations-agent/publish.log index b111a4d..a78eba9 100644 --- a/agents/bot-operations-agent/publish.log +++ b/agents/bot-operations-agent/publish.log @@ -2,3 +2,4 @@ Tue, 30 Aug 2022 19:58:56 GMT: successfully added agent id 0xb62f2ab23098094bf40 Wed, 31 Aug 2022 20:59:13 GMT: successfully updated agent id 0xb62f2ab23098094bf40db49a7379222d44f94bc656cd67906cf173b2eadb2e9c with manifest QmVKTNiRU9k4naWSwVNK54GovBfVzd7xTin77hFp31g7yK Mon, 05 Sep 2022 18:28:37 GMT: successfully updated agent id 0xb62f2ab23098094bf40db49a7379222d44f94bc656cd67906cf173b2eadb2e9c with manifest QmYKn7Ytmdi6BWCS1McXiYME3VoSwNaejHqicjnWX4axEs Wed, 07 Sep 2022 19:30:06 GMT: successfully updated agent id 0xb62f2ab23098094bf40db49a7379222d44f94bc656cd67906cf173b2eadb2e9c with manifest QmWDUsnKTu2FTq9UXpSeJcEu9FiFjBiyoqnoqWnsPc5gzq +Thu, 22 Dec 2022 16:28:37 GMT: successfully updated agent id 0xb62f2ab23098094bf40db49a7379222d44f94bc656cd67906cf173b2eadb2e9c with manifest QmRjvzT164q3cEpYqCD6HJ4KK13sLWtRM8J17SCP9JR1cY diff --git a/agents/bot-operations-agent/src/agent.ts b/agents/bot-operations-agent/src/agent.ts index 1b84c4e..42dc214 100644 --- a/agents/bot-operations-agent/src/agent.ts +++ b/agents/bot-operations-agent/src/agent.ts @@ -1,4 +1,3 @@ -import { BigNumber } from "ethers"; import { Finding, FindingSeverity, @@ -12,24 +11,21 @@ import { COMPLETE_UNDELEGATION_DELAY, COMPLETE_UNDELEGATION_FN, protocol, - REWARD_CHANGE_BPS, REWARD_DELAY_HOURS, REWARD_EVENT, STAKE_MANAGER, START_DELEGATION_DELAY, START_DELEGATION_FN, START_UNDELEGATION_DELAY, + START_UNDELEGATION_DELAY_MINS, START_UNDELEGATION_FN, - TOTAL_BPS, - UNDELEGATION_UPDATE_DELAY, + UNDELEGATION_UPDATE_DELAY_MINS, UNDELEGATION_UPDATE_FN, } from "./constants"; -import { getHours } from "./utils"; +import { getHours, getMins } from "./utils"; -let dailyRewardsFailed: boolean, - lastRewardsTime: Date, - lastRewardAmount: BigNumber; +let dailyRewardsFailed: boolean, lastRewardsTime: Date; let lastStartDelegationTime: Date, startDelegationFailed: boolean; let lastCompleteDelegationTime: Date, completeDelegationFailed: boolean; @@ -43,7 +39,7 @@ const handleTransaction: HandleTransaction = async ( ) => { const findings: Finding[] = ( await Promise.all([ - // handleRewardTransaction(txEvent), + handleRewardTransaction(txEvent), handleStartDelegationTransaction(txEvent), handleCompleteDelegationTransaction(txEvent), handleStartUndelegationTransaction(txEvent), @@ -55,73 +51,44 @@ const handleTransaction: HandleTransaction = async ( return findings; }; -// const handleRewardTransaction: HandleTransaction = async ( -// txEvent: TransactionEvent -// ) => { -// const findings: Finding[] = []; -// const bnbxRewardEvents = txEvent.filterLog(REWARD_EVENT, STAKE_MANAGER); - -// if (bnbxRewardEvents.length) { -// const { _amount } = bnbxRewardEvents[0].args; -// if (lastRewardsTime) { -// if ( -// lastRewardAmount -// .sub(_amount) -// .abs() -// .gt(lastRewardAmount.mul(REWARD_CHANGE_BPS).div(TOTAL_BPS)) -// ) { -// findings.push( -// Finding.fromObject({ -// name: "Significant Reward Change", -// description: `Reward changed more than ${ -// (REWARD_CHANGE_BPS * 100) / TOTAL_BPS -// } %`, -// alertId: "BNBx-REWARD-CHANGE", -// protocol: protocol, -// severity: FindingSeverity.Medium, -// type: FindingType.Info, -// metadata: { -// lastRewardAmount: lastRewardAmount.toString(), -// cuurentRewardAmount: _amount.toString(), -// }, -// }) -// ); -// } -// } - -// lastRewardsTime = new Date(); -// dailyRewardsFailed = false; -// lastRewardAmount = _amount; - -// return findings; -// } - -// if (!lastRewardsTime) return findings; - -// if (dailyRewardsFailed) return findings; - -// const currentTime = new Date(); -// const diff = currentTime.getTime() - lastRewardsTime.getTime(); -// const diffHours = getHours(diff); -// if (diffHours > REWARD_DELAY_HOURS) { -// findings.push( -// Finding.fromObject({ -// name: "Daily Rewards Failed", -// description: `Daily Rewards Autocompund not invoked since ${REWARD_DELAY_HOURS} Hours`, -// alertId: "BNBx-DAILY-REWARDS", -// protocol: protocol, -// severity: FindingSeverity.Critical, -// type: FindingType.Info, -// metadata: { -// lastRewardsTime: lastRewardsTime.toUTCString(), -// }, -// }) -// ); -// dailyRewardsFailed = true; -// } - -// return findings; -// }; +const handleRewardTransaction: HandleTransaction = async ( + txEvent: TransactionEvent +) => { + const findings: Finding[] = []; + const bnbxRewardEvents = txEvent.filterLog(REWARD_EVENT, STAKE_MANAGER); + + if (bnbxRewardEvents.length) { + lastRewardsTime = new Date(); + dailyRewardsFailed = false; + return []; + } + + if (!lastRewardsTime) return []; + + if (dailyRewardsFailed) return []; + + const currentTime = new Date(); + const diff = currentTime.getTime() - lastRewardsTime.getTime(); + const diffHours = getHours(diff); + if (diffHours > REWARD_DELAY_HOURS) { + findings.push( + Finding.fromObject({ + name: "Daily Rewards Failed", + description: `Rewards Autocompound not invoked since ${REWARD_DELAY_HOURS} Hours`, + alertId: "BNBx-DAILY-REWARDS", + protocol: protocol, + severity: FindingSeverity.Critical, + type: FindingType.Info, + metadata: { + lastRewardsTime: lastRewardsTime.toUTCString(), + }, + }) + ); + dailyRewardsFailed = true; + } + + return findings; +}; const handleStartDelegationTransaction: HandleTransaction = async ( txEvent: TransactionEvent @@ -132,15 +99,18 @@ const handleStartDelegationTransaction: HandleTransaction = async ( STAKE_MANAGER ); + // found startDelegation -> no alert if (startDelegationInvocations.length) { lastStartDelegationTime = new Date(); startDelegationFailed = false; - return findings; + return []; } - if (!lastStartDelegationTime) return findings; + // startDelegation not invoked since forta bot deployment -> no alert + if (!lastStartDelegationTime) return []; - if (startDelegationFailed) return findings; + // already alerted earlier -> repeated alert will make channel noisy + if (startDelegationFailed) return []; const currentTime = new Date(); const diff = currentTime.getTime() - lastStartDelegationTime.getTime(); @@ -177,27 +147,27 @@ const handleCompleteDelegationTransaction: HandleTransaction = async ( if (completeDelegationInvocations.length) { lastCompleteDelegationTime = new Date(); completeDelegationFailed = false; - return findings; + return []; } if (!lastStartDelegationTime || !lastCompleteDelegationTime) { - return findings; + return []; } - if (startDelegationFailed || completeDelegationFailed) return findings; + if (startDelegationFailed || completeDelegationFailed) return []; const currentTime = new Date(); const diff = currentTime.getTime() - lastStartDelegationTime.getTime(); const diffHours = getHours(diff); if ( - diffHours > COMPLETE_DELEGATION_DELAY && + diffHours >= COMPLETE_DELEGATION_DELAY && lastStartDelegationTime.getTime() > lastCompleteDelegationTime.getTime() ) { findings.push( Finding.fromObject({ name: "Complete Delegation Failed", - description: `Complete Delegation not invoked since ${COMPLETE_DELEGATION_DELAY} Hours past last Start Delegation`, + description: `Complete Delegation not invoked since ${COMPLETE_DELEGATION_DELAY} Hour past last Start Delegation`, alertId: "BNBx-COMPLETE-DELEGATION", protocol: protocol, severity: FindingSeverity.Critical, @@ -226,21 +196,26 @@ const handleStartUndelegationTransaction: HandleTransaction = async ( if (startUndelegationInvocations.length) { lastStartUndelegationTime = new Date(); startUndelegationFailed = false; - return findings; + return []; } - if (!lastStartUndelegationTime) return findings; + if (!lastStartUndelegationTime) return []; - if (startUndelegationFailed) return findings; + if (startUndelegationFailed) return []; const currentTime = new Date(); const diff = currentTime.getTime() - lastStartUndelegationTime.getTime(); const diffHours = getHours(diff); - if (diffHours > START_UNDELEGATION_DELAY) { + const diffMins = getMins(diff); + + if ( + diffHours >= START_UNDELEGATION_DELAY && + diffMins >= START_UNDELEGATION_DELAY_MINS + ) { findings.push( Finding.fromObject({ name: "Start Undelegation Failed", - description: `Start Undelegation not invoked since ${START_UNDELEGATION_DELAY} Hours`, + description: `Start Undelegation not invoked ${START_UNDELEGATION_DELAY_MINS} Mins since its schedule time`, alertId: "BNBx-START-UNDELEGATION", protocol: protocol, severity: FindingSeverity.Critical, @@ -268,27 +243,27 @@ const handleUndelegationUpdateTransaction: HandleTransaction = async ( if (UndelegationUpdateInvocations.length) { lastUndelegationUpdateTime = new Date(); undelegationUpdateFailed = false; - return findings; + return []; } if (!lastStartUndelegationTime || !lastUndelegationUpdateTime) { - return findings; + return []; } - if (startUndelegationFailed || undelegationUpdateFailed) return findings; + if (startUndelegationFailed || undelegationUpdateFailed) return []; const currentTime = new Date(); const diff = currentTime.getTime() - lastStartUndelegationTime.getTime(); - const diffHours = getHours(diff); + const diffMins = getMins(diff); if ( - diffHours > UNDELEGATION_UPDATE_DELAY && + diffMins >= UNDELEGATION_UPDATE_DELAY_MINS && lastStartUndelegationTime.getTime() > lastUndelegationUpdateTime.getTime() ) { findings.push( Finding.fromObject({ name: "Undelegation Update Failed", - description: `Undelegation not invoked at Beacon Chain since ${UNDELEGATION_UPDATE_DELAY} Hours past last Start UnDelegation`, + description: `Undelegation not invoked at Beacon Chain since ${UNDELEGATION_UPDATE_DELAY_MINS} Mins past last Start UnDelegation`, alertId: "BNBx-UNDELEGATION-UPDATE", protocol: protocol, severity: FindingSeverity.Critical, @@ -317,21 +292,21 @@ const handleCompleteUndelegationTransaction: HandleTransaction = async ( if (completeUndelegationInvocations.length) { lastCompleteUndelegationTime = new Date(); completeUndelegationFailed = false; - return findings; + return []; } if (!lastStartUndelegationTime || !lastCompleteUndelegationTime) { - return findings; + return []; } - if (startUndelegationFailed || completeUndelegationFailed) return findings; + if (startUndelegationFailed || completeUndelegationFailed) return []; const currentTime = new Date(); const diff = currentTime.getTime() - lastStartUndelegationTime.getTime(); const diffHours = getHours(diff); if ( - diffHours > COMPLETE_UNDELEGATION_DELAY && + diffHours >= COMPLETE_UNDELEGATION_DELAY && lastStartUndelegationTime.getTime() > lastCompleteUndelegationTime.getTime() ) { findings.push( diff --git a/agents/bot-operations-agent/src/constants.ts b/agents/bot-operations-agent/src/constants.ts index 6ff968f..2d64082 100644 --- a/agents/bot-operations-agent/src/constants.ts +++ b/agents/bot-operations-agent/src/constants.ts @@ -1,40 +1,24 @@ -const protocol = "BNBx Stader"; -const STAKE_MANAGER = "0x7276241a669489E4BBB76f63d2A43Bfe63080F2F"; -const REWARD_EVENT = "event Redelegate(uint256 _rewardsId, uint256 _amount)"; -const REWARD_DELAY_HOURS = 24; -const REWARD_CHANGE_BPS = 50; // 0 - 10_000 -const TOTAL_BPS = 10000; +export const protocol = "BNBx Stader"; +export const STAKE_MANAGER = "0x7276241a669489E4BBB76f63d2A43Bfe63080F2F"; +export const REWARD_EVENT = + "event Redelegate(uint256 _rewardsId, uint256 _amount)"; +export const REWARD_DELAY_HOURS = 24; -const START_DELEGATION_FN = "function startDelegation()"; -const START_DELEGATION_DELAY = 36; +export const START_DELEGATION_FN = "function startDelegation()"; +export const START_DELEGATION_DELAY = 48; -const COMPLETE_DELEGATION_FN = "function completeDelegation(uint256 _uuid)"; -const COMPLETE_DELEGATION_DELAY = 12; +export const COMPLETE_DELEGATION_FN = + "function completeDelegation(uint256 _uuid)"; +export const COMPLETE_DELEGATION_DELAY = 1; -const START_UNDELEGATION_FN = "function startUndelegation()"; -const START_UNDELEGATION_DELAY = 169; +export const START_UNDELEGATION_FN = "function startUndelegation()"; +export const START_UNDELEGATION_DELAY = 168; +export const START_UNDELEGATION_DELAY_MINS = 10; -const UNDELEGATION_UPDATE_FN = "function undelegationStarted(uint256 _uuid)"; -const UNDELEGATION_UPDATE_DELAY = 12; +export const UNDELEGATION_UPDATE_FN = + "function undelegationStarted(uint256 _uuid)"; +export const UNDELEGATION_UPDATE_DELAY_MINS = 30; -const COMPLETE_UNDELEGATION_FN = "function completeUndelegation(uint256 _uuid)"; -const COMPLETE_UNDELEGATION_DELAY = 193; - -export { - protocol, - REWARD_EVENT, - REWARD_DELAY_HOURS, - STAKE_MANAGER, - REWARD_CHANGE_BPS, - TOTAL_BPS, - START_DELEGATION_FN, - START_DELEGATION_DELAY, - COMPLETE_DELEGATION_FN, - COMPLETE_DELEGATION_DELAY, - START_UNDELEGATION_FN, - START_UNDELEGATION_DELAY, - UNDELEGATION_UPDATE_FN, - UNDELEGATION_UPDATE_DELAY, - COMPLETE_UNDELEGATION_FN, - COMPLETE_UNDELEGATION_DELAY, -}; +export const COMPLETE_UNDELEGATION_FN = + "function completeUndelegation(uint256 _uuid)"; +export const COMPLETE_UNDELEGATION_DELAY = 171; diff --git a/agents/bot-operations-agent/src/utils.ts b/agents/bot-operations-agent/src/utils.ts index 3f37348..b341242 100644 --- a/agents/bot-operations-agent/src/utils.ts +++ b/agents/bot-operations-agent/src/utils.ts @@ -1,5 +1,8 @@ -const getHours = (miliSecs: number) => { - return miliSecs / (1000 * 60 * 60); +export const getHours = (miliSecs: number) => { + return Math.trunc(miliSecs / (1000 * 60 * 60)); }; -export { getHours }; +export const getMins = (miliSecs: number) => { + const mins = Math.trunc(miliSecs / (1000 * 60)); + return mins % 60; +}; diff --git a/agents/suspicious-amount-agent/README.md b/agents/suspicious-amount-agent/README.md index 9838325..e9733ed 100644 --- a/agents/suspicious-amount-agent/README.md +++ b/agents/suspicious-amount-agent/README.md @@ -6,16 +6,23 @@ ## Alerts +- BNBx-REWARD-CHANGE + + - Fired when Reward changes by more than 20 % + - Severity is set to "Medium" + - Type is set to "Info" + - metadata: lastRewardAmount, curentRewardAmount + - BNBx-LARGE-MINT - - Fired when BNBx is minted in large amount (500 BNBx) + - Fired when BNBx is minted in large amount (250 BNBx) - Severity is set to "High" - Type is set to "Info" - metadata: to, value - BNBx-LARGE-UNSTAKE - - Fired when User unstakes large amount of BNBx (500 BNBx) + - Fired when User unstakes large amount of BNBx (250 BNBx) - Severity is set to "High" - Type is set to "Info" - metadata: account, amountInBnbX, @@ -40,3 +47,11 @@ - Severity is set to "High" - Type is set to "Suspicious" - metadata: lastSupply, currentSupply + +- BNBX-TIMELOCK + + - Fired when contract upgrade proposal is scheduled + - Severity is set to "High" + - Type is set to "Info" + - metadata: delay + - addresses: [timelock, proxy_admin] diff --git a/agents/suspicious-amount-agent/package-lock.json b/agents/suspicious-amount-agent/package-lock.json index 76acc3b..3f7b19e 100644 --- a/agents/suspicious-amount-agent/package-lock.json +++ b/agents/suspicious-amount-agent/package-lock.json @@ -1,11 +1,11 @@ { - "name": "bnbx-concerning-amount-bot", + "name": "bnbx-suspicious-amount-agent", "version": "0.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "bnbx-concerning-amount-bot", + "name": "bnbx-suspicious-amount-agent", "version": "0.0.1", "dependencies": { "forta-agent": "^0.1.9" diff --git a/agents/suspicious-amount-agent/package.json b/agents/suspicious-amount-agent/package.json index 008cb6d..981fa21 100644 --- a/agents/suspicious-amount-agent/package.json +++ b/agents/suspicious-amount-agent/package.json @@ -2,8 +2,12 @@ "name": "bnbx-suspicious-amount-agent", "version": "0.0.1", "description": "Alerts on suspicious amount of BNBx minted, unstaked, Rewards", + "repository": { + "url": "https://github.com/stader-labs/bnbX/tree/forta-update/agents/suspicious-amount-agent", + "type": "git" + }, "chainIds": [ - 1 + 56 ], "scripts": { "build": "tsc", diff --git a/agents/suspicious-amount-agent/publish.log b/agents/suspicious-amount-agent/publish.log index dbe22ef..9cfb535 100644 --- a/agents/suspicious-amount-agent/publish.log +++ b/agents/suspicious-amount-agent/publish.log @@ -6,3 +6,8 @@ Mon, 05 Sep 2022 18:21:48 GMT: successfully updated agent id 0x1b10e9f5cd672d1b7 Tue, 06 Sep 2022 10:56:42 GMT: successfully updated agent id 0x1b10e9f5cd672d1b759031ac829aaac2d4466ee01724254a62e479259ac03d93 with manifest QmZ46oxayDxyC5khFQ9M2Xv7C5Rsid4wcgSSYHk74Z3PD9 Wed, 07 Sep 2022 06:05:40 GMT: successfully updated agent id 0x1b10e9f5cd672d1b759031ac829aaac2d4466ee01724254a62e479259ac03d93 with manifest QmP6WuQbcAWfxMN3nS9jd8c19biy3iE5idd8n9kSgt2Kec Mon, 03 Oct 2022 19:15:16 GMT: successfully updated agent id 0x1b10e9f5cd672d1b759031ac829aaac2d4466ee01724254a62e479259ac03d93 with manifest QmQ1JbT3SiDyi7g2u5xhnWDwJvU7coMXnwDoaoWNrSGBQN +Thu, 22 Dec 2022 16:25:27 GMT: successfully updated agent id 0x1b10e9f5cd672d1b759031ac829aaac2d4466ee01724254a62e479259ac03d93 with manifest QmYD7ckPMPMJDUFB5WPsvTE9awyUKx4wNymsXVdQg8GUMc +Sun, 26 Feb 2023 20:48:45 GMT: successfully updated agent id 0x1b10e9f5cd672d1b759031ac829aaac2d4466ee01724254a62e479259ac03d93 with manifest QmT4qsndMNfrFQkSa2NntpH622XoSXov98C6NNh7vNim2b +Mon, 27 Feb 2023 11:53:47 GMT: successfully updated agent id 0x1b10e9f5cd672d1b759031ac829aaac2d4466ee01724254a62e479259ac03d93 with manifest QmSVmhWmBWbwuSt4ECwwzU94Q1pz8YnxwWhQTMTNyQyB5b +Mon, 27 Feb 2023 14:18:40 GMT: successfully updated agent id 0x1b10e9f5cd672d1b759031ac829aaac2d4466ee01724254a62e479259ac03d93 with manifest QmbB7PwL2GoHeA9X3xbdD5iVDPBTJdH5YMvSMvbNL4vDET +Tue, 28 Feb 2023 13:03:07 GMT: successfully updated agent id 0x1b10e9f5cd672d1b759031ac829aaac2d4466ee01724254a62e479259ac03d93 with manifest Qma28fc3A5g9UrM6DdMdgwNaMbFtr5cNRV6JRoyd8Cyoz9 diff --git a/agents/suspicious-amount-agent/src/agent.ts b/agents/suspicious-amount-agent/src/agent.ts index 73b5387..c56a510 100644 --- a/agents/suspicious-amount-agent/src/agent.ts +++ b/agents/suspicious-amount-agent/src/agent.ts @@ -10,6 +10,8 @@ import * as suspiciousMint from "./suspicious-mint"; import * as suspiciousWithdraw from "./suspicious-withdraw"; import * as erDrop from "./er-drop"; import * as supplyChange from "./bnbx-supply"; +import * as rewardChange from "./suspicious-rewards"; +import * as upgradeProposal from "./upgrade-proposal"; const handleTransaction: HandleTransaction = async ( txEvent: TransactionEvent @@ -18,6 +20,8 @@ const handleTransaction: HandleTransaction = async ( await Promise.all([ suspiciousMint.handleTransaction(txEvent), suspiciousWithdraw.handleTransaction(txEvent), + rewardChange.handleTransaction(txEvent), + upgradeProposal.handleTransaction(txEvent), ]) ).flat(); diff --git a/agents/suspicious-amount-agent/src/constants.ts b/agents/suspicious-amount-agent/src/constants.ts index d5dfba5..b3d9b22 100644 --- a/agents/suspicious-amount-agent/src/constants.ts +++ b/agents/suspicious-amount-agent/src/constants.ts @@ -1,17 +1,21 @@ const protocol = "BNBx Stader"; const BNBx = "0x1bdd3Cf7F79cfB8EdbB955f20ad99211551BA275"; const STAKE_MANAGER = "0x7276241a669489E4BBB76f63d2A43Bfe63080F2F"; +export const TIMELOCK_CONTRACT = "0xd990a252e7e36700d47520e46cd2b3e446836488"; +export const PROXY_ADMIN = "0xF90e293D34a42CB592Be6BE6CA19A9963655673C"; const BEP20_TRANSFER_EVENT = "event Transfer(address indexed from, address indexed to, uint256 value)"; const REQUEST_WITHDRAW_EVENT = "event RequestWithdraw(address indexed _account, uint256 _amountInBnbX)"; const REWARD_EVENT = "event Redelegate(uint256 _rewardsId, uint256 _amount)"; +const REWARD_CHANGE_PCT = 10; // 0 - 100 -const BNBX_MINT_THRESHOLD = "500"; -const BNBX_UNSTAKE_THRESHOLD = "500"; -const MIN_REWARD_THRESHOLD = "1"; -const MAX_REWARD_THRESHOLD = "20"; +export const TIMELOCK_SCHEDULE_EVENT = + "event CallScheduled(bytes32 indexed id, uint256 indexed index, address target,uint256 value,bytes data,bytes32 predecessor, uint256 delay)"; + +const BNBX_MINT_THRESHOLD = "250"; +const BNBX_UNSTAKE_THRESHOLD = "250"; const BNBX_SUPPLY_CHANGE_PCT = 10; const BNBX_SUPPLY_CHANGE_HOURS = 1; @@ -23,10 +27,9 @@ export { BEP20_TRANSFER_EVENT, REQUEST_WITHDRAW_EVENT, REWARD_EVENT, + REWARD_CHANGE_PCT, BNBX_MINT_THRESHOLD, BNBX_UNSTAKE_THRESHOLD, - MIN_REWARD_THRESHOLD, - MAX_REWARD_THRESHOLD, BNBX_SUPPLY_CHANGE_PCT, BNBX_SUPPLY_CHANGE_HOURS, }; diff --git a/agents/suspicious-amount-agent/src/suspicious-rewards.ts b/agents/suspicious-amount-agent/src/suspicious-rewards.ts index 334aca9..2756085 100644 --- a/agents/suspicious-amount-agent/src/suspicious-rewards.ts +++ b/agents/suspicious-amount-agent/src/suspicious-rewards.ts @@ -1,5 +1,5 @@ +import { BigNumber } from "ethers"; import { - ethers, Finding, FindingSeverity, FindingType, @@ -7,13 +7,14 @@ import { TransactionEvent, } from "forta-agent"; import { - MAX_REWARD_THRESHOLD, - MIN_REWARD_THRESHOLD, protocol, + REWARD_CHANGE_PCT, REWARD_EVENT, STAKE_MANAGER, } from "./constants"; +let lastRewardAmount: BigNumber; + const handleTransaction: HandleTransaction = async ( txEvent: TransactionEvent ) => { @@ -22,45 +23,31 @@ const handleTransaction: HandleTransaction = async ( const bnbxRewardEvents = txEvent.filterLog(REWARD_EVENT, STAKE_MANAGER); bnbxRewardEvents.forEach((rewardEvents) => { - const { _rewardsId, _amount } = rewardEvents.args; - - const normalizedValue = ethers.utils.formatEther(_amount); - const minThreshold = ethers.utils.parseEther(MIN_REWARD_THRESHOLD); - const maxThreshold = ethers.utils.parseEther(MAX_REWARD_THRESHOLD); - - if (_amount.lt(minThreshold)) { + const { _amount } = rewardEvents.args; + if ( + lastRewardAmount && + lastRewardAmount + .sub(_amount) + .abs() + .gt(lastRewardAmount.mul(REWARD_CHANGE_PCT).div(100)) + ) { findings.push( Finding.fromObject({ - name: "Low BNBx Reward", - description: `Low amount of BNBx Reward Received: ${normalizedValue}`, - alertId: "BNBx-3", + name: "Significant Reward Change", + description: `Reward changed more than ${REWARD_CHANGE_PCT} %`, + alertId: "BNBx-REWARD-CHANGE", protocol: protocol, - severity: FindingSeverity.High, + severity: FindingSeverity.Medium, type: FindingType.Info, metadata: { - rewardsId: _rewardsId.toString(), - amount: _amount.toString(), + lastRewardAmount: lastRewardAmount.toString(), + cuurentRewardAmount: _amount.toString(), }, }) ); } - if (_amount.gt(maxThreshold)) { - findings.push( - Finding.fromObject({ - name: "High BNBx Reward", - description: `High amount of BNBx Reward Received: ${normalizedValue}`, - alertId: "BNBx-4", - protocol: "BNBx Stader", - severity: FindingSeverity.High, - type: FindingType.Info, - metadata: { - _rewardsId, - _amount, - }, - }) - ); - } + lastRewardAmount = _amount; }); return findings; diff --git a/agents/suspicious-amount-agent/src/upgrade-proposal.ts b/agents/suspicious-amount-agent/src/upgrade-proposal.ts new file mode 100644 index 0000000..8143285 --- /dev/null +++ b/agents/suspicious-amount-agent/src/upgrade-proposal.ts @@ -0,0 +1,52 @@ +import { + Finding, + FindingSeverity, + FindingType, + HandleTransaction, + TransactionEvent, +} from "forta-agent"; +import { + protocol, + PROXY_ADMIN, + TIMELOCK_CONTRACT, + TIMELOCK_SCHEDULE_EVENT, +} from "./constants"; +import { getHours, getMins } from "./utils"; + +const handleTransaction: HandleTransaction = async ( + txEvent: TransactionEvent +) => { + const findings: Finding[] = []; + + const upgradeEvents = txEvent + .filterLog(TIMELOCK_SCHEDULE_EVENT, TIMELOCK_CONTRACT) + .filter((transferEvent) => { + const { target } = transferEvent.args; + return target === PROXY_ADMIN; + }); + + if (upgradeEvents.length) { + const delayInMiliSecs = + parseInt(upgradeEvents[0].args.delay.toString()) * 1000; + findings.push( + Finding.fromObject({ + name: "Proposal Scheduled", + description: `Upgrade Proposal Scheduled. Timelock expires in ${getHours( + delayInMiliSecs + )} hours ${getMins(delayInMiliSecs) % 60} mins`, + alertId: "BNBX-TIMELOCK", + protocol: protocol, + severity: FindingSeverity.High, + type: FindingType.Info, + metadata: { + delay: upgradeEvents[0].args.delay.toString(), + }, + addresses: [TIMELOCK_CONTRACT, PROXY_ADMIN], + }) + ); + } + + return findings; +}; + +export { handleTransaction }; diff --git a/agents/suspicious-amount-agent/src/utils.ts b/agents/suspicious-amount-agent/src/utils.ts index 3f37348..d831eb1 100644 --- a/agents/suspicious-amount-agent/src/utils.ts +++ b/agents/suspicious-amount-agent/src/utils.ts @@ -1,5 +1,7 @@ -const getHours = (miliSecs: number) => { - return miliSecs / (1000 * 60 * 60); +export const getHours = (miliSecs: number) => { + return parseInt((miliSecs / (1000 * 60 * 60)).toString()); }; -export { getHours }; +export const getMins = (miliSecs: number) => { + return parseInt((miliSecs / (1000 * 60)).toString()); +};