Skip to content

Commit

Permalink
Add session account + connector (#802)
Browse files Browse the repository at this point in the history
* Add session account + connector

* feat: work on connector

* refactor: try retrive

* refactor: retrieve

* decoe base64 string

* unique session id

* set last used connector to this

* remove session param after retrieve

* session connect fix

* expose username

* set uisername

* fix: connector

* chore: rename

* feat: disconnect

* unified backend

* feat: telegram connector

* telegram custom session impl

* fmt

* Update pnpm lock

* Resolve build issues

* Fix imports

* Improve package exports for better tree shaking

---------

Co-authored-by: Nasr <[email protected]>
  • Loading branch information
tarrencev and Larkooo authored Oct 16, 2024
1 parent b5c5cbc commit 5554745
Show file tree
Hide file tree
Showing 31 changed files with 7,843 additions and 9,223 deletions.
19 changes: 10 additions & 9 deletions docs/docs/controller/examples/react.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
---
* * *

title: React
sidebar_position: 1
---

## sidebar_position: 1

### Installation

Expand All @@ -23,19 +24,19 @@ pnpm add @cartridge/connector @cartridge/controller @starknet-react/core starkne
Import the `CartridgeConnector` and create an instance:

```typescript
import CartridgeConnector from "@cartridge/connector";
import {ControllerConnector} from "@cartridge/connector";

const connector = new CartridgeConnector();
const connector = new ControllerConnector();
```

### Configuring the Connector

You can customize the `CartridgeConnector` by providing configuration options during instantiation. The `CartridgeConnector` accepts an options object that allows you to configure various settings such as policies, RPC URLs, theme, and more.
You can customize the `ControllerConnector` by providing configuration options during instantiation. The `ControllerConnector` accepts an options object that allows you to configure various settings such as policies, RPC URLs, theme, and more.

Here's an example:

```typescript
import CartridgeConnector from "@cartridge/connector";
import {ControllerConnector} from "@cartridge/connector";
import { shortString } from "starknet";

const ETH_TOKEN_ADDRESS =
Expand Down Expand Up @@ -98,15 +99,15 @@ Use the `useConnect`, `useDisconnect`, and `useAccount` hooks to manage wallet c

```typescript
import { useAccount, useConnect, useDisconnect } from "@starknet-react/core";
import CartridgeConnector from "@cartridge/connector";
import ControllerConnector from "@cartridge/connector/controller";
import { useEffect, useState } from "react";
export function ConnectWallet() {
const { connect, connectors } = useConnect();
const { disconnect } = useDisconnect();
const { address } = useAccount();
const connector = connectors[0] as CartridgeConnector;
const connector = connectors[0] as ControllerConnector;
const [username, setUsername] = useState<string>();
useEffect(() => {
Expand Down
2 changes: 1 addition & 1 deletion examples/starknet-react-next/next-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
12 changes: 12 additions & 0 deletions examples/starknet-react-next/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,18 @@ const nextConfig = {
KEYCHAIN_FRAME_URL: process.env.KEYCHAIN_FRAME_URL,
PROFILE_FRAME_URL: process.env.PROFILE_FRAME_URL,
},
webpack: (config, { isServer, dev }) => {
// Use the client static directory in the server bundle and prod mode
// Fixes `Error occurred prerendering page "/"`
config.output.webassemblyModuleFilename =
isServer && !dev
? "../static/wasm/[modulehash].wasm"
: "static/wasm/[modulehash].wasm";

// Since Webpack 5 doesn't enable WebAssembly by default, we should do it manually
config.experiments = { ...config.experiments, asyncWebAssembly: true };
return config;
},
};

module.exports = nextConfig;
2 changes: 0 additions & 2 deletions examples/starknet-react-next/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { InvalidTxn } from "components/InvalidTxn";
import { SignMessage } from "components/SignMessage";
import { DelegateAccount } from "components/DelegateAccount";
import { ColorModeToggle } from "components/ColorModeToggle";
import { RegisterSession } from "components/RegisterSession";
import { Profile } from "components/Profile";
import { Settings } from "components/Settings";
import { FetchControllers } from "components/FetchControllers";
Expand All @@ -27,7 +26,6 @@ export default function Home() {
<DelegateAccount />
<InvalidTxn />
<SignMessage />
<RegisterSession />
<FetchControllers />
</div>
);
Expand Down
27 changes: 17 additions & 10 deletions examples/starknet-react-next/src/components/ConnectWallet.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { useAccount, useConnect, useDisconnect } from "@starknet-react/core";
import CartridgeConnector from "@cartridge/connector";
import ControllerConnector from "@cartridge/connector/controller";
import React, { useEffect, useState } from "react";
import { Button } from "@cartridge/ui-next";

Expand All @@ -10,14 +10,21 @@ export function ConnectWallet() {
const { disconnect } = useDisconnect();
const { address } = useAccount();

const connector = connectors[0] as CartridgeConnector;
const connector = connectors[0] as ControllerConnector;

const [username, setUsername] = useState<string>();
useEffect(() => {
if (!address) return;
connector.username()?.then((n) => setUsername(n));
}, [address, connector]);

const registerSessionUrl =
"http://localhost:3001/session?public_key=0x2cb057c18198ae4555a144bfdace051433b9a545dc88224de58fa04e323f269&redirect_uri=http://localhost:3002&policies=%5B%7B%22target%22:%220x03661Ea5946211b312e8eC71B94550928e8Fd3D3806e43c6d60F41a6c5203645%22,%22method%22:%22attack%22,%22description%22:%22Attack%20the%20beast%22%7D,%7B%22target%22:%220x03661Ea5946211b312e8eC71B94550928e8Fd3D3806e43c6d60F41a6c5203645%22,%22method%22:%22claim%22,%22description%22:%22Claim%20your%20tokens%22%7D%5D&rpc_url=http://localhost:8001/x/starknet/sepolia";

const openRegisterSessionUrl = () => {
window.open(registerSessionUrl, "_blank", "noopener,noreferrer");
};

return (
<div>
{address && (
Expand All @@ -26,14 +33,14 @@ export function ConnectWallet() {
{username && <p>Username: {username}</p>}
</>
)}

<Button
onClick={() => {
address ? disconnect() : connect({ connector });
}}
>
{address ? "Disconnect" : "Connect"}
</Button>
{address ? (
<Button onClick={() => disconnect()}>Disconnect</Button>
) : (
<>
<Button onClick={() => connect({ connector })}>Connect</Button>
<Button onClick={openRegisterSessionUrl}>Register Session</Button>
</>
)}
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { useAccount } from "@starknet-react/core";
import { useCallback, useEffect, useState } from "react";
import { constants } from "starknet";
import CartridgeConnector from "@cartridge/connector";
import ControllerConnector from "@cartridge/connector/controller";
import { Button, Input } from "@cartridge/ui-next";

export const DelegateAccount = () => {
Expand All @@ -17,22 +17,22 @@ export const DelegateAccount = () => {
const [delegateAddressInput, setDelegateAddressInput] = useState("");
const [isDelegateSupported, setIsDelegateSupported] = useState(false);

const cartridgeConnector = connector as unknown as CartridgeConnector;
const controller = connector as unknown as ControllerConnector;

const load = useCallback(async () => {
if (!account) {
return;
}

try {
const delegate = await cartridgeConnector.delegateAccount();
const delegate = await controller.delegateAccount();
setDelegateAddress(delegate?.toString() || "");
setIsDelegateSupported(true);
} catch (e: any) {
console.log(e);
// controller doesnt support delegateAccount, ignore
}
}, [account, cartridgeConnector]);
}, [account, controller]);

const execute = useCallback(async () => {
if (!account) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

import { useAccount } from "@starknet-react/core";
import { useCallback, useState } from "react";
import CartridgeConnector from "@cartridge/connector";
import ControllerConnector from "@cartridge/connector/controller";
import { ControllerAccounts } from "@cartridge/controller";
import { Button } from "@cartridge/ui-next";

export function FetchControllers() {
const { address, connector } = useAccount();
const [error, setError] = useState<Error>();
const [controllers, setControllers] = useState<ControllerAccounts>();
const cartridgeConnector = connector as never as CartridgeConnector;
const cartridgeConnector = connector as never as ControllerConnector;

const onFetch = useCallback(async () => {
if (!address) {
Expand Down
2 changes: 1 addition & 1 deletion examples/starknet-react-next/src/components/Profile.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { useAccount } from "@starknet-react/core";
import ControllerConnector from "@cartridge/connector";
import ControllerConnector from "@cartridge/connector/controller";
import { Button } from "@cartridge/ui-next";

export function Profile() {
Expand Down
4 changes: 2 additions & 2 deletions examples/starknet-react-next/src/components/Settings.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
"use client";

import { useAccount, useDisconnect } from "@starknet-react/core";
import CartridgeConnector from "@cartridge/connector";
import ControllerConnector from "@cartridge/connector/controller";
import { Button } from "@cartridge/ui-next";

export function Settings() {
const { account, connector } = useAccount();
const { disconnect } = useDisconnect();
const cartridgeConnector = connector as unknown as CartridgeConnector;
const cartridgeConnector = connector as unknown as ControllerConnector;

const onOpenSettings = async () => {
if (!account) return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { Chain, mainnet, sepolia } from "@starknet-react/chains";
import { StarknetConfig, starkscan } from "@starknet-react/core";
import { PropsWithChildren } from "react";
import ControllerConnector from "@cartridge/connector";
import ControllerConnector from "@cartridge/connector/controller";
import { RpcProvider, shortString } from "starknet";

export function StarknetProvider({ children }: PropsWithChildren) {
Expand Down
4 changes: 3 additions & 1 deletion examples/svelte/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@
"svelte-check": "^4.0.0",
"typescript": "^5.0.0",
"typescript-eslint": "^8.0.0",
"vite": "^5.0.3"
"vite": "^5.0.3",
"vite-plugin-top-level-await": "^1.4.4",
"vite-plugin-wasm": "^3.3.0"
},
"type": "module"
}
4 changes: 3 additions & 1 deletion examples/svelte/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
import wasm from 'vite-plugin-wasm';
import topLevelAwait from 'vite-plugin-top-level-await';

export default defineConfig({
plugins: [sveltekit()]
plugins: [wasm(), topLevelAwait(), sveltekit()]
});
20 changes: 20 additions & 0 deletions packages/account-wasm/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,26 @@ impl CartridgeSessionAccount {
Ok(Felts(res.into_iter().map(JsFelt).collect()))
}

pub async fn sign_transaction(&self, calls: Vec<JsCall>, max_fee: JsFelt) -> Result<Felts> {
let calls = calls
.into_iter()
.map(TryInto::try_into)
.collect::<std::result::Result<Vec<_>, _>>()?;

let nonce = self.0.get_nonce().await?;
let tx_hash = self
.0
.execute_v1(calls.clone())
.nonce(nonce)
.max_fee(max_fee.0)
.prepared()?
.transaction_hash(false);

let signature = self.0.sign_hash_and_calls(tx_hash, &calls).await?;

Ok(Felts(signature.into_iter().map(JsFelt).collect()))
}

pub async fn execute(&self, calls: Vec<JsCall>) -> Result<JsValue> {
let calls = calls
.into_iter()
Expand Down
25 changes: 21 additions & 4 deletions packages/connector/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,39 @@
"module": "dist/index.js",
"types": "dist/index.d.ts",
"type": "module",
"sideEffects": false,
"scripts": {
"build:deps": "tsc",
"prepublish": "pnpm build:deps",
"format": "prettier --write \"src/**/*.ts\"",
"format:check": "prettier --check \"src/**/*.ts\""
},
"files": [
"dist"
],
"exports": {
"./controller": "./dist/controller.js",
"./session": "./dist/session.js",
"./telegram": "./dist/telegram.js"
},
"typesVersions": {
"*": {
"controller": [
"./dist/controller.d.ts"
],
"session": [
"./dist/session.d.ts"
],
"telegram": [
"./dist/telegram.d.ts"
]
}
},
"dependencies": {
"@cartridge/controller": "workspace:^",
"@starknet-react/core": "^2.1.5",
"@telegram-apps/sdk": "^2.4.0",
"starknet": "^6.11.0"
},
"devDependencies": {
"@cartridge/tsconfig": "workspace:^",
"typescript": "^5.4.5"
}
}
}
1 change: 1 addition & 0 deletions packages/connector/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const KEYCHAIN_URL = "https://x.cartridge.gg";
Loading

0 comments on commit 5554745

Please sign in to comment.