@elizaos/plugin-squid-router
Version:
ElizaOS plugin for cross-chain token swaps using Squid Router
435 lines (373 loc) • 12.4 kB
text/typescript
import { describe, it, expect, beforeEach, mock } from "bun:test";
import {
convertToWei,
isXChainSwapContent,
validateSquidRouterConfig,
squidRouterEnvSchema,
} from "../helpers/utils";
import { createMockRuntime } from "./test-utils";
import { z } from "zod";
describe("convertToWei", () => {
it("should convert whole numbers correctly", () => {
const token = {
decimals: 18,
isNative: true,
symbol: "ETH",
address: "0x",
enabled: true,
};
const result = convertToWei("1", token);
expect(result).toBe("1000000000000000000");
});
it("should convert decimal numbers correctly", () => {
const token = {
decimals: 6,
isNative: false,
symbol: "USDC",
address: "0x",
enabled: true,
};
const result = convertToWei("100.5", token);
expect(result).toBe("100500000");
});
it("should handle numbers as input", () => {
const token = {
decimals: 18,
isNative: true,
symbol: "ETH",
address: "0x",
enabled: true,
};
const result = convertToWei(0.5, token);
expect(result).toBe("500000000000000000");
});
it("should handle zero decimals", () => {
const token = {
decimals: 0,
isNative: false,
symbol: "TEST",
address: "0x",
enabled: true,
};
const result = convertToWei("100", token);
expect(result).toBe("100");
});
it("should throw error for invalid decimals", () => {
const token = {
decimals: -1,
isNative: false,
symbol: "TEST",
address: "0x",
enabled: true,
};
expect(() => convertToWei("100", token)).toThrow("Invalid decimals value");
});
it("should throw error for decimals > 255", () => {
const token = {
decimals: 256,
isNative: false,
symbol: "TEST",
address: "0x",
enabled: true,
};
expect(() => convertToWei("100", token)).toThrow("Invalid decimals value");
});
it("should throw error for invalid amount", () => {
const token = {
decimals: 18,
isNative: false,
symbol: "TEST",
address: "0x",
enabled: true,
};
expect(() => convertToWei("invalid", token)).toThrow(
"Failed to convert amount"
);
});
it("should handle very small amounts", () => {
const token = {
decimals: 18,
isNative: true,
symbol: "ETH",
address: "0x",
enabled: true,
};
const result = convertToWei("0.000000000000000001", token);
expect(result).toBe("1");
});
it("should handle very large amounts", () => {
const token = {
decimals: 18,
isNative: true,
symbol: "ETH",
address: "0x",
enabled: true,
};
const result = convertToWei("1000000", token);
expect(result).toBe("1000000000000000000000000");
});
});
describe("isXChainSwapContent", () => {
it("should return true for valid content", () => {
const content = {
fromChain: "ethereum",
toChain: "base",
fromToken: "ETH",
toToken: "ETH",
amount: "1",
toAddress: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
};
expect(isXChainSwapContent(content as any)).toBe(true);
});
it("should accept number amount", () => {
const content = {
fromChain: "ethereum",
toChain: "base",
fromToken: "ETH",
toToken: "ETH",
amount: 1,
toAddress: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
};
expect(isXChainSwapContent(content as any)).toBe(true);
});
it("should return false for missing fromChain", () => {
const content = {
toChain: "base",
fromToken: "ETH",
toToken: "ETH",
amount: "1",
toAddress: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
};
expect(isXChainSwapContent(content as any)).toBe(false);
});
it("should return false for missing toChain", () => {
const content = {
fromChain: "ethereum",
fromToken: "ETH",
toToken: "ETH",
amount: "1",
toAddress: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
};
expect(isXChainSwapContent(content as any)).toBe(false);
});
it("should return false for missing fromToken", () => {
const content = {
fromChain: "ethereum",
toChain: "base",
toToken: "ETH",
amount: "1",
toAddress: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
};
expect(isXChainSwapContent(content as any)).toBe(false);
});
it("should return false for missing toToken", () => {
const content = {
fromChain: "ethereum",
toChain: "base",
fromToken: "ETH",
amount: "1",
toAddress: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
};
expect(isXChainSwapContent(content as any)).toBe(false);
});
it("should return false for missing amount", () => {
const content = {
fromChain: "ethereum",
toChain: "base",
fromToken: "ETH",
toToken: "ETH",
toAddress: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
};
expect(isXChainSwapContent(content as any)).toBe(false);
});
it("should return false for missing toAddress", () => {
const content = {
fromChain: "ethereum",
toChain: "base",
fromToken: "ETH",
toToken: "ETH",
amount: "1",
};
expect(isXChainSwapContent(content as any)).toBe(false);
});
it("should return false for wrong type fields", () => {
const content = {
fromChain: 123,
toChain: "base",
fromToken: "ETH",
toToken: "ETH",
amount: "1",
toAddress: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
};
expect(isXChainSwapContent(content as any)).toBe(false);
});
it("should return false for null values", () => {
const content = {
fromChain: null,
toChain: "base",
fromToken: "ETH",
toToken: "ETH",
amount: "1",
toAddress: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
};
expect(isXChainSwapContent(content as any)).toBe(false);
});
});
describe("validateSquidRouterConfig", () => {
let mockRuntime: any;
beforeEach(() => {
mockRuntime = createMockRuntime();
});
it("should validate valid configuration", async () => {
const config = await validateSquidRouterConfig(mockRuntime);
expect(config).toBeDefined();
expect(config.SQUID_INTEGRATOR_ID).toBe("test-integrator-id");
expect(config.SQUID_SDK_URL).toBe("https://apiplus.squidrouter.com");
expect(config.SQUID_EVM_ADDRESS).toBe(
"0x742D35Cc6634c0532925A3b844BC9E7595f6F269"
);
expect(config.SQUID_EVM_PRIVATE_KEY).toBe(
"0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
);
});
it("should throw error for missing integrator ID", async () => {
mockRuntime.getSetting.mockImplementation((key: string) => {
if (key === "SQUID_INTEGRATOR_ID") return undefined;
return createMockRuntime().getSetting(key);
});
await expect(validateSquidRouterConfig(mockRuntime)).rejects.toThrow(
"Required"
);
});
it("should throw error for missing SDK URL", async () => {
mockRuntime.getSetting.mockImplementation((key: string) => {
if (key === "SQUID_SDK_URL") return undefined;
return createMockRuntime().getSetting(key);
});
await expect(validateSquidRouterConfig(mockRuntime)).rejects.toThrow(
"Required"
);
});
it("should throw error for invalid EVM address", async () => {
mockRuntime.getSetting.mockImplementation((key: string) => {
if (key === "SQUID_EVM_ADDRESS") return "invalid-address";
return createMockRuntime().getSetting(key);
});
await expect(validateSquidRouterConfig(mockRuntime)).rejects.toThrow(
"EVM_ADDRESS is invalid"
);
});
it("should throw error for invalid EVM private key", async () => {
mockRuntime.getSetting.mockImplementation((key: string) => {
if (key === "SQUID_EVM_PRIVATE_KEY") return "invalid-key";
return createMockRuntime().getSetting(key);
});
await expect(validateSquidRouterConfig(mockRuntime)).rejects.toThrow(
"64-character hexadecimal string"
);
});
it("should accept private key with 0x prefix", async () => {
mockRuntime.getSetting.mockImplementation((key: string) => {
if (key === "SQUID_EVM_PRIVATE_KEY") {
return "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
}
return createMockRuntime().getSetting(key);
});
const config = await validateSquidRouterConfig(mockRuntime);
expect(config.SQUID_EVM_PRIVATE_KEY).toBeDefined();
});
it("should accept private key without 0x prefix", async () => {
mockRuntime.getSetting.mockImplementation((key: string) => {
if (key === "SQUID_EVM_PRIVATE_KEY") {
return "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
}
return createMockRuntime().getSetting(key);
});
const config = await validateSquidRouterConfig(mockRuntime);
expect(config.SQUID_EVM_PRIVATE_KEY).toBeDefined();
});
it("should throw error when EVM address is provided without private key", async () => {
mockRuntime.getSetting.mockImplementation((key: string) => {
if (key === "SQUID_EVM_PRIVATE_KEY") return undefined;
return createMockRuntime().getSetting(key);
});
await expect(validateSquidRouterConfig(mockRuntime)).rejects.toThrow(
"SQUID_EVM_PRIVATE_KEY: Required"
);
});
it("should throw error when EVM private key is provided without address", async () => {
mockRuntime.getSetting.mockImplementation((key: string) => {
if (key === "SQUID_EVM_ADDRESS") return undefined;
return createMockRuntime().getSetting(key);
});
await expect(validateSquidRouterConfig(mockRuntime)).rejects.toThrow(
"SQUID_EVM_ADDRESS: Required"
);
});
it("should handle non-Zod errors", async () => {
mockRuntime.getSetting.mockImplementation(() => {
throw new Error("Unexpected error");
});
await expect(validateSquidRouterConfig(mockRuntime)).rejects.toThrow(
"Unexpected error"
);
});
});
describe("squidRouterEnvSchema", () => {
it("should accept valid configuration", () => {
const config = {
SQUID_INTEGRATOR_ID: "test-id",
SQUID_SDK_URL: "https://api.squid.com",
SQUID_EVM_ADDRESS: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
SQUID_EVM_PRIVATE_KEY:
"0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
};
const result = squidRouterEnvSchema.parse(config);
expect(result).toEqual(config);
});
it("should reject empty integrator ID", () => {
const config = {
SQUID_INTEGRATOR_ID: "",
SQUID_SDK_URL: "https://api.squid.com",
SQUID_EVM_ADDRESS: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
SQUID_EVM_PRIVATE_KEY:
"0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
};
expect(() => squidRouterEnvSchema.parse(config)).toThrow();
});
it("should validate EVM address checksum", () => {
const config = {
SQUID_INTEGRATOR_ID: "test-id",
SQUID_SDK_URL: "https://api.squid.com",
SQUID_EVM_ADDRESS: "0x742d35cc6634c0532925a3b844bc9e7595f62999", // lowercase
SQUID_EVM_PRIVATE_KEY:
"0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
};
// ethers.isAddress accepts both checksummed and non-checksummed addresses
const result = squidRouterEnvSchema.parse(config);
expect(result).toBeDefined();
});
it("should validate private key length", () => {
const config = {
SQUID_INTEGRATOR_ID: "test-id",
SQUID_SDK_URL: "https://api.squid.com",
SQUID_EVM_ADDRESS: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
SQUID_EVM_PRIVATE_KEY: "0x123", // too short
};
expect(() => squidRouterEnvSchema.parse(config)).toThrow(
"64-character hexadecimal"
);
});
it("should validate private key contains only hex characters", () => {
const config = {
SQUID_INTEGRATOR_ID: "test-id",
SQUID_SDK_URL: "https://api.squid.com",
SQUID_EVM_ADDRESS: "0x742D35Cc6634c0532925A3b844BC9E7595f6F269",
SQUID_EVM_PRIVATE_KEY:
"0xGGGG456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", // invalid hex
};
expect(() => squidRouterEnvSchema.parse(config)).toThrow(
"64-character hexadecimal"
);
});
});