93 lines
3.9 KiB
C++
93 lines
3.9 KiB
C++
|
|
/*
|
||
|
|
catcrafts.net
|
||
|
|
Copyright (C) 2026 Catcrafts
|
||
|
|
|
||
|
|
The source code of this website is made available for viewing purposes only.
|
||
|
|
No permission is granted to copy, modify, distribute, or create derivative works.
|
||
|
|
*/
|
||
|
|
|
||
|
|
// The EURC rail's two parsers.
|
||
|
|
//
|
||
|
|
// The balance decoder is the line between "the buyer paid" and "the node
|
||
|
|
// errored", and the chains parser is the line between "watching a chain"
|
||
|
|
// and "silently not watching it". Both refuse rather than guess.
|
||
|
|
|
||
|
|
import std;
|
||
|
|
import Catcrafts.Shared;
|
||
|
|
import Catcrafts.Server;
|
||
|
|
|
||
|
|
using namespace Catcrafts;
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
int failures = 0;
|
||
|
|
|
||
|
|
void Check(bool ok, std::string_view what, std::string_view got = {}) {
|
||
|
|
if (ok) return;
|
||
|
|
++failures;
|
||
|
|
std::println(std::cerr, "FAIL: {}{}{}", what,
|
||
|
|
got.empty() ? "" : " got: ", got);
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
int main() {
|
||
|
|
using Server::ParseEthCallUint;
|
||
|
|
Check(ParseEthCallUint(R"({"jsonrpc":"2.0","id":1,"result":"0x0"})") == 0,
|
||
|
|
"eurc: zero balance");
|
||
|
|
Check(ParseEthCallUint(
|
||
|
|
R"({"jsonrpc":"2.0","id":1,"result":"0x0000000000000000000000000000000000000000000000000000000022001230"})")
|
||
|
|
== 570430000,
|
||
|
|
"eurc: €570.43 as 6-decimal base units, full 32-byte word");
|
||
|
|
Check(ParseEthCallUint(R"({"result":"0xFF"})") == 255,
|
||
|
|
"eurc: uppercase hex accepted");
|
||
|
|
// 2^63 does not fit; the decoder must saturate, never wrap to negative.
|
||
|
|
Check(ParseEthCallUint(R"({"result":"0x8000000000000000"})")
|
||
|
|
== std::numeric_limits<std::int64_t>::max(),
|
||
|
|
"eurc: overflow saturates");
|
||
|
|
Check(!ParseEthCallUint(
|
||
|
|
R"({"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"x"}})")
|
||
|
|
.has_value(),
|
||
|
|
"eurc: an RPC error is not a zero balance");
|
||
|
|
Check(!ParseEthCallUint(R"({"result":"21ff361c"})").has_value(),
|
||
|
|
"eurc: missing 0x rejected");
|
||
|
|
Check(!ParseEthCallUint(R"({"result":"0x"})").has_value(),
|
||
|
|
"eurc: empty word rejected");
|
||
|
|
Check(!ParseEthCallUint(R"({"result":42})").has_value(),
|
||
|
|
"eurc: numeric result rejected — the wire type is a hex string");
|
||
|
|
Check(!ParseEthCallUint("garbage").has_value(), "eurc: malformed payload");
|
||
|
|
|
||
|
|
const auto chains = Server::ParseEurcChains(R"({"chains":[
|
||
|
|
{"name":"base","rpc":"https://mainnet.base.org",
|
||
|
|
"contract":"0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42",
|
||
|
|
"chain_id":8453,"note":"lowest fees"},
|
||
|
|
{"name":"ethereum","rpc":"https://ethereum-rpc.publicnode.com",
|
||
|
|
"contract":"0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c","chain_id":1}]})");
|
||
|
|
Check(chains.has_value() && chains->size() == 2, "eurc: chains file parses");
|
||
|
|
if (chains && chains->size() == 2) {
|
||
|
|
Check((*chains)[0].name == "base" && (*chains)[0].chainId == 8453,
|
||
|
|
"eurc: file order preserved — first entry is the recommendation");
|
||
|
|
Check((*chains)[0].contract == "0x60a3e35cc302bfa44cb288bc5a4f316fdb1adb42",
|
||
|
|
"eurc: contract lowercased for comparison");
|
||
|
|
Check((*chains)[0].note == "lowest fees", "eurc: note carried");
|
||
|
|
Check((*chains)[1].blockTag == "finalized" && (*chains)[1].decimals == 6,
|
||
|
|
"eurc: defaults");
|
||
|
|
}
|
||
|
|
Check(!Server::ParseEurcChains(R"({"chains":[
|
||
|
|
{"name":"base","rpc":"https://x.org","contract":"0xdeadbeef"}]})").has_value(),
|
||
|
|
"eurc: short contract rejects the whole file");
|
||
|
|
Check(!Server::ParseEurcChains(R"({"chains":[
|
||
|
|
{"name":"base","rpc":"ftp://x.org",
|
||
|
|
"contract":"0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42"}]})").has_value(),
|
||
|
|
"eurc: non-http rpc rejects the whole file");
|
||
|
|
Check(!Server::ParseEurcChains(R"({"chains":[]})").has_value(),
|
||
|
|
"eurc: empty chain list rejected");
|
||
|
|
Check(!Server::ParseEurcChains("garbage").has_value(),
|
||
|
|
"eurc: malformed chains file rejected");
|
||
|
|
|
||
|
|
if (failures != 0) {
|
||
|
|
std::println(std::cerr, "{} check(s) failed", failures);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|