@wlfi-agent/cli 1.4.17 → 1.4.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +5 -0
- package/README.md +61 -28
- package/crates/vault-cli-admin/src/io_utils.rs +149 -1
- package/crates/vault-cli-admin/src/main.rs +639 -16
- package/crates/vault-cli-admin/src/shared_config.rs +18 -18
- package/crates/vault-cli-admin/src/tui/token_rpc.rs +190 -3
- package/crates/vault-cli-admin/src/tui/utils.rs +59 -0
- package/crates/vault-cli-admin/src/tui.rs +1205 -120
- package/crates/vault-cli-agent/Cargo.toml +1 -0
- package/crates/vault-cli-agent/src/io_utils.rs +163 -2
- package/crates/vault-cli-agent/src/main.rs +648 -32
- package/crates/vault-cli-daemon/Cargo.toml +4 -0
- package/crates/vault-cli-daemon/src/main.rs +617 -67
- package/crates/vault-cli-daemon/src/relay_sync.rs +776 -4
- package/crates/vault-cli-daemon/tests/system_keychain_helper_acl.rs +5 -0
- package/crates/vault-daemon/src/daemon_parts/api_impl_and_utils.rs +32 -1
- package/crates/vault-daemon/src/persistence.rs +637 -100
- package/crates/vault-daemon/src/tests.rs +1013 -3
- package/crates/vault-daemon/src/tests_parts/part2.rs +99 -0
- package/crates/vault-daemon/src/tests_parts/part4.rs +11 -7
- package/crates/vault-domain/src/nonce.rs +4 -0
- package/crates/vault-domain/src/tests.rs +616 -0
- package/crates/vault-policy/src/engine.rs +55 -32
- package/crates/vault-policy/src/tests.rs +195 -0
- package/crates/vault-sdk-agent/src/lib.rs +415 -22
- package/crates/vault-signer/Cargo.toml +3 -0
- package/crates/vault-signer/src/lib.rs +266 -40
- package/crates/vault-transport-unix/src/lib.rs +653 -5
- package/crates/vault-transport-xpc/src/tests.rs +531 -3
- package/crates/vault-transport-xpc/tests/e2e_flow.rs +3 -0
- package/dist/cli.cjs +663 -190
- package/dist/cli.cjs.map +1 -1
- package/package.json +5 -2
- package/packages/cache/.turbo/turbo-build.log +20 -20
- package/packages/cache/coverage/clover.xml +529 -394
- package/packages/cache/coverage/coverage-final.json +2 -2
- package/packages/cache/coverage/index.html +21 -21
- package/packages/cache/coverage/src/client/index.html +1 -1
- package/packages/cache/coverage/src/client/index.ts.html +1 -1
- package/packages/cache/coverage/src/errors/index.html +1 -1
- package/packages/cache/coverage/src/errors/index.ts.html +12 -12
- package/packages/cache/coverage/src/index.html +1 -1
- package/packages/cache/coverage/src/index.ts.html +1 -1
- package/packages/cache/coverage/src/service/index.html +21 -21
- package/packages/cache/coverage/src/service/index.ts.html +769 -313
- package/packages/cache/dist/{chunk-QNK6GOTI.js → chunk-KC53LH5Z.js} +35 -2
- package/packages/cache/dist/chunk-KC53LH5Z.js.map +1 -0
- package/packages/cache/dist/{chunk-QF4XKEIA.cjs → chunk-UVU7VFE3.cjs} +35 -2
- package/packages/cache/dist/chunk-UVU7VFE3.cjs.map +1 -0
- package/packages/cache/dist/index.cjs +2 -2
- package/packages/cache/dist/index.js +1 -1
- package/packages/cache/dist/service/index.cjs +2 -2
- package/packages/cache/dist/service/index.js +1 -1
- package/packages/cache/node_modules/.bin/tsc +2 -2
- package/packages/cache/node_modules/.bin/tsserver +2 -2
- package/packages/cache/node_modules/.bin/tsup +2 -2
- package/packages/cache/node_modules/.bin/tsup-node +2 -2
- package/packages/cache/node_modules/.bin/vitest +4 -4
- package/packages/cache/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/packages/cache/src/service/index.test.ts +165 -19
- package/packages/cache/src/service/index.ts +38 -1
- package/packages/config/.turbo/turbo-build.log +4 -4
- package/packages/config/dist/index.cjs +0 -17
- package/packages/config/dist/index.cjs.map +1 -1
- package/packages/config/src/index.ts +0 -17
- package/packages/rpc/.turbo/turbo-build.log +11 -11
- package/packages/rpc/dist/index.cjs +0 -17
- package/packages/rpc/dist/index.cjs.map +1 -1
- package/packages/rpc/src/index.js +1 -0
- package/packages/ui/node_modules/.bin/tsc +2 -2
- package/packages/ui/node_modules/.bin/tsserver +2 -2
- package/packages/ui/node_modules/.bin/tsup +2 -2
- package/packages/ui/node_modules/.bin/tsup-node +2 -2
- package/scripts/install-cli-launcher.mjs +37 -0
- package/scripts/install-rust-binaries.mjs +47 -0
- package/scripts/run-tests-isolated.mjs +210 -0
- package/src/cli.ts +310 -50
- package/src/lib/admin-reset.ts +15 -30
- package/src/lib/admin-setup.ts +246 -55
- package/src/lib/agent-auth-migrate.ts +5 -1
- package/src/lib/asset-broadcast.ts +15 -4
- package/src/lib/config-amounts.ts +6 -4
- package/src/lib/hidden-tty-prompt.js +1 -0
- package/src/lib/hidden-tty-prompt.ts +105 -0
- package/src/lib/keychain.ts +1 -0
- package/src/lib/local-admin-access.ts +4 -29
- package/src/lib/rust.ts +129 -33
- package/src/lib/signed-tx.ts +1 -0
- package/src/lib/sudo.ts +15 -5
- package/src/lib/wallet-profile.ts +3 -0
- package/src/lib/wallet-setup.ts +52 -0
- package/packages/cache/dist/chunk-QF4XKEIA.cjs.map +0 -1
- package/packages/cache/dist/chunk-QNK6GOTI.js.map +0 -1
|
@@ -392,9 +392,9 @@ mod tests {
|
|
|
392
392
|
r#"{
|
|
393
393
|
"chains": {},
|
|
394
394
|
"tokens": {},
|
|
395
|
-
"chainId":
|
|
396
|
-
"chainName": "
|
|
397
|
-
"rpcUrl": "https://rpc.
|
|
395
|
+
"chainId": 56,
|
|
396
|
+
"chainName": "bsc",
|
|
397
|
+
"rpcUrl": "https://rpc.bsc.example"
|
|
398
398
|
}
|
|
399
399
|
"#,
|
|
400
400
|
)
|
|
@@ -405,11 +405,11 @@ mod tests {
|
|
|
405
405
|
assert!(config.chains.contains_key("bsc"));
|
|
406
406
|
assert!(config.tokens.contains_key("usd1"));
|
|
407
407
|
assert!(config.tokens.contains_key("bnb"));
|
|
408
|
-
assert_eq!(config.chain_id, Some(
|
|
409
|
-
assert_eq!(config.chain_name.as_deref(), Some("
|
|
408
|
+
assert_eq!(config.chain_id, Some(56));
|
|
409
|
+
assert_eq!(config.chain_name.as_deref(), Some("bsc"));
|
|
410
410
|
assert_eq!(
|
|
411
411
|
config.rpc_url.as_deref(),
|
|
412
|
-
Some("https://rpc.
|
|
412
|
+
Some("https://rpc.bsc.example")
|
|
413
413
|
);
|
|
414
414
|
|
|
415
415
|
fs::remove_dir_all(&root).expect("cleanup temp root");
|
|
@@ -489,25 +489,25 @@ mod tests {
|
|
|
489
489
|
let path = root.join("config.json");
|
|
490
490
|
|
|
491
491
|
let mut config = WlfiConfig {
|
|
492
|
-
chain_id: Some(
|
|
493
|
-
chain_name: Some("
|
|
494
|
-
rpc_url: Some("https://rpc.
|
|
492
|
+
chain_id: Some(1),
|
|
493
|
+
chain_name: Some("eth".to_string()),
|
|
494
|
+
rpc_url: Some("https://rpc.ethereum.example".to_string()),
|
|
495
495
|
..WlfiConfig::default()
|
|
496
496
|
};
|
|
497
497
|
config.chains.insert(
|
|
498
|
-
"
|
|
498
|
+
"eth".to_string(),
|
|
499
499
|
super::ChainProfile {
|
|
500
|
-
chain_id:
|
|
501
|
-
name: "
|
|
502
|
-
rpc_url: Some("https://rpc.
|
|
500
|
+
chain_id: 1,
|
|
501
|
+
name: "eth".to_string(),
|
|
502
|
+
rpc_url: Some("https://rpc.ethereum.example".to_string()),
|
|
503
503
|
extra: BTreeMap::new(),
|
|
504
504
|
},
|
|
505
505
|
);
|
|
506
506
|
config.tokens.insert(
|
|
507
|
-
"
|
|
507
|
+
"usd1".to_string(),
|
|
508
508
|
TokenProfile {
|
|
509
|
-
name: Some("
|
|
510
|
-
symbol: "
|
|
509
|
+
name: Some("USD1".to_string()),
|
|
510
|
+
symbol: "USD1".to_string(),
|
|
511
511
|
default_policy: Some(TokenPolicyProfile {
|
|
512
512
|
per_tx_amount: Some(25.0),
|
|
513
513
|
daily_amount: Some(100.0),
|
|
@@ -559,9 +559,9 @@ mod tests {
|
|
|
559
559
|
extra: BTreeMap::new(),
|
|
560
560
|
}],
|
|
561
561
|
chains: BTreeMap::from([(
|
|
562
|
-
"
|
|
562
|
+
"eth".to_string(),
|
|
563
563
|
TokenChainProfile {
|
|
564
|
-
chain_id:
|
|
564
|
+
chain_id: 1,
|
|
565
565
|
is_native: false,
|
|
566
566
|
address: Some("0x1000000000000000000000000000000000000000".to_string()),
|
|
567
567
|
decimals: 6,
|
|
@@ -229,7 +229,54 @@ fn decode_abi_usize(bytes: &[u8]) -> Result<usize> {
|
|
|
229
229
|
|
|
230
230
|
#[cfg(test)]
|
|
231
231
|
mod tests {
|
|
232
|
-
use
|
|
232
|
+
use std::io::{Read, Write};
|
|
233
|
+
use std::net::TcpListener;
|
|
234
|
+
use std::thread;
|
|
235
|
+
use std::time::Duration;
|
|
236
|
+
|
|
237
|
+
use super::{
|
|
238
|
+
call_rpc, decode_abi_string, decode_hex_bytes, decode_abi_usize, eth_call,
|
|
239
|
+
fetch_token_metadata, native_metadata_for_chain, parse_hex_u64, read_erc20_decimals,
|
|
240
|
+
};
|
|
241
|
+
use reqwest::Client;
|
|
242
|
+
use serde_json::json;
|
|
243
|
+
use vault_domain::EvmAddress;
|
|
244
|
+
|
|
245
|
+
fn start_mock_rpc_server(responses: Vec<String>) -> (String, thread::JoinHandle<()>) {
|
|
246
|
+
let listener = TcpListener::bind("127.0.0.1:0").expect("bind");
|
|
247
|
+
let addr = listener.local_addr().expect("addr");
|
|
248
|
+
let handle = thread::spawn(move || {
|
|
249
|
+
for body in responses {
|
|
250
|
+
let (mut stream, _) = listener.accept().expect("accept");
|
|
251
|
+
stream
|
|
252
|
+
.set_read_timeout(Some(Duration::from_millis(250)))
|
|
253
|
+
.expect("timeout");
|
|
254
|
+
let mut buffer = [0u8; 4096];
|
|
255
|
+
loop {
|
|
256
|
+
match stream.read(&mut buffer) {
|
|
257
|
+
Ok(0) => break,
|
|
258
|
+
Ok(_) => continue,
|
|
259
|
+
Err(err)
|
|
260
|
+
if err.kind() == std::io::ErrorKind::WouldBlock
|
|
261
|
+
|| err.kind() == std::io::ErrorKind::TimedOut =>
|
|
262
|
+
{
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
Err(err) => panic!("failed to read request: {err}"),
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
let response = format!(
|
|
269
|
+
"HTTP/1.1 200 OK\r\ncontent-type: application/json\r\ncontent-length: {}\r\nconnection: close\r\n\r\n{}",
|
|
270
|
+
body.len(),
|
|
271
|
+
body
|
|
272
|
+
);
|
|
273
|
+
stream
|
|
274
|
+
.write_all(response.as_bytes())
|
|
275
|
+
.expect("write response");
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
(format!("http://{addr}"), handle)
|
|
279
|
+
}
|
|
233
280
|
|
|
234
281
|
#[test]
|
|
235
282
|
fn parse_hex_u64_reads_eth_chain_id_payloads() {
|
|
@@ -243,8 +290,148 @@ mod tests {
|
|
|
243
290
|
"0x",
|
|
244
291
|
"0000000000000000000000000000000000000000000000000000000000000020",
|
|
245
292
|
"0000000000000000000000000000000000000000000000000000000000000004",
|
|
246
|
-
"
|
|
293
|
+
"5553443100000000000000000000000000000000000000000000000000000000"
|
|
294
|
+
);
|
|
295
|
+
assert_eq!(decode_abi_string(encoded).expect("string"), "USD1");
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
#[test]
|
|
299
|
+
fn helper_parsers_reject_invalid_inputs() {
|
|
300
|
+
assert!(parse_hex_u64("1", "chain").is_err());
|
|
301
|
+
assert!(parse_hex_u64("0xzz", "chain").is_err());
|
|
302
|
+
assert!(decode_hex_bytes("deadbeef").is_err());
|
|
303
|
+
assert!(decode_hex_bytes("0xzz").is_err());
|
|
304
|
+
assert!(decode_abi_usize(&[0u8; 31]).is_err());
|
|
305
|
+
|
|
306
|
+
let too_short = "0x1234";
|
|
307
|
+
assert!(decode_abi_string(too_short).is_err());
|
|
308
|
+
let bad_offset = concat!(
|
|
309
|
+
"0x",
|
|
310
|
+
"0000000000000000000000000000000000000000000000000000000000000060",
|
|
311
|
+
"0000000000000000000000000000000000000000000000000000000000000004",
|
|
312
|
+
"5553443100000000000000000000000000000000000000000000000000000000"
|
|
247
313
|
);
|
|
248
|
-
|
|
314
|
+
assert!(decode_abi_string(bad_offset).is_err());
|
|
315
|
+
let bad_utf8 = concat!(
|
|
316
|
+
"0x",
|
|
317
|
+
"0000000000000000000000000000000000000000000000000000000000000020",
|
|
318
|
+
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
319
|
+
"ffff000000000000000000000000000000000000000000000000000000000000"
|
|
320
|
+
);
|
|
321
|
+
assert!(decode_abi_string(bad_utf8).is_err());
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
#[test]
|
|
325
|
+
fn native_metadata_for_chain_covers_known_and_fallback_symbols() {
|
|
326
|
+
assert_eq!(native_metadata_for_chain(1, "eth"), ("Ether".to_string(), "ETH".to_string()));
|
|
327
|
+
assert_eq!(native_metadata_for_chain(56, "bsc"), ("BNB".to_string(), "BNB".to_string()));
|
|
328
|
+
assert_eq!(native_metadata_for_chain(137, "polygon"), ("MATIC".to_string(), "MATIC".to_string()));
|
|
329
|
+
assert_eq!(
|
|
330
|
+
native_metadata_for_chain(999, "custom"),
|
|
331
|
+
("Native Asset (custom)".to_string(), "NATIVE".to_string())
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
#[tokio::test]
|
|
336
|
+
async fn rpc_helpers_cover_success_and_error_responses() {
|
|
337
|
+
let (rpc_url, handle) = start_mock_rpc_server(vec![
|
|
338
|
+
json!({"jsonrpc":"2.0","id":1,"result":"0x1"}).to_string(),
|
|
339
|
+
json!({"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"boom"}}).to_string(),
|
|
340
|
+
json!({"jsonrpc":"2.0","id":1,"result":42}).to_string(),
|
|
341
|
+
]);
|
|
342
|
+
|
|
343
|
+
let client = Client::new();
|
|
344
|
+
let value = call_rpc(&client, &rpc_url, "eth_chainId", json!([]))
|
|
345
|
+
.await
|
|
346
|
+
.expect("chain id");
|
|
347
|
+
assert_eq!(value, json!("0x1"));
|
|
348
|
+
|
|
349
|
+
let err = call_rpc(&client, &rpc_url, "eth_call", json!([]))
|
|
350
|
+
.await
|
|
351
|
+
.expect_err("rpc error");
|
|
352
|
+
assert!(err.to_string().contains("boom"));
|
|
353
|
+
|
|
354
|
+
let address: EvmAddress = "0x1111111111111111111111111111111111111111"
|
|
355
|
+
.parse()
|
|
356
|
+
.expect("address");
|
|
357
|
+
let err = eth_call(&client, &rpc_url, &address, "0x313ce567")
|
|
358
|
+
.await
|
|
359
|
+
.expect_err("non-string eth_call");
|
|
360
|
+
assert!(err.to_string().contains("non-string"));
|
|
361
|
+
|
|
362
|
+
handle.join().expect("server join");
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
#[tokio::test]
|
|
366
|
+
async fn fetch_token_metadata_covers_native_erc20_and_validation_failures() {
|
|
367
|
+
let symbol_payload = concat!(
|
|
368
|
+
"0x",
|
|
369
|
+
"0000000000000000000000000000000000000000000000000000000000000020",
|
|
370
|
+
"0000000000000000000000000000000000000000000000000000000000000004",
|
|
371
|
+
"5553443100000000000000000000000000000000000000000000000000000000"
|
|
372
|
+
);
|
|
373
|
+
let name_payload = concat!(
|
|
374
|
+
"0x",
|
|
375
|
+
"0000000000000000000000000000000000000000000000000000000000000020",
|
|
376
|
+
"0000000000000000000000000000000000000000000000000000000000000004",
|
|
377
|
+
"5553443100000000000000000000000000000000000000000000000000000000"
|
|
378
|
+
);
|
|
379
|
+
let decimals_payload = format!("0x{}", "00".repeat(31) + "06");
|
|
380
|
+
let address: EvmAddress = "0x1111111111111111111111111111111111111111"
|
|
381
|
+
.parse()
|
|
382
|
+
.expect("address");
|
|
383
|
+
|
|
384
|
+
let (rpc_url, handle) = start_mock_rpc_server(vec![
|
|
385
|
+
json!({"jsonrpc":"2.0","id":1,"result":"0x1"}).to_string(),
|
|
386
|
+
json!({"jsonrpc":"2.0","id":1,"result":"0x1"}).to_string(),
|
|
387
|
+
json!({"jsonrpc":"2.0","id":1,"result":decimals_payload}).to_string(),
|
|
388
|
+
json!({"jsonrpc":"2.0","id":1,"result":symbol_payload}).to_string(),
|
|
389
|
+
json!({"jsonrpc":"2.0","id":1,"result":name_payload}).to_string(),
|
|
390
|
+
json!({"jsonrpc":"2.0","id":1,"result":"0x2"}).to_string(),
|
|
391
|
+
json!({"jsonrpc":"2.0","id":1,"result":"0x1"}).to_string(),
|
|
392
|
+
]);
|
|
393
|
+
|
|
394
|
+
let native = fetch_token_metadata("eth", &rpc_url, 1, true, None)
|
|
395
|
+
.await
|
|
396
|
+
.expect("native metadata");
|
|
397
|
+
assert_eq!(native.symbol, "ETH");
|
|
398
|
+
assert_eq!(native.decimals, 18);
|
|
399
|
+
|
|
400
|
+
let erc20 = fetch_token_metadata("eth", &rpc_url, 1, false, Some(&address))
|
|
401
|
+
.await
|
|
402
|
+
.expect("erc20 metadata");
|
|
403
|
+
assert_eq!(erc20.symbol, "USD1");
|
|
404
|
+
assert_eq!(erc20.name, "USD1");
|
|
405
|
+
assert_eq!(erc20.decimals, 6);
|
|
406
|
+
|
|
407
|
+
let err = fetch_token_metadata("eth", &rpc_url, 1, true, None)
|
|
408
|
+
.await
|
|
409
|
+
.expect_err("chain mismatch");
|
|
410
|
+
assert!(err.to_string().contains("expects 1"));
|
|
411
|
+
|
|
412
|
+
let err = fetch_token_metadata("eth", &rpc_url, 1, false, None)
|
|
413
|
+
.await
|
|
414
|
+
.expect_err("missing address");
|
|
415
|
+
assert!(err.to_string().contains("token address is required"));
|
|
416
|
+
|
|
417
|
+
handle.join().expect("server join");
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
#[tokio::test]
|
|
421
|
+
async fn read_erc20_decimals_rejects_wrong_length_payloads() {
|
|
422
|
+
let (rpc_url, handle) = start_mock_rpc_server(vec![
|
|
423
|
+
json!({"jsonrpc":"2.0","id":1,"result":"0x0102"}).to_string(),
|
|
424
|
+
]);
|
|
425
|
+
let client = Client::new();
|
|
426
|
+
let address: EvmAddress = "0x1111111111111111111111111111111111111111"
|
|
427
|
+
.parse()
|
|
428
|
+
.expect("address");
|
|
429
|
+
|
|
430
|
+
let err = read_erc20_decimals(&client, &rpc_url, &address)
|
|
431
|
+
.await
|
|
432
|
+
.expect_err("bad decimals payload");
|
|
433
|
+
assert!(err.to_string().contains("expected 32"));
|
|
434
|
+
|
|
435
|
+
handle.join().expect("server join");
|
|
249
436
|
}
|
|
250
437
|
}
|
|
@@ -80,3 +80,62 @@ pub(super) fn is_allowed_input_char(field: Field, ch: char) -> bool {
|
|
|
80
80
|
_ => true,
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
+
|
|
84
|
+
#[cfg(test)]
|
|
85
|
+
mod tests {
|
|
86
|
+
use super::{
|
|
87
|
+
bool_label, is_allowed_input_char, parse_address, parse_non_negative_u128,
|
|
88
|
+
parse_positive_u128, parse_positive_u64,
|
|
89
|
+
};
|
|
90
|
+
use crate::tui::Field;
|
|
91
|
+
|
|
92
|
+
#[test]
|
|
93
|
+
fn parsers_accept_trimmed_values_and_reject_invalid_or_zero_inputs() {
|
|
94
|
+
assert_eq!(parse_positive_u128("amount", " 42 ").expect("u128"), 42);
|
|
95
|
+
assert_eq!(
|
|
96
|
+
parse_non_negative_u128("count", " 0 ").expect("non-negative"),
|
|
97
|
+
0
|
|
98
|
+
);
|
|
99
|
+
assert_eq!(parse_positive_u64("chain", " 7 ").expect("u64"), 7);
|
|
100
|
+
|
|
101
|
+
assert!(parse_positive_u128("amount", "0").is_err());
|
|
102
|
+
assert!(parse_positive_u128("amount", "nope").is_err());
|
|
103
|
+
assert!(parse_non_negative_u128("count", "nope").is_err());
|
|
104
|
+
assert!(parse_positive_u64("chain", "0").is_err());
|
|
105
|
+
assert!(parse_positive_u64("chain", "bad").is_err());
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
#[test]
|
|
109
|
+
fn parse_address_and_bool_label_cover_happy_and_error_paths() {
|
|
110
|
+
assert_eq!(bool_label(true), "yes");
|
|
111
|
+
assert_eq!(bool_label(false), "no");
|
|
112
|
+
|
|
113
|
+
let address = parse_address(
|
|
114
|
+
"recipient",
|
|
115
|
+
" 0x1111111111111111111111111111111111111111 ",
|
|
116
|
+
)
|
|
117
|
+
.expect("address");
|
|
118
|
+
assert_eq!(address.as_str(), "0x1111111111111111111111111111111111111111");
|
|
119
|
+
assert!(parse_address("recipient", "not-an-address").is_err());
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
#[test]
|
|
123
|
+
fn allowed_input_characters_match_field_types() {
|
|
124
|
+
assert!(is_allowed_input_char(Field::TokenKey, 'a'));
|
|
125
|
+
assert!(is_allowed_input_char(Field::TokenKey, '-'));
|
|
126
|
+
assert!(!is_allowed_input_char(Field::TokenKey, '!'));
|
|
127
|
+
|
|
128
|
+
assert!(is_allowed_input_char(Field::PerTxLimit, '9'));
|
|
129
|
+
assert!(is_allowed_input_char(Field::PerTxLimit, '.'));
|
|
130
|
+
assert!(!is_allowed_input_char(Field::PerTxLimit, 'x'));
|
|
131
|
+
|
|
132
|
+
assert!(is_allowed_input_char(Field::DailyMaxTxCount, '4'));
|
|
133
|
+
assert!(!is_allowed_input_char(Field::DailyMaxTxCount, '.'));
|
|
134
|
+
|
|
135
|
+
assert!(is_allowed_input_char(Field::NetworkAddress, 'a'));
|
|
136
|
+
assert!(is_allowed_input_char(Field::NetworkAddress, 'X'));
|
|
137
|
+
assert!(!is_allowed_input_char(Field::NetworkAddress, 'g'));
|
|
138
|
+
|
|
139
|
+
assert!(is_allowed_input_char(Field::TokenName, '!'));
|
|
140
|
+
}
|
|
141
|
+
}
|