safehands-pharos 1.4.0 → 1.5.0
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/README.md +174 -63
- package/dist/cli.d.ts +5 -5
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +126 -124
- package/dist/cli.js.map +1 -1
- package/dist/demo.d.ts +1 -1
- package/dist/demo.js +171 -171
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +73 -65
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +1 -1
- package/dist/init.js +65 -65
- package/dist/lib/constants.d.ts +303 -291
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +302 -292
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/dodoApi.d.ts +78 -78
- package/dist/lib/dodoApi.js +196 -196
- package/dist/lib/envLoader.d.ts +2 -0
- package/dist/lib/envLoader.d.ts.map +1 -0
- package/dist/lib/envLoader.js +44 -0
- package/dist/lib/envLoader.js.map +1 -0
- package/dist/lib/http.d.ts +14 -14
- package/dist/lib/http.js +118 -118
- package/dist/lib/pharosClient.d.ts +58 -58
- package/dist/lib/pharosClient.js +63 -63
- package/dist/lib/policy/actionPolicyEngine.d.ts +53 -53
- package/dist/lib/policy/actionPolicyEngine.js +212 -212
- package/dist/lib/riskEngine.d.ts +26 -26
- package/dist/lib/riskEngine.d.ts.map +1 -1
- package/dist/lib/riskEngine.js +288 -283
- package/dist/lib/riskEngine.js.map +1 -1
- package/dist/lib/signer/index.d.ts +24 -24
- package/dist/lib/signer/index.js +88 -88
- package/dist/lib/testDodoLive.d.ts +1 -1
- package/dist/lib/testDodoLive.js +104 -104
- package/dist/lib/testLiveSafehands.d.ts +1 -1
- package/dist/lib/testLiveSafehands.js +92 -92
- package/dist/lib/testRpcLive.d.ts +1 -1
- package/dist/lib/testRpcLive.js +88 -88
- package/dist/lib/testTools.js +397 -398
- package/dist/lib/testX402Live.d.ts +1 -1
- package/dist/lib/testX402Live.js +159 -159
- package/dist/lib/toolResponse.d.ts +25 -25
- package/dist/lib/toolResponse.js +53 -53
- package/dist/lib/wallet/index.d.ts +37 -37
- package/dist/lib/wallet/index.js +128 -128
- package/dist/scripts/checkDeploy.d.ts +1 -1
- package/dist/scripts/checkDeploy.js +24 -24
- package/dist/scripts/deployRegistry.d.ts +1 -1
- package/dist/scripts/deployRegistry.js +100 -100
- package/dist/scripts/testRegistry.d.ts +1 -1
- package/dist/scripts/testRegistry.js +43 -43
- package/dist/tools/approveToken.d.ts +45 -45
- package/dist/tools/approveToken.js +85 -85
- package/dist/tools/assessRisk.d.ts +79 -79
- package/dist/tools/assessRisk.js +104 -104
- package/dist/tools/checkAllowance.d.ts +43 -43
- package/dist/tools/checkAllowance.js +56 -56
- package/dist/tools/checkTokenSecurity.d.ts +46 -46
- package/dist/tools/checkTokenSecurity.js +95 -95
- package/dist/tools/createAgentWallet.d.ts +28 -26
- package/dist/tools/createAgentWallet.d.ts.map +1 -1
- package/dist/tools/createAgentWallet.js +82 -58
- package/dist/tools/createAgentWallet.js.map +1 -1
- package/dist/tools/estimateGas.d.ts +79 -79
- package/dist/tools/estimateGas.js +124 -124
- package/dist/tools/executeSwap.d.ts +61 -61
- package/dist/tools/executeSwap.js +141 -141
- package/dist/tools/explainRisk.d.ts +29 -29
- package/dist/tools/explainRisk.js +32 -32
- package/dist/tools/getAgentWallet.d.ts +21 -21
- package/dist/tools/getAgentWallet.js +27 -27
- package/dist/tools/getAgentWalletBalance.d.ts +11 -11
- package/dist/tools/getAgentWalletBalance.js +70 -70
- package/dist/tools/getExecutionHistory.d.ts +49 -49
- package/dist/tools/getExecutionHistory.js +154 -154
- package/dist/tools/getGasPrice.d.ts +43 -43
- package/dist/tools/getGasPrice.js +59 -59
- package/dist/tools/getPoolInfo.d.ts +75 -75
- package/dist/tools/getPoolInfo.js +137 -137
- package/dist/tools/getTokenPrice.d.ts +113 -113
- package/dist/tools/getTokenPrice.js +117 -117
- package/dist/tools/getTransactionStatus.d.ts +43 -43
- package/dist/tools/getTransactionStatus.js +59 -59
- package/dist/tools/getWalletBalance.d.ts +68 -68
- package/dist/tools/getWalletBalance.js +87 -87
- package/dist/tools/publishRiskScore.d.ts +63 -63
- package/dist/tools/publishRiskScore.js +88 -88
- package/dist/tools/queryRiskRegistry.d.ts +38 -38
- package/dist/tools/queryRiskRegistry.js +55 -55
- package/dist/tools/safehandsPreflightCheck.d.ts +77 -77
- package/dist/tools/safehandsPreflightCheck.js +47 -47
- package/dist/tools/safehandsRiskReport.d.ts +81 -81
- package/dist/tools/safehandsRiskReport.js +28 -28
- package/dist/tools/safehandsSafeExecute.d.ts +20 -20
- package/dist/tools/safehandsSafeExecute.js +81 -81
- package/dist/tools/safehandsWalletHealth.d.ts +14 -14
- package/dist/tools/safehandsWalletHealth.js +103 -103
- package/dist/tools/safehandsX402Preflight.d.ts +26 -26
- package/dist/tools/safehandsX402Preflight.js +65 -65
- package/dist/tools/sendPayment.d.ts +57 -57
- package/dist/tools/sendPayment.js +117 -117
- package/dist/tools/simulateTransaction.d.ts +60 -60
- package/dist/tools/simulateTransaction.js +83 -83
- package/dist/tools/tokenRegistryStatus.d.ts +26 -26
- package/dist/tools/tokenRegistryStatus.js +96 -96
- package/dist/tools/x402PayAndFetch.d.ts +81 -81
- package/dist/tools/x402PayAndFetch.js +152 -152
- package/dist/x402Server.d.ts +1 -1
- package/dist/x402Server.js +300 -252
- package/dist/x402Server.js.map +1 -1
- package/package.json +6 -16
- package/examples/dashboard/index.html +0 -337
- package/examples/pharos-skill-engine/SKILL.safehands.md +0 -85
- package/examples/pharos-skill-engine/assets/safehands/example-actions.json +0 -49
- package/examples/pharos-skill-engine/assets/safehands/policy-defaults.json +0 -11
- package/examples/pharos-skill-engine/references/safehands.md +0 -345
- package/examples/scenario-hack.ts +0 -38
- package/skill/SKILL.md +0 -133
- package/skill/assets/safehands/example-actions.json +0 -49
- package/skill/assets/safehands/policy-defaults.json +0 -11
- package/skill/references/safehands.md +0 -345
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import type { Account } from "viem";
|
|
2
|
-
export type SignerMode = "none" | "env" | "managed-testnet" | "external-signer" | "x402-env";
|
|
3
|
-
export type SignerPurpose = "write" | "x402";
|
|
4
|
-
export interface SignerResult {
|
|
5
|
-
account: Account;
|
|
6
|
-
address: `0x${string}`;
|
|
7
|
-
mode: SignerMode;
|
|
8
|
-
}
|
|
9
|
-
export interface SignerFailure {
|
|
10
|
-
error: {
|
|
11
|
-
code: string;
|
|
12
|
-
message: string;
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
export type GetSignerResult = SignerResult | SignerFailure;
|
|
16
|
-
export declare function isSignerFailure(r: GetSignerResult): r is SignerFailure;
|
|
17
|
-
/**
|
|
18
|
-
* Get a signer for write/payment operations.
|
|
19
|
-
* Priority for x402: managed wallet > X402_SIGNER_PRIVATE_KEY > PRIVATE_KEY fallback.
|
|
20
|
-
* Priority for writes: managed wallet > PRIVATE_KEY fallback.
|
|
21
|
-
*/
|
|
22
|
-
export declare function getSigner(agentId?: string, options?: {
|
|
23
|
-
purpose?: SignerPurpose;
|
|
24
|
-
}): Promise<GetSignerResult>;
|
|
1
|
+
import type { Account } from "viem";
|
|
2
|
+
export type SignerMode = "none" | "env" | "managed-testnet" | "external-signer" | "x402-env";
|
|
3
|
+
export type SignerPurpose = "write" | "x402";
|
|
4
|
+
export interface SignerResult {
|
|
5
|
+
account: Account;
|
|
6
|
+
address: `0x${string}`;
|
|
7
|
+
mode: SignerMode;
|
|
8
|
+
}
|
|
9
|
+
export interface SignerFailure {
|
|
10
|
+
error: {
|
|
11
|
+
code: string;
|
|
12
|
+
message: string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export type GetSignerResult = SignerResult | SignerFailure;
|
|
16
|
+
export declare function isSignerFailure(r: GetSignerResult): r is SignerFailure;
|
|
17
|
+
/**
|
|
18
|
+
* Get a signer for write/payment operations.
|
|
19
|
+
* Priority for x402: managed wallet > X402_SIGNER_PRIVATE_KEY > PRIVATE_KEY fallback.
|
|
20
|
+
* Priority for writes: managed wallet > PRIVATE_KEY fallback.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getSigner(agentId?: string, options?: {
|
|
23
|
+
purpose?: SignerPurpose;
|
|
24
|
+
}): Promise<GetSignerResult>;
|
|
25
25
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/lib/signer/index.js
CHANGED
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
// ─── SignerProvider ─────────────────────────────────────────────────────
|
|
2
|
-
// Abstraction layer for signing. Tools never read process.env.PRIVATE_KEY
|
|
3
|
-
// directly — they request a signer through this provider.
|
|
4
|
-
// ────────────────────────────────────────────────────────────────────────
|
|
5
|
-
import { privateKeyToAccount } from "viem/accounts";
|
|
6
|
-
import { walletStore, decryptKey, getEffectiveEncryptionKey } from "../wallet/index.js";
|
|
7
|
-
export function isSignerFailure(r) {
|
|
8
|
-
return "error" in r;
|
|
9
|
-
}
|
|
10
|
-
function normalizePrivateKey(pk) {
|
|
11
|
-
return (pk.startsWith("0x") ? pk : `0x${pk}`);
|
|
12
|
-
}
|
|
13
|
-
function accountFromEnvKey(envName, mode) {
|
|
14
|
-
const pk = process.env[envName];
|
|
15
|
-
if (!pk)
|
|
16
|
-
return null;
|
|
17
|
-
try {
|
|
18
|
-
const account = privateKeyToAccount(normalizePrivateKey(pk));
|
|
19
|
-
return { account, address: account.address, mode };
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
return {
|
|
23
|
-
error: {
|
|
24
|
-
code: "INVALID_PRIVATE_KEY",
|
|
25
|
-
message: `${envName} is not a valid private key.`,
|
|
26
|
-
},
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
async function accountFromManagedWallet(agentId) {
|
|
31
|
-
const walletMode = process.env.WALLET_MODE || "none";
|
|
32
|
-
if (walletMode !== "managed-testnet" || !agentId)
|
|
33
|
-
return null;
|
|
34
|
-
if (process.env.WALLET_STORE_PATH && !process.env.WALLET_ENCRYPTION_KEY) {
|
|
35
|
-
return {
|
|
36
|
-
error: {
|
|
37
|
-
code: "WALLET_ENCRYPTION_KEY_REQUIRED",
|
|
38
|
-
message: "WALLET_ENCRYPTION_KEY is required when WALLET_STORE_PATH is used for persistent managed-testnet wallets.",
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
const stored = await walletStore.get(agentId);
|
|
43
|
-
if (!stored)
|
|
44
|
-
return null;
|
|
45
|
-
try {
|
|
46
|
-
const privateKey = decryptKey(stored.encryptedKey, getEffectiveEncryptionKey());
|
|
47
|
-
const account = privateKeyToAccount(normalizePrivateKey(privateKey));
|
|
48
|
-
return { account, address: account.address, mode: "managed-testnet" };
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
return {
|
|
52
|
-
error: {
|
|
53
|
-
code: "NO_SIGNER_AVAILABLE",
|
|
54
|
-
message: "Managed wallet key could not be decrypted for signing. Check WALLET_ENCRYPTION_KEY matches the key used when the wallet was created.",
|
|
55
|
-
},
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Get a signer for write/payment operations.
|
|
61
|
-
* Priority for x402: managed wallet > X402_SIGNER_PRIVATE_KEY > PRIVATE_KEY fallback.
|
|
62
|
-
* Priority for writes: managed wallet > PRIVATE_KEY fallback.
|
|
63
|
-
*/
|
|
64
|
-
export async function getSigner(agentId, options = {}) {
|
|
65
|
-
const purpose = options.purpose || "write";
|
|
66
|
-
const managed = await accountFromManagedWallet(agentId);
|
|
67
|
-
if (managed)
|
|
68
|
-
return managed;
|
|
69
|
-
if (purpose === "x402") {
|
|
70
|
-
const x402Env = accountFromEnvKey("X402_SIGNER_PRIVATE_KEY", "x402-env");
|
|
71
|
-
if (x402Env)
|
|
72
|
-
return x402Env;
|
|
73
|
-
}
|
|
74
|
-
const walletMode = process.env.WALLET_MODE || "none";
|
|
75
|
-
if (walletMode === "env" || process.env.PRIVATE_KEY) {
|
|
76
|
-
const env = accountFromEnvKey("PRIVATE_KEY", "env");
|
|
77
|
-
if (env)
|
|
78
|
-
return env;
|
|
79
|
-
}
|
|
80
|
-
return {
|
|
81
|
-
error: {
|
|
82
|
-
code: "NO_SIGNER_AVAILABLE",
|
|
83
|
-
message: purpose === "x402"
|
|
84
|
-
? "No x402 signer available. Use WALLET_MODE=managed-testnet with agentId, X402_SIGNER_PRIVATE_KEY, or PRIVATE_KEY as testnet developer fallback."
|
|
85
|
-
: "No signer available. Use WALLET_MODE=managed-testnet with agentId, or WALLET_MODE=env with PRIVATE_KEY for testnet developer mode.",
|
|
86
|
-
},
|
|
87
|
-
};
|
|
88
|
-
}
|
|
1
|
+
// ─── SignerProvider ─────────────────────────────────────────────────────
|
|
2
|
+
// Abstraction layer for signing. Tools never read process.env.PRIVATE_KEY
|
|
3
|
+
// directly — they request a signer through this provider.
|
|
4
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
5
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
6
|
+
import { walletStore, decryptKey, getEffectiveEncryptionKey } from "../wallet/index.js";
|
|
7
|
+
export function isSignerFailure(r) {
|
|
8
|
+
return "error" in r;
|
|
9
|
+
}
|
|
10
|
+
function normalizePrivateKey(pk) {
|
|
11
|
+
return (pk.startsWith("0x") ? pk : `0x${pk}`);
|
|
12
|
+
}
|
|
13
|
+
function accountFromEnvKey(envName, mode) {
|
|
14
|
+
const pk = process.env[envName];
|
|
15
|
+
if (!pk)
|
|
16
|
+
return null;
|
|
17
|
+
try {
|
|
18
|
+
const account = privateKeyToAccount(normalizePrivateKey(pk));
|
|
19
|
+
return { account, address: account.address, mode };
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return {
|
|
23
|
+
error: {
|
|
24
|
+
code: "INVALID_PRIVATE_KEY",
|
|
25
|
+
message: `${envName} is not a valid private key.`,
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function accountFromManagedWallet(agentId) {
|
|
31
|
+
const walletMode = process.env.WALLET_MODE || "none";
|
|
32
|
+
if (walletMode !== "managed-testnet" || !agentId)
|
|
33
|
+
return null;
|
|
34
|
+
if (process.env.WALLET_STORE_PATH && !process.env.WALLET_ENCRYPTION_KEY) {
|
|
35
|
+
return {
|
|
36
|
+
error: {
|
|
37
|
+
code: "WALLET_ENCRYPTION_KEY_REQUIRED",
|
|
38
|
+
message: "WALLET_ENCRYPTION_KEY is required when WALLET_STORE_PATH is used for persistent managed-testnet wallets.",
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const stored = await walletStore.get(agentId);
|
|
43
|
+
if (!stored)
|
|
44
|
+
return null;
|
|
45
|
+
try {
|
|
46
|
+
const privateKey = decryptKey(stored.encryptedKey, getEffectiveEncryptionKey());
|
|
47
|
+
const account = privateKeyToAccount(normalizePrivateKey(privateKey));
|
|
48
|
+
return { account, address: account.address, mode: "managed-testnet" };
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return {
|
|
52
|
+
error: {
|
|
53
|
+
code: "NO_SIGNER_AVAILABLE",
|
|
54
|
+
message: "Managed wallet key could not be decrypted for signing. Check WALLET_ENCRYPTION_KEY matches the key used when the wallet was created.",
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get a signer for write/payment operations.
|
|
61
|
+
* Priority for x402: managed wallet > X402_SIGNER_PRIVATE_KEY > PRIVATE_KEY fallback.
|
|
62
|
+
* Priority for writes: managed wallet > PRIVATE_KEY fallback.
|
|
63
|
+
*/
|
|
64
|
+
export async function getSigner(agentId, options = {}) {
|
|
65
|
+
const purpose = options.purpose || "write";
|
|
66
|
+
const managed = await accountFromManagedWallet(agentId);
|
|
67
|
+
if (managed)
|
|
68
|
+
return managed;
|
|
69
|
+
if (purpose === "x402") {
|
|
70
|
+
const x402Env = accountFromEnvKey("X402_SIGNER_PRIVATE_KEY", "x402-env");
|
|
71
|
+
if (x402Env)
|
|
72
|
+
return x402Env;
|
|
73
|
+
}
|
|
74
|
+
const walletMode = process.env.WALLET_MODE || "none";
|
|
75
|
+
if (walletMode === "env" || process.env.PRIVATE_KEY) {
|
|
76
|
+
const env = accountFromEnvKey("PRIVATE_KEY", "env");
|
|
77
|
+
if (env)
|
|
78
|
+
return env;
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
error: {
|
|
82
|
+
code: "NO_SIGNER_AVAILABLE",
|
|
83
|
+
message: purpose === "x402"
|
|
84
|
+
? "No x402 signer available. Use WALLET_MODE=managed-testnet with agentId, X402_SIGNER_PRIVATE_KEY, or PRIVATE_KEY as testnet developer fallback."
|
|
85
|
+
: "No signer available. Use WALLET_MODE=managed-testnet with agentId, or WALLET_MODE=env with PRIVATE_KEY for testnet developer mode.",
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
89
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {};
|
|
1
|
+
export {};
|
|
2
2
|
//# sourceMappingURL=testDodoLive.d.ts.map
|
package/dist/lib/testDodoLive.js
CHANGED
|
@@ -1,105 +1,105 @@
|
|
|
1
|
-
// ─── Live DODO/FaroSwap Verification ───────────────────────────────────
|
|
2
|
-
// Tests DODO API reachability if DODO_API_KEY is present.
|
|
3
|
-
// Skips cleanly if API key is missing.
|
|
4
|
-
// ────────────────────────────────────────────────────────────────────────
|
|
5
|
-
import { DODO_API_BASE, DODO_API_KEY } from "./constants.js";
|
|
6
|
-
import { getDodoRoute } from "./dodoApi.js";
|
|
7
|
-
const results = [];
|
|
8
|
-
async function main() {
|
|
9
|
-
console.log("═══════════════════════════════════════════════════════════");
|
|
10
|
-
console.log(" SafeHands — DODO/FaroSwap Live Verification");
|
|
11
|
-
console.log("═══════════════════════════════════════════════════════════");
|
|
12
|
-
console.log(` DODO API Base: ${DODO_API_BASE}`);
|
|
13
|
-
const maskedKey = DODO_API_KEY ? `pub_****${DODO_API_KEY.slice(-4)}` : "NOT SET";
|
|
14
|
-
console.log(` API Key: ${maskedKey}`);
|
|
15
|
-
console.log("");
|
|
16
|
-
if (!DODO_API_KEY) {
|
|
17
|
-
console.log(" ⏭️ SKIPPED_MISSING_DODO_API_KEY — set DODO_API_KEY to enable live DODO checks.");
|
|
18
|
-
results.push({
|
|
19
|
-
name: "dodo_api_route_check",
|
|
20
|
-
status: "SKIPPED_MISSING_DODO_API_KEY",
|
|
21
|
-
detail: "DODO_API_KEY env var not set. This is expected for CI/demo environments.",
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
else {
|
|
25
|
-
// Try a read-only route quote
|
|
26
|
-
try {
|
|
27
|
-
const quote = await getDodoRoute({
|
|
28
|
-
fromToken: "PHRS",
|
|
29
|
-
toToken: "USDC",
|
|
30
|
-
amountHuman: "0.000001",
|
|
31
|
-
walletAddress: "0x0000000000000000000000000000000000000001",
|
|
32
|
-
});
|
|
33
|
-
if (quote.routeAvailable && parseFloat(quote.amountOut) > 0) {
|
|
34
|
-
results.push({
|
|
35
|
-
name: "dodo_api_route_check",
|
|
36
|
-
status: "PASS",
|
|
37
|
-
detail: `Route found: 0.000001 PHRS → ${quote.amountOut} USDC`,
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
results.push({
|
|
42
|
-
name: "dodo_api_route_check",
|
|
43
|
-
status: "NO_ROUTE_AVAILABLE",
|
|
44
|
-
detail: `API reachable. No route available (may be no liquidity).`,
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
catch (err) {
|
|
49
|
-
const msg = err.message || String(err);
|
|
50
|
-
if (msg.includes("DODO_API_AUTH_REQUIRED")) {
|
|
51
|
-
results.push({
|
|
52
|
-
name: "dodo_api_route_check",
|
|
53
|
-
status: "DODO_API_AUTH_REQUIRED",
|
|
54
|
-
detail: msg,
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
else if (msg.includes("DODO_API_UNAVAILABLE") || msg.includes("DODO_API_RATE_LIMITED") || msg.includes("fetch failed") || msg.includes("network timeout")) {
|
|
58
|
-
results.push({
|
|
59
|
-
name: "dodo_api_route_check",
|
|
60
|
-
status: "DODO_API_UNAVAILABLE",
|
|
61
|
-
detail: `Network/timeout/rate-limited: ${msg}`,
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
results.push({
|
|
66
|
-
name: "dodo_api_route_check",
|
|
67
|
-
status: "FAIL",
|
|
68
|
-
detail: msg,
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
// Router address verification status
|
|
74
|
-
results.push({
|
|
75
|
-
name: "dodo_approve_address_status",
|
|
76
|
-
status: "PASS",
|
|
77
|
-
detail: "0x4Cf317b8918FbE8A890c01eDAb7d548555Ac2cE9 — PROJECT_CONFIGURED (not verified by official FaroSwap/DODO docs)",
|
|
78
|
-
});
|
|
79
|
-
results.push({
|
|
80
|
-
name: "dodo_route_proxy_status",
|
|
81
|
-
status: "PASS",
|
|
82
|
-
detail: "0x819829e5CF6e19F9fED92F6b4CC1edF45a2cC4A2 — PROJECT_CONFIGURED (not verified by official FaroSwap/DODO docs)",
|
|
83
|
-
});
|
|
84
|
-
// Print results
|
|
85
|
-
console.log(`\n${"#".padStart(2)} ${"Status".padEnd(32)} ${"Check".padEnd(34)} Detail`);
|
|
86
|
-
console.log("─".repeat(110));
|
|
87
|
-
for (const [i, r] of results.entries()) {
|
|
88
|
-
const icon = r.status === "PASS" ? "✅" : r.status.startsWith("SKIPPED") ? "⏭️" : "❌";
|
|
89
|
-
console.log(`${String(i + 1).padStart(2)} ${icon} ${r.status.padEnd(29)} ${r.name.padEnd(34)} ${r.detail.slice(0, 80)}`);
|
|
90
|
-
}
|
|
91
|
-
const failed = results.filter(r => r.status === "FAIL");
|
|
92
|
-
console.log("─".repeat(110));
|
|
93
|
-
console.log(`${results.filter(r => r.status === "PASS").length}/${results.length} checks passed, ${results.filter(r => r.status.startsWith("SKIPPED")).length} skipped.`);
|
|
94
|
-
if (failed.length > 0) {
|
|
95
|
-
console.error("\nFailed:");
|
|
96
|
-
for (const f of failed)
|
|
97
|
-
console.error(` - ${f.name}: ${f.detail}`);
|
|
98
|
-
process.exit(1);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
main().catch((err) => {
|
|
102
|
-
console.error("❌ Unexpected error:", err);
|
|
103
|
-
process.exit(1);
|
|
104
|
-
});
|
|
1
|
+
// ─── Live DODO/FaroSwap Verification ───────────────────────────────────
|
|
2
|
+
// Tests DODO API reachability if DODO_API_KEY is present.
|
|
3
|
+
// Skips cleanly if API key is missing.
|
|
4
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
5
|
+
import { DODO_API_BASE, DODO_API_KEY } from "./constants.js";
|
|
6
|
+
import { getDodoRoute } from "./dodoApi.js";
|
|
7
|
+
const results = [];
|
|
8
|
+
async function main() {
|
|
9
|
+
console.log("═══════════════════════════════════════════════════════════");
|
|
10
|
+
console.log(" SafeHands — DODO/FaroSwap Live Verification");
|
|
11
|
+
console.log("═══════════════════════════════════════════════════════════");
|
|
12
|
+
console.log(` DODO API Base: ${DODO_API_BASE}`);
|
|
13
|
+
const maskedKey = DODO_API_KEY ? `pub_****${DODO_API_KEY.slice(-4)}` : "NOT SET";
|
|
14
|
+
console.log(` API Key: ${maskedKey}`);
|
|
15
|
+
console.log("");
|
|
16
|
+
if (!DODO_API_KEY) {
|
|
17
|
+
console.log(" ⏭️ SKIPPED_MISSING_DODO_API_KEY — set DODO_API_KEY to enable live DODO checks.");
|
|
18
|
+
results.push({
|
|
19
|
+
name: "dodo_api_route_check",
|
|
20
|
+
status: "SKIPPED_MISSING_DODO_API_KEY",
|
|
21
|
+
detail: "DODO_API_KEY env var not set. This is expected for CI/demo environments.",
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
// Try a read-only route quote
|
|
26
|
+
try {
|
|
27
|
+
const quote = await getDodoRoute({
|
|
28
|
+
fromToken: "PHRS",
|
|
29
|
+
toToken: "USDC",
|
|
30
|
+
amountHuman: "0.000001",
|
|
31
|
+
walletAddress: "0x0000000000000000000000000000000000000001",
|
|
32
|
+
});
|
|
33
|
+
if (quote.routeAvailable && parseFloat(quote.amountOut) > 0) {
|
|
34
|
+
results.push({
|
|
35
|
+
name: "dodo_api_route_check",
|
|
36
|
+
status: "PASS",
|
|
37
|
+
detail: `Route found: 0.000001 PHRS → ${quote.amountOut} USDC`,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
results.push({
|
|
42
|
+
name: "dodo_api_route_check",
|
|
43
|
+
status: "NO_ROUTE_AVAILABLE",
|
|
44
|
+
detail: `API reachable. No route available (may be no liquidity).`,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
const msg = err.message || String(err);
|
|
50
|
+
if (msg.includes("DODO_API_AUTH_REQUIRED")) {
|
|
51
|
+
results.push({
|
|
52
|
+
name: "dodo_api_route_check",
|
|
53
|
+
status: "DODO_API_AUTH_REQUIRED",
|
|
54
|
+
detail: msg,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
else if (msg.includes("DODO_API_UNAVAILABLE") || msg.includes("DODO_API_RATE_LIMITED") || msg.includes("fetch failed") || msg.includes("network timeout")) {
|
|
58
|
+
results.push({
|
|
59
|
+
name: "dodo_api_route_check",
|
|
60
|
+
status: "DODO_API_UNAVAILABLE",
|
|
61
|
+
detail: `Network/timeout/rate-limited: ${msg}`,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
results.push({
|
|
66
|
+
name: "dodo_api_route_check",
|
|
67
|
+
status: "FAIL",
|
|
68
|
+
detail: msg,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Router address verification status
|
|
74
|
+
results.push({
|
|
75
|
+
name: "dodo_approve_address_status",
|
|
76
|
+
status: "PASS",
|
|
77
|
+
detail: "0x4Cf317b8918FbE8A890c01eDAb7d548555Ac2cE9 — PROJECT_CONFIGURED (not verified by official FaroSwap/DODO docs)",
|
|
78
|
+
});
|
|
79
|
+
results.push({
|
|
80
|
+
name: "dodo_route_proxy_status",
|
|
81
|
+
status: "PASS",
|
|
82
|
+
detail: "0x819829e5CF6e19F9fED92F6b4CC1edF45a2cC4A2 — PROJECT_CONFIGURED (not verified by official FaroSwap/DODO docs)",
|
|
83
|
+
});
|
|
84
|
+
// Print results
|
|
85
|
+
console.log(`\n${"#".padStart(2)} ${"Status".padEnd(32)} ${"Check".padEnd(34)} Detail`);
|
|
86
|
+
console.log("─".repeat(110));
|
|
87
|
+
for (const [i, r] of results.entries()) {
|
|
88
|
+
const icon = r.status === "PASS" ? "✅" : r.status.startsWith("SKIPPED") ? "⏭️" : "❌";
|
|
89
|
+
console.log(`${String(i + 1).padStart(2)} ${icon} ${r.status.padEnd(29)} ${r.name.padEnd(34)} ${r.detail.slice(0, 80)}`);
|
|
90
|
+
}
|
|
91
|
+
const failed = results.filter(r => r.status === "FAIL");
|
|
92
|
+
console.log("─".repeat(110));
|
|
93
|
+
console.log(`${results.filter(r => r.status === "PASS").length}/${results.length} checks passed, ${results.filter(r => r.status.startsWith("SKIPPED")).length} skipped.`);
|
|
94
|
+
if (failed.length > 0) {
|
|
95
|
+
console.error("\nFailed:");
|
|
96
|
+
for (const f of failed)
|
|
97
|
+
console.error(` - ${f.name}: ${f.detail}`);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
main().catch((err) => {
|
|
102
|
+
console.error("❌ Unexpected error:", err);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
});
|
|
105
105
|
//# sourceMappingURL=testDodoLive.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {};
|
|
1
|
+
export {};
|
|
2
2
|
//# sourceMappingURL=testLiveSafehands.d.ts.map
|
|
@@ -1,93 +1,93 @@
|
|
|
1
|
-
// ─── Live SafeHands CLI Verification ───────────────────────────────────
|
|
2
|
-
// Runs real CLI safety checks without broadcasting transactions.
|
|
3
|
-
// All checks are read-only or dry-run preflight checks.
|
|
4
|
-
// ────────────────────────────────────────────────────────────────────────
|
|
5
|
-
import { spawnSync } from "node:child_process";
|
|
6
|
-
import { join } from "node:path";
|
|
7
|
-
import { existsSync } from "node:fs";
|
|
8
|
-
const results = [];
|
|
9
|
-
function runCli(args) {
|
|
10
|
-
const entry = join(process.cwd(), "dist", "index.js");
|
|
11
|
-
if (!existsSync(entry)) {
|
|
12
|
-
console.error("❌ dist/index.js not found. Run: npm run build");
|
|
13
|
-
process.exit(1);
|
|
14
|
-
}
|
|
15
|
-
const result = spawnSync(process.execPath, [entry, ...args], {
|
|
16
|
-
cwd: process.cwd(),
|
|
17
|
-
env: { ...process.env, WALLET_MODE: "none", WRITE_TOOLS_ENABLED: "false", PRIVATE_KEY: "" },
|
|
18
|
-
encoding: "utf8",
|
|
19
|
-
timeout: 30_000,
|
|
20
|
-
});
|
|
21
|
-
return { exitCode: result.status ?? 1, stdout: result.stdout || "", stderr: result.stderr || "" };
|
|
22
|
-
}
|
|
23
|
-
function parseJson(stdout) {
|
|
24
|
-
try {
|
|
25
|
-
return JSON.parse(stdout.trim());
|
|
26
|
-
}
|
|
27
|
-
catch {
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
function check(name, args, expected, validator) {
|
|
32
|
-
const { stdout } = runCli(args);
|
|
33
|
-
const json = parseJson(stdout);
|
|
34
|
-
const passed = json !== null && validator(json);
|
|
35
|
-
const actual = json !== null
|
|
36
|
-
? (json.success ? `success: ${JSON.stringify(json.data).slice(0, 120)}` : `error: ${json.error?.code}`)
|
|
37
|
-
: `invalid JSON: ${stdout.slice(0, 100)}`;
|
|
38
|
-
results.push({ name, status: passed ? "PASS" : "FAIL", expected, actual });
|
|
39
|
-
}
|
|
40
|
-
console.log("═══════════════════════════════════════════════════════════");
|
|
41
|
-
console.log(" SafeHands — Live CLI Verification (read-only)");
|
|
42
|
-
console.log("═══════════════════════════════════════════════════════════");
|
|
43
|
-
console.log("");
|
|
44
|
-
// 1. Wallet health — no wallet configured
|
|
45
|
-
check("wallet_health_no_wallet", ["skill", "safehands_wallet_health", "--input-json", "{}"], "valid JSON with status NOT_READY or DEGRADED", (json) => json.success && ["NOT_READY", "DEGRADED"].includes(json.data?.status));
|
|
46
|
-
// 2. Token registry — Pharos Skill Engine USDC (DOCS_VERIFIED_FROM_PHAROS_SKILL_ENGINE)
|
|
47
|
-
check("token_registry_skill_engine_usdc", ["skill", "token_registry_status", "--input-json", JSON.stringify({ tokenAddress: "0xE0BE08c77f415F577A1B3A9aD7a1Df1479564ec8" })], "SKILL_ENGINE_CANONICAL_TOKEN with DOCS_VERIFIED_FROM_PHAROS_SKILL_ENGINE", (json) => json.success && json.data?.status === "SKILL_ENGINE_CANONICAL_TOKEN" && json.data?.verificationStatus === "DOCS_VERIFIED_FROM_PHAROS_SKILL_ENGINE");
|
|
48
|
-
// 3. Token registry — Circle USDC (ALTERNATE_SOURCE_TOKEN)
|
|
49
|
-
check("token_registry_circle_usdc", ["skill", "token_registry_status", "--input-json", JSON.stringify({ tokenAddress: "0xcfC8330f4BCAB529c625D12781b1C19466A9Fc8B" })], "ALTERNATE_SOURCE_TOKEN with CIRCLE_REFERENCED_USDC", (json) => json.success && json.data?.status === "ALTERNATE_SOURCE_TOKEN" && json.data?.verificationStatus === "CIRCLE_REFERENCED_USDC");
|
|
50
|
-
// 4. Token registry — USDT (DOCS_VERIFIED)
|
|
51
|
-
check("token_registry_usdt_docs_verified", ["skill", "token_registry_status", "--input-json", JSON.stringify({ tokenAddress: "0xE7E84B8B4f39C507499c40B4ac199B050e2882d5" })], "CANONICAL_TESTNET_TOKEN with DOCS_VERIFIED", (json) => json.success && json.data?.status === "CANONICAL_TESTNET_TOKEN" && json.data?.verificationStatus === "DOCS_VERIFIED");
|
|
52
|
-
// 5. Preflight — unlimited approval → BLOCK
|
|
53
|
-
check("preflight_block_unlimited_approval", ["skill", "safehands_preflight_check", "--input-json", JSON.stringify({
|
|
54
|
-
actionType: "approve_token",
|
|
55
|
-
chainId: 688689,
|
|
56
|
-
tokenAddress: "0xE0BE08c77f415F577A1B3A9aD7a1Df1479564ec8",
|
|
57
|
-
targetAddress: "0x0000000000000000000000000000000000000001",
|
|
58
|
-
amount: "unlimited",
|
|
59
|
-
approvalAmount: "max",
|
|
60
|
-
})], "decision=BLOCK", (json) => json.success && json.data?.decision === "BLOCK");
|
|
61
|
-
// 6. Preflight — mainnet action → BLOCK
|
|
62
|
-
check("preflight_block_mainnet", ["skill", "safehands_preflight_check", "--input-json", JSON.stringify({
|
|
63
|
-
actionType: "send_payment",
|
|
64
|
-
chainId: 1,
|
|
65
|
-
isMainnet: true,
|
|
66
|
-
targetAddress: "0x0000000000000000000000000000000000000001",
|
|
67
|
-
amount: "0.001",
|
|
68
|
-
})], "decision=BLOCK", (json) => json.success && json.data?.decision === "BLOCK");
|
|
69
|
-
// 7. Preflight — safe testnet action → ALLOW or WARN (not BLOCK)
|
|
70
|
-
check("preflight_allow_testnet", ["skill", "safehands_preflight_check", "--input-json", JSON.stringify({
|
|
71
|
-
actionType: "send_payment",
|
|
72
|
-
chainId: 688689,
|
|
73
|
-
isMainnet: false,
|
|
74
|
-
amount: "0.001",
|
|
75
|
-
recipient: "0x000000000000000000000000000000000000dEaD",
|
|
76
|
-
})], "decision=ALLOW or WARN (not BLOCK)", (json) => json.success && json.data?.decision !== "BLOCK");
|
|
77
|
-
// Print results
|
|
78
|
-
console.log(`\n${"#".padStart(2)} ${"Status".padEnd(8)} ${"Check".padEnd(42)} Expected`);
|
|
79
|
-
console.log("─".repeat(100));
|
|
80
|
-
for (const [i, r] of results.entries()) {
|
|
81
|
-
const icon = r.status === "PASS" ? "✅" : "❌";
|
|
82
|
-
console.log(`${String(i + 1).padStart(2)} ${icon} ${r.status.padEnd(5)} ${r.name.padEnd(42)} ${r.expected}`);
|
|
83
|
-
}
|
|
84
|
-
const failed = results.filter(r => r.status === "FAIL");
|
|
85
|
-
console.log("─".repeat(100));
|
|
86
|
-
console.log(`${results.length - failed.length}/${results.length} live CLI checks passed.`);
|
|
87
|
-
if (failed.length > 0) {
|
|
88
|
-
console.error("\nFailed checks:");
|
|
89
|
-
for (const f of failed)
|
|
90
|
-
console.error(` - ${f.name}: got ${f.actual}`);
|
|
91
|
-
process.exit(1);
|
|
92
|
-
}
|
|
1
|
+
// ─── Live SafeHands CLI Verification ───────────────────────────────────
|
|
2
|
+
// Runs real CLI safety checks without broadcasting transactions.
|
|
3
|
+
// All checks are read-only or dry-run preflight checks.
|
|
4
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
const results = [];
|
|
9
|
+
function runCli(args) {
|
|
10
|
+
const entry = join(process.cwd(), "dist", "index.js");
|
|
11
|
+
if (!existsSync(entry)) {
|
|
12
|
+
console.error("❌ dist/index.js not found. Run: npm run build");
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
const result = spawnSync(process.execPath, [entry, ...args], {
|
|
16
|
+
cwd: process.cwd(),
|
|
17
|
+
env: { ...process.env, WALLET_MODE: "none", WRITE_TOOLS_ENABLED: "false", PRIVATE_KEY: "" },
|
|
18
|
+
encoding: "utf8",
|
|
19
|
+
timeout: 30_000,
|
|
20
|
+
});
|
|
21
|
+
return { exitCode: result.status ?? 1, stdout: result.stdout || "", stderr: result.stderr || "" };
|
|
22
|
+
}
|
|
23
|
+
function parseJson(stdout) {
|
|
24
|
+
try {
|
|
25
|
+
return JSON.parse(stdout.trim());
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function check(name, args, expected, validator) {
|
|
32
|
+
const { stdout } = runCli(args);
|
|
33
|
+
const json = parseJson(stdout);
|
|
34
|
+
const passed = json !== null && validator(json);
|
|
35
|
+
const actual = json !== null
|
|
36
|
+
? (json.success ? `success: ${JSON.stringify(json.data).slice(0, 120)}` : `error: ${json.error?.code}`)
|
|
37
|
+
: `invalid JSON: ${stdout.slice(0, 100)}`;
|
|
38
|
+
results.push({ name, status: passed ? "PASS" : "FAIL", expected, actual });
|
|
39
|
+
}
|
|
40
|
+
console.log("═══════════════════════════════════════════════════════════");
|
|
41
|
+
console.log(" SafeHands — Live CLI Verification (read-only)");
|
|
42
|
+
console.log("═══════════════════════════════════════════════════════════");
|
|
43
|
+
console.log("");
|
|
44
|
+
// 1. Wallet health — no wallet configured
|
|
45
|
+
check("wallet_health_no_wallet", ["skill", "safehands_wallet_health", "--input-json", "{}"], "valid JSON with status NOT_READY or DEGRADED", (json) => json.success && ["NOT_READY", "DEGRADED"].includes(json.data?.status));
|
|
46
|
+
// 2. Token registry — Pharos Skill Engine USDC (DOCS_VERIFIED_FROM_PHAROS_SKILL_ENGINE)
|
|
47
|
+
check("token_registry_skill_engine_usdc", ["skill", "token_registry_status", "--input-json", JSON.stringify({ tokenAddress: "0xE0BE08c77f415F577A1B3A9aD7a1Df1479564ec8" })], "SKILL_ENGINE_CANONICAL_TOKEN with DOCS_VERIFIED_FROM_PHAROS_SKILL_ENGINE", (json) => json.success && json.data?.status === "SKILL_ENGINE_CANONICAL_TOKEN" && json.data?.verificationStatus === "DOCS_VERIFIED_FROM_PHAROS_SKILL_ENGINE");
|
|
48
|
+
// 3. Token registry — Circle USDC (ALTERNATE_SOURCE_TOKEN)
|
|
49
|
+
check("token_registry_circle_usdc", ["skill", "token_registry_status", "--input-json", JSON.stringify({ tokenAddress: "0xcfC8330f4BCAB529c625D12781b1C19466A9Fc8B" })], "ALTERNATE_SOURCE_TOKEN with CIRCLE_REFERENCED_USDC", (json) => json.success && json.data?.status === "ALTERNATE_SOURCE_TOKEN" && json.data?.verificationStatus === "CIRCLE_REFERENCED_USDC");
|
|
50
|
+
// 4. Token registry — USDT (DOCS_VERIFIED)
|
|
51
|
+
check("token_registry_usdt_docs_verified", ["skill", "token_registry_status", "--input-json", JSON.stringify({ tokenAddress: "0xE7E84B8B4f39C507499c40B4ac199B050e2882d5" })], "CANONICAL_TESTNET_TOKEN with DOCS_VERIFIED", (json) => json.success && json.data?.status === "CANONICAL_TESTNET_TOKEN" && json.data?.verificationStatus === "DOCS_VERIFIED");
|
|
52
|
+
// 5. Preflight — unlimited approval → BLOCK
|
|
53
|
+
check("preflight_block_unlimited_approval", ["skill", "safehands_preflight_check", "--input-json", JSON.stringify({
|
|
54
|
+
actionType: "approve_token",
|
|
55
|
+
chainId: 688689,
|
|
56
|
+
tokenAddress: "0xE0BE08c77f415F577A1B3A9aD7a1Df1479564ec8",
|
|
57
|
+
targetAddress: "0x0000000000000000000000000000000000000001",
|
|
58
|
+
amount: "unlimited",
|
|
59
|
+
approvalAmount: "max",
|
|
60
|
+
})], "decision=BLOCK", (json) => json.success && json.data?.decision === "BLOCK");
|
|
61
|
+
// 6. Preflight — mainnet action → BLOCK
|
|
62
|
+
check("preflight_block_mainnet", ["skill", "safehands_preflight_check", "--input-json", JSON.stringify({
|
|
63
|
+
actionType: "send_payment",
|
|
64
|
+
chainId: 1,
|
|
65
|
+
isMainnet: true,
|
|
66
|
+
targetAddress: "0x0000000000000000000000000000000000000001",
|
|
67
|
+
amount: "0.001",
|
|
68
|
+
})], "decision=BLOCK", (json) => json.success && json.data?.decision === "BLOCK");
|
|
69
|
+
// 7. Preflight — safe testnet action → ALLOW or WARN (not BLOCK)
|
|
70
|
+
check("preflight_allow_testnet", ["skill", "safehands_preflight_check", "--input-json", JSON.stringify({
|
|
71
|
+
actionType: "send_payment",
|
|
72
|
+
chainId: 688689,
|
|
73
|
+
isMainnet: false,
|
|
74
|
+
amount: "0.001",
|
|
75
|
+
recipient: "0x000000000000000000000000000000000000dEaD",
|
|
76
|
+
})], "decision=ALLOW or WARN (not BLOCK)", (json) => json.success && json.data?.decision !== "BLOCK");
|
|
77
|
+
// Print results
|
|
78
|
+
console.log(`\n${"#".padStart(2)} ${"Status".padEnd(8)} ${"Check".padEnd(42)} Expected`);
|
|
79
|
+
console.log("─".repeat(100));
|
|
80
|
+
for (const [i, r] of results.entries()) {
|
|
81
|
+
const icon = r.status === "PASS" ? "✅" : "❌";
|
|
82
|
+
console.log(`${String(i + 1).padStart(2)} ${icon} ${r.status.padEnd(5)} ${r.name.padEnd(42)} ${r.expected}`);
|
|
83
|
+
}
|
|
84
|
+
const failed = results.filter(r => r.status === "FAIL");
|
|
85
|
+
console.log("─".repeat(100));
|
|
86
|
+
console.log(`${results.length - failed.length}/${results.length} live CLI checks passed.`);
|
|
87
|
+
if (failed.length > 0) {
|
|
88
|
+
console.error("\nFailed checks:");
|
|
89
|
+
for (const f of failed)
|
|
90
|
+
console.error(` - ${f.name}: got ${f.actual}`);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
93
|
//# sourceMappingURL=testLiveSafehands.js.map
|