Skip to content

Commit

Permalink
feat:kakarot connectors (#519)
Browse files Browse the repository at this point in the history
Initial work on implementing a wrapper around EVM connectors through
Kakarot
  • Loading branch information
fracek authored Oct 29, 2024
2 parents 63b3268 + 1119b9a commit 38176c4
Show file tree
Hide file tree
Showing 23 changed files with 857 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "add chainId hint in connector's `connect`",
"packageName": "@starknet-react/core",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "none",
"comment": "feat: add EVM wallets through kakarot connectors",
"packageName": "@starknet-react/kakarot",
"email": "[email protected]",
"dependentChangeType": "none"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "add kakarotConnectors to starknet-provider",
"packageName": "create-starknet",
"email": "[email protected]",
"dependentChangeType": "patch"
}
5 changes: 4 additions & 1 deletion docs/components/demo/starknetkit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
publicProvider,
useAccount,
useConnect,
useNetwork,
} from "@starknet-react/core";
import { useState } from "react";
import {
Expand Down Expand Up @@ -68,7 +69,7 @@ function StarknetKitInner() {
</Button>
))}

{/*
{/*
<p>Experimental (for internal testing only)</p>
{connectors.map((connector, index) => (
<WalletButton connector={connector} key={connector.id} />
Expand All @@ -87,6 +88,8 @@ function WalletButton({ connector }: { connector: Connector }) {
const [time1, setTime1] = useState<number | undefined>(undefined);
const [time2, setTime2] = useState<number | undefined>(undefined);

const { chain } = useNetwork();

async function connectWallet() {
const _res = await connector.connect();
setRes(JSON.stringify(_res.account));
Expand Down
3 changes: 2 additions & 1 deletion docs/components/starknet/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
publicProvider,
useInjectedConnectors,
} from "@starknet-react/core";
import { kakarotConnectors } from "@starknet-react/kakarot";

export function StarknetProvider({
defaultChainId,
Expand All @@ -21,7 +22,7 @@ export function StarknetProvider({
const provider = publicProvider();
const { connectors } = useInjectedConnectors({
// Show these connectors if the user has no connector installed.
recommended: [argent(), braavos()],
recommended: [argent(), braavos(), ...kakarotConnectors(provider)],
// Hide recommended connectors if the user has any connector installed.
includeRecommended: "always",
// Randomize the order of the connectors.
Expand Down
1 change: 1 addition & 0 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@radix-ui/react-slot": "^1.1.0",
"@starknet-react/chains": "workspace:*",
"@starknet-react/core": "workspace:*",
"@starknet-react/kakarot": "workspace:*",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"lucide-react": "^0.438.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
"@starknet-react/chains": "workspace:^",
"@tanstack/react-query": "^5.25.0",
"eventemitter3": "^5.0.1",
"viem": "^2.19.1",
"viem": "^2.21.1",
"zod": "^3.22.4"
}
}
6 changes: 5 additions & 1 deletion packages/core/src/connectors/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ export interface ConnectorEvents {
disconnect(): void;
}

export type ConnectArgs = {
chainIdHint?: bigint;
};

export abstract class Connector extends EventEmitter<ConnectorEvents> {
/** Unique connector id. */
abstract get id(): string;
Expand All @@ -45,7 +49,7 @@ export abstract class Connector extends EventEmitter<ConnectorEvents> {
/** Whether connector is already authorized */
abstract ready(): Promise<boolean>;
/** Connect wallet. */
abstract connect(): Promise<ConnectorData>;
abstract connect(args: ConnectArgs): Promise<ConnectorData>;
/** Disconnect wallet. */
abstract disconnect(): Promise<void>;
/** Get current account. */
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/connectors/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { Connector } from "./base";
export { Connector, type ConnectArgs } from "./base";
export { InjectedConnector, type InjectedConnectorOptions } from "./injected";
export {
type UseInjectedConnectorsProps,
Expand Down
11 changes: 8 additions & 3 deletions packages/core/src/connectors/injected.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ import {
ConnectorNotFoundError,
UserRejectedRequestError,
} from "../errors";
import { Connector, type ConnectorData, type ConnectorIcons } from "./base";
import {
type ConnectArgs,
Connector,
type ConnectorData,
type ConnectorIcons,
} from "./base";

/** Injected connector options. */
export interface InjectedConnectorOptions {
Expand Down Expand Up @@ -122,7 +127,7 @@ export class InjectedConnector extends Connector {
return new WalletAccount(provider, this._wallet);
}

async connect(): Promise<ConnectorData> {
async connect(_args: ConnectArgs = {}): Promise<ConnectorData> {
this.ensureWallet();

if (!this._wallet) {
Expand Down Expand Up @@ -210,7 +215,7 @@ export class InjectedConnector extends Connector {
}
}

private async onAccountsChanged(accounts?: string[]): Promise<void> {
protected async onAccountsChanged(accounts?: string[]): Promise<void> {
if (!accounts) {
this.emit("disconnect");
} else {
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/context/starknet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,9 @@ function useStarknetManager({
}

try {
const { chainId, account: address } = await connector.connect();
const { chainId, account: address } = await connector.connect({
chainIdHint: defaultChain.id,
});
const account = await connector.account(state.currentProvider);

if (address !== state.currentAccount?.address) {
Expand Down Expand Up @@ -233,6 +235,7 @@ function useStarknetManager({
autoConnect,
state.currentAccount,
state.currentProvider,
defaultChain.id,
handleConnectorChange,
updateChainAndProvider,
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ import {
useInjectedConnectors,
voyager,
} from "@starknet-react/core";
import { kakarotConnectors } from "@starknet-react/kakarot";

export function StarknetProvider({ children }: { children: ReactNode }) {
const { connectors } = useInjectedConnectors({
// Show these connectors if the user has no connector installed.
recommended: [argent(), braavos()],
recommended: [argent(), braavos(), ...kakarotConnectors()],
// Hide recommended connectors if the user has any connector installed.
includeRecommended: "onlyIfNoConnectors",
// Randomize the order of the connectors.
Expand Down
1 change: 1 addition & 0 deletions packages/kakarot/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# @starknet-react/kakarot
19 changes: 19 additions & 0 deletions packages/kakarot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# `@starknet-react/kakarot`

Support of EVM wallets through Kakarot.

## Installation

```
npm install @starknet-react/kakarot
# or
yarn add @starknet-react/kakarot
```

## Usage

Simply import the kakarotConnectors.

```ts
import { kakarotConnectors } from "@starknet-react/kakarot"
```
44 changes: 44 additions & 0 deletions packages/kakarot/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"name": "@starknet-react/kakarot",
"version": "3.0.2",
"license": "MIT",
"repository": "apibara/starknet-react",
"homepage": "https://www.starknet-react.com/",
"keywords": ["starknet", "ethereum", "l2", "kakarot"],
"type": "module",
"main": "./src/index.ts",
"exports": "./src/index.ts",
"publishConfig": {
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"default": "./dist/index.js"
}
},
"files": ["dist", "src", "README.md"]
},
"scripts": {
"build": "tsup",
"clean": "rimraf dist",
"lint": "biome check src",
"lint:fix": "pnpm lint --write",
"format:check": "biome format .",
"format": "biome format . --write"
},
"devDependencies": {
"@starknet-react/typescript-config": "workspace:*",
"rimraf": "^4.1.2",
"tsup": "^8.0.2"
},
"dependencies": {
"@starknet-io/types-js": "^0.7.7",
"@starknet-react/chains": "^3.0.2",
"@starknet-react/core": "workspace:^",
"mipd": "^0.0.7",
"starknet": "^6.11.0",
"viem": "^2.21.1"
}
}
67 changes: 67 additions & 0 deletions packages/kakarot/src/chains.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {
type Chain as StarknetChain,
mainnet,
sepolia,
} from "@starknet-react/chains";
import { type Chain as KakarotChain, defineChain } from "viem";

type ChainConfig = {
kakarotChain: KakarotChain;
starknetChain: StarknetChain;
kakarotDeployment: string;
};

export const CHAIN_CONFIGS: Record<number, ChainConfig> = {
[Number(sepolia.id)]: {
kakarotChain: /*#__PURE__*/ defineChain({
id: 920637907288165,
name: "Kakarot Sepolia",
nativeCurrency: {
name: "Ether",
symbol: "ETH",
decimals: 18,
},
rpcUrls: {
default: {
http: ["https://sepolia-rpc.kakarot.org"],
},
},
blockExplorers: {
default: {
name: "Kakarot Scan",
url: "https://sepolia.kakarotscan.org",
},
},
testnet: true,
}),
starknetChain: sepolia,
kakarotDeployment:
"0x1d2e513630d8120666fc6e7d52ad0c01479fd99c183baac79fff9135f46e359",
},
};

export const kakarotSepolia = CHAIN_CONFIGS[Number(sepolia.id)].kakarotChain;
export const DEFAULT_CHAIN = CHAIN_CONFIGS[Number(sepolia.id)];

export const KAKAROT_DEPLOYMENTS: Record<number, string> = Object.fromEntries(
Object.values(CHAIN_CONFIGS).map((config) => [
Number(config.starknetChain.id),
config.kakarotDeployment,
]),
);

export const getCorrespondingStarknetChain = (
chainId: number,
): StarknetChain | undefined => {
return Object.values(CHAIN_CONFIGS).find(
(config) => config.kakarotChain.id === chainId,
)?.starknetChain;
};

export const getCorrespondingKakarotChain = (
starknetChainId: number,
): KakarotChain | undefined => {
return Object.values(CHAIN_CONFIGS).find(
(config) => Number(config.starknetChain.id) === starknetChainId,
)?.kakarotChain;
};
16 changes: 16 additions & 0 deletions packages/kakarot/src/connectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { ChainProviderFactory } from "@starknet-react/core";
import { createStore } from "mipd";
import type { RpcProvider } from "starknet";
import { KakarotConnector } from "./kakarot";

export function kakarotConnectors(
starknetRpcProvider: ChainProviderFactory<RpcProvider>,
): KakarotConnector[] {
// Set up a MIPD Store, and request Providers.
const store = createStore();

const allProviders = store.getProviders();
return allProviders.map((provider) => {
return new KakarotConnector(provider, starknetRpcProvider);
});
}
2 changes: 2 additions & 0 deletions packages/kakarot/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { kakarotSepolia } from "./chains";
export { kakarotConnectors } from "./connectors";
Loading

0 comments on commit 38176c4

Please sign in to comment.