-
Notifications
You must be signed in to change notification settings - Fork 0
/
bot.ts
117 lines (102 loc) · 3.1 KB
/
bot.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import { JsonRpcProvider } from "ethers";
import {
BlockEvent,
Finding,
Initialize,
HandleBlock,
HealthCheck,
HandleTransaction,
HandleAlert,
AlertEvent,
TransactionEvent,
FindingSeverity,
FindingType,
scanEthereum,
scanPolygon,
scanAlerts,
runHealthCheck,
} from "@fortanetwork/forta-bot";
export const ERC20_TRANSFER_EVENT =
"event Transfer(address indexed from, address indexed to, uint256 value)";
export const TETHER_ADDRESS = "0xdAC17F958D2ee523a2206206994597C13D831ec7";
export const TETHER_DECIMALS = 6;
let findingsCount = 0;
export const handleTransaction: HandleTransaction = async (
txEvent: TransactionEvent,
provider: JsonRpcProvider
) => {
const findings: Finding[] = [];
// limiting this bot to emit only 5 findings so that the alert feed is not spammed
if (findingsCount >= 5) return findings;
// filter the transaction logs for Tether transfer events
const tetherTransferEvents = txEvent.filterLog(
ERC20_TRANSFER_EVENT,
TETHER_ADDRESS
);
tetherTransferEvents.forEach((transferEvent) => {
// extract transfer event arguments
const { to, from, value } = transferEvent.args;
// shift decimals of transfer value
const normalizedValue = value / BigInt(10 ** TETHER_DECIMALS);
// if more than 10,000 Tether were transferred, report it
if (normalizedValue > 10000) {
findings.push(
Finding.fromObject({
name: "High Tether Transfer",
description: `High amount of USDT transferred: ${normalizedValue}`,
alertId: "FORTA-1",
severity: FindingSeverity.Low,
type: FindingType.Info,
metadata: {
to,
from,
},
source: {
chains: [{ chainId: txEvent.chainId }],
transactions: [{ hash: txEvent.hash, chainId: txEvent.chainId }],
},
})
);
findingsCount++;
}
});
return findings;
};
// export const handleBlock: HandleBlock = async (blockEvent: BlockEvent, provider: JsonRpcProvider) => {
// const findings: Finding[] = [];
// // detect some block condition
// return findings;
// }
// export const handleAlert: HandleAlert = async (alertEvent: AlertEvent) => {
// const findings: Finding[] = [];
// // detect some alert condition
// return findings;
// }
// export const healthCheck: HealthCheck = async () => {
// const errors: string[] = [];
// // detect some custom health check condition
// errors.push("not healthy due to some condition")
// return errors;
// }
async function main() {
scanEthereum({
rpcUrl: "https://cloudflare-eth.com/",
handleTransaction,
});
// scanPolygon({
// rpcUrl: "https://polygon-mainnet.g.alchemy.com/v2",
// rpcKeyId: "d7f5e66f-0deb-4002-a52d-9b17ad254b38",
// localRpcUrl: "137",
// handleBlock,
// });
// scanAlerts({
// subscriptions: [{ botId: "0xbotId123" }],
// handleAlert,
// });
// health checks are required to run on scan nodes
runHealthCheck();
}
// only run main() method if this file is directly invoked (vs just imported for testing)
if (require.main === module) {
main();
}