@spectratools/tx-shared 0.5.1 → 0.5.2
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 +128 -36
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,13 +16,13 @@ This package is designed for consuming CLIs (for example `@spectratools/assembly
|
|
|
16
16
|
pnpm add @spectratools/tx-shared
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
## Signer
|
|
19
|
+
## Signer providers
|
|
20
20
|
|
|
21
21
|
`resolveSigner()` uses deterministic precedence:
|
|
22
22
|
|
|
23
23
|
1. `privateKey`
|
|
24
24
|
2. `keystorePath` (+ `keystorePassword`)
|
|
25
|
-
3. `privy` / `PRIVY_*`
|
|
25
|
+
3. Privy (`privy` flag and/or `PRIVY_*` env)
|
|
26
26
|
|
|
27
27
|
If no provider is configured, it throws `TxError` with code `SIGNER_NOT_CONFIGURED`.
|
|
28
28
|
|
|
@@ -50,17 +50,36 @@ export KEYSTORE_PASSWORD="..."
|
|
|
50
50
|
|
|
51
51
|
#### 3) Privy
|
|
52
52
|
|
|
53
|
+
Required env:
|
|
54
|
+
|
|
53
55
|
```bash
|
|
54
56
|
export PRIVY_APP_ID="..."
|
|
55
57
|
export PRIVY_WALLET_ID="..."
|
|
56
|
-
export PRIVY_AUTHORIZATION_KEY="
|
|
58
|
+
export PRIVY_AUTHORIZATION_KEY="wallet-auth:<base64-pkcs8-p256-private-key>"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Optional env:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
export PRIVY_API_URL="https://api.sandbox.privy.io"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Optional CLI flags (from `signerFlagSchema`):
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
--privy
|
|
71
|
+
--privy-api-url "https://api.sandbox.privy.io"
|
|
57
72
|
```
|
|
58
73
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
74
|
+
Notes:
|
|
75
|
+
|
|
76
|
+
- `--privy` is optional when `PRIVY_APP_ID` + `PRIVY_WALLET_ID` + `PRIVY_AUTHORIZATION_KEY` are present; `resolveSigner()` auto-detects Privy config.
|
|
77
|
+
- `--privy-api-url` / `PRIVY_API_URL` override the default `https://api.privy.io`.
|
|
78
|
+
- Privy account methods are mapped as:
|
|
79
|
+
- `sendTransaction` → `eth_sendTransaction`
|
|
80
|
+
- `signMessage` → `personal_sign`
|
|
81
|
+
- `signTypedData` → `eth_signTypedData_v4`
|
|
82
|
+
- `signTransaction` → `eth_signTransaction` (returns serialized tx hex; broadcast separately if needed)
|
|
64
83
|
|
|
65
84
|
## `resolveSigner()` usage
|
|
66
85
|
|
|
@@ -74,7 +93,7 @@ const signer = await resolveSigner({
|
|
|
74
93
|
});
|
|
75
94
|
|
|
76
95
|
console.log(signer.provider); // 'private-key' | 'keystore' | 'privy'
|
|
77
|
-
console.log(signer.address);
|
|
96
|
+
console.log(signer.address); // 0x...
|
|
78
97
|
```
|
|
79
98
|
|
|
80
99
|
### From shared CLI flags + env
|
|
@@ -88,8 +107,8 @@ import {
|
|
|
88
107
|
} from '@spectratools/tx-shared';
|
|
89
108
|
|
|
90
109
|
const flags = signerFlagSchema.parse({
|
|
91
|
-
|
|
92
|
-
privy:
|
|
110
|
+
privy: true,
|
|
111
|
+
'privy-api-url': process.env.PRIVY_API_URL,
|
|
93
112
|
});
|
|
94
113
|
|
|
95
114
|
const env = signerEnvSchema.parse(process.env);
|
|
@@ -102,46 +121,108 @@ const signer = await resolveSigner(toSignerOptions(flags, env));
|
|
|
102
121
|
|
|
103
122
|
1. estimate gas (`estimateContractGas`)
|
|
104
123
|
2. simulate (`simulateContract`)
|
|
105
|
-
3.
|
|
106
|
-
4.
|
|
107
|
-
5.
|
|
124
|
+
3. preflight Privy policies (when signer is Privy-backed)
|
|
125
|
+
4. submit (`writeContract`) unless `dryRun: true`
|
|
126
|
+
5. wait for receipt (`waitForTransactionReceipt`)
|
|
127
|
+
6. normalize result into a shared output shape
|
|
108
128
|
|
|
109
|
-
###
|
|
129
|
+
### Privy transaction flow example (dry-run + live)
|
|
110
130
|
|
|
111
131
|
```ts
|
|
112
|
-
import {
|
|
132
|
+
import { http, createPublicClient, createWalletClient, parseAbi } from 'viem';
|
|
133
|
+
import {
|
|
134
|
+
abstractMainnet,
|
|
135
|
+
executeTx,
|
|
136
|
+
resolveSigner,
|
|
137
|
+
signerEnvSchema,
|
|
138
|
+
signerFlagSchema,
|
|
139
|
+
toSignerOptions,
|
|
140
|
+
} from '@spectratools/tx-shared';
|
|
141
|
+
|
|
142
|
+
const flags = signerFlagSchema.parse({
|
|
143
|
+
privy: true,
|
|
144
|
+
'privy-api-url': process.env.PRIVY_API_URL,
|
|
145
|
+
});
|
|
146
|
+
const env = signerEnvSchema.parse(process.env);
|
|
147
|
+
const signer = await resolveSigner(toSignerOptions(flags, env));
|
|
148
|
+
|
|
149
|
+
const publicClient = createPublicClient({
|
|
150
|
+
chain: abstractMainnet,
|
|
151
|
+
transport: http(process.env.ABSTRACT_RPC_URL),
|
|
152
|
+
});
|
|
153
|
+
const walletClient = createWalletClient({
|
|
154
|
+
account: signer.account,
|
|
155
|
+
chain: abstractMainnet,
|
|
156
|
+
transport: http(process.env.ABSTRACT_RPC_URL),
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
const registryAbi = parseAbi(['function register() payable']);
|
|
113
160
|
|
|
114
|
-
const
|
|
161
|
+
const dryRun = await executeTx({
|
|
115
162
|
publicClient,
|
|
116
163
|
walletClient,
|
|
117
164
|
account: signer.account,
|
|
118
|
-
address,
|
|
119
|
-
abi,
|
|
165
|
+
address: '0x1111111111111111111111111111111111111111',
|
|
166
|
+
abi: registryAbi,
|
|
120
167
|
functionName: 'register',
|
|
121
|
-
value:
|
|
168
|
+
value: 1000000000000000n,
|
|
169
|
+
dryRun: true,
|
|
122
170
|
});
|
|
123
171
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
172
|
+
if (dryRun.status === 'dry-run') {
|
|
173
|
+
console.log(dryRun.estimatedGas.toString());
|
|
174
|
+
console.log(dryRun.privyPolicy?.status); // 'allowed' | 'blocked'
|
|
175
|
+
}
|
|
128
176
|
|
|
129
|
-
|
|
130
|
-
const result = await executeTx({
|
|
177
|
+
const live = await executeTx({
|
|
131
178
|
publicClient,
|
|
132
179
|
walletClient,
|
|
133
180
|
account: signer.account,
|
|
134
|
-
address,
|
|
135
|
-
abi,
|
|
181
|
+
address: '0x1111111111111111111111111111111111111111',
|
|
182
|
+
abi: registryAbi,
|
|
136
183
|
functionName: 'register',
|
|
137
|
-
value:
|
|
138
|
-
dryRun: true,
|
|
184
|
+
value: 1000000000000000n,
|
|
139
185
|
});
|
|
140
186
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
187
|
+
console.log(live.hash, live.status);
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Privy signing flow example
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
const signer = await resolveSigner({
|
|
194
|
+
privy: true,
|
|
195
|
+
privyAppId: process.env.PRIVY_APP_ID,
|
|
196
|
+
privyWalletId: process.env.PRIVY_WALLET_ID,
|
|
197
|
+
privyAuthorizationKey: process.env.PRIVY_AUTHORIZATION_KEY,
|
|
198
|
+
privyApiUrl: process.env.PRIVY_API_URL,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
if (signer.provider !== 'privy') throw new Error('Expected Privy signer');
|
|
202
|
+
|
|
203
|
+
const messageSig = await signer.account.signMessage({ message: 'hello from tx-shared' });
|
|
204
|
+
|
|
205
|
+
const typedDataSig = await signer.account.signTypedData({
|
|
206
|
+
domain: { name: 'SpectraTools', version: '1', chainId: 2741 },
|
|
207
|
+
primaryType: 'Ping',
|
|
208
|
+
types: {
|
|
209
|
+
Ping: [{ name: 'message', type: 'string' }],
|
|
210
|
+
},
|
|
211
|
+
message: { message: 'privy typed data' },
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const signedTxHex = await signer.account.signTransaction({
|
|
215
|
+
to: '0x1111111111111111111111111111111111111111',
|
|
216
|
+
chainId: 2741,
|
|
217
|
+
nonce: 1,
|
|
218
|
+
gas: 21000n,
|
|
219
|
+
maxFeePerGas: 1000000000n,
|
|
220
|
+
maxPriorityFeePerGas: 1000000000n,
|
|
221
|
+
value: 0n,
|
|
222
|
+
data: '0x',
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
console.log(messageSig, typedDataSig, signedTxHex);
|
|
145
226
|
```
|
|
146
227
|
|
|
147
228
|
## Structured errors
|
|
@@ -155,6 +236,8 @@ if (result.status === 'dry-run') {
|
|
|
155
236
|
- `SIGNER_NOT_CONFIGURED`
|
|
156
237
|
- `KEYSTORE_DECRYPT_FAILED`
|
|
157
238
|
- `PRIVY_AUTH_FAILED`
|
|
239
|
+
- `PRIVY_TRANSPORT_FAILED`
|
|
240
|
+
- `PRIVY_POLICY_BLOCKED`
|
|
158
241
|
|
|
159
242
|
```ts
|
|
160
243
|
import { TxError } from '@spectratools/tx-shared';
|
|
@@ -171,6 +254,8 @@ try {
|
|
|
171
254
|
case 'SIGNER_NOT_CONFIGURED':
|
|
172
255
|
case 'KEYSTORE_DECRYPT_FAILED':
|
|
173
256
|
case 'PRIVY_AUTH_FAILED':
|
|
257
|
+
case 'PRIVY_TRANSPORT_FAILED':
|
|
258
|
+
case 'PRIVY_POLICY_BLOCKED':
|
|
174
259
|
throw error;
|
|
175
260
|
}
|
|
176
261
|
}
|
|
@@ -188,8 +273,15 @@ try {
|
|
|
188
273
|
- verify file path and password
|
|
189
274
|
- ensure keystore is valid V3 JSON
|
|
190
275
|
- **`PRIVY_AUTH_FAILED`**
|
|
191
|
-
- verify
|
|
192
|
-
-
|
|
276
|
+
- verify required env: `PRIVY_APP_ID`, `PRIVY_WALLET_ID`, `PRIVY_AUTHORIZATION_KEY`
|
|
277
|
+
- validate `PRIVY_AUTHORIZATION_KEY` format: `wallet-auth:<base64-pkcs8-p256-private-key>`
|
|
278
|
+
- verify `PRIVY_API_URL` override points at a valid Privy API host
|
|
279
|
+
- **`PRIVY_TRANSPORT_FAILED`**
|
|
280
|
+
- check Privy API availability and credentials
|
|
281
|
+
- inspect upstream intent failure payloads for status + reason
|
|
282
|
+
- **`PRIVY_POLICY_BLOCKED`**
|
|
283
|
+
- review active wallet policy allowlists and native value caps
|
|
284
|
+
- use `dryRun: true` and inspect `result.privyPolicy` before broadcasting
|
|
193
285
|
- **`GAS_ESTIMATION_FAILED` / `TX_REVERTED`**
|
|
194
286
|
- validate function args and `value`
|
|
195
287
|
- run with `dryRun: true` first
|