Skip to content
This repository was archived by the owner on Jun 24, 2025. It is now read-only.

Commit f4d1dca

Browse files
committed
add gas network interface and ploting prices code
1 parent 1538234 commit f4d1dca

File tree

8 files changed

+259
-8
lines changed

8 files changed

+259
-8
lines changed

foundry.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ out = "out"
55
libs = ["dependencies"]
66
auto_detect_remappings = false
77
# Permissions
8-
fs_permissions = [{ access = "read-write", path = "gas.csv" }]
8+
fs_permissions = [{ access = "read-write", path = "gas.csv" },{ access = "read-write", path = "uni_prices.csv" }]
99

1010
########
1111
# Lint #

plot_prices.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import csv
2+
import os
3+
import requests
4+
import matplotlib.pyplot as plt
5+
import matplotlib.dates as mdates
6+
from datetime import datetime
7+
from matplotlib.ticker import MultipleLocator
8+
9+
def read_csv(path):
10+
data = {}
11+
with open(path, 'r') as f:
12+
reader = csv.reader(f)
13+
for row in reader:
14+
if row[0].isdigit():
15+
timestamp = int(row[1])
16+
price = float(row[2])
17+
data[timestamp] = price
18+
return data
19+
20+
def fetch_prices(timestamp, api_key):
21+
url = "https://min-api.cryptocompare.com/data/v2/histominute"
22+
params = {
23+
'fsym': 'ETH',
24+
'tsym': 'USDT',
25+
'toTs': timestamp,
26+
'limit': 1,
27+
}
28+
29+
response = requests.get(url, params=params)
30+
if response.status_code == 200:
31+
data = response.json()
32+
data_len = len(data['Data']['Data'])
33+
last_item = data['Data']['Data'][data_len - 1]
34+
if data['Response'] == 'Success' and data_len > 0:
35+
if timestamp - last_item['time'] <= 60:
36+
# allowing one min diff
37+
return data['Data']['Data'][0]['close']
38+
else:
39+
return 0
40+
def main():
41+
csv_data = read_csv('uni_prices.csv')
42+
43+
timestamps = []
44+
csv_prices = []
45+
api_prices = []
46+
47+
for ts, price in csv_data.items():
48+
api_price = fetch_prices(ts, api_key)
49+
if api_price:
50+
timestamps.append(ts)
51+
csv_prices.append(price)
52+
api_prices.append(api_price)
53+
54+
print(timestamps);
55+
print(csv_prices);
56+
print(api_prices);
57+
58+
# timestamps = [1749105683, 1749102011, 1749098375, 1749094775, 1749091175, 1749087551, 1749083951, 1749080363, 1749076763, 1749073079, 1749069491, 1749065879, 1749062255, 1749058619, 1749055019, 1749051419, 1749047807, 1749044183, 1749040559, 1749036923, 1749033275, 1749029675, 1749026075, 1749022427, 1749018779, 1749015179, 1749011567, 1749007979, 1749004343, 1749000731, 1748997119, 1748993519, 1748989919, 1748986259, 1748982671, 1748979047, 1748975447, 1748971847, 1748968247, 1748964647, 1748961035, 1748957387, 1748953775, 1748950187, 1748946587, 1748942951, 1748939291, 1748935667, 1748932055, 1748928443]
59+
# csv_prices = [2609.8080762655322, 2621.480490403204, 2624.393160328884, 2622.7686279534264, 2619.7231588548534, 2610.3963278671695, 2609.545936613066, 2608.729875162142, 2614.1021109699313, 2612.849426415079, 2609.528150105592, 2629.978419276374, 2639.693585306237, 2641.1199286093947, 2651.1515553967374, 2653.429993570316, 2610.912013801284, 2616.554217935566, 2623.7705098568176, 2634.658956898744, 2640.6144521020756, 2634.892883303356, 2637.2214214676064, 2626.727496929253, 2620.77461018866, 2628.379186142654, 2631.9788198198744, 2635.816798914804, 2617.5044214035506, 2610.3798778314335, 2595.2227034083376, 2600.5514233505082, 2595.497388260199, 2608.125684065392, 2605.341895595558, 2620.625110387102, 2614.4468008942767, 2620.2157734437033, 2616.703457689552, 2643.3060943956352, 2628.4498082456616, 2620.172618189739, 2603.6126702299416, 2613.240784817929, 2612.434634630224, 2601.7559333260524, 2601.6135961284067, 2613.2477388445636, 2612.322965102058, 2599.466433363167]
60+
# api_prices = [2611.79, 2615.1, 2630.77, 2629.6, 2627.53, 2606.69, 2612.21, 2609.82, 2612.91, 2618.81, 2610.49, 2626.76, 2636.21, 2638.42, 2655.51, 2648.87, 2614.97, 2621.86, 2622.5, 2627.96, 2636.65, 2632.8, 2639.91, 2627.62, 2624.14, 2621.03, 2625.66, 2634.53, 2614.45, 2612.09, 2589.82, 2595.93, 2600.22, 2611.75, 2598.33, 2626.38, 2614.02, 2623.73, 2610.51, 2642.56, 2632.72, 2621.35, 2608.12, 2607.72, 2616.27, 2601.82, 2604.33, 2609.74, 2604.84, 2600.63]
61+
dates = [datetime.fromtimestamp(ts) for ts in timestamps]
62+
63+
plt.figure(figsize=(14, 7))
64+
65+
# Plot with different colors and markers
66+
plt.plot(dates, csv_prices, label='UNI Price', marker='o', color='blue', markersize=5)
67+
plt.plot(dates, api_prices, label='API Price', marker='x', color='green', markersize=5)
68+
69+
plt.title('ETH Price Comparison: UNI vs API', fontsize=14)
70+
plt.xlabel('Time', fontsize=12)
71+
plt.ylabel('Price (USD)', fontsize=12)
72+
plt.legend(fontsize=12)
73+
74+
# Add more grid lines
75+
plt.grid(True, which='both', ls='-', alpha=0.5)
76+
77+
ax = plt.gca()
78+
79+
# X-axis: grid/ticks every 30 minutes
80+
ax.xaxis.set_major_locator(mdates.HourLocator(interval=2))
81+
ax.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d %H:%M'))
82+
83+
# Y-axis: grid/ticks every $1
84+
ax.yaxis.set_major_locator(MultipleLocator(2))
85+
ax.yaxis.set_minor_locator(MultipleLocator(2))
86+
87+
# Grid lines
88+
ax.grid(which='major', axis='both', linestyle='-', alpha=0.7)
89+
ax.grid(which='minor', axis='both', linestyle=':', alpha=0.4)
90+
91+
plt.gcf().autofmt_xdate()
92+
93+
plt.show()
94+
if __name__ == "__main__":
95+
main()

src/oracle/GasNetworkOracle.sol

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// SPDX-License-Identifier: MIT
2+
// Analog's Contracts (last updated v0.1.0) (src/Gateway.sol)
3+
4+
pragma solidity >=0.8.0;
5+
6+
import {IGasPriceOracle} from "src/oracle/IOracle.sol";
7+
import {Test, console} from "forge-std/Test.sol";
8+
9+
interface IGasNetOracle {
10+
/**
11+
* @param systemid:
12+
* 1 for Bitcoin chains
13+
* 2 for Evm chains
14+
* @param cid:
15+
* chainId of the chain
16+
* @param typ:
17+
* 107: Base fee (EIP-1559)
18+
* 115: Blob base fee (post-EIP-4844 chains)
19+
* 322: 90th percentile priority fee
20+
* @param tin:
21+
* miliseconds, return zero if the data is older than mili seconds
22+
*/
23+
function getInTime(uint8 systemid, uint64 cid, uint16 typ, uint48 tin)
24+
external
25+
view
26+
returns (uint256 value, uint64 height, uint48 timestamp);
27+
}
28+
29+
contract GasNetworkOracle is IGasPriceOracle {
30+
address public immutable gasNet;
31+
32+
constructor(address _gasNet) {
33+
gasNet = _gasNet;
34+
}
35+
36+
function getGasPrice(uint64 chainId, uint16 ty, uint48 tin) external view returns (uint256 value) {
37+
// miliseconds are of two hours need to check on this later.
38+
(uint256 gasPrice,,) = IGasNetOracle(gasNet).getInTime(2, chainId, ty, tin);
39+
return gasPrice;
40+
}
41+
}

src/oracle/IOracle.sol

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33

44
pragma solidity >=0.8.0;
55

6-
interface IOracle {
6+
interface IPriceOracle {
77
function getPrice(address token) external view returns (uint256, uint256);
88
function getAmountIn(address tokenIn, address tokenOut, uint256 amountOut) external view returns (uint256);
99
}
10+
11+
interface IGasPriceOracle {
12+
function getGasPrice(uint64 chainId, uint16 ty, uint48 maxAge) external view returns (uint256 value);
13+
}

src/oracle/UniswapV2Oracle.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
pragma solidity >=0.8.0;
55

6-
import "src/oracle/IOracle.sol";
6+
import {IPriceOracle} from "src/oracle/IOracle.sol";
77
import {Test, console} from "forge-std/Test.sol";
88

99
interface IUniswapV2Factory {
@@ -20,7 +20,7 @@ interface IERC20 {
2020
function decimals() external view returns (uint8);
2121
}
2222

23-
contract UniswapV2Oracle is IOracle {
23+
contract UniswapV2Oracle is IPriceOracle {
2424
address public immutable factory;
2525
address public immutable USDT;
2626

test/GasNetworkOracle.t.sol

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// SPDX-License-Identifier: MIT
2+
// Analog's Contracts (last updated v0.1.0) (src/Gateway.sol)
3+
4+
pragma solidity >=0.8.0;
5+
6+
import {Test, console} from "forge-std/Test.sol";
7+
import {IGasPriceOracle} from "src/oracle/IOracle.sol";
8+
import {GasNetworkOracle} from "src/oracle/GasNetworkOracle.sol";
9+
10+
contract GasNetworkOracleTest is Test {
11+
GasNetworkOracle oracle;
12+
address constant ArbitrumMainnet = 0x1c51B22954af03FE11183aaDF43F6415907a9287;
13+
14+
function setUp() public {
15+
vm.createSelectFork({urlOrAlias: "https://arb1.arbitrum.io/rpc"});
16+
oracle = new GasNetworkOracle(ArbitrumMainnet);
17+
}
18+
19+
function testGasPrice() public view {
20+
(uint256 value) = IGasPriceOracle(oracle).getGasPrice(1, 322, 7200000);
21+
assert(value > 0);
22+
}
23+
}

test/UniswapV2Oracle.t.sol

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pragma solidity >=0.8.0;
55

66
import {Test, console} from "forge-std/Test.sol";
77
import {UniswapV2Oracle} from "src/oracle/UniswapV2Oracle.sol";
8+
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
89

910
interface IERC20 {
1011
function decimals() external view returns (uint8);
@@ -20,16 +21,52 @@ contract UniswapV2OracleTest is Test {
2021
function setUp() public {
2122
vm.createSelectFork({urlOrAlias: "https://eth.meowrpc.com"});
2223
oracle = new UniswapV2Oracle(FACTORY, USDT);
24+
vm.makePersistent(address(oracle));
2325
}
2426

2527
function testGetNativePrice() public view {
26-
(uint256 ints,) = oracle.getPrice(WETH);
27-
// same query as above one
28+
(uint256 usdPrice,) = oracle.getPrice(WETH);
29+
console.log("ETH USDT price", usdPrice);
30+
assert(usdPrice > 0);
31+
}
32+
33+
function testGetAmountIn() public view {
34+
(uint256 usdPrice,) = oracle.getPrice(WETH);
2835
(uint256 usdtRequired) = oracle.getAmountIn(USDT, WETH, 1 ether);
2936
uint256 usdtDecimals = IERC20(USDT).decimals();
3037
uint256 usdtScale = 10 ** usdtDecimals;
3138
uint256 usdtRequiredForOneEth = usdtRequired / usdtScale;
32-
// adding 20 threshold due to algo of uniswap v2
33-
require(usdtRequiredForOneEth - ints < 20);
39+
console.log("ETH pool price", usdtRequiredForOneEth);
40+
require(usdtRequiredForOneEth - usdPrice < 50);
41+
}
42+
43+
function testGeneratePricesRange() public {
44+
uint256 BLOCKS_TO_ITERATE = 50;
45+
uint256 BLOCK_STEP = 299;
46+
string memory path = "uni_prices.csv";
47+
string memory rpc_url = "https://eth-mainnet.public.blastapi.io";
48+
49+
uint256 startBlock = block.number;
50+
string memory csv = string.concat("block_number,timestamp,price\n");
51+
52+
for (uint256 i = 0; i < BLOCKS_TO_ITERATE; i++) {
53+
uint256 targetBlock = startBlock - (i * BLOCK_STEP);
54+
vm.createSelectFork(rpc_url, targetBlock);
55+
uint256 timestamp = block.timestamp;
56+
(uint256 usdPrice, uint256 fraction) = oracle.getPrice(WETH);
57+
csv = string.concat(
58+
csv,
59+
Strings.toString(targetBlock),
60+
",",
61+
Strings.toString(timestamp),
62+
",",
63+
Strings.toString(usdPrice),
64+
".",
65+
Strings.toString(fraction),
66+
"\n"
67+
);
68+
}
69+
70+
vm.writeFile(path, csv);
3471
}
3572
}

uni_prices.csv

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
block_number,timestamp,price
2+
22636693,1749105683,2609.808076265532095180
3+
22636394,1749102011,2621.480490403203857798
4+
22636095,1749098375,2624.393160328883899511
5+
22635796,1749094775,2622.768627953426326819
6+
22635497,1749091175,2619.723158854853223527
7+
22635198,1749087551,2610.396327867169402364
8+
22634899,1749083951,2609.545936613065795023
9+
22634600,1749080363,2608.729875162141764401
10+
22634301,1749076763,2614.102110969931344932
11+
22634002,1749073079,2612.849426415078947522
12+
22633703,1749069491,2609.528150105592056902
13+
22633404,1749065879,2629.978419276373883105
14+
22633105,1749062255,2639.69358530623668582
15+
22632806,1749058619,2641.119928609394508552
16+
22632507,1749055019,2651.151555396737483315
17+
22632208,1749051419,2653.429993570316036952
18+
22631909,1749047807,2610.91201380128385796
19+
22631610,1749044183,2616.554217935565935726
20+
22631311,1749040559,2623.770509856817489539
21+
22631012,1749036923,2634.658956898743669552
22+
22630713,1749033275,2640.61445210207564004
23+
22630414,1749029675,2634.892883303356012749
24+
22630115,1749026075,2637.221421467606547742
25+
22629816,1749022427,2626.72749692925304526
26+
22629517,1749018779,2620.774610188659997120
27+
22629218,1749015179,2628.379186142653812449
28+
22628919,1749011567,2631.978819819874542670
29+
22628620,1749007979,2635.816798914804067936
30+
22628321,1749004343,2617.504421403550660296
31+
22628022,1749000731,2610.379877831433309262
32+
22627723,1748997119,2595.222703408337425286
33+
22627424,1748993519,2600.55142335050829777
34+
22627125,1748989919,2595.497388260198852413
35+
22626826,1748986259,2608.125684065391981683
36+
22626527,1748982671,2605.341895595557931087
37+
22626228,1748979047,2620.625110387101725716
38+
22625929,1748975447,2614.446800894276899947
39+
22625630,1748971847,2620.215773443703316764
40+
22625331,1748968247,2616.703457689552053223
41+
22625032,1748964647,2643.306094395635139333
42+
22624733,1748961035,2628.449808245661732807
43+
22624434,1748957387,2620.172618189738950580
44+
22624135,1748953775,2603.612670229941585613
45+
22623836,1748950187,2613.240784817929330125
46+
22623537,1748946587,2612.434634630224209653
47+
22623238,1748942951,2601.755933326052318361
48+
22622939,1748939291,2601.613596128406849868
49+
22622640,1748935667,2613.247738844563448784
50+
22622341,1748932055,2612.322965102058051715
51+
22622042,1748928443,2599.466433363166989073

0 commit comments

Comments
 (0)