Skip to content

Commit 6d42e65

Browse files
🔄 synced file(s) with circlefin/modularwallets-web-sdk-internal (#6)
synced local file(s) with [circlefin/modularwallets-web-sdk-internal](https://github.com/circlefin/modularwallets-web-sdk-internal). <details> <summary>Changed files</summary> <ul> <li>synced local directory <code>examples/</code> with remote directory <code>examples/</code></li><li>synced local directory <code>packages/</code> with remote directory <code>packages/</code></li><li>synced local <code>yarn.lock</code> with remote <code>yarn.lock</code></li> </ul> </details> --- This PR was created automatically by the [repo-file-sync-action](https://github.com/BetaHuhn/repo-file-sync-action) workflow run [#13315206750](https://github.com/circlefin/modularwallets-web-sdk-internal/actions/runs/13315206750)
1 parent 1e6a689 commit 6d42e65

File tree

34 files changed

+1691
-632
lines changed

34 files changed

+1691
-632
lines changed

‎examples/circle-smart-account/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@
1010
"react": "^18.2.0",
1111
"react-dom": "^18.2.0",
1212
"viem": "^2.21.27",
13-
"@circle-fin/modular-wallets-core": "^1.0.0"
13+
"@circle-fin/modular-wallets-core": "^1.x.x"
1414
},
1515
"devDependencies": {
1616
"@types/react": "^18.0.27",
1717
"@types/react-dom": "^18.0.10",
1818
"@vitejs/plugin-react": "^4.3.2",
1919
"typescript": "^5.0.3",
20-
"vite": "^5.4.8"
20+
"vite": "^5.4.14"
2121
}
2222
}

‎examples/eip-1193/.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
VITE_CLIENT_KEY=
2+
VITE_CLIENT_URL=
3+
VITE_PUBLIC_RPC_URL=

‎examples/eip-1193/README.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# EIP-1193 Example
2+
3+
This example Vite application demonstrates how to register and log in to a Circle Smart Account using passkeys. It also showcases how to perform RPC actions, including retrieving an account address, signing personal messages, signing typed data, and sending transactions using the EIP-1193 provider.
4+
5+
## Run the example app
6+
7+
Please follow the instructions to run the example app on your local machine.
8+
9+
### Environment Variables
10+
11+
Before you start to run the app, you need to make sure that all environment variables are configured correctly.
12+
13+
Make a copy of `.env.example` and rename it to `.env`.
14+
15+
Under `.env`, make sure the following environment variables are configured properly:
16+
17+
- `VITE_CLIENT_KEY`: Paste your Client Key here. You can create one in [Circle Developer Console](https://console.circle.com/wallets/modular/configurator).
18+
- `VITE_CLIENT_URL`: Paste the Client URL here. You can copy it from [Circle Developer Console](https://console.circle.com/wallets/modular/configurator).
19+
- `VITE_PUBLIC_RPC_URL`: Paste your public RPC endpoint URL here. You can create one in [Infura](https://infura.io/).
20+
21+
Once you have these environment variables setup, you can now follow the steps below to run the app locally.
22+
23+
### Install dependencies
24+
25+
You first need to make sure you have followed the [README](https://github.com/circlefin/w3s-web-core-sdk/blob/master/README.md) under project root and have installed all dependencies under root folder:
26+
27+
```bash
28+
$ yarn install
29+
```
30+
31+
Now you need to go to this example folder:
32+
33+
```bash
34+
$ cd examples/eip-1193
35+
```
36+
37+
Once you are under the example folder, install all dependencies for the app:
38+
39+
```bash
40+
$ yarn install
41+
```
42+
43+
### Run the app
44+
45+
To run the app locally:
46+
47+
```bash
48+
$ yarn dev
49+
```
50+
51+
Now you should be able to see your app up and running in your browser at: `http://localhost:5173/`.
52+
53+
### Important Notes
54+
55+
- __Do Not Import from `src` or `dist` Directories Directly:__
56+
57+
Always import the Core SDK using the package name:
58+
59+
```ts
60+
import { yourFunction } from 'w3s-web-core-sdk'
61+
```
62+
63+
- __Watching Changes from the Core SDK Package__
64+
65+
If you are developing new SDK features, run `yarn dev` from the [core SDK package directory](../../packages/w3s-web-core-sdk) to build your changes in real time.
66+
67+
- __Ensure Build-Time Constants Are Replaced:__
68+
69+
Variables like `SDK_VERSION` should be replaced during the build process. If you encounter issues, make sure you're using the compiled code from the dist directory.

‎examples/eip-1193/index.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
</head>
7+
<body>
8+
<h1>EIP-1193 example</h1>
9+
<div id="root"></div>
10+
<script type="module" src="/index.tsx"></script>
11+
</body>
12+
</html>

‎examples/eip-1193/index.tsx

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
import * as React from "react"
2+
import * as ReactDOM from "react-dom/client"
3+
import { polygonAmoy } from "viem/chains"
4+
import Web3 from "web3"
5+
6+
import { type Hex, createClient, createPublicClient, http } from "viem"
7+
import {
8+
type P256Credential,
9+
type SmartAccount,
10+
WebAuthnAccount,
11+
createBundlerClient,
12+
toWebAuthnAccount,
13+
} from "viem/account-abstraction"
14+
import {
15+
EIP1193Provider,
16+
WebAuthnMode,
17+
toCircleSmartAccount,
18+
toModularTransport,
19+
toPasskeyTransport,
20+
toWebAuthnCredential,
21+
} from "@circle-fin/modular-wallets-core"
22+
23+
const clientKey = import.meta.env.VITE_CLIENT_KEY as string
24+
const clientUrl = import.meta.env.VITE_CLIENT_URL as string
25+
const publicRpcUrl = import.meta.env.VITE_PUBLIC_RPC_URL as string
26+
27+
// Create Circle transports
28+
const passkeyTransport = toPasskeyTransport(clientUrl, clientKey)
29+
const modularTransport = toModularTransport(`${clientUrl}/polygonAmoy`, clientKey)
30+
31+
// Create a public client
32+
const client = createClient({
33+
chain: polygonAmoy,
34+
transport: modularTransport,
35+
})
36+
37+
function Example() {
38+
const [account, setAccount] = React.useState<SmartAccount>()
39+
const [credential, setCredential] = React.useState<P256Credential>(() =>
40+
JSON.parse(localStorage.getItem("credential") || "null")
41+
)
42+
43+
const [web3, setWeb3] = React.useState<Web3>()
44+
const [hash, setHash] = React.useState<Hex>()
45+
const [address, setAddress] = React.useState<string>()
46+
const [personalSignature, setPersonalSignature] = React.useState<string>()
47+
const [typedDataSignature, setTypedDataSignature] = React.useState<string>()
48+
49+
React.useEffect(() => {
50+
if (!credential) return
51+
52+
init()
53+
54+
async function init() {
55+
// Create a circle smart account
56+
const account = await toCircleSmartAccount({
57+
client,
58+
owner: toWebAuthnAccount({ credential }) as WebAuthnAccount,
59+
})
60+
61+
setAccount(account)
62+
63+
const publicClientInstance = createPublicClient({
64+
chain: polygonAmoy,
65+
transport: http(publicRpcUrl),
66+
})
67+
const bundlerClientInstance = createBundlerClient({
68+
account,
69+
chain: polygonAmoy,
70+
transport: modularTransport,
71+
})
72+
73+
const provider = new EIP1193Provider(bundlerClientInstance, publicClientInstance)
74+
setWeb3(new Web3(provider))
75+
}
76+
}, [credential])
77+
78+
const register = async () => {
79+
const username = (document.getElementById("username") as HTMLInputElement).value
80+
const credential = await toWebAuthnCredential({
81+
transport: passkeyTransport,
82+
mode: WebAuthnMode.Register,
83+
username,
84+
})
85+
localStorage.setItem("credential", JSON.stringify(credential))
86+
setCredential(credential)
87+
}
88+
89+
const login = async () => {
90+
const credential = await toWebAuthnCredential({
91+
transport: passkeyTransport,
92+
mode: WebAuthnMode.Login,
93+
})
94+
localStorage.setItem("credential", JSON.stringify(credential))
95+
setCredential(credential)
96+
}
97+
98+
const getProviderAddress = async () => {
99+
if (!web3) return
100+
101+
const accounts = await web3.eth.getAccounts()
102+
103+
setAddress(accounts[0])
104+
}
105+
106+
const signPersonalMessage = async () => {
107+
if (!web3) return
108+
109+
const accounts = await web3.eth.getAccounts()
110+
const signature = await web3.eth.personal.sign("Hello World", accounts[0], "passphrase")
111+
112+
setPersonalSignature(signature)
113+
}
114+
115+
const sendTx = async (event: React.FormEvent<HTMLFormElement>) => {
116+
event.preventDefault()
117+
118+
if (!web3) return
119+
120+
const formData = new FormData(event.currentTarget)
121+
const to = formData.get("to") as `0x${string}`
122+
const value = formData.get("value") as string
123+
124+
try {
125+
const suggestedGasPrice = ((await web3.eth.getGasPrice()) * 11n) / 10n // 10% higher than the current gas price to ensure the transaction goes through
126+
127+
// Send tokens to the address that was input
128+
const tx = await web3.eth.sendTransaction({
129+
to,
130+
value: web3.utils.toWei(value, "ether"),
131+
gas: 53638, // Estimated gas limit for a simple transaction
132+
gasPrice: suggestedGasPrice,
133+
})
134+
135+
setHash(tx.transactionHash as Hex)
136+
} catch (err) {
137+
console.log(err)
138+
}
139+
}
140+
141+
const signTypedData = async () => {
142+
if (!web3) return
143+
144+
const accounts = await web3.eth.getAccounts()
145+
const from = accounts[0]
146+
147+
const domain = {
148+
name: "MyDApp",
149+
version: "1.0",
150+
chainId: 80002,
151+
verifyingContract: "0x1111111111111111111111111111111111111111",
152+
}
153+
const message = {
154+
content: "Hello from typed data!",
155+
sender: from,
156+
timestamp: Math.floor(Date.now() / 1000),
157+
}
158+
const dataToSign = {
159+
domain,
160+
message,
161+
primaryType: "Message",
162+
types: {
163+
EIP712Domain: [
164+
{ name: "name", type: "string" },
165+
{ name: "version", type: "string" },
166+
{ name: "chainId", type: "uint256" },
167+
{ name: "verifyingContract", type: "address" },
168+
],
169+
Message: [
170+
{ name: "content", type: "string" },
171+
{ name: "sender", type: "address" },
172+
{ name: "timestamp", type: "uint256" },
173+
],
174+
},
175+
}
176+
177+
const signature = await web3.eth.signTypedData(from, dataToSign)
178+
179+
setTypedDataSignature(signature)
180+
}
181+
182+
if (!credential)
183+
return (
184+
<>
185+
<input id="username" name="username" placeholder="Username" />
186+
<br />
187+
<button onClick={register}>Register</button>
188+
<button onClick={login}>Login</button>
189+
</>
190+
)
191+
if (!account) return <p>Loading...</p>
192+
193+
return (
194+
<>
195+
<h2>Account</h2>
196+
<p>Address: {account?.address}</p>
197+
198+
<h2>Send Transaction</h2>
199+
<form onSubmit={sendTx}>
200+
<input name="to" placeholder="Address" />
201+
<input name="value" placeholder="Amount (ETH)" />
202+
<button type="submit">Send</button>
203+
</form>
204+
<button onClick={getProviderAddress}>Get address</button>
205+
<button onClick={signPersonalMessage}>SignPersonalMessage</button>
206+
<button onClick={signTypedData}>SignTypedData</button>
207+
{address && <p>Address: {address}</p>}
208+
{personalSignature && (
209+
<p
210+
style={{
211+
width: "100%",
212+
wordWrap: "break-word",
213+
}}
214+
>
215+
Personal Signature: {personalSignature}
216+
</p>
217+
)}
218+
{typedDataSignature && (
219+
<p
220+
style={{
221+
width: "100%",
222+
wordWrap: "break-word",
223+
}}
224+
>
225+
Typed Data Signature: {typedDataSignature}
226+
</p>
227+
)}
228+
{hash && <p>Transaction Hash: {hash}</p>}
229+
</>
230+
)
231+
}
232+
233+
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(<Example />)

‎examples/eip-1193/package.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "eip-1193-web3js",
3+
"version": "0.0.0",
4+
"private": true,
5+
"type": "module",
6+
"scripts": {
7+
"dev": "vite"
8+
},
9+
"dependencies": {
10+
"react": "^18.2.0",
11+
"react-dom": "^18.2.0",
12+
"viem": "^2.21.27",
13+
"@circle-fin/modular-wallets-core": "^1.x.x",
14+
"web3": "^4.16.0"
15+
},
16+
"devDependencies": {
17+
"@types/react": "^18.0.27",
18+
"@types/react-dom": "^18.0.10",
19+
"@vitejs/plugin-react": "^4.3.2",
20+
"typescript": "^5.0.3",
21+
"vite": "^5.4.14"
22+
}
23+
}

‎examples/eip-1193/tsconfig.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ESNext",
4+
"useDefineForClassFields": true,
5+
"lib": ["DOM", "DOM.Iterable", "ESNext"],
6+
"allowJs": false,
7+
"skipLibCheck": true,
8+
"esModuleInterop": false,
9+
"allowSyntheticDefaultImports": true,
10+
"strict": true,
11+
"forceConsistentCasingInFileNames": true,
12+
"module": "ESNext",
13+
"moduleResolution": "Node",
14+
"resolveJsonModule": true,
15+
"isolatedModules": true,
16+
"noEmit": true,
17+
"jsx": "react-jsx",
18+
},
19+
"include": ["src"],
20+
"references": [{ "path": "./tsconfig.node.json" }]
21+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"compilerOptions": {
3+
"composite": true,
4+
"module": "ESNext",
5+
"moduleResolution": "Node",
6+
"allowSyntheticDefaultImports": true
7+
},
8+
"include": ["vite.config.ts"]
9+
}

0 commit comments

Comments
 (0)