-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMirrorNodeFFI.sol
150 lines (135 loc) · 5.5 KB
/
MirrorNodeFFI.sol
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import {Vm} from "forge-std/Vm.sol";
import {Surl} from "surl/src/Surl.sol";
import {MirrorNode} from "./MirrorNode.sol";
import {HtsSystemContract, HTS_ADDRESS} from "./HtsSystemContract.sol";
import {storeString} from "./StrStore.sol";
/**
* Fetches Token data from the Mirror Node through Foundry's FFI.
*
* For the endpoints that support it, fetches data honoring the current `block.number`.
* The `block.number` can be specified either in the CLI with the
* `--fork-block-number <block-number>` CLI option or using
* Foundry's `vm.(create/select|createSelect|roll)Fork` methods.
* But it is always a valid block in the forked network.
*/
contract MirrorNodeFFI is MirrorNode {
/**
* @dev Cache of responses by `endpoint`.
* See `_get` method for details.
*/
mapping (string endpoint => string response) private _responses;
function fetchTokenData(address token) isValid(token) external override returns (string memory) {
return _get(string.concat(
"tokens/0.0.",
vm.toString(uint160(token))
));
}
function fetchBalance(address token, uint32 accountNum) isValid(token) external override returns (string memory) {
return _get(string.concat(
"tokens/0.0.",
vm.toString(uint160(token)),
"/balances?account.id=0.0.",
vm.toString(accountNum),
_getBlockQueryParam()
));
}
function fetchAllowance(address token, uint32 ownerNum, uint32 spenderNum) isValid(token) external override returns (string memory) {
return _get(string.concat(
"accounts/0.0.",
vm.toString(ownerNum),
"/allowances/tokens?token.id=0.0.",
vm.toString(uint160(token)),
"&spender.id=0.0.",
vm.toString(spenderNum)
));
}
function fetchNftAllowance(address token, uint32 ownerNum, uint32 operatorNum) isValid(token) external override returns (string memory) {
return _get(string.concat(
"accounts/0.0.",
vm.toString(ownerNum),
"/allowances/nfts?token.id=0.0.",
vm.toString(uint160(token)),
"&account.id=0.0.",
vm.toString(operatorNum)
));
}
function fetchAccount(string memory idOrAliasOrEvmAddress) external override returns (string memory) {
return _get(string.concat(
"accounts/",
idOrAliasOrEvmAddress,
"?transactions=false"
));
}
function fetchTokenRelationshipOfAccount(string memory idOrAliasOrEvmAddress, address token) external override returns (string memory) {
return _get(string.concat(
"accounts/",
idOrAliasOrEvmAddress,
"/tokens?token.id=0.0.",
vm.toString(uint160(token))
));
}
function fetchNonFungibleToken(address token, uint32 serial) isValid(token) external override returns (string memory) {
return _get(string.concat(
"tokens/0.0.",
vm.toString(uint160(token)),
"/nfts/",
vm.toString(serial)
));
}
/**
* @dev Returns the block information by given number.
*
* Uses Mirror Node `/api/v1/blocks/` endpoint.
*/
function fetchBlock(uint256 blockNumber) external returns (string memory) {
return _get(string.concat(
"blocks/",
vm.toString(blockNumber)
));
}
/**
* @dev Returns the timestamp query filter according to the current `block.number`.
*/
function _getBlockQueryParam() private returns (string memory) {
string memory json = this.fetchBlock(block.number);
string memory timestamp = vm.parseJsonString(json, ".timestamp.to");
return string.concat("×tamp=", timestamp);
}
/**
* @dev Sends the request specified by `endpoint` to the currently selected Mirror Node.
*
* This method caches a successful (`200`) or not found (`404`) response in its own storage.
* This is specially helpful when requesting block information.
*/
function _get(string memory endpoint) private returns (string memory json) {
json = _responses[endpoint];
if (bytes(json).length == 0) {
(uint256 status, bytes memory result) = Surl.get(string.concat(_mirrorNodeUrl(), endpoint));
json = string(result);
require(status == 200 || status == 404, json);
// `_responses[endpoint] = json;`
// To avoid `EvmError: StateChangeDuringStaticCall`
uint256 slot;
assembly { slot := _responses.slot }
storeString(address(this), uint256(keccak256(abi.encodePacked(endpoint, slot))), json);
}
}
/**
* @dev Returns the currently selected Mirror Node base URL.
*/
function _mirrorNodeUrl() private view returns (string memory url) {
if (block.chainid == 295) {
url = "https://mainnet-public.mirrornode.hedera.com/api/v1/";
} else if (block.chainid == 296) {
url = "https://testnet.mirrornode.hedera.com/api/v1/";
} else if (block.chainid == 297) {
url = "https://previewnet.mirrornode.hedera.com/api/v1/";
} else if (block.chainid == 298) {
url = "http://localhost:5551/api/v1/";
} else {
revert("The provided chain ID is not supported by the Hedera Mirror Node library. Please use one of the supported chain IDs: 295, 296, or 297.");
}
}
}