@cartridge/controller 0.5.9 → 0.7.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 +53 -115
- package/.turbo/turbo-build.log +54 -116
- package/dist/controller.cjs +860 -0
- package/dist/controller.cjs.map +1 -0
- package/dist/controller.d.cts +33 -0
- package/dist/controller.d.ts +4 -4
- package/dist/controller.js +254 -170
- package/dist/controller.js.map +1 -1
- package/dist/index.cjs +2200 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +17 -0
- package/dist/index.d.ts +12 -6
- package/dist/index.js +296 -7666
- package/dist/index.js.map +1 -1
- package/dist/lookup.cjs +59 -0
- package/dist/lookup.cjs.map +1 -0
- package/dist/lookup.d.cts +4 -0
- package/dist/lookup.js +7 -7
- package/dist/lookup.js.map +1 -1
- package/dist/node/index.cjs +726 -0
- package/dist/node/index.cjs.map +1 -0
- package/dist/node/index.d.cts +56 -0
- package/dist/node/index.d.ts +56 -0
- package/dist/{telegram/provider.js → node/index.js} +521 -216
- package/dist/node/index.js.map +1 -0
- package/dist/policies-DD1aPjQ4.d.cts +21 -0
- package/dist/policies-DD1aPjQ4.d.ts +21 -0
- package/dist/{types-CVnDQVqD.d.ts → provider-ap1C1ypF.d.cts} +27 -10
- package/dist/provider-ap1C1ypF.d.ts +201 -0
- package/dist/session/{provider.js → index.cjs} +342 -168
- package/dist/session/index.cjs.map +1 -0
- package/dist/session/index.d.cts +38 -0
- package/dist/session/index.d.ts +37 -7
- package/dist/session/index.js +309 -161
- package/dist/session/index.js.map +1 -1
- package/package.json +49 -16
- package/src/controller.ts +44 -15
- package/src/iframe/base.ts +1 -11
- package/src/mutex.ts +22 -0
- package/src/node/account.ts +72 -0
- package/src/node/backend.ts +159 -0
- package/src/node/index.ts +4 -0
- package/src/node/provider.ts +178 -0
- package/src/node/server.ts +89 -0
- package/src/policies.ts +49 -0
- package/src/provider.ts +33 -2
- package/src/session/account.ts +2 -1
- package/src/session/provider.ts +123 -10
- package/src/telegram/provider.ts +3 -2
- package/src/types.ts +3 -6
- package/src/utils.ts +4 -1
- package/tsconfig.json +3 -3
- package/dist/__tests__/parseChainId.test.d.ts +0 -2
- package/dist/__tests__/parseChainId.test.js +0 -89
- package/dist/__tests__/parseChainId.test.js.map +0 -1
- package/dist/account.d.ts +0 -38
- package/dist/account.js +0 -106
- package/dist/account.js.map +0 -1
- package/dist/constants.d.ts +0 -5
- package/dist/constants.js +0 -10
- package/dist/constants.js.map +0 -1
- package/dist/errors.d.ts +0 -5
- package/dist/errors.js +0 -11
- package/dist/errors.js.map +0 -1
- package/dist/icon.d.ts +0 -3
- package/dist/icon.js +0 -6
- package/dist/icon.js.map +0 -1
- package/dist/iframe/base.d.ts +0 -5
- package/dist/iframe/base.js +0 -122
- package/dist/iframe/base.js.map +0 -1
- package/dist/iframe/index.d.ts +0 -5
- package/dist/iframe/index.js +0 -184
- package/dist/iframe/index.js.map +0 -1
- package/dist/iframe/keychain.d.ts +0 -5
- package/dist/iframe/keychain.js +0 -143
- package/dist/iframe/keychain.js.map +0 -1
- package/dist/iframe/profile.d.ts +0 -5
- package/dist/iframe/profile.js +0 -163
- package/dist/iframe/profile.js.map +0 -1
- package/dist/index.d-BbTUPBeO.d.ts +0 -68
- package/dist/provider.d.ts +0 -22
- package/dist/provider.js +0 -198
- package/dist/provider.js.map +0 -1
- package/dist/session/account.d.ts +0 -37
- package/dist/session/account.js +0 -92
- package/dist/session/account.js.map +0 -1
- package/dist/session/backend.d.ts +0 -60
- package/dist/session/backend.js +0 -39
- package/dist/session/backend.js.map +0 -1
- package/dist/session/provider.d.ts +0 -30
- package/dist/session/provider.js.map +0 -1
- package/dist/telegram/backend.d.ts +0 -33
- package/dist/telegram/backend.js +0 -40
- package/dist/telegram/backend.js.map +0 -1
- package/dist/telegram/provider.d.ts +0 -26
- package/dist/telegram/provider.js.map +0 -1
- package/dist/types.d.ts +0 -5
- package/dist/types.js +0 -13
- package/dist/types.js.map +0 -1
- package/dist/utils.d.ts +0 -18
- package/dist/utils.js +0 -139
- package/dist/utils.js.map +0 -1
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,31 @@ export default class SessionProvider extends BaseProvider {
|
|
|
135
217
|
return;
|
|
136
218
|
}
|
|
137
219
|
|
|
220
|
+
// Check expiration
|
|
221
|
+
const expirationTime = parseInt(sessionRegistration.expiresAt) * 1000;
|
|
222
|
+
if (Date.now() >= expirationTime) {
|
|
223
|
+
this.clearStoredSession();
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Check stored policies
|
|
228
|
+
const storedPoliciesStr = localStorage.getItem("sessionPolicies");
|
|
229
|
+
if (storedPoliciesStr) {
|
|
230
|
+
const storedPolicies = JSON.parse(
|
|
231
|
+
storedPoliciesStr,
|
|
232
|
+
) as ParsedSessionPolicies;
|
|
233
|
+
|
|
234
|
+
const isValid = this.validatePoliciesSubset(
|
|
235
|
+
this._policies,
|
|
236
|
+
storedPolicies,
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
if (!isValid) {
|
|
240
|
+
this.clearStoredSession();
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
138
245
|
this._username = sessionRegistration.username;
|
|
139
246
|
this.account = new SessionAccount(this, {
|
|
140
247
|
rpcUrl: this._rpcUrl,
|
|
@@ -148,4 +255,10 @@ export default class SessionProvider extends BaseProvider {
|
|
|
148
255
|
|
|
149
256
|
return this.account;
|
|
150
257
|
}
|
|
258
|
+
|
|
259
|
+
private clearStoredSession(): void {
|
|
260
|
+
localStorage.removeItem("sessionSigner");
|
|
261
|
+
localStorage.removeItem("session");
|
|
262
|
+
localStorage.removeItem("sessionPolicies");
|
|
263
|
+
}
|
|
151
264
|
}
|
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/types.ts
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
TypedData,
|
|
13
13
|
} from "@starknet-io/types-js";
|
|
14
14
|
import { KeychainIFrame, ProfileIFrame } from "./iframe";
|
|
15
|
-
import {
|
|
15
|
+
import { Policy, SessionPolicies } from "@cartridge/presets";
|
|
16
16
|
|
|
17
17
|
export type Session = {
|
|
18
18
|
chainId: constants.StarknetChainId;
|
|
@@ -62,6 +62,7 @@ export type ExecuteReply =
|
|
|
62
62
|
export type ProbeReply = {
|
|
63
63
|
code: ResponseCodes.SUCCESS;
|
|
64
64
|
address: string;
|
|
65
|
+
rpcUrl?: string;
|
|
65
66
|
};
|
|
66
67
|
|
|
67
68
|
export type DeployReply = {
|
|
@@ -126,9 +127,9 @@ export interface Keychain {
|
|
|
126
127
|
}>;
|
|
127
128
|
delegateAccount(): string;
|
|
128
129
|
username(): string;
|
|
129
|
-
fetchControllers(contractAddresses: string[]): Promise<ControllerAccounts>;
|
|
130
130
|
openPurchaseCredits(): void;
|
|
131
131
|
openExecute(calls: Call[]): Promise<void>;
|
|
132
|
+
switchChain(rpcUrl: string): Promise<void>;
|
|
132
133
|
}
|
|
133
134
|
|
|
134
135
|
export interface Profile {
|
|
@@ -150,12 +151,8 @@ export type ControllerOptions = ProviderOptions &
|
|
|
150
151
|
export type IFrameOptions = {
|
|
151
152
|
/** The ID of the starter pack to use */
|
|
152
153
|
starterPackId?: string;
|
|
153
|
-
/** The theme to use */
|
|
154
|
-
theme?: string;
|
|
155
154
|
/** The preset to use */
|
|
156
155
|
preset?: string;
|
|
157
|
-
/** The color mode to use */
|
|
158
|
-
colorMode?: ColorMode;
|
|
159
156
|
};
|
|
160
157
|
|
|
161
158
|
export type Chain = {
|
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
|
];
|
package/tsconfig.json
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
"rootDir": ".",
|
|
6
6
|
"outDir": "./dist",
|
|
7
7
|
"composite": false,
|
|
8
|
-
"incremental": false
|
|
9
|
-
"moduleResolution": "node"
|
|
8
|
+
"incremental": false
|
|
10
9
|
},
|
|
11
|
-
"include": ["src/**/*",
|
|
10
|
+
"include": ["src/**/*"],
|
|
11
|
+
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
|
12
12
|
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
// src/__tests__/parseChainId.test.ts
|
|
2
|
-
import { constants as constants2, shortString as shortString2 } from "starknet";
|
|
3
|
-
|
|
4
|
-
// src/utils.ts
|
|
5
|
-
import {
|
|
6
|
-
addAddressPadding,
|
|
7
|
-
CallData,
|
|
8
|
-
constants,
|
|
9
|
-
getChecksumAddress,
|
|
10
|
-
hash,
|
|
11
|
-
shortString,
|
|
12
|
-
typedData,
|
|
13
|
-
TypedDataRevision
|
|
14
|
-
} from "starknet";
|
|
15
|
-
function parseChainId(url) {
|
|
16
|
-
const parts = url.pathname.split("/");
|
|
17
|
-
if (parts.includes("starknet")) {
|
|
18
|
-
if (parts.includes("mainnet")) {
|
|
19
|
-
return constants.StarknetChainId.SN_MAIN;
|
|
20
|
-
} else if (parts.includes("sepolia")) {
|
|
21
|
-
return constants.StarknetChainId.SN_SEPOLIA;
|
|
22
|
-
}
|
|
23
|
-
} else if (parts.length >= 3) {
|
|
24
|
-
const projectName = parts[2];
|
|
25
|
-
if (parts.includes("katana")) {
|
|
26
|
-
return shortString.encodeShortString(
|
|
27
|
-
`WP_${projectName.toUpperCase().replace(/-/g, "_")}`
|
|
28
|
-
);
|
|
29
|
-
} else if (parts.includes("mainnet")) {
|
|
30
|
-
return shortString.encodeShortString(
|
|
31
|
-
`GG_${projectName.toUpperCase().replace(/-/g, "_")}`
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
throw new Error(`Chain ${url.toString()} not supported`);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// src/__tests__/parseChainId.test.ts
|
|
39
|
-
describe("parseChainId", () => {
|
|
40
|
-
describe("Starknet chains", () => {
|
|
41
|
-
test("identifies mainnet", () => {
|
|
42
|
-
expect(
|
|
43
|
-
parseChainId(new URL("https://api.cartridge.gg/x/starknet/mainnet"))
|
|
44
|
-
).toBe(constants2.StarknetChainId.SN_MAIN);
|
|
45
|
-
});
|
|
46
|
-
test("identifies sepolia", () => {
|
|
47
|
-
expect(
|
|
48
|
-
parseChainId(new URL("https://api.cartridge.gg/x/starknet/sepolia"))
|
|
49
|
-
).toBe(constants2.StarknetChainId.SN_SEPOLIA);
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
describe("Project-specific chains", () => {
|
|
53
|
-
test("identifies slot chain", () => {
|
|
54
|
-
expect(
|
|
55
|
-
parseChainId(new URL("https://api.cartridge.gg/x/slot/katana"))
|
|
56
|
-
).toBe(shortString2.encodeShortString("WP_SLOT"));
|
|
57
|
-
});
|
|
58
|
-
test("identifies slot chain on localhost", () => {
|
|
59
|
-
expect(parseChainId(new URL("http://localhost:8001/x/slot/katana"))).toBe(
|
|
60
|
-
shortString2.encodeShortString("WP_SLOT")
|
|
61
|
-
);
|
|
62
|
-
});
|
|
63
|
-
test("identifies slot chain with hyphenated name", () => {
|
|
64
|
-
expect(
|
|
65
|
-
parseChainId(
|
|
66
|
-
new URL("https://api.cartridge.gg/x/my-slot-chain/katana")
|
|
67
|
-
)
|
|
68
|
-
).toBe(shortString2.encodeShortString("WP_MY_SLOT_CHAIN"));
|
|
69
|
-
});
|
|
70
|
-
test("identifies slot mainnet chain", () => {
|
|
71
|
-
expect(
|
|
72
|
-
parseChainId(new URL("https://api.cartridge.gg/x/slot/mainnet"))
|
|
73
|
-
).toBe(shortString2.encodeShortString("GG_SLOT"));
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
describe("Error cases", () => {
|
|
77
|
-
test("throws error for unsupported URL format", () => {
|
|
78
|
-
expect(
|
|
79
|
-
() => parseChainId(new URL("https://api.example.com/unsupported"))
|
|
80
|
-
).toThrow("Chain https://api.example.com/unsupported not supported");
|
|
81
|
-
});
|
|
82
|
-
test("throws error for URLs without proper chain identifiers", () => {
|
|
83
|
-
expect(
|
|
84
|
-
() => parseChainId(new URL("https://api.example.com/v1/starknet"))
|
|
85
|
-
).toThrow("Chain https://api.example.com/v1/starknet not supported");
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
//# sourceMappingURL=parseChainId.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/__tests__/parseChainId.test.ts","../../src/utils.ts"],"sourcesContent":["import { constants, shortString } from \"starknet\";\nimport { parseChainId } from \"../utils\";\n\ndescribe(\"parseChainId\", () => {\n describe(\"Starknet chains\", () => {\n test(\"identifies mainnet\", () => {\n expect(\n parseChainId(new URL(\"https://api.cartridge.gg/x/starknet/mainnet\")),\n ).toBe(constants.StarknetChainId.SN_MAIN);\n });\n\n test(\"identifies sepolia\", () => {\n expect(\n parseChainId(new URL(\"https://api.cartridge.gg/x/starknet/sepolia\")),\n ).toBe(constants.StarknetChainId.SN_SEPOLIA);\n });\n });\n\n describe(\"Project-specific chains\", () => {\n test(\"identifies slot chain\", () => {\n expect(\n parseChainId(new URL(\"https://api.cartridge.gg/x/slot/katana\")),\n ).toBe(shortString.encodeShortString(\"WP_SLOT\"));\n });\n\n test(\"identifies slot chain on localhost\", () => {\n expect(parseChainId(new URL(\"http://localhost:8001/x/slot/katana\"))).toBe(\n shortString.encodeShortString(\"WP_SLOT\"),\n );\n });\n\n test(\"identifies slot chain with hyphenated name\", () => {\n expect(\n parseChainId(\n new URL(\"https://api.cartridge.gg/x/my-slot-chain/katana\"),\n ),\n ).toBe(shortString.encodeShortString(\"WP_MY_SLOT_CHAIN\"));\n });\n\n test(\"identifies slot mainnet chain\", () => {\n expect(\n parseChainId(new URL(\"https://api.cartridge.gg/x/slot/mainnet\")),\n ).toBe(shortString.encodeShortString(\"GG_SLOT\"));\n });\n });\n\n describe(\"Error cases\", () => {\n test(\"throws error for unsupported URL format\", () => {\n expect(() =>\n parseChainId(new URL(\"https://api.example.com/unsupported\")),\n ).toThrow(\"Chain https://api.example.com/unsupported not supported\");\n });\n\n test(\"throws error for URLs without proper chain identifiers\", () => {\n expect(() =>\n parseChainId(new URL(\"https://api.example.com/v1/starknet\")),\n ).toThrow(\"Chain https://api.example.com/v1/starknet not supported\");\n });\n });\n});\n","import {\n addAddressPadding,\n Call,\n CallData,\n constants,\n getChecksumAddress,\n hash,\n shortString,\n typedData,\n TypedDataRevision,\n} from \"starknet\";\nimport wasm from \"@cartridge/account-wasm/controller\";\nimport { Policies, SessionPolicies } from \"@cartridge/presets\";\nimport { ChainId } from \"@starknet-io/types-js\";\n\n// Whitelist of allowed property names to prevent prototype pollution\nconst ALLOWED_PROPERTIES = new Set([\n \"contracts\",\n \"messages\",\n \"target\",\n \"method\",\n \"name\",\n \"description\",\n \"types\",\n \"domain\",\n \"primaryType\",\n]);\n\nfunction validatePropertyName(prop: string): void {\n if (!ALLOWED_PROPERTIES.has(prop)) {\n throw new Error(`Invalid property name: ${prop}`);\n }\n}\n\nfunction safeObjectAccess<T>(obj: any, prop: string): T {\n validatePropertyName(prop);\n return obj[prop];\n}\n\nexport function normalizeCalls(calls: Call | Call[]) {\n return toArray(calls).map((call) => {\n return {\n entrypoint: call.entrypoint,\n contractAddress: addAddressPadding(call.contractAddress),\n calldata: CallData.toHex(call.calldata),\n };\n });\n}\n\nexport function toSessionPolicies(policies: Policies): SessionPolicies {\n return Array.isArray(policies)\n ? policies.reduce<SessionPolicies>(\n (prev, p) => {\n if (safeObjectAccess<string>(p, \"target\")) {\n const target = getChecksumAddress(\n safeObjectAccess<string>(p, \"target\"),\n );\n const entrypoint = safeObjectAccess<string>(p, \"method\");\n const contracts = safeObjectAccess<Record<string, any>>(\n prev,\n \"contracts\",\n );\n const item = {\n name: humanizeString(entrypoint),\n entrypoint: entrypoint,\n description: safeObjectAccess<string>(p, \"description\"),\n };\n\n if (target in contracts) {\n const methods = toArray(contracts[target].methods);\n contracts[target] = {\n methods: [...methods, item],\n };\n } else {\n contracts[target] = {\n methods: [item],\n };\n }\n } else {\n const messages = safeObjectAccess<any[]>(prev, \"messages\");\n messages.push(p);\n }\n\n return prev;\n },\n { contracts: {}, messages: [] },\n )\n : policies;\n}\n\nexport function toWasmPolicies(policies: SessionPolicies): wasm.Policy[] {\n return [\n ...Object.entries(policies.contracts ?? {}).flatMap(\n ([target, { methods }]) =>\n toArray(methods).map((m) => ({\n target,\n method: m.entrypoint,\n })),\n ),\n ...(policies.messages ?? []).map((p) => {\n const domainHash = typedData.getStructHash(\n p.types,\n \"StarknetDomain\",\n p.domain,\n TypedDataRevision.ACTIVE,\n );\n const typeHash = typedData.getTypeHash(\n p.types,\n p.primaryType,\n TypedDataRevision.ACTIVE,\n );\n\n return {\n scope_hash: hash.computePoseidonHash(domainHash, typeHash),\n };\n }),\n ];\n}\n\nexport function toArray<T>(val: T | T[]): T[] {\n return Array.isArray(val) ? val : [val];\n}\n\nexport function humanizeString(str: string): string {\n return (\n str\n // Convert from camelCase or snake_case\n .replace(/([a-z])([A-Z])/g, \"$1 $2\") // camelCase to spaces\n .replace(/_/g, \" \") // snake_case to spaces\n .toLowerCase()\n // Capitalize first letter\n .replace(/^\\w/, (c) => c.toUpperCase())\n );\n}\n\nexport function parseChainId(url: URL): ChainId {\n const parts = url.pathname.split(\"/\");\n\n if (parts.includes(\"starknet\")) {\n if (parts.includes(\"mainnet\")) {\n return constants.StarknetChainId.SN_MAIN;\n } else if (parts.includes(\"sepolia\")) {\n return constants.StarknetChainId.SN_SEPOLIA;\n }\n } else if (parts.length >= 3) {\n const projectName = parts[2];\n if (parts.includes(\"katana\")) {\n return shortString.encodeShortString(\n `WP_${projectName.toUpperCase().replace(/-/g, \"_\")}`,\n ) as ChainId;\n } else if (parts.includes(\"mainnet\")) {\n return shortString.encodeShortString(\n `GG_${projectName.toUpperCase().replace(/-/g, \"_\")}`,\n ) as ChainId;\n }\n }\n\n throw new Error(`Chain ${url.toString()} not supported`);\n}\n"],"mappings":";AAAA,SAAS,aAAAA,YAAW,eAAAC,oBAAmB;;;ACAvC;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AA6HA,SAAS,aAAa,KAAmB;AAC9C,QAAM,QAAQ,IAAI,SAAS,MAAM,GAAG;AAEpC,MAAI,MAAM,SAAS,UAAU,GAAG;AAC9B,QAAI,MAAM,SAAS,SAAS,GAAG;AAC7B,aAAO,UAAU,gBAAgB;AAAA,IACnC,WAAW,MAAM,SAAS,SAAS,GAAG;AACpC,aAAO,UAAU,gBAAgB;AAAA,IACnC;AAAA,EACF,WAAW,MAAM,UAAU,GAAG;AAC5B,UAAM,cAAc,MAAM,CAAC;AAC3B,QAAI,MAAM,SAAS,QAAQ,GAAG;AAC5B,aAAO,YAAY;AAAA,QACjB,MAAM,YAAY,YAAY,EAAE,QAAQ,MAAM,GAAG,CAAC;AAAA,MACpD;AAAA,IACF,WAAW,MAAM,SAAS,SAAS,GAAG;AACpC,aAAO,YAAY;AAAA,QACjB,MAAM,YAAY,YAAY,EAAE,QAAQ,MAAM,GAAG,CAAC;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AAEA,QAAM,IAAI,MAAM,SAAS,IAAI,SAAS,CAAC,gBAAgB;AACzD;;;AD3JA,SAAS,gBAAgB,MAAM;AAC7B,WAAS,mBAAmB,MAAM;AAChC,SAAK,sBAAsB,MAAM;AAC/B;AAAA,QACE,aAAa,IAAI,IAAI,6CAA6C,CAAC;AAAA,MACrE,EAAE,KAAKC,WAAU,gBAAgB,OAAO;AAAA,IAC1C,CAAC;AAED,SAAK,sBAAsB,MAAM;AAC/B;AAAA,QACE,aAAa,IAAI,IAAI,6CAA6C,CAAC;AAAA,MACrE,EAAE,KAAKA,WAAU,gBAAgB,UAAU;AAAA,IAC7C,CAAC;AAAA,EACH,CAAC;AAED,WAAS,2BAA2B,MAAM;AACxC,SAAK,yBAAyB,MAAM;AAClC;AAAA,QACE,aAAa,IAAI,IAAI,wCAAwC,CAAC;AAAA,MAChE,EAAE,KAAKC,aAAY,kBAAkB,SAAS,CAAC;AAAA,IACjD,CAAC;AAED,SAAK,sCAAsC,MAAM;AAC/C,aAAO,aAAa,IAAI,IAAI,qCAAqC,CAAC,CAAC,EAAE;AAAA,QACnEA,aAAY,kBAAkB,SAAS;AAAA,MACzC;AAAA,IACF,CAAC;AAED,SAAK,8CAA8C,MAAM;AACvD;AAAA,QACE;AAAA,UACE,IAAI,IAAI,iDAAiD;AAAA,QAC3D;AAAA,MACF,EAAE,KAAKA,aAAY,kBAAkB,kBAAkB,CAAC;AAAA,IAC1D,CAAC;AAED,SAAK,iCAAiC,MAAM;AAC1C;AAAA,QACE,aAAa,IAAI,IAAI,yCAAyC,CAAC;AAAA,MACjE,EAAE,KAAKA,aAAY,kBAAkB,SAAS,CAAC;AAAA,IACjD,CAAC;AAAA,EACH,CAAC;AAED,WAAS,eAAe,MAAM;AAC5B,SAAK,2CAA2C,MAAM;AACpD;AAAA,QAAO,MACL,aAAa,IAAI,IAAI,qCAAqC,CAAC;AAAA,MAC7D,EAAE,QAAQ,yDAAyD;AAAA,IACrE,CAAC;AAED,SAAK,0DAA0D,MAAM;AACnE;AAAA,QAAO,MACL,aAAa,IAAI,IAAI,qCAAqC,CAAC;AAAA,MAC7D,EAAE,QAAQ,yDAAyD;AAAA,IACrE,CAAC;AAAA,EACH,CAAC;AACH,CAAC;","names":["constants","shortString","constants","shortString"]}
|
package/dist/account.d.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { WalletAccount, AllowArray, Call, InvokeFunctionResponse, TypedData } from 'starknet';
|
|
2
|
-
import { SPEC } from '@starknet-io/types-js';
|
|
3
|
-
import { K as Keychain, k as KeychainOptions, M as Modal } from './types-CVnDQVqD.js';
|
|
4
|
-
import { AsyncMethodReturns } from '@cartridge/penpal';
|
|
5
|
-
import BaseProvider from './provider.js';
|
|
6
|
-
import './index.d-BbTUPBeO.js';
|
|
7
|
-
|
|
8
|
-
declare class ControllerAccount extends WalletAccount {
|
|
9
|
-
address: string;
|
|
10
|
-
private keychain;
|
|
11
|
-
private modal;
|
|
12
|
-
private options?;
|
|
13
|
-
constructor(provider: BaseProvider, rpcUrl: string, address: string, keychain: AsyncMethodReturns<Keychain>, options: KeychainOptions, modal: Modal);
|
|
14
|
-
/**
|
|
15
|
-
* Invoke execute function in account contract
|
|
16
|
-
*
|
|
17
|
-
* @param calls the invocation object or an array of them, containing:
|
|
18
|
-
* - contractAddress - the address of the contract
|
|
19
|
-
* - entrypoint - the entrypoint of the contract
|
|
20
|
-
* - calldata - (defaults to []) the calldata
|
|
21
|
-
* - signature - (defaults to []) the signature
|
|
22
|
-
* @param abis (optional) the abi of the contract for better displaying
|
|
23
|
-
*
|
|
24
|
-
* @returns response from addTransaction
|
|
25
|
-
*/
|
|
26
|
-
execute(calls: AllowArray<Call>): Promise<InvokeFunctionResponse>;
|
|
27
|
-
/**
|
|
28
|
-
* Sign an JSON object for off-chain usage with the starknet private key and return the signature
|
|
29
|
-
* This adds a message prefix so it cant be interchanged with transactions
|
|
30
|
-
*
|
|
31
|
-
* @param json - JSON object to be signed
|
|
32
|
-
* @returns the signature of the JSON object
|
|
33
|
-
* @throws {Error} if the JSON object is not a valid JSON
|
|
34
|
-
*/
|
|
35
|
-
signMessage(typedData: TypedData): Promise<SPEC.SIGNATURE>;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export { ControllerAccount as default };
|
package/dist/account.js
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
// src/account.ts
|
|
2
|
-
import {
|
|
3
|
-
WalletAccount
|
|
4
|
-
} from "starknet";
|
|
5
|
-
|
|
6
|
-
// src/utils.ts
|
|
7
|
-
import {
|
|
8
|
-
addAddressPadding,
|
|
9
|
-
CallData,
|
|
10
|
-
constants,
|
|
11
|
-
getChecksumAddress,
|
|
12
|
-
hash,
|
|
13
|
-
shortString,
|
|
14
|
-
typedData,
|
|
15
|
-
TypedDataRevision
|
|
16
|
-
} from "starknet";
|
|
17
|
-
function toArray(val) {
|
|
18
|
-
return Array.isArray(val) ? val : [val];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// src/account.ts
|
|
22
|
-
var ControllerAccount = class extends WalletAccount {
|
|
23
|
-
constructor(provider, rpcUrl, address, keychain, options, modal) {
|
|
24
|
-
super({ nodeUrl: rpcUrl }, provider);
|
|
25
|
-
this.address = address;
|
|
26
|
-
this.keychain = keychain;
|
|
27
|
-
this.options = options;
|
|
28
|
-
this.modal = modal;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Invoke execute function in account contract
|
|
32
|
-
*
|
|
33
|
-
* @param calls the invocation object or an array of them, containing:
|
|
34
|
-
* - contractAddress - the address of the contract
|
|
35
|
-
* - entrypoint - the entrypoint of the contract
|
|
36
|
-
* - calldata - (defaults to []) the calldata
|
|
37
|
-
* - signature - (defaults to []) the signature
|
|
38
|
-
* @param abis (optional) the abi of the contract for better displaying
|
|
39
|
-
*
|
|
40
|
-
* @returns response from addTransaction
|
|
41
|
-
*/
|
|
42
|
-
async execute(calls) {
|
|
43
|
-
calls = toArray(calls);
|
|
44
|
-
return new Promise(async (resolve, reject) => {
|
|
45
|
-
const sessionExecute = await this.keychain.execute(
|
|
46
|
-
calls,
|
|
47
|
-
void 0,
|
|
48
|
-
void 0,
|
|
49
|
-
false
|
|
50
|
-
);
|
|
51
|
-
if (sessionExecute.code === "SUCCESS" /* SUCCESS */) {
|
|
52
|
-
resolve(sessionExecute);
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
if (this.options?.propagateSessionErrors) {
|
|
56
|
-
reject(sessionExecute.error);
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
this.modal.open();
|
|
60
|
-
const manualExecute = await this.keychain.execute(
|
|
61
|
-
calls,
|
|
62
|
-
void 0,
|
|
63
|
-
void 0,
|
|
64
|
-
true,
|
|
65
|
-
sessionExecute.error
|
|
66
|
-
);
|
|
67
|
-
if (manualExecute.code === "SUCCESS" /* SUCCESS */) {
|
|
68
|
-
resolve(manualExecute);
|
|
69
|
-
this.modal.close();
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
reject(manualExecute.error);
|
|
73
|
-
return;
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Sign an JSON object for off-chain usage with the starknet private key and return the signature
|
|
78
|
-
* This adds a message prefix so it cant be interchanged with transactions
|
|
79
|
-
*
|
|
80
|
-
* @param json - JSON object to be signed
|
|
81
|
-
* @returns the signature of the JSON object
|
|
82
|
-
* @throws {Error} if the JSON object is not a valid JSON
|
|
83
|
-
*/
|
|
84
|
-
async signMessage(typedData2) {
|
|
85
|
-
return new Promise(async (resolve, reject) => {
|
|
86
|
-
const sessionSign = await this.keychain.signMessage(typedData2, "", true);
|
|
87
|
-
if (!("code" in sessionSign)) {
|
|
88
|
-
resolve(sessionSign);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
this.modal.open();
|
|
92
|
-
const manualSign = await this.keychain.signMessage(typedData2, "", false);
|
|
93
|
-
if (!("code" in manualSign)) {
|
|
94
|
-
resolve(manualSign);
|
|
95
|
-
} else {
|
|
96
|
-
reject(manualSign.error);
|
|
97
|
-
}
|
|
98
|
-
this.modal.close();
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
var account_default = ControllerAccount;
|
|
103
|
-
export {
|
|
104
|
-
account_default as default
|
|
105
|
-
};
|
|
106
|
-
//# sourceMappingURL=account.js.map
|
package/dist/account.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/account.ts","../src/utils.ts"],"sourcesContent":["import {\n InvokeFunctionResponse,\n TypedData,\n WalletAccount,\n Call,\n AllowArray,\n} from \"starknet\";\n\nimport { SPEC } from \"@starknet-io/types-js\";\n\nimport {\n ConnectError,\n Keychain,\n KeychainOptions,\n Modal,\n ResponseCodes,\n} from \"./types\";\nimport { AsyncMethodReturns } from \"@cartridge/penpal\";\nimport BaseProvider from \"./provider\";\nimport { toArray } from \"./utils\";\n\nclass ControllerAccount extends WalletAccount {\n address: string;\n private keychain: AsyncMethodReturns<Keychain>;\n private modal: Modal;\n private options?: KeychainOptions;\n\n constructor(\n provider: BaseProvider,\n rpcUrl: string,\n address: string,\n keychain: AsyncMethodReturns<Keychain>,\n options: KeychainOptions,\n modal: Modal,\n ) {\n super({ nodeUrl: rpcUrl }, provider);\n\n this.address = address;\n this.keychain = keychain;\n this.options = options;\n this.modal = modal;\n }\n\n /**\n * Invoke execute function in account contract\n *\n * @param calls the invocation object or an array of them, containing:\n * - contractAddress - the address of the contract\n * - entrypoint - the entrypoint of the contract\n * - calldata - (defaults to []) the calldata\n * - signature - (defaults to []) the signature\n * @param abis (optional) the abi of the contract for better displaying\n *\n * @returns response from addTransaction\n */\n async execute(calls: AllowArray<Call>): Promise<InvokeFunctionResponse> {\n calls = toArray(calls);\n\n return new Promise(async (resolve, reject) => {\n const sessionExecute = await this.keychain.execute(\n calls,\n undefined,\n undefined,\n false,\n );\n\n // Session call succeeded\n if (sessionExecute.code === ResponseCodes.SUCCESS) {\n resolve(sessionExecute as InvokeFunctionResponse);\n return;\n }\n\n // Propagates session txn error back to caller\n if (this.options?.propagateSessionErrors) {\n reject((sessionExecute as ConnectError).error);\n return;\n }\n\n // Session call or Paymaster flow failed.\n // Session not avaialble, manual flow fallback\n this.modal.open();\n const manualExecute = await this.keychain.execute(\n calls,\n undefined,\n undefined,\n true,\n (sessionExecute as ConnectError).error,\n );\n\n // Manual call succeeded\n if (manualExecute.code === ResponseCodes.SUCCESS) {\n resolve(manualExecute as InvokeFunctionResponse);\n this.modal.close();\n return;\n }\n\n reject((manualExecute as ConnectError).error);\n return;\n });\n }\n\n /**\n * Sign an JSON object for off-chain usage with the starknet private key and return the signature\n * This adds a message prefix so it cant be interchanged with transactions\n *\n * @param json - JSON object to be signed\n * @returns the signature of the JSON object\n * @throws {Error} if the JSON object is not a valid JSON\n */\n async signMessage(typedData: TypedData): Promise<SPEC.SIGNATURE> {\n return new Promise(async (resolve, reject) => {\n const sessionSign = await this.keychain.signMessage(typedData, \"\", true);\n\n // Session sign succeeded\n if (!(\"code\" in sessionSign)) {\n resolve(sessionSign as SPEC.SIGNATURE);\n return;\n }\n\n // Session not avaialble, manual flow fallback\n this.modal.open();\n const manualSign = await this.keychain.signMessage(typedData, \"\", false);\n\n if (!(\"code\" in manualSign)) {\n resolve(manualSign as SPEC.SIGNATURE);\n } else {\n reject((manualSign as ConnectError).error);\n }\n this.modal.close();\n });\n }\n}\n\nexport default ControllerAccount;\n","import {\n addAddressPadding,\n Call,\n CallData,\n constants,\n getChecksumAddress,\n hash,\n shortString,\n typedData,\n TypedDataRevision,\n} from \"starknet\";\nimport wasm from \"@cartridge/account-wasm/controller\";\nimport { Policies, SessionPolicies } from \"@cartridge/presets\";\nimport { ChainId } from \"@starknet-io/types-js\";\n\n// Whitelist of allowed property names to prevent prototype pollution\nconst ALLOWED_PROPERTIES = new Set([\n \"contracts\",\n \"messages\",\n \"target\",\n \"method\",\n \"name\",\n \"description\",\n \"types\",\n \"domain\",\n \"primaryType\",\n]);\n\nfunction validatePropertyName(prop: string): void {\n if (!ALLOWED_PROPERTIES.has(prop)) {\n throw new Error(`Invalid property name: ${prop}`);\n }\n}\n\nfunction safeObjectAccess<T>(obj: any, prop: string): T {\n validatePropertyName(prop);\n return obj[prop];\n}\n\nexport function normalizeCalls(calls: Call | Call[]) {\n return toArray(calls).map((call) => {\n return {\n entrypoint: call.entrypoint,\n contractAddress: addAddressPadding(call.contractAddress),\n calldata: CallData.toHex(call.calldata),\n };\n });\n}\n\nexport function toSessionPolicies(policies: Policies): SessionPolicies {\n return Array.isArray(policies)\n ? policies.reduce<SessionPolicies>(\n (prev, p) => {\n if (safeObjectAccess<string>(p, \"target\")) {\n const target = getChecksumAddress(\n safeObjectAccess<string>(p, \"target\"),\n );\n const entrypoint = safeObjectAccess<string>(p, \"method\");\n const contracts = safeObjectAccess<Record<string, any>>(\n prev,\n \"contracts\",\n );\n const item = {\n name: humanizeString(entrypoint),\n entrypoint: entrypoint,\n description: safeObjectAccess<string>(p, \"description\"),\n };\n\n if (target in contracts) {\n const methods = toArray(contracts[target].methods);\n contracts[target] = {\n methods: [...methods, item],\n };\n } else {\n contracts[target] = {\n methods: [item],\n };\n }\n } else {\n const messages = safeObjectAccess<any[]>(prev, \"messages\");\n messages.push(p);\n }\n\n return prev;\n },\n { contracts: {}, messages: [] },\n )\n : policies;\n}\n\nexport function toWasmPolicies(policies: SessionPolicies): wasm.Policy[] {\n return [\n ...Object.entries(policies.contracts ?? {}).flatMap(\n ([target, { methods }]) =>\n toArray(methods).map((m) => ({\n target,\n method: m.entrypoint,\n })),\n ),\n ...(policies.messages ?? []).map((p) => {\n const domainHash = typedData.getStructHash(\n p.types,\n \"StarknetDomain\",\n p.domain,\n TypedDataRevision.ACTIVE,\n );\n const typeHash = typedData.getTypeHash(\n p.types,\n p.primaryType,\n TypedDataRevision.ACTIVE,\n );\n\n return {\n scope_hash: hash.computePoseidonHash(domainHash, typeHash),\n };\n }),\n ];\n}\n\nexport function toArray<T>(val: T | T[]): T[] {\n return Array.isArray(val) ? val : [val];\n}\n\nexport function humanizeString(str: string): string {\n return (\n str\n // Convert from camelCase or snake_case\n .replace(/([a-z])([A-Z])/g, \"$1 $2\") // camelCase to spaces\n .replace(/_/g, \" \") // snake_case to spaces\n .toLowerCase()\n // Capitalize first letter\n .replace(/^\\w/, (c) => c.toUpperCase())\n );\n}\n\nexport function parseChainId(url: URL): ChainId {\n const parts = url.pathname.split(\"/\");\n\n if (parts.includes(\"starknet\")) {\n if (parts.includes(\"mainnet\")) {\n return constants.StarknetChainId.SN_MAIN;\n } else if (parts.includes(\"sepolia\")) {\n return constants.StarknetChainId.SN_SEPOLIA;\n }\n } else if (parts.length >= 3) {\n const projectName = parts[2];\n if (parts.includes(\"katana\")) {\n return shortString.encodeShortString(\n `WP_${projectName.toUpperCase().replace(/-/g, \"_\")}`,\n ) as ChainId;\n } else if (parts.includes(\"mainnet\")) {\n return shortString.encodeShortString(\n `GG_${projectName.toUpperCase().replace(/-/g, \"_\")}`,\n ) as ChainId;\n }\n }\n\n throw new Error(`Chain ${url.toString()} not supported`);\n}\n"],"mappings":";AAAA;AAAA,EAGE;AAAA,OAGK;;;ACNP;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AA6GA,SAAS,QAAW,KAAmB;AAC5C,SAAO,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,GAAG;AACxC;;;ADpGA,IAAM,oBAAN,cAAgC,cAAc;AAAA,EAM5C,YACE,UACA,QACA,SACA,UACA,SACA,OACA;AACA,UAAM,EAAE,SAAS,OAAO,GAAG,QAAQ;AAEnC,SAAK,UAAU;AACf,SAAK,WAAW;AAChB,SAAK,UAAU;AACf,SAAK,QAAQ;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,QAAQ,OAA0D;AACtE,YAAQ,QAAQ,KAAK;AAErB,WAAO,IAAI,QAAQ,OAAO,SAAS,WAAW;AAC5C,YAAM,iBAAiB,MAAM,KAAK,SAAS;AAAA,QACzC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAGA,UAAI,eAAe,kCAAgC;AACjD,gBAAQ,cAAwC;AAChD;AAAA,MACF;AAGA,UAAI,KAAK,SAAS,wBAAwB;AACxC,eAAQ,eAAgC,KAAK;AAC7C;AAAA,MACF;AAIA,WAAK,MAAM,KAAK;AAChB,YAAM,gBAAgB,MAAM,KAAK,SAAS;AAAA,QACxC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACC,eAAgC;AAAA,MACnC;AAGA,UAAI,cAAc,kCAAgC;AAChD,gBAAQ,aAAuC;AAC/C,aAAK,MAAM,MAAM;AACjB;AAAA,MACF;AAEA,aAAQ,cAA+B,KAAK;AAC5C;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,YAAYA,YAA+C;AAC/D,WAAO,IAAI,QAAQ,OAAO,SAAS,WAAW;AAC5C,YAAM,cAAc,MAAM,KAAK,SAAS,YAAYA,YAAW,IAAI,IAAI;AAGvE,UAAI,EAAE,UAAU,cAAc;AAC5B,gBAAQ,WAA6B;AACrC;AAAA,MACF;AAGA,WAAK,MAAM,KAAK;AAChB,YAAM,aAAa,MAAM,KAAK,SAAS,YAAYA,YAAW,IAAI,KAAK;AAEvE,UAAI,EAAE,UAAU,aAAa;AAC3B,gBAAQ,UAA4B;AAAA,MACtC,OAAO;AACL,eAAQ,WAA4B,KAAK;AAAA,MAC3C;AACA,WAAK,MAAM,MAAM;AAAA,IACnB,CAAC;AAAA,EACH;AACF;AAEA,IAAO,kBAAQ;","names":["typedData"]}
|
package/dist/constants.d.ts
DELETED
package/dist/constants.js
DELETED
package/dist/constants.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts"],"sourcesContent":["export const KEYCHAIN_URL = \"https://x.cartridge.gg\";\nexport const PROFILE_URL = \"https://profile.cartridge.gg\";\nexport const API_URL = \"https://api.cartridge.gg\";\n"],"mappings":";AAAO,IAAM,eAAe;AACrB,IAAM,cAAc;AACpB,IAAM,UAAU;","names":[]}
|
package/dist/errors.d.ts
DELETED
package/dist/errors.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
// src/errors.ts
|
|
2
|
-
var NotReadyToConnect = class _NotReadyToConnect extends Error {
|
|
3
|
-
constructor() {
|
|
4
|
-
super("Not ready to connect");
|
|
5
|
-
Object.setPrototypeOf(this, _NotReadyToConnect.prototype);
|
|
6
|
-
}
|
|
7
|
-
};
|
|
8
|
-
export {
|
|
9
|
-
NotReadyToConnect
|
|
10
|
-
};
|
|
11
|
-
//# sourceMappingURL=errors.js.map
|
package/dist/errors.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts"],"sourcesContent":["export class NotReadyToConnect extends Error {\n constructor() {\n super(\"Not ready to connect\");\n\n Object.setPrototypeOf(this, NotReadyToConnect.prototype);\n }\n}\n"],"mappings":";AAAO,IAAM,oBAAN,MAAM,2BAA0B,MAAM;AAAA,EAC3C,cAAc;AACZ,UAAM,sBAAsB;AAE5B,WAAO,eAAe,MAAM,mBAAkB,SAAS;AAAA,EACzD;AACF;","names":[]}
|