cornerstone-autonomous-agent 1.0.1
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/.env.example +49 -0
- package/LICENSE.md +384 -0
- package/README.md +163 -0
- package/SKILL-clawdhub.md +180 -0
- package/adapters/anthropic/tools.json +58 -0
- package/adapters/local/README.md +11 -0
- package/adapters/openai/openapi.yaml +72 -0
- package/adapters/openclaw/SKILL.md +47 -0
- package/package.json +71 -0
- package/src/agent/agent.js +52 -0
- package/src/agent/index.js +4 -0
- package/src/agent/llm.js +25 -0
- package/src/agent/tools/localTools.js +259 -0
- package/src/agent/tools/mcpTools.js +69 -0
- package/src/balance.js +264 -0
- package/src/check-update.js +191 -0
- package/src/contract.js +390 -0
- package/src/credit-aptos-agent.js +103 -0
- package/src/lib/aptos/balance.js +32 -0
- package/src/lib/aptos/config.js +48 -0
- package/src/lib/aptos/index.js +4 -0
- package/src/lib/aptos/signPayment.js +94 -0
- package/src/lib/aptos/wallet.js +143 -0
- package/src/lib/chains.js +157 -0
- package/src/lib/evm/index.js +1 -0
- package/src/lib/evm/signPayment.js +91 -0
- package/src/lib/gas.js +186 -0
- package/src/lib/mcp/client.js +225 -0
- package/src/lib/mcp/index.js +1 -0
- package/src/lib/rpc.js +175 -0
- package/src/lib/wallet.js +255 -0
- package/src/lib/x402/client.js +195 -0
- package/src/lib/x402/index.js +7 -0
- package/src/lib/x402/types.js +50 -0
- package/src/register-aptos-agent.js +92 -0
- package/src/run-agent.js +65 -0
- package/src/setup-aptos.js +57 -0
- package/src/setup.js +104 -0
- package/src/show-agent-addresses.js +32 -0
- package/src/swap.js +449 -0
- package/src/transfer.js +326 -0
package/src/transfer.js
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Transfer Script - Send ETH or ERC20 tokens
|
|
5
|
+
* Usage:
|
|
6
|
+
* node src/transfer.js <chain> <to> <amount> # Send native ETH
|
|
7
|
+
* node src/transfer.js <chain> <to> <amount> <tokenAddress> # Send ERC20
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { parseEther, parseUnits, formatEther, parseAbi, isAddress } from 'viem';
|
|
11
|
+
import { printUpdateNag } from './check-update.js';
|
|
12
|
+
import { getWalletClient, exists } from './lib/wallet.js';
|
|
13
|
+
import { createPublicClientWithRetry } from './lib/rpc.js';
|
|
14
|
+
import { getChain, getExplorerTxUrl } from './lib/chains.js';
|
|
15
|
+
import { estimateGas, estimateGasLimit, formatGwei } from './lib/gas.js';
|
|
16
|
+
|
|
17
|
+
// Standard ERC20 ABI
|
|
18
|
+
const ERC20_ABI = parseAbi([
|
|
19
|
+
'function transfer(address to, uint256 amount) returns (bool)',
|
|
20
|
+
'function balanceOf(address) view returns (uint256)',
|
|
21
|
+
'function decimals() view returns (uint8)',
|
|
22
|
+
'function symbol() view returns (string)',
|
|
23
|
+
'function name() view returns (string)'
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
// Parse command line arguments
|
|
27
|
+
const args = process.argv.slice(2);
|
|
28
|
+
const jsonFlag = args.includes('--json');
|
|
29
|
+
const yesFlag = args.includes('--yes') || args.includes('-y');
|
|
30
|
+
const helpFlag = args.includes('--help') || args.includes('-h');
|
|
31
|
+
|
|
32
|
+
function showHelp() {
|
|
33
|
+
console.log(`
|
|
34
|
+
EVM Wallet Transfer
|
|
35
|
+
|
|
36
|
+
Usage: node src/transfer.js [options] <chain> <to> <amount> [tokenAddress]
|
|
37
|
+
|
|
38
|
+
Arguments:
|
|
39
|
+
chain Chain name (base, ethereum, polygon, arbitrum, optimism)
|
|
40
|
+
to Recipient address
|
|
41
|
+
amount Amount to send
|
|
42
|
+
tokenAddress ERC20 token contract address (optional, for token transfers)
|
|
43
|
+
|
|
44
|
+
Options:
|
|
45
|
+
--yes Skip confirmation prompt
|
|
46
|
+
--json Output in JSON format
|
|
47
|
+
--help Show this help message
|
|
48
|
+
|
|
49
|
+
Examples:
|
|
50
|
+
node src/transfer.js base 0x123... 0.01 # Send 0.01 ETH on Base
|
|
51
|
+
node src/transfer.js base 0x123... 100 0x833589fcd... # Send 100 USDC on Base
|
|
52
|
+
node src/transfer.js ethereum 0x123... 0.5 --yes # Send 0.5 ETH, skip confirmation
|
|
53
|
+
`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function exitWithError(message, code = 1) {
|
|
57
|
+
if (jsonFlag) {
|
|
58
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
59
|
+
} else {
|
|
60
|
+
console.error(`Error: ${message}`);
|
|
61
|
+
}
|
|
62
|
+
process.exit(code);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get token info (symbol, decimals, name)
|
|
67
|
+
*/
|
|
68
|
+
async function getTokenInfo(client, tokenAddress) {
|
|
69
|
+
try {
|
|
70
|
+
const [symbol, decimals, name] = await Promise.all([
|
|
71
|
+
client.readContract({
|
|
72
|
+
address: tokenAddress,
|
|
73
|
+
abi: ERC20_ABI,
|
|
74
|
+
functionName: 'symbol'
|
|
75
|
+
}),
|
|
76
|
+
client.readContract({
|
|
77
|
+
address: tokenAddress,
|
|
78
|
+
abi: ERC20_ABI,
|
|
79
|
+
functionName: 'decimals'
|
|
80
|
+
}),
|
|
81
|
+
client.readContract({
|
|
82
|
+
address: tokenAddress,
|
|
83
|
+
abi: ERC20_ABI,
|
|
84
|
+
functionName: 'name'
|
|
85
|
+
})
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
return { symbol, decimals, name };
|
|
89
|
+
} catch (error) {
|
|
90
|
+
throw new Error(`Failed to get token info: ${error.message}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Check token balance
|
|
96
|
+
*/
|
|
97
|
+
async function checkTokenBalance(client, tokenAddress, walletAddress) {
|
|
98
|
+
try {
|
|
99
|
+
const balance = await client.readContract({
|
|
100
|
+
address: tokenAddress,
|
|
101
|
+
abi: ERC20_ABI,
|
|
102
|
+
functionName: 'balanceOf',
|
|
103
|
+
args: [walletAddress]
|
|
104
|
+
});
|
|
105
|
+
return balance;
|
|
106
|
+
} catch (error) {
|
|
107
|
+
throw new Error(`Failed to check token balance: ${error.message}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Prompt for user confirmation
|
|
113
|
+
*/
|
|
114
|
+
async function confirm(message) {
|
|
115
|
+
if (yesFlag || jsonFlag) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
process.stdout.write(`${message} (y/N): `);
|
|
120
|
+
|
|
121
|
+
return new Promise((resolve) => {
|
|
122
|
+
process.stdin.once('data', (data) => {
|
|
123
|
+
const response = data.toString().trim().toLowerCase();
|
|
124
|
+
resolve(response === 'y' || response === 'yes');
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function main() {
|
|
130
|
+
try {
|
|
131
|
+
if (helpFlag) {
|
|
132
|
+
showHelp();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Check if wallet exists
|
|
137
|
+
if (!exists()) {
|
|
138
|
+
exitWithError('No wallet found. Run setup.js first to generate a wallet.');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Parse arguments
|
|
142
|
+
const filteredArgs = args.filter(arg => !arg.startsWith('--'));
|
|
143
|
+
const [chainName, to, amount, tokenAddress] = filteredArgs;
|
|
144
|
+
|
|
145
|
+
if (!chainName || !to || !amount) {
|
|
146
|
+
exitWithError('Missing required arguments. Use --help for usage information.');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Validate recipient address
|
|
150
|
+
if (!isAddress(to)) {
|
|
151
|
+
exitWithError('Invalid recipient address.');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Validate token address if provided
|
|
155
|
+
if (tokenAddress && !isAddress(tokenAddress)) {
|
|
156
|
+
exitWithError('Invalid token address.');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const chain = getChain(chainName);
|
|
160
|
+
const publicClient = createPublicClientWithRetry(chainName);
|
|
161
|
+
const walletClient = getWalletClient(chainName);
|
|
162
|
+
const walletAddress = walletClient.account.address;
|
|
163
|
+
|
|
164
|
+
let transferAmount, symbol, decimals, name;
|
|
165
|
+
let isNativeTransfer = !tokenAddress;
|
|
166
|
+
|
|
167
|
+
if (isNativeTransfer) {
|
|
168
|
+
// Native token transfer
|
|
169
|
+
transferAmount = parseEther(amount);
|
|
170
|
+
symbol = chain.nativeToken.symbol;
|
|
171
|
+
decimals = 18;
|
|
172
|
+
name = `Native ${symbol}`;
|
|
173
|
+
|
|
174
|
+
// Check ETH balance
|
|
175
|
+
const balance = await publicClient.getBalance({ address: walletAddress });
|
|
176
|
+
if (balance < transferAmount) {
|
|
177
|
+
exitWithError(`Insufficient balance. Have: ${formatEther(balance)} ${symbol}, Need: ${amount} ${symbol}`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
} else {
|
|
181
|
+
// ERC20 token transfer
|
|
182
|
+
const tokenInfo = await getTokenInfo(publicClient, tokenAddress);
|
|
183
|
+
symbol = tokenInfo.symbol;
|
|
184
|
+
decimals = tokenInfo.decimals;
|
|
185
|
+
name = tokenInfo.name;
|
|
186
|
+
|
|
187
|
+
transferAmount = parseUnits(amount, decimals);
|
|
188
|
+
|
|
189
|
+
// Check token balance
|
|
190
|
+
const tokenBalance = await checkTokenBalance(publicClient, tokenAddress, walletAddress);
|
|
191
|
+
if (tokenBalance < transferAmount) {
|
|
192
|
+
const formattedBalance = decimals === 18 ?
|
|
193
|
+
formatEther(tokenBalance) :
|
|
194
|
+
(Number(tokenBalance) / (10 ** decimals)).toString();
|
|
195
|
+
exitWithError(`Insufficient token balance. Have: ${formattedBalance} ${symbol}, Need: ${amount} ${symbol}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Estimate gas
|
|
200
|
+
let gasEstimate;
|
|
201
|
+
try {
|
|
202
|
+
if (isNativeTransfer) {
|
|
203
|
+
gasEstimate = await estimateGas(chainName);
|
|
204
|
+
const gasLimit = await estimateGasLimit(publicClient, {
|
|
205
|
+
to,
|
|
206
|
+
value: transferAmount,
|
|
207
|
+
account: walletAddress
|
|
208
|
+
});
|
|
209
|
+
gasEstimate.gasLimit = gasLimit;
|
|
210
|
+
} else {
|
|
211
|
+
gasEstimate = await estimateGas(chainName);
|
|
212
|
+
const gasLimit = await estimateGasLimit(publicClient, {
|
|
213
|
+
to: tokenAddress,
|
|
214
|
+
data: walletClient.encodeFunctionData({
|
|
215
|
+
abi: ERC20_ABI,
|
|
216
|
+
functionName: 'transfer',
|
|
217
|
+
args: [to, transferAmount]
|
|
218
|
+
}),
|
|
219
|
+
account: walletAddress
|
|
220
|
+
});
|
|
221
|
+
gasEstimate.gasLimit = gasLimit;
|
|
222
|
+
}
|
|
223
|
+
} catch (error) {
|
|
224
|
+
exitWithError(`Gas estimation failed: ${error.message}`);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Calculate total cost for native transfers
|
|
228
|
+
const estimatedGasCost = gasEstimate.maxFeePerGas * gasEstimate.gasLimit;
|
|
229
|
+
const estimatedGasCostEth = formatEther(estimatedGasCost);
|
|
230
|
+
|
|
231
|
+
// Show confirmation details
|
|
232
|
+
const confirmationMessage = `
|
|
233
|
+
š Transfer Details:
|
|
234
|
+
From: ${walletAddress}
|
|
235
|
+
To: ${to}
|
|
236
|
+
Amount: ${amount} ${symbol}${tokenAddress ? ` (${name})` : ''}
|
|
237
|
+
Chain: ${chain.name}
|
|
238
|
+
|
|
239
|
+
ā½ Gas Estimate:
|
|
240
|
+
Gas Limit: ${gasEstimate.gasLimit.toLocaleString()}
|
|
241
|
+
Max Fee: ${formatGwei(gasEstimate.maxFeePerGas)} gwei
|
|
242
|
+
Est. Cost: ${estimatedGasCostEth} ETH
|
|
243
|
+
|
|
244
|
+
${isNativeTransfer ? `š° Total Deduction: ${(parseFloat(amount) + parseFloat(estimatedGasCostEth)).toFixed(6)} ETH` : `š° Gas Cost: ${estimatedGasCostEth} ETH (separate from token transfer)`}
|
|
245
|
+
|
|
246
|
+
Proceed with transfer?`;
|
|
247
|
+
|
|
248
|
+
if (!jsonFlag) {
|
|
249
|
+
console.log(confirmationMessage);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const confirmed = await confirm('');
|
|
253
|
+
if (!confirmed) {
|
|
254
|
+
if (jsonFlag) {
|
|
255
|
+
console.log(JSON.stringify({ success: false, error: 'Transfer cancelled by user' }));
|
|
256
|
+
} else {
|
|
257
|
+
console.log('ā Transfer cancelled.');
|
|
258
|
+
}
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Execute transfer
|
|
263
|
+
let txHash;
|
|
264
|
+
try {
|
|
265
|
+
if (isNativeTransfer) {
|
|
266
|
+
// Send native token
|
|
267
|
+
txHash = await walletClient.sendTransaction({
|
|
268
|
+
to,
|
|
269
|
+
value: transferAmount,
|
|
270
|
+
maxFeePerGas: gasEstimate.maxFeePerGas,
|
|
271
|
+
maxPriorityFeePerGas: gasEstimate.maxPriorityFeePerGas,
|
|
272
|
+
gas: gasEstimate.gasLimit
|
|
273
|
+
});
|
|
274
|
+
} else {
|
|
275
|
+
// Send ERC20 token
|
|
276
|
+
txHash = await walletClient.writeContract({
|
|
277
|
+
address: tokenAddress,
|
|
278
|
+
abi: ERC20_ABI,
|
|
279
|
+
functionName: 'transfer',
|
|
280
|
+
args: [to, transferAmount],
|
|
281
|
+
maxFeePerGas: gasEstimate.maxFeePerGas,
|
|
282
|
+
maxPriorityFeePerGas: gasEstimate.maxPriorityFeePerGas,
|
|
283
|
+
gas: gasEstimate.gasLimit
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
} catch (error) {
|
|
287
|
+
exitWithError(`Transfer failed: ${error.message}`);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const explorerUrl = getExplorerTxUrl(chainName, txHash);
|
|
291
|
+
|
|
292
|
+
if (jsonFlag) {
|
|
293
|
+
console.log(JSON.stringify({
|
|
294
|
+
success: true,
|
|
295
|
+
txHash,
|
|
296
|
+
explorerUrl,
|
|
297
|
+
from: walletAddress,
|
|
298
|
+
to,
|
|
299
|
+
amount,
|
|
300
|
+
symbol,
|
|
301
|
+
chain: chainName,
|
|
302
|
+
tokenAddress: tokenAddress || null,
|
|
303
|
+
gasUsed: {
|
|
304
|
+
maxFeePerGas: gasEstimate.maxFeePerGas.toString(),
|
|
305
|
+
maxPriorityFeePerGas: gasEstimate.maxPriorityFeePerGas.toString(),
|
|
306
|
+
gasLimit: gasEstimate.gasLimit.toString(),
|
|
307
|
+
estimatedCostEth: estimatedGasCostEth
|
|
308
|
+
}
|
|
309
|
+
}, null, 2));
|
|
310
|
+
} else {
|
|
311
|
+
console.log('\nā
Transfer successful!');
|
|
312
|
+
console.log(`Tx Hash: ${txHash}`);
|
|
313
|
+
console.log(`Explorer: ${explorerUrl}`);
|
|
314
|
+
console.log(`\nSent ${amount} ${symbol} to ${to}`);
|
|
315
|
+
console.log(`Gas used: ~${estimatedGasCostEth} ETH`);
|
|
316
|
+
console.log('\nš” Transaction may take a few minutes to confirm.');
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
} catch (error) {
|
|
320
|
+
exitWithError(`Unexpected error: ${error.message}`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
main().then(() => printUpdateNag()).catch(error => {
|
|
325
|
+
exitWithError(`Unexpected error: ${error.message}`);
|
|
326
|
+
});
|