Skip to content

Commit 04e7997

Browse files
committed
Sync from opensea-devtools
1 parent e9b43a3 commit 04e7997

28 files changed

Lines changed: 3421 additions & 2 deletions

.github/workflows/ci.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
build-and-test:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v6
14+
- uses: actions/setup-node@v6
15+
with:
16+
node-version-file: .nvmrc
17+
- run: npm install
18+
- run: npm run build
19+
- run: npm run lint
20+
- run: npm run type-check
21+
- run: npm test
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: Publish npm package
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
build-before-publish:
7+
description: Run build and test before publishing
8+
type: boolean
9+
default: true
10+
stub-package-dir:
11+
description: Path to stub package directory (e.g. packages/opensea-js-stub)
12+
type: string
13+
default: ""
14+
node-version-file:
15+
description: Path to node version file
16+
type: string
17+
default: .nvmrc
18+
secrets:
19+
OPENSEA_API_KEY:
20+
required: false
21+
ALCHEMY_API_KEY:
22+
required: false
23+
24+
jobs:
25+
publish-npm:
26+
runs-on: ubuntu-latest
27+
permissions:
28+
id-token: write
29+
contents: read
30+
steps:
31+
- uses: actions/checkout@v6
32+
- uses: actions/setup-node@v6
33+
with:
34+
node-version-file: ${{ inputs.node-version-file }}
35+
registry-url: https://registry.npmjs.org/
36+
37+
# Use npm install (not npm ci) because the package-lock.json may be
38+
# out of sync after the sync-package workflow resolves workspace: protocols
39+
# and updates dependencies. npm ci requires exact lockfile match.
40+
#
41+
# --legacy-peer-deps mirrors pnpm's permissive peer-dep behavior used in the
42+
# monorepo. Without it, packages like api-types fail on openapi-typescript@^7
43+
# which declares a strict `typescript@^5.x` peer while we ship typescript@^6.
44+
- run: npm install --legacy-peer-deps
45+
46+
- if: ${{ inputs.build-before-publish }}
47+
run: npm run build
48+
49+
- if: ${{ inputs.build-before-publish }}
50+
run: npm test
51+
env:
52+
OPENSEA_API_KEY: ${{ secrets.OPENSEA_API_KEY }}
53+
ALCHEMY_API_KEY: ${{ secrets.ALCHEMY_API_KEY }}
54+
55+
- run: npm publish --provenance --access public
56+
57+
- if: ${{ inputs.stub-package-dir != '' }}
58+
run: npm publish --provenance --access public
59+
working-directory: ${{ inputs.stub-package-dir }}
60+
continue-on-error: true

.github/workflows/npm-publish.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name: Node.js Package
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
jobs:
8+
publish:
9+
uses: ./.github/workflows/npm-publish-reusable.yml
10+
secrets: inherit

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
24.14.1

AGENTS.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# wallet-adapters — Agent Conventions
2+
3+
Provider-agnostic wallet adapter library for signing and sending transactions across managed (Privy, Turnkey, Fireblocks) and local (private key) backends. Supports both viem and ethers.js via optional bridge utilities.
4+
5+
## Quick Reference
6+
7+
```bash
8+
cd packages/wallet-adapters
9+
pnpm install
10+
pnpm run build # Build with tsup
11+
pnpm run test # Run tests with Vitest
12+
pnpm run lint # Lint with Biome
13+
pnpm run format # Format with Biome
14+
pnpm run type-check # TypeScript type checking
15+
```
16+
17+
## Architecture
18+
19+
| Path | Role |
20+
|------|------|
21+
| `src/index.ts` | Library entry point — public exports |
22+
| `src/types/index.ts` | Core interfaces: `WalletAdapter`, `TransactionRequest`, `WalletCapabilities` |
23+
| `src/adapters/privy.ts` | Privy server-side wallet API adapter |
24+
| `src/adapters/turnkey.ts` | Turnkey HSM-backed signing with P-256 stamp auth |
25+
| `src/adapters/fireblocks.ts` | Fireblocks enterprise MPC custody adapter |
26+
| `src/adapters/private-key.ts` | Raw private key adapter (dev/testing) |
27+
| `src/factory.ts` | `createWalletFromEnv()` — auto-detection from env vars |
28+
| `src/bridges/viem.ts` | Bridge: WalletAdapter → viem WalletClient |
29+
| `src/bridges/ethers.ts` | Bridge: WalletAdapter → ethers.js Signer |
30+
| `src/__tests__/` | Vitest test suite |
31+
32+
## Design Principles
33+
34+
1. **Minimal runtime dependencies.** Only `@noble/hashes` and `@noble/curves` for private key operations. Managed adapters (Privy, Turnkey, Fireblocks) use Web Crypto APIs for their auth cryptography.
35+
36+
2. **Framework-agnostic core.** Adapters know nothing about viem or ethers.js. Bridge utilities (`./viem`, `./ethers`) are separate entry points with viem/ethers as optional peer dependencies.
37+
38+
3. **Capabilities-based interface.** Each adapter declares its `WalletCapabilities` (signMessage, signTypedData, managedGas, managedNonce). Consumers check capabilities before calling optional methods.
39+
40+
4. **Observability hooks.** All adapters support optional `onRequest` / `onResponse` callbacks for metrics, logging, and tracing.
41+
42+
5. **TransactionRequest is extensible.** Optional fields (`gas`, `nonce`, `maxFeePerGas`, `maxPriorityFeePerGas`) let callers pass pre-estimated values to avoid redundant RPC calls.
43+
44+
6. **Environment-based construction.** Each adapter has a `fromEnv()` static factory. `createWalletFromEnv()` auto-detects with priority: Privy > Fireblocks > Turnkey > PrivateKey.
45+
46+
## Review Checklist
47+
48+
When reviewing changes to this package, verify:
49+
50+
1. **Interface backward compatibility.** The `WalletAdapter` interface is consumed by `@opensea/cli` and `@opensea/tool-sdk`. New optional fields are fine; removing or changing required fields is a breaking change.
51+
52+
2. **Adapter parity.** All adapters must implement the full `WalletAdapter` interface and correctly declare their `capabilities`. Test coverage must verify both the happy path and error cases.
53+
54+
3. **Security of key material.** Private keys, API secrets, and signing keys must never be logged, included in error messages, or exposed in stack traces.
55+
56+
4. **Cryptographic correctness.** The Turnkey adapter uses P-256 ECDSA with DER-encoded signatures. The Fireblocks adapter uses RS256 (RSASSA-PKCS1-v1_5 with SHA-256) for JWT signing. The private-key adapter uses `@noble/curves/secp256k1` for ECDSA signing.
57+
58+
5. **Bridge isolation.** viem and ethers bridges must remain in separate entry points (`./viem`, `./ethers`). They must not be imported by the core adapters.
59+
60+
## Conventions
61+
62+
- ESM-only (`"type": "module"`). Use `.js` extensions in import paths.
63+
- Biome for linting and formatting: double quotes, 2-space indent, trailing commas.
64+
- viem and ethers.js are optional peer dependencies — never import them from core adapter code.
65+
- Each adapter has a `fromEnv()` static factory and a constructor accepting a typed config object.
66+
- Environment variable names follow the pattern: `PROVIDER_FIELD` (e.g., `PRIVY_APP_ID`, `TURNKEY_API_PUBLIC_KEY`).

README.md

Lines changed: 144 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,145 @@
1-
# wallet-adapters
1+
# @opensea/wallet-adapters
22

3-
This repo is bootstrapping. Initial sync from opensea-devtools is in progress.
3+
Provider-agnostic wallet adapters for signing and sending transactions across managed and local backends.
4+
5+
## Features
6+
7+
- **Provider-agnostic interface** — unified `WalletAdapter` abstraction with capabilities declaration
8+
- **Managed providers** — Privy, Turnkey, Fireblocks (handle gas/nonce server-side)
9+
- **Local providers** — PrivateKey (handle gas/nonce client-side via RPC)
10+
- **Framework bridges** — optional adapters for viem and ethers.js
11+
- **Zero heavy dependencies** — core uses Web Crypto + `@noble/hashes` / `@noble/curves`
12+
- **Auto-detection**`createWalletFromEnv()` picks the right provider from environment variables
13+
14+
## Installation
15+
16+
```bash
17+
npm install @opensea/wallet-adapters
18+
# or
19+
pnpm add @opensea/wallet-adapters
20+
```
21+
22+
## Quick Start
23+
24+
```ts
25+
import { createWalletFromEnv } from "@opensea/wallet-adapters"
26+
27+
// Auto-detects provider from environment variables
28+
// Priority: Privy > Fireblocks > Turnkey > PrivateKey
29+
const wallet = createWalletFromEnv()
30+
31+
const address = await wallet.getAddress()
32+
const result = await wallet.sendTransaction({
33+
to: "0x...",
34+
data: "0x...",
35+
value: "0",
36+
chainId: 8453,
37+
})
38+
console.log(`TX hash: ${result.hash}`)
39+
```
40+
41+
## Adapters
42+
43+
### Privy
44+
45+
Server-side wallet via Privy's API. Handles gas estimation and nonce management.
46+
47+
```ts
48+
import { PrivyAdapter } from "@opensea/wallet-adapters"
49+
50+
const wallet = PrivyAdapter.fromEnv()
51+
// Requires: PRIVY_APP_ID, PRIVY_APP_SECRET, PRIVY_WALLET_ID
52+
```
53+
54+
### Fireblocks
55+
56+
Enterprise MPC custody via Fireblocks API.
57+
58+
```ts
59+
import { FireblocksAdapter } from "@opensea/wallet-adapters"
60+
61+
const wallet = FireblocksAdapter.fromEnv()
62+
// Requires: FIREBLOCKS_API_KEY, FIREBLOCKS_API_SECRET, FIREBLOCKS_VAULT_ID
63+
```
64+
65+
### Turnkey
66+
67+
HSM-backed signing with P-256 stamp authentication.
68+
69+
```ts
70+
import { TurnkeyAdapter } from "@opensea/wallet-adapters"
71+
72+
const wallet = TurnkeyAdapter.fromEnv()
73+
// Requires: TURNKEY_API_PUBLIC_KEY, TURNKEY_API_PRIVATE_KEY,
74+
// TURNKEY_ORGANIZATION_ID, TURNKEY_WALLET_ADDRESS, TURNKEY_RPC_URL
75+
```
76+
77+
### PrivateKey
78+
79+
Local signing for development and testing.
80+
81+
```ts
82+
import { PrivateKeyAdapter } from "@opensea/wallet-adapters"
83+
84+
const wallet = PrivateKeyAdapter.fromEnv()
85+
// Requires: PRIVATE_KEY, RPC_URL
86+
```
87+
88+
## Framework Bridges
89+
90+
### viem
91+
92+
```ts
93+
import { walletAdapterToViemClient } from "@opensea/wallet-adapters/viem"
94+
import { base } from "viem/chains"
95+
96+
const client = walletAdapterToViemClient(wallet, base, "https://mainnet.base.org")
97+
// Use as a standard viem WalletClient
98+
```
99+
100+
### ethers.js
101+
102+
```ts
103+
import { walletAdapterToEthersSigner } from "@opensea/wallet-adapters/ethers"
104+
105+
const signer = walletAdapterToEthersSigner(wallet, provider)
106+
// Use as a standard ethers.js Signer
107+
```
108+
109+
## Capabilities
110+
111+
Each adapter declares its capabilities so consumers can check before calling:
112+
113+
```ts
114+
if (wallet.capabilities.signMessage) {
115+
const sig = await wallet.signMessage({ message: "hello" })
116+
}
117+
118+
if (wallet.capabilities.managedGas) {
119+
// No need to estimate gas — the provider handles it
120+
}
121+
```
122+
123+
| Capability | Privy | Fireblocks | Turnkey | PrivateKey |
124+
|------------|-------|------------|---------|------------|
125+
| `signMessage` | true | true | true | true |
126+
| `signTypedData` | true | true | true | true |
127+
| `managedGas` | true | true | false | false |
128+
| `managedNonce` | true | true | false | false |
129+
130+
## Observability
131+
132+
Attach hooks for tracing and monitoring:
133+
134+
```ts
135+
wallet.onRequest = (method, params) => {
136+
console.log(`→ ${method}`, params)
137+
}
138+
wallet.onResponse = (method, result, durationMs) => {
139+
console.log(`← ${method} (${durationMs}ms)`, result)
140+
}
141+
```
142+
143+
## License
144+
145+
MIT

biome.json

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
{
2+
"$schema": "node_modules/@biomejs/biome/configuration_schema.json",
3+
"files": {
4+
"includes": [
5+
"**/*.ts",
6+
"**/*.js",
7+
"**/*.json",
8+
"!**/node_modules",
9+
"!**/dist",
10+
"!**/lib",
11+
"!**/coverage",
12+
"!**/.nyc_output",
13+
"!packages/sdk/src/typechain",
14+
"!**/typechain",
15+
"!packages/api-types/src/generated.ts",
16+
"!**/pnpm-lock.yaml",
17+
"!**/package-lock.json"
18+
]
19+
},
20+
"formatter": {
21+
"enabled": true,
22+
"indentStyle": "space",
23+
"indentWidth": 2,
24+
"lineEnding": "lf",
25+
"lineWidth": 80
26+
},
27+
"javascript": {
28+
"formatter": {
29+
"quoteStyle": "double",
30+
"trailingCommas": "all",
31+
"semicolons": "asNeeded",
32+
"arrowParentheses": "asNeeded"
33+
}
34+
},
35+
"linter": {
36+
"enabled": true,
37+
"rules": {
38+
"recommended": true,
39+
"suspicious": {
40+
"noConsole": "off",
41+
"noExplicitAny": "off"
42+
},
43+
"correctness": {
44+
"noUnusedImports": "error"
45+
},
46+
"style": {
47+
"noNonNullAssertion": "error",
48+
"noUnusedTemplateLiteral": "off"
49+
}
50+
}
51+
},
52+
"overrides": [
53+
{
54+
"includes": ["**/*.test.ts", "**/*.spec.ts", "**/test/**"],
55+
"linter": {
56+
"rules": {
57+
"style": {
58+
"noNonNullAssertion": "off"
59+
}
60+
}
61+
}
62+
}
63+
]
64+
}

0 commit comments

Comments
 (0)