Skip to content

Commit 73c731c

Browse files
committed
Release v1.0.1
1 parent 861953a commit 73c731c

17 files changed

Lines changed: 365 additions & 56 deletions

.github/workflows/ci.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
cache: npm
18+
- run: npm install
19+
- run: npm run format:check
20+
- run: npm run lint
21+
- run: npm run type-check
22+
- run: npm test
23+
- run: npm run build
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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+
- run: npm install
41+
42+
- if: ${{ inputs.build-before-publish }}
43+
run: npm run build
44+
45+
- if: ${{ inputs.build-before-publish }}
46+
run: npm test
47+
env:
48+
OPENSEA_API_KEY: ${{ secrets.OPENSEA_API_KEY }}
49+
ALCHEMY_API_KEY: ${{ secrets.ALCHEMY_API_KEY }}
50+
51+
- run: npm publish --provenance --access public
52+
53+
- if: ${{ inputs.stub-package-dir != '' }}
54+
run: npm publish --provenance --access public
55+
working-directory: ${{ inputs.stub-package-dir }}
56+
continue-on-error: true

.github/workflows/npm-publish.yml

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,9 @@ on:
55
types: [published]
66

77
jobs:
8-
publish-npm:
9-
runs-on: ubuntu-latest
10-
permissions:
11-
id-token: write
12-
contents: read
13-
steps:
14-
- uses: actions/checkout@v6
15-
- uses: actions/setup-node@v6
16-
with:
17-
node-version-file: .nvmrc
18-
registry-url: https://registry.npmjs.org/
19-
# Publish @opensea/cli
20-
- run: npm install
21-
- run: npm publish --provenance --access public
22-
23-
# Publish opensea-cli stub (skip if version already exists)
24-
- run: npm publish --provenance --access public
25-
working-directory: packages/opensea-cli-stub
26-
continue-on-error: true
8+
publish:
9+
uses: ./.github/workflows/npm-publish-reusable.yml
10+
with:
11+
build-before-publish: false
12+
stub-package-dir: packages/opensea-cli-stub
13+
secrets: inherit

.gitignore

Lines changed: 0 additions & 5 deletions
This file was deleted.

CHANGELOG.md

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

3+
## 1.0.1
4+
5+
### Patch Changes
6+
7+
- 6bb30d7: Fix `--version` to report correct version; auto-convert decimal quantities in swap commands
8+
39
## 1.0.0
410

511
### Minor Changes

biome.json

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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": "warn"
45+
},
46+
"style": {
47+
"noUnusedTemplateLiteral": "off"
48+
}
49+
}
50+
},
51+
"overrides": [
52+
{
53+
"includes": ["**/*.test.ts", "**/*.spec.ts", "**/test/**"],
54+
"linter": {
55+
"rules": {
56+
"correctness": {
57+
"noUnusedImports": "off"
58+
}
59+
}
60+
}
61+
}
62+
]
63+
}

package.json

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@opensea/cli",
3-
"version": "1.0.0",
3+
"version": "1.0.1",
44
"type": "module",
55
"description": "OpenSea CLI - Query the OpenSea API from the command line or programmatically",
66
"main": "dist/index.js",
@@ -14,10 +14,10 @@
1414
"scripts": {
1515
"build": "tsup",
1616
"dev": "tsup --watch",
17-
"format": "biome format --config-path ../.. --write .",
18-
"format:check": "biome format --config-path ../.. .",
19-
"lint": "biome check --config-path ../.. .",
20-
"lint:fix": "biome check --config-path ../.. --fix .",
17+
"format": "biome format --config-path . --write .",
18+
"format:check": "biome format --config-path . .",
19+
"lint": "biome check --config-path . .",
20+
"lint:fix": "biome check --config-path . --write .",
2121
"type-check": "tsc --noEmit",
2222
"test": "vitest run",
2323
"prepublishOnly": "npm run build"
@@ -32,7 +32,8 @@
3232
"@vitest/coverage-v8": "^3.2.4",
3333
"tsup": "^8.3.0",
3434
"typescript": "^6.0.2",
35-
"vitest": "^3.2.4"
35+
"vitest": "^3.2.4",
36+
"@biomejs/biome": "^2.4.10"
3637
},
3738
"engines": {
3839
"node": ">=18.0.0"

src/cli.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import {
1717
import { type OutputFormat, setOutputOptions } from "./output.js"
1818
import { parseIntOption } from "./parse.js"
1919

20+
declare const __VERSION__: string
21+
2022
const EXIT_API_ERROR = 1
2123
const EXIT_AUTH_ERROR = 2
2224
const EXIT_RATE_LIMITED = 3
@@ -37,7 +39,7 @@ const program = new Command()
3739
program
3840
.name("opensea")
3941
.description("OpenSea CLI - Query the OpenSea API from the command line")
40-
.version(process.env.npm_package_version ?? "0.0.0")
42+
.version(__VERSION__)
4143
.addHelpText("before", BANNER)
4244
.option("--api-key <key>", "OpenSea API key (or set OPENSEA_API_KEY env var)")
4345
.option("--chain <chain>", "Default chain", "ethereum")

src/commands/swaps.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { OpenSeaClient } from "../client.js"
33
import type { OutputFormat } from "../output.js"
44
import { formatOutput } from "../output.js"
55
import { parseFloatOption } from "../parse.js"
6-
import { SwapsAPI } from "../sdk.js"
6+
import { resolveQuantity, SwapsAPI } from "../sdk.js"
77
import type { SwapQuoteResponse } from "../types/index.js"
88
import type { WalletProvider } from "../wallet/index.js"
99
import { createWalletFromEnv, WALLET_PROVIDERS } from "../wallet/index.js"
@@ -31,7 +31,10 @@ export function swapsCommand(
3131
"--to-address <address>",
3232
"Contract address of the token to swap to",
3333
)
34-
.requiredOption("--quantity <quantity>", "Amount to swap (in token units)")
34+
.requiredOption(
35+
"--quantity <quantity>",
36+
"Amount to swap (decimals like 0.1 are auto-converted to smallest units)",
37+
)
3538
.requiredOption("--address <address>", "Wallet address executing the swap")
3639
.option(
3740
"--slippage <slippage>",
@@ -53,14 +56,20 @@ export function swapsCommand(
5356
recipient?: string
5457
}) => {
5558
const client = getClient()
59+
const quantity = await resolveQuantity(
60+
client,
61+
options.fromChain,
62+
options.fromAddress,
63+
options.quantity,
64+
)
5665
const result = await client.get<SwapQuoteResponse>(
5766
"/api/v2/swap/quote",
5867
{
5968
from_chain: options.fromChain,
6069
from_address: options.fromAddress,
6170
to_chain: options.toChain,
6271
to_address: options.toAddress,
63-
quantity: options.quantity,
72+
quantity,
6473
address: options.address,
6574
slippage: options.slippage
6675
? parseFloatOption(options.slippage, "--slippage")
@@ -88,7 +97,10 @@ export function swapsCommand(
8897
"--to-address <address>",
8998
"Contract address of the token to swap to",
9099
)
91-
.requiredOption("--quantity <quantity>", "Amount to swap (in token units)")
100+
.requiredOption(
101+
"--quantity <quantity>",
102+
"Amount to swap (decimals like 0.1 are auto-converted to smallest units)",
103+
)
92104
.option(
93105
"--slippage <slippage>",
94106
"Slippage tolerance (0.0 to 0.5, default: 0.01)",
@@ -120,7 +132,14 @@ export function swapsCommand(
120132
const address = await wallet.getAddress()
121133
console.error(`Using ${wallet.name} wallet: ${address}`)
122134

123-
const swaps = new SwapsAPI(getClient())
135+
const client = getClient()
136+
const quantity = await resolveQuantity(
137+
client,
138+
options.fromChain,
139+
options.fromAddress,
140+
options.quantity,
141+
)
142+
const swaps = new SwapsAPI(client)
124143
const format = getFormat()
125144
const slippage = options.slippage
126145
? parseFloatOption(options.slippage, "--slippage")
@@ -129,6 +148,7 @@ export function swapsCommand(
129148
if (options.dryRun) {
130149
const quote = await swaps.quote({
131150
...options,
151+
quantity,
132152
address,
133153
slippage,
134154
})
@@ -139,6 +159,7 @@ export function swapsCommand(
139159
const results = await swaps.execute(
140160
{
141161
...options,
162+
quantity,
142163
address,
143164
slippage,
144165
},

src/sdk.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,43 @@ import type {
3333
import type { TransactionResult, WalletAdapter } from "./wallet/index.js"
3434
import { resolveChainId } from "./wallet/index.js"
3535

36+
function convertToSmallestUnit(amount: string, decimals: number): string {
37+
const [whole = "0", frac = ""] = amount.split(".")
38+
if (frac.length > decimals) {
39+
throw new Error(
40+
`Too many decimal places (${frac.length}) for token with ${decimals} decimals`,
41+
)
42+
}
43+
const paddedFrac = frac.padEnd(decimals, "0")
44+
return (
45+
BigInt(whole) * BigInt(10) ** BigInt(decimals) +
46+
BigInt(paddedFrac)
47+
).toString()
48+
}
49+
50+
export async function resolveQuantity(
51+
client: OpenSeaClient,
52+
chain: string,
53+
tokenAddress: string,
54+
quantity: string,
55+
): Promise<string> {
56+
if (/^\d+$/.test(quantity)) {
57+
return quantity
58+
}
59+
if (!/^\d+\.\d+$/.test(quantity)) {
60+
throw new Error(
61+
`Invalid quantity "${quantity}": must be an integer or decimal number`,
62+
)
63+
}
64+
const token = await client.get<TokenDetails>(
65+
`/api/v2/chain/${chain}/token/${tokenAddress}`,
66+
)
67+
return convertToSmallestUnit(quantity, token.decimals)
68+
}
69+
70+
/** @internal Exported for testing only */
71+
export { convertToSmallestUnit as _convertToSmallestUnit }
72+
3673
export class OpenSeaCLI {
3774
private client: OpenSeaClient
3875

0 commit comments

Comments
 (0)