Skip to content

Commit

Permalink
Move verifier into @3cities/verifier
Browse files Browse the repository at this point in the history
The package @3cities/verifier was added to contain the standalone static
verifier.

The package @3cities/core was added to contain dependencies shared
across the verifier and interface. Many facilities were moved into
@3cities/core.

The root package.json tasks were standardized across subpackages using
the new runTaskInAllPackages helper.

The interface's tsconfig was updated to use module:"es2020" (old:
"commonjs") and moduleResolution:"bundler" (old: "node"). This adds
compatibility to import sub-exports like
@3cities/core/proto/checkout-settings. However, a weird item is that
type ConnectKitOptions could no longer be imported from any path.

Dependencies shared between any two packages were lifted into the root
package.json. Right now, that's @bufbuild/protobuf,
@tanstack/react-query, immer, viem, and wagmi.
  • Loading branch information
ryanberckmans committed Jul 12, 2024
1 parent a79a7b7 commit 0d70344
Show file tree
Hide file tree
Showing 96 changed files with 522 additions and 332 deletions.
4 changes: 0 additions & 4 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,6 @@ module.exports = {
"error",
{
"paths": [
{
"name": "ethers",
"message": "Please import from '@ethersproject/module' directly to support tree-shaking."
},
{
"name": "@lingui/macro",
"importNames": [
Expand Down
16 changes: 12 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
"react"
],
"private": true,
"dependencies": {
"@bufbuild/protobuf": "^1.10.0",
"@tanstack/react-query": "^5.47",
"immer": "^10.1.1",
"viem": "2.17.2",
"wagmi": "2.10.9"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^7.14.1",
"@typescript-eslint/parser": "^7.14.1",
Expand All @@ -15,10 +22,11 @@
"typescript": "^5.5.2"
},
"scripts": {
"lint": "yarn workspace @3cities/react-app lint && yarn workspace @3cities/eth-transfer-proxy lint",
"build": "yarn workspace @3cities/eth-transfer-proxy build && yarn workspace @3cities/react-app build",
"build:prod-test": "yarn workspace @3cities/eth-transfer-proxy build && yarn workspace @3cities/react-app build:prod-test",
"build:dev": "yarn workspace @3cities/eth-transfer-proxy build && yarn workspace @3cities/react-app build:dev",
"runTaskInAllPackages": "for package in core eth-transfer-proxy verifier react-app; do echo \"*** $TASK $package ***\" && yarn workspace @3cities/$package $TASK || exit 1; done",
"lint": "TASK=lint yarn runTaskInAllPackages",
"build": "TASK=build yarn runTaskInAllPackages",
"build:prod-test": "TASK='build:prod-test' yarn runTaskInAllPackages",
"build:dev": "TASK='build:dev' yarn runTaskInAllPackages",
"start": "yarn workspace @3cities/react-app start"
},
"workspaces": {
Expand Down
11 changes: 11 additions & 0 deletions packages/core/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// This package-specific .eslintrc.js file is automatically merged by `eslint` at runtime with our project's root .eslintrs.js https://eslint.org/docs/latest/use/configure/configuration-files#cascading-and-hierarchy
const path = require('path');

module.exports = {
"env": {
"es2023": true,
},
"parserOptions": {
"project": path.resolve(__dirname, './tsconfig.json'),
},
};
2 changes: 2 additions & 0 deletions packages/core/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# build directories
dist
6 changes: 6 additions & 0 deletions packages/core/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

# Overview

TODO

# Usage
File renamed without changes.
File renamed without changes.
55 changes: 55 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"name": "@3cities/core",
"license": "MIT",
"version": "1.0.0",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/types/index.d.ts",
"files": [
"dist/cjs/**/*",
"dist/esm/**/*",
"dist/types/**/*"
],
"exports": {
".": {
"require": "./dist/cjs/index.js",
"import": "./dist/esm/index.js",
"types": "./dist/types/index.d.ts"
},
"./proto/checkout-settings": {
"require": "./dist/cjs/gen/threecities/v1/checkout_settings_pb.js",
"import": "./dist/esm/gen/threecities/v1/checkout_settings_pb.js",
"types": "./dist/types/gen/threecities/v1/checkout_settings_pb.d.ts"
},
"./proto/logical-assets": {
"require": "./dist/cjs/gen/threecities/v1/logical_assets_pb.js",
"import": "./dist/esm/gen/threecities/v1/logical_assets_pb.js",
"types": "./dist/types/gen/threecities/v1/logical_assets_pb.d.ts"
}
},
"devDependencies": {
"@bufbuild/buf": "^1.34.0",
"@bufbuild/protoc-gen-es": "^1.10.0",
"nodemon": "^3.1.4"
},
"scripts": {
"clean:ts": "rm -rf ./dist",
"clean:cjs": "rm -rf ./dist/cjs",
"clean:esm": "rm -rf ./dist/esm",
"clean:types": "rm -rf ./dist/types",
"clean": "yarn clean:ts",
"build:cjs": "yarn clean:cjs && tsc --module commonjs --moduleResolution node --outDir ./dist/cjs --verbatimModuleSyntax false --sourceMap",
"build:esm": "yarn clean:esm && tsc --outDir ./dist/esm --sourceMap",
"build:types": "yarn clean:types && tsc --declarationDir ./dist/types --emitDeclarationOnly --declaration --declarationMap",
"build:ts": "yarn build:cjs && yarn build:esm && yarn build:types",
"build": "yarn build:ts",
"build:dev": "yarn build # NB core has no dev build, but we include this so that the root build:dev task works for all packages. Clients must apply any env-specific settings, WARNING especially to pass appropriate env vars to core's isProduction",
"build:prod-test": "yarn build # NB core has no prod-test build, but we include this so that the root build:prod-test task works for all packages. Clients must apply any env-specific settings, WARNING especially to pass appropriate env vars to core's isProduction",
"gen": "npx buf generate proto",
"lint": "eslint . --max-warnings 0 && buf lint",
"prepack": "yarn && yarn build",
"start": "nodemon --watch 'src/**/*' --ext 'ts,proto' --exec 'yarn build'",
"test": "forge test",
"test:coverage": "forge coverage"
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,7 @@
syntax = "proto3";
package threecities.v1; // we follow a major versioning scheme for our proto files. All files in the threecities.v1 package are compatible with the first major version. In the future, new major versions like threecities.v2 will be introduced. At that point, threecities.v1 types will be deprecated. The v2 app layer will attempt v2 deserialization first, falling back to v1 only if the former fails.

// LogicalAssetTicker is the set of all logical asset tickers we
// support. Our app-layer LogicalAssetTicker types are automatically
// derived from this single authoritative definition in a typesafe
// manner.
enum LogicalAssetTicker {
LOGICAL_ASSET_TICKER_UNSPECIFIED = 0;
LOGICAL_ASSET_TICKER_ETH = 1;
LOGICAL_ASSET_TICKER_USD = 2;
LOGICAL_ASSET_TICKER_EUR = 3;
LOGICAL_ASSET_TICKER_CAD = 4;
}
import "threecities/v1/logical_assets.proto";

// CheckoutSettings is a flattened variant of our app-layer type
// CheckoutSettings. This CheckoutSettings has been flattened into a
Expand Down
14 changes: 14 additions & 0 deletions packages/core/proto/threecities/v1/logical_assets.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
syntax = "proto3";
package threecities.v1; // we follow a major versioning scheme for our proto files. All files in the threecities.v1 package are compatible with the first major version. In the future, new major versions like threecities.v2 will be introduced. At that point, threecities.v1 types will be deprecated. The v2 app layer will attempt v2 deserialization first, falling back to v1 only if the former fails.

// LogicalAssetTicker is the set of all logical asset tickers we
// support. Our app-layer LogicalAssetTicker types are automatically
// derived from this single authoritative definition in a typesafe
// manner.
enum LogicalAssetTicker {
LOGICAL_ASSET_TICKER_UNSPECIFIED = 0;
LOGICAL_ASSET_TICKER_ETH = 1;
LOGICAL_ASSET_TICKER_USD = 2;
LOGICAL_ASSET_TICKER_EUR = 3;
LOGICAL_ASSET_TICKER_CAD = 4;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { DeepWritable } from "./Writable";
import { type DeepWritable } from "./Writable";
import { hasOwnProperty } from "./hasOwnProperty";
import { LogicalAssetTicker, parseLogicalAssetAmount } from "./logicalAssets";
import { type LogicalAssetTicker, parseLogicalAssetAmount } from "./logicalAssets";
import { toUppercase } from "./toUppercase";

export type ExchangeRates = Readonly<{
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IntRange } from "./IntRange";
import { type IntRange } from "./IntRange";
import { hasOwnProperty } from "./hasOwnProperty";

// Token is our type for fungible (ERC-20) tokens. An instance of
Expand Down
File renamed without changes.
41 changes: 41 additions & 0 deletions packages/core/src/caip222StyleSignature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { mainnet, sepolia } from "./chains";
import { isProduction } from "./isProduction";

// ERC-1271
// isValidSignature(bytes32 hash, bytes signature) → bytes4 magicValue
export const erc1271SmartAccountAbi = [
{
name: 'isValidSignature',
type: 'function',
stateMutability: 'view',
inputs: [
{ name: 'hash', type: 'bytes32' },
{ name: 'signature', type: 'bytes' },
],
outputs: [{ name: '', type: 'bytes4' }],
},
] as const;

export const erc1271MagicValue = "0x1626ba7e" as const;

export const caip222StyleSignatureMessageDomain = { // EIP-712 message domain
name: '3cities',
version: '1',
// NB chainId is not needed for our message as we only want to prove sender address ownership (on any chain, assuming it then applies to all chains). Note that any chainId provided here is purely signed data and not actually used by any wallet to eg. switch to that chainId prior to signing
} as const;

export const caip222StyleSignatureMessageTypes = { // EIP-712 message types
SenderAddress: [
{ name: 'senderAddress', type: 'address' },
],
} as const;

export const caip222StyleSignatureMessagePrimaryType = 'SenderAddress' as const; // EIP-712 message primaryType

export type Caip222StyleSignature = `0x${string}` | `eip1271-chainId-${number}`; // a successfully collected Caip222-style signature. `0x${string}` indicates an ordinary signature. `eip1271-chainId-${number}` indicates a smart contract wallet verified the message using eip1271 verification via a isValidSignature call on the provided chainId

export type Caip222StyleMessageToSign = {
senderAddress: `0x${string}`;
};

export const chainIdOnWhichToSignMessagesAndVerifySignatures: number = isProduction ? mainnet.id : sepolia.id; // certain wallets generate different signatures depending on which chain is active at the time of message signing. To ensure that 3cities is later able to verify any generated signature, we only allow signature generation on a static sentinel chain (ie. the L1 in production). NB eip1271 onchain signatures are handled separately and use one of chains on which the eip1271 wallet exists
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// eslint-disable-next-line no-restricted-imports -- here is our single allowed use of importing from wagmi/chains, used to construct 3cities chains which are then exported to the rest of 3cities
import { arbitrum, arbitrumNova, arbitrumSepolia, base, baseSepolia, blast, blastSepolia, immutableZkEvm, optimism, optimismSepolia, polygon, polygonAmoy, polygonZkEvm, polygonZkEvmCardona, scroll, scrollSepolia, sepolia, linea as wagmiLinea, lineaSepolia as wagmiLineaSepolia, mainnet as wagmiMainnet, mode as wagmiMode, taiko as wagmiTaiko, zkSyncSepoliaTestnet as wagmiZkSyncSeplia, zkSync, zora, zoraSepolia, type Chain } from 'wagmi/chains';
import { NonEmptyArray } from './NonEmptyArray';
import { type NonEmptyArray } from './NonEmptyArray';
import { isProduction } from './isProduction';

// ***************************************************************
Expand Down
Original file line number Diff line number Diff line change
@@ -1,53 +1,11 @@
// @generated by protoc-gen-es v1.10.0 with parameter "target=ts"
// @generated from file threecities/v1/v1.proto (package threecities.v1, syntax proto3)
// @generated from file threecities/v1/checkout_settings.proto (package threecities.v1, syntax proto3)
/* eslint-disable */
// @ts-nocheck

import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf";
import { Message, proto3 } from "@bufbuild/protobuf";

/**
* LogicalAssetTicker is the set of all logical asset tickers we
* support. Our app-layer LogicalAssetTicker types are automatically
* derived from this single authoritative definition in a typesafe
* manner.
*
* @generated from enum threecities.v1.LogicalAssetTicker
*/
export enum LogicalAssetTicker {
/**
* @generated from enum value: LOGICAL_ASSET_TICKER_UNSPECIFIED = 0;
*/
UNSPECIFIED = 0,

/**
* @generated from enum value: LOGICAL_ASSET_TICKER_ETH = 1;
*/
ETH = 1,

/**
* @generated from enum value: LOGICAL_ASSET_TICKER_USD = 2;
*/
USD = 2,

/**
* @generated from enum value: LOGICAL_ASSET_TICKER_EUR = 3;
*/
EUR = 3,

/**
* @generated from enum value: LOGICAL_ASSET_TICKER_CAD = 4;
*/
CAD = 4,
}
// Retrieve enum metadata with: proto3.getEnumType(LogicalAssetTicker)
proto3.util.setEnumType(LogicalAssetTicker, "threecities.v1.LogicalAssetTicker", [
{ no: 0, name: "LOGICAL_ASSET_TICKER_UNSPECIFIED" },
{ no: 1, name: "LOGICAL_ASSET_TICKER_ETH" },
{ no: 2, name: "LOGICAL_ASSET_TICKER_USD" },
{ no: 3, name: "LOGICAL_ASSET_TICKER_EUR" },
{ no: 4, name: "LOGICAL_ASSET_TICKER_CAD" },
]);
import { LogicalAssetTicker } from "./logical_assets_pb.js";

/**
* MessageType is an enum to help clients differentiate between
Expand Down
50 changes: 50 additions & 0 deletions packages/core/src/gen/threecities/v1/logical_assets_pb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// @generated by protoc-gen-es v1.10.0 with parameter "target=ts"
// @generated from file threecities/v1/logical_assets.proto (package threecities.v1, syntax proto3)
/* eslint-disable */
// @ts-nocheck

import { proto3 } from "@bufbuild/protobuf";

/**
* LogicalAssetTicker is the set of all logical asset tickers we
* support. Our app-layer LogicalAssetTicker types are automatically
* derived from this single authoritative definition in a typesafe
* manner.
*
* @generated from enum threecities.v1.LogicalAssetTicker
*/
export enum LogicalAssetTicker {
/**
* @generated from enum value: LOGICAL_ASSET_TICKER_UNSPECIFIED = 0;
*/
UNSPECIFIED = 0,

/**
* @generated from enum value: LOGICAL_ASSET_TICKER_ETH = 1;
*/
ETH = 1,

/**
* @generated from enum value: LOGICAL_ASSET_TICKER_USD = 2;
*/
USD = 2,

/**
* @generated from enum value: LOGICAL_ASSET_TICKER_EUR = 3;
*/
EUR = 3,

/**
* @generated from enum value: LOGICAL_ASSET_TICKER_CAD = 4;
*/
CAD = 4,
}
// Retrieve enum metadata with: proto3.getEnumType(LogicalAssetTicker)
proto3.util.setEnumType(LogicalAssetTicker, "threecities.v1.LogicalAssetTicker", [
{ no: 0, name: "LOGICAL_ASSET_TICKER_UNSPECIFIED" },
{ no: 1, name: "LOGICAL_ASSET_TICKER_ETH" },
{ no: 2, name: "LOGICAL_ASSET_TICKER_USD" },
{ no: 3, name: "LOGICAL_ASSET_TICKER_EUR" },
{ no: 4, name: "LOGICAL_ASSET_TICKER_CAD" },
]);

File renamed without changes.
File renamed without changes.
19 changes: 19 additions & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { type Caip222StyleMessageToSign, type Caip222StyleSignature, caip222StyleSignatureMessageDomain, caip222StyleSignatureMessagePrimaryType, caip222StyleSignatureMessageTypes, chainIdOnWhichToSignMessagesAndVerifySignatures, erc1271MagicValue, erc1271SmartAccountAbi } from "./caip222StyleSignature";
import { type Chain, allSupportedChainIds, arbitrum, arbitrumNova, arbitrumSepolia, base, baseSepolia, blast, blastSepolia, chainsSupportedBy3cities, fluentTestnet, getChain, getSupportedChainName, immutableZkEvm, linea, lineaSepolia, mainnet, mode, optimism, optimismSepolia, polygon, polygonAmoy, polygonZkEvm, polygonZkEvmCardona, scroll, scrollSepolia, sepolia, taiko, zkSync, zkSyncSepolia, zora, zoraSepolia } from "./chains";
import { type ExchangeRates, areExchangeRatesEqual, convert, mergeExchangeRates } from "./ExchangeRates";
import { getConfirmationsToWait } from "./getConfirmationsToWait";
import { hasOwnProperty, hasOwnPropertyOfType } from "./hasOwnProperty";
import { type IntRange } from "./IntRange";
import { isProduction } from "./isProduction";
import { type LogicalAsset, type LogicalAssetTicker, addCanonicalFormatToLogicalAssetValue, addSymbolToLogicalAssetValue, addVerboseFormatToLogicalAssetValue, allLogicalAssetTickers, convertLogicalAssetUnits, defaultSmallAmountsPerLogicalAsset, getDecimalsToRenderForLogicalAssetTicker, getDefaultTruncateTrailingZeroesForLogicalAssetTicker, logicalAssetDecimals, logicalAssets, logicalAssetsByTicker, parseLogicalAssetAmount } from "./logicalAssets";
import { getAllNativeCurrenciesAndTokensForLogicalAssetTicker, getDecimalsToRenderForTokenTicker, getDefaultTruncateTrailingZeroesForTokenTicker, getLogicalAssetTickerForTokenOrNativeCurrencyTicker, isTokenTickerSupportedByLogicalAsset } from "./logicalAssetsToTokens";
import { type NonEmptyArray, ensureNonEmptyArray } from "./NonEmptyArray";
import { alchemyHttpUrl, infuraHttpUrl } from './rpcUrls';
import { type NativeCurrency, type Token, isNativeCurrency, isToken } from './Token';
import { type TokenKey, allTokenTickers, getTokenByTokenKey, getTokenKey, isTokenSupported, nativeCurrencies, tokens } from "./tokens";
import { toUppercase } from "./toUppercase";
import { type DeepWritable, type Writable } from "./Writable";

// TODO consider adding more export subpaths (similar to @3cities/core/proto/checkout-settings), so not all exports are in the @3cities/core namespace, eg. @3cities/core/chains

export { addCanonicalFormatToLogicalAssetValue, addSymbolToLogicalAssetValue, addVerboseFormatToLogicalAssetValue, alchemyHttpUrl, allLogicalAssetTickers, allSupportedChainIds, allTokenTickers, arbitrum, arbitrumNova, arbitrumSepolia, areExchangeRatesEqual, base, baseSepolia, blast, blastSepolia, caip222StyleSignatureMessageDomain, caip222StyleSignatureMessagePrimaryType, caip222StyleSignatureMessageTypes, chainIdOnWhichToSignMessagesAndVerifySignatures, chainsSupportedBy3cities, convert, convertLogicalAssetUnits, defaultSmallAmountsPerLogicalAsset, ensureNonEmptyArray, erc1271MagicValue, erc1271SmartAccountAbi, fluentTestnet, getAllNativeCurrenciesAndTokensForLogicalAssetTicker, getChain, getConfirmationsToWait, getDecimalsToRenderForLogicalAssetTicker, getDecimalsToRenderForTokenTicker, getDefaultTruncateTrailingZeroesForLogicalAssetTicker, getDefaultTruncateTrailingZeroesForTokenTicker, getLogicalAssetTickerForTokenOrNativeCurrencyTicker, getSupportedChainName, getTokenByTokenKey, getTokenKey, hasOwnProperty, hasOwnPropertyOfType, immutableZkEvm, infuraHttpUrl, isNativeCurrency, isProduction, isToken, isTokenSupported, isTokenTickerSupportedByLogicalAsset, linea, lineaSepolia, logicalAssetDecimals, logicalAssets, logicalAssetsByTicker, mainnet, mergeExchangeRates, mode, nativeCurrencies, optimism, optimismSepolia, parseLogicalAssetAmount, polygon, polygonAmoy, polygonZkEvm, polygonZkEvmCardona, scroll, scrollSepolia, sepolia, taiko, toUppercase, tokens, zkSync, zkSyncSepolia, zora, zoraSepolia, type Caip222StyleMessageToSign, type Caip222StyleSignature, type Chain, type DeepWritable, type ExchangeRates, type IntRange, type LogicalAsset, type LogicalAssetTicker, type NativeCurrency, type NonEmptyArray, type Token, type TokenKey, type Writable };
5 changes: 5 additions & 0 deletions packages/core/src/isProduction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

// TODO split isProduction into two different dimensions with four vars, 1. isMainnetDependencies (isTestnetDependencies = !isMainnetDependencies) and 2. isProductionEnvironment (isDevelopmentEnvironment = !isProductionEnvironment) --> see .env-cmdrc.js --> then, update some usages of today's isProduction to instead be isMainnetDependencies. For example, token/chains should be mainnet based on isMainnetDependencies and not isProduction. For example, the "Open Link" dev feature in Request Money should use isDevelopmentEnvironment --> this change allows our three environments (dev, prod-test, prod) to properly configure themselves (eg. today prod-test has isProduction=false but instead it should have isMainnetDependencies=false isProduction=true)

// isProduction is true if and only if the app is being run in production mode, ie. against production dependencies, eg. to use blockchain mainnets instead of testnets. Note that whether or not the app is being run in production mode (ie. the truthiness of isProduction) is orthogonal to the type of build being used to run the app. The app may be run with isProduction==true while in built in development mode (eg. to debug locally against production dependencies), or the app may be run with isProduction==false in a production minified build (eg. to offer a production deployment against test dependencies for demo purposes)
export const isProduction: boolean = process.env['REACT_APP_IS_PRODUCTION'] === 'true';
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { parseUnits } from 'viem';
import { IntRange } from "./IntRange";
import { LogicalAssetTicker as LogicalAssetTickerPb } from "./gen/threecities/v1/v1_pb";
import { type IntRange } from "./IntRange";
import { LogicalAssetTicker as LogicalAssetTickerPb } from "./gen/threecities/v1/logical_assets_pb";

export type LogicalAssetTicker = Exclude<keyof typeof LogicalAssetTickerPb, 'UNSPECIFIED'>; // here, we use the protobuf definition of logical asset tickers to be our single authoritative definition, and derive our app-layer logical asset ticker types from protobuf definitions in a typesafe manner.

Expand Down
Loading

0 comments on commit 0d70344

Please sign in to comment.