-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.ts
127 lines (102 loc) · 4.21 KB
/
index.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
118
119
120
121
122
123
124
125
126
127
import { JsonRpcProvider, BaseContract, Network, FetchUrlFeeDataNetworkPlugin, parseUnits } from 'ethers';
import { GasPriceOracle, GasPriceOracle__factory, Multicall3, Multicall3__factory } from '../typechain-types';
import 'dotenv/config';
const ORACLE_ADDRESS = '0xF81A8D8D3581985D3969fe53bFA67074aDFa8F3C';
const MULTICALL_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11';
const RPC_URL = process.env.RPC_URL || 'https://polygon-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607';
const GAS_STATION = process.env.GAS_STATION || 'https://gasstation.polygon.technology/v2';
export interface Call3 {
contract: BaseContract;
name: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
params?: any[];
allowFailure?: boolean;
}
export async function multicall(Multicall: Multicall3, calls: Call3[]) {
const calldata = calls.map((call) => ({
target: call.contract.target,
callData: call.contract.interface.encodeFunctionData(call.name, call.params),
allowFailure: call.allowFailure ?? false
}));
const returnData = await Multicall.aggregate3.staticCall(calldata);
const res = returnData.map((call, i) => {
const [result, data] = call;
const decodeResult = (result && data && data !== '0x') ? calls[i].contract.interface.decodeFunctionResult(calls[i].name, data) : null;
return !decodeResult ? null : decodeResult.length === 1 ? decodeResult[0] : decodeResult;
});
return res;
}
// caching to improve performance
const oracleMapper = new Map();
const multicallMapper = new Map();
export function getGasOraclePlugin(networkKey: string, gasStation: string) {
return new FetchUrlFeeDataNetworkPlugin(gasStation, async (fetchFeeData, provider, request) => {
if (!oracleMapper.has(networkKey)) {
oracleMapper.set(networkKey, GasPriceOracle__factory.connect(ORACLE_ADDRESS, provider));
}
if (!multicallMapper.has(networkKey)) {
multicallMapper.set(networkKey, Multicall3__factory.connect(MULTICALL_ADDRESS, provider));
}
const Oracle = oracleMapper.get(networkKey) as GasPriceOracle;
const Multicall = multicallMapper.get(networkKey) as Multicall3;
const [timestamp, heartbeat, feePerGas, priorityFeePerGas] = await multicall(Multicall, [
{
contract: Oracle,
name: 'timestamp'
},
{
contract: Oracle,
name: 'heartbeat'
},
{
contract: Oracle,
name: 'maxFeePerGas'
},
{
contract: Oracle,
name: 'maxPriorityFeePerGas'
},
]);
const isOutdated = Number(timestamp) <= (Date.now() / 1000) - Number(heartbeat);
if (!isOutdated) {
const maxPriorityFeePerGas = priorityFeePerGas * 13n / 10n;
const maxFeePerGas = feePerGas * 2n + maxPriorityFeePerGas;
console.log(`Fetched from oracle: ${JSON.stringify({
gasPrice: feePerGas.toString(),
maxFeePerGas: feePerGas.toString(),
maxPriorityFeePerGas: priorityFeePerGas.toString(),
})}`);
return {
gasPrice: maxFeePerGas,
maxFeePerGas,
maxPriorityFeePerGas
};
}
// Prevent Cloudflare from blocking our request in node.js
request.setHeader('User-Agent', 'ethers');
const [ { bodyJson: { fast }}, { gasPrice } ] = await Promise.all([
request.send(), fetchFeeData()
]);
console.log(`Fetched from gasStation: ${JSON.stringify({
gasPrice: gasPrice ? gasPrice.toString() : undefined,
maxFeePerGas: parseUnits(`${fast.maxFee}`, 9).toString(),
maxPriorityFeePerGas: parseUnits(`${fast.maxPriorityFee}`, 9).toString(),
})}`);
return {
gasPrice,
maxFeePerGas: parseUnits(`${fast.maxFee}`, 9),
maxPriorityFeePerGas: parseUnits(`${fast.maxPriorityFee}`, 9),
};
});
}
export async function start() {
const previousNetwork = await new JsonRpcProvider(RPC_URL).getNetwork();
const network = new Network(previousNetwork.name, previousNetwork.chainId);
network.attachPlugin(getGasOraclePlugin(`${previousNetwork.chainId}_${RPC_URL}`, GAS_STATION));
const provider = new JsonRpcProvider(RPC_URL, network, { staticNetwork: network });
provider.pollingInterval = 1000;
provider.on('block', async () => {
console.log(await provider.getFeeData());
});
}
start();