@cartridge/controller 0.5.9 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/.turbo/turbo-build$colon$deps.log +44 -82
  2. package/.turbo/turbo-build.log +47 -85
  3. package/dist/__tests__/parseChainId.test.js.map +1 -1
  4. package/dist/account.js +4 -0
  5. package/dist/account.js.map +1 -1
  6. package/dist/controller.js +177 -109
  7. package/dist/controller.js.map +1 -1
  8. package/dist/iframe/base.js +4 -0
  9. package/dist/iframe/base.js.map +1 -1
  10. package/dist/iframe/index.js +4 -0
  11. package/dist/iframe/index.js.map +1 -1
  12. package/dist/iframe/keychain.js +4 -0
  13. package/dist/iframe/keychain.js.map +1 -1
  14. package/dist/iframe/profile.js +4 -0
  15. package/dist/iframe/profile.js.map +1 -1
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.js +181 -111
  18. package/dist/index.js.map +1 -1
  19. package/dist/mutex.d.ts +14 -0
  20. package/dist/mutex.js +22 -0
  21. package/dist/mutex.js.map +1 -0
  22. package/dist/policies.d.ts +19 -0
  23. package/dist/policies.js +26 -0
  24. package/dist/policies.js.map +1 -0
  25. package/dist/provider.d.ts +2 -0
  26. package/dist/provider.js +163 -109
  27. package/dist/provider.js.map +1 -1
  28. package/dist/session/account.js +2 -0
  29. package/dist/session/account.js.map +1 -1
  30. package/dist/session/index.d.ts +1 -0
  31. package/dist/session/index.js +286 -121
  32. package/dist/session/index.js.map +1 -1
  33. package/dist/session/provider.d.ts +7 -2
  34. package/dist/session/provider.js +286 -121
  35. package/dist/session/provider.js.map +1 -1
  36. package/dist/telegram/provider.d.ts +2 -1
  37. package/dist/telegram/provider.js +198 -112
  38. package/dist/telegram/provider.js.map +1 -1
  39. package/dist/utils.d.ts +2 -1
  40. package/dist/utils.js +4 -2
  41. package/dist/utils.js.map +1 -1
  42. package/package.json +22 -8
  43. package/src/controller.ts +1 -0
  44. package/src/mutex.ts +22 -0
  45. package/src/policies.ts +49 -0
  46. package/src/provider.ts +33 -2
  47. package/src/session/account.ts +1 -0
  48. package/src/session/provider.ts +139 -10
  49. package/src/telegram/provider.ts +3 -2
  50. package/src/utils.ts +4 -1
  51. package/tsconfig.json +1 -2
@@ -6,6 +6,7 @@ import BaseProvider from "../provider";
6
6
  import { toWasmPolicies } from "../utils";
7
7
  import { SessionPolicies } from "@cartridge/presets";
8
8
  import { AddStarknetChainParameters } from "@starknet-io/types-js";
9
+ import { ParsedSessionPolicies } from "../policies";
9
10
 
10
11
  interface SessionRegistration {
11
12
  username: string;
@@ -20,6 +21,7 @@ export type SessionOptions = {
20
21
  chainId: string;
21
22
  policies: SessionPolicies;
22
23
  redirectUrl: string;
24
+ keychainUrl?: string;
23
25
  };
24
26
 
25
27
  export default class SessionProvider extends BaseProvider {
@@ -30,39 +32,110 @@ export default class SessionProvider extends BaseProvider {
30
32
  protected _rpcUrl: string;
31
33
  protected _username?: string;
32
34
  protected _redirectUrl: string;
33
- protected _policies: SessionPolicies;
35
+ protected _policies: ParsedSessionPolicies;
36
+ protected _keychainUrl: string;
34
37
 
35
- constructor({ rpc, chainId, policies, redirectUrl }: SessionOptions) {
38
+ constructor({
39
+ rpc,
40
+ chainId,
41
+ policies,
42
+ redirectUrl,
43
+ keychainUrl,
44
+ }: SessionOptions) {
36
45
  super();
37
46
 
47
+ this._policies = {
48
+ verified: false,
49
+ contracts: policies.contracts
50
+ ? Object.fromEntries(
51
+ Object.entries(policies.contracts).map(([address, contract]) => [
52
+ address,
53
+ {
54
+ ...contract,
55
+ methods: contract.methods.map((method) => ({
56
+ ...method,
57
+ authorized: true,
58
+ })),
59
+ },
60
+ ]),
61
+ )
62
+ : undefined,
63
+ messages: policies.messages?.map((message) => ({
64
+ ...message,
65
+ authorized: true,
66
+ })),
67
+ };
68
+
38
69
  this._rpcUrl = rpc;
39
70
  this._chainId = chainId;
40
71
  this._redirectUrl = redirectUrl;
41
- this._policies = policies;
72
+ this._keychainUrl = keychainUrl || KEYCHAIN_URL;
42
73
 
43
74
  if (typeof window !== "undefined") {
44
75
  (window as any).starknet_controller_session = this;
45
76
  }
46
77
  }
47
78
 
79
+ private validatePoliciesSubset(
80
+ newPolicies: ParsedSessionPolicies,
81
+ existingPolicies: ParsedSessionPolicies,
82
+ ): boolean {
83
+ if (newPolicies.contracts) {
84
+ if (!existingPolicies.contracts) return false;
85
+
86
+ for (const [address, contract] of Object.entries(newPolicies.contracts)) {
87
+ const existingContract = existingPolicies.contracts[address];
88
+ if (!existingContract) return false;
89
+
90
+ for (const method of contract.methods) {
91
+ const existingMethod = existingContract.methods.find(
92
+ (m) => m.entrypoint === method.entrypoint,
93
+ );
94
+ if (!existingMethod || !existingMethod.authorized) return false;
95
+ }
96
+ }
97
+ }
98
+
99
+ if (newPolicies.messages) {
100
+ if (!existingPolicies.messages) return false;
101
+
102
+ for (const message of newPolicies.messages) {
103
+ const existingMessage = existingPolicies.messages.find(
104
+ (m) =>
105
+ JSON.stringify(m.domain) === JSON.stringify(message.domain) &&
106
+ JSON.stringify(m.types) === JSON.stringify(message.types),
107
+ );
108
+ if (!existingMessage || !existingMessage.authorized) return false;
109
+ }
110
+ }
111
+
112
+ return true;
113
+ }
114
+
48
115
  async username() {
49
116
  await this.tryRetrieveFromQueryOrStorage();
50
117
  return this._username;
51
118
  }
52
119
 
53
120
  async probe(): Promise<WalletAccount | undefined> {
54
- await this.tryRetrieveFromQueryOrStorage();
55
- return;
121
+ if (this.account) {
122
+ return this.account;
123
+ }
124
+
125
+ this.account = await this.tryRetrieveFromQueryOrStorage();
126
+ return this.account;
56
127
  }
57
128
 
58
129
  async connect(): Promise<WalletAccount | undefined> {
59
- await this.tryRetrieveFromQueryOrStorage();
130
+ if (this.account) {
131
+ return this.account;
132
+ }
60
133
 
134
+ this.account = await this.tryRetrieveFromQueryOrStorage();
61
135
  if (this.account) {
62
- return;
136
+ return this.account;
63
137
  }
64
138
 
65
- // Generate a random local key pair
66
139
  const pk = stark.randomAddress();
67
140
  const publicKey = ec.starkCurve.getStarkKey(pk);
68
141
 
@@ -74,7 +147,11 @@ export default class SessionProvider extends BaseProvider {
74
147
  }),
75
148
  );
76
149
 
77
- const url = `${KEYCHAIN_URL}/session?public_key=${publicKey}&redirect_uri=${
150
+ localStorage.setItem("sessionPolicies", JSON.stringify(this._policies));
151
+
152
+ const url = `${
153
+ this._keychainUrl
154
+ }/session?public_key=${publicKey}&redirect_uri=${
78
155
  this._redirectUrl
79
156
  }&redirect_query_name=startapp&policies=${JSON.stringify(
80
157
  this._policies,
@@ -83,7 +160,7 @@ export default class SessionProvider extends BaseProvider {
83
160
  localStorage.setItem("lastUsedConnector", this.id);
84
161
  window.open(url, "_blank");
85
162
 
86
- return;
163
+ return this.account;
87
164
  }
88
165
 
89
166
  switchStarknetChain(_chainId: string): Promise<boolean> {
@@ -97,12 +174,17 @@ export default class SessionProvider extends BaseProvider {
97
174
  disconnect(): Promise<void> {
98
175
  localStorage.removeItem("sessionSigner");
99
176
  localStorage.removeItem("session");
177
+ localStorage.removeItem("sessionPolicies");
100
178
  this.account = undefined;
101
179
  this._username = undefined;
102
180
  return Promise.resolve();
103
181
  }
104
182
 
105
183
  async tryRetrieveFromQueryOrStorage() {
184
+ if (this.account) {
185
+ return this.account;
186
+ }
187
+
106
188
  const signerString = localStorage.getItem("sessionSigner");
107
189
  const signer = signerString ? JSON.parse(signerString) : null;
108
190
  let sessionRegistration: SessionRegistration | null = null;
@@ -135,6 +217,47 @@ export default class SessionProvider extends BaseProvider {
135
217
  return;
136
218
  }
137
219
 
220
+ // Check expiration
221
+ const expirationTime = parseInt(sessionRegistration.expiresAt) * 1000;
222
+ console.log("Session expiration check:", {
223
+ expirationTime,
224
+ currentTime: Date.now(),
225
+ expired: Date.now() >= expirationTime,
226
+ });
227
+ if (Date.now() >= expirationTime) {
228
+ console.log("Session expired, clearing stored session");
229
+ this.clearStoredSession();
230
+ return;
231
+ }
232
+
233
+ // Check stored policies
234
+ const storedPoliciesStr = localStorage.getItem("sessionPolicies");
235
+ console.log("Checking stored policies:", {
236
+ storedPoliciesStr,
237
+ currentPolicies: this._policies,
238
+ });
239
+ if (storedPoliciesStr) {
240
+ const storedPolicies = JSON.parse(
241
+ storedPoliciesStr,
242
+ ) as ParsedSessionPolicies;
243
+
244
+ const isValid = this.validatePoliciesSubset(
245
+ this._policies,
246
+ storedPolicies,
247
+ );
248
+ console.log("Policy validation result:", {
249
+ isValid,
250
+ storedPolicies,
251
+ requestedPolicies: this._policies,
252
+ });
253
+
254
+ if (!isValid) {
255
+ console.log("Policy validation failed, clearing stored session");
256
+ this.clearStoredSession();
257
+ return;
258
+ }
259
+ }
260
+
138
261
  this._username = sessionRegistration.username;
139
262
  this.account = new SessionAccount(this, {
140
263
  rpcUrl: this._rpcUrl,
@@ -148,4 +271,10 @@ export default class SessionProvider extends BaseProvider {
148
271
 
149
272
  return this.account;
150
273
  }
274
+
275
+ private clearStoredSession(): void {
276
+ localStorage.removeItem("sessionSigner");
277
+ localStorage.removeItem("session");
278
+ localStorage.removeItem("sessionPolicies");
279
+ }
151
280
  }
@@ -12,6 +12,7 @@ import BaseProvider from "../provider";
12
12
  import { toWasmPolicies } from "../utils";
13
13
  import { SessionPolicies } from "@cartridge/presets";
14
14
  import { AddStarknetChainParameters } from "@starknet-io/types-js";
15
+ import { ParsedSessionPolicies, parsePolicies } from "../policies";
15
16
 
16
17
  interface SessionRegistration {
17
18
  username: string;
@@ -25,7 +26,7 @@ export default class TelegramProvider extends BaseProvider {
25
26
  private _tmaUrl: string;
26
27
  protected _chainId: string;
27
28
  protected _username?: string;
28
- protected _policies: SessionPolicies;
29
+ protected _policies: ParsedSessionPolicies;
29
30
  private _rpcUrl: string;
30
31
 
31
32
  constructor({
@@ -44,7 +45,7 @@ export default class TelegramProvider extends BaseProvider {
44
45
  this._rpcUrl = rpc;
45
46
  this._tmaUrl = tmaUrl;
46
47
  this._chainId = chainId;
47
- this._policies = policies;
48
+ this._policies = parsePolicies(policies);
48
49
 
49
50
  if (typeof window !== "undefined") {
50
51
  (window as any).starknet_controller = this;
package/src/utils.ts CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  import wasm from "@cartridge/account-wasm/controller";
13
13
  import { Policies, SessionPolicies } from "@cartridge/presets";
14
14
  import { ChainId } from "@starknet-io/types-js";
15
+ import { ParsedSessionPolicies } from "./policies";
15
16
 
16
17
  // Whitelist of allowed property names to prevent prototype pollution
17
18
  const ALLOWED_PROPERTIES = new Set([
@@ -88,13 +89,14 @@ export function toSessionPolicies(policies: Policies): SessionPolicies {
88
89
  : policies;
89
90
  }
90
91
 
91
- export function toWasmPolicies(policies: SessionPolicies): wasm.Policy[] {
92
+ export function toWasmPolicies(policies: ParsedSessionPolicies): wasm.Policy[] {
92
93
  return [
93
94
  ...Object.entries(policies.contracts ?? {}).flatMap(
94
95
  ([target, { methods }]) =>
95
96
  toArray(methods).map((m) => ({
96
97
  target,
97
98
  method: m.entrypoint,
99
+ authorized: m.authorized,
98
100
  })),
99
101
  ),
100
102
  ...(policies.messages ?? []).map((p) => {
@@ -112,6 +114,7 @@ export function toWasmPolicies(policies: SessionPolicies): wasm.Policy[] {
112
114
 
113
115
  return {
114
116
  scope_hash: hash.computePoseidonHash(domainHash, typeHash),
117
+ authorized: p.authorized,
115
118
  };
116
119
  }),
117
120
  ];
package/tsconfig.json CHANGED
@@ -5,8 +5,7 @@
5
5
  "rootDir": ".",
6
6
  "outDir": "./dist",
7
7
  "composite": false,
8
- "incremental": false,
9
- "moduleResolution": "node"
8
+ "incremental": false
10
9
  },
11
10
  "include": ["src/**/*", "package.json"]
12
11
  }