mirror of
https://gitlab.com/pezkuwichain/pezkuwi-sdk.git
synced 2026-10-24 20:55:09 +00:00
Rewrite pezbridge-bot as notify-only, drop Noter key custody entirely
Renewing the automation key's spending allowance always needs 3-of-5 multisig approval regardless, so having this bot hold one signer's (Noter's) private key on a VPS to auto-submit one of those three signatures was never actually saving a real step - the other two signers still had to sign manually every time. Once pwap-web shipped a real multisig operations UI (and now wallet-android's Bridge screen too), the bot's only remaining job is to detect a low allowance and notify all 5 signers via Telegram - they each sign with their own wallet through one of pwap-web, pezbridge-sign.pex.mom, or the wallet app directly. Noter's mnemonic was removed from the VPS entirely (shred -u) after this rewrite was deployed and verified. Also adds gen_automation_key.rs (one-off key generation helper) and the updated systemd unit with LoadCredential removed.
This commit is contained in:
1 parent
042e1df314
commit
eb821b2ff5
6 files changed
+741
-44
No files matched your search
Generated
+404
-44
@@ -256,7 +256,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -482,7 +482,7 @@ dependencies = [
|
||||
"bs58",
|
||||
"hmac 0.12.1",
|
||||
"k256",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"ripemd",
|
||||
"secp256k1 0.27.0",
|
||||
"sha2 0.10.9",
|
||||
@@ -686,6 +686,12 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "chacha20"
|
||||
version = "0.9.1"
|
||||
@@ -694,7 +700,18 @@ checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cpufeatures",
|
||||
"cpufeatures 0.2.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chacha20"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.3.0",
|
||||
"rand_core 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -871,6 +888,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.3.12"
|
||||
@@ -899,7 +925,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
@@ -911,7 +937,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
@@ -947,7 +973,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"cpufeatures 0.2.17",
|
||||
"curve25519-dalek-derive",
|
||||
"digest 0.10.7",
|
||||
"fiat-crypto",
|
||||
@@ -1253,7 +1279,7 @@ dependencies = [
|
||||
"ed25519",
|
||||
"hashbrown 0.15.5",
|
||||
"pkcs8",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"sha2 0.10.9",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
@@ -1278,7 +1304,7 @@ dependencies = [
|
||||
"generic-array",
|
||||
"group",
|
||||
"pkcs8",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"sec1",
|
||||
"serdect",
|
||||
"subtle",
|
||||
@@ -1355,7 +1381,7 @@ version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
@@ -1388,7 +1414,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"rustc-hex",
|
||||
"static_assertions",
|
||||
]
|
||||
@@ -1617,8 +1643,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1629,18 +1657,32 @@ checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"r-efi 5.3.0",
|
||||
"wasip2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"r-efi 6.0.0",
|
||||
"rand_core 0.10.1",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom_or_panic"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ea1015b5a70616b688dc230cfe50c8af89d972cb132d5a622814d29773b10b9"
|
||||
dependencies = [
|
||||
"rand",
|
||||
"rand_core",
|
||||
"rand 0.8.5",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1656,7 +1698,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
|
||||
dependencies = [
|
||||
"ff",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
@@ -1793,12 +1835,94 @@ dependencies = [
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body-util"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-body",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.27.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f"
|
||||
dependencies = [
|
||||
"http",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"rustls",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"ipnet",
|
||||
"libc",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.64"
|
||||
@@ -2023,6 +2147,12 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.2"
|
||||
@@ -2203,7 +2333,7 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654"
|
||||
dependencies = [
|
||||
"cpufeatures",
|
||||
"cpufeatures 0.2.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2247,7 +2377,7 @@ dependencies = [
|
||||
"libsecp256k1-core",
|
||||
"libsecp256k1-gen-ecmult",
|
||||
"libsecp256k1-gen-genmult",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"sha2 0.9.9",
|
||||
"typenum",
|
||||
@@ -2318,6 +2448,12 @@ dependencies = [
|
||||
"hashbrown 0.15.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lru-slab"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.2.0"
|
||||
@@ -2350,7 +2486,7 @@ checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"keccak",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
@@ -2542,8 +2678,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e69bf016dc406eff7d53a7d3f7cf1c2e72c82b9088aac1118591e36dd2cd3e9"
|
||||
dependencies = [
|
||||
"bitcoin_hashes 0.13.0",
|
||||
"rand",
|
||||
"rand_core",
|
||||
"rand 0.8.5",
|
||||
"rand_core 0.6.4",
|
||||
"serde",
|
||||
"unicode-normalization",
|
||||
]
|
||||
@@ -2613,7 +2749,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
@@ -2983,7 +3119,7 @@ version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
|
||||
dependencies = [
|
||||
"cpufeatures",
|
||||
"cpufeatures 0.2.17",
|
||||
"opaque-debug",
|
||||
"universal-hash",
|
||||
]
|
||||
@@ -3089,6 +3225,62 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn"
|
||||
version = "0.11.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"cfg_aliases",
|
||||
"pin-project-lite",
|
||||
"quinn-proto",
|
||||
"quinn-udp",
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
"socket2",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn-proto"
|
||||
version = "0.11.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"getrandom 0.4.3",
|
||||
"lru-slab",
|
||||
"rand 0.10.2",
|
||||
"rand_pcg",
|
||||
"ring",
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"slab",
|
||||
"thiserror 2.0.18",
|
||||
"tinyvec",
|
||||
"tracing",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn-udp"
|
||||
version = "0.5.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694"
|
||||
dependencies = [
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"socket2",
|
||||
"tracing",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.44"
|
||||
@@ -3104,6 +3296,12 @@ version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||
|
||||
[[package]]
|
||||
name = "radium"
|
||||
version = "0.7.0"
|
||||
@@ -3118,7 +3316,18 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
|
||||
dependencies = [
|
||||
"chacha20 0.10.1",
|
||||
"getrandom 0.4.3",
|
||||
"rand_core 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3128,7 +3337,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3140,6 +3349,21 @@ dependencies = [
|
||||
"getrandom 0.2.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
|
||||
|
||||
[[package]]
|
||||
name = "rand_pcg"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a"
|
||||
dependencies = [
|
||||
"rand_core 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.18"
|
||||
@@ -3198,6 +3422,44 @@ version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-rustls",
|
||||
"hyper-util",
|
||||
"js-sys",
|
||||
"log",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rfc6979"
|
||||
version = "0.4.0"
|
||||
@@ -3304,6 +3566,7 @@ version = "1.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
|
||||
dependencies = [
|
||||
"web-time",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
@@ -3367,6 +3630,12 @@ version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7c1c839d570d835527c9a5e4db7cb2198683a988cb9d7293fc8674e6bd58fc8"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
|
||||
|
||||
[[package]]
|
||||
name = "salsa20"
|
||||
version = "0.10.2"
|
||||
@@ -3667,7 +3936,7 @@ dependencies = [
|
||||
"curve25519-dalek",
|
||||
"getrandom_or_panic",
|
||||
"merlin",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"serde_bytes",
|
||||
"sha2 0.10.9",
|
||||
"subtle",
|
||||
@@ -3732,7 +4001,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252"
|
||||
dependencies = [
|
||||
"bitcoin_hashes 0.14.1",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"secp256k1-sys 0.10.1",
|
||||
]
|
||||
|
||||
@@ -3872,6 +4141,18 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_urlencoded"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serdect"
|
||||
version = "0.2.0"
|
||||
@@ -3889,7 +4170,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"cpufeatures 0.2.17",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
@@ -3901,7 +4182,7 @@ checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
|
||||
dependencies = [
|
||||
"block-buffer 0.9.0",
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"cpufeatures 0.2.17",
|
||||
"digest 0.9.0",
|
||||
"opaque-debug",
|
||||
]
|
||||
@@ -3913,7 +4194,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"cpufeatures 0.2.17",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
@@ -3959,7 +4240,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
|
||||
dependencies = [
|
||||
"digest 0.10.7",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4025,7 +4306,7 @@ dependencies = [
|
||||
"bip39",
|
||||
"blake2-rfc",
|
||||
"bs58",
|
||||
"chacha20",
|
||||
"chacha20 0.9.1",
|
||||
"crossbeam-queue",
|
||||
"derive_more 0.99.20",
|
||||
"ed25519-zebra",
|
||||
@@ -4048,7 +4329,7 @@ dependencies = [
|
||||
"pbkdf2",
|
||||
"pin-project",
|
||||
"poly1305",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"rand_chacha",
|
||||
"ruzstd 0.6.0",
|
||||
"schnorrkel",
|
||||
@@ -4079,7 +4360,7 @@ dependencies = [
|
||||
"bip39",
|
||||
"blake2-rfc",
|
||||
"bs58",
|
||||
"chacha20",
|
||||
"chacha20 0.9.1",
|
||||
"crossbeam-queue",
|
||||
"derive_more 2.1.1",
|
||||
"ed25519-zebra",
|
||||
@@ -4102,7 +4383,7 @@ dependencies = [
|
||||
"pbkdf2",
|
||||
"pin-project",
|
||||
"poly1305",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"rand_chacha",
|
||||
"ruzstd 0.8.3",
|
||||
"schnorrkel",
|
||||
@@ -4145,7 +4426,7 @@ dependencies = [
|
||||
"lru",
|
||||
"parking_lot",
|
||||
"pin-project",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"rand_chacha",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4181,7 +4462,7 @@ dependencies = [
|
||||
"lru",
|
||||
"parking_lot",
|
||||
"pin-project",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"rand_chacha",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4213,7 +4494,7 @@ dependencies = [
|
||||
"futures",
|
||||
"httparse",
|
||||
"log",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"sha1",
|
||||
]
|
||||
|
||||
@@ -4272,7 +4553,7 @@ dependencies = [
|
||||
"parking_lot",
|
||||
"paste",
|
||||
"primitive-types 0.12.2",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"scale-info",
|
||||
"schnorrkel",
|
||||
"secp256k1 0.28.2",
|
||||
@@ -4402,7 +4683,7 @@ dependencies = [
|
||||
"num-traits",
|
||||
"parity-scale-codec",
|
||||
"paste",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"scale-info",
|
||||
"serde",
|
||||
"simple-mermaid",
|
||||
@@ -4459,7 +4740,7 @@ dependencies = [
|
||||
"log",
|
||||
"parity-scale-codec",
|
||||
"parking_lot",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"smallvec",
|
||||
"sp-core",
|
||||
"sp-externalities",
|
||||
@@ -4514,7 +4795,7 @@ dependencies = [
|
||||
"nohash-hasher",
|
||||
"parity-scale-codec",
|
||||
"parking_lot",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"scale-info",
|
||||
"schnellru",
|
||||
"sp-core",
|
||||
@@ -4842,6 +5123,15 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.2"
|
||||
@@ -5116,6 +5406,51 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"pin-project-lite",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.6.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"pin-project-lite",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-layer"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.44"
|
||||
@@ -5200,6 +5535,12 @@ dependencies = [
|
||||
"hash-db",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "twox-hash"
|
||||
version = "1.6.3"
|
||||
@@ -5208,7 +5549,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"digest 0.10.7",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
@@ -5315,6 +5656,7 @@ dependencies = [
|
||||
"hex",
|
||||
"pezkuwi-subxt",
|
||||
"pezkuwi-subxt-signer",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sp-core",
|
||||
@@ -5366,9 +5708,9 @@ dependencies = [
|
||||
"ark-serialize-derive",
|
||||
"arrayref",
|
||||
"digest 0.10.7",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"sha2 0.10.9",
|
||||
"sha3",
|
||||
"zeroize",
|
||||
@@ -5384,6 +5726,15 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
|
||||
dependencies = [
|
||||
"try-lock",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
@@ -5595,6 +5946,15 @@ dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@@ -5953,7 +6313,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277"
|
||||
dependencies = [
|
||||
"curve25519-dalek",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"serde",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
@@ -12,6 +12,10 @@ license = "Apache-2.0"
|
||||
name = "usdt-bridge"
|
||||
path = "src/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "pezbridge-bot"
|
||||
path = "src/bin/pezbridge_bot.rs"
|
||||
|
||||
[dependencies]
|
||||
# Async runtime
|
||||
tokio = { version = "1.36", features = ["full"] }
|
||||
@@ -54,6 +58,10 @@ chrono = { version = "0.4", features = ["serde"] }
|
||||
# Async streams
|
||||
futures = "0.3"
|
||||
|
||||
# Telegram Bot API HTTP client (pezbridge-bot only). rustls instead of native-tls/OpenSSL so
|
||||
# this cross-compiles cleanly to a static musl binary (no OpenSSL sysroot needed for the target).
|
||||
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
|
||||
|
||||
# Error handling
|
||||
anyhow = "1.0"
|
||||
thiserror = "1.0"
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
[Unit]
|
||||
Description=PezbridgeBot - USDT bridge automation-key allowance renewal notifier (holds no key)
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=pezbridgebot
|
||||
Group=pezbridgebot
|
||||
WorkingDirectory=/opt/pezbridgebot/data
|
||||
ExecStart=/opt/pezbridgebot/bin/pezbridge-bot
|
||||
Environment=PEZBRIDGE_CONFIG=/opt/pezbridgebot/bin/pezbridge_bot_config.json
|
||||
Environment=RUST_LOG=info
|
||||
EnvironmentFile=/etc/pezbridgebot/telegram_token.env
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
|
||||
# --- Hardening ---
|
||||
# Deployed 2026-07-13 on NEW-10 (89.116.25.180), simplified 2026-07-14: this process used to
|
||||
# also hold Noter's (Validator_04's) mnemonic via LoadCredential so it could automate 1-of-3
|
||||
# required signatures. That stopped being worth the risk once pwap-web shipped a real signing
|
||||
# UI (any of the 5 signers can now approve directly with their own wallet extension at
|
||||
# /multisig/pending) - the bot-held key only ever saved "2 humans click instead of 3", not
|
||||
# enough to justify a private key living on a server. Noter's mnemonic was removed from this
|
||||
# host entirely; this unit now only reads chain state and sends Telegram messages - it has no
|
||||
# LoadCredential, no signing capability, and nothing worth exfiltrating. The hardening below is
|
||||
# kept anyway as good baseline practice, not because there's a key to protect anymore.
|
||||
NoNewPrivileges=yes
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
PrivateTmp=yes
|
||||
PrivateDevices=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectKernelLogs=yes
|
||||
ProtectControlGroups=yes
|
||||
ProtectClock=yes
|
||||
ProtectHostname=yes
|
||||
RestrictSUIDSGID=yes
|
||||
RemoveIPC=yes
|
||||
LockPersonality=yes
|
||||
RestrictRealtime=yes
|
||||
RestrictNamespaces=yes
|
||||
MemoryDenyWriteExecute=yes
|
||||
SystemCallFilter=@system-service
|
||||
SystemCallArchitectures=native
|
||||
CapabilityBoundingSet=
|
||||
AmbientCapabilities=
|
||||
ReadWritePaths=/opt/pezbridgebot/data
|
||||
UMask=0077
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,27 @@
|
||||
use anyhow::Result;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let (pair, phrase, _) = sp_core::sr25519::Pair::generate_with_phrase(None);
|
||||
use sp_core::Pair as _;
|
||||
let public = pair.public();
|
||||
let account = sp_core::crypto::AccountId32::from(public.0);
|
||||
let ss58 = sp_core::crypto::Ss58Codec::to_ss58check_with_version(
|
||||
&account,
|
||||
sp_core::crypto::Ss58AddressFormat::custom(42),
|
||||
);
|
||||
|
||||
let out = serde_json::json!({
|
||||
"warning": "CONTAINS PRIVATE SEED PHRASE - KEEP SECURE - NOT IN GIT - NOT IN LOGS",
|
||||
"role": "usdt-bridge relayer automation key - LIMITED delegated allowance only via Assets.approve_transfer, never asset owner/admin",
|
||||
"ss58_address_pezkuwi": ss58,
|
||||
"scheme": "sr25519",
|
||||
"mnemonic": phrase,
|
||||
});
|
||||
std::fs::write("/home/myhez/res/bridge_automation_key.json", serde_json::to_string_pretty(&out)?)?;
|
||||
|
||||
println!("Generated automation key.");
|
||||
println!("Address (never the mnemonic): {}", ss58);
|
||||
println!("Saved to /home/myhez/res/bridge_automation_key.json");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"pezkuwi_rpc": "wss://asset-hub-rpc.pezkuwichain.io",
|
||||
"serok_chat_id": 5362991649,
|
||||
"ops_chat_id": -5420200114,
|
||||
"poll_interval_secs": 600,
|
||||
"renewal_threshold": 3000000000,
|
||||
"topup_amount": 10000000000,
|
||||
"state_path": "/opt/pezbridgebot/data/pezbridge_bot_state.json"
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
//! PezbridgeBot - notifies signers when the USDT bridge automation key's `Assets.approve_transfer`
|
||||
//! spending allowance needs renewal (see /home/myhez/res/validators-tiki.md and the
|
||||
//! usdt-bridge-multisig-migration memory for the full custody model).
|
||||
//!
|
||||
//! This process holds NO private key and cannot sign or submit anything. An earlier version of
|
||||
//! this bot held Noter's (Validator_04's) key so it could automate 1-of-3 required signatures -
|
||||
//! that made sense when it was written, since pwap-web had no working signing UI at all and
|
||||
//! every approval required a hand-written script. It stopped making sense once pwap-web's real
|
||||
//! multisig operations UI shipped (shared/lib/multisig.ts + web/src/components/
|
||||
//! MultisigOperations.tsx, at /multisig/pending): any of the 5 signers can now approve directly
|
||||
//! with their own wallet extension, so the only thing a bot-held key still bought was "2 humans
|
||||
//! click instead of 3" - not worth a private key sitting on a server for. Simplified back down
|
||||
//! to detect-and-notify only; Noter's mnemonic was removed from this host entirely.
|
||||
//!
|
||||
//! Flow:
|
||||
//! 1. Poll the automation key's remaining Assets.Approvals allowance for wUSDT (asset 1000).
|
||||
//! 2. When it drops below `renewal_threshold`, send ONE Telegram notification (to Serok's chat
|
||||
//! and the ops group) pointing signers at pwap-web's /multisig/pending page, where any 3 of
|
||||
//! the 5 real signatories can approve the renewal with their own wallets.
|
||||
//! 3. Suppress repeat notifications until the allowance is actually renewed (goes back above
|
||||
//! threshold) - avoids spamming every poll cycle while humans coordinate.
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sp_core::crypto::Ss58Codec;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use subxt::dynamic::{At, Value};
|
||||
use subxt::{OnlineClient, SubstrateConfig};
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
const WUSDT_ASSET_ID: u32 = 1000;
|
||||
const MULTISIG_ADDR: &str = "5GvwxmCDp3PC33KHoeWSgj3S7ocE7nzk1jiCCZMPSDBFeNcj";
|
||||
const AUTOMATION_KEY_ADDR: &str = "5GQu4PFUb1f3MTJ7i7c1CtLgDk3TVvpSW1VbQCRmfkMoC8cM";
|
||||
const MULTISIG_THRESHOLD: u16 = 3;
|
||||
const PWAP_WEB_OPERATIONS_URL: &str = "https://app.pezkuwichain.io/multisig/pending";
|
||||
|
||||
// ============================================================================
|
||||
// Config / state
|
||||
// ============================================================================
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct BotConfig {
|
||||
pezkuwi_rpc: String,
|
||||
serok_chat_id: i64,
|
||||
ops_chat_id: i64,
|
||||
poll_interval_secs: u64,
|
||||
renewal_threshold: u128,
|
||||
topup_amount: u128,
|
||||
state_path: PathBuf,
|
||||
}
|
||||
|
||||
impl Default for BotConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
pezkuwi_rpc: "wss://asset-hub-rpc.pezkuwichain.io".to_string(),
|
||||
serok_chat_id: 0,
|
||||
ops_chat_id: 0,
|
||||
poll_interval_secs: 600,
|
||||
renewal_threshold: 3_000_000_000, // 3,000 wUSDT
|
||||
topup_amount: 10_000_000_000, // 10,000 wUSDT
|
||||
state_path: PathBuf::from("pezbridge_bot_state.json"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_config() -> Result<BotConfig> {
|
||||
let path = std::env::var("PEZBRIDGE_CONFIG").unwrap_or_else(|_| "pezbridge_bot_config.json".to_string());
|
||||
if std::path::Path::new(&path).exists() {
|
||||
let content = std::fs::read_to_string(&path).context("reading config file")?;
|
||||
Ok(serde_json::from_str(&content).context("parsing config file")?)
|
||||
} else {
|
||||
warn!("No config file at {path}, using defaults - serok_chat_id/ops_chat_id MUST be set for this to be useful");
|
||||
Ok(BotConfig::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
struct BotState {
|
||||
/// Whether a notification has already been sent for the CURRENT low-allowance episode -
|
||||
/// reset back to false once the allowance rises back above threshold (meaning humans
|
||||
/// renewed it via pwap-web), so the next depletion gets its own fresh notification.
|
||||
already_notified: bool,
|
||||
}
|
||||
|
||||
fn load_state(path: &PathBuf) -> Result<BotState> {
|
||||
if path.exists() {
|
||||
let content = std::fs::read_to_string(path)?;
|
||||
Ok(serde_json::from_str(&content).unwrap_or_default())
|
||||
} else {
|
||||
Ok(BotState::default())
|
||||
}
|
||||
}
|
||||
|
||||
fn save_state(path: &PathBuf, state: &BotState) -> Result<()> {
|
||||
std::fs::write(path, serde_json::to_string_pretty(state)?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Chain read
|
||||
// ============================================================================
|
||||
|
||||
async fn get_approval_remaining(
|
||||
client: &OnlineClient<SubstrateConfig>,
|
||||
asset_id: u32,
|
||||
owner: &str,
|
||||
delegate: &str,
|
||||
) -> Result<u128> {
|
||||
let owner_bytes = sp_core::crypto::AccountId32::from_ss58check(owner)
|
||||
.map_err(|e| anyhow!("invalid owner address: {e:?}"))?;
|
||||
let delegate_bytes = sp_core::crypto::AccountId32::from_ss58check(delegate)
|
||||
.map_err(|e| anyhow!("invalid delegate address: {e:?}"))?;
|
||||
|
||||
let storage_query = subxt::dynamic::storage(
|
||||
"Assets",
|
||||
"Approvals",
|
||||
vec![
|
||||
Value::primitive(asset_id.into()),
|
||||
Value::from_bytes(<sp_core::crypto::AccountId32 as AsRef<[u8; 32]>>::as_ref(&owner_bytes)),
|
||||
Value::from_bytes(<sp_core::crypto::AccountId32 as AsRef<[u8; 32]>>::as_ref(&delegate_bytes)),
|
||||
],
|
||||
);
|
||||
|
||||
let result = client.storage().at_latest().await?.fetch(&storage_query).await?;
|
||||
if let Some(v) = result {
|
||||
if let Some(amount) = v.to_value()?.at("amount").and_then(|a| a.as_u128()) {
|
||||
return Ok(amount);
|
||||
}
|
||||
}
|
||||
// No approval entry at all = the allowance is fully exhausted (or was never granted) -
|
||||
// treat identically to "0 remaining" so it triggers a notification.
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Telegram (send-only - no polling/callbacks needed anymore, there's no action this bot takes)
|
||||
// ============================================================================
|
||||
|
||||
async fn tg_send_message(http: &reqwest::Client, token: &str, chat_id: i64, text: &str) -> Result<()> {
|
||||
if chat_id == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
let url = format!("https://api.telegram.org/bot{token}/sendMessage");
|
||||
let body = serde_json::json!({ "chat_id": chat_id, "text": text });
|
||||
let resp = http.post(&url).json(&body).send().await?;
|
||||
let json: serde_json::Value = resp.json().await?;
|
||||
if json["ok"].as_bool() != Some(true) {
|
||||
return Err(anyhow!("sendMessage failed: {json}"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn notify(http: &reqwest::Client, token: &str, config: &BotConfig, msg: &str) {
|
||||
info!("[pezbridge-bot] {msg}");
|
||||
if let Err(e) = tg_send_message(http, token, config.serok_chat_id, msg).await {
|
||||
error!("failed to notify Serok: {e:#}");
|
||||
}
|
||||
if let Err(e) = tg_send_message(http, token, config.ops_chat_id, msg).await {
|
||||
error!("failed to notify ops chat: {e:#}");
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Core logic
|
||||
// ============================================================================
|
||||
|
||||
async fn check_allowance_and_maybe_notify(
|
||||
read_client: &OnlineClient<SubstrateConfig>,
|
||||
http: &reqwest::Client,
|
||||
token: &str,
|
||||
config: &BotConfig,
|
||||
state: &mut BotState,
|
||||
) -> Result<()> {
|
||||
let remaining = get_approval_remaining(read_client, WUSDT_ASSET_ID, MULTISIG_ADDR, AUTOMATION_KEY_ADDR).await?;
|
||||
info!("Automation key remaining allowance: {:.2} wUSDT", remaining as f64 / 1_000_000.0);
|
||||
|
||||
if remaining >= config.renewal_threshold {
|
||||
if state.already_notified {
|
||||
info!("Allowance is back above threshold - renewal happened, resetting notification state");
|
||||
state.already_notified = false;
|
||||
save_state(&config.state_path, state)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if state.already_notified {
|
||||
return Ok(()); // already told everyone, don't spam while they coordinate
|
||||
}
|
||||
|
||||
let topup = config.topup_amount;
|
||||
let text = format!(
|
||||
"\u{26A0}\u{FE0F} USDT Bridge - otomasyon anahtarı izni azaldı\n\n\
|
||||
Kalan izin: {:.2} wUSDT\nEşik: {:.2} wUSDT\n\n\
|
||||
Önerilen yenileme: {:.2} wUSDT (Assets.approve_transfer, multisig'den otomasyon anahtarına).\n\n\
|
||||
{} imzacının (5'ten herhangi 3'ü) pwap-web üzerinden kendi cüzdanlarıyla onaylaması gerekiyor:\n{}",
|
||||
remaining as f64 / 1_000_000.0,
|
||||
config.renewal_threshold as f64 / 1_000_000.0,
|
||||
topup as f64 / 1_000_000.0,
|
||||
MULTISIG_THRESHOLD,
|
||||
PWAP_WEB_OPERATIONS_URL
|
||||
);
|
||||
|
||||
notify(http, token, config, &text).await;
|
||||
|
||||
state.already_notified = true;
|
||||
save_state(&config.state_path, state)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
tracing_subscriber::fmt().with_env_filter(tracing_subscriber::EnvFilter::from_default_env()).init();
|
||||
|
||||
let config = load_config()?;
|
||||
if config.serok_chat_id == 0 && config.ops_chat_id == 0 {
|
||||
return Err(anyhow!("both serok_chat_id and ops_chat_id are unset - this bot would notify no one"));
|
||||
}
|
||||
|
||||
let token = std::env::var("PEZBRIDGE_TELEGRAM_TOKEN").context("PEZBRIDGE_TELEGRAM_TOKEN env var not set")?;
|
||||
|
||||
info!("Connecting to {}...", config.pezkuwi_rpc);
|
||||
let read_client = OnlineClient::<SubstrateConfig>::from_url(&config.pezkuwi_rpc)
|
||||
.await
|
||||
.context("failed to connect")?;
|
||||
|
||||
let http = reqwest::Client::new();
|
||||
let mut state = load_state(&config.state_path)?;
|
||||
|
||||
notify(&http, &token, &config, "PezbridgeBot started (notify-only, holds no key).").await;
|
||||
|
||||
let mut poll_timer = tokio::time::interval(Duration::from_secs(config.poll_interval_secs));
|
||||
loop {
|
||||
poll_timer.tick().await;
|
||||
if let Err(e) = check_allowance_and_maybe_notify(&read_client, &http, &token, &config, &mut state).await {
|
||||
error!("allowance check failed: {e:#}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user