Skip to content

Commit

Permalink
Merge branch 'main' into @mago/connectors
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielSinclair committed May 17, 2024
2 parents b674952 + 90d6931 commit 0e004de
Show file tree
Hide file tree
Showing 17 changed files with 8,526 additions and 6,461 deletions.
7 changes: 7 additions & 0 deletions .changeset/shaggy-windows-grab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@rainbow-me/rainbowkit": minor
---

Introduced the Enhanced Provider to handle fallback resolutions when a Mainnet provider transport is unavailable.

ENS names for dApps without a Mainnet provider will now properly resolve. Additional conveniences will be soon be rolling out in RainbowKit.
2 changes: 1 addition & 1 deletion .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ It's never a fun experience to have your pull request declined after investing a

## Prerequisites

This project uses [`pnpm`](https://pnpm.io) as a package manager. The required `pnpm` version to get started is `9.1.1`.
This project uses [`pnpm`](https://pnpm.io) as a package manager. The required `pnpm` version to get started is `^9.1.0.

## Development environment

Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ name: CI
on: [pull_request, push]

env:
pnpm: 9.1.1
pnpm: 9.1.0
RAINBOW_PROVIDER_API_KEY: RAINBOW_PROVIDER_API_KEY
WALLETCONNECT_PROJECT_ID: YOUR_PROJECT_ID
ALCHEMY_ID: YOUR_ALCHEMY_ID

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
- main

env:
pnpm: 9
pnpm: 9.1.0

concurrency: ${{ github.workflow }}-${{ github.ref }}

Expand Down
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ enable-pre-post-scripts = true
package-lock = true
strict-peer-dependencies = false
dedupe-peer-dependents = true
package-manager-strict = false
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@
"devDependencies": {
"@biomejs/biome": "1.4.1",
"@changesets/cli": "2.27.1",
"@tanstack/react-query": "^5.28.4",
"@commitlint/cli": "^18.4.3",
"@commitlint/config-conventional": "^18.4.3",
"@lavamoat/preinstall-always-fail": "^2.0.0",
"@tanstack/react-query": "^5.28.4",
"@testing-library/jest-dom": "^6.2.0",
"@testing-library/react": "^14.1.2",
"@testing-library/user-event": "^14.5.2",
Expand All @@ -56,6 +56,7 @@
"@vanilla-extract/esbuild-plugin": "^2.3.5",
"@vanilla-extract/vite-plugin": "^4.0.9",
"autoprefixer": "^10.4.16",
"dotenv": "^16.4.5",
"esbuild": "^0.20.2",
"esbuild-plugin-replace": "^1.4.0",
"ethers": "^5.6.8",
Expand All @@ -75,7 +76,7 @@
"vitest": "^0.33.0",
"wagmi": "^2.5.11"
},
"packageManager": "pnpm@9.1.1",
"packageManager": "pnpm@9.1.0",
"pnpm": {
"onlyBuiltDependencies": [
"esbuild"
Expand Down
11 changes: 10 additions & 1 deletion packages/rainbowkit/build.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { vanillaExtractPlugin } from '@vanilla-extract/esbuild-plugin';
import autoprefixer from 'autoprefixer';
import 'dotenv/config';
import * as esbuild from 'esbuild';
import { replace } from 'esbuild-plugin-replace';
import postcss from 'postcss';
Expand All @@ -20,6 +21,12 @@ const getAllEntryPoints = async (rootPath) =>
);

const baseBuildConfig = (onEnd) => {
const rainbowProviderApiKey = process.env.RAINBOW_PROVIDER_API_KEY;

if (!rainbowProviderApiKey) {
throw new Error('missing RAINBOW_PROVIDER_API_KEY env variable');
}

return {
banner: {
js: '"use client";', // Required for Next 13 App Router
Expand All @@ -34,9 +41,11 @@ const baseBuildConfig = (onEnd) => {
},
plugins: [
replace({
include: /src\/components\/RainbowKitProvider\/useFingerprint.ts$/,
include:
/src\/components\/RainbowKitProvider\/useFingerprint.ts$|src\/core\/network\/enhancedProvider.ts$/,
values: {
__buildVersion: process.env.npm_package_version,
__rainbowProviderApiKey: rainbowProviderApiKey,
},
}),
vanillaExtractPlugin({
Expand Down
5 changes: 3 additions & 2 deletions packages/rainbowkit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"author": "Rainbow",
"license": "MIT",
"peerDependencies": {
"@tanstack/react-query": ">=5.0.0",
"react": ">=18",
"react-dom": ">=18",
"viem": "2.x",
Expand All @@ -52,15 +53,15 @@
"@testing-library/react": "^14.1.2",
"@testing-library/user-event": "^14.5.2",
"@types/qrcode": "^1.5.5",
"@types/ua-parser-js": "^0.7.39",
"@vanilla-extract/css-utils": "0.1.3",
"@vanilla-extract/private": "^1.0.3",
"autoprefixer": "^10.4.16",
"jsdom": "^23.0.1",
"nock": "^13.4.0",
"postcss": "^8.4.32",
"react": "^18.3.0",
"vitest": "^0.33.0",
"@types/ua-parser-js": "^0.7.39"
"vitest": "^0.33.0"
},
"dependencies": {
"@coinbase/wallet-sdk": "3.9.3",
Expand Down
19 changes: 19 additions & 0 deletions packages/rainbowkit/src/core/network/enhancedProvider.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { describe, expect, it } from 'vitest';
import { enhancedProviderHttp } from './enhancedProvider';

describe.skip('createHttpClient', () => {
it("should return 'ok' status for health check endpoint", async () => {
const { data } = await enhancedProviderHttp.get('/healthcheck');
expect(data).toStrictEqual({ status: 'ok' });
});

it('should throw an error if operation is aborted', async () => {
await expect(() => enhancedProviderHttp.get('/unknown')).rejects.toThrow();
});

it("should throw an error if endpoint doesn't exist", async () => {
await expect(() => enhancedProviderHttp.get('/unknown')).rejects.toThrow(
'Not Found',
);
});
});
9 changes: 9 additions & 0 deletions packages/rainbowkit/src/core/network/enhancedProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createHttpClient } from './internal/createHttpClient';

export const enhancedProviderHttp = createHttpClient({
baseUrl: 'https://enhanced-provider.rainbow.me',
headers: {
'x-api-key':
process.env.RAINBOW_PROVIDER_API_KEY ?? '__rainbowProviderApiKey',
},
});
12 changes: 12 additions & 0 deletions packages/rainbowkit/src/core/network/internal/createHttpClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { RainbowFetchClient, RainbowFetchRequestOpts } from './rainbowFetch';

export function createHttpClient({
baseUrl,
headers,
params,
timeout,
}: {
baseUrl: string;
} & RainbowFetchRequestOpts) {
return new RainbowFetchClient({ baseUrl, headers, params, timeout });
}
200 changes: 200 additions & 0 deletions packages/rainbowkit/src/core/network/internal/rainbowFetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
export const RAINBOW_FETCH_ERROR = 'rainbowFetchError';

export interface RainbowFetchRequestOpts extends RequestInit {
params?: ConstructorParameters<typeof URLSearchParams>[0]; // type of first argument of URLSearchParams constructor.
timeout?: number;
}

/**
* rainbowFetch fetches data and handles response edge cases and error handling.
*/
export async function rainbowFetch<TData>(
url: RequestInfo,
opts: RainbowFetchRequestOpts,
) {
// biome-ignore lint/style/noParameterAssign: ignore
opts = {
headers: {},
method: 'get',
...opts, // Any other fetch options
timeout: opts.timeout ?? 10_000, // 10 secs
};

if (!url) throw new Error('rainbowFetch: Missing url argument');

const controller = new AbortController();
const id = setTimeout(() => controller.abort(), opts.timeout);

const { body, params, headers, ...otherOpts } = opts;

const requestBody =
body && typeof body === 'object' ? JSON.stringify(opts.body) : opts.body;

const response = await fetch(`${url}${createParams(params)}`, {
...otherOpts,
body: requestBody,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
...headers,
},
signal: controller.signal,
});

clearTimeout(id);

const responseBody = (await getBody(response)) as TData;

if (response.ok) {
const { headers, status } = response;
return { data: responseBody, headers, status };
}
const errorResponseBody =
typeof responseBody === 'string' ? { error: responseBody } : responseBody;

const error = generateError({
requestBody: body,
response,
responseBody: errorResponseBody,
});

throw error;
}

function getBody(response: Response) {
const contentType = response.headers.get('Content-Type');
if (contentType?.startsWith('application/json')) {
return response.json();
}
return response.text();
}

function createParams(params: RainbowFetchRequestOpts['params']) {
return params && Object.keys(params).length
? `?${new URLSearchParams(params)}`
: '';
}

interface RainbowFetchError extends Error {
response?: Response;
responseBody?: any;
requestBody?: RequestInit['body'];
}

function generateError({
requestBody,
response,
responseBody,
}: {
requestBody: RequestInit['body'];
response: Response;
responseBody: any;
}) {
const message =
responseBody?.error ||
response?.statusText ||
'There was an error with the request.';

const error: RainbowFetchError = new Error(message);

error.response = response;
error.responseBody = responseBody;
error.requestBody = requestBody;

return error;
}

interface RainbowFetchClientOpts extends RainbowFetchRequestOpts {
baseUrl?: string;
}

export class RainbowFetchClient {
baseUrl: string;
opts: RainbowFetchRequestOpts;

constructor(opts: RainbowFetchClientOpts = {}) {
const { baseUrl = '', ...otherOpts } = opts;
this.baseUrl = baseUrl;
this.opts = otherOpts;
}

/**
* Perform a GET request with the RainbowFetchClient.
*/
get<TData>(url?: RequestInfo, opts?: RainbowFetchRequestOpts) {
return rainbowFetch<TData>(`${this.baseUrl}${url}`, {
...this.opts,
...(opts || {}),
method: 'get',
});
}

/**
* Perform a DELETE request with the RainbowFetchClient.
*/
delete(url?: RequestInfo, opts?: RainbowFetchRequestOpts) {
return rainbowFetch(`${this.baseUrl}${url}`, {
...this.opts,
...(opts || {}),
method: 'delete',
});
}

/**
* Perform a HEAD request with the RainbowFetchClient.
*/
head(url?: RequestInfo, opts?: RainbowFetchRequestOpts) {
return rainbowFetch(`${this.baseUrl}${url}`, {
...this.opts,
...(opts || {}),
method: 'head',
});
}

/**
* Perform a OPTIONS request with the RainbowFetchClient.
*/
options(url?: RequestInfo, opts?: RainbowFetchRequestOpts) {
return rainbowFetch(`${this.baseUrl}${url}`, {
...this.opts,
...(opts || {}),
method: 'options',
});
}

/**
* Perform a POST request with the RainbowFetchClient.
*/
post<TData>(url?: RequestInfo, body?: any, opts?: RainbowFetchRequestOpts) {
return rainbowFetch<TData>(`${this.baseUrl}${url}`, {
...this.opts,
...(opts || {}),
body,
method: 'post',
});
}

/**
* Perform a PUT request with the RainbowFetchClient.
*/
put<TData>(url?: RequestInfo, body?: any, opts?: RainbowFetchRequestOpts) {
return rainbowFetch<TData>(`${this.baseUrl}${url}`, {
...this.opts,
...(opts || {}),
body,
method: 'put',
});
}

/**
* Perform a PATCH request with the RainbowFetchClient.
*/
patch<TData>(url?: RequestInfo, body?: any, opts?: RainbowFetchRequestOpts) {
return rainbowFetch<TData>(`${this.baseUrl}${url}`, {
...this.opts,
...(opts || {}),
body,
method: 'patch',
});
}
}
22 changes: 22 additions & 0 deletions packages/rainbowkit/src/core/react-query/createQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export function createQueryKey<TArgs>(
/** A categorial key for the query. */
key: string,

/** Arguments to pass onto the query function. */
args: TArgs,

/** Configuration for the query key. */
config: {
/**
* A persister version number for the query.
* If a persisterVersion exists, this means that this query
* will be stored in AsyncStorage.
* When a query is stored against a persisterVersion,
* and is later changed, the cache will bust for this query,
* and it will be invalidated.
*/
persisterVersion?: number;
} = {},
) {
return [key, args, config] as const;
}

0 comments on commit 0e004de

Please sign in to comment.