Skip to content
Open
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
14 changes: 8 additions & 6 deletions .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,17 @@ jobs:

- name: Show simulator logs on failure
if: failure() && env.INTERNAL_EVENT == 'true'
working-directory: lattice-simulator
run: |
echo "=== Simulator logs ==="
cat simulator.log || echo "No simulator logs found"
if [ -d lattice-simulator ]; then
echo "=== Simulator logs ==="
cat lattice-simulator/simulator.log || echo "No simulator logs found"
else
echo "Simulator directory not found, skipping logs"
fi

- name: Stop simulator
if: always() && env.INTERNAL_EVENT == 'true'
working-directory: lattice-simulator
run: |
if [ -f simulator.pid ]; then
kill $(cat simulator.pid) || true
if [ -f lattice-simulator/simulator.pid ]; then
kill $(cat lattice-simulator/simulator.pid) || true
fi
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
"name": "gridplus-sdk-monorepo",
"private": true,
"scripts": {
"build": "pnpm --filter gridplus-sdk run build",
"test": "pnpm --filter gridplus-sdk run test",
"lint": "pnpm --filter gridplus-sdk run lint",
"lint:fix": "pnpm --filter gridplus-sdk run lint:fix",
"build": "pnpm --filter @gridplus/btc run build && pnpm --filter gridplus-sdk run build",
Copy link
Collaborator

Choose a reason for hiding this comment

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

@netbonus should we use Turbo for the dependency orchestration? and we'll need to add @gridplus/btc as a depedency here for a true monorepo experience.

"test": "pnpm --filter @gridplus/btc run build && pnpm --filter @gridplus/btc run test && pnpm --filter gridplus-sdk run test",
"test-unit": "pnpm --filter @gridplus/btc run build && pnpm --filter @gridplus/btc run test && pnpm --filter gridplus-sdk run test-unit",
"lint": "pnpm --filter @gridplus/btc run lint && pnpm --filter gridplus-sdk run lint",
"lint:fix": "pnpm --filter @gridplus/btc run lint:fix && pnpm --filter gridplus-sdk run lint:fix",
"e2e": "pnpm --filter gridplus-sdk run e2e",
"docs:build": "pnpm --filter gridplus-sdk-docs run build",
"docs:start": "pnpm --filter gridplus-sdk-docs run start"
Expand Down
107 changes: 107 additions & 0 deletions packages/btc/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import js from '@eslint/js';
import tsPlugin from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import prettierConfig from 'eslint-config-prettier';
import prettierPlugin from 'eslint-plugin-prettier';

const restrictedNodeImports = [
{ name: 'crypto', message: 'Use node:crypto instead.' },
{ name: 'fs', message: 'Use node:fs instead.' },
{ name: 'os', message: 'Use node:os instead.' },
{ name: 'path', message: 'Use node:path instead.' },
{ name: 'stream', message: 'Use node:stream instead.' },
{ name: 'url', message: 'Use node:url instead.' },
{ name: 'util', message: 'Use node:util instead.' },
];

export default [
js.configs.recommended,
{
files: ['src/**/*.ts', 'src/**/*.tsx'],
plugins: {
'@typescript-eslint': tsPlugin,
prettier: prettierPlugin,
},
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
globals: {
Buffer: 'readonly',
URL: 'readonly',
URLSearchParams: 'readonly',
fetch: 'readonly',
Response: 'readonly',
Request: 'readonly',
RequestInit: 'readonly',
AbortController: 'readonly',
setTimeout: 'readonly',
clearTimeout: 'readonly',
// Test globals
vi: 'readonly',
describe: 'readonly',
it: 'readonly',
test: 'readonly',
expect: 'readonly',
beforeAll: 'readonly',
afterAll: 'readonly',
beforeEach: 'readonly',
afterEach: 'readonly',
// Node.js globals
process: 'readonly',
// Browser globals
console: 'readonly',
},
},
rules: {
...tsPlugin.configs.recommended.rules,
...prettierPlugin.configs.recommended.rules,
'prettier/prettier': 'error',
eqeqeq: ['error'],
'no-var': ['warn'],
'no-duplicate-imports': ['error'],
'prefer-const': ['error'],
'prefer-spread': ['error'],
'no-console': ['off'],
'no-redeclare': 'off',
'@typescript-eslint/no-redeclare': 'error',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unused-vars': [
'warn',
{ argsIgnorePattern: '^_' },
],
quotes: [
'warn',
'single',
{ avoidEscape: true, allowTemplateLiterals: true },
],
'no-restricted-imports': ['error', { paths: restrictedNodeImports }],
'no-restricted-syntax': [
'error',
{
selector: "CallExpression[callee.name='require']",
message: 'Use ESM imports instead of require.',
},
{
selector:
"AssignmentExpression[left.object.name='module'][left.property.name='exports']",
message: 'Use ESM exports instead of module.exports.',
},
],
},
},
prettierConfig,
{
ignores: [
'dist/**',
'node_modules/**',
'coverage/**',
'*.js',
'*.cjs',
'*.mjs',
'build/**',
],
},
];
51 changes: 51 additions & 0 deletions packages/btc/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"name": "@gridplus/btc",
Copy link
Collaborator

Choose a reason for hiding this comment

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

"@gridplus/btc" looks clean and concise in the code, but I’m not sure it clearly signals what the package contains (wallet, utils, etc.). I was considering something like "@gridplus/btc-utils" instead, though I’m not 100% sold 🤔

"version": "0.1.0",
"type": "module",
"description": "BTC utilities for GridPlus SDK - SLIP-132, network inference, transaction building, wallet utilities",
"scripts": {
"build": "tsup",
"test": "vitest run ./src/__test__/unit",
"lint": "eslint src --config eslint.config.mjs",
"lint:fix": "eslint src --config eslint.config.mjs --fix"
},
"files": [
"dist"
],
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"./package.json": "./package.json"
},
"types": "./dist/index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/GridPlus/gridplus-sdk.git",
"directory": "packages/btc"
},
"dependencies": {
"bs58check": "^4.0.0"
},
"devDependencies": {
"@eslint/js": "^9.36.0",
"@types/node": "^24.10.4",
"@typescript-eslint/eslint-plugin": "^8.44.1",
"@typescript-eslint/parser": "^8.44.1",
"eslint": "^9.36.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.4",
"prettier": "^3.6.2",
"tsup": "^8.5.0",
"typescript": "^5.9.2",
"vitest": "3.2.4"
},
"license": "MIT",
"engines": {
"node": ">=20"
}
}
51 changes: 51 additions & 0 deletions packages/btc/src/__test__/unit/network.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import bs58check from 'bs58check';
import { SLIP132_VERSION_BYTES } from '../../constants';
import {
inferFromXpub,
getCoinType,
getNetworkFromCoinType,
isTestnet,
} from '../../network';

const TEST_XPUB =
'xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8';
const toVersion = (xpub: string, version: number) => {
const decoded = bs58check.decode(xpub);
const converted = new Uint8Array(decoded.length);
const view = new DataView(converted.buffer);
view.setUint32(0, version, false);
converted.set(decoded.subarray(4), 4);
return bs58check.encode(converted);
};

const TEST_TPUB = toVersion(TEST_XPUB, SLIP132_VERSION_BYTES.tpub.public);

describe('btc/network', () => {
it('infers mainnet from xpub', () => {
expect(inferFromXpub(TEST_XPUB)).toBe('mainnet');
});

it('infers testnet from tpub', () => {
expect(inferFromXpub(TEST_TPUB)).toBe('testnet');
});

it('returns coin type for mainnet', () => {
expect(getCoinType('mainnet')).toBe(0);
});

it('returns coin type for testnet/regtest', () => {
expect(getCoinType('testnet')).toBe(1);
expect(getCoinType('regtest')).toBe(1);
});

it('returns network from coin type', () => {
expect(getNetworkFromCoinType(0)).toBe('mainnet');
expect(getNetworkFromCoinType(1)).toBe('testnet');
});

it('identifies testnet-like networks', () => {
expect(isTestnet('mainnet')).toBe(false);
expect(isTestnet('testnet')).toBe(true);
expect(isTestnet('regtest')).toBe(true);
});
});
Loading
Loading