@cartridge/controller 0.5.1 → 0.5.2
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 +116 -115
- package/.turbo/turbo-build.log +120 -0
- package/.turbo/turbo-format.log +25 -0
- package/dist/account.d.ts +2 -1
- package/dist/account.js +19 -4
- package/dist/account.js.map +1 -1
- package/dist/constants.d.ts +2 -1
- package/dist/constants.js +2 -0
- package/dist/constants.js.map +1 -1
- package/dist/controller.d.ts +2 -1
- package/dist/controller.js +27 -184
- package/dist/controller.js.map +1 -1
- package/dist/iframe/base.d.ts +2 -1
- package/dist/iframe/base.js +3 -177
- package/dist/iframe/base.js.map +1 -1
- package/dist/iframe/index.d.ts +2 -1
- package/dist/iframe/index.js +8 -180
- package/dist/iframe/index.js.map +1 -1
- package/dist/iframe/keychain.d.ts +2 -1
- package/dist/iframe/keychain.js +3 -177
- package/dist/iframe/keychain.js.map +1 -1
- package/dist/iframe/profile.d.ts +2 -1
- package/dist/iframe/profile.js +8 -180
- package/dist/iframe/profile.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +166 -185
- package/dist/index.js.map +1 -1
- package/dist/lookup.d.ts +4 -0
- package/dist/lookup.js +56 -0
- package/dist/lookup.js.map +1 -0
- package/dist/provider.d.ts +2 -1
- package/dist/session/account.d.ts +2 -2
- package/dist/session/account.js +6 -170
- package/dist/session/account.js.map +1 -1
- package/dist/session/index.d.ts +2 -2
- package/dist/session/index.js +20 -183
- package/dist/session/index.js.map +1 -1
- package/dist/session/provider.d.ts +4 -3
- package/dist/session/provider.js +19 -14
- package/dist/session/provider.js.map +1 -1
- package/dist/telegram/provider.d.ts +7 -4
- package/dist/telegram/provider.js +19 -14
- package/dist/telegram/provider.js.map +1 -1
- package/dist/{types-ikHqoYmG.d.ts → types-1WsOoNO2.d.ts} +17 -37
- package/dist/types.d.ts +2 -1
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +5 -5
- package/dist/utils.js +80 -14
- package/dist/utils.js.map +1 -1
- package/package.json +5 -3
- package/src/account.ts +2 -1
- package/src/constants.ts +1 -0
- package/src/controller.ts +1 -1
- package/src/iframe/base.ts +3 -9
- package/src/iframe/profile.ts +5 -3
- package/src/index.ts +3 -1
- package/src/lookup.ts +68 -0
- package/src/session/account.ts +0 -1
- package/src/session/index.ts +0 -2
- package/src/session/provider.ts +4 -4
- package/src/telegram/provider.ts +7 -7
- package/src/types.ts +23 -44
- package/src/utils.ts +100 -16
- package/dist/presets.d.ts +0 -8
- package/dist/presets.js +0 -170
- package/dist/presets.js.map +0 -1
- package/src/presets.ts +0 -167
package/dist/utils.js
CHANGED
|
@@ -2,12 +2,33 @@
|
|
|
2
2
|
import {
|
|
3
3
|
addAddressPadding,
|
|
4
4
|
CallData,
|
|
5
|
+
getChecksumAddress,
|
|
5
6
|
hash,
|
|
6
7
|
typedData,
|
|
7
8
|
TypedDataRevision
|
|
8
9
|
} from "starknet";
|
|
10
|
+
var ALLOWED_PROPERTIES = /* @__PURE__ */ new Set([
|
|
11
|
+
"contracts",
|
|
12
|
+
"messages",
|
|
13
|
+
"target",
|
|
14
|
+
"method",
|
|
15
|
+
"name",
|
|
16
|
+
"description",
|
|
17
|
+
"types",
|
|
18
|
+
"domain",
|
|
19
|
+
"primaryType"
|
|
20
|
+
]);
|
|
21
|
+
function validatePropertyName(prop) {
|
|
22
|
+
if (!ALLOWED_PROPERTIES.has(prop)) {
|
|
23
|
+
throw new Error(`Invalid property name: ${prop}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function safeObjectAccess(obj, prop) {
|
|
27
|
+
validatePropertyName(prop);
|
|
28
|
+
return obj[prop];
|
|
29
|
+
}
|
|
9
30
|
function normalizeCalls(calls) {
|
|
10
|
-
return (
|
|
31
|
+
return toArray(calls).map((call) => {
|
|
11
32
|
return {
|
|
12
33
|
entrypoint: call.entrypoint,
|
|
13
34
|
contractAddress: addAddressPadding(call.contractAddress),
|
|
@@ -15,33 +36,78 @@ function normalizeCalls(calls) {
|
|
|
15
36
|
};
|
|
16
37
|
});
|
|
17
38
|
}
|
|
39
|
+
function toSessionPolicies(policies) {
|
|
40
|
+
return Array.isArray(policies) ? policies.reduce(
|
|
41
|
+
(prev, p) => {
|
|
42
|
+
if (safeObjectAccess(p, "target")) {
|
|
43
|
+
const target = getChecksumAddress(
|
|
44
|
+
safeObjectAccess(p, "target")
|
|
45
|
+
);
|
|
46
|
+
const entrypoint = safeObjectAccess(p, "method");
|
|
47
|
+
const contracts = safeObjectAccess(
|
|
48
|
+
prev,
|
|
49
|
+
"contracts"
|
|
50
|
+
);
|
|
51
|
+
const item = {
|
|
52
|
+
name: humanizeString(entrypoint),
|
|
53
|
+
entrypoint,
|
|
54
|
+
description: safeObjectAccess(p, "description")
|
|
55
|
+
};
|
|
56
|
+
if (target in contracts) {
|
|
57
|
+
const methods = toArray(contracts[target].methods);
|
|
58
|
+
contracts[target] = {
|
|
59
|
+
methods: [...methods, item]
|
|
60
|
+
};
|
|
61
|
+
} else {
|
|
62
|
+
contracts[target] = {
|
|
63
|
+
methods: [item]
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
const messages = safeObjectAccess(prev, "messages");
|
|
68
|
+
messages.push(p);
|
|
69
|
+
}
|
|
70
|
+
return prev;
|
|
71
|
+
},
|
|
72
|
+
{ contracts: {}, messages: [] }
|
|
73
|
+
) : policies;
|
|
74
|
+
}
|
|
18
75
|
function toWasmPolicies(policies) {
|
|
19
|
-
return
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
target
|
|
23
|
-
method:
|
|
24
|
-
}
|
|
25
|
-
|
|
76
|
+
return [
|
|
77
|
+
...Object.entries(policies.contracts ?? {}).flatMap(
|
|
78
|
+
([target, { methods }]) => toArray(methods).map((m) => ({
|
|
79
|
+
target,
|
|
80
|
+
method: m.entrypoint
|
|
81
|
+
}))
|
|
82
|
+
),
|
|
83
|
+
...(policies.messages ?? []).map((p) => {
|
|
26
84
|
const domainHash = typedData.getStructHash(
|
|
27
|
-
|
|
85
|
+
p.types,
|
|
28
86
|
"StarknetDomain",
|
|
29
|
-
|
|
87
|
+
p.domain,
|
|
30
88
|
TypedDataRevision.ACTIVE
|
|
31
89
|
);
|
|
32
90
|
const typeHash = typedData.getTypeHash(
|
|
33
|
-
|
|
34
|
-
|
|
91
|
+
p.types,
|
|
92
|
+
p.primaryType,
|
|
35
93
|
TypedDataRevision.ACTIVE
|
|
36
94
|
);
|
|
37
95
|
return {
|
|
38
96
|
scope_hash: hash.computePoseidonHash(domainHash, typeHash)
|
|
39
97
|
};
|
|
40
|
-
}
|
|
41
|
-
|
|
98
|
+
})
|
|
99
|
+
];
|
|
100
|
+
}
|
|
101
|
+
function toArray(val) {
|
|
102
|
+
return Array.isArray(val) ? val : [val];
|
|
103
|
+
}
|
|
104
|
+
function humanizeString(str) {
|
|
105
|
+
return str.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/_/g, " ").toLowerCase().replace(/^\w/, (c) => c.toUpperCase());
|
|
42
106
|
}
|
|
43
107
|
export {
|
|
44
108
|
normalizeCalls,
|
|
109
|
+
toArray,
|
|
110
|
+
toSessionPolicies,
|
|
45
111
|
toWasmPolicies
|
|
46
112
|
};
|
|
47
113
|
//# sourceMappingURL=utils.js.map
|
package/dist/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils.ts"],"sourcesContent":["import {\n addAddressPadding,\n Call,\n CallData,\n hash,\n typedData,\n TypedDataRevision,\n} from \"starknet\";\nimport
|
|
1
|
+
{"version":3,"sources":["../src/utils.ts"],"sourcesContent":["import {\n addAddressPadding,\n Call,\n CallData,\n getChecksumAddress,\n hash,\n typedData,\n TypedDataRevision,\n} from \"starknet\";\nimport wasm from \"@cartridge/account-wasm/controller\";\nimport { Policies, SessionPolicies } from \"@cartridge/presets\";\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\nfunction 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"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cartridge/controller",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Cartridge Controller",
|
|
5
5
|
"module": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -31,15 +31,17 @@
|
|
|
31
31
|
"fast-deep-equal": "^3.1.3",
|
|
32
32
|
"query-string": "^7.1.1",
|
|
33
33
|
"starknet": "^6.11.0",
|
|
34
|
-
"@cartridge/account-wasm": "
|
|
34
|
+
"@cartridge/account-wasm": "0.5.2",
|
|
35
|
+
"@cartridge/presets": "0.5.2"
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|
|
37
38
|
"@types/node": "^20.6.0",
|
|
38
39
|
"typescript": "^5.4.5",
|
|
39
|
-
"@cartridge/tsconfig": "
|
|
40
|
+
"@cartridge/tsconfig": "0.5.2"
|
|
40
41
|
},
|
|
41
42
|
"scripts": {
|
|
42
43
|
"build:deps": "tsup --dts-resolve",
|
|
44
|
+
"build": "pnpm build:deps",
|
|
43
45
|
"format": "prettier --write \"src/**/*.ts\"",
|
|
44
46
|
"format:check": "prettier --check \"src/**/*.ts\"",
|
|
45
47
|
"version": "pnpm pkg get version"
|
package/src/account.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
} from "./types";
|
|
18
18
|
import { AsyncMethodReturns } from "@cartridge/penpal";
|
|
19
19
|
import BaseProvider from "./provider";
|
|
20
|
+
import { toArray } from "./utils";
|
|
20
21
|
|
|
21
22
|
class ControllerAccount extends WalletAccount {
|
|
22
23
|
address: string;
|
|
@@ -52,7 +53,7 @@ class ControllerAccount extends WalletAccount {
|
|
|
52
53
|
* @returns response from addTransaction
|
|
53
54
|
*/
|
|
54
55
|
async execute(calls: AllowArray<Call>): Promise<InvokeFunctionResponse> {
|
|
55
|
-
calls =
|
|
56
|
+
calls = toArray(calls);
|
|
56
57
|
|
|
57
58
|
return new Promise(async (resolve, reject) => {
|
|
58
59
|
const sessionExecute = await this.keychain.execute(
|
package/src/constants.ts
CHANGED
package/src/controller.ts
CHANGED
|
@@ -5,7 +5,6 @@ import { KeychainIFrame, ProfileIFrame } from "./iframe";
|
|
|
5
5
|
import { NotReadyToConnect } from "./errors";
|
|
6
6
|
import {
|
|
7
7
|
Keychain,
|
|
8
|
-
Policy,
|
|
9
8
|
ResponseCodes,
|
|
10
9
|
ConnectReply,
|
|
11
10
|
ProbeReply,
|
|
@@ -17,6 +16,7 @@ import {
|
|
|
17
16
|
} from "./types";
|
|
18
17
|
import BaseProvider from "./provider";
|
|
19
18
|
import { WalletAccount } from "starknet";
|
|
19
|
+
import { Policy } from "@cartridge/presets";
|
|
20
20
|
|
|
21
21
|
export default class ControllerProvider extends BaseProvider {
|
|
22
22
|
private keychain?: AsyncMethodReturns<Keychain>;
|
package/src/iframe/base.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { AsyncMethodReturns, connectToChild } from "@cartridge/penpal";
|
|
2
|
-
import { defaultPresets } from "../presets";
|
|
3
2
|
import { ControllerOptions, Modal } from "../types";
|
|
4
3
|
|
|
5
4
|
export type IFrameOptions<CallSender> = Omit<
|
|
@@ -35,14 +34,9 @@ export class IFrame<CallSender extends {}> implements Modal {
|
|
|
35
34
|
return;
|
|
36
35
|
}
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
"theme",
|
|
40
|
-
|
|
41
|
-
JSON.stringify(
|
|
42
|
-
defaultPresets[theme ?? "cartridge"] ?? defaultPresets.cartridge,
|
|
43
|
-
),
|
|
44
|
-
),
|
|
45
|
-
);
|
|
37
|
+
if (theme) {
|
|
38
|
+
url.searchParams.set("theme", encodeURIComponent(theme));
|
|
39
|
+
}
|
|
46
40
|
|
|
47
41
|
if (colorMode) {
|
|
48
42
|
url.searchParams.set("colorMode", colorMode);
|
package/src/iframe/profile.ts
CHANGED
|
@@ -24,10 +24,12 @@ export class ProfileIFrame extends IFrame<Profile> {
|
|
|
24
24
|
const _url = new URL(
|
|
25
25
|
slot
|
|
26
26
|
? namespace
|
|
27
|
-
? `${_profileUrl}/account/${username}/slot/${slot}?
|
|
28
|
-
|
|
27
|
+
? `${_profileUrl}/account/${username}/slot/${slot}?ps=${encodeURIComponent(
|
|
28
|
+
slot,
|
|
29
|
+
)}&ns=${encodeURIComponent(namespace)}`
|
|
30
|
+
: `${_profileUrl}/account/${username}/slot/${slot}?ps=${encodeURIComponent(
|
|
31
|
+
slot,
|
|
29
32
|
)}`
|
|
30
|
-
: `${_profileUrl}/account/${username}/slot/${slot}`
|
|
31
33
|
: `${_profileUrl}/account/${username}`,
|
|
32
34
|
);
|
|
33
35
|
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { default } from "./controller";
|
|
2
2
|
export * from "./errors";
|
|
3
3
|
export * from "./types";
|
|
4
|
+
export * from "./lookup";
|
|
4
5
|
|
|
5
|
-
export {
|
|
6
|
+
export { toWasmPolicies, toSessionPolicies, toArray } from "./utils";
|
|
7
|
+
export * from "@cartridge/presets";
|
package/src/lookup.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { LookupRequest, LookupResponse } from "./types";
|
|
2
|
+
import { num } from "starknet";
|
|
3
|
+
import { API_URL } from "./constants";
|
|
4
|
+
|
|
5
|
+
const cache = new Map<string, string>();
|
|
6
|
+
|
|
7
|
+
async function lookup(request: LookupRequest): Promise<LookupResponse> {
|
|
8
|
+
if (!request.addresses?.length && !request.usernames?.length) {
|
|
9
|
+
return { results: [] };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const response = await fetch(`${API_URL}/lookup`, {
|
|
13
|
+
method: "POST",
|
|
14
|
+
headers: {
|
|
15
|
+
"Content-Type": "application/json",
|
|
16
|
+
},
|
|
17
|
+
body: JSON.stringify(request),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
if (!response.ok) {
|
|
21
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return response.json();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function lookupUsernames(
|
|
28
|
+
usernames: string[],
|
|
29
|
+
): Promise<Map<string, string>> {
|
|
30
|
+
const uncachedUsernames = usernames.filter((name) => !cache.has(name));
|
|
31
|
+
|
|
32
|
+
if (uncachedUsernames.length > 0) {
|
|
33
|
+
const response = await lookup({ usernames: uncachedUsernames });
|
|
34
|
+
|
|
35
|
+
response.results.forEach((result) => {
|
|
36
|
+
cache.set(result.username, result.addresses[0]); // TODO: handle multiple controller addresses
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return new Map(
|
|
41
|
+
usernames
|
|
42
|
+
.map((name) => [name, cache.get(name)] as [string, string])
|
|
43
|
+
.filter((entry): entry is [string, string] => entry[1] !== undefined),
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function lookupAddresses(
|
|
48
|
+
addresses: string[],
|
|
49
|
+
): Promise<Map<string, string>> {
|
|
50
|
+
addresses = addresses.map(num.toHex);
|
|
51
|
+
const uncachedAddresses = addresses.filter((addr) => !cache.has(addr));
|
|
52
|
+
|
|
53
|
+
if (uncachedAddresses.length > 0) {
|
|
54
|
+
const response = await lookup({
|
|
55
|
+
addresses: uncachedAddresses,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
response.results.forEach((result) => {
|
|
59
|
+
cache.set(result.addresses[0], result.username); // TODO: handle multiple controller addresses
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return new Map(
|
|
64
|
+
addresses
|
|
65
|
+
.map((addr) => [addr, cache.get(addr)] as [string, string])
|
|
66
|
+
.filter((entry): entry is [string, string] => entry[1] !== undefined),
|
|
67
|
+
);
|
|
68
|
+
}
|
package/src/session/account.ts
CHANGED
package/src/session/index.ts
CHANGED
package/src/session/provider.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Policy } from "../types";
|
|
2
1
|
import { ec, stark, WalletAccount } from "starknet";
|
|
3
2
|
|
|
4
3
|
import SessionAccount from "./account";
|
|
5
4
|
import { KEYCHAIN_URL } from "../constants";
|
|
6
5
|
import BaseProvider from "../provider";
|
|
7
|
-
import { toWasmPolicies } from "
|
|
6
|
+
import { toWasmPolicies } from "../utils";
|
|
7
|
+
import { SessionPolicies } from "@cartridge/presets";
|
|
8
8
|
|
|
9
9
|
interface SessionRegistration {
|
|
10
10
|
username: string;
|
|
@@ -17,7 +17,7 @@ interface SessionRegistration {
|
|
|
17
17
|
export type SessionOptions = {
|
|
18
18
|
rpc: string;
|
|
19
19
|
chainId: string;
|
|
20
|
-
policies:
|
|
20
|
+
policies: SessionPolicies;
|
|
21
21
|
redirectUrl: string;
|
|
22
22
|
};
|
|
23
23
|
|
|
@@ -29,7 +29,7 @@ export default class SessionProvider extends BaseProvider {
|
|
|
29
29
|
|
|
30
30
|
protected _username?: string;
|
|
31
31
|
protected _redirectUrl: string;
|
|
32
|
-
protected _policies:
|
|
32
|
+
protected _policies: SessionPolicies;
|
|
33
33
|
|
|
34
34
|
constructor({ rpc, chainId, policies, redirectUrl }: SessionOptions) {
|
|
35
35
|
super({ rpc });
|
package/src/telegram/provider.ts
CHANGED
|
@@ -6,11 +6,11 @@ import {
|
|
|
6
6
|
} from "@telegram-apps/sdk";
|
|
7
7
|
import { ec, stark, WalletAccount } from "starknet";
|
|
8
8
|
|
|
9
|
-
import { KEYCHAIN_URL } from "
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
9
|
+
import { KEYCHAIN_URL } from "../constants";
|
|
10
|
+
import SessionAccount from "../session/account";
|
|
11
|
+
import BaseProvider from "../provider";
|
|
12
|
+
import { toWasmPolicies } from "../utils";
|
|
13
|
+
import { SessionPolicies } from "@cartridge/presets";
|
|
14
14
|
|
|
15
15
|
interface SessionRegistration {
|
|
16
16
|
username: string;
|
|
@@ -24,7 +24,7 @@ export default class TelegramProvider extends BaseProvider {
|
|
|
24
24
|
private _tmaUrl: string;
|
|
25
25
|
protected _chainId: string;
|
|
26
26
|
protected _username?: string;
|
|
27
|
-
protected _policies:
|
|
27
|
+
protected _policies: SessionPolicies;
|
|
28
28
|
|
|
29
29
|
constructor({
|
|
30
30
|
rpc,
|
|
@@ -34,7 +34,7 @@ export default class TelegramProvider extends BaseProvider {
|
|
|
34
34
|
}: {
|
|
35
35
|
rpc: string;
|
|
36
36
|
chainId: string;
|
|
37
|
-
policies:
|
|
37
|
+
policies: SessionPolicies;
|
|
38
38
|
tmaUrl: string;
|
|
39
39
|
}) {
|
|
40
40
|
super({
|
package/src/types.ts
CHANGED
|
@@ -8,11 +8,15 @@ import {
|
|
|
8
8
|
import {
|
|
9
9
|
AddInvokeTransactionResult,
|
|
10
10
|
Signature,
|
|
11
|
-
StarknetDomain,
|
|
12
|
-
StarknetType,
|
|
13
11
|
TypedData,
|
|
14
12
|
} from "@starknet-io/types-js";
|
|
15
13
|
import { KeychainIFrame, ProfileIFrame } from "./iframe";
|
|
14
|
+
import {
|
|
15
|
+
ColorMode,
|
|
16
|
+
Policies,
|
|
17
|
+
Policy,
|
|
18
|
+
SessionPolicies,
|
|
19
|
+
} from "@cartridge/presets";
|
|
16
20
|
|
|
17
21
|
export type Session = {
|
|
18
22
|
chainId: constants.StarknetChainId;
|
|
@@ -25,20 +29,6 @@ export type Session = {
|
|
|
25
29
|
};
|
|
26
30
|
};
|
|
27
31
|
|
|
28
|
-
export type Policy = CallPolicy | TypedDataPolicy;
|
|
29
|
-
|
|
30
|
-
export type CallPolicy = {
|
|
31
|
-
target: string;
|
|
32
|
-
method: string;
|
|
33
|
-
description?: string;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export type TypedDataPolicy = {
|
|
37
|
-
types: Record<string, StarknetType[]>;
|
|
38
|
-
primaryType: string;
|
|
39
|
-
domain: StarknetDomain;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
32
|
export enum ResponseCodes {
|
|
43
33
|
SUCCESS = "SUCCESS",
|
|
44
34
|
NOT_CONNECTED = "NOT_CONNECTED",
|
|
@@ -62,7 +52,7 @@ export type ControllerError = {
|
|
|
62
52
|
export type ConnectReply = {
|
|
63
53
|
code: ResponseCodes.SUCCESS;
|
|
64
54
|
address: string;
|
|
65
|
-
policies:
|
|
55
|
+
policies: SessionPolicies;
|
|
66
56
|
};
|
|
67
57
|
|
|
68
58
|
export type ExecuteReply =
|
|
@@ -88,6 +78,20 @@ export type IFrames = {
|
|
|
88
78
|
profile?: ProfileIFrame;
|
|
89
79
|
};
|
|
90
80
|
|
|
81
|
+
export interface LookupRequest {
|
|
82
|
+
usernames?: string[];
|
|
83
|
+
addresses?: string[];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface LookupResult {
|
|
87
|
+
username: string;
|
|
88
|
+
addresses: string[];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface LookupResponse {
|
|
92
|
+
results: LookupResult[];
|
|
93
|
+
}
|
|
94
|
+
|
|
91
95
|
type ContractAddress = string;
|
|
92
96
|
type CartridgeID = string;
|
|
93
97
|
export type ControllerAccounts = Record<ContractAddress, CartridgeID>;
|
|
@@ -95,7 +99,7 @@ export type ControllerAccounts = Record<ContractAddress, CartridgeID>;
|
|
|
95
99
|
export interface Keychain {
|
|
96
100
|
probe(rpcUrl: string): Promise<ProbeReply | ConnectError>;
|
|
97
101
|
connect(
|
|
98
|
-
policies:
|
|
102
|
+
policies: Policies,
|
|
99
103
|
rpcUrl: string,
|
|
100
104
|
): Promise<ConnectReply | ConnectError>;
|
|
101
105
|
disconnect(): void;
|
|
@@ -160,7 +164,7 @@ export type ProviderOptions = {
|
|
|
160
164
|
};
|
|
161
165
|
|
|
162
166
|
export type KeychainOptions = IFrameOptions & {
|
|
163
|
-
policies?:
|
|
167
|
+
policies?: Policies;
|
|
164
168
|
/** The URL of keychain */
|
|
165
169
|
url?: string;
|
|
166
170
|
/** The origin of keychain */
|
|
@@ -186,31 +190,6 @@ export type ProfileContextTypeVariant =
|
|
|
186
190
|
| "achievements"
|
|
187
191
|
| "activity";
|
|
188
192
|
|
|
189
|
-
export type ColorMode = "light" | "dark";
|
|
190
|
-
|
|
191
|
-
export type ControllerTheme = {
|
|
192
|
-
id: string;
|
|
193
|
-
name: string;
|
|
194
|
-
icon: string;
|
|
195
|
-
cover: ThemeValue<string>;
|
|
196
|
-
colorMode: ColorMode;
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
export type ControllerThemePresets = Record<string, ControllerThemePreset>;
|
|
200
|
-
|
|
201
|
-
export type ControllerThemePreset = Omit<ControllerTheme, "colorMode"> & {
|
|
202
|
-
colors?: ControllerColors;
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
export type ControllerColors = {
|
|
206
|
-
primary?: ControllerColor;
|
|
207
|
-
primaryForeground?: ControllerColor;
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
export type ControllerColor = ThemeValue<string>;
|
|
211
|
-
|
|
212
|
-
export type ThemeValue<T> = T | { dark: T; light: T };
|
|
213
|
-
|
|
214
193
|
export type Prefund = { address: string; min: string };
|
|
215
194
|
|
|
216
195
|
export type Tokens = {
|
package/src/utils.ts
CHANGED
|
@@ -2,15 +2,40 @@ import {
|
|
|
2
2
|
addAddressPadding,
|
|
3
3
|
Call,
|
|
4
4
|
CallData,
|
|
5
|
+
getChecksumAddress,
|
|
5
6
|
hash,
|
|
6
7
|
typedData,
|
|
7
8
|
TypedDataRevision,
|
|
8
9
|
} from "starknet";
|
|
9
|
-
import { Policy } from "./types";
|
|
10
10
|
import wasm from "@cartridge/account-wasm/controller";
|
|
11
|
+
import { Policies, SessionPolicies } from "@cartridge/presets";
|
|
12
|
+
|
|
13
|
+
// Whitelist of allowed property names to prevent prototype pollution
|
|
14
|
+
const ALLOWED_PROPERTIES = new Set([
|
|
15
|
+
"contracts",
|
|
16
|
+
"messages",
|
|
17
|
+
"target",
|
|
18
|
+
"method",
|
|
19
|
+
"name",
|
|
20
|
+
"description",
|
|
21
|
+
"types",
|
|
22
|
+
"domain",
|
|
23
|
+
"primaryType",
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
function validatePropertyName(prop: string): void {
|
|
27
|
+
if (!ALLOWED_PROPERTIES.has(prop)) {
|
|
28
|
+
throw new Error(`Invalid property name: ${prop}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function safeObjectAccess<T>(obj: any, prop: string): T {
|
|
33
|
+
validatePropertyName(prop);
|
|
34
|
+
return obj[prop];
|
|
35
|
+
}
|
|
11
36
|
|
|
12
37
|
export function normalizeCalls(calls: Call | Call[]) {
|
|
13
|
-
return (
|
|
38
|
+
return toArray(calls).map((call) => {
|
|
14
39
|
return {
|
|
15
40
|
entrypoint: call.entrypoint,
|
|
16
41
|
contractAddress: addAddressPadding(call.contractAddress),
|
|
@@ -19,29 +44,88 @@ export function normalizeCalls(calls: Call | Call[]) {
|
|
|
19
44
|
});
|
|
20
45
|
}
|
|
21
46
|
|
|
22
|
-
export function
|
|
23
|
-
return
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
47
|
+
export function toSessionPolicies(policies: Policies): SessionPolicies {
|
|
48
|
+
return Array.isArray(policies)
|
|
49
|
+
? policies.reduce<SessionPolicies>(
|
|
50
|
+
(prev, p) => {
|
|
51
|
+
if (safeObjectAccess<string>(p, "target")) {
|
|
52
|
+
const target = getChecksumAddress(
|
|
53
|
+
safeObjectAccess<string>(p, "target"),
|
|
54
|
+
);
|
|
55
|
+
const entrypoint = safeObjectAccess<string>(p, "method");
|
|
56
|
+
const contracts = safeObjectAccess<Record<string, any>>(
|
|
57
|
+
prev,
|
|
58
|
+
"contracts",
|
|
59
|
+
);
|
|
60
|
+
const item = {
|
|
61
|
+
name: humanizeString(entrypoint),
|
|
62
|
+
entrypoint: entrypoint,
|
|
63
|
+
description: safeObjectAccess<string>(p, "description"),
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
if (target in contracts) {
|
|
67
|
+
const methods = toArray(contracts[target].methods);
|
|
68
|
+
contracts[target] = {
|
|
69
|
+
methods: [...methods, item],
|
|
70
|
+
};
|
|
71
|
+
} else {
|
|
72
|
+
contracts[target] = {
|
|
73
|
+
methods: [item],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
const messages = safeObjectAccess<any[]>(prev, "messages");
|
|
78
|
+
messages.push(p);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return prev;
|
|
82
|
+
},
|
|
83
|
+
{ contracts: {}, messages: [] },
|
|
84
|
+
)
|
|
85
|
+
: policies;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function toWasmPolicies(policies: SessionPolicies): wasm.Policy[] {
|
|
89
|
+
return [
|
|
90
|
+
...Object.entries(policies.contracts ?? {}).flatMap(
|
|
91
|
+
([target, { methods }]) =>
|
|
92
|
+
toArray(methods).map((m) => ({
|
|
93
|
+
target,
|
|
94
|
+
method: m.entrypoint,
|
|
95
|
+
})),
|
|
96
|
+
),
|
|
97
|
+
...(policies.messages ?? []).map((p) => {
|
|
30
98
|
const domainHash = typedData.getStructHash(
|
|
31
|
-
|
|
99
|
+
p.types,
|
|
32
100
|
"StarknetDomain",
|
|
33
|
-
|
|
101
|
+
p.domain,
|
|
34
102
|
TypedDataRevision.ACTIVE,
|
|
35
103
|
);
|
|
36
104
|
const typeHash = typedData.getTypeHash(
|
|
37
|
-
|
|
38
|
-
|
|
105
|
+
p.types,
|
|
106
|
+
p.primaryType,
|
|
39
107
|
TypedDataRevision.ACTIVE,
|
|
40
108
|
);
|
|
41
109
|
|
|
42
110
|
return {
|
|
43
111
|
scope_hash: hash.computePoseidonHash(domainHash, typeHash),
|
|
44
112
|
};
|
|
45
|
-
}
|
|
46
|
-
|
|
113
|
+
}),
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function toArray<T>(val: T | T[]): T[] {
|
|
118
|
+
return Array.isArray(val) ? val : [val];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function humanizeString(str: string): string {
|
|
122
|
+
return (
|
|
123
|
+
str
|
|
124
|
+
// Convert from camelCase or snake_case
|
|
125
|
+
.replace(/([a-z])([A-Z])/g, "$1 $2") // camelCase to spaces
|
|
126
|
+
.replace(/_/g, " ") // snake_case to spaces
|
|
127
|
+
.toLowerCase()
|
|
128
|
+
// Capitalize first letter
|
|
129
|
+
.replace(/^\w/, (c) => c.toUpperCase())
|
|
130
|
+
);
|
|
47
131
|
}
|