@arcsign/hardhat-plugin 1.0.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 +245 -0
- package/dist/ArcSignClient.d.ts +148 -0
- package/dist/ArcSignClient.d.ts.map +1 -0
- package/dist/ArcSignClient.js +220 -0
- package/dist/ArcSignClient.js.map +1 -0
- package/dist/ArcSignProvider.d.ts +82 -0
- package/dist/ArcSignProvider.d.ts.map +1 -0
- package/dist/ArcSignProvider.js +126 -0
- package/dist/ArcSignProvider.js.map +1 -0
- package/dist/ArcSignSigner.d.ts +56 -0
- package/dist/ArcSignSigner.d.ts.map +1 -0
- package/dist/ArcSignSigner.js +201 -0
- package/dist/ArcSignSigner.js.map +1 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +187 -0
- package/dist/index.js.map +1 -0
- package/dist/utils.d.ts +29 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +108 -0
- package/dist/utils.js.map +1 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# @arcsign/hardhat-plugin
|
|
2
|
+
|
|
3
|
+
Hardhat plugin for secure transaction signing with ArcSign wallet.
|
|
4
|
+
|
|
5
|
+
**No more private keys in .env files!**
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- 🔐 Sign transactions using your ArcSign cold wallet
|
|
10
|
+
- 🔑 Private keys never leave the USB device
|
|
11
|
+
- 👁️ Visual confirmation in ArcSign Dashboard for each transaction
|
|
12
|
+
- ⚡ Session mode for rapid testnet development
|
|
13
|
+
- ✍️ Full EIP-191 and EIP-712 signing support
|
|
14
|
+
- 📝 Works with Hardhat's existing deployment scripts (no changes needed!)
|
|
15
|
+
- 🔍 Auto-inject Block Explorer API keys for contract verification
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @arcsign/hardhat-plugin
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
### 1. Update your `hardhat.config.js`
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
require("@nomicfoundation/hardhat-toolbox");
|
|
29
|
+
require("@arcsign/hardhat-plugin");
|
|
30
|
+
|
|
31
|
+
module.exports = {
|
|
32
|
+
solidity: "0.8.20",
|
|
33
|
+
networks: {
|
|
34
|
+
mainnet: {
|
|
35
|
+
url: process.env.RPC_URL,
|
|
36
|
+
accounts: [], // Empty - ArcSign provides signers
|
|
37
|
+
arcsign: true, // Enable ArcSign for this network
|
|
38
|
+
},
|
|
39
|
+
sepolia: {
|
|
40
|
+
url: process.env.SEPOLIA_RPC_URL,
|
|
41
|
+
accounts: [],
|
|
42
|
+
arcsign: true,
|
|
43
|
+
},
|
|
44
|
+
bsc: {
|
|
45
|
+
url: "https://bsc-dataseed.binance.org/",
|
|
46
|
+
accounts: [],
|
|
47
|
+
arcsign: true,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 2. Make sure ArcSign Dashboard is running
|
|
54
|
+
|
|
55
|
+
The plugin connects to ArcSign Dashboard via WebSocket (127.0.0.1:9527).
|
|
56
|
+
|
|
57
|
+
1. Open ArcSign Dashboard
|
|
58
|
+
2. Unlock your wallet
|
|
59
|
+
3. Go to Developer Mode (🔧 button)
|
|
60
|
+
|
|
61
|
+
### 3. Run your deploy script
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npx hardhat run scripts/deploy.ts --network mainnet
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
You'll see output like:
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
🔗 Connecting to ArcSign wallet...
|
|
71
|
+
✓ Connected to ArcSign v1.0.0
|
|
72
|
+
Available accounts:
|
|
73
|
+
[0] 0x742d35Cc6634C0532925a3b844Bc9e7595f45321
|
|
74
|
+
|
|
75
|
+
Deploying with: 0x742d35Cc...5321
|
|
76
|
+
⏳ Waiting for approval in ArcSign Dashboard...
|
|
77
|
+
Transaction: Deploy MyToken.sol
|
|
78
|
+
Network: Ethereum Mainnet
|
|
79
|
+
Estimated Gas: ~0.05 ETH
|
|
80
|
+
|
|
81
|
+
✓ Transaction signed!
|
|
82
|
+
✓ Transaction submitted: 0xabc123...
|
|
83
|
+
|
|
84
|
+
Token deployed to: 0xdef456...
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Deploy Scripts
|
|
88
|
+
|
|
89
|
+
Your existing deploy scripts work without modification:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
// scripts/deploy.ts
|
|
93
|
+
import { ethers } from "hardhat";
|
|
94
|
+
|
|
95
|
+
async function main() {
|
|
96
|
+
const [deployer] = await ethers.getSigners();
|
|
97
|
+
console.log("Deploying with:", deployer.address);
|
|
98
|
+
|
|
99
|
+
const Token = await ethers.getContractFactory("MyToken");
|
|
100
|
+
const token = await Token.deploy();
|
|
101
|
+
// ↑ ArcSign Dashboard will show approval request
|
|
102
|
+
|
|
103
|
+
await token.waitForDeployment();
|
|
104
|
+
console.log("Token deployed to:", await token.getAddress());
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
main().catch(console.error);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Session Mode (Optional)
|
|
111
|
+
|
|
112
|
+
For rapid testnet development, enable Session Mode in ArcSign Dashboard:
|
|
113
|
+
|
|
114
|
+
1. Go to Developer Mode → Session Settings
|
|
115
|
+
2. Enable "Session Mode"
|
|
116
|
+
3. Testnet transactions will auto-sign for 30 minutes
|
|
117
|
+
|
|
118
|
+
**Security**: Mainnet transactions always require manual confirmation.
|
|
119
|
+
|
|
120
|
+
## Supported Networks
|
|
121
|
+
|
|
122
|
+
The plugin works with any EVM-compatible network:
|
|
123
|
+
|
|
124
|
+
- Ethereum (mainnet, sepolia, goerli)
|
|
125
|
+
- BNB Chain (BSC mainnet, testnet)
|
|
126
|
+
- Polygon (mainnet, mumbai)
|
|
127
|
+
- Arbitrum
|
|
128
|
+
- Optimism
|
|
129
|
+
- Base
|
|
130
|
+
- And any custom EVM chain
|
|
131
|
+
|
|
132
|
+
## API Reference
|
|
133
|
+
|
|
134
|
+
### `hre.arcsign`
|
|
135
|
+
|
|
136
|
+
The plugin extends Hardhat's runtime environment with an `arcsign` namespace:
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// Check connection status
|
|
140
|
+
const isConnected = await hre.arcsign.isConnected();
|
|
141
|
+
|
|
142
|
+
// Get available accounts
|
|
143
|
+
const accounts = await hre.arcsign.getAccounts();
|
|
144
|
+
|
|
145
|
+
// Get the underlying provider
|
|
146
|
+
const provider = hre.arcsign.provider;
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Session Management
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
// Get session status
|
|
153
|
+
const session = await hre.arcsign.provider.getSession();
|
|
154
|
+
|
|
155
|
+
// Create a session (for CLI tools)
|
|
156
|
+
await hre.arcsign.provider.createSession({
|
|
157
|
+
walletId: "wallet-id",
|
|
158
|
+
durationMinutes: 30,
|
|
159
|
+
trustedNetworks: ["sepolia", "bsc-testnet"],
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// End session
|
|
163
|
+
await hre.arcsign.provider.endSession();
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Contract Verification
|
|
167
|
+
|
|
168
|
+
ArcSign automatically injects Block Explorer API keys when you run `npx hardhat verify`. No need to configure API keys in `.env` files!
|
|
169
|
+
|
|
170
|
+
### Setup API Keys
|
|
171
|
+
|
|
172
|
+
1. Open ArcSign Dashboard
|
|
173
|
+
2. Go to Developer Mode → Settings tab
|
|
174
|
+
3. Add your Block Explorer API keys (Etherscan, BSCScan, etc.)
|
|
175
|
+
|
|
176
|
+
### Usage
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
# Deploy your contract
|
|
180
|
+
npx hardhat run scripts/deploy.ts --network bscTestnet
|
|
181
|
+
|
|
182
|
+
# Verify - API key is auto-injected from ArcSign!
|
|
183
|
+
npx hardhat verify --network bscTestnet 0xYourContractAddress "constructor" "args"
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Terminal output:
|
|
187
|
+
|
|
188
|
+
```text
|
|
189
|
+
[ArcSign] Network "bscTestnet" will use ArcSign wallet for signing
|
|
190
|
+
[ArcSign] Checking for bscscan API key...
|
|
191
|
+
[ArcSign] Found bscscan API key, injecting into config...
|
|
192
|
+
[ArcSign] Injected API key successfully
|
|
193
|
+
Successfully verified contract MyContract on the block explorer.
|
|
194
|
+
https://testnet.bscscan.com/address/0xYourContractAddress#code
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Supported Block Explorers
|
|
198
|
+
|
|
199
|
+
| Explorer | Networks | Get API Key |
|
|
200
|
+
|----------|----------|-------------|
|
|
201
|
+
| Etherscan | Ethereum, Sepolia | [etherscan.io/apis](https://etherscan.io/apis) |
|
|
202
|
+
| BSCScan | BSC, BSC Testnet | [bscscan.com/apis](https://bscscan.com/apis) |
|
|
203
|
+
| Polygonscan | Polygon, Mumbai, Amoy | [polygonscan.com/apis](https://polygonscan.com/apis) |
|
|
204
|
+
| Arbiscan | Arbitrum, Arbitrum Sepolia | [arbiscan.io/apis](https://arbiscan.io/apis) |
|
|
205
|
+
| Optimism | Optimism, Optimism Sepolia | [optimistic.etherscan.io/apis](https://optimistic.etherscan.io/apis) |
|
|
206
|
+
| Basescan | Base, Base Sepolia | [basescan.org/apis](https://basescan.org/apis) |
|
|
207
|
+
| Snowtrace | Avalanche, Avalanche Fuji | [snowtrace.io/apis](https://snowtrace.io/apis) |
|
|
208
|
+
|
|
209
|
+
### Fallback Behavior
|
|
210
|
+
|
|
211
|
+
If ArcSign Dashboard is not running or no API key is configured:
|
|
212
|
+
|
|
213
|
+
- The plugin falls back to `hardhat.config.js` etherscan settings
|
|
214
|
+
- You can still use traditional `.env` configuration as a backup
|
|
215
|
+
|
|
216
|
+
## Troubleshooting
|
|
217
|
+
|
|
218
|
+
### "Failed to connect to ArcSign"
|
|
219
|
+
|
|
220
|
+
Make sure:
|
|
221
|
+
1. ArcSign Dashboard is running
|
|
222
|
+
2. You're on the correct version (check with ping)
|
|
223
|
+
3. No firewall is blocking localhost:9527
|
|
224
|
+
|
|
225
|
+
### "No accounts available"
|
|
226
|
+
|
|
227
|
+
Make sure:
|
|
228
|
+
1. A wallet is unlocked in ArcSign Dashboard
|
|
229
|
+
2. The wallet has at least one address
|
|
230
|
+
3. You're in Developer Mode
|
|
231
|
+
|
|
232
|
+
### "Transaction confirmation timed out"
|
|
233
|
+
|
|
234
|
+
Transactions require approval within 5 minutes. Check ArcSign Dashboard for pending requests.
|
|
235
|
+
|
|
236
|
+
## Security
|
|
237
|
+
|
|
238
|
+
- Private keys never leave your USB device
|
|
239
|
+
- All transactions require visual confirmation (except testnet in session mode)
|
|
240
|
+
- The plugin only communicates with localhost (127.0.0.1)
|
|
241
|
+
- No data is sent to external servers
|
|
242
|
+
|
|
243
|
+
## License
|
|
244
|
+
|
|
245
|
+
MIT
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ArcSign WebSocket Client
|
|
3
|
+
*
|
|
4
|
+
* Handles communication with the ArcSign Dashboard via WebSocket.
|
|
5
|
+
*/
|
|
6
|
+
export interface WsRequest {
|
|
7
|
+
id: number;
|
|
8
|
+
method: string;
|
|
9
|
+
params?: Record<string, unknown>;
|
|
10
|
+
}
|
|
11
|
+
export interface WsResponse {
|
|
12
|
+
id: number;
|
|
13
|
+
success: boolean;
|
|
14
|
+
result?: unknown;
|
|
15
|
+
error?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface DevContext {
|
|
18
|
+
script_name?: string;
|
|
19
|
+
project_path?: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
is_dev_wallet?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface TransactionParams {
|
|
24
|
+
from: string;
|
|
25
|
+
to: string;
|
|
26
|
+
data: string;
|
|
27
|
+
value?: string;
|
|
28
|
+
gas?: string;
|
|
29
|
+
gasPrice?: string;
|
|
30
|
+
maxFeePerGas?: string;
|
|
31
|
+
maxPriorityFeePerGas?: string;
|
|
32
|
+
chainId: number;
|
|
33
|
+
nonce?: number;
|
|
34
|
+
context?: DevContext;
|
|
35
|
+
}
|
|
36
|
+
export declare class ArcSignClient {
|
|
37
|
+
private ws;
|
|
38
|
+
private url;
|
|
39
|
+
private requestId;
|
|
40
|
+
private pendingRequests;
|
|
41
|
+
private connectPromise;
|
|
42
|
+
private reconnectAttempts;
|
|
43
|
+
private maxReconnectAttempts;
|
|
44
|
+
constructor(url?: string);
|
|
45
|
+
/**
|
|
46
|
+
* Connect to ArcSign Dashboard
|
|
47
|
+
*/
|
|
48
|
+
connect(): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Disconnect from ArcSign Dashboard
|
|
51
|
+
*/
|
|
52
|
+
disconnect(): void;
|
|
53
|
+
/**
|
|
54
|
+
* Check if connected
|
|
55
|
+
*/
|
|
56
|
+
isConnected(): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Send a request and wait for response
|
|
59
|
+
*/
|
|
60
|
+
private sendRequest;
|
|
61
|
+
/**
|
|
62
|
+
* Ping the server
|
|
63
|
+
*/
|
|
64
|
+
ping(): Promise<{
|
|
65
|
+
status: string;
|
|
66
|
+
version: string;
|
|
67
|
+
wallet: string;
|
|
68
|
+
}>;
|
|
69
|
+
/**
|
|
70
|
+
* Get available accounts
|
|
71
|
+
*/
|
|
72
|
+
getAccounts(): Promise<{
|
|
73
|
+
accounts: string[];
|
|
74
|
+
chainId: number;
|
|
75
|
+
}>;
|
|
76
|
+
/**
|
|
77
|
+
* Sign a transaction (developer mode)
|
|
78
|
+
*/
|
|
79
|
+
devSignTransaction(params: TransactionParams): Promise<{
|
|
80
|
+
status: string;
|
|
81
|
+
tx_hash?: string;
|
|
82
|
+
signed_tx?: string;
|
|
83
|
+
network?: string;
|
|
84
|
+
}>;
|
|
85
|
+
/**
|
|
86
|
+
* Personal sign (EIP-191)
|
|
87
|
+
*/
|
|
88
|
+
personalSign(address: string, message: string, context?: DevContext): Promise<{
|
|
89
|
+
signature: string;
|
|
90
|
+
}>;
|
|
91
|
+
/**
|
|
92
|
+
* Sign typed data (EIP-712)
|
|
93
|
+
*/
|
|
94
|
+
signTypedData(address: string, typedData: unknown, context?: DevContext): Promise<{
|
|
95
|
+
signature: string;
|
|
96
|
+
}>;
|
|
97
|
+
/**
|
|
98
|
+
* Get developer session status
|
|
99
|
+
*/
|
|
100
|
+
getDevSession(): Promise<{
|
|
101
|
+
active: boolean;
|
|
102
|
+
session?: {
|
|
103
|
+
enabled: boolean;
|
|
104
|
+
expires_at: number;
|
|
105
|
+
trusted_networks: string[];
|
|
106
|
+
sign_count: number;
|
|
107
|
+
};
|
|
108
|
+
remaining_ms?: number;
|
|
109
|
+
message?: string;
|
|
110
|
+
}>;
|
|
111
|
+
/**
|
|
112
|
+
* Create a developer session
|
|
113
|
+
*/
|
|
114
|
+
createDevSession(params: {
|
|
115
|
+
wallet_id: string;
|
|
116
|
+
duration_minutes?: number;
|
|
117
|
+
trusted_networks?: string[];
|
|
118
|
+
max_gas_limit?: string;
|
|
119
|
+
}): Promise<{
|
|
120
|
+
status: string;
|
|
121
|
+
session: {
|
|
122
|
+
enabled: boolean;
|
|
123
|
+
created_at: number;
|
|
124
|
+
expires_at: number;
|
|
125
|
+
trusted_networks: string[];
|
|
126
|
+
sign_count: number;
|
|
127
|
+
};
|
|
128
|
+
}>;
|
|
129
|
+
/**
|
|
130
|
+
* End the developer session
|
|
131
|
+
*/
|
|
132
|
+
endDevSession(): Promise<{
|
|
133
|
+
status: string;
|
|
134
|
+
sign_count?: number;
|
|
135
|
+
message?: string;
|
|
136
|
+
}>;
|
|
137
|
+
/**
|
|
138
|
+
* Get block explorer API key from ArcSign settings
|
|
139
|
+
*
|
|
140
|
+
* The USB path is retrieved from the Dashboard's server state,
|
|
141
|
+
* so the plugin doesn't need to know it.
|
|
142
|
+
*
|
|
143
|
+
* @param explorer - Explorer type: 'etherscan' | 'bscscan' | 'polygonscan' | 'arbiscan' | 'optimism' | 'basescan' | 'snowtrace'
|
|
144
|
+
* @returns API key or null if not configured
|
|
145
|
+
*/
|
|
146
|
+
getExplorerApiKey(explorer: string): Promise<string | null>;
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=ArcSignClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ArcSignClient.d.ts","sourceRoot":"","sources":["../src/ArcSignClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ArcSign WebSocket Client
|
|
4
|
+
*
|
|
5
|
+
* Handles communication with the ArcSign Dashboard via WebSocket.
|
|
6
|
+
*/
|
|
7
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
+
};
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.ArcSignClient = void 0;
|
|
12
|
+
const ws_1 = __importDefault(require("ws"));
|
|
13
|
+
class ArcSignClient {
|
|
14
|
+
constructor(url = "ws://127.0.0.1:9527") {
|
|
15
|
+
this.ws = null;
|
|
16
|
+
this.requestId = 0;
|
|
17
|
+
this.pendingRequests = new Map();
|
|
18
|
+
this.connectPromise = null;
|
|
19
|
+
this.reconnectAttempts = 0;
|
|
20
|
+
this.maxReconnectAttempts = 3;
|
|
21
|
+
this.url = url;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Connect to ArcSign Dashboard
|
|
25
|
+
*/
|
|
26
|
+
async connect() {
|
|
27
|
+
if (this.ws?.readyState === ws_1.default.OPEN) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (this.connectPromise) {
|
|
31
|
+
return this.connectPromise;
|
|
32
|
+
}
|
|
33
|
+
this.connectPromise = new Promise((resolve, reject) => {
|
|
34
|
+
console.log(`[ArcSign] Connecting to ${this.url}...`);
|
|
35
|
+
this.ws = new ws_1.default(this.url);
|
|
36
|
+
this.ws.on("open", () => {
|
|
37
|
+
console.log("[ArcSign] Connected to ArcSign Dashboard");
|
|
38
|
+
this.reconnectAttempts = 0;
|
|
39
|
+
this.connectPromise = null;
|
|
40
|
+
resolve();
|
|
41
|
+
});
|
|
42
|
+
this.ws.on("message", (data) => {
|
|
43
|
+
try {
|
|
44
|
+
const response = JSON.parse(data.toString());
|
|
45
|
+
const pending = this.pendingRequests.get(response.id);
|
|
46
|
+
if (pending) {
|
|
47
|
+
this.pendingRequests.delete(response.id);
|
|
48
|
+
pending.resolve(response);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
console.error("[ArcSign] Failed to parse response:", err);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
this.ws.on("error", (err) => {
|
|
56
|
+
console.error("[ArcSign] WebSocket error:", err.message);
|
|
57
|
+
this.connectPromise = null;
|
|
58
|
+
reject(new Error(`Failed to connect to ArcSign: ${err.message}`));
|
|
59
|
+
});
|
|
60
|
+
this.ws.on("close", () => {
|
|
61
|
+
console.log("[ArcSign] Connection closed");
|
|
62
|
+
this.ws = null;
|
|
63
|
+
this.connectPromise = null;
|
|
64
|
+
// Reject all pending requests
|
|
65
|
+
for (const [id, pending] of this.pendingRequests) {
|
|
66
|
+
pending.reject(new Error("Connection closed"));
|
|
67
|
+
this.pendingRequests.delete(id);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
// Connection timeout
|
|
71
|
+
setTimeout(() => {
|
|
72
|
+
if (this.ws?.readyState !== ws_1.default.OPEN) {
|
|
73
|
+
this.ws?.close();
|
|
74
|
+
this.connectPromise = null;
|
|
75
|
+
reject(new Error("Connection timeout - is ArcSign Dashboard running?"));
|
|
76
|
+
}
|
|
77
|
+
}, 10000);
|
|
78
|
+
});
|
|
79
|
+
return this.connectPromise;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Disconnect from ArcSign Dashboard
|
|
83
|
+
*/
|
|
84
|
+
disconnect() {
|
|
85
|
+
if (this.ws) {
|
|
86
|
+
this.ws.close();
|
|
87
|
+
this.ws = null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Check if connected
|
|
92
|
+
*/
|
|
93
|
+
isConnected() {
|
|
94
|
+
return this.ws?.readyState === ws_1.default.OPEN;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Send a request and wait for response
|
|
98
|
+
*/
|
|
99
|
+
async sendRequest(method, params) {
|
|
100
|
+
if (!this.isConnected()) {
|
|
101
|
+
await this.connect();
|
|
102
|
+
}
|
|
103
|
+
const id = ++this.requestId;
|
|
104
|
+
const request = { id, method, params };
|
|
105
|
+
return new Promise((resolve, reject) => {
|
|
106
|
+
// Set timeout for response
|
|
107
|
+
const timeout = setTimeout(() => {
|
|
108
|
+
this.pendingRequests.delete(id);
|
|
109
|
+
reject(new Error(`Request timeout: ${method}`));
|
|
110
|
+
}, 300000); // 5 minute timeout for signing
|
|
111
|
+
this.pendingRequests.set(id, {
|
|
112
|
+
resolve: (response) => {
|
|
113
|
+
clearTimeout(timeout);
|
|
114
|
+
if (response.success) {
|
|
115
|
+
resolve(response.result);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
reject(new Error(response.error || "Request failed"));
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
reject: (err) => {
|
|
122
|
+
clearTimeout(timeout);
|
|
123
|
+
reject(err);
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
this.ws.send(JSON.stringify(request));
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Ping the server
|
|
131
|
+
*/
|
|
132
|
+
async ping() {
|
|
133
|
+
return this.sendRequest("ping");
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Get available accounts
|
|
137
|
+
*/
|
|
138
|
+
async getAccounts() {
|
|
139
|
+
return this.sendRequest("get_accounts");
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Sign a transaction (developer mode)
|
|
143
|
+
*/
|
|
144
|
+
async devSignTransaction(params) {
|
|
145
|
+
return this.sendRequest("dev_sign_transaction", {
|
|
146
|
+
from: params.from,
|
|
147
|
+
to: params.to,
|
|
148
|
+
data: params.data,
|
|
149
|
+
value: params.value || "0x0",
|
|
150
|
+
gas: params.gas,
|
|
151
|
+
gas_price: params.gasPrice,
|
|
152
|
+
max_fee_per_gas: params.maxFeePerGas,
|
|
153
|
+
max_priority_fee_per_gas: params.maxPriorityFeePerGas,
|
|
154
|
+
chain_id: params.chainId,
|
|
155
|
+
nonce: params.nonce,
|
|
156
|
+
context: params.context,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Personal sign (EIP-191)
|
|
161
|
+
*/
|
|
162
|
+
async personalSign(address, message, context) {
|
|
163
|
+
return this.sendRequest("personal_sign", {
|
|
164
|
+
address,
|
|
165
|
+
message,
|
|
166
|
+
context,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Sign typed data (EIP-712)
|
|
171
|
+
*/
|
|
172
|
+
async signTypedData(address, typedData, context) {
|
|
173
|
+
return this.sendRequest("sign_typed_data_v4", {
|
|
174
|
+
address,
|
|
175
|
+
typed_data: typedData,
|
|
176
|
+
context,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Get developer session status
|
|
181
|
+
*/
|
|
182
|
+
async getDevSession() {
|
|
183
|
+
return this.sendRequest("dev_get_session");
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Create a developer session
|
|
187
|
+
*/
|
|
188
|
+
async createDevSession(params) {
|
|
189
|
+
return this.sendRequest("dev_create_session", params);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* End the developer session
|
|
193
|
+
*/
|
|
194
|
+
async endDevSession() {
|
|
195
|
+
return this.sendRequest("dev_end_session");
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Get block explorer API key from ArcSign settings
|
|
199
|
+
*
|
|
200
|
+
* The USB path is retrieved from the Dashboard's server state,
|
|
201
|
+
* so the plugin doesn't need to know it.
|
|
202
|
+
*
|
|
203
|
+
* @param explorer - Explorer type: 'etherscan' | 'bscscan' | 'polygonscan' | 'arbiscan' | 'optimism' | 'basescan' | 'snowtrace'
|
|
204
|
+
* @returns API key or null if not configured
|
|
205
|
+
*/
|
|
206
|
+
async getExplorerApiKey(explorer) {
|
|
207
|
+
try {
|
|
208
|
+
const result = await this.sendRequest("get_explorer_api_key", {
|
|
209
|
+
explorer,
|
|
210
|
+
});
|
|
211
|
+
return result.api_key;
|
|
212
|
+
}
|
|
213
|
+
catch (err) {
|
|
214
|
+
console.error(`[ArcSign] Failed to get ${explorer} API key:`, err);
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
exports.ArcSignClient = ArcSignClient;
|
|
220
|
+
//# sourceMappingURL=ArcSignClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ArcSignClient.js","sourceRoot":"","sources":["../src/ArcSignClient.ts"],"names":[],"mappings":"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"}
|