Skip to content

Commit 85dd9c1

Browse files
CodySearsOSryanio
andcommitted
Release v0.3.1
Origin-SHA: aab183ad42e10a44f34d71f4ecd1ef027be8d8c8 Co-authored-by: Ryan Ghods <ryan@ryanio.com>
1 parent 8de4537 commit 85dd9c1

8 files changed

Lines changed: 73 additions & 14 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pnpm run type-check # TypeScript type checking
2626
| `src/adapters/fireblocks.ts` | Fireblocks enterprise MPC custody adapter |
2727
| `src/adapters/private-key.ts` | Raw private key adapter (dev/testing) |
2828
| `src/factory.ts` | `createWalletFromEnv()` — auto-detection from env vars |
29+
| `src/util/eip712.ts` | EIP-712 typed data hashing utilities used by adapters for `signTypedData` |
2930
| `src/bridges/viem.ts` | Bridge: WalletAdapter → viem WalletClient |
3031
| `src/bridges/ethers.ts` | Bridge: WalletAdapter → ethers.js Signer |
3132
| `src/__tests__/` | Vitest test suite |

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# @opensea/wallet-adapters
22

3+
## 0.3.1
4+
5+
### Patch Changes
6+
7+
- c982513: Make `RPC_URL` optional for signing-only workflows. The factory and the private-key adapter no longer require `RPC_URL` when an adapter is only used to sign (not broadcast) transactions, so a key configured purely for signing no longer fails to initialize. A read provider is created lazily and only when a chain operation actually needs one.
8+
39
## 0.3.0
410

511
### Minor Changes

README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
Provider-agnostic wallet adapters for signing and sending transactions across managed and local backends.
44

5+
## Why wallet-adapters?
6+
7+
`@opensea/wallet-adapters` is the shared wallet layer used across the OpenSea developer toolchain. Every package that needs to sign transactions depends on the same `WalletAdapter` interface:
8+
9+
- [`@opensea/sdk`](https://github.com/ProjectOpenSea/opensea-js) — TypeScript SDK for buying, selling, and managing NFTs
10+
- [`@opensea/cli`](https://github.com/ProjectOpenSea/opensea-cli) — command-line interface for the OpenSea API
11+
- [`@opensea/tool-sdk`](https://github.com/ProjectOpenSea/tool-sdk) — SDK for building ERC-8257 AI agent tools
12+
- [`opensea-skill`](https://github.com/ProjectOpenSea/opensea-skill) — modular AI agent skills for Claude, Devin, and other assistants
13+
14+
By implementing the `WalletAdapter` interface once, a new wallet provider automatically works everywhere — the CLI, the SDK, AI agent tool execution, and any future package that depends on this library.
15+
516
## Features
617

718
- **Provider-agnostic interface** — unified `WalletAdapter` abstraction with capabilities declaration
@@ -151,6 +162,33 @@ wallet.onResponse = (method, result, durationMs) => {
151162
}
152163
```
153164

165+
## Adding a New Provider
166+
167+
To add a wallet provider, implement the `WalletAdapter` interface:
168+
169+
```ts
170+
import type { WalletAdapter, WalletCapabilities } from "@opensea/wallet-adapters"
171+
172+
export class MyProviderAdapter implements WalletAdapter {
173+
readonly name = "my-provider"
174+
readonly capabilities: WalletCapabilities = {
175+
signMessage: true,
176+
signTypedData: true,
177+
managedGas: true,
178+
managedNonce: true,
179+
}
180+
181+
async getAddress(): Promise<string> { /* ... */ }
182+
async sendTransaction(tx) { /* ... */ }
183+
async signMessage(request) { /* ... */ }
184+
async signTypedData(request) { /* ... */ }
185+
186+
static fromEnv(): MyProviderAdapter { /* ... */ }
187+
}
188+
```
189+
190+
Then register it in the `createWalletFromEnv()` factory in `src/factory.ts` so the CLI and tool-sdk auto-detect it from environment variables.
191+
154192
## License
155193

156194
MIT

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@opensea/wallet-adapters",
3-
"version": "0.3.0",
3+
"version": "0.3.1",
44
"type": "module",
55
"description": "Provider-agnostic wallet adapters for signing and sending transactions across managed and local backends",
66
"license": "MIT",

src/__tests__/adapters.test.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,14 +150,25 @@ describe("PrivateKeyAdapter", () => {
150150
)
151151
})
152152

153-
it("fromEnv throws when RPC_URL is missing", () => {
153+
it("fromEnv succeeds without RPC_URL (signing-only mode)", () => {
154154
process.env.PRIVATE_KEY = TEST_PRIVATE_KEY
155-
expect(() => PrivateKeyAdapter.fromEnv()).toThrow(
156-
"RPC_URL environment variable is required",
157-
)
155+
const adapter = PrivateKeyAdapter.fromEnv()
156+
expect(adapter.getRpcUrl()).toBeUndefined()
158157
delete process.env.PRIVATE_KEY
159158
})
160159

160+
it("sendTransaction throws when RPC_URL is not configured", async () => {
161+
const adapter = new PrivateKeyAdapter({ privateKey: TEST_PRIVATE_KEY })
162+
await expect(
163+
adapter.sendTransaction({
164+
to: "0x0000000000000000000000000000000000000001",
165+
data: "0x",
166+
value: "0",
167+
chainId: 1,
168+
}),
169+
).rejects.toThrow("RPC_URL is required for sending transactions")
170+
})
171+
161172
it("signMessage produces a valid 65-byte signature", async () => {
162173
const adapter = new PrivateKeyAdapter({
163174
privateKey: TEST_PRIVATE_KEY,

src/adapters/private-key.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88
*
99
* Required environment variables:
1010
* PRIVATE_KEY — Hex-encoded private key (with or without 0x prefix)
11-
* RPC_URL — JSON-RPC endpoint for broadcasting and gas estimation
1211
*
1312
* Optional:
13+
* RPC_URL — JSON-RPC endpoint for broadcasting and gas estimation
14+
* (required only when sending transactions)
1415
* WALLET_ADDRESS — Pre-computed address (skips derivation)
1516
*/
1617

@@ -30,7 +31,7 @@ import { bytesToHex, hexToBytes, rlpEncodeEip1559Tx } from "./turnkey.js"
3031

3132
export interface PrivateKeyConfig {
3233
privateKey: string
33-
rpcUrl: string
34+
rpcUrl?: string
3435
address?: string
3536
}
3637

@@ -59,9 +60,6 @@ export class PrivateKeyAdapter implements WalletAdapter {
5960
if (!privateKey) {
6061
throw new Error("PRIVATE_KEY environment variable is required")
6162
}
62-
if (!rpcUrl) {
63-
throw new Error("RPC_URL environment variable is required")
64-
}
6563

6664
const clean = privateKey.startsWith("0x") ? privateKey.slice(2) : privateKey
6765
if (!/^[0-9a-fA-F]{64}$/.test(clean)) {
@@ -77,7 +75,7 @@ export class PrivateKeyAdapter implements WalletAdapter {
7775
})
7876
}
7977

80-
getRpcUrl(): string {
78+
getRpcUrl(): string | undefined {
8179
return this.config.rpcUrl
8280
}
8381

@@ -99,7 +97,12 @@ export class PrivateKeyAdapter implements WalletAdapter {
9997
const startTime = Date.now()
10098

10199
const from = await this.getAddress()
102-
const { rpcUrl } = this.config
100+
const rpcUrl = this.config.rpcUrl
101+
if (!rpcUrl) {
102+
throw new Error(
103+
"RPC_URL is required for sending transactions. Set the RPC_URL environment variable or pass --rpc-url.",
104+
)
105+
}
103106

104107
let nonce: bigint
105108
let gasLimit: bigint

src/factory.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export function createWalletFromEnv(): WalletAdapter {
4242
" • Fireblocks: FIREBLOCKS_API_KEY, FIREBLOCKS_API_SECRET, FIREBLOCKS_VAULT_ID\n" +
4343
" • Turnkey: TURNKEY_API_PUBLIC_KEY, TURNKEY_API_PRIVATE_KEY, TURNKEY_ORGANIZATION_ID, TURNKEY_WALLET_ADDRESS, TURNKEY_RPC_URL\n" +
4444
" • Bankr: BANKR_API_KEY\n" +
45-
" • PrivateKey: PRIVATE_KEY, RPC_URL",
45+
" • PrivateKey: PRIVATE_KEY (optionally RPC_URL for sending transactions)",
4646
)
4747
}
4848

src/types/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ export interface WalletAdapter {
131131
getWalletInfo?(): Promise<WalletInfo>
132132

133133
/** Optional RPC URL for read operations (gas estimation, nonce, etc.) */
134-
getRpcUrl?(): string
134+
getRpcUrl?(): string | undefined
135135

136136
/** Optional hook called before each adapter request (for metrics/logging) */
137137
onRequest?: (method: string, params: unknown) => void

0 commit comments

Comments
 (0)