@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.
- package/.turbo/turbo-build$colon$deps.log +44 -82
- package/.turbo/turbo-build.log +47 -85
- package/dist/__tests__/parseChainId.test.js.map +1 -1
- package/dist/account.js +4 -0
- package/dist/account.js.map +1 -1
- package/dist/controller.js +177 -109
- package/dist/controller.js.map +1 -1
- package/dist/iframe/base.js +4 -0
- package/dist/iframe/base.js.map +1 -1
- package/dist/iframe/index.js +4 -0
- package/dist/iframe/index.js.map +1 -1
- package/dist/iframe/keychain.js +4 -0
- package/dist/iframe/keychain.js.map +1 -1
- package/dist/iframe/profile.js +4 -0
- package/dist/iframe/profile.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +181 -111
- package/dist/index.js.map +1 -1
- package/dist/mutex.d.ts +14 -0
- package/dist/mutex.js +22 -0
- package/dist/mutex.js.map +1 -0
- package/dist/policies.d.ts +19 -0
- package/dist/policies.js +26 -0
- package/dist/policies.js.map +1 -0
- package/dist/provider.d.ts +2 -0
- package/dist/provider.js +163 -109
- package/dist/provider.js.map +1 -1
- package/dist/session/account.js +2 -0
- package/dist/session/account.js.map +1 -1
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.js +286 -121
- package/dist/session/index.js.map +1 -1
- package/dist/session/provider.d.ts +7 -2
- package/dist/session/provider.js +286 -121
- package/dist/session/provider.js.map +1 -1
- package/dist/telegram/provider.d.ts +2 -1
- package/dist/telegram/provider.js +198 -112
- package/dist/telegram/provider.js.map +1 -1
- package/dist/utils.d.ts +2 -1
- package/dist/utils.js +4 -2
- package/dist/utils.js.map +1 -1
- package/package.json +22 -8
- package/src/controller.ts +1 -0
- package/src/mutex.ts +22 -0
- package/src/policies.ts +49 -0
- package/src/provider.ts +33 -2
- package/src/session/account.ts +1 -0
- package/src/session/provider.ts +139 -10
- package/src/telegram/provider.ts +3 -2
- package/src/utils.ts +4 -1
- package/tsconfig.json +1 -2
package/src/session/provider.ts
CHANGED
|
@@ -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:
|
|
35
|
+
protected _policies: ParsedSessionPolicies;
|
|
36
|
+
protected _keychainUrl: string;
|
|
34
37
|
|
|
35
|
-
constructor({
|
|
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.
|
|
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
|
-
|
|
55
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|
package/src/telegram/provider.ts
CHANGED
|
@@ -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:
|
|
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:
|
|
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
|
];
|