Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# CLAUDE.md

## Project
TypeScript monorepo (pnpm workspaces) with 5 packages under `packages/`:
- `@lifi/sdk` — core SDK (no external deps on other packages)
- `@lifi/sdk-provider-ethereum` — EVM provider (depends on sdk, uses viem)
- `@lifi/sdk-provider-solana` — Solana provider (depends on sdk, uses @solana/kit)
- `@lifi/sdk-provider-bitcoin` — Bitcoin provider (depends on sdk, uses bitcoinjs-lib)
- `@lifi/sdk-provider-sui` — Sui provider (depends on sdk, ESM-only, no CJS)

## Build
- `pnpm build` — runs tsdown in all packages in parallel (no dependency ordering needed)
- `pnpm check` — biome lint/format
- `pnpm check:types` — tsc --noEmit (separate from build)
- Build outputs: `dist/esm/` (ESM + .d.ts), `dist/cjs/` (CJS only, not for sui)
- tsdown configs in each package root (`tsdown.config.ts`)
- `isolatedDeclarations: true` — all exports need explicit return type annotations

## Testing & CI
- `pnpm test:unit` — vitest unit tests
- `pnpm pre-commit` — runs check + check:types + circular-deps + knip (via husky)
- `pnpm pre-push` — runs unit tests (via husky)

## Code Style
- Biome for formatting and linting (`pnpm check:write` to auto-fix)
- No default exports in library code
- Import types with `import type` syntax

## Known Issues
- `sdk-provider-ethereum/src/errors/parseEthereumErrors.ts` — pre-existing TS2339 error in check:types
- `sdk-provider-ethereum/src/utils/abi.ts` — parseAbi results typed as `Abi` (broader than inferred); downstream code uses `as` casts for readContract results

## Release
- `pnpm release` → `pnpm release:publish` (uses lerna + standard-version)
- Pre-publish strips devDependencies via `scripts/prerelease.js`, restores via `scripts/postrelease.js`
- `scripts/version.js` inlines package name/version into `src/version.ts` at build time
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"version": "4.0.0-beta.1",
"version": "4.0.0-beta.2",
"private": true,
"sideEffects": false,
"type": "module",
"scripts": {
"build": "pnpm --filter @lifi/sdk build && pnpm -r --parallel --filter './packages/**' --filter '!@lifi/sdk' build",
"build": "pnpm -r --parallel --filter './packages/**' build",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SDK provider packages rely on core SDK package - shouldn't we still build core SDK first, or it's handled anyhow differently now?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since tsdown resolves workspace packages from source (not compiled output), all packages are built in a single parallel pass 👌

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

"clean": "pnpm -r --parallel --filter './packages/**' clean",
"clean:cache": "pnpm store prune",
"clean:modules": "find . -name \"node_modules\" -type d -prune -exec rm -rf {} +",
Expand Down Expand Up @@ -46,15 +46,16 @@
}
},
"devDependencies": {
"@biomejs/biome": "^2.4.7",
"@biomejs/biome": "^2.4.8",
"@commitlint/cli": "^20.5.0",
"@commitlint/config-conventional": "^20.5.0",
"@types/ws": "^8.18.1",
"@vitest/coverage-v8": "^4.1.0",
"husky": "^9.1.7",
"knip": "^5.87.0",
"knip": "^6.0.2",
"lerna": "^9.0.7",
"standard-version": "^9.5.0",
"tsdown": "^0.21.4",
"typescript": "^5.9.3",
"vitest": "^4.1.0"
},
Expand Down
20 changes: 8 additions & 12 deletions packages/sdk-provider-bitcoin/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@lifi/sdk-provider-bitcoin",
"version": "4.0.0-beta.1",
"version": "4.0.0-beta.2",
"description": "LI.FI Bitcoin SDK Provider for Any-to-Any Cross-Chain-Swap",
"homepage": "https://github.com/lifinance/sdk",
"bugs": {
Expand All @@ -17,34 +17,30 @@
"sideEffects": false,
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/types/index.d.ts",
"typings": "./dist/types/index.d.ts",
"types": "./dist/esm/index.d.ts",
"typings": "./dist/esm/index.d.ts",
"exports": {
".": {
"types": "./dist/types/index.d.ts",
"types": "./dist/esm/index.d.ts",
"import": "./dist/esm/index.js",
"default": "./dist/cjs/index.js"
},
"./package.json": "./package.json"
},
"scripts": {
"build": "pnpm clean && pnpm build:version && pnpm build:cjs && pnpm build:esm && pnpm build:types && pnpm build:clean",
"build:cjs": "tsc --project ./tsconfig.json --module commonjs --outDir ./dist/cjs --removeComments --verbatimModuleSyntax false && mkdir -p ./dist/cjs && printf '{\"type\":\"commonjs\"}' > ./dist/cjs/package.json",
"build:esm": "tsc --project ./tsconfig.json --module es2015 --outDir ./dist/esm && mkdir -p ./dist/esm && printf '{\"type\": \"module\",\"sideEffects\":false}' > ./dist/esm/package.json",
"build:types": "tsc --project ./tsconfig.json --module esnext --declarationDir ./dist/types --emitDeclarationOnly --declaration --declarationMap",
"build": "rm -rf dist && node ../../scripts/version.js && node ../../scripts/build.js && printf '{\"type\":\"commonjs\"}' > ./dist/cjs/package.json && printf '{\"type\":\"module\",\"sideEffects\":false}' > ./dist/esm/package.json",
"build:prerelease": "node ../../scripts/prerelease.js && cpy '../../*.md' .",
"build:postrelease": "node ../../scripts/postrelease.js && rm -rf *.md",
"build:clean": "rm -rf tsconfig.tsbuildinfo ./dist/tsconfig.tsbuildinfo",
"build:version": "node ../../scripts/version.js",
"clean": "pnpm build:clean && rm -rf dist",
"clean": "rm -rf dist",
"coverage": "vitest run --coverage",
"test": "vitest --run --dangerouslyIgnoreUnhandledErrors",
"test:cov": "pnpm test --coverage",
"test:unit": "pnpm test .unit.spec.ts --passWithNoTests",
"check:types": "tsc --noEmit",
"check:circular-deps": "madge --circular $(find ./src -name '*.ts')",
"check:circular-deps-graph": "madge --circular $(find ./src -name '*.ts') --image graph.svg",
"watch": "tsc -w -p ./tsconfig.json"
"watch": "tsdown --watch"
},
"dependencies": {
"@bigmi/core": "^0.7.1",
Expand All @@ -54,7 +50,7 @@
"bitcoinjs-lib": "^7.0.1"
},
"devDependencies": {
"@lifi/data-types": "^6.67.0",
"@lifi/data-types": "^6.69.0",
"@vitest/coverage-v8": "^4.1.0",
"cpy-cli": "^7.0.0",
"madge": "^8.0.0",
Expand Down
6 changes: 4 additions & 2 deletions packages/sdk-provider-bitcoin/src/core/BitcoinStepExecutor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class BitcoinStepExecutor extends BaseStepExecutor {
this.client = options.client
}

checkClient = (step: LiFiStepExtended) => {
checkClient = (step: LiFiStepExtended): void => {
// TODO: check chain and possibly implement chain switch?
// Prevent execution of the quote by wallet different from the one which requested the quote
if (this.client.account?.address !== step.action.fromAddress) {
Expand All @@ -42,7 +42,9 @@ export class BitcoinStepExecutor extends BaseStepExecutor {
}
}

override createPipeline = (context: BitcoinStepExecutorContext) => {
override createPipeline = (
context: BitcoinStepExecutorContext
): TaskPipeline => {
const { step, isBridgeExecution } = context

const tasks = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { payments } from 'bitcoinjs-lib'
* @param publicKey
* @returns redeem script
*/
export const generateRedeemScript = (publicKey: Uint8Array) =>
export const generateRedeemScript = (
publicKey: Uint8Array
): Uint8Array | undefined =>
// P2SH addresses are created by hashing the public key and using the result as the script
payments.p2wpkh({ pubkey: publicKey }).output
2 changes: 1 addition & 1 deletion packages/sdk-provider-bitcoin/src/utils/toXOnly.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// helper function to convert full public key (33 bytes) to x-only compressed format (32 bytes) required after taproot update
export const toXOnly = (pubKey: Uint8Array) =>
export const toXOnly = (pubKey: Uint8Array): Uint8Array =>
pubKey.length === 32 ? pubKey : pubKey.subarray(1, 33)
2 changes: 1 addition & 1 deletion packages/sdk-provider-bitcoin/src/version.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export const name = '@lifi/sdk-provider-bitcoin'
export const version = '4.0.0-beta.1'
export const version = '4.0.0-beta.2'
38 changes: 38 additions & 0 deletions packages/sdk-provider-bitcoin/tsdown.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { defineConfig } from 'tsdown'

const entry = [
'src/**/*.ts',
'!src/**/*.spec.ts',
'!src/**/*.test.ts',
'!src/**/*.mock.ts',
'!src/**/*.handlers.ts',
]

const shared = {
unbundle: true,
target: 'es2020' as const,
logLevel: 'warn' as const,
deps: {
skipNodeModulesBundle: true,
},
}

export default defineConfig([
{
...shared,
entry,
outDir: 'dist/esm',
format: 'esm',
sourcemap: true,
dts: { sourcemap: true },
outExtensions: () => ({ js: '.js', dts: '.d.ts' }),
},
{
...shared,
entry,
outDir: 'dist/cjs',
format: 'cjs',
sourcemap: true,
outExtensions: () => ({ js: '.js' }),
},
])
22 changes: 9 additions & 13 deletions packages/sdk-provider-ethereum/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@lifi/sdk-provider-ethereum",
"version": "4.0.0-beta.1",
"version": "4.0.0-beta.2",
"description": "LI.FI Ethereum SDK Provider for Any-to-Any Cross-Chain-Swap",
"homepage": "https://github.com/lifinance/sdk",
"bugs": {
Expand All @@ -17,41 +17,37 @@
"sideEffects": false,
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/types/index.d.ts",
"typings": "./dist/types/index.d.ts",
"types": "./dist/esm/index.d.ts",
"typings": "./dist/esm/index.d.ts",
"exports": {
".": {
"types": "./dist/types/index.d.ts",
"types": "./dist/esm/index.d.ts",
"import": "./dist/esm/index.js",
"default": "./dist/cjs/index.js"
},
"./package.json": "./package.json"
},
"scripts": {
"build": "pnpm clean && pnpm build:version && pnpm build:cjs && pnpm build:esm && pnpm build:types && pnpm build:clean",
"build:cjs": "tsc --project ./tsconfig.json --module commonjs --outDir ./dist/cjs --removeComments --verbatimModuleSyntax false && mkdir -p ./dist/cjs && printf '{\"type\":\"commonjs\"}' > ./dist/cjs/package.json",
"build:esm": "tsc --project ./tsconfig.json --module es2015 --outDir ./dist/esm && mkdir -p ./dist/esm && printf '{\"type\": \"module\",\"sideEffects\":false}' > ./dist/esm/package.json",
"build:types": "tsc --project ./tsconfig.json --module esnext --declarationDir ./dist/types --emitDeclarationOnly --declaration --declarationMap",
"build": "rm -rf dist && node ../../scripts/version.js && node ../../scripts/build.js && printf '{\"type\":\"commonjs\"}' > ./dist/cjs/package.json && printf '{\"type\":\"module\",\"sideEffects\":false}' > ./dist/esm/package.json",
"build:prerelease": "node ../../scripts/prerelease.js && cpy '../../*.md' .",
"build:postrelease": "node ../../scripts/postrelease.js && rm -rf *.md",
"build:clean": "rm -rf tsconfig.tsbuildinfo ./dist/tsconfig.tsbuildinfo",
"build:version": "node ../../scripts/version.js",
"clean": "pnpm build:clean && rm -rf dist",
"clean": "rm -rf dist",
"coverage": "vitest run --coverage",
"test": "vitest --run --dangerouslyIgnoreUnhandledErrors",
"test:cov": "pnpm test --coverage",
"test:unit": "pnpm test .unit.spec.ts --passWithNoTests",
"check:types": "tsc --noEmit",
"check:circular-deps": "madge --circular $(find ./src -name '*.ts')",
"check:circular-deps-graph": "madge --circular $(find ./src -name '*.ts') --image graph.svg",
"watch": "tsc -w -p ./tsconfig.json"
"watch": "tsdown --watch"
},
"dependencies": {
"@lifi/sdk": "workspace:*",
"viem": "^2.47.4"
"viem": "^2.47.6"
},
"devDependencies": {
"@lifi/data-types": "^6.67.0",
"@lifi/data-types": "^6.69.0",
"@vitest/coverage-v8": "^4.1.0",
"cpy-cli": "^7.0.0",
"madge": "^8.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk-provider-ethereum/src/actions/getAllowance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const getAllowance = async (
args: [ownerAddress, spenderAddress] as const,
}
)
return approved
return approved as bigint
} catch (_e) {
return 0n
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const getEthereumBalance = async (
client: SDKClient,
walletAddress: Address,
tokens: Token[]
) => {
): Promise<TokenAmount[]> => {
if (tokens.length === 0) {
return []
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const resolveTransactionHash = async (
viemClient: Client,
txHashOrSignature: Hash,
chainId: number
) => {
): Promise<Hash> => {
const isSignature = await isSafeSignature(client, {
viemClient: viemClient,
hash: txHashOrSignature,
Expand Down
14 changes: 13 additions & 1 deletion packages/sdk-provider-ethereum/src/client/safeClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,19 @@ async function request<T>(
return response.json() as Promise<T>
}

export const getSafeClient = (chainId: number, apiKey?: string) => ({
export interface SafeClientInterface {
getInfo: (address: Address) => Promise<SafeInfo>
getTransaction: (safeTxHash: Hash) => Promise<SafeMultisigTransaction>
getTransactions: (
safeAddress: Address,
options?: { executed?: boolean; limit?: number }
) => Promise<SafeMultisigTransactionList>
}

export const getSafeClient = (
chainId: number,
apiKey?: string
): SafeClientInterface => ({
getInfo: (address: Address) =>
request<SafeInfo>(chainId, `/api/v1/safes/${address}/`, apiKey),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ export class EthereumStepExecutor extends BaseStepExecutor {
parseEthereumErrors(error, step, action, retryParams)

// Ensure that we are using the right chain and wallet when executing transactions.
checkClient = async (step: LiFiStepExtended, targetChainId?: number) => {
checkClient = async (
step: LiFiStepExtended,
targetChainId?: number
): Promise<Client | undefined> => {
const updatedClient = await switchChain(
this.client,
targetChainId ?? step.action.fromChainId,
Expand Down Expand Up @@ -128,7 +131,9 @@ export class EthereumStepExecutor extends BaseStepExecutor {
}
}

override createPipeline = (context: EthereumStepExecutorContext) => {
override createPipeline = (
context: EthereumStepExecutorContext
): TaskPipeline => {
const { step, isBridgeExecution, isFromNativeToken } = context

const tasks = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const estimateTransactionRequest = async (
client: SDKClient,
viemClient: Client,
transactionRequest: TransactionParameters
) => {
): Promise<TransactionParameters> => {
try {
// Try to re-estimate the gas due to additional Permit data
const estimatedGas = await getActionWithFallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const shouldCheckForAllowance = (
isBridgeExecution: boolean,
isFromNativeToken: boolean,
statusManager: StatusManager
) => {
): boolean => {
const exchangeActionType = isBridgeExecution ? 'CROSS_CHAIN' : 'SWAP'

const swapOrBridgeAction = statusManager.findAction(step, exchangeActionType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ const handleSpecificErrors = async (
step.action.fromChainId
)

const errorMessage = response?.error_message
const errorMessage = (response as Record<string, string>)?.error_message

if (errorMessage?.toLowerCase().includes('out of gas')) {
return new TransactionError(
Expand All @@ -124,7 +124,7 @@ const handleSpecificErrors = async (
return new UnknownError(e.message || ErrorMessage.UnknownError, e)
}

export const isAtomicReadyWalletRejectedUpgradeError = (e: any) => {
export const isAtomicReadyWalletRejectedUpgradeError = (e: any): boolean => {
if (e.cause?.code === AtomicReadyWalletRejectedUpgradeError.code) {
return true
}
Expand Down
Loading
Loading