diff --git a/examples/next/src/app/page.tsx b/examples/next/src/app/page.tsx
index 46f4984ec..adb934ec9 100644
--- a/examples/next/src/app/page.tsx
+++ b/examples/next/src/app/page.tsx
@@ -1,13 +1,12 @@
import { Transfer } from "components/Transfer";
import { ManualTransferEth } from "components/ManualTransferEth";
-import { ConnectWallet } from "components/ConnectWallet";
import { InvalidTxn } from "components/InvalidTxn";
import { SignMessage } from "components/SignMessage";
import { DelegateAccount } from "components/DelegateAccount";
import { ColorModeToggle } from "components/ColorModeToggle";
import { Profile } from "components/Profile";
-import { Settings } from "components/Settings";
import { LookupControllers } from "components/LookupControllers";
+import Header from "components/Header";
export default function Home() {
return (
@@ -18,8 +17,7 @@ export default function Home() {
-
-
+
diff --git a/examples/next/src/components/Header.tsx b/examples/next/src/components/Header.tsx
new file mode 100644
index 000000000..76cf50999
--- /dev/null
+++ b/examples/next/src/components/Header.tsx
@@ -0,0 +1,135 @@
+"use client";
+
+import { Button } from "@cartridge/ui-next";
+import ControllerConnector from "@cartridge/connector/controller";
+import { useAccount, useConnect, useDisconnect } from "@starknet-react/core";
+import { useState } from "react";
+
+const Header = ({
+ showBack,
+ lockChain,
+}: {
+ showBack?: boolean;
+ lockChain?: boolean;
+}) => {
+ const { connect, connectors } = useConnect();
+ const { disconnect } = useDisconnect();
+ const { address, connector } = useAccount();
+ const controllerConnector = connector as never as ControllerConnector;
+ const chains = [
+ { name: "Mainnet", id: "mainnet" },
+ { name: "Sepolia (Testnet)", id: "sepolia" },
+ { name: "Slot (L3)", id: "slot" },
+ ];
+
+ const chainName = "chain name";
+ const [networkOpen, setNetworkOpen] = useState(false);
+ const [profileOpen, setProfileOpen] = useState(false);
+
+ // const chainName = useMemo(() => {
+ // return chains.find((c) => c.id === getCurrentChain())?.name;
+ // }, [chains, getCurrentChain]);
+
+ return (
+
+ {showBack && (
+
+ )}
+
+
+
+ {networkOpen && (
+
+ {chains.map((c) => (
+
+ ))}
+
+ )}
+
+ {address ? (
+
+
+ {profileOpen && (
+
+
+
+
+
+ )}
+
+ ) : (
+
+ )}
+
+ );
+};
+
+export default Header;
diff --git a/examples/next/src/components/providers/StarknetProvider.tsx b/examples/next/src/components/providers/StarknetProvider.tsx
index edf0cb66e..3501d1a06 100644
--- a/examples/next/src/components/providers/StarknetProvider.tsx
+++ b/examples/next/src/components/providers/StarknetProvider.tsx
@@ -108,7 +108,7 @@ export function StarknetProvider({ children }: PropsWithChildren) {
const controller = new ControllerConnector({
policies,
- rpc,
+ rpcUrl: rpc,
url:
process.env.NEXT_PUBLIC_KEYCHAIN_DEPLOYMENT_URL ??
process.env.NEXT_PUBLIC_KEYCHAIN_FRAME_URL,
diff --git a/packages/controller/src/account.ts b/packages/controller/src/account.ts
index 13499d8e4..52e5b0a0b 100644
--- a/packages/controller/src/account.ts
+++ b/packages/controller/src/account.ts
@@ -32,7 +32,7 @@ class ControllerAccount extends WalletAccount {
options: KeychainOptions,
modal: Modal,
) {
- super({ nodeUrl: provider.rpc.toString() }, provider);
+ super(provider, provider);
this.address = address;
this.keychain = keychain;
diff --git a/packages/controller/src/controller.ts b/packages/controller/src/controller.ts
index 847259602..066c53369 100644
--- a/packages/controller/src/controller.ts
+++ b/packages/controller/src/controller.ts
@@ -17,6 +17,7 @@ import {
import BaseProvider from "./provider";
import { WalletAccount } from "starknet";
import { Policy } from "@cartridge/presets";
+import { AddStarknetChainParameters } from "@starknet-io/types-js";
export default class ControllerProvider extends BaseProvider {
private keychain?: AsyncMethodReturns;
@@ -25,9 +26,9 @@ export default class ControllerProvider extends BaseProvider {
private iframes: IFrames;
constructor(options: ControllerOptions) {
- const { rpc } = options;
- super({ rpc });
+ super(options);
+ this.rpcUrl = options.rpcUrl;
this.iframes = {
keychain: new KeychainIFrame({
...options,
@@ -54,9 +55,7 @@ export default class ControllerProvider extends BaseProvider {
return;
}
- const response = (await this.keychain.probe(
- this.rpc.toString(),
- )) as ProbeReply;
+ const response = (await this.keychain.probe(this.rpcUrl)) as ProbeReply;
this.account = new ControllerAccount(
this,
@@ -83,7 +82,7 @@ export default class ControllerProvider extends BaseProvider {
openPurchaseCredits: () => this.openPurchaseCredits.bind(this),
openExecute: () => this.openExecute.bind(this),
},
- rpcUrl: this.rpc.toString(),
+ rpcUrl: this.rpcUrl,
username,
version: this.version,
});
@@ -114,7 +113,7 @@ export default class ControllerProvider extends BaseProvider {
try {
let response = await this.keychain.connect(
this.options.policies || [],
- this.rpc.toString(),
+ this.rpcUrl,
);
if (response.code !== ResponseCodes.SUCCESS) {
throw new Error(response.message);
@@ -137,6 +136,14 @@ export default class ControllerProvider extends BaseProvider {
}
}
+ switchStarknetChain(_chainId: string): Promise {
+ return Promise.resolve(true);
+ }
+
+ addStarknetChain(_chain: AddStarknetChainParameters): Promise {
+ return Promise.resolve(true);
+ }
+
async disconnect() {
if (!this.keychain) {
console.error(new NotReadyToConnect().message);
diff --git a/packages/controller/src/provider.ts b/packages/controller/src/provider.ts
index ab65cb2c6..bb8b93df1 100644
--- a/packages/controller/src/provider.ts
+++ b/packages/controller/src/provider.ts
@@ -1,10 +1,12 @@
-import { WalletAccount } from "starknet";
+import { Provider, WalletAccount } from "starknet";
import {
AddInvokeTransactionParameters,
+ AddStarknetChainParameters,
Errors,
Permission,
RequestFn,
StarknetWindowObject,
+ SwitchStarknetChainParameters,
TypedData,
WalletEventHandlers,
WalletEventListener,
@@ -14,20 +16,22 @@ import {
import { icon } from "./icon";
import { ProviderOptions } from "./types";
-export default abstract class BaseProvider implements StarknetWindowObject {
+export default abstract class BaseProvider
+ extends Provider
+ implements StarknetWindowObject
+{
public id = "controller";
public name = "Controller";
public version = "0.4.0";
public icon = icon;
- public rpc: URL;
public account?: WalletAccount;
public subscriptions: WalletEvents[] = [];
+ protected rpcUrl: string;
constructor(options: ProviderOptions) {
- const { rpc } = options;
-
- this.rpc = new URL(rpc);
+ super({ nodeUrl: options.rpcUrl });
+ this.rpcUrl = options.rpcUrl;
}
request: RequestFn = async (call) => {
@@ -65,19 +69,15 @@ export default abstract class BaseProvider implements StarknetWindowObject {
data: "wallet_watchAsset not implemented",
} as Errors.UNEXPECTED_ERROR;
- case "wallet_addStarknetChain":
- throw {
- code: 63,
- message: "An unexpected error occurred",
- data: "wallet_addStarknetChain not implemented",
- } as Errors.UNEXPECTED_ERROR;
+ case "wallet_addStarknetChain": {
+ let params = call.params as AddStarknetChainParameters;
+ return this.addStarknetChain(params);
+ }
- case "wallet_switchStarknetChain":
- throw {
- code: 63,
- message: "An unexpected error occurred",
- data: "wallet_switchStarknetChain not implemented",
- } as Errors.UNEXPECTED_ERROR;
+ case "wallet_switchStarknetChain": {
+ let params = call.params as SwitchStarknetChainParameters;
+ return this.switchStarknetChain(params.chainId);
+ }
case "wallet_requestChainId":
if (!this.account) {
@@ -174,4 +174,8 @@ export default abstract class BaseProvider implements StarknetWindowObject {
abstract probe(): Promise;
abstract connect(): Promise;
+ abstract switchStarknetChain(chainId: string): Promise;
+ abstract addStarknetChain(
+ chain: AddStarknetChainParameters,
+ ): Promise;
}
diff --git a/packages/controller/src/session/provider.ts b/packages/controller/src/session/provider.ts
index 5611227ba..3d07bf7d3 100644
--- a/packages/controller/src/session/provider.ts
+++ b/packages/controller/src/session/provider.ts
@@ -5,6 +5,7 @@ import { KEYCHAIN_URL } from "../constants";
import BaseProvider from "../provider";
import { toWasmPolicies } from "../utils";
import { SessionPolicies } from "@cartridge/presets";
+import { AddStarknetChainParameters } from "@starknet-io/types-js";
interface SessionRegistration {
username: string;
@@ -32,7 +33,7 @@ export default class SessionProvider extends BaseProvider {
protected _policies: SessionPolicies;
constructor({ rpc, chainId, policies, redirectUrl }: SessionOptions) {
- super({ rpc });
+ super({ rpcUrl: rpc });
this._chainId = chainId;
this._redirectUrl = redirectUrl;
@@ -76,7 +77,7 @@ export default class SessionProvider extends BaseProvider {
this._redirectUrl
}&redirect_query_name=startapp&policies=${JSON.stringify(
this._policies,
- )}&rpc_url=${this.rpc}`;
+ )}&rpc_url=${this.rpcUrl}`;
localStorage.setItem("lastUsedConnector", this.id);
window.open(url, "_blank");
@@ -84,6 +85,14 @@ export default class SessionProvider extends BaseProvider {
return;
}
+ switchStarknetChain(_chainId: string): Promise {
+ return Promise.resolve(true);
+ }
+
+ addStarknetChain(_chain: AddStarknetChainParameters): Promise {
+ return Promise.resolve(true);
+ }
+
disconnect(): Promise {
localStorage.removeItem("sessionSigner");
localStorage.removeItem("session");
@@ -127,7 +136,7 @@ export default class SessionProvider extends BaseProvider {
this._username = sessionRegistration.username;
this.account = new SessionAccount(this, {
- rpcUrl: this.rpc.toString(),
+ rpcUrl: this.rpcUrl.toString(),
privateKey: signer.privKey,
address: sessionRegistration.address,
ownerGuid: sessionRegistration.ownerGuid,
diff --git a/packages/controller/src/telegram/provider.ts b/packages/controller/src/telegram/provider.ts
index 83cc3cbc2..4de75a364 100644
--- a/packages/controller/src/telegram/provider.ts
+++ b/packages/controller/src/telegram/provider.ts
@@ -11,6 +11,7 @@ import SessionAccount from "../session/account";
import BaseProvider from "../provider";
import { toWasmPolicies } from "../utils";
import { SessionPolicies } from "@cartridge/presets";
+import { AddStarknetChainParameters } from "@starknet-io/types-js";
interface SessionRegistration {
username: string;
@@ -38,7 +39,7 @@ export default class TelegramProvider extends BaseProvider {
tmaUrl: string;
}) {
super({
- rpc,
+ rpcUrl: rpc,
});
this._tmaUrl = tmaUrl;
@@ -77,7 +78,7 @@ export default class TelegramProvider extends BaseProvider {
this._tmaUrl
}&redirect_query_name=startapp&policies=${JSON.stringify(
this._policies,
- )}&rpc_url=${this.rpc}`;
+ )}&rpc_url=${this.rpcUrl}`;
localStorage.setItem("lastUsedConnector", this.id);
openLink(url);
@@ -86,6 +87,14 @@ export default class TelegramProvider extends BaseProvider {
return;
}
+ switchStarknetChain(_chainId: string): Promise {
+ return Promise.resolve(true);
+ }
+
+ addStarknetChain(_chain: AddStarknetChainParameters): Promise {
+ return Promise.resolve(true);
+ }
+
disconnect(): Promise {
cloudStorage.deleteItem("sessionSigner");
cloudStorage.deleteItem("session");
@@ -118,7 +127,7 @@ export default class TelegramProvider extends BaseProvider {
this._username = sessionRegistration.username;
this.account = new SessionAccount(this, {
- rpcUrl: this.rpc.toString(),
+ rpcUrl: this.rpcUrl.toString(),
privateKey: signer.privKey,
address: sessionRegistration.address,
ownerGuid: sessionRegistration.ownerGuid,
diff --git a/packages/controller/src/types.ts b/packages/controller/src/types.ts
index 2faf8ac06..f14bcf04d 100644
--- a/packages/controller/src/types.ts
+++ b/packages/controller/src/types.ts
@@ -17,6 +17,7 @@ import {
Policy,
SessionPolicies,
} from "@cartridge/presets";
+import { RequestFn } from "@starknet-io/types-js";
export type Session = {
chainId: constants.StarknetChainId;
@@ -133,7 +134,10 @@ export interface Keychain {
fetchControllers(contractAddresses: string[]): Promise;
openPurchaseCredits(): void;
openExecute(calls: Call[]): Promise;
+
+ request: RequestFn;
}
+
export interface Profile {
navigate(path: string): void;
}
@@ -162,8 +166,7 @@ export type IFrameOptions = {
};
export type ProviderOptions = {
- /** The URL of the RPC */
- rpc: string;
+ rpcUrl: string;
};
export type KeychainOptions = IFrameOptions & {