sovereignclaw 0.1.0 → 0.1.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.
@@ -109,8 +109,11 @@ Then run: sovereignclaw portfolio
109
109
  }
110
110
 
111
111
  function runScript(script, ...args) {
112
- const scriptPath = join(__dirname, '..', 'scripts', script);
113
- const child = spawn('npx', ['tsx', scriptPath, ...args, ...process.argv.slice(3)], {
112
+ // Use bundled JS from dist/ instead of TS
113
+ const jsScript = script.replace('.ts', '.js');
114
+ const scriptPath = join(__dirname, '..', 'dist', jsScript);
115
+
116
+ const child = spawn('node', [scriptPath, ...args, ...process.argv.slice(3)], {
114
117
  stdio: 'inherit',
115
118
  env: {
116
119
  ...process.env,
package/dist/bridge.js ADDED
@@ -0,0 +1,336 @@
1
+ // scripts/bridge.ts
2
+ import { createWalletClient, createPublicClient, http, encodeFunctionData, parseUnits, formatUnits } from "viem";
3
+ import { privateKeyToAccount } from "viem/accounts";
4
+ import { sepolia, baseSepolia, arbitrumSepolia, optimismSepolia } from "viem/chains";
5
+ var TESTNET_CHAINS = {
6
+ "ethereum-sepolia": {
7
+ name: "Ethereum Sepolia",
8
+ domain: 0,
9
+ chainId: 11155111,
10
+ chain: sepolia,
11
+ usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
12
+ rpcUrl: "https://ethereum-sepolia-rpc.publicnode.com"
13
+ },
14
+ "base-sepolia": {
15
+ name: "Base Sepolia",
16
+ domain: 6,
17
+ chainId: 84532,
18
+ chain: baseSepolia,
19
+ usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
20
+ rpcUrl: "https://base-sepolia-rpc.publicnode.com"
21
+ },
22
+ "arbitrum-sepolia": {
23
+ name: "Arbitrum Sepolia",
24
+ domain: 3,
25
+ chainId: 421614,
26
+ chain: arbitrumSepolia,
27
+ usdc: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d"
28
+ },
29
+ "op-sepolia": {
30
+ name: "OP Sepolia",
31
+ domain: 2,
32
+ chainId: 11155420,
33
+ chain: optimismSepolia,
34
+ usdc: "0x5fd84259d66Cd46123540766Be93DFE6D43130D7"
35
+ }
36
+ };
37
+ var CCTP_CONTRACTS = {
38
+ testnet: {
39
+ tokenMessenger: "0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA",
40
+ messageTransmitter: "0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275",
41
+ attestationApi: "https://iris-api-sandbox.circle.com"
42
+ },
43
+ mainnet: {
44
+ tokenMessenger: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d",
45
+ messageTransmitter: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64",
46
+ attestationApi: "https://iris-api.circle.com"
47
+ }
48
+ };
49
+ var ERC20_ABI = [
50
+ {
51
+ type: "function",
52
+ name: "approve",
53
+ stateMutability: "nonpayable",
54
+ inputs: [
55
+ { name: "spender", type: "address" },
56
+ { name: "amount", type: "uint256" }
57
+ ],
58
+ outputs: [{ name: "", type: "bool" }]
59
+ },
60
+ {
61
+ type: "function",
62
+ name: "balanceOf",
63
+ stateMutability: "view",
64
+ inputs: [{ name: "account", type: "address" }],
65
+ outputs: [{ name: "", type: "uint256" }]
66
+ },
67
+ {
68
+ type: "function",
69
+ name: "allowance",
70
+ stateMutability: "view",
71
+ inputs: [
72
+ { name: "owner", type: "address" },
73
+ { name: "spender", type: "address" }
74
+ ],
75
+ outputs: [{ name: "", type: "uint256" }]
76
+ }
77
+ ];
78
+ var TOKEN_MESSENGER_ABI = [
79
+ {
80
+ type: "function",
81
+ name: "depositForBurn",
82
+ stateMutability: "nonpayable",
83
+ inputs: [
84
+ { name: "amount", type: "uint256" },
85
+ { name: "destinationDomain", type: "uint32" },
86
+ { name: "mintRecipient", type: "bytes32" },
87
+ { name: "burnToken", type: "address" },
88
+ { name: "destinationCaller", type: "bytes32" },
89
+ { name: "maxFee", type: "uint256" },
90
+ { name: "minFinalityThreshold", type: "uint32" }
91
+ ],
92
+ outputs: []
93
+ }
94
+ ];
95
+ var MESSAGE_TRANSMITTER_ABI = [
96
+ {
97
+ type: "function",
98
+ name: "receiveMessage",
99
+ stateMutability: "nonpayable",
100
+ inputs: [
101
+ { name: "message", type: "bytes" },
102
+ { name: "attestation", type: "bytes" }
103
+ ],
104
+ outputs: [{ name: "success", type: "bool" }]
105
+ }
106
+ ];
107
+ function addressToBytes32(address) {
108
+ return `0x000000000000000000000000${address.slice(2)}`;
109
+ }
110
+ var ZERO_BYTES32 = "0x0000000000000000000000000000000000000000000000000000000000000000";
111
+ async function sleep(ms) {
112
+ return new Promise((resolve) => setTimeout(resolve, ms));
113
+ }
114
+ var CCTPBridge = class {
115
+ privateKey;
116
+ isTestnet;
117
+ contracts;
118
+ constructor(privateKey, isTestnet = true) {
119
+ this.privateKey = privateKey;
120
+ this.isTestnet = isTestnet;
121
+ this.contracts = isTestnet ? CCTP_CONTRACTS.testnet : CCTP_CONTRACTS.mainnet;
122
+ }
123
+ getChainConfig(chainName) {
124
+ const normalized = chainName.toLowerCase().replace(/\s+/g, "-");
125
+ const config = TESTNET_CHAINS[normalized];
126
+ if (!config) {
127
+ throw new Error(`Unsupported chain: ${chainName}. Supported: ${Object.keys(TESTNET_CHAINS).join(", ")}`);
128
+ }
129
+ return config;
130
+ }
131
+ createClients(chainConfig) {
132
+ const account = privateKeyToAccount(this.privateKey);
133
+ const transport = chainConfig.rpcUrl ? http(chainConfig.rpcUrl) : http();
134
+ const walletClient = createWalletClient({
135
+ chain: chainConfig.chain,
136
+ transport,
137
+ account
138
+ });
139
+ const publicClient = createPublicClient({
140
+ chain: chainConfig.chain,
141
+ transport
142
+ });
143
+ return { walletClient, publicClient, account };
144
+ }
145
+ async getBalance(chainName) {
146
+ const chainConfig = this.getChainConfig(chainName);
147
+ const { publicClient, account } = this.createClients(chainConfig);
148
+ const balance = await publicClient.readContract({
149
+ address: chainConfig.usdc,
150
+ abi: ERC20_ABI,
151
+ functionName: "balanceOf",
152
+ args: [account.address]
153
+ });
154
+ return formatUnits(balance, 6);
155
+ }
156
+ async quote(sourceChain, destChain, amount) {
157
+ const source = this.getChainConfig(sourceChain);
158
+ const dest = this.getChainConfig(destChain);
159
+ const fee = "0.0005";
160
+ const received = (parseFloat(amount) - parseFloat(fee)).toFixed(4);
161
+ return {
162
+ sourceChain: source.name,
163
+ destChain: dest.name,
164
+ amount,
165
+ estimatedReceived: received,
166
+ fee,
167
+ estimatedTime: "1-2 minutes (Fast Transfer)"
168
+ };
169
+ }
170
+ async bridge(sourceChain, destChain, amount, recipient, onStatus) {
171
+ const log = onStatus || console.log;
172
+ const source = this.getChainConfig(sourceChain);
173
+ const dest = this.getChainConfig(destChain);
174
+ const amountWei = parseUnits(amount, 6);
175
+ const { walletClient: sourceWallet, publicClient: sourcePublic, account } = this.createClients(source);
176
+ const { walletClient: destWallet } = this.createClients(dest);
177
+ const recipientAddress = recipient || account.address;
178
+ const recipientBytes32 = addressToBytes32(recipientAddress);
179
+ log("\u{1F4CA} Checking USDC balance...");
180
+ const balance = await sourcePublic.readContract({
181
+ address: source.usdc,
182
+ abi: ERC20_ABI,
183
+ functionName: "balanceOf",
184
+ args: [account.address]
185
+ });
186
+ if (balance < amountWei) {
187
+ throw new Error(`Insufficient USDC. Have: ${formatUnits(balance, 6)}, Need: ${amount}`);
188
+ }
189
+ log("\u2705 Step 1/4: Approving USDC...");
190
+ const allowance = await sourcePublic.readContract({
191
+ address: source.usdc,
192
+ abi: ERC20_ABI,
193
+ functionName: "allowance",
194
+ args: [account.address, this.contracts.tokenMessenger]
195
+ });
196
+ if (allowance < amountWei) {
197
+ const approveTx = await sourceWallet.sendTransaction({
198
+ to: source.usdc,
199
+ data: encodeFunctionData({
200
+ abi: ERC20_ABI,
201
+ functionName: "approve",
202
+ args: [this.contracts.tokenMessenger, amountWei * 10n]
203
+ // Approve extra for future txs
204
+ })
205
+ });
206
+ log(` Approval TX: ${approveTx}`);
207
+ await sourcePublic.waitForTransactionReceipt({ hash: approveTx });
208
+ } else {
209
+ log(" Already approved.");
210
+ }
211
+ log("\u{1F525} Step 2/4: Burning USDC on source chain...");
212
+ const maxFee = 100000n;
213
+ const minFinalityThreshold = 1e3;
214
+ const burnTx = await sourceWallet.sendTransaction({
215
+ to: this.contracts.tokenMessenger,
216
+ data: encodeFunctionData({
217
+ abi: TOKEN_MESSENGER_ABI,
218
+ functionName: "depositForBurn",
219
+ args: [
220
+ amountWei,
221
+ dest.domain,
222
+ recipientBytes32,
223
+ source.usdc,
224
+ ZERO_BYTES32,
225
+ maxFee,
226
+ minFinalityThreshold
227
+ ]
228
+ })
229
+ });
230
+ log(` Burn TX: ${burnTx}`);
231
+ await sourcePublic.waitForTransactionReceipt({ hash: burnTx });
232
+ log("\u23F3 Step 3/4: Waiting for attestation...");
233
+ const attestation = await this.waitForAttestation(source.domain, burnTx, log);
234
+ log(" \u2705 Attestation received!");
235
+ log("\u{1FA99} Step 4/4: Minting USDC on destination chain...");
236
+ const mintTx = await destWallet.sendTransaction({
237
+ to: this.contracts.messageTransmitter,
238
+ data: encodeFunctionData({
239
+ abi: MESSAGE_TRANSMITTER_ABI,
240
+ functionName: "receiveMessage",
241
+ args: [attestation.message, attestation.attestation]
242
+ })
243
+ });
244
+ log(` Mint TX: ${mintTx}`);
245
+ const destPublic = createPublicClient({
246
+ chain: dest.chain,
247
+ transport: http()
248
+ });
249
+ await destPublic.waitForTransactionReceipt({ hash: mintTx });
250
+ log("\u2705 Bridge complete!");
251
+ return {
252
+ burnTxHash: burnTx,
253
+ mintTxHash: mintTx,
254
+ amountReceived: formatUnits(amountWei - maxFee, 6)
255
+ };
256
+ }
257
+ async waitForAttestation(sourceDomain, txHash, log) {
258
+ const url = `${this.contracts.attestationApi}/v2/messages/${sourceDomain}?transactionHash=${txHash}`;
259
+ let attempts = 0;
260
+ const maxAttempts = 60;
261
+ while (attempts < maxAttempts) {
262
+ try {
263
+ const response = await fetch(url);
264
+ if (response.ok) {
265
+ const data = await response.json();
266
+ if (data.messages?.[0]?.status === "complete") {
267
+ return {
268
+ message: data.messages[0].message,
269
+ attestation: data.messages[0].attestation
270
+ };
271
+ }
272
+ }
273
+ attempts++;
274
+ if (attempts % 6 === 0) {
275
+ log(` Still waiting... (${attempts * 5}s)`);
276
+ }
277
+ await sleep(5e3);
278
+ } catch (error) {
279
+ attempts++;
280
+ await sleep(5e3);
281
+ }
282
+ }
283
+ throw new Error(`Attestation timeout after ${maxAttempts * 5}s. TX: ${txHash}`);
284
+ }
285
+ };
286
+ async function main() {
287
+ const args = process.argv.slice(2);
288
+ if (args.length < 4) {
289
+ console.log(`
290
+ Usage: npx tsx bridge.ts <action> <source> <dest> <amount> [recipient]
291
+
292
+ Actions:
293
+ quote - Get a quote without executing
294
+ bridge - Execute the bridge
295
+
296
+ Examples:
297
+ npx tsx bridge.ts quote ethereum-sepolia base-sepolia 10
298
+ npx tsx bridge.ts bridge ethereum-sepolia base-sepolia 10
299
+
300
+ Environment:
301
+ PRIVATE_KEY - Your wallet private key
302
+ `);
303
+ process.exit(1);
304
+ }
305
+ const [action, source, dest, amount, recipient] = args;
306
+ const privateKey = process.env.PRIVATE_KEY;
307
+ if (action === "bridge" && !privateKey) {
308
+ console.error("Error: PRIVATE_KEY environment variable required for bridging");
309
+ process.exit(1);
310
+ }
311
+ const bridge = new CCTPBridge(privateKey || "0x0", true);
312
+ if (action === "quote") {
313
+ const quote = await bridge.quote(source, dest, amount);
314
+ console.log(`
315
+ \u{1F504} CCTP Bridge Quote: ${quote.amount} USDC
316
+ \u{1F4E4} From: ${quote.sourceChain}
317
+ \u{1F4E5} To: ${quote.destChain}
318
+
319
+ \u{1F4B0} You receive: ~${quote.estimatedReceived} USDC
320
+ \u26FD Fee: ~${quote.fee} USDC
321
+ \u23F1\uFE0F Est. time: ${quote.estimatedTime}
322
+ `);
323
+ } else if (action === "bridge") {
324
+ const result = await bridge.bridge(source, dest, amount, recipient);
325
+ console.log(`
326
+ \u2705 Bridge Complete!
327
+ \u{1F525} Burn TX: ${result.burnTxHash}
328
+ \u{1FA99} Mint TX: ${result.mintTxHash}
329
+ \u{1F4B0} Received: ${result.amountReceived} USDC
330
+ `);
331
+ }
332
+ }
333
+ main().catch(console.error);
334
+ export {
335
+ CCTPBridge
336
+ };