@cartridge/controller 0.3.45 → 0.4.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/src/presets.ts ADDED
@@ -0,0 +1,140 @@
1
+ import { ControllerThemePresets } from "./types";
2
+
3
+ export const defaultPresets: ControllerThemePresets = {
4
+ cartridge: {
5
+ id: "cartridge",
6
+ name: "Cartridge",
7
+ icon: "/whitelabel/cartridge/icon.svg",
8
+ cover: {
9
+ light: "/whitelabel/cartridge/cover-light.png",
10
+ dark: "/whitelabel/cartridge/cover-dark.png",
11
+ },
12
+ },
13
+ "force-prime": {
14
+ id: "force-prime",
15
+ name: "Force Prime",
16
+ icon: "/whitelabel/force-prime/icon.png",
17
+ cover: "/whitelabel/force-prime/cover.png",
18
+ colors: {
19
+ primary: "#E1CC89",
20
+ },
21
+ },
22
+ paved: {
23
+ id: "paved",
24
+ name: "Paved",
25
+ icon: "/whitelabel/paved/icon.svg",
26
+ cover: "/whitelabel/paved/cover.png",
27
+ colors: {
28
+ primary: "#B0CAF8",
29
+ },
30
+ },
31
+ pistols: {
32
+ id: "pistols",
33
+ name: "Pistols at Ten Blocks",
34
+ icon: "/whitelabel/pistols/icon.png",
35
+ cover: "/whitelabel/pistols/cover.png",
36
+ colors: {
37
+ primary: "#EF9758",
38
+ },
39
+ },
40
+ pixelaw: {
41
+ id: "pixelaw",
42
+ name: "Pixelaw",
43
+ icon: "/whitelabel/pixelaw/icon.svg",
44
+ cover: "/whitelabel/pixelaw/cover.png",
45
+ colors: {
46
+ primary: "#7C00B1",
47
+ primaryForeground: "white",
48
+ },
49
+ },
50
+ "dope-wars": {
51
+ id: "dope-wars",
52
+ name: "Dope Wars",
53
+ icon: "/whitelabel/dope-wars/icon.png",
54
+ cover: "/whitelabel/dope-wars/cover.png",
55
+ colors: {
56
+ primary: "#11ED83",
57
+ },
58
+ },
59
+ zkastle: {
60
+ id: "zkastle",
61
+ name: "zKastle",
62
+ icon: "/whitelabel/zkastle/icon.svg",
63
+ cover: "/whitelabel/zkastle/cover.png",
64
+ colors: {
65
+ primary: "#E50D2C",
66
+ },
67
+ },
68
+ "loot-survivor": {
69
+ id: "loot-survivor",
70
+ name: "Loot Survivor",
71
+ icon: "/whitelabel/loot-survivor/icon.png",
72
+ cover: "/whitelabel/loot-survivor/cover.png",
73
+ colors: {
74
+ primary: "#33FF33",
75
+ },
76
+ },
77
+ "tale-weaver": {
78
+ id: "tale-weaver",
79
+ name: "Tale Weaver",
80
+ icon: "/whitelabel/tale-weaver/icon.png",
81
+ cover: "/whitelabel/tale-weaver/cover.png",
82
+ colors: {
83
+ primary: "#fce377",
84
+ },
85
+ },
86
+ "realm-of-ra": {
87
+ id: "realm-of-ra",
88
+ name: "Realm of Ra",
89
+ icon: "/whitelabel/realm-of-ra/icon.png",
90
+ cover: "/whitelabel/realm-of-ra/cover.png",
91
+ colors: {
92
+ primary: "#de9534",
93
+ },
94
+ },
95
+ "jokers-of-neon": {
96
+ id: "jokers-of-neon",
97
+ name: "Jokers of Neon",
98
+ icon: "/whitelabel/jokers-of-neon/icon.png",
99
+ cover: "/whitelabel/jokers-of-neon/cover.png",
100
+ colors: {
101
+ primary: "#A144B2",
102
+ },
103
+ },
104
+ flippyflop: {
105
+ id: "flippyflop",
106
+ name: "FlippyFlop",
107
+ icon: "/whitelabel/flippyflop/icon.png",
108
+ cover: "/whitelabel/flippyflop/cover.png",
109
+ colors: {
110
+ primary: "#F38332",
111
+ },
112
+ },
113
+ "savage-summit": {
114
+ id: "savage-summit",
115
+ name: "Savage Summit",
116
+ icon: "/whitelabel/savage-summit/icon.png",
117
+ cover: "/whitelabel/savage-summit/cover.png",
118
+ colors: {
119
+ primary: "#fbf7da",
120
+ },
121
+ },
122
+ "dark-shuffle": {
123
+ id: "dark-shuffle",
124
+ name: "Dark Shuffle",
125
+ icon: "/whitelabel/dark-shuffle/icon.svg",
126
+ cover: "/whitelabel/dark-shuffle/cover.png",
127
+ colors: {
128
+ primary: "#F59100",
129
+ },
130
+ },
131
+ "blob-arena": {
132
+ id: "blob-arena",
133
+ name: "Blob Arena",
134
+ icon: "/whitelabel/blob-arena/icon.png",
135
+ cover: "/whitelabel/blob-arena/cover.png",
136
+ colors: {
137
+ primary: "#980f06",
138
+ },
139
+ },
140
+ };
package/src/session.ts ADDED
@@ -0,0 +1,92 @@
1
+ import { Policy } from "@cartridge/account-wasm";
2
+ import { CartridgeSessionAccount } from "@cartridge/account-wasm/session";
3
+ import {
4
+ Abi,
5
+ Account,
6
+ Call,
7
+ InvokeFunctionResponse,
8
+ RpcProvider,
9
+ Signature,
10
+ TypedData,
11
+ UniversalDetails,
12
+ } from "starknet";
13
+ import { SessionSigner } from "./signer";
14
+ import { normalizeCalls } from "./utils";
15
+
16
+ export * from "./errors";
17
+ export * from "./types";
18
+ export { defaultPresets } from "./presets";
19
+
20
+ export default class SessionAccount extends Account {
21
+ public controller: CartridgeSessionAccount;
22
+
23
+ constructor({
24
+ rpcUrl,
25
+ privateKey,
26
+ address,
27
+ ownerGuid,
28
+ chainId,
29
+ expiresAt,
30
+ policies,
31
+ }: {
32
+ rpcUrl: string;
33
+ privateKey: string;
34
+ address: string;
35
+ ownerGuid: string;
36
+ chainId: string;
37
+ expiresAt: number;
38
+ policies: Policy[];
39
+ }) {
40
+ const controller = CartridgeSessionAccount.new_as_registered(
41
+ rpcUrl,
42
+ privateKey,
43
+ address,
44
+ ownerGuid,
45
+ chainId,
46
+ {
47
+ expiresAt,
48
+ policies,
49
+ },
50
+ );
51
+
52
+ super(
53
+ new RpcProvider({ nodeUrl: rpcUrl }),
54
+ address,
55
+ new SessionSigner(controller),
56
+ );
57
+
58
+ this.controller = controller;
59
+ }
60
+
61
+ /**
62
+ * Invoke execute function in account contract
63
+ *
64
+ * @param calls the invocation object or an array of them, containing:
65
+ * - contractAddress - the address of the contract
66
+ * - entrypoint - the entrypoint of the contract
67
+ * - calldata - (defaults to []) the calldata
68
+ * - signature - (defaults to []) the signature
69
+ * @param abis (optional) the abi of the contract for better displaying
70
+ *
71
+ * @returns response from addTransaction
72
+ */
73
+ async execute(
74
+ calls: Call | Call[],
75
+ _abisOrDetails?: Abi[] | UniversalDetails,
76
+ _transactionsDetail?: UniversalDetails,
77
+ ): Promise<InvokeFunctionResponse> {
78
+ return this.controller.execute(normalizeCalls(calls));
79
+ }
80
+
81
+ /**
82
+ * Sign an JSON object for off-chain usage with the starknet private key and return the signature
83
+ * This adds a message prefix so it cant be interchanged with transactions
84
+ *
85
+ * @param json - JSON object to be signed
86
+ * @returns the signature of the JSON object
87
+ * @throws {Error} if the JSON object is not a valid JSON
88
+ */
89
+ async signMessage(_typedData: TypedData): Promise<Signature> {
90
+ throw new Error("signMessage not implemented for SessionSigner");
91
+ }
92
+ }
package/src/signer.ts ADDED
@@ -0,0 +1,144 @@
1
+ import {
2
+ Abi,
3
+ BigNumberish,
4
+ Call,
5
+ DeclareSignerDetails,
6
+ DeployAccountSignerDetails,
7
+ InvocationsSignerDetails,
8
+ Signature,
9
+ SignerInterface,
10
+ TypedData,
11
+ } from "starknet";
12
+
13
+ import { Keychain, Modal } from "./types";
14
+ import { AsyncMethodReturns } from "@cartridge/penpal";
15
+ import { CartridgeSessionAccount } from "@cartridge/account-wasm/session";
16
+ import { normalizeCalls } from "./utils";
17
+
18
+ export class Signer implements SignerInterface {
19
+ private keychain: AsyncMethodReturns<Keychain>;
20
+ modal: Modal;
21
+ constructor(keychain: AsyncMethodReturns<Keychain>, modal: Modal) {
22
+ this.keychain = keychain;
23
+ this.modal = modal;
24
+ }
25
+
26
+ /**
27
+ * Method to get the public key of the signer
28
+ *
29
+ * @returns public key of signer as hex string with 0x prefix
30
+ */
31
+ public getPubKey(): Promise<string> {
32
+ return Promise.resolve("");
33
+ }
34
+
35
+ /**
36
+ * Sign an JSON object for off-chain usage with the starknet private key and return the signature
37
+ * This adds a message prefix so it cant be interchanged with transactions
38
+ *
39
+ * @param typedData - JSON object to be signed
40
+ * @param accountAddress - account
41
+ * @returns the signature of the JSON object
42
+ * @throws {Error} if the JSON object is not a valid JSON
43
+ */
44
+ public async signMessage(
45
+ typedData: TypedData,
46
+ account: string,
47
+ ): Promise<Signature> {
48
+ this.modal.open();
49
+ const res = await this.keychain.signMessage(typedData, account);
50
+ this.modal.close();
51
+ return res as Signature;
52
+ }
53
+
54
+ public async signTransaction(
55
+ transactions: Call[],
56
+ transactionsDetail: InvocationsSignerDetails,
57
+ abis?: Abi[],
58
+ ): Promise<Signature> {
59
+ this.modal.open();
60
+ const res = await this.keychain.signTransaction(
61
+ transactions,
62
+ transactionsDetail,
63
+ abis,
64
+ );
65
+ this.modal.close();
66
+ return res;
67
+ }
68
+
69
+ public async signDeployAccountTransaction(
70
+ transaction: DeployAccountSignerDetails,
71
+ ): Promise<Signature> {
72
+ this.modal.open();
73
+ const res = await this.keychain.signDeployAccountTransaction(transaction);
74
+ this.modal.close();
75
+ return res;
76
+ }
77
+
78
+ public async signDeclareTransaction(
79
+ transaction: DeclareSignerDetails,
80
+ ): Promise<Signature> {
81
+ this.modal.open();
82
+ const res = await this.keychain.signDeclareTransaction(transaction);
83
+ this.modal.close();
84
+ return res;
85
+ }
86
+ }
87
+
88
+ export class SessionSigner implements SignerInterface {
89
+ controller: CartridgeSessionAccount;
90
+
91
+ constructor(controller: CartridgeSessionAccount) {
92
+ this.controller = controller;
93
+ }
94
+
95
+ /**
96
+ * Method to get the public key of the signer
97
+ *
98
+ * @returns public key of signer as hex string with 0x prefix
99
+ */
100
+ public getPubKey(): Promise<string> {
101
+ return Promise.resolve("");
102
+ }
103
+
104
+ /**
105
+ * Sign an JSON object for off-chain usage with the starknet private key and return the signature
106
+ * This adds a message prefix so it cant be interchanged with transactions
107
+ *
108
+ * @param typedData - JSON object to be signed
109
+ * @param accountAddress - account
110
+ * @returns the signature of the JSON object
111
+ * @throws {Error} if the JSON object is not a valid JSON
112
+ */
113
+ public async signMessage(
114
+ _typedData: TypedData,
115
+ _account: string,
116
+ ): Promise<Signature> {
117
+ throw new Error("signMessage not implemented for SessionSigner");
118
+ }
119
+
120
+ public async signTransaction(
121
+ transactions: Call[],
122
+ transactionsDetail: InvocationsSignerDetails & { maxFee: BigNumberish },
123
+ _abis?: Abi[],
124
+ ): Promise<Signature> {
125
+ return this.controller.sign_transaction(
126
+ normalizeCalls(transactions),
127
+ transactionsDetail.maxFee,
128
+ );
129
+ }
130
+
131
+ public async signDeployAccountTransaction(
132
+ _transaction: DeployAccountSignerDetails,
133
+ ): Promise<Signature> {
134
+ throw new Error(
135
+ "signDeployAccountTransaction not implemented for SessionSigner",
136
+ );
137
+ }
138
+
139
+ public async signDeclareTransaction(
140
+ _transaction: DeclareSignerDetails,
141
+ ): Promise<Signature> {
142
+ throw new Error("signDeclareTransaction not implemented for SessionSigner");
143
+ }
144
+ }
package/src/types.ts ADDED
@@ -0,0 +1,246 @@
1
+ import {
2
+ constants,
3
+ Abi,
4
+ Call,
5
+ InvocationsDetails,
6
+ TypedData,
7
+ InvokeFunctionResponse,
8
+ Signature,
9
+ EstimateFeeDetails,
10
+ EstimateFee,
11
+ DeclareContractPayload,
12
+ BigNumberish,
13
+ InvocationsSignerDetails,
14
+ DeployAccountSignerDetails,
15
+ DeclareSignerDetails,
16
+ } from "starknet";
17
+ import { KeychainIFrame, ProfileIFrame } from "./iframe";
18
+ import wasm from "@cartridge/account-wasm/controller";
19
+
20
+ export type Session = {
21
+ chainId: constants.StarknetChainId;
22
+ policies: Policy[];
23
+ maxFee: BigNumberish;
24
+ expiresAt: bigint;
25
+ credentials: {
26
+ authorization: string[];
27
+ privateKey: string;
28
+ };
29
+ };
30
+
31
+ export type Policy = wasm.Policy & {
32
+ description?: string;
33
+ };
34
+
35
+ export enum ResponseCodes {
36
+ SUCCESS = "SUCCESS",
37
+ NOT_CONNECTED = "NOT_CONNECTED",
38
+ ERROR = "ERROR",
39
+ CANCELED = "CANCELED",
40
+ USER_INTERACTION_REQUIRED = "USER_INTERACTION_REQUIRED",
41
+ }
42
+
43
+ export type ConnectError = {
44
+ code: ResponseCodes;
45
+ message: string;
46
+ error?: ControllerError;
47
+ };
48
+
49
+ export type ControllerError = {
50
+ code: Number;
51
+ message: string;
52
+ data?: any;
53
+ };
54
+
55
+ export type ConnectReply = {
56
+ code: ResponseCodes.SUCCESS;
57
+ address: string;
58
+ policies: Policy[];
59
+ };
60
+
61
+ export type ExecuteReply =
62
+ | (InvokeFunctionResponse & {
63
+ code: ResponseCodes.SUCCESS;
64
+ })
65
+ | {
66
+ code: ResponseCodes.USER_INTERACTION_REQUIRED;
67
+ };
68
+
69
+ export type ProbeReply = {
70
+ code: ResponseCodes.SUCCESS;
71
+ address: string;
72
+ };
73
+
74
+ export type DeployReply = {
75
+ code: ResponseCodes.SUCCESS;
76
+ transaction_hash: string;
77
+ };
78
+
79
+ export type IFrames = {
80
+ keychain: KeychainIFrame;
81
+ profile?: ProfileIFrame;
82
+ };
83
+
84
+ type ContractAddress = string;
85
+ type CartridgeID = string;
86
+ export type ControllerAccounts = Record<ContractAddress, CartridgeID>;
87
+
88
+ export interface Keychain {
89
+ probe(rpcUrl: string): Promise<ProbeReply | ConnectError>;
90
+ connect(
91
+ policies: Policy[],
92
+ rpcUrl: string,
93
+ ): Promise<ConnectReply | ConnectError>;
94
+ disconnect(): void;
95
+
96
+ reset(): void;
97
+ revoke(origin: string): void;
98
+
99
+ deploy(): Promise<DeployReply | ConnectError>;
100
+ estimateDeclareFee(
101
+ payload: DeclareContractPayload,
102
+ details?: EstimateFeeDetails,
103
+ ): Promise<EstimateFee>;
104
+ estimateInvokeFee(
105
+ calls: Call | Call[],
106
+ estimateFeeDetails?: EstimateFeeDetails,
107
+ ): Promise<EstimateFee>;
108
+ execute(
109
+ calls: Call | Call[],
110
+ abis?: Abi[],
111
+ transactionsDetail?: InvocationsDetails,
112
+ sync?: boolean,
113
+ paymaster?: PaymasterOptions,
114
+ error?: ControllerError,
115
+ ): Promise<ExecuteReply | ConnectError>;
116
+ logout(): Promise<void>;
117
+ openSettings(): Promise<void | ConnectError>;
118
+ session(): Promise<Session>;
119
+ sessions(): Promise<{
120
+ [key: string]: Session;
121
+ }>;
122
+ signMessage(
123
+ typedData: TypedData,
124
+ account: string,
125
+ ): Promise<Signature | ConnectError>;
126
+ signTransaction(
127
+ transactions: Call[],
128
+ transactionsDetail: InvocationsSignerDetails,
129
+ abis?: Abi[],
130
+ ): Promise<Signature>;
131
+ signDeployAccountTransaction(
132
+ transaction: DeployAccountSignerDetails,
133
+ ): Promise<Signature>;
134
+ signDeclareTransaction(transaction: DeclareSignerDetails): Promise<Signature>;
135
+ delegateAccount(): string;
136
+ username(): string;
137
+ fetchControllers(contractAddresses: string[]): Promise<ControllerAccounts>;
138
+ }
139
+
140
+ export interface Profile {
141
+ navigate(tab: ProfileContextTypeVariant): void;
142
+ }
143
+
144
+ export interface Modal {
145
+ open: () => void;
146
+ close: () => void;
147
+ }
148
+
149
+ /**
150
+ * Options for configuring the controller
151
+ */
152
+ export type ControllerOptions = KeychainOptions & ProfileOptions;
153
+
154
+ export type TokenOptions = {
155
+ tokens: Tokens;
156
+ };
157
+
158
+ export type IFrameOptions = {
159
+ /** The ID of the starter pack to use */
160
+ starterPackId?: string;
161
+ /** The theme to use */
162
+ theme?: string;
163
+ /** The color mode to use */
164
+ colorMode?: ColorMode;
165
+ /** Additional configuration options */
166
+ config?: {
167
+ /** Preset themes for the controller */
168
+ presets?: ControllerThemePresets;
169
+ };
170
+ };
171
+
172
+ export type KeychainOptions = IFrameOptions & {
173
+ policies?: Policy[];
174
+ /** The URL of keychain */
175
+ url?: string;
176
+ /** The URL of the RPC */
177
+ rpc?: string;
178
+ /** The origin of keychain */
179
+ origin?: string;
180
+ /** Paymaster options for transaction fee management */
181
+ paymaster?: PaymasterOptions;
182
+ /** Propagate transaction errors back to caller instead of showing modal */
183
+ propagateSessionErrors?: boolean;
184
+ };
185
+
186
+ export type ProfileOptions = IFrameOptions & {
187
+ /** The URL of profile. Mainly for internal development purpose */
188
+ profileUrl?: string;
189
+ /** The URL of Torii indexer. Will be mandatory once profile page is in production */
190
+ indexerUrl?: string;
191
+ /** The tokens to be listed on Inventory modal */
192
+ tokens?: Tokens;
193
+ };
194
+
195
+ export type ProfileContextTypeVariant = "quest" | "inventory" | "history";
196
+
197
+ /**
198
+ * Options for configuring a paymaster
199
+ */
200
+ export type PaymasterOptions = {
201
+ /**
202
+ * The address of the account paying for the transaction.
203
+ * This should be a valid Starknet address or "ANY_CALLER" short string.
204
+ */
205
+ caller: string;
206
+ /**
207
+ * The URL of the paymaster. Currently not used.
208
+ */
209
+ url?: string;
210
+ };
211
+
212
+ export type ColorMode = "light" | "dark";
213
+
214
+ export type ControllerTheme = {
215
+ id: string;
216
+ name: string;
217
+ icon: string;
218
+ cover: ThemeValue<string>;
219
+ colorMode: ColorMode;
220
+ };
221
+
222
+ export type ControllerThemePresets = Record<string, ControllerThemePreset>;
223
+
224
+ export type ControllerThemePreset = Omit<ControllerTheme, "colorMode"> & {
225
+ colors?: ControllerColors;
226
+ };
227
+
228
+ export type ControllerColors = {
229
+ primary?: ControllerColor;
230
+ primaryForeground?: ControllerColor;
231
+ };
232
+
233
+ export type ControllerColor = ThemeValue<string>;
234
+
235
+ export type ThemeValue<T> = T | { dark: T; light: T };
236
+
237
+ export type Prefund = { address: string; min: string };
238
+
239
+ export type Tokens = {
240
+ erc20?: ERC20[];
241
+ };
242
+
243
+ export type ERC20 = {
244
+ address: string;
245
+ logoUrl?: string;
246
+ };
package/src/utils.ts ADDED
@@ -0,0 +1,21 @@
1
+ import equal from "fast-deep-equal";
2
+ import { Policy } from "./types";
3
+ import { addAddressPadding, Call, CallData } from "starknet";
4
+
5
+ export function diff(a: Policy[], b: Policy[]): Policy[] {
6
+ return a.reduce(
7
+ (prev, policyA) =>
8
+ b.some((policyB) => equal(policyB, policyA)) ? prev : [...prev, policyA],
9
+ [] as Policy[],
10
+ );
11
+ }
12
+
13
+ export function normalizeCalls(calls: Call | Call[]) {
14
+ return (Array.isArray(calls) ? calls : [calls]).map((call) => {
15
+ return {
16
+ entrypoint: call.entrypoint,
17
+ contractAddress: addAddressPadding(call.contractAddress),
18
+ calldata: CallData.toHex(call.calldata),
19
+ };
20
+ });
21
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "extends": "@cartridge/tsconfig/base.json",
4
+ "compilerOptions": {
5
+ "baseUrl": ".",
6
+ "rootDir": "./src",
7
+ "outDir": "./dist"
8
+ },
9
+ "include": ["src/**/*"]
10
+ }