mirror of
https://github.com/alsk1992/CloddsBot.git
synced 2026-10-02 02:54:40 +08:00
EVM Wallet: - Wallet generation with mnemonic and keystore encryption - Multi-chain balances (7 chains: ETH, Base, Polygon, Arbitrum, BSC, OP, AVAX) - Odos swap aggregator integration - Token transfers (ETH and ERC20) - Generic contract read/write calls - CLI commands: /wallet, /swap, /send, /chains Feeds: - Virtuals Protocol feed (Base chain AI agents) - Opinion.trade feed (BNB Chain prediction market) Skills: - /harden skill for VPS security auditing - 10-point security checklist - Automated safe fixes Documentation: - docs/EVM_WALLET.md - docs/VIRTUALS.md - docs/OPINION.md - docs/VPS_SECURITY.md Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
8.6 KiB
8.6 KiB
EVM Wallet Integration
Self-custody wallet management and multi-chain trading.
Overview
The EVM module provides complete wallet and trading capabilities:
┌─────────────────────────────────────────────────────────────┐
│ EVM Wallet Suite │
├─────────────────┬─────────────────┬─────────────────────────┤
│ Wallet │ Trading │ Multi-Chain │
├─────────────────┼─────────────────┼─────────────────────────┤
│ • Generation │ • Odos swaps │ • 7 chains supported │
│ • Mnemonic │ • Token sends │ • Balance checking │
│ • Keystore │ • ETH transfers │ • Common tokens │
│ • Encryption │ • Contract calls│ • Gas estimation │
└─────────────────┴─────────────────┴─────────────────────────┘
Quick Start
1. Generate Wallet
# Via CLI
/wallet create
# Or programmatically
import { generateWallet } from './evm/wallet';
const wallet = generateWallet();
console.log(wallet.address, wallet.privateKey);
2. Configure
# Set wallet key for trading
export EVM_PRIVATE_KEY="0x..."
# Optional: Custom RPC endpoints
export ETH_RPC_URL="https://your-eth-rpc.com"
export BASE_RPC_URL="https://your-base-rpc.com"
3. Check Balances
/wallet balance 0x1234...
4. Swap & Send
/swap base ETH USDC 0.1 # Swap via Odos
/send base 0x123... 0.1 # Send ETH
Commands
Wallet Management
| Command | Description |
|---|---|
/wallet create [name] |
Generate new wallet |
/wallet list |
List saved wallets |
/wallet balance <address> |
Check balances across all chains |
/chains |
List supported chains |
Trading
| Command | Description |
|---|---|
/swap <chain> <from> <to> <amount> |
Get swap quote via Odos |
/send <chain> <to> <amount> [token] |
Send ETH or tokens |
Supported Chains
| Chain | ID | Native | Explorer |
|---|---|---|---|
| Ethereum | 1 | ETH | etherscan.io |
| Base | 8453 | ETH | basescan.org |
| Polygon | 137 | MATIC | polygonscan.com |
| Arbitrum | 42161 | ETH | arbiscan.io |
| BNB Chain | 56 | BNB | bscscan.com |
| Optimism | 10 | ETH | optimistic.etherscan.io |
| Avalanche | 43114 | AVAX | snowtrace.io |
Programmatic Usage
Wallet Generation
import {
generateWallet,
walletFromMnemonic,
walletFromPrivateKey,
encryptKeystore,
decryptKeystore,
saveWallet,
loadWallet,
listWallets,
} from './evm/wallet';
// Generate random wallet
const wallet = generateWallet();
// { address, privateKey, mnemonic, publicKey }
// From mnemonic (with derivation index)
const wallet2 = walletFromMnemonic('word word word...', 0);
// From private key
const info = walletFromPrivateKey('0x...');
// Encrypt and save
const path = saveWallet(wallet.privateKey, 'mypassword', 'main-wallet');
// Load later
const loaded = loadWallet('main-wallet', 'mypassword');
// List saved
const wallets = listWallets();
// [{ name: 'main-wallet', address: '0x...' }]
Multi-Chain Balances
import {
getMultiChainBalances,
getNativeBalance,
getTokenBalance,
getChainBalances,
CHAINS,
CHAIN_TOKENS,
} from './evm/multichain';
// All chains at once
const balances = await getMultiChainBalances('0x...');
for (const chain of balances.balances) {
console.log(chain.chainName, chain.native.balance, chain.native.symbol);
for (const token of chain.tokens) {
console.log(' ', token.symbol, token.balance);
}
}
// Single chain
const ethBal = await getNativeBalance('ethereum', '0x...');
// Specific token
const usdc = await getTokenBalance(
'base',
'0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
'0x...'
);
// Common tokens are pre-configured
console.log(CHAIN_TOKENS.base.USDC); // USDC address on Base
Odos Swaps
import {
getOdosQuote,
executeOdosSwap,
swapNativeToToken,
swapTokenToNative,
swapTokens,
getSupportedChains,
} from './evm/odos';
// Get quote
const quote = await getOdosQuote({
chain: 'base',
inputToken: 'ETH', // or address
outputToken: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC
amount: '0.1',
slippageBps: 50, // 0.5%
});
console.log('Output:', quote.outputAmount);
console.log('Price impact:', quote.priceImpact);
console.log('Route:', quote.route);
// Execute swap
const result = await executeOdosSwap({
chain: 'base',
inputToken: 'ETH',
outputToken: '0x833589...',
amount: '0.1',
privateKey: process.env.EVM_PRIVATE_KEY!,
slippageBps: 50,
});
if (result.success) {
console.log('TX:', result.txHash);
console.log('Received:', result.outputAmount);
}
// Convenience functions
await swapNativeToToken('polygon', '0x2791...', '100', privateKey);
await swapTokenToNative('arbitrum', '0xaf88...', '1000', privateKey);
await swapTokens('base', '0x...', '0x...', '50', privateKey);
Token Transfers
import {
sendNative,
sendToken,
sendNativeBatch,
sendTokenBatch,
estimateNativeTransferGas,
estimateTokenTransferGas,
} from './evm/transfers';
// Send ETH
const result = await sendNative({
chain: 'base',
to: '0x...',
amount: '0.1',
privateKey: '0x...',
});
// Send ERC20
const tokenResult = await sendToken({
chain: 'polygon',
to: '0x...',
amount: '100',
privateKey: '0x...',
tokenAddress: '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', // USDC
});
// Batch transfers
const recipients = [
{ to: '0x111...', amount: '0.01' },
{ to: '0x222...', amount: '0.02' },
];
const results = await sendNativeBatch('ethereum', recipients, privateKey);
// Gas estimation
const gas = await estimateNativeTransferGas('ethereum', '0x...', '0.1');
console.log('Estimated cost:', gas.estimatedCost, 'ETH');
Contract Calls
import {
callContract,
writeContract,
getEventLogs,
isContract,
getContractInfo,
COMMON_ABIS,
encodeFunctionData,
decodeFunctionData,
} from './evm/contracts';
// Read-only call
const result = await callContract({
chain: 'base',
contractAddress: '0x...',
abi: COMMON_ABIS.erc20,
method: 'balanceOf',
args: ['0x...'],
});
console.log('Balance:', result.result);
// Write call
const writeResult = await writeContract({
chain: 'polygon',
contractAddress: '0x...',
abi: COMMON_ABIS.erc20,
method: 'approve',
args: ['0xspender', '1000000000000'],
privateKey: '0x...',
});
console.log('TX:', writeResult.txHash);
// Query events
const logs = await getEventLogs({
chain: 'ethereum',
contractAddress: '0x...',
abi: COMMON_ABIS.erc20,
eventName: 'Transfer',
fromBlock: 19000000,
toBlock: 'latest',
});
// Check if contract
const isCtx = await isContract('base', '0x...');
// Use pre-built ABIs
// COMMON_ABIS.erc20, erc721, erc1155, multicall, uniswapV2Pair, uniswapV2Router
Keystore Encryption
Wallets are encrypted using:
- KDF: scrypt (N=262144, r=8, p=1)
- Cipher: AES-256-CTR
- MAC: Keccak256
Storage location: ~/.clodds/wallets/
// Encrypt
const keystore = encryptKeystore(privateKey, 'mypassword');
// Decrypt
const privateKey = decryptKeystore(keystore, 'mypassword');
Configuration
Environment Variables
| Variable | Description |
|---|---|
EVM_PRIVATE_KEY |
Wallet private key for trading |
ETH_RPC_URL |
Ethereum RPC (default: llamarpc) |
BASE_RPC_URL |
Base RPC (default: mainnet.base.org) |
POLYGON_RPC_URL |
Polygon RPC |
ARBITRUM_RPC_URL |
Arbitrum RPC |
BSC_RPC_URL |
BNB Chain RPC |
OPTIMISM_RPC_URL |
Optimism RPC |
AVALANCHE_RPC_URL |
Avalanche RPC |
Config File
In ~/.clodds/clodds.json:
{
"evm": {
"defaultChain": "base",
"slippageBps": 50
}
}
Security Notes
- Self-Custody: Private keys never leave your machine
- Encrypted Storage: Keystores use scrypt + AES-256
- No Cloud: Nothing sent to external services (except RPC calls)
- Validate First: Always check quotes before executing swaps