Skip to content

Commit 1d59753

Browse files
feat: Add wagmi adapter for in-app wallet
1 parent ce0da51 commit 1d59753

File tree

9 files changed

+394
-23
lines changed

9 files changed

+394
-23
lines changed

.changeset/strong-panthers-notice.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
"@thirdweb-dev/wagmi-adapter": major
3+
---
4+
5+
Wagmi connector for in-app wallets
6+
7+
You can now connect to an in-app wallet in your wagmi applications.
8+
9+
```ts
10+
import { http, createConfig } from "wagmi";
11+
import { inAppWalletConnector } from "@thirdweb-dev/wagmi-adapter";
12+
13+
export const config = createConfig({
14+
chains: [sepolia],
15+
connectors: [
16+
inAppWalletConnector({
17+
clientId: "...",
18+
strategy: "google",
19+
}),
20+
],
21+
transports: {
22+
[sepolia.id]: http(),
23+
},
24+
});
25+
```

packages/wagmi-adapter/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Wagmi Adapter
2+
3+
This package enables the use of thirdweb's in-app wallets with wagmi.

packages/wagmi-adapter/package.json

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
{
2+
"name": "@thirdweb-dev/wagmi-adapter",
3+
"version": "1.0.0",
4+
"repository": {
5+
"type": "git",
6+
"url": "git+https://github.com/thirdweb-dev/js.git#main"
7+
},
8+
"license": "Apache-2.0",
9+
"bugs": {
10+
"url": "https://github.com/thirdweb-dev/js/issues"
11+
},
12+
"author": "thirdweb eng <eng@thirdweb.com>",
13+
"type": "module",
14+
"main": "./dist/cjs/exports/thirdweb.js",
15+
"module": "./dist/esm/exports/thirdweb.js",
16+
"types": "./dist/types/exports/thirdweb.d.ts",
17+
"typings": "./dist/types/exports/thirdweb.d.ts",
18+
"exports": {
19+
".": {
20+
"types": "./dist/types/exports/thirdweb.d.ts",
21+
"import": "./dist/esm/exports/thirdweb.js",
22+
"default": "./dist/cjs/exports/thirdweb.js"
23+
},
24+
"./package.json": "./package.json"
25+
},
26+
"files": ["dist/*", "src/*"],
27+
"devDependencies": {
28+
"@wagmi/core": "0.0.0-canary-20241211210803",
29+
"rimraf": "6.0.1",
30+
"thirdweb": "workspace:*"
31+
},
32+
"peerDependencies": {
33+
"@wagmi/core": "^2",
34+
"thirdweb": "^5",
35+
"typescript": ">=5.0.4"
36+
},
37+
"peerDependenciesMeta": {
38+
"typescript": {
39+
"optional": true
40+
}
41+
},
42+
"scripts": {
43+
"format": "biome format ./src --write",
44+
"lint": "biome check ./src",
45+
"fix": "biome check ./src --fix",
46+
"build": "pnpm clean && pnpm build:cjs && pnpm build:esm && pnpm build:types",
47+
"build:cjs": "tsc --project ./tsconfig.build.json --module commonjs --outDir ./dist/cjs --verbatimModuleSyntax false && printf '{\"type\":\"commonjs\"}' > ./dist/cjs/package.json",
48+
"build:esm": "tsc --project ./tsconfig.build.json --module es2020 --outDir ./dist/esm && printf '{\"type\": \"module\",\"sideEffects\":false}' > ./dist/esm/package.json",
49+
"build:types": "tsc --project ./tsconfig.build.json --module esnext --declarationDir ./dist/types --emitDeclarationOnly --declaration --declarationMap",
50+
"clean": "rimraf dist"
51+
},
52+
"engines": {
53+
"node": ">=18"
54+
}
55+
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import { EventEmitter } from "stream";
2+
import { type CreateConnectorFn, createConnector } from "@wagmi/core";
3+
import type { Prettify } from "@wagmi/core/chains";
4+
import { createThirdwebClient, defineChain, getAddress } from "thirdweb";
5+
import {
6+
EIP1193,
7+
type InAppWalletConnectionOptions,
8+
ecosystemWallet,
9+
inAppWallet as thirdwebInAppWallet,
10+
} from "thirdweb/wallets";
11+
import type { InAppWalletCreationOptions } from "thirdweb/wallets/in-app";
12+
13+
export type InAppWalletParameters = Prettify<
14+
Omit<InAppWalletConnectionOptions, "client"> &
15+
InAppWalletCreationOptions & {
16+
clientId: string;
17+
ecosystemId?: `ecosystem.${string}`;
18+
}
19+
>;
20+
21+
type Provider = EIP1193.EIP1193Provider | undefined;
22+
type Properties = {
23+
connect(parameters?: {
24+
chainId?: number | undefined;
25+
isReconnecting?: boolean | undefined;
26+
foo?: string;
27+
}): Promise<{
28+
accounts: readonly `0x${string}`[];
29+
chainId: number;
30+
}>;
31+
};
32+
type StorageItem = { "tw.lastChainId": number };
33+
34+
/**
35+
* Connect to an in-app wallet using the auth strategy of your choice.
36+
* @param args - Options for the in-app wallet connection.
37+
* @returns A wagmi connector.
38+
* @example
39+
* ```ts
40+
* import { http, createConfig } from "wagmi";
41+
* import { inAppWalletConnector } from "@thirdweb-dev/wagmi-adapter";
42+
*
43+
* export const config = createConfig({
44+
* chains: [sepolia],
45+
* connectors: [
46+
* inAppWalletConnector({
47+
* clientId: "...",
48+
* strategy: "google",
49+
* }),
50+
* ],
51+
* transports: {
52+
* [sepolia.id]: http(),
53+
* },
54+
* });
55+
* ```
56+
*/
57+
export function inAppWalletConnector(
58+
args: InAppWalletParameters,
59+
): CreateConnectorFn<Provider, Properties, StorageItem> {
60+
const client = createThirdwebClient({ clientId: args.clientId });
61+
const wallet = args.ecosystemId
62+
? ecosystemWallet(args.ecosystemId, { partnerId: args.partnerId })
63+
: thirdwebInAppWallet(args);
64+
return createConnector<Provider, Properties, StorageItem>((config) => ({
65+
id: "in-app-wallet",
66+
name: "In-App wallet",
67+
type: "in-app",
68+
connect: async (params) => {
69+
const inAppOptions = params && "client" in params ? params : undefined;
70+
const wagmiConnectOptions =
71+
params && "chainId" in params ? params : undefined;
72+
console.log("inAppOPtions", inAppOptions);
73+
console.log("wagmiConnectOptions", wagmiConnectOptions);
74+
const lastChainId = await config.storage?.getItem("tw.lastChainId");
75+
const chain = defineChain(
76+
wagmiConnectOptions?.chainId || lastChainId || 1,
77+
);
78+
const options = {
79+
client,
80+
chain,
81+
...args,
82+
} as unknown as InAppWalletConnectionOptions;
83+
const account = wagmiConnectOptions?.isReconnecting
84+
? await wallet.autoConnect({
85+
client,
86+
chain,
87+
})
88+
: await wallet.connect(options);
89+
await config.storage?.setItem("tw.lastChainId", chain.id);
90+
return { accounts: [getAddress(account.address)], chainId: chain.id };
91+
},
92+
disconnect: async () => {
93+
await wallet.disconnect();
94+
},
95+
getAccounts: async () => {
96+
const account = wallet.getAccount();
97+
if (!account) {
98+
throw new Error("Wallet not connected");
99+
}
100+
return [getAddress(account.address)];
101+
},
102+
getChainId: async () => {
103+
return wallet.getChain()?.id || 1;
104+
},
105+
getProvider: async (params) => {
106+
return EIP1193.toProvider({
107+
wallet,
108+
client,
109+
chain: wallet.getChain() || defineChain(params?.chainId || 1),
110+
});
111+
},
112+
isAuthorized: async () => true,
113+
switchChain: async (params) => {
114+
const chain = config.chains.find((x) => x.id === params.chainId);
115+
if (!chain) {
116+
throw new Error(`Chain ${params.chainId} not supported`);
117+
}
118+
await wallet.switchChain(defineChain(chain.id));
119+
return chain;
120+
},
121+
onAccountsChanged: () => {
122+
// no-op
123+
},
124+
onChainChanged: () => {
125+
// no-op
126+
},
127+
onDisconnect: () => {
128+
// no-op
129+
},
130+
}));
131+
}
132+
133+
const c = inAppWalletConnector({
134+
clientId: "...",
135+
strategy: "google",
136+
})({
137+
chains: [sepolia],
138+
emitter: new EventEmitter() as any,
139+
});
140+
141+
c.connect({});
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export {
2+
inAppWalletConnector,
3+
type InAppWalletParameters,
4+
} from "../connector.js";
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
// This tsconfig file contains the shared config for the build (tsconfig.build.json) and type checking (tsconfig.json) config.
3+
"include": [],
4+
"compilerOptions": {
5+
// Incremental builds
6+
// NOTE: Enabling incremental builds speeds up `tsc`. Keep in mind though that it does not reliably bust the cache when the `tsconfig.json` file changes.
7+
"incremental": false,
8+
9+
// Type checking
10+
"strict": true,
11+
"useDefineForClassFields": true, // Not enabled by default in `strict` mode unless we bump `target` to ES2022.
12+
"noFallthroughCasesInSwitch": true, // Not enabled by default in `strict` mode.
13+
"noImplicitReturns": true, // Not enabled by default in `strict` mode.
14+
"useUnknownInCatchVariables": true, // TODO: This would normally be enabled in `strict` mode but would require some adjustments to the codebase.
15+
"noImplicitOverride": true, // Not enabled by default in `strict` mode.
16+
"noUnusedLocals": true, // Not enabled by default in `strict` mode.
17+
"noUnusedParameters": true, // Not enabled by default in `strict` mode.
18+
"exactOptionalPropertyTypes": false, // Not enabled by default in `strict` mode.
19+
"noUncheckedIndexedAccess": true, // Not enabled by default in `strict` mode.
20+
21+
// JavaScript support
22+
"allowJs": false,
23+
"checkJs": false,
24+
25+
// Interop constraints
26+
"esModuleInterop": false,
27+
"allowSyntheticDefaultImports": true,
28+
"forceConsistentCasingInFileNames": true,
29+
"verbatimModuleSyntax": true,
30+
"importHelpers": true, // This is only used for build validation. Since we do not have `tslib` installed, this will fail if we accidentally make use of anything that'd require injection of helpers.
31+
32+
// Language and environment
33+
"moduleResolution": "NodeNext",
34+
"module": "NodeNext",
35+
"target": "ES2021", // Setting this to `ES2021` enables native support for `Node v16+`: https://github.com/microsoft/TypeScript/wiki/Node-Target-Mapping.
36+
"lib": [
37+
"ES2022", // By using ES2022 we get access to the `.cause` property on `Error` instances.
38+
"DOM" // We are adding `DOM` here to get the `fetch`, etc. types. This should be removed once these types are available via DefinitelyTyped.
39+
],
40+
41+
// Skip type checking for node modules
42+
"skipLibCheck": true,
43+
44+
// jsx for "/react" portion
45+
"jsx": "react-jsx"
46+
}
47+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"extends": "./tsconfig.base.json",
3+
"include": ["src"],
4+
"exclude": [
5+
"src/**/*.test.ts",
6+
"src/**/*.test.tsx",
7+
"src/**/*.test-d.ts",
8+
"src/**/*.bench.ts",
9+
"src/**/*.macro.ts"
10+
],
11+
"compilerOptions": {
12+
"moduleResolution": "node",
13+
"sourceMap": true,
14+
"rootDir": "./src"
15+
}
16+
}

packages/wagmi-adapter/tsconfig.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
// This configuration is used for local development and type checking.
3+
"extends": "./tsconfig.base.json",
4+
"include": ["src", "test"],
5+
"exclude": [],
6+
// "references": [{ "path": "./scripts/tsconfig.json" }],
7+
"compilerOptions": {
8+
"baseUrl": ".",
9+
"paths": {
10+
"~test/*": ["./test/src/*"]
11+
}
12+
}
13+
}

0 commit comments

Comments
 (0)