From 2c8ecdbe97c16bca2a67e76d223423a78aac7991 Mon Sep 17 00:00:00 2001 From: Shane Earley Date: Thu, 9 Mar 2023 22:48:15 -0500 Subject: [PATCH] Add database schema docs --- apps/web/src/composables/ledger.ts | 3 +- apps/web/src/composables/trezor.ts | 2 +- common/bitcoin-ledger-signer/src/index.ts | 121 --------- .../interfaces/BitcoinLedgerSignerOptions.ts | 5 - .../src/providers/transports.ts | 16 -- common/data/README.md | 49 +++- common/data/package.json | 8 +- common/data/scripts/{db.ts => postgres.ts} | 9 +- common/data/src/index.ts | 4 +- .../data}/src/providers/postgres.ts | 33 +-- common/data/src/providers/schema.ts | 24 +- common/ethers-helpers/package.json | 16 -- common/ethers-helpers/src/index.ts | 21 -- common/ethers-helpers/tsconfig.json | 24 -- common/ethers-ledger-signer/.gitignore | 2 - common/ethers-ledger-signer/package.json | 15 -- common/ethers-ledger-signer/src/index.ts | 131 ---------- .../interfaces/EthersLedgerSignerOptions.ts | 8 - .../src/providers/transports.ts | 16 -- common/ethers-ledger-signer/tsconfig.json | 21 -- common/ethers-trezor-signer/.gitignore | 2 - common/ethers-trezor-signer/package.json | 14 - .../interfaces/EthersTrezorSignerOptions.ts | 6 - .../src/interfaces/MessageSignature.ts | 4 - common/ethers-trezor-signer/tsconfig.json | 21 -- common/helpers/package.json | 3 +- common/helpers/src/index.ts | 34 +++ .../.gitignore | 0 .../package.json | 2 +- .../src/index.ts | 4 +- .../tsconfig.json | 0 common/transport-speculos-http/.gitignore | 2 - common/{ethers-helpers => wallets}/.gitignore | 0 .../package.json | 7 +- common/wallets/src/index.ts | 4 + .../src/interfaces/TransactionInit.ts | 0 common/wallets/src/providers/ledger.ts | 241 ++++++++++++++++++ .../src/providers/trezor.ts} | 38 ++- .../tsconfig.json | 0 package-lock.json | 165 +++--------- scripts/aws/configure | 48 ---- scripts/crawler/dev | 90 ------- scripts/crawler/test | 83 ------ scripts/local/dev.ts | 4 +- services/keys/build.js | 3 +- services/users/package.json | 1 - services/users/src/providers/db.ts | 28 ++ services/users/src/providers/users.ts | 13 +- services/users/src/routes/user.ts | 4 +- 49 files changed, 473 insertions(+), 876 deletions(-) delete mode 100644 common/bitcoin-ledger-signer/src/index.ts delete mode 100644 common/bitcoin-ledger-signer/src/interfaces/BitcoinLedgerSignerOptions.ts delete mode 100644 common/bitcoin-ledger-signer/src/providers/transports.ts rename common/data/scripts/{db.ts => postgres.ts} (84%) rename {services/users => common/data}/src/providers/postgres.ts (59%) delete mode 100644 common/ethers-helpers/package.json delete mode 100644 common/ethers-helpers/src/index.ts delete mode 100644 common/ethers-helpers/tsconfig.json delete mode 100644 common/ethers-ledger-signer/.gitignore delete mode 100644 common/ethers-ledger-signer/package.json delete mode 100644 common/ethers-ledger-signer/src/index.ts delete mode 100644 common/ethers-ledger-signer/src/interfaces/EthersLedgerSignerOptions.ts delete mode 100644 common/ethers-ledger-signer/src/providers/transports.ts delete mode 100644 common/ethers-ledger-signer/tsconfig.json delete mode 100644 common/ethers-trezor-signer/.gitignore delete mode 100644 common/ethers-trezor-signer/package.json delete mode 100644 common/ethers-trezor-signer/src/interfaces/EthersTrezorSignerOptions.ts delete mode 100644 common/ethers-trezor-signer/src/interfaces/MessageSignature.ts delete mode 100644 common/ethers-trezor-signer/tsconfig.json rename common/{bitcoin-ledger-signer => speculos}/.gitignore (100%) rename common/{transport-speculos-http => speculos}/package.json (87%) rename common/{transport-speculos-http => speculos}/src/index.ts (96%) rename common/{transport-speculos-http => speculos}/tsconfig.json (100%) delete mode 100644 common/transport-speculos-http/.gitignore rename common/{ethers-helpers => wallets}/.gitignore (100%) rename common/{bitcoin-ledger-signer => wallets}/package.json (66%) create mode 100644 common/wallets/src/index.ts rename common/{bitcoin-ledger-signer => wallets}/src/interfaces/TransactionInit.ts (100%) create mode 100644 common/wallets/src/providers/ledger.ts rename common/{ethers-trezor-signer/src/index.ts => wallets/src/providers/trezor.ts} (76%) rename common/{bitcoin-ledger-signer => wallets}/tsconfig.json (100%) delete mode 100755 scripts/aws/configure delete mode 100755 scripts/crawler/dev delete mode 100755 scripts/crawler/test create mode 100644 services/users/src/providers/db.ts diff --git a/apps/web/src/composables/ledger.ts b/apps/web/src/composables/ledger.ts index 1019c4a1a..8cd11835c 100644 --- a/apps/web/src/composables/ledger.ts +++ b/apps/web/src/composables/ledger.ts @@ -1,5 +1,4 @@ -import EthersLedgerSigner from '@casimir/ethers-ledger-signer' -import BitcoinLedgerSigner from '@casimir/bitcoin-ledger-signer' +import { BitcoinLedgerSigner, EthersLedgerSigner } from '@casimir/wallets' import { ethers } from 'ethers' import { TransactionInit } from '@/interfaces/TransactionInit' import { MessageInit } from '@/interfaces/MessageInit' diff --git a/apps/web/src/composables/trezor.ts b/apps/web/src/composables/trezor.ts index a66ca7736..5741abe15 100644 --- a/apps/web/src/composables/trezor.ts +++ b/apps/web/src/composables/trezor.ts @@ -1,4 +1,4 @@ -import EthersTrezorSigner from '@casimir/ethers-trezor-signer' +import { EthersTrezorSigner } from '@casimir/wallets' import useEthers from '@/composables/ethers' import useEnvironment from '@/composables/environment' import { ethers } from 'ethers' diff --git a/common/bitcoin-ledger-signer/src/index.ts b/common/bitcoin-ledger-signer/src/index.ts deleted file mode 100644 index c55a63128..000000000 --- a/common/bitcoin-ledger-signer/src/index.ts +++ /dev/null @@ -1,121 +0,0 @@ -import Btc from '@ledgerhq/hw-app-btc' -import useTransports from './providers/transports' -import { BitcoinLedgerSignerOptions } from './interfaces/BitcoinLedgerSignerOptions' -import { TransactionInit } from './interfaces/TransactionInit' - -const defaultPath = '84\'/0\'/0\'/0/0' // Legacy addresses: '44\'/0\'/0\'/0/0', Segwit: '49\'/0\'/0\'/0/0', Native Segwit: '84\'/0\'/0\'/0/0' -// const defaultPath = '84\'/1\'/0\'/0/0' // TestNetPath -const defaultType = 'usb' -const { createUSBTransport, createSpeculosTransport } = useTransports() -const transportCreators = { - 'usb': createUSBTransport, - 'speculos': createSpeculosTransport -} - -export default class BitcoinLedgerSigner { - readonly type: string - readonly path: string - readonly baseURL?: string - _btc?: Promise - - constructor(options: BitcoinLedgerSignerOptions) { - if (!options.type) options.type = defaultType - if (!options.path) options.path = defaultPath - this.type = options.type - this.path = options.path - this.baseURL = options.baseURL - - const transportCreatorType = this.type as keyof typeof transportCreators - const transportCreator = transportCreators[transportCreatorType] - if (!transportCreator) console.log('Unknown or unsupported type', this.type) - this._btc = transportCreator(this.baseURL).then(transport => { - // TODO: Add conditional for testnet - return new Btc({ transport, currency: 'bitcoin_testnet' }) - return new Btc({ transport, currency: 'bitcoin' }) - }) - } - - _retry(callback: (btc: Btc) => Promise, timeout?: number): Promise { - // The async-promise-executor is ok since _retry handles necessary errors - // eslint-disable-next-line no-async-promise-executor - return new Promise(async (resolve, reject) => { - const ledgerConnectionError = 'Please make sure Ledger is ready and retry' - if (timeout && timeout > 0) { - setTimeout(() => reject(new Error(ledgerConnectionError)), timeout) - } - - const btc = await this._btc as Btc - - // Wait up to 5 seconds - for (let i = 0; i < 50; i++) { - try { - const result = await callback(btc) - return resolve(result) - } catch (error) { - if ((error as { id: string }).id !== 'TransportLocked') { - return reject(error) - } - } - await new Promise((resolve) => { - setTimeout(resolve, 100) - }) - } - - return reject(new Error(ledgerConnectionError)) - }) - } - - async getAddress(): Promise { - const { bitcoinAddress: address } = await this._retry((btc) => btc.getWalletPublicKey( - this.path, - { verify: false, format: 'bech32'} - )) - return address - } - - async signMessage(message: string): Promise { - try { - const messageHex = Buffer.from(message).toString('hex') - const result = await this._retry((btc) => btc.signMessage( - this.path, - messageHex - )) - const v = result['v'] + 27 + 4 - const signature = Buffer.from(v.toString(16) + result['r'] + result['s'], 'hex').toString('base64') - console.log('Signature : ' + signature) - return signature - } catch (err) { - console.log(err) - throw err - } - } - - // async signTransaction(path: string, transaction: any) { - // } - - async sendTransaction({from, to, value}: TransactionInit): Promise { - try { - // Build the transaction object using splitTransaction to then pass into getTrustedInput - // Create a transaction hex string - const transactionHex = 'f85f49b51366f7150d2adea6544bc256743707a38e2bdfbf839349ba1ff2875c' - const isSegwitSupported = false - const tx = await this._retry(async (btc) => btc.splitTransaction( - transactionHex, - isSegwitSupported - )) - return new Promise(resolve => resolve('')) - // const output_index = 1 - // const inputs = [[tx, output_index]] - // Get trusted input (returns a string) using getTrustedInput - // const trustedInput = await this._retry((btc) => btc.getTrustedInput( - // 0, - - // )) - - // Build the CreateTransactionArg and pass it to createPaymentTransaction - } catch (err) { - console.log('Error in bitcoin-ledger-signer sendTransaction: ', err) - throw err - } - } -} \ No newline at end of file diff --git a/common/bitcoin-ledger-signer/src/interfaces/BitcoinLedgerSignerOptions.ts b/common/bitcoin-ledger-signer/src/interfaces/BitcoinLedgerSignerOptions.ts deleted file mode 100644 index a6ec7d2da..000000000 --- a/common/bitcoin-ledger-signer/src/interfaces/BitcoinLedgerSignerOptions.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface BitcoinLedgerSignerOptions { - type?: string - path?: string - baseURL?: string -} \ No newline at end of file diff --git a/common/bitcoin-ledger-signer/src/providers/transports.ts b/common/bitcoin-ledger-signer/src/providers/transports.ts deleted file mode 100644 index 7e0de2360..000000000 --- a/common/bitcoin-ledger-signer/src/providers/transports.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Transport from '@ledgerhq/hw-transport' -import TransportSpeculosHTTP from '@casimir/transport-speculos-http' -import TransportWebUSB from '@ledgerhq/hw-transport-webusb' - -export default function useTransports() { - - async function createUSBTransport(): Promise { - return await TransportWebUSB.create() - } - - async function createSpeculosTransport(baseURL?: string): Promise { - return await TransportSpeculosHTTP.open(baseURL) - } - - return { createUSBTransport, createSpeculosTransport } -} \ No newline at end of file diff --git a/common/data/README.md b/common/data/README.md index 2d2cb1587..150b406a2 100644 --- a/common/data/README.md +++ b/common/data/README.md @@ -2,15 +2,54 @@ JSON Schemas and Jupyter Notebooks for Casimir data modeling, exploration, and analytics -## JSON Schemas +## Database Schemas -Find the `event` table schema in [src/schemas/event.schema.json](src/schemas/event.schema.json) and the `agg` table schema in [src/schemas/agg.schema.json](src/schemas/agg.schema.json). +Find the core JSON object schemas in [src/schemas/](src/schemas/). These are the source of truth for data modeling in Casimir. When we deploy our Glue and Postgres tables, we use the schemas to generate columns from the object properties. See the reference tables below (one for Glue, and one for Postgres) for descriptions and links to the current schemas. -### Making Changes +> 🚩 To make a schema change, create a branch from `develop`, edit the JSON, and then make a PR to `develop`. -The JSON Schemas in [src/schemas](src/schemas/) are the source of truth for the data model. When we deploy our Glue tables, we use the JSON Schemas to generate the Glue columns. To make a schema change, create a branch from `develop`, edit the JSON, and then make a PR to `develop`. +### Glue -> 🚩 Schema versioning is coming soon. +| Table | Schema | Description | +| --- | --- | --- | +| `events` | [event.schema.json](src/schemas/event.schema.json) | on or off-chain event | +| `aggs` | [agg.schema.json](src/schemas/agg.schema.json) | aggregate of events | + +### Postgres + +| Table | Schema | Description | +| --- | --- | --- | +| `accounts` | [account.schema.json](src/schemas/account.schema.json) | wallet account | +| `users` | [user.schema.json](src/schemas/user.schema.json) | user profile | + +Run a local Postgres instance with the schemas above. + +```zsh +npm run dev:postgres --workspace @casimir/data +``` + +Run and watch a local Postgres instance. + +```zsh +npm run watch:postgres --workspace @casimir/data +``` + +Query the local Postgres instance. + +```sql +"Add a user" + +INSERT INTO users (address) VALUES ('0xd557a5745d4560B24D36A68b52351ffF9c86A212'); + +"Add an account (with the same address as the user)" + +INSERT INTO accounts (address, owner_address) VALUES ('0xd557a5745d4560B24D36A68b52351ffF9c86A212', '0xd557a5745d4560B24D36A68b52351ffF9c86A212'); + +"Query the user" + +SELECT u.*, json_agg(a.*) AS accounts FROM users u JOIN accounts a ON u.address = a.owner_address WHERE u.address = '0xd557a5745d4560B24D36A68b52351ffF9c86A212' GROUP BY u.address; + +``` ## Notebooks diff --git a/common/data/package.json b/common/data/package.json index 76858ef3c..41bb274d8 100644 --- a/common/data/package.json +++ b/common/data/package.json @@ -6,13 +6,13 @@ "build": "echo '@casimir/data build not specified. Disregard this warning and any listed errors above if @casimir/types is not needed for the current project build.' && exit 0", "clean": "npx rimraf ./scripts/pgdata ./scripts/sql", "configure:python": "poetry install && poetry run ipython kernel install --user --name=casimir-data", - "dev": "ts-node --transpile-only scripts/db.ts --clean \"$npm_config_clean\" --tables \"$npm_config_tables\"", - "seed": "ts-node --transpile-only scripts/seed.ts", - "watch": "ts-node-dev --watch src --respawn --transpile-only scripts/db.ts --clean \"$npm_config_clean\" --tables \"$npm_config_tables\"", + "dev:postgres": "ts-node --transpile-only scripts/postgres.ts --clean \"$npm_config_clean\" --tables \"$npm_config_tables\"", + "watch:postgres": "ts-node-dev --watch src --respawn --transpile-only scripts/postgres.ts --clean \"$npm_config_clean\" --tables \"$npm_config_tables\"", "test": "echo \"Error: no test specified\" && exit 1" }, "dependencies": { - "@aws-cdk/aws-glue-alpha": "^2.33.0-alpha.0" + "@aws-cdk/aws-glue-alpha": "^2.33.0-alpha.0", + "pg": "^8.10.0" }, "devDependencies": { "@types/node": "^17.0.38", diff --git a/common/data/scripts/db.ts b/common/data/scripts/postgres.ts similarity index 84% rename from common/data/scripts/db.ts rename to common/data/scripts/postgres.ts index 51753098b..8836cea53 100644 --- a/common/data/scripts/db.ts +++ b/common/data/scripts/postgres.ts @@ -11,11 +11,10 @@ const tableSchemas = { } /** - * Run a local PG database with the given tables + * Run a local postgres database with the given tables * * Arguments: * --clean: delete existing pgdata before deploy (optional, i.e., --clear) - * --seed: seed database with test data (optional, i.e., --seed) * --tables: tables to deploy (optional, i.e., --tables=accounts,users) */ void async function () { @@ -47,10 +46,4 @@ void async function () { /** Start local database */ await spawnPromise('docker compose -f ./scripts/docker-compose.yaml up -d') - - /** Default to no seed data */ - const seed = argv.seed === true || argv.seed === 'true' - if (seed) { - await spawnPromise('npm run seed --workspace @casimir/data') - } }() \ No newline at end of file diff --git a/common/data/src/index.ts b/common/data/src/index.ts index 2ff519036..650eaf9f9 100644 --- a/common/data/src/index.ts +++ b/common/data/src/index.ts @@ -5,8 +5,9 @@ import eventSchema from './schemas/event.schema.json' import aggSchema from './schemas/agg.schema.json' import operatorStore from './mock/operator.store.json' import validatorStore from './mock/validator.store.json' -import { JsonSchema } from './interfaces/JsonSchema' +import { Postgres } from './providers/postgres' import { JsonType, GlueType, PgType, Schema } from './providers/schema' +import { JsonSchema } from './interfaces/JsonSchema' export { accountSchema, @@ -16,6 +17,7 @@ export { aggSchema, operatorStore, validatorStore, + Postgres, Schema } diff --git a/services/users/src/providers/postgres.ts b/common/data/src/providers/postgres.ts similarity index 59% rename from services/users/src/providers/postgres.ts rename to common/data/src/providers/postgres.ts index f51fbbf56..cd6917919 100644 --- a/services/users/src/providers/postgres.ts +++ b/common/data/src/providers/postgres.ts @@ -1,38 +1,41 @@ -import { Pool } from 'pg' +import { Pool, PoolConfig } from 'pg' import { pascalCase } from '@casimir/helpers' +/** + * Postgres database provider with pool client auto-connect-and-release + */ export class Postgres { /** Postgres connection pool */ private pool: Pool - constructor() { - this.pool = new Pool({ // These will become environment variables - host: '0.0.0.0', - port: 5432, - database: 'postgres', - user: 'postgres', - password: 'postgres' - }) + /** + * Create a new Postgres database provider + * @param {PoolConfig} config - Postgres connection pool config + * @example + * ```ts + * const postgres = new Postgres() + * ``` + */ + constructor(config?: PoolConfig) { + this.pool = new Pool(config) process.on('exit', () => this.close()) } /** - * Query the database (with pool client auto-connect-and-release) + * Query the database * @param text SQL query * @param params Query parameters * @returns Query result * * @example * ```ts - * const pg = new Postgres() - * const res = await pg.query('SELECT $1::text as message', ['Hello world!']) - * console.log(res.rows[0].message) // Hello world! + * const { rows } = await postgres.query('SELECT * FROM messages WHERE text = $1', ['Hello world!']) + * if (rows.length) console.log(rows[0].text) // Hello world! * ``` */ - async query(text: string, params: any[] = []) { // Todo - use strict @casimir/types for params + async query(text: string, params: any[] = []) { // Todo - use union of stricter @casimir/types for params const client = await this.pool.connect() const res = await client.query(text, params) - console.log('Result:', res) const { rows } = res /** Convert snake_case to PascalCase */ diff --git a/common/data/src/providers/schema.ts b/common/data/src/providers/schema.ts index 0a86871bd..e6fbf0e26 100644 --- a/common/data/src/providers/schema.ts +++ b/common/data/src/providers/schema.ts @@ -3,7 +3,7 @@ import { JsonSchema } from '../interfaces/JsonSchema' export type JsonType = 'string' | 'number' | 'integer' | 'boolean' | 'object' | 'array' | 'null' export type GlueType = glue.Type -export type PgType = 'string' | 'integer' | 'boolean' | 'double' | 'decimal' | 'bigint' | 'timestamp' | 'json' | 'date' +export type PgType = 'STRING' | 'INTEGER' | 'BOOLEAN' | 'DOUBLE' | 'DECIMAL' | 'BIGINT' | 'TIMESTAMP' | 'JSON' | 'DATE' export class Schema { /** Input JSON Schema object */ @@ -66,22 +66,22 @@ export class Schema { const columns = Object.keys(this.jsonSchema.properties).map((name: string) => { const property = this.jsonSchema.properties[name] let type = { - string: 'varchar', - number: 'double', - integer: 'integer', - boolean: 'boolean', - object: 'json', - array: 'json', - null: 'varchar' + string: 'VARCHAR', + number: 'DOUBLE', + integer: 'INTEGER', + boolean: 'BOOLEAN', + object: 'JSON', + array: 'JSON', + null: 'VARCHAR' }[property.type as JsonType] as PgType - if (name.endsWith('_at')) type = 'timestamp' - if (name.includes('balance')) type = 'bigint' + if (name.endsWith('_at')) type = 'TIMESTAMP' + if (name.includes('balance')) type = 'BIGINT' let column = `${name} ${type}` const comment = property.description - if (comment.includes('PK')) column += ' primary key' + if (comment.includes('PK')) column += ' PRIMARY KEY' return column }) @@ -89,6 +89,6 @@ export class Schema { /** Make table name plural of schema objects (todo: check edge-cases) */ const tableName = this.jsonSchema.title.toLowerCase() + 's' - return `create table ${tableName} (\n\t${columns.join(',\n\t')}\n);` + return `CREATE TABLE ${tableName} (\n\t${columns.join(',\n\t')}\n);` } } \ No newline at end of file diff --git a/common/ethers-helpers/package.json b/common/ethers-helpers/package.json deleted file mode 100644 index 96a301fae..000000000 --- a/common/ethers-helpers/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "@casimir/ethers-helpers", - "private": true, - "main": "src/index.ts", - "scripts": { - "build": "esbuild src/index.ts --bundle --minify --sourcemap --platform=node --target=esnext --outfile=dist/index.js", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "dependencies": { - "ethers": "^5.7.2" - }, - "devDependencies": { - "@types/node": "^17.0.38", - "esbuild": "^0.15.9" - } -} diff --git a/common/ethers-helpers/src/index.ts b/common/ethers-helpers/src/index.ts deleted file mode 100644 index 40ffaad4f..000000000 --- a/common/ethers-helpers/src/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { ethers } from 'ethers' - -async function getAddress(mnemonic?: string) { - const wallet = getWallet(mnemonic) - return wallet.address -} - -async function getKeystore(mnemonic?: string) { - const wallet = getWallet(mnemonic) - const keystoreString = await wallet.encrypt('') - return JSON.parse(keystoreString) -} - -function getWallet(mnemonic?: string) { - if (mnemonic) { - return ethers.Wallet.fromMnemonic(mnemonic) - } - return ethers.Wallet.createRandom() -} - -export { getAddress, getKeystore, getWallet } diff --git a/common/ethers-helpers/tsconfig.json b/common/ethers-helpers/tsconfig.json deleted file mode 100644 index ff180ef05..000000000 --- a/common/ethers-helpers/tsconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "compilerOptions": { - "target": "ESNext", - "strict": true, - "preserveConstEnums": true, - "noEmit": true, - "sourceMap": false, - "module": "CommonJS", - "moduleResolution": "Node", - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "isolatedModules": true - }, - "exclude": [ - "node_modules" - ], - "include": [ - "./src/*" - ], - "typeRoots": [ - "node_modules/@types" - ] -} \ No newline at end of file diff --git a/common/ethers-ledger-signer/.gitignore b/common/ethers-ledger-signer/.gitignore deleted file mode 100644 index 04c01ba7b..000000000 --- a/common/ethers-ledger-signer/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules/ -dist/ \ No newline at end of file diff --git a/common/ethers-ledger-signer/package.json b/common/ethers-ledger-signer/package.json deleted file mode 100644 index 3452efbd8..000000000 --- a/common/ethers-ledger-signer/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "@casimir/ethers-ledger-signer", - "private": "true", - "main": "src/index.ts", - "scripts": { - "build": "esbuild src/index.ts --bundle --minify --sourcemap --platform=node --target=esnext --outfile=dist/index.js", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "dependencies": { - "@ledgerhq/hw-app-eth": "^6.30.4", - "@ledgerhq/hw-transport": "^6.27.10", - "@ledgerhq/hw-transport-webusb": "^6.27.10", - "ethers": "^5.7.2" - } -} diff --git a/common/ethers-ledger-signer/src/index.ts b/common/ethers-ledger-signer/src/index.ts deleted file mode 100644 index b4e5d235d..000000000 --- a/common/ethers-ledger-signer/src/index.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { ethers } from 'ethers' -import Eth, { ledgerService } from '@ledgerhq/hw-app-eth' -import useTransports from './providers/transports' -import { EthersLedgerSignerOptions } from './interfaces/EthersLedgerSignerOptions' - -const defaultPath = 'm/44\'/60\'/0\'/0/0' -const defaultType = 'usb' -const { createUSBTransport, createSpeculosTransport } = useTransports() -const transportCreators = { - 'usb': createUSBTransport, - 'speculos': createSpeculosTransport -} - -export default class EthersLedgerSigner extends ethers.Signer { - readonly type: string - readonly path: string - readonly baseURL?: string - readonly _eth?: Promise - - constructor(options: EthersLedgerSignerOptions) { - super() - - if (!options.type) options.type = defaultType - if (!options.path) options.path = defaultPath - this.type = options.type - this.path = options.path - this.baseURL = options.baseURL - - // Override readonly provider for ethers.Signer - if (options.provider) { - ethers.utils.defineReadOnly(this, 'provider', options.provider) - } - - // Set readonly _eth to Promise - const transportCreatorType = this.type as keyof typeof transportCreators - const transportCreator = transportCreators[transportCreatorType] - if (!transportCreator) console.log('Unknown or unsupported type', this.type) - ethers.utils.defineReadOnly(this, '_eth', transportCreator(this.baseURL).then(transport => { - return new Eth(transport) - })) - } - - _retry(callback: (eth: Eth) => Promise, timeout?: number): Promise { - // The async-promise-executor is ok since _retry handles necessary errors - // eslint-disable-next-line no-async-promise-executor - return new Promise(async (resolve, reject) => { - const ledgerConnectionError = 'Please make sure Ledger is ready and retry' - if (timeout && timeout > 0) { - setTimeout(() => reject(new Error(ledgerConnectionError)), timeout) - } - - const eth = await this._eth as Eth - - // Wait up to 5 seconds - for (let i = 0; i < 50; i++) { - try { - const result = await callback(eth) - return resolve(result) - } catch (error) { - if ((error as { id: string }).id !== 'TransportLocked') { - return reject(error) - } - } - await new Promise((resolve) => { - setTimeout(resolve, 100) - }) - } - - return reject(new Error(ledgerConnectionError)) - }) - } - - async getAddress(): Promise { - const { address } = await this._retry((eth) => eth.getAddress(this.path)) - return ethers.utils.getAddress(address) - } - - async signMessage(message: ethers.utils.Bytes | string): Promise { - if (typeof (message) === 'string') { - message = ethers.utils.toUtf8Bytes(message) - } - const messageHex = ethers.utils.hexlify(message).substring(2) - - const signature = await this._retry((eth) => eth.signPersonalMessage(this.path, messageHex)) - signature.r = '0x' + signature.r - signature.s = '0x' + signature.s - return ethers.utils.joinSignature(signature) - } - - async signTransaction(transaction: ethers.providers.TransactionRequest): Promise { - const tx = await ethers.utils.resolveProperties(transaction) - const baseTx: ethers.utils.UnsignedTransaction = { - chainId: (tx.chainId || undefined), - data: (tx.data || undefined), - gasLimit: (tx.gasLimit || undefined), - gasPrice: (tx.gasPrice || undefined), - nonce: (tx.nonce ? ethers.BigNumber.from(tx.nonce).toNumber(): undefined), - to: (tx.to || undefined), - value: (tx.value || undefined), - type: (tx.type || undefined) - } - - const unsignedTx = ethers.utils.serializeTransaction(baseTx).substring(2) - const resolution = await ledgerService.resolveTransaction(unsignedTx, {}, {}) - const signature = await this._retry((eth) => eth.signTransaction(this.path, unsignedTx, resolution)) - - return ethers.utils.serializeTransaction(baseTx, { - v: ethers.BigNumber.from('0x' + signature.v).toNumber(), - r: ('0x' + signature.r), - s: ('0x' + signature.s), - }) - } - - // Populates all fields in a transaction, signs it and sends it to the network - async sendTransaction(transaction: ethers.utils.Deferrable): Promise { - this._checkProvider('sendTransaction') - const tx = await this.populateTransaction(transaction) - const signedTx = await this.signTransaction(tx) - return await (this.provider as ethers.providers.JsonRpcProvider).sendTransaction(signedTx) - } - - connect(provider: ethers.providers.Provider): ethers.Signer { - const options = { - provider, - type: this.type, - path: this.path, - baseURL: this.baseURL - } - return new EthersLedgerSigner(options) - } -} \ No newline at end of file diff --git a/common/ethers-ledger-signer/src/interfaces/EthersLedgerSignerOptions.ts b/common/ethers-ledger-signer/src/interfaces/EthersLedgerSignerOptions.ts deleted file mode 100644 index 2af5eb2da..000000000 --- a/common/ethers-ledger-signer/src/interfaces/EthersLedgerSignerOptions.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { ethers } from 'ethers' - -export interface EthersLedgerSignerOptions { - provider?: ethers.providers.Provider - type?: string - path?: string - baseURL?: string -} \ No newline at end of file diff --git a/common/ethers-ledger-signer/src/providers/transports.ts b/common/ethers-ledger-signer/src/providers/transports.ts deleted file mode 100644 index 7e0de2360..000000000 --- a/common/ethers-ledger-signer/src/providers/transports.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Transport from '@ledgerhq/hw-transport' -import TransportSpeculosHTTP from '@casimir/transport-speculos-http' -import TransportWebUSB from '@ledgerhq/hw-transport-webusb' - -export default function useTransports() { - - async function createUSBTransport(): Promise { - return await TransportWebUSB.create() - } - - async function createSpeculosTransport(baseURL?: string): Promise { - return await TransportSpeculosHTTP.open(baseURL) - } - - return { createUSBTransport, createSpeculosTransport } -} \ No newline at end of file diff --git a/common/ethers-ledger-signer/tsconfig.json b/common/ethers-ledger-signer/tsconfig.json deleted file mode 100644 index 4c868b9bc..000000000 --- a/common/ethers-ledger-signer/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "target": "ESNext", - "strict": true, - "preserveConstEnums": true, - "noEmit": true, - "sourceMap": false, - "module": "commonjs", - "moduleResolution": "node", - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "isolatedModules": true, - }, - "exclude": [ - "node_modules" - ], - "include": [ - "./src/*" - ] -} \ No newline at end of file diff --git a/common/ethers-trezor-signer/.gitignore b/common/ethers-trezor-signer/.gitignore deleted file mode 100644 index 04c01ba7b..000000000 --- a/common/ethers-trezor-signer/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules/ -dist/ \ No newline at end of file diff --git a/common/ethers-trezor-signer/package.json b/common/ethers-trezor-signer/package.json deleted file mode 100644 index 421782392..000000000 --- a/common/ethers-trezor-signer/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "@casimir/ethers-trezor-signer", - "private": "true", - "main": "src/index.ts", - "scripts": { - "dev": "npx esno src/index.ts", - "build": "esbuild src/index.ts --bundle --minify --sourcemap --platform=node --target=esnext --outfile=dist/index.js", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "dependencies": { - "@trezor/connect-web": "^9.0.3", - "ethers": "^5.7.2" - } -} diff --git a/common/ethers-trezor-signer/src/interfaces/EthersTrezorSignerOptions.ts b/common/ethers-trezor-signer/src/interfaces/EthersTrezorSignerOptions.ts deleted file mode 100644 index 424100895..000000000 --- a/common/ethers-trezor-signer/src/interfaces/EthersTrezorSignerOptions.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { ethers } from 'ethers' - -export interface EthersTrezorSignerOptions { - provider?: ethers.providers.Provider - path?: string -} \ No newline at end of file diff --git a/common/ethers-trezor-signer/src/interfaces/MessageSignature.ts b/common/ethers-trezor-signer/src/interfaces/MessageSignature.ts deleted file mode 100644 index e3f013ea8..000000000 --- a/common/ethers-trezor-signer/src/interfaces/MessageSignature.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface MessageSignature { - address: string - signature: string -} \ No newline at end of file diff --git a/common/ethers-trezor-signer/tsconfig.json b/common/ethers-trezor-signer/tsconfig.json deleted file mode 100644 index 4c868b9bc..000000000 --- a/common/ethers-trezor-signer/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "target": "ESNext", - "strict": true, - "preserveConstEnums": true, - "noEmit": true, - "sourceMap": false, - "module": "commonjs", - "moduleResolution": "node", - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "isolatedModules": true, - }, - "exclude": [ - "node_modules" - ], - "include": [ - "./src/*" - ] -} \ No newline at end of file diff --git a/common/helpers/package.json b/common/helpers/package.json index e3db889f1..76502f285 100644 --- a/common/helpers/package.json +++ b/common/helpers/package.json @@ -8,7 +8,8 @@ }, "dependencies": { "@aws-sdk/client-secrets-manager": "^3.204.0", - "@aws-sdk/credential-providers": "^3.204.0" + "@aws-sdk/credential-providers": "^3.204.0", + "ethers": "^5.7.2" }, "devDependencies": { "@types/node": "^17.0.38", diff --git a/common/helpers/src/index.ts b/common/helpers/src/index.ts index 586137295..e75dc48f6 100644 --- a/common/helpers/src/index.ts +++ b/common/helpers/src/index.ts @@ -1,6 +1,7 @@ import { spawn } from 'child_process' import { fromIni } from '@aws-sdk/credential-providers' import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager' +import { ethers } from 'ethers' /** * Gets a secret from AWS Secrets Manager. @@ -106,4 +107,37 @@ export async function spawnPromise(fullCommand: string) { child.on('error', reject) child.on('exit', resolve) }) +} + +/** + * Get a wallet address (optionially from a mnemonic) + * @param mnemonic - The wallet mnemonic (optional) + * @returns The wallet address + */ +export async function getWalletAddress(mnemonic?: string) { + const wallet = getWallet(mnemonic) + return wallet.address +} + +/** + * Get a wallet keystore (optionially from a mnemonic) + * @param mnemonic - The wallet mnemonic (optional) + * @returns The wallet keystore + */ +export async function getWalletKeystore(mnemonic?: string) { + const wallet = getWallet(mnemonic) + const keystoreString = await wallet.encrypt('') + return JSON.parse(keystoreString) +} + +/** + * Get a wallet (optionially from a mnemonic) + * @param mnemonic - The wallet mnemonic (optional) + * @returns The wallet + */ +export function getWallet(mnemonic?: string) { + if (mnemonic) { + return ethers.Wallet.fromMnemonic(mnemonic) + } + return ethers.Wallet.createRandom() } \ No newline at end of file diff --git a/common/bitcoin-ledger-signer/.gitignore b/common/speculos/.gitignore similarity index 100% rename from common/bitcoin-ledger-signer/.gitignore rename to common/speculos/.gitignore diff --git a/common/transport-speculos-http/package.json b/common/speculos/package.json similarity index 87% rename from common/transport-speculos-http/package.json rename to common/speculos/package.json index d2ebe8122..ca69c674b 100644 --- a/common/transport-speculos-http/package.json +++ b/common/speculos/package.json @@ -1,5 +1,5 @@ { - "name": "@casimir/transport-speculos-http", + "name": "@casimir/speculos", "private":"true", "main": "src/index.ts", "scripts": { diff --git a/common/transport-speculos-http/src/index.ts b/common/speculos/src/index.ts similarity index 96% rename from common/transport-speculos-http/src/index.ts rename to common/speculos/src/index.ts index ba95c97ce..0c0df8c55 100644 --- a/common/transport-speculos-http/src/index.ts +++ b/common/speculos/src/index.ts @@ -8,7 +8,7 @@ import Transport from '@ledgerhq/hw-transport' * const transport = await TransportSpeculosHTTP.create() * const res = await transport.send(0xE0, 0x01, 0, 0) */ -export default class TransportSpeculosHTTP extends Transport { +export class TransportSpeculosHTTP extends Transport { baseURL: string eventStream!: EventSource @@ -22,7 +22,7 @@ export default class TransportSpeculosHTTP extends Transport { static list = (): Promise => Promise.resolve([]) static listen = () => ({ // eslint-disable-next-line @typescript-eslint/no-empty-function - unsubscribe: () => {}, + unsubscribe: () => { }, }) static open = async ( diff --git a/common/transport-speculos-http/tsconfig.json b/common/speculos/tsconfig.json similarity index 100% rename from common/transport-speculos-http/tsconfig.json rename to common/speculos/tsconfig.json diff --git a/common/transport-speculos-http/.gitignore b/common/transport-speculos-http/.gitignore deleted file mode 100644 index 04c01ba7b..000000000 --- a/common/transport-speculos-http/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules/ -dist/ \ No newline at end of file diff --git a/common/ethers-helpers/.gitignore b/common/wallets/.gitignore similarity index 100% rename from common/ethers-helpers/.gitignore rename to common/wallets/.gitignore diff --git a/common/bitcoin-ledger-signer/package.json b/common/wallets/package.json similarity index 66% rename from common/bitcoin-ledger-signer/package.json rename to common/wallets/package.json index f91022181..37c47ae61 100644 --- a/common/bitcoin-ledger-signer/package.json +++ b/common/wallets/package.json @@ -1,5 +1,5 @@ { - "name": "@casimir/bitcoin-ledger-signer", + "name": "@casimir/wallets", "private": "true", "main": "src/index.ts", "scripts": { @@ -8,7 +8,10 @@ }, "dependencies": { "@ledgerhq/hw-app-btc": "^9.1.2", + "@ledgerhq/hw-app-eth": "^6.30.4", "@ledgerhq/hw-transport": "^6.27.10", - "@ledgerhq/hw-transport-webusb": "^6.27.10" + "@ledgerhq/hw-transport-webusb": "^6.27.10", + "@trezor/connect-web": "^9.0.3", + "ethers": "^5.7.2" } } diff --git a/common/wallets/src/index.ts b/common/wallets/src/index.ts new file mode 100644 index 000000000..d5136f81d --- /dev/null +++ b/common/wallets/src/index.ts @@ -0,0 +1,4 @@ +import { BitcoinLedgerSigner, EthersLedgerSigner } from './providers/ledger' +import { EthersTrezorSigner } from './providers/trezor' + +export { BitcoinLedgerSigner, EthersLedgerSigner, EthersTrezorSigner } \ No newline at end of file diff --git a/common/bitcoin-ledger-signer/src/interfaces/TransactionInit.ts b/common/wallets/src/interfaces/TransactionInit.ts similarity index 100% rename from common/bitcoin-ledger-signer/src/interfaces/TransactionInit.ts rename to common/wallets/src/interfaces/TransactionInit.ts diff --git a/common/wallets/src/providers/ledger.ts b/common/wallets/src/providers/ledger.ts new file mode 100644 index 000000000..17e13cfa8 --- /dev/null +++ b/common/wallets/src/providers/ledger.ts @@ -0,0 +1,241 @@ +import { ethers } from 'ethers' +import Btc from '@ledgerhq/hw-app-btc' +import Eth, { ledgerService } from '@ledgerhq/hw-app-eth' +import Transport from '@ledgerhq/hw-transport' +import { TransportSpeculosHTTP } from '@casimir/speculos' +import TransportWebUSB from '@ledgerhq/hw-transport-webusb' +import { TransactionInit } from '../interfaces/TransactionInit' + +const transports = { + 'usb': async function createUSBTransport(): Promise { + return await TransportWebUSB.create() + }, + 'speculos': async function createSpeculosTransport(baseURL?: string): Promise { + return await TransportSpeculosHTTP.open(baseURL) + } +} + +export interface LedgerSignerOptions { + provider?: ethers.providers.Provider + type?: string + path?: string + baseURL?: string +} + +export class BitcoinLedgerSigner { + readonly type: string = 'usb' + readonly path: string = '84\'/0\'/0\'/0/0' + readonly baseURL?: string + btc?: Promise + + constructor(options: LedgerSignerOptions) { + if (options.type) this.type = options.type + if (options.path) this.path = options.path + this.baseURL = options.baseURL + + const transportCreatorType = this.type as keyof typeof transports + const transportCreator = transports[transportCreatorType] + if (!transportCreator) console.log('Unknown or unsupported type', this.type) + this.btc = transportCreator(this.baseURL).then(transport => { + // TODO: Add conditional for testnet + return new Btc({ transport, currency: 'bitcoin_testnet' }) + // return new Btc({ transport, currency: 'bitcoin' }) + }) + } + + retry(callback: (btc: Btc) => Promise, timeout?: number): Promise { + // The async-promise-executor is ok since retry handles necessary errors + // eslint-disable-next-line no-async-promise-executor + return new Promise(async (resolve, reject) => { + const ledgerConnectionError = 'Please make sure Ledger is ready and retry' + if (timeout && timeout > 0) { + setTimeout(() => reject(new Error(ledgerConnectionError)), timeout) + } + + const btc = await this.btc as Btc + + // Wait up to 5 seconds + for (let i = 0; i < 50; i++) { + try { + const result = await callback(btc) + return resolve(result) + } catch (error) { + if ((error as { id: string }).id !== 'TransportLocked') { + return reject(error) + } + } + await new Promise((resolve) => { + setTimeout(resolve, 100) + }) + } + + return reject(new Error(ledgerConnectionError)) + }) + } + + async getAddress(): Promise { + const { bitcoinAddress: address } = await this.retry((btc) => btc.getWalletPublicKey( + this.path, + { verify: false, format: 'bech32' } + )) + return address + } + + async signMessage(message: string): Promise { + try { + const messageHex = Buffer.from(message).toString('hex') + const result = await this.retry((btc) => btc.signMessage( + this.path, + messageHex + )) + const v = result['v'] + 27 + 4 + const signature = Buffer.from(v.toString(16) + result['r'] + result['s'], 'hex').toString('base64') + console.log('Signature : ' + signature) + return signature + } catch (err) { + console.log(err) + throw err + } + } + + // async signTransaction(path: string, transaction: any) { + // } + + async sendTransaction({ from, to, value }: TransactionInit): Promise { + // Build the transaction object using splitTransaction to then pass into getTrustedInput + // Create a transaction hex string + const transactionHex = 'f85f49b51366f7150d2adea6544bc256743707a38e2bdfbf839349ba1ff2875c' + const isSegwitSupported = false + const tx = await this.retry(async (btc) => btc.splitTransaction( + transactionHex, + isSegwitSupported + )) + return new Promise(resolve => resolve('')) + // const output_index = 1 + // const inputs = [[tx, output_index]] + // Get trusted input (returns a string) using getTrustedInput + // const trustedInput = await this.retry((btc) => btc.getTrustedInput( + // 0, + + // )) + + // Build the CreateTransactionArg and pass it to createPaymentTransaction + } +} + +export class EthersLedgerSigner extends ethers.Signer { + readonly type: string = 'usb' + readonly path: string = 'm/44\'/60\'/0\'/0/0' + readonly baseURL?: string + readonly eth?: Promise + + constructor(options: LedgerSignerOptions) { + super() + + if (options.type) this.type + if (options.path) this.path = options.path + this.baseURL = options.baseURL + + // Override readonly provider for ethers.Signer + if (options.provider) { + ethers.utils.defineReadOnly(this, 'provider', options.provider) + } + + // Set readonly eth to Promise + const transportCreatorType = this.type as keyof typeof transports + const transportCreator = transports[transportCreatorType] + if (!transportCreator) console.log('Unknown or unsupported type', this.type) + ethers.utils.defineReadOnly(this, 'eth', transportCreator(this.baseURL).then(transport => { + return new Eth(transport) + })) + } + + retry(callback: (eth: Eth) => Promise, timeout?: number): Promise { + // The async-promise-executor is ok since retry handles necessary errors + // eslint-disable-next-line no-async-promise-executor + return new Promise(async (resolve, reject) => { + const ledgerConnectionError = 'Please make sure Ledger is ready and retry' + if (timeout && timeout > 0) { + setTimeout(() => reject(new Error(ledgerConnectionError)), timeout) + } + + const eth = await this.eth as Eth + + // Wait up to 5 seconds + for (let i = 0; i < 50; i++) { + try { + const result = await callback(eth) + return resolve(result) + } catch (error) { + if ((error as { id: string }).id !== 'TransportLocked') { + return reject(error) + } + } + await new Promise((resolve) => { + setTimeout(resolve, 100) + }) + } + + return reject(new Error(ledgerConnectionError)) + }) + } + + async getAddress(): Promise { + const { address } = await this.retry((eth) => eth.getAddress(this.path)) + return ethers.utils.getAddress(address) + } + + async signMessage(message: ethers.utils.Bytes | string): Promise { + if (typeof (message) === 'string') { + message = ethers.utils.toUtf8Bytes(message) + } + const messageHex = ethers.utils.hexlify(message).substring(2) + + const signature = await this.retry((eth) => eth.signPersonalMessage(this.path, messageHex)) + signature.r = '0x' + signature.r + signature.s = '0x' + signature.s + return ethers.utils.joinSignature(signature) + } + + async signTransaction(transaction: ethers.providers.TransactionRequest): Promise { + const tx = await ethers.utils.resolveProperties(transaction) + const baseTx: ethers.utils.UnsignedTransaction = { + chainId: (tx.chainId || undefined), + data: (tx.data || undefined), + gasLimit: (tx.gasLimit || undefined), + gasPrice: (tx.gasPrice || undefined), + nonce: (tx.nonce ? ethers.BigNumber.from(tx.nonce).toNumber() : undefined), + to: (tx.to || undefined), + value: (tx.value || undefined), + type: (tx.type || undefined) + } + + const unsignedTx = ethers.utils.serializeTransaction(baseTx).substring(2) + const resolution = await ledgerService.resolveTransaction(unsignedTx, {}, {}) + const signature = await this.retry((eth) => eth.signTransaction(this.path, unsignedTx, resolution)) + + return ethers.utils.serializeTransaction(baseTx, { + v: ethers.BigNumber.from('0x' + signature.v).toNumber(), + r: ('0x' + signature.r), + s: ('0x' + signature.s), + }) + } + + // Populates all fields in a transaction, signs it and sends it to the network + async sendTransaction(transaction: ethers.utils.Deferrable): Promise { + this._checkProvider('sendTransaction') + const tx = await this.populateTransaction(transaction) + const signedTx = await this.signTransaction(tx) + return await (this.provider as ethers.providers.JsonRpcProvider).sendTransaction(signedTx) + } + + connect(provider: ethers.providers.Provider): ethers.Signer { + const options = { + provider, + type: this.type, + path: this.path, + baseURL: this.baseURL + } + return new EthersLedgerSigner(options) + } +} \ No newline at end of file diff --git a/common/ethers-trezor-signer/src/index.ts b/common/wallets/src/providers/trezor.ts similarity index 76% rename from common/ethers-trezor-signer/src/index.ts rename to common/wallets/src/providers/trezor.ts index 8f5bb2ae5..cfd8c8e47 100644 --- a/common/ethers-trezor-signer/src/index.ts +++ b/common/wallets/src/providers/trezor.ts @@ -1,21 +1,28 @@ import { ethers } from 'ethers' -import { EthersTrezorSignerOptions } from './interfaces/EthersTrezorSignerOptions' -import { MessageSignature } from './interfaces/MessageSignature' import TrezorConnect, { Address, EthereumTransaction, EthereumSignedTx } from '@trezor/connect-web' +export interface TrezorMessageSignature { + address: string + signature: string +} + +export interface TrezorSignerOptions { + provider?: ethers.providers.Provider + path?: string +} + TrezorConnect.manifest({ email: 'support@consensusnetworks.com', appUrl: 'casimir.co' }) -const defaultPath = 'm/44\'/60\'/0\'/0/0' -export default class EthersTrezorSigner extends ethers.Signer { - readonly path: string - readonly _eth = TrezorConnect +export class EthersTrezorSigner extends ethers.Signer { + readonly path: string = 'm/44\'/60\'/0\'/0/0' + readonly eth = TrezorConnect - constructor(options: EthersTrezorSignerOptions) { + constructor(options: TrezorSignerOptions) { super() - if (!options.path) options.path = defaultPath - this.path = options.path - + /** Override readonly wallet path */ + if (options.path) this.path = options.path + // Override readonly provider for ethers.Signer if (options.provider) { ethers.utils.defineReadOnly(this, 'provider', options.provider) @@ -24,7 +31,7 @@ export default class EthersTrezorSigner extends ethers.Signer { } async getAddress(): Promise { - const { payload } = await this._eth.ethereumGetAddress({ path: this.path }) + const { payload } = await this.eth.ethereumGetAddress({ path: this.path }) const { address } = payload as Address return ethers.utils.getAddress(address) } @@ -35,8 +42,8 @@ export default class EthersTrezorSigner extends ethers.Signer { } const messageHex = ethers.utils.hexlify(message).substring(2) - const { payload } = await this._eth.ethereumSignMessage({ path: this.path, message: messageHex, hex: true}) - const { signature } = payload as MessageSignature + const { payload } = await this.eth.ethereumSignMessage({ path: this.path, message: messageHex, hex: true}) + const { signature } = payload as TrezorMessageSignature return signature } @@ -54,10 +61,13 @@ export default class EthersTrezorSigner extends ethers.Signer { nonce: transaction.nonce as string, gasLimit: transaction.gasLimit as string, gasPrice: transaction.gasPrice as string, + // Todo fix type + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore type: transaction.type as number } - const ethereumSignedTransaction = await this._eth.ethereumSignTransaction({ path: this.path, transaction: unsignedTx }) + const ethereumSignedTransaction = await this.eth.ethereumSignTransaction({ path: this.path, transaction: unsignedTx }) const { payload } = ethereumSignedTransaction const signature = payload as EthereumSignedTx diff --git a/common/bitcoin-ledger-signer/tsconfig.json b/common/wallets/tsconfig.json similarity index 100% rename from common/bitcoin-ledger-signer/tsconfig.json rename to common/wallets/tsconfig.json diff --git a/package-lock.json b/package-lock.json index 8b0ac9726..74abb8571 100644 --- a/package-lock.json +++ b/package-lock.json @@ -86,18 +86,11 @@ "vue-tsc": "^0.34.7" } }, - "common/bitcoin-ledger-signer": { - "name": "@casimir/bitcoin-ledger-signer", - "dependencies": { - "@ledgerhq/hw-app-btc": "^9.1.2", - "@ledgerhq/hw-transport": "^6.27.10", - "@ledgerhq/hw-transport-webusb": "^6.27.10" - } - }, "common/data": { "name": "@casimir/data", "dependencies": { - "@aws-cdk/aws-glue-alpha": "^2.33.0-alpha.0" + "@aws-cdk/aws-glue-alpha": "^2.33.0-alpha.0", + "pg": "^8.10.0" }, "devDependencies": { "@types/node": "^17.0.38", @@ -124,69 +117,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "common/ethers-helpers": { - "name": "@casimir/ethers-helpers", - "dependencies": { - "ethers": "^5.7.2" - }, - "devDependencies": { - "@types/node": "^17.0.38", - "esbuild": "^0.15.9" - } - }, - "common/ethers-helpers/node_modules/esbuild": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz", - "integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.15.18", - "@esbuild/linux-loong64": "0.15.18", - "esbuild-android-64": "0.15.18", - "esbuild-android-arm64": "0.15.18", - "esbuild-darwin-64": "0.15.18", - "esbuild-darwin-arm64": "0.15.18", - "esbuild-freebsd-64": "0.15.18", - "esbuild-freebsd-arm64": "0.15.18", - "esbuild-linux-32": "0.15.18", - "esbuild-linux-64": "0.15.18", - "esbuild-linux-arm": "0.15.18", - "esbuild-linux-arm64": "0.15.18", - "esbuild-linux-mips64le": "0.15.18", - "esbuild-linux-ppc64le": "0.15.18", - "esbuild-linux-riscv64": "0.15.18", - "esbuild-linux-s390x": "0.15.18", - "esbuild-netbsd-64": "0.15.18", - "esbuild-openbsd-64": "0.15.18", - "esbuild-sunos-64": "0.15.18", - "esbuild-windows-32": "0.15.18", - "esbuild-windows-64": "0.15.18", - "esbuild-windows-arm64": "0.15.18" - } - }, - "common/ethers-ledger-signer": { - "name": "@casimir/ethers-ledger-signer", - "dependencies": { - "@ledgerhq/hw-app-eth": "^6.30.4", - "@ledgerhq/hw-transport": "^6.27.10", - "@ledgerhq/hw-transport-webusb": "^6.27.10", - "ethers": "^5.7.2" - } - }, - "common/ethers-trezor-signer": { - "name": "@casimir/ethers-trezor-signer", - "dependencies": { - "@trezor/connect-web": "^9.0.3", - "ethers": "^5.7.2" - } - }, "common/exchange": { "name": "@casimir/exchange", "version": "1.0.0", @@ -245,12 +175,12 @@ "name": "@casimir/helpers", "dependencies": { "@aws-sdk/client-secrets-manager": "^3.204.0", - "@aws-sdk/credential-providers": "^3.204.0" + "@aws-sdk/credential-providers": "^3.204.0", + "ethers": "^5.7.2" }, "devDependencies": { "@types/node": "^17.0.38", - "esbuild": "^0.15.9", - "zx": "^7.1.1" + "esbuild": "^0.15.9" } }, "common/helpers/node_modules/esbuild": { @@ -290,8 +220,8 @@ "esbuild-windows-arm64": "0.15.18" } }, - "common/transport-speculos-http": { - "name": "@casimir/transport-speculos-http", + "common/speculos": { + "name": "@casimir/speculos", "dependencies": { "@ledgerhq/hw-transport": "^6.27.10" } @@ -299,6 +229,17 @@ "common/types": { "name": "@casimir/types" }, + "common/wallets": { + "name": "@casimir/wallets", + "dependencies": { + "@ledgerhq/hw-app-btc": "^9.1.2", + "@ledgerhq/hw-app-eth": "^6.30.4", + "@ledgerhq/hw-transport": "^6.27.10", + "@ledgerhq/hw-transport-webusb": "^6.27.10", + "@trezor/connect-web": "^9.0.3", + "ethers": "^5.7.2" + } + }, "contracts/ethereum": { "name": "@casimir/ethereum", "dependencies": { @@ -377,9 +318,9 @@ } }, "node_modules/@aws-cdk/asset-awscli-v1": { - "version": "2.2.99", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.99.tgz", - "integrity": "sha512-r4W/w49xFmZHxF63rcdDWt1JoWbeOoS0pGgVit75GC1mrDvW6+UcOB42m/UMR2U92j+l4JGJut4PqIeSMeKJcQ==" + "version": "2.2.100", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.100.tgz", + "integrity": "sha512-DL/2mMudltcj8zx8BV6k3AglPYzhvBDrRzAyNt4xrKE8NoA85bUlb3P6UZxU0+qjTUwbx5/+7jgFuJ9wOuQlgw==" }, "node_modules/@aws-cdk/asset-kubectl-v20": { "version": "2.1.1", @@ -387,9 +328,9 @@ "integrity": "sha512-U1ntiX8XiMRRRH5J1IdC+1t5CE89015cwyt5U63Cpk0GnMlN5+h9WsWMlKlPXZR4rdq/m806JRlBMRpBUB2Dhw==" }, "node_modules/@aws-cdk/asset-node-proxy-agent-v5": { - "version": "2.0.79", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.79.tgz", - "integrity": "sha512-vqVD0HaUp5LTpIxfVEYyV5ajIVZ4Z5drib6ZUFSRRftdiJT/UKmSJUnpkIE2jQ8UYYoBtHaLt4Odvgelr7rz1w==" + "version": "2.0.80", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.80.tgz", + "integrity": "sha512-SC6H0UBO+xv2F3TUEF/ODRgXX3BtnD8ga3pr0iey6134jvs9SioJFQ/nJ+cHnNH7L6e5T4nE73Jj8TciCbamQg==" }, "node_modules/@aws-cdk/aws-glue-alpha": { "version": "2.33.0-alpha.0", @@ -2071,10 +2012,6 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, - "node_modules/@casimir/bitcoin-ledger-signer": { - "resolved": "common/bitcoin-ledger-signer", - "link": true - }, "node_modules/@casimir/cdk": { "resolved": "infrastructure/cdk", "link": true @@ -2087,18 +2024,6 @@ "resolved": "contracts/ethereum", "link": true }, - "node_modules/@casimir/ethers-helpers": { - "resolved": "common/ethers-helpers", - "link": true - }, - "node_modules/@casimir/ethers-ledger-signer": { - "resolved": "common/ethers-ledger-signer", - "link": true - }, - "node_modules/@casimir/ethers-trezor-signer": { - "resolved": "common/ethers-trezor-signer", - "link": true - }, "node_modules/@casimir/exchange": { "resolved": "common/exchange", "link": true @@ -2119,8 +2044,8 @@ "resolved": "apps/landing", "link": true }, - "node_modules/@casimir/transport-speculos-http": { - "resolved": "common/transport-speculos-http", + "node_modules/@casimir/speculos": { + "resolved": "common/speculos", "link": true }, "node_modules/@casimir/types": { @@ -2131,6 +2056,10 @@ "resolved": "services/users", "link": true }, + "node_modules/@casimir/wallets": { + "resolved": "common/wallets", + "link": true + }, "node_modules/@casimir/web": { "resolved": "apps/web", "link": true @@ -7354,8 +7283,6 @@ "integrity": "sha512-bdM5cEGCOhDSwminryHJbRmXc1x7dPKg6Pqns3qyTwFlxsqUgxE29lsERS3PlIW1HTjoIGMUqsk1zQQwST1Yxw==", "dev": true, "hasInstallScript": true, - "optional": true, - "peer": true, "dependencies": { "node-gyp-build": "4.3.0" }, @@ -7368,8 +7295,6 @@ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", "dev": true, - "optional": true, - "peer": true, "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -8693,7 +8618,6 @@ "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.3.0.tgz", "integrity": "sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ==", "dev": true, - "peer": true, "dependencies": { "buffer": "^5.5.0", "immediate": "^3.2.3", @@ -8724,7 +8648,6 @@ "url": "https://feross.org/support" } ], - "peer": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -8735,7 +8658,6 @@ "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-1.0.1.tgz", "integrity": "sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg==", "dev": true, - "peer": true, "dependencies": { "xtend": "^4.0.2" }, @@ -9246,9 +9168,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.13", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", - "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", + "version": "10.4.14", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", + "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", "dev": true, "funding": [ { @@ -9261,8 +9183,8 @@ } ], "dependencies": { - "browserslist": "^4.21.4", - "caniuse-lite": "^1.0.30001426", + "browserslist": "^4.21.5", + "caniuse-lite": "^1.0.30001464", "fraction.js": "^4.2.0", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -11511,9 +11433,9 @@ "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==" }, "node_modules/constructs": { - "version": "10.1.272", - "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.1.272.tgz", - "integrity": "sha512-Rn5nwQoWjsVTeUOXqPsXE//uwd2Aj6Ln59/PTNIgyCUVuRMgRGTuKNT5w4cDauJWYnwsE+z15v2AvcHA85RToA==", + "version": "10.1.273", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.1.273.tgz", + "integrity": "sha512-JRaydmKm58aQm7crUcai3qGXtqhsh9e/dXyjQoZV6RiiEamf1V9A3yLLJIjit66Kl0Sub9quXgPmtqRfPYYlIg==", "engines": { "node": ">= 14.17.0" } @@ -12820,9 +12742,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.326", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.326.tgz", - "integrity": "sha512-tFNZP7VlGDp88vR7TYQ/h5svw8lzfu44PU5tfDJ+JYdcsEuWv4GpEm7hOF3c4Z/o4QQ6lNxmlGtvGaBVMRM9uQ==" + "version": "1.4.327", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.327.tgz", + "integrity": "sha512-DIk2H4g/3ZhjgiABJjVdQvUdMlSABOsjeCm6gmUzIdKxAuFrGiJ8QXMm3i09grZdDBMC/d8MELMrdwYRC0+YHg==" }, "node_modules/elliptic": { "version": "6.5.4", @@ -16330,7 +16252,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", @@ -18245,7 +18166,6 @@ "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true, "engines": { "node": ">= 4" } @@ -20355,7 +20275,6 @@ "resolved": "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz", "integrity": "sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw==", "dev": true, - "peer": true, "engines": { "node": ">=6" } @@ -32294,7 +32213,6 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.1.tgz", "integrity": "sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==", - "dev": true, "engines": { "node": ">= 14" } @@ -32618,7 +32536,6 @@ "cors": "^2.8.5", "ethers": "^5.7.2", "express": "^4.18.1", - "pg": "^8.10.0", "supertokens-node": "^13.1.2" }, "devDependencies": { diff --git a/scripts/aws/configure b/scripts/aws/configure deleted file mode 100755 index 715f198b7..000000000 --- a/scripts/aws/configure +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash -# Configure process for a specific AWS profile -# -# Example: -# -# scripts/aws/configure -# -# Further information: -# See https://docs.aws.amazon.com/ -# - -# Get variables from root .env -export $(xargs < .env) - -# Set default profile -profile="consensus-networks-dev" - -if [ ${PROFILE+x} ]; then - echo "PROFILE is set to '$PROFILE'" - profile=$PROFILE -else - export PROFILE="$profile" - echo "PROFILE is not set, using default profile '$PROFILE'" -fi - -# Override project if PROJECT is set in .env -if [ ${PROJECT+x} ]; then - echo "PROJECT is set to '$PROJECT'" -else - export PROJECT="casimir" - echo "PROJECT is not set, using default project '$PROJECT'" -fi - -# Override stage if STAGE is set in .env -if [ ${STAGE+x} ]; then - echo "STAGE is set to '$STAGE'" -else - export STAGE="dev" - echo "STAGE is not set, using default stage $STAGE" -fi - -export AWS_ACCESS_KEY_ID=$(aws configure get aws_access_key_id --profile $profile) -export AWS_SECRET_ACCESS_KEY=$(aws configure get aws_secret_access_key --profile $profile) - -if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then - echo "🙈 Could not find AWS credentials for profile '$profile'" - exit 1 -fi diff --git a/scripts/crawler/dev b/scripts/crawler/dev deleted file mode 100755 index 33597d86a..000000000 --- a/scripts/crawler/dev +++ /dev/null @@ -1,90 +0,0 @@ -#!/bin/bash -# Run a Casimir crawler -# -# Example: -# -# scripts/crawler/dev -c chains -f fork-name -n network-name -upload whether-to-upload (network overrides fork) -# -# Further information: -# See https://docs.aws.amazon.com/cdk/api/v2/ -# - -# Configure and expose variables -source scripts/aws/configure - -# Set RPC URL bases -ethereum_mainnet=https://eth-mainnet.g.alchemy.com/v2 -ethereum_testnet=https://eth-testnet.g.alchemy.com/v2 - -# Set the stage -export PUBLIC_STAGE=${STAGE} - -# Get args -while getopts :c:f:n:u: flag -do - case "${flag}" in - c) chains=${OPTARG};; - f) fork=${OPTARG};; - n) network=${OPTARG};; - u) upload=${OPTARG};; - esac -done - -# Default to ethereum if chains is not set or set vaguely -if [ -z "$chains" ] || [ "$chains" = true ]; then - chains="ethereum" -fi - -# Default to mainnet if fork is set vaguely -if [ "$fork" = true ]; then - fork="mainnet" -fi - -# Default to mainnet if network is not set or set vaguely -if [ -z "$network" ] || [ "$network" = true ]; then - network="mainnet" -fi - -# Default to true if upload is not set -if [ -z "$upload" ]; then - upload="enabled" -fi - - -export CHAINS=$chains -export FORK=$fork -export NETWORK=$network -export UPLOAD=$upload - -commands=("npm run dev --workspace @casimir/crawler") - -# Loop over comma-separated string of chains -IFS=',' read -r -a chain_list <<< "$chains" - -for chain in "${chain_list[@]}" -do - # Expose RPC URL directly if network is set to mainnet or testnet - if [ -n "$network" ]; then - # Get the RPC API key from AWS - rpc_secret_id=consensus-networks-$chain-$network - rpc_key=$(aws secretsmanager get-secret-value \ - --secret-id $rpc_secret_id \ - --query SecretString \ - --output text \ - --profile $profile) - - CHAIN=$(echo $chain | tr '[:lower:]' '[:upper:]') - rpc_base=${chain}_${network} - export "PUBLIC_${CHAIN}_URL"="${!rpc_base}/$rpc_key" - else - # Pass fork or network to chain-specific script - commands+=("npm run dev:$chain --fork=$fork") - fi -done - -for command in "${commands[@]}" -do - $command & -done - -wait && pkill -P $$ \ No newline at end of file diff --git a/scripts/crawler/test b/scripts/crawler/test deleted file mode 100755 index dff20640b..000000000 --- a/scripts/crawler/test +++ /dev/null @@ -1,83 +0,0 @@ -#!/bin/bash -# Test Casimir crawler with various network options -# -# Example: -# -# scripts/crawler/test -f -n -# -# Further information: -# See https://hardhat.org/hardhat-network/docs/overview -# - -# Set RPC URL bases -ethereum_mainnet=https://eth-mainnet.g.alchemy.com/v2 -ethereum_testnet=https://eth-testnet.g.alchemy.com/v2 - -# Get variables from root .env -export $(xargs < .env) - -# Set default profile -profile="consensus-networks-dev" - -if [ ${PROFILE+x} ]; then - echo "PROFILE is set to '$PROFILE'" - profile=$PROFILE -else - export PROFILE="$profile" - echo "PROFILE is not set, using default profile '$PROFILE'" -fi - -# Get args -while getopts :f:n: flag -do - case "${flag}" in - f) fork=${OPTARG};; - n) network=${OPTARG};; - esac -done - -# Default to mainnet if fork is set vaguely -if [ "$fork" = true ]; then - fork=mainnet -fi - -# Default to mainnet if network is set vaguely -if [ "$network" = true ]; then - network=mainnet -fi - -commands=("npm run test --workspace @casimir/crawler") - -chains="ethereum" - -# Loop over comma-separated string of chains -IFS=',' read -r -a chain_list <<< "$chains" - -for chain in "${chain_list[@]}" -do - # Expose RPC URL directly if network is set to mainnet or testnet - if [ -n "$network" ]; then - # Get the RPC API key from AWS - rpc_secret_id=consensus-networks-$chain-$network - rpc_key=$(aws secretsmanager get-secret-value \ - --secret-id $rpc_secret_id \ - --query SecretString \ - --output text \ - --profile $profile) - - CHAIN=$(echo $chain | tr '[:lower:]' '[:upper:]') - rpc_base=${chain}_${network} - - export "PUBLIC_${CHAIN}_URL"="${!rpc_base}/$rpc_key" - else - # Pass fork or network to chain-specific script - commands+=("npm run dev:$chain --fork=$fork") - fi -done - -for command in "${commands[@]}" -do - $command & -done - -wait && pkill -P $$ \ No newline at end of file diff --git a/scripts/local/dev.ts b/scripts/local/dev.ts index 1f1212b78..9a32c87d0 100644 --- a/scripts/local/dev.ts +++ b/scripts/local/dev.ts @@ -71,8 +71,8 @@ void async function () { const { chains, services, tables } = apps[app as keyof typeof apps] if (headless || mock) { - /** Mock pg database */ - $`npm run watch --seed --tables=${tables.join(',')} --workspace @casimir/data` + /** Mock postgres database */ + $`npm run watch:postgres --tables=${tables.join(',')} --workspace @casimir/data` /** Mock services */ let port = 4000 diff --git a/services/keys/build.js b/services/keys/build.js index 96ef546ac..369b16c90 100644 --- a/services/keys/build.js +++ b/services/keys/build.js @@ -8,5 +8,6 @@ esbuild.build({ minify: true, sourcemap: true, platform: 'node', - target: 'esnext' + target: 'esnext', + external: ['pg-native'] }) \ No newline at end of file diff --git a/services/users/package.json b/services/users/package.json index 559dd304e..e741725ba 100644 --- a/services/users/package.json +++ b/services/users/package.json @@ -11,7 +11,6 @@ "cors": "^2.8.5", "ethers": "^5.7.2", "express": "^4.18.1", - "pg": "^8.10.0", "supertokens-node": "^13.1.2" }, "devDependencies": { diff --git a/services/users/src/providers/db.ts b/services/users/src/providers/db.ts new file mode 100644 index 000000000..a6a990370 --- /dev/null +++ b/services/users/src/providers/db.ts @@ -0,0 +1,28 @@ +import { Postgres } from '@casimir/data' +import { User } from '@casimir/types' + +const postgres = new Postgres({ + // These will become environment variables + host: '0.0.0.0', + port: 5432, + database: 'postgres', + user: 'postgres', + password: 'postgres' +}) + +export default function useDB() { + + /** + * Get a user by address + * @param address - The user's address + * @returns + */ + async function getUser(address: string) { + const text = 'SELECT u.*, json_agg(a.*) AS accounts FROM users u JOIN accounts a ON u.address = a.owner_address WHERE u.address = $1 GROUP BY u.address' + const params = [address] + const rows = await postgres.query(text, params) + if (rows.length) return rows[0] as User + } + + return { getUser } +} \ No newline at end of file diff --git a/services/users/src/providers/users.ts b/services/users/src/providers/users.ts index 1ce74c4bd..4f9d03e41 100644 --- a/services/users/src/providers/users.ts +++ b/services/users/src/providers/users.ts @@ -1,20 +1,9 @@ import { userCollection } from '../collections/users' -import { Postgres } from './postgres' import { ProviderString } from '@casimir/types' import { User } from '@casimir/types' export default function useUsers() { - async function getUser(address: string) { - const pg = new Postgres() - const text = 'SELECT u.*, json_agg(a.*) AS accounts FROM users u JOIN accounts a ON u.address = a.owner_address WHERE u.address = $1 GROUP BY u.address' - const params = [address] - const rows = await pg.query(text, params) - if (rows.length) { - const user: User = rows[0] - return user - } - } function getMessage (address: string) { const user = userCollection.find(user => user.address === address) @@ -45,5 +34,5 @@ export default function useUsers() { * (Number.MAX_SAFE_INTEGER - 1)) + 1).toString() } - return { getUser, getMessage, updateMessage, generateNonce } + return { getMessage, updateMessage, generateNonce } } \ No newline at end of file diff --git a/services/users/src/routes/user.ts b/services/users/src/routes/user.ts index cec0a4b84..0369f657c 100644 --- a/services/users/src/routes/user.ts +++ b/services/users/src/routes/user.ts @@ -2,10 +2,10 @@ import express from 'express' import { userCollection } from '../collections/users' import { verifySession } from 'supertokens-node/recipe/session/framework/express' import { SessionRequest } from 'supertokens-node/framework/express' -import useUsers from '../providers/users' +import useDB from '../providers/db' const router = express.Router() -const { getUser } = useUsers() +const { getUser } = useDB() router.get('/', verifySession(), async (req: SessionRequest, res: express.Response) => { const address = req.session?.getUserId() as string