New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@cartridge/controller

Package Overview
Dependencies
Maintainers
0
Versions
129
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@cartridge/controller - npm Package Compare versions

Comparing version 0.5.9 to 0.6.0

dist/mutex.d.ts

4

dist/account.js

@@ -23,2 +23,6 @@ // src/account.ts

var ControllerAccount = class extends WalletAccount {
address;
keychain;
modal;
options;
constructor(provider, rpcUrl, address, keychain, options, modal) {

@@ -25,0 +29,0 @@ super({ nodeUrl: rpcUrl }, provider);

286

dist/controller.js

@@ -45,2 +45,6 @@ // src/account.ts

var ControllerAccount = class extends WalletAccount {
address;
keychain;
modal;
options;
constructor(provider, rpcUrl, address, keychain, options, modal) {

@@ -130,2 +134,6 @@ super({ nodeUrl: rpcUrl }, provider);

var IFrame = class {
url;
iframe;
container;
onClose;
constructor({

@@ -323,3 +331,3 @@ id,

name: "@cartridge/controller",
version: "0.5.9",
version: "0.6.0",
description: "Cartridge Controller",

@@ -338,6 +346,18 @@ module: "dist/index.js",

exports: {
".": "./dist/index.js",
"./session": "./dist/session/index.js",
"./provider": "./dist/provider/index.js",
"./types": "./dist/types/index.js"
".": {
types: "./dist/index.d.ts",
default: "./dist/index.js"
},
"./session": {
types: "./dist/session/index.d.ts",
default: "./dist/session/index.js"
},
"./provider": {
types: "./dist/provider/index.d.ts",
default: "./dist/provider/index.js"
},
"./types": {
types: "./dist/types/index.d.ts",
default: "./dist/types/index.js"
}
},

@@ -355,2 +375,5 @@ tsup: {

},
peerDependencies: {
starknet: "^6.21.0"
},
dependencies: {

@@ -364,4 +387,3 @@ "@cartridge/account-wasm": "workspace:*",

"fast-deep-equal": "^3.1.3",
"query-string": "^7.1.1",
starknet: "^6.11.0"
"query-string": "^7.1.1"
},

@@ -381,123 +403,163 @@ devDependencies: {

// src/mutex.ts
function releaseStub() {
}
var Mutex = class {
m_lastPromise = Promise.resolve();
/**
* Acquire lock
* @param [bypass=false] option to skip lock acquisition
*/
async obtain(bypass = false) {
let release = releaseStub;
if (bypass) return release;
const lastPromise = this.m_lastPromise;
this.m_lastPromise = new Promise((resolve) => release = resolve);
await lastPromise;
return release;
}
};
// src/provider.ts
var mutex = new Mutex();
var BaseProvider = class {
constructor() {
this.id = "controller";
this.name = "Controller";
this.version = package_default.version;
this.icon = icon;
this.subscriptions = [];
this.request = async (call) => {
switch (call.type) {
case "wallet_getPermissions":
await this.probe();
if (this.account) {
return [Permission.ACCOUNTS];
}
return [];
case "wallet_requestAccounts": {
if (this.account) {
return [this.account.address];
}
const silentMode = call.params && call.params.silent_mode;
this.account = await this.probe();
if (!this.account && !silentMode) {
this.account = await this.connect();
}
if (this.account) {
return [this.account.address];
}
return [];
id = "controller";
name = "Controller";
version = package_default.version;
icon = icon;
account;
subscriptions = [];
_probePromise = null;
async safeProbe() {
if (this.account) {
return this.account;
}
if (this._probePromise) {
return this._probePromise;
}
const release = await mutex.obtain();
return await new Promise(async (resolve) => {
try {
this._probePromise = this.probe();
const result = await this._probePromise;
resolve(result);
} finally {
this._probePromise = null;
}
}).finally(() => {
release();
});
}
request = async (call) => {
switch (call.type) {
case "wallet_getPermissions":
await this.safeProbe();
if (this.account) {
return [Permission.ACCOUNTS];
}
case "wallet_watchAsset":
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_watchAsset not implemented"
};
case "wallet_addStarknetChain": {
let params2 = call.params;
return this.addStarknetChain(params2);
return [];
case "wallet_requestAccounts": {
if (this.account) {
return [this.account.address];
}
case "wallet_switchStarknetChain": {
let params2 = call.params;
return this.switchStarknetChain(params2.chainId);
const silentMode = call.params && call.params.silent_mode;
this.account = await this.safeProbe();
if (!this.account && !silentMode) {
this.account = await this.connect();
}
case "wallet_requestChainId":
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "Account not initialized"
};
}
return await this.account.getChainId();
case "wallet_deploymentData":
if (this.account) {
return [this.account.address];
}
return [];
}
case "wallet_watchAsset":
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_watchAsset not implemented"
};
case "wallet_addStarknetChain": {
let params2 = call.params;
return this.addStarknetChain(params2);
}
case "wallet_switchStarknetChain": {
let params2 = call.params;
return this.switchStarknetChain(params2.chainId);
}
case "wallet_requestChainId":
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_deploymentData not implemented"
data: "Account not initialized"
};
case "wallet_addInvokeTransaction":
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "Account not initialized"
};
}
let params = call.params;
return await this.account.execute(
params.calls.map((call2) => ({
contractAddress: call2.contract_address,
entrypoint: call2.entry_point,
calldata: call2.calldata
}))
);
case "wallet_addDeclareTransaction":
}
return await this.account.getChainId();
case "wallet_deploymentData":
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_deploymentData not implemented"
};
case "wallet_addInvokeTransaction":
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_addDeclareTransaction not implemented"
data: "Account not initialized"
};
case "wallet_signTypedData": {
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "Account not initialized"
};
}
return await this.account.signMessage(call.params);
}
case "wallet_supportedSpecs":
return [];
case "wallet_supportedWalletApi":
return [];
default:
let params = call.params;
return await this.account.execute(
params.calls.map((call2) => ({
contractAddress: call2.contract_address,
entrypoint: call2.entry_point,
calldata: call2.calldata
}))
);
case "wallet_addDeclareTransaction":
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_addDeclareTransaction not implemented"
};
case "wallet_signTypedData": {
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: `Unknown RPC call type: ${call.type}`
data: "Account not initialized"
};
}
return await this.account.signMessage(call.params);
}
};
this.on = (event, handler) => {
if (event !== "accountsChanged" && event !== "networkChanged") {
throw new Error(`Unknown event: ${event}`);
}
this.subscriptions.push({ type: event, handler });
};
this.off = (event, handler) => {
if (event !== "accountsChanged" && event !== "networkChanged") {
throw new Error(`Unknown event: ${event}`);
}
const idx = this.subscriptions.findIndex(
(sub) => sub.type === event && sub.handler === handler
);
if (idx >= 0) {
this.subscriptions.splice(idx, 1);
}
};
}
case "wallet_supportedSpecs":
return [];
case "wallet_supportedWalletApi":
return [];
default:
throw {
code: 63,
message: "An unexpected error occurred",
data: `Unknown RPC call type: ${call.type}`
};
}
};
on = (event, handler) => {
if (event !== "accountsChanged" && event !== "networkChanged") {
throw new Error(`Unknown event: ${event}`);
}
this.subscriptions.push({ type: event, handler });
};
off = (event, handler) => {
if (event !== "accountsChanged" && event !== "networkChanged") {
throw new Error(`Unknown event: ${event}`);
}
const idx = this.subscriptions.findIndex(
(sub) => sub.type === event && sub.handler === handler
);
if (idx >= 0) {
this.subscriptions.splice(idx, 1);
}
};
emitNetworkChanged(chainId) {

@@ -517,2 +579,8 @@ this.subscriptions.filter((sub) => sub.type === "networkChanged").forEach((sub) => {

var ControllerProvider = class extends BaseProvider {
keychain;
profile;
options;
iframes;
selectedChain;
chains;
constructor(options) {

@@ -519,0 +587,0 @@ super();

// src/iframe/base.ts
import { connectToChild } from "@cartridge/penpal";
var IFrame = class {
url;
iframe;
container;
onClose;
constructor({

@@ -5,0 +9,0 @@ id,

// src/iframe/base.ts
import { connectToChild } from "@cartridge/penpal";
var IFrame = class {
url;
iframe;
container;
onClose;
constructor({

@@ -5,0 +9,0 @@ id,

@@ -7,2 +7,6 @@ // src/constants.ts

var IFrame = class {
url;
iframe;
container;
onClose;
constructor({

@@ -9,0 +13,0 @@ id,

@@ -7,2 +7,6 @@ // src/constants.ts

var IFrame = class {
url;
iframe;
container;
onClose;
constructor({

@@ -9,0 +13,0 @@ id,

@@ -12,1 +12,2 @@ export { default } from './controller.js';

import '@cartridge/account-wasm/controller';
import './policies.js';

@@ -11,2 +11,4 @@ import { WalletAccount } from 'starknet';

subscriptions: WalletEvents[];
private _probePromise;
protected safeProbe(): Promise<WalletAccount | undefined>;
request: RequestFn;

@@ -13,0 +15,0 @@ on: WalletEventListener;

@@ -9,3 +9,3 @@ // src/provider.ts

name: "@cartridge/controller",
version: "0.5.9",
version: "0.6.0",
description: "Cartridge Controller",

@@ -24,6 +24,18 @@ module: "dist/index.js",

exports: {
".": "./dist/index.js",
"./session": "./dist/session/index.js",
"./provider": "./dist/provider/index.js",
"./types": "./dist/types/index.js"
".": {
types: "./dist/index.d.ts",
default: "./dist/index.js"
},
"./session": {
types: "./dist/session/index.d.ts",
default: "./dist/session/index.js"
},
"./provider": {
types: "./dist/provider/index.d.ts",
default: "./dist/provider/index.js"
},
"./types": {
types: "./dist/types/index.d.ts",
default: "./dist/types/index.js"
}
},

@@ -41,2 +53,5 @@ tsup: {

},
peerDependencies: {
starknet: "^6.21.0"
},
dependencies: {

@@ -50,4 +65,3 @@ "@cartridge/account-wasm": "workspace:*",

"fast-deep-equal": "^3.1.3",
"query-string": "^7.1.1",
starknet: "^6.11.0"
"query-string": "^7.1.1"
},

@@ -67,123 +81,163 @@ devDependencies: {

// src/mutex.ts
function releaseStub() {
}
var Mutex = class {
m_lastPromise = Promise.resolve();
/**
* Acquire lock
* @param [bypass=false] option to skip lock acquisition
*/
async obtain(bypass = false) {
let release = releaseStub;
if (bypass) return release;
const lastPromise = this.m_lastPromise;
this.m_lastPromise = new Promise((resolve) => release = resolve);
await lastPromise;
return release;
}
};
// src/provider.ts
var mutex = new Mutex();
var BaseProvider = class {
constructor() {
this.id = "controller";
this.name = "Controller";
this.version = package_default.version;
this.icon = icon;
this.subscriptions = [];
this.request = async (call) => {
switch (call.type) {
case "wallet_getPermissions":
await this.probe();
if (this.account) {
return [Permission.ACCOUNTS];
}
return [];
case "wallet_requestAccounts": {
if (this.account) {
return [this.account.address];
}
const silentMode = call.params && call.params.silent_mode;
this.account = await this.probe();
if (!this.account && !silentMode) {
this.account = await this.connect();
}
if (this.account) {
return [this.account.address];
}
return [];
id = "controller";
name = "Controller";
version = package_default.version;
icon = icon;
account;
subscriptions = [];
_probePromise = null;
async safeProbe() {
if (this.account) {
return this.account;
}
if (this._probePromise) {
return this._probePromise;
}
const release = await mutex.obtain();
return await new Promise(async (resolve) => {
try {
this._probePromise = this.probe();
const result = await this._probePromise;
resolve(result);
} finally {
this._probePromise = null;
}
}).finally(() => {
release();
});
}
request = async (call) => {
switch (call.type) {
case "wallet_getPermissions":
await this.safeProbe();
if (this.account) {
return [Permission.ACCOUNTS];
}
case "wallet_watchAsset":
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_watchAsset not implemented"
};
case "wallet_addStarknetChain": {
let params2 = call.params;
return this.addStarknetChain(params2);
return [];
case "wallet_requestAccounts": {
if (this.account) {
return [this.account.address];
}
case "wallet_switchStarknetChain": {
let params2 = call.params;
return this.switchStarknetChain(params2.chainId);
const silentMode = call.params && call.params.silent_mode;
this.account = await this.safeProbe();
if (!this.account && !silentMode) {
this.account = await this.connect();
}
case "wallet_requestChainId":
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "Account not initialized"
};
}
return await this.account.getChainId();
case "wallet_deploymentData":
if (this.account) {
return [this.account.address];
}
return [];
}
case "wallet_watchAsset":
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_watchAsset not implemented"
};
case "wallet_addStarknetChain": {
let params2 = call.params;
return this.addStarknetChain(params2);
}
case "wallet_switchStarknetChain": {
let params2 = call.params;
return this.switchStarknetChain(params2.chainId);
}
case "wallet_requestChainId":
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_deploymentData not implemented"
data: "Account not initialized"
};
case "wallet_addInvokeTransaction":
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "Account not initialized"
};
}
let params = call.params;
return await this.account.execute(
params.calls.map((call2) => ({
contractAddress: call2.contract_address,
entrypoint: call2.entry_point,
calldata: call2.calldata
}))
);
case "wallet_addDeclareTransaction":
}
return await this.account.getChainId();
case "wallet_deploymentData":
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_deploymentData not implemented"
};
case "wallet_addInvokeTransaction":
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_addDeclareTransaction not implemented"
data: "Account not initialized"
};
case "wallet_signTypedData": {
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "Account not initialized"
};
}
return await this.account.signMessage(call.params);
}
case "wallet_supportedSpecs":
return [];
case "wallet_supportedWalletApi":
return [];
default:
let params = call.params;
return await this.account.execute(
params.calls.map((call2) => ({
contractAddress: call2.contract_address,
entrypoint: call2.entry_point,
calldata: call2.calldata
}))
);
case "wallet_addDeclareTransaction":
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_addDeclareTransaction not implemented"
};
case "wallet_signTypedData": {
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: `Unknown RPC call type: ${call.type}`
data: "Account not initialized"
};
}
return await this.account.signMessage(call.params);
}
};
this.on = (event, handler) => {
if (event !== "accountsChanged" && event !== "networkChanged") {
throw new Error(`Unknown event: ${event}`);
}
this.subscriptions.push({ type: event, handler });
};
this.off = (event, handler) => {
if (event !== "accountsChanged" && event !== "networkChanged") {
throw new Error(`Unknown event: ${event}`);
}
const idx = this.subscriptions.findIndex(
(sub) => sub.type === event && sub.handler === handler
);
if (idx >= 0) {
this.subscriptions.splice(idx, 1);
}
};
}
case "wallet_supportedSpecs":
return [];
case "wallet_supportedWalletApi":
return [];
default:
throw {
code: 63,
message: "An unexpected error occurred",
data: `Unknown RPC call type: ${call.type}`
};
}
};
on = (event, handler) => {
if (event !== "accountsChanged" && event !== "networkChanged") {
throw new Error(`Unknown event: ${event}`);
}
this.subscriptions.push({ type: event, handler });
};
off = (event, handler) => {
if (event !== "accountsChanged" && event !== "networkChanged") {
throw new Error(`Unknown event: ${event}`);
}
const idx = this.subscriptions.findIndex(
(sub) => sub.type === event && sub.handler === handler
);
if (idx >= 0) {
this.subscriptions.splice(idx, 1);
}
};
emitNetworkChanged(chainId) {

@@ -190,0 +244,0 @@ this.subscriptions.filter((sub) => sub.type === "networkChanged").forEach((sub) => {

@@ -49,2 +49,3 @@ // src/session/account.ts

var SessionAccount = class extends WalletAccount {
controller;
constructor(provider, {

@@ -60,2 +61,3 @@ rpcUrl,

super({ nodeUrl: rpcUrl }, provider);
this.address = address;
this.controller = CartridgeSessionAccount.new_as_registered(

@@ -62,0 +64,0 @@ rpcUrl,

@@ -8,2 +8,3 @@ export { SessionOptions, default } from './provider.js';

import '../index.d-BbTUPBeO.js';
import '../policies.js';
import '@cartridge/penpal';

@@ -33,3 +33,4 @@ // src/session/provider.ts

target,
method: m.entrypoint
method: m.entrypoint,
authorized: m.authorized
}))

@@ -50,3 +51,4 @@ ),

return {
scope_hash: hash.computePoseidonHash(domainHash, typeHash)
scope_hash: hash.computePoseidonHash(domainHash, typeHash),
authorized: p.authorized
};

@@ -80,2 +82,3 @@ })

var SessionAccount = class extends WalletAccount {
controller;
constructor(provider, {

@@ -91,2 +94,3 @@ rpcUrl,

super({ nodeUrl: rpcUrl }, provider);
this.address = address;
this.controller = CartridgeSessionAccount.new_as_registered(

@@ -132,3 +136,3 @@ rpcUrl,

name: "@cartridge/controller",
version: "0.5.9",
version: "0.6.0",
description: "Cartridge Controller",

@@ -147,6 +151,18 @@ module: "dist/index.js",

exports: {
".": "./dist/index.js",
"./session": "./dist/session/index.js",
"./provider": "./dist/provider/index.js",
"./types": "./dist/types/index.js"
".": {
types: "./dist/index.d.ts",
default: "./dist/index.js"
},
"./session": {
types: "./dist/session/index.d.ts",
default: "./dist/session/index.js"
},
"./provider": {
types: "./dist/provider/index.d.ts",
default: "./dist/provider/index.js"
},
"./types": {
types: "./dist/types/index.d.ts",
default: "./dist/types/index.js"
}
},

@@ -164,2 +180,5 @@ tsup: {

},
peerDependencies: {
starknet: "^6.21.0"
},
dependencies: {

@@ -173,4 +192,3 @@ "@cartridge/account-wasm": "workspace:*",

"fast-deep-equal": "^3.1.3",
"query-string": "^7.1.1",
starknet: "^6.11.0"
"query-string": "^7.1.1"
},

@@ -190,123 +208,163 @@ devDependencies: {

// src/mutex.ts
function releaseStub() {
}
var Mutex = class {
m_lastPromise = Promise.resolve();
/**
* Acquire lock
* @param [bypass=false] option to skip lock acquisition
*/
async obtain(bypass = false) {
let release = releaseStub;
if (bypass) return release;
const lastPromise = this.m_lastPromise;
this.m_lastPromise = new Promise((resolve) => release = resolve);
await lastPromise;
return release;
}
};
// src/provider.ts
var mutex = new Mutex();
var BaseProvider = class {
constructor() {
this.id = "controller";
this.name = "Controller";
this.version = package_default.version;
this.icon = icon;
this.subscriptions = [];
this.request = async (call) => {
switch (call.type) {
case "wallet_getPermissions":
await this.probe();
if (this.account) {
return [Permission.ACCOUNTS];
}
return [];
case "wallet_requestAccounts": {
if (this.account) {
return [this.account.address];
}
const silentMode = call.params && call.params.silent_mode;
this.account = await this.probe();
if (!this.account && !silentMode) {
this.account = await this.connect();
}
if (this.account) {
return [this.account.address];
}
return [];
id = "controller";
name = "Controller";
version = package_default.version;
icon = icon;
account;
subscriptions = [];
_probePromise = null;
async safeProbe() {
if (this.account) {
return this.account;
}
if (this._probePromise) {
return this._probePromise;
}
const release = await mutex.obtain();
return await new Promise(async (resolve) => {
try {
this._probePromise = this.probe();
const result = await this._probePromise;
resolve(result);
} finally {
this._probePromise = null;
}
}).finally(() => {
release();
});
}
request = async (call) => {
switch (call.type) {
case "wallet_getPermissions":
await this.safeProbe();
if (this.account) {
return [Permission.ACCOUNTS];
}
case "wallet_watchAsset":
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_watchAsset not implemented"
};
case "wallet_addStarknetChain": {
let params2 = call.params;
return this.addStarknetChain(params2);
return [];
case "wallet_requestAccounts": {
if (this.account) {
return [this.account.address];
}
case "wallet_switchStarknetChain": {
let params2 = call.params;
return this.switchStarknetChain(params2.chainId);
const silentMode = call.params && call.params.silent_mode;
this.account = await this.safeProbe();
if (!this.account && !silentMode) {
this.account = await this.connect();
}
case "wallet_requestChainId":
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "Account not initialized"
};
}
return await this.account.getChainId();
case "wallet_deploymentData":
if (this.account) {
return [this.account.address];
}
return [];
}
case "wallet_watchAsset":
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_watchAsset not implemented"
};
case "wallet_addStarknetChain": {
let params2 = call.params;
return this.addStarknetChain(params2);
}
case "wallet_switchStarknetChain": {
let params2 = call.params;
return this.switchStarknetChain(params2.chainId);
}
case "wallet_requestChainId":
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_deploymentData not implemented"
data: "Account not initialized"
};
case "wallet_addInvokeTransaction":
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "Account not initialized"
};
}
let params = call.params;
return await this.account.execute(
params.calls.map((call2) => ({
contractAddress: call2.contract_address,
entrypoint: call2.entry_point,
calldata: call2.calldata
}))
);
case "wallet_addDeclareTransaction":
}
return await this.account.getChainId();
case "wallet_deploymentData":
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_deploymentData not implemented"
};
case "wallet_addInvokeTransaction":
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_addDeclareTransaction not implemented"
data: "Account not initialized"
};
case "wallet_signTypedData": {
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "Account not initialized"
};
}
return await this.account.signMessage(call.params);
}
case "wallet_supportedSpecs":
return [];
case "wallet_supportedWalletApi":
return [];
default:
let params = call.params;
return await this.account.execute(
params.calls.map((call2) => ({
contractAddress: call2.contract_address,
entrypoint: call2.entry_point,
calldata: call2.calldata
}))
);
case "wallet_addDeclareTransaction":
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_addDeclareTransaction not implemented"
};
case "wallet_signTypedData": {
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: `Unknown RPC call type: ${call.type}`
data: "Account not initialized"
};
}
return await this.account.signMessage(call.params);
}
};
this.on = (event, handler) => {
if (event !== "accountsChanged" && event !== "networkChanged") {
throw new Error(`Unknown event: ${event}`);
}
this.subscriptions.push({ type: event, handler });
};
this.off = (event, handler) => {
if (event !== "accountsChanged" && event !== "networkChanged") {
throw new Error(`Unknown event: ${event}`);
}
const idx = this.subscriptions.findIndex(
(sub) => sub.type === event && sub.handler === handler
);
if (idx >= 0) {
this.subscriptions.splice(idx, 1);
}
};
}
case "wallet_supportedSpecs":
return [];
case "wallet_supportedWalletApi":
return [];
default:
throw {
code: 63,
message: "An unexpected error occurred",
data: `Unknown RPC call type: ${call.type}`
};
}
};
on = (event, handler) => {
if (event !== "accountsChanged" && event !== "networkChanged") {
throw new Error(`Unknown event: ${event}`);
}
this.subscriptions.push({ type: event, handler });
};
off = (event, handler) => {
if (event !== "accountsChanged" && event !== "networkChanged") {
throw new Error(`Unknown event: ${event}`);
}
const idx = this.subscriptions.findIndex(
(sub) => sub.type === event && sub.handler === handler
);
if (idx >= 0) {
this.subscriptions.splice(idx, 1);
}
};
emitNetworkChanged(chainId) {

@@ -326,10 +384,41 @@ this.subscriptions.filter((sub) => sub.type === "networkChanged").forEach((sub) => {

var SessionProvider = class extends BaseProvider {
constructor({ rpc, chainId, policies, redirectUrl }) {
id = "controller_session";
name = "Controller Session";
_chainId;
_rpcUrl;
_username;
_redirectUrl;
_policies;
_keychainUrl;
constructor({
rpc,
chainId,
policies,
redirectUrl,
keychainUrl
}) {
super();
this.id = "controller_session";
this.name = "Controller Session";
this._policies = {
verified: false,
contracts: policies.contracts ? Object.fromEntries(
Object.entries(policies.contracts).map(([address, contract]) => [
address,
{
...contract,
methods: contract.methods.map((method) => ({
...method,
authorized: true
}))
}
])
) : void 0,
messages: policies.messages?.map((message) => ({
...message,
authorized: true
}))
};
this._rpcUrl = rpc;
this._chainId = chainId;
this._redirectUrl = redirectUrl;
this._policies = policies;
this._keychainUrl = keychainUrl || KEYCHAIN_URL;
if (typeof window !== "undefined") {

@@ -339,2 +428,27 @@ window.starknet_controller_session = this;

}
validatePoliciesSubset(newPolicies, existingPolicies) {
if (newPolicies.contracts) {
if (!existingPolicies.contracts) return false;
for (const [address, contract] of Object.entries(newPolicies.contracts)) {
const existingContract = existingPolicies.contracts[address];
if (!existingContract) return false;
for (const method of contract.methods) {
const existingMethod = existingContract.methods.find(
(m) => m.entrypoint === method.entrypoint
);
if (!existingMethod || !existingMethod.authorized) return false;
}
}
}
if (newPolicies.messages) {
if (!existingPolicies.messages) return false;
for (const message of newPolicies.messages) {
const existingMessage = existingPolicies.messages.find(
(m) => JSON.stringify(m.domain) === JSON.stringify(message.domain) && JSON.stringify(m.types) === JSON.stringify(message.types)
);
if (!existingMessage || !existingMessage.authorized) return false;
}
}
return true;
}
async username() {

@@ -345,10 +459,16 @@ await this.tryRetrieveFromQueryOrStorage();

async probe() {
await this.tryRetrieveFromQueryOrStorage();
return;
if (this.account) {
return this.account;
}
this.account = await this.tryRetrieveFromQueryOrStorage();
return this.account;
}
async connect() {
await this.tryRetrieveFromQueryOrStorage();
if (this.account) {
return;
return this.account;
}
this.account = await this.tryRetrieveFromQueryOrStorage();
if (this.account) {
return this.account;
}
const pk = stark.randomAddress();

@@ -363,3 +483,4 @@ const publicKey = ec.starkCurve.getStarkKey(pk);

);
const url = `${KEYCHAIN_URL}/session?public_key=${publicKey}&redirect_uri=${this._redirectUrl}&redirect_query_name=startapp&policies=${JSON.stringify(
localStorage.setItem("sessionPolicies", JSON.stringify(this._policies));
const url = `${this._keychainUrl}/session?public_key=${publicKey}&redirect_uri=${this._redirectUrl}&redirect_query_name=startapp&policies=${JSON.stringify(
this._policies

@@ -369,3 +490,3 @@ )}&rpc_url=${this._rpcUrl}`;

window.open(url, "_blank");
return;
return this.account;
}

@@ -381,2 +502,3 @@ switchStarknetChain(_chainId) {

localStorage.removeItem("session");
localStorage.removeItem("sessionPolicies");
this.account = void 0;

@@ -387,2 +509,5 @@ this._username = void 0;

async tryRetrieveFromQueryOrStorage() {
if (this.account) {
return this.account;
}
const signerString = localStorage.getItem("sessionSigner");

@@ -411,2 +536,37 @@ const signer = signerString ? JSON.parse(signerString) : null;

}
const expirationTime = parseInt(sessionRegistration.expiresAt) * 1e3;
console.log("Session expiration check:", {
expirationTime,
currentTime: Date.now(),
expired: Date.now() >= expirationTime
});
if (Date.now() >= expirationTime) {
console.log("Session expired, clearing stored session");
this.clearStoredSession();
return;
}
const storedPoliciesStr = localStorage.getItem("sessionPolicies");
console.log("Checking stored policies:", {
storedPoliciesStr,
currentPolicies: this._policies
});
if (storedPoliciesStr) {
const storedPolicies = JSON.parse(
storedPoliciesStr
);
const isValid = this.validatePoliciesSubset(
this._policies,
storedPolicies
);
console.log("Policy validation result:", {
isValid,
storedPolicies,
requestedPolicies: this._policies
});
if (!isValid) {
console.log("Policy validation failed, clearing stored session");
this.clearStoredSession();
return;
}
}
this._username = sessionRegistration.username;

@@ -424,2 +584,7 @@ this.account = new SessionAccount(this, {

}
clearStoredSession() {
localStorage.removeItem("sessionSigner");
localStorage.removeItem("session");
localStorage.removeItem("sessionPolicies");
}
};

@@ -426,0 +591,0 @@ export {

@@ -5,2 +5,3 @@ import { WalletAccount } from 'starknet';

import { AddStarknetChainParameters } from '@starknet-io/types-js';
import { ParsedSessionPolicies } from '../policies.js';

@@ -12,2 +13,3 @@ type SessionOptions = {

redirectUrl: string;
keychainUrl?: string;
};

@@ -21,4 +23,6 @@ declare class SessionProvider extends BaseProvider {

protected _redirectUrl: string;
protected _policies: SessionPolicies;
constructor({ rpc, chainId, policies, redirectUrl }: SessionOptions);
protected _policies: ParsedSessionPolicies;
protected _keychainUrl: string;
constructor({ rpc, chainId, policies, redirectUrl, keychainUrl, }: SessionOptions);
private validatePoliciesSubset;
username(): Promise<string | undefined>;

@@ -31,4 +35,5 @@ probe(): Promise<WalletAccount | undefined>;

tryRetrieveFromQueryOrStorage(): Promise<WalletAccount | undefined>;
private clearStoredSession;
}
export { type SessionOptions, SessionProvider as default };

@@ -33,3 +33,4 @@ // src/session/provider.ts

target,
method: m.entrypoint
method: m.entrypoint,
authorized: m.authorized
}))

@@ -50,3 +51,4 @@ ),

return {
scope_hash: hash.computePoseidonHash(domainHash, typeHash)
scope_hash: hash.computePoseidonHash(domainHash, typeHash),
authorized: p.authorized
};

@@ -62,2 +64,3 @@ })

var SessionAccount = class extends WalletAccount {
controller;
constructor(provider, {

@@ -73,2 +76,3 @@ rpcUrl,

super({ nodeUrl: rpcUrl }, provider);
this.address = address;
this.controller = CartridgeSessionAccount.new_as_registered(

@@ -114,3 +118,3 @@ rpcUrl,

name: "@cartridge/controller",
version: "0.5.9",
version: "0.6.0",
description: "Cartridge Controller",

@@ -129,6 +133,18 @@ module: "dist/index.js",

exports: {
".": "./dist/index.js",
"./session": "./dist/session/index.js",
"./provider": "./dist/provider/index.js",
"./types": "./dist/types/index.js"
".": {
types: "./dist/index.d.ts",
default: "./dist/index.js"
},
"./session": {
types: "./dist/session/index.d.ts",
default: "./dist/session/index.js"
},
"./provider": {
types: "./dist/provider/index.d.ts",
default: "./dist/provider/index.js"
},
"./types": {
types: "./dist/types/index.d.ts",
default: "./dist/types/index.js"
}
},

@@ -146,2 +162,5 @@ tsup: {

},
peerDependencies: {
starknet: "^6.21.0"
},
dependencies: {

@@ -155,4 +174,3 @@ "@cartridge/account-wasm": "workspace:*",

"fast-deep-equal": "^3.1.3",
"query-string": "^7.1.1",
starknet: "^6.11.0"
"query-string": "^7.1.1"
},

@@ -172,123 +190,163 @@ devDependencies: {

// src/mutex.ts
function releaseStub() {
}
var Mutex = class {
m_lastPromise = Promise.resolve();
/**
* Acquire lock
* @param [bypass=false] option to skip lock acquisition
*/
async obtain(bypass = false) {
let release = releaseStub;
if (bypass) return release;
const lastPromise = this.m_lastPromise;
this.m_lastPromise = new Promise((resolve) => release = resolve);
await lastPromise;
return release;
}
};
// src/provider.ts
var mutex = new Mutex();
var BaseProvider = class {
constructor() {
this.id = "controller";
this.name = "Controller";
this.version = package_default.version;
this.icon = icon;
this.subscriptions = [];
this.request = async (call) => {
switch (call.type) {
case "wallet_getPermissions":
await this.probe();
if (this.account) {
return [Permission.ACCOUNTS];
}
return [];
case "wallet_requestAccounts": {
if (this.account) {
return [this.account.address];
}
const silentMode = call.params && call.params.silent_mode;
this.account = await this.probe();
if (!this.account && !silentMode) {
this.account = await this.connect();
}
if (this.account) {
return [this.account.address];
}
return [];
id = "controller";
name = "Controller";
version = package_default.version;
icon = icon;
account;
subscriptions = [];
_probePromise = null;
async safeProbe() {
if (this.account) {
return this.account;
}
if (this._probePromise) {
return this._probePromise;
}
const release = await mutex.obtain();
return await new Promise(async (resolve) => {
try {
this._probePromise = this.probe();
const result = await this._probePromise;
resolve(result);
} finally {
this._probePromise = null;
}
}).finally(() => {
release();
});
}
request = async (call) => {
switch (call.type) {
case "wallet_getPermissions":
await this.safeProbe();
if (this.account) {
return [Permission.ACCOUNTS];
}
case "wallet_watchAsset":
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_watchAsset not implemented"
};
case "wallet_addStarknetChain": {
let params2 = call.params;
return this.addStarknetChain(params2);
return [];
case "wallet_requestAccounts": {
if (this.account) {
return [this.account.address];
}
case "wallet_switchStarknetChain": {
let params2 = call.params;
return this.switchStarknetChain(params2.chainId);
const silentMode = call.params && call.params.silent_mode;
this.account = await this.safeProbe();
if (!this.account && !silentMode) {
this.account = await this.connect();
}
case "wallet_requestChainId":
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "Account not initialized"
};
}
return await this.account.getChainId();
case "wallet_deploymentData":
if (this.account) {
return [this.account.address];
}
return [];
}
case "wallet_watchAsset":
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_watchAsset not implemented"
};
case "wallet_addStarknetChain": {
let params2 = call.params;
return this.addStarknetChain(params2);
}
case "wallet_switchStarknetChain": {
let params2 = call.params;
return this.switchStarknetChain(params2.chainId);
}
case "wallet_requestChainId":
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_deploymentData not implemented"
data: "Account not initialized"
};
case "wallet_addInvokeTransaction":
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "Account not initialized"
};
}
let params = call.params;
return await this.account.execute(
params.calls.map((call2) => ({
contractAddress: call2.contract_address,
entrypoint: call2.entry_point,
calldata: call2.calldata
}))
);
case "wallet_addDeclareTransaction":
}
return await this.account.getChainId();
case "wallet_deploymentData":
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_deploymentData not implemented"
};
case "wallet_addInvokeTransaction":
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_addDeclareTransaction not implemented"
data: "Account not initialized"
};
case "wallet_signTypedData": {
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "Account not initialized"
};
}
return await this.account.signMessage(call.params);
}
case "wallet_supportedSpecs":
return [];
case "wallet_supportedWalletApi":
return [];
default:
let params = call.params;
return await this.account.execute(
params.calls.map((call2) => ({
contractAddress: call2.contract_address,
entrypoint: call2.entry_point,
calldata: call2.calldata
}))
);
case "wallet_addDeclareTransaction":
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_addDeclareTransaction not implemented"
};
case "wallet_signTypedData": {
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: `Unknown RPC call type: ${call.type}`
data: "Account not initialized"
};
}
return await this.account.signMessage(call.params);
}
};
this.on = (event, handler) => {
if (event !== "accountsChanged" && event !== "networkChanged") {
throw new Error(`Unknown event: ${event}`);
}
this.subscriptions.push({ type: event, handler });
};
this.off = (event, handler) => {
if (event !== "accountsChanged" && event !== "networkChanged") {
throw new Error(`Unknown event: ${event}`);
}
const idx = this.subscriptions.findIndex(
(sub) => sub.type === event && sub.handler === handler
);
if (idx >= 0) {
this.subscriptions.splice(idx, 1);
}
};
}
case "wallet_supportedSpecs":
return [];
case "wallet_supportedWalletApi":
return [];
default:
throw {
code: 63,
message: "An unexpected error occurred",
data: `Unknown RPC call type: ${call.type}`
};
}
};
on = (event, handler) => {
if (event !== "accountsChanged" && event !== "networkChanged") {
throw new Error(`Unknown event: ${event}`);
}
this.subscriptions.push({ type: event, handler });
};
off = (event, handler) => {
if (event !== "accountsChanged" && event !== "networkChanged") {
throw new Error(`Unknown event: ${event}`);
}
const idx = this.subscriptions.findIndex(
(sub) => sub.type === event && sub.handler === handler
);
if (idx >= 0) {
this.subscriptions.splice(idx, 1);
}
};
emitNetworkChanged(chainId) {

@@ -308,10 +366,41 @@ this.subscriptions.filter((sub) => sub.type === "networkChanged").forEach((sub) => {

var SessionProvider = class extends BaseProvider {
constructor({ rpc, chainId, policies, redirectUrl }) {
id = "controller_session";
name = "Controller Session";
_chainId;
_rpcUrl;
_username;
_redirectUrl;
_policies;
_keychainUrl;
constructor({
rpc,
chainId,
policies,
redirectUrl,
keychainUrl
}) {
super();
this.id = "controller_session";
this.name = "Controller Session";
this._policies = {
verified: false,
contracts: policies.contracts ? Object.fromEntries(
Object.entries(policies.contracts).map(([address, contract]) => [
address,
{
...contract,
methods: contract.methods.map((method) => ({
...method,
authorized: true
}))
}
])
) : void 0,
messages: policies.messages?.map((message) => ({
...message,
authorized: true
}))
};
this._rpcUrl = rpc;
this._chainId = chainId;
this._redirectUrl = redirectUrl;
this._policies = policies;
this._keychainUrl = keychainUrl || KEYCHAIN_URL;
if (typeof window !== "undefined") {

@@ -321,2 +410,27 @@ window.starknet_controller_session = this;

}
validatePoliciesSubset(newPolicies, existingPolicies) {
if (newPolicies.contracts) {
if (!existingPolicies.contracts) return false;
for (const [address, contract] of Object.entries(newPolicies.contracts)) {
const existingContract = existingPolicies.contracts[address];
if (!existingContract) return false;
for (const method of contract.methods) {
const existingMethod = existingContract.methods.find(
(m) => m.entrypoint === method.entrypoint
);
if (!existingMethod || !existingMethod.authorized) return false;
}
}
}
if (newPolicies.messages) {
if (!existingPolicies.messages) return false;
for (const message of newPolicies.messages) {
const existingMessage = existingPolicies.messages.find(
(m) => JSON.stringify(m.domain) === JSON.stringify(message.domain) && JSON.stringify(m.types) === JSON.stringify(message.types)
);
if (!existingMessage || !existingMessage.authorized) return false;
}
}
return true;
}
async username() {

@@ -327,10 +441,16 @@ await this.tryRetrieveFromQueryOrStorage();

async probe() {
await this.tryRetrieveFromQueryOrStorage();
return;
if (this.account) {
return this.account;
}
this.account = await this.tryRetrieveFromQueryOrStorage();
return this.account;
}
async connect() {
await this.tryRetrieveFromQueryOrStorage();
if (this.account) {
return;
return this.account;
}
this.account = await this.tryRetrieveFromQueryOrStorage();
if (this.account) {
return this.account;
}
const pk = stark.randomAddress();

@@ -345,3 +465,4 @@ const publicKey = ec.starkCurve.getStarkKey(pk);

);
const url = `${KEYCHAIN_URL}/session?public_key=${publicKey}&redirect_uri=${this._redirectUrl}&redirect_query_name=startapp&policies=${JSON.stringify(
localStorage.setItem("sessionPolicies", JSON.stringify(this._policies));
const url = `${this._keychainUrl}/session?public_key=${publicKey}&redirect_uri=${this._redirectUrl}&redirect_query_name=startapp&policies=${JSON.stringify(
this._policies

@@ -351,3 +472,3 @@ )}&rpc_url=${this._rpcUrl}`;

window.open(url, "_blank");
return;
return this.account;
}

@@ -363,2 +484,3 @@ switchStarknetChain(_chainId) {

localStorage.removeItem("session");
localStorage.removeItem("sessionPolicies");
this.account = void 0;

@@ -369,2 +491,5 @@ this._username = void 0;

async tryRetrieveFromQueryOrStorage() {
if (this.account) {
return this.account;
}
const signerString = localStorage.getItem("sessionSigner");

@@ -393,2 +518,37 @@ const signer = signerString ? JSON.parse(signerString) : null;

}
const expirationTime = parseInt(sessionRegistration.expiresAt) * 1e3;
console.log("Session expiration check:", {
expirationTime,
currentTime: Date.now(),
expired: Date.now() >= expirationTime
});
if (Date.now() >= expirationTime) {
console.log("Session expired, clearing stored session");
this.clearStoredSession();
return;
}
const storedPoliciesStr = localStorage.getItem("sessionPolicies");
console.log("Checking stored policies:", {
storedPoliciesStr,
currentPolicies: this._policies
});
if (storedPoliciesStr) {
const storedPolicies = JSON.parse(
storedPoliciesStr
);
const isValid = this.validatePoliciesSubset(
this._policies,
storedPolicies
);
console.log("Policy validation result:", {
isValid,
storedPolicies,
requestedPolicies: this._policies
});
if (!isValid) {
console.log("Policy validation failed, clearing stored session");
this.clearStoredSession();
return;
}
}
this._username = sessionRegistration.username;

@@ -406,2 +566,7 @@ this.account = new SessionAccount(this, {

}
clearStoredSession() {
localStorage.removeItem("sessionSigner");
localStorage.removeItem("session");
localStorage.removeItem("sessionPolicies");
}
};

@@ -408,0 +573,0 @@ export {

@@ -5,2 +5,3 @@ import { WalletAccount } from 'starknet';

import { AddStarknetChainParameters } from '@starknet-io/types-js';
import { ParsedSessionPolicies } from '../policies.js';

@@ -11,3 +12,3 @@ declare class TelegramProvider extends BaseProvider {

protected _username?: string;
protected _policies: SessionPolicies;
protected _policies: ParsedSessionPolicies;
private _rpcUrl;

@@ -14,0 +15,0 @@ constructor({ rpc, chainId, policies, tmaUrl, }: {

@@ -42,3 +42,4 @@ // src/telegram/provider.ts

target,
method: m.entrypoint
method: m.entrypoint,
authorized: m.authorized
}))

@@ -59,3 +60,4 @@ ),

return {
scope_hash: hash.computePoseidonHash(domainHash, typeHash)
scope_hash: hash.computePoseidonHash(domainHash, typeHash),
authorized: p.authorized
};

@@ -71,2 +73,3 @@ })

var SessionAccount = class extends WalletAccount {
controller;
constructor(provider, {

@@ -82,2 +85,3 @@ rpcUrl,

super({ nodeUrl: rpcUrl }, provider);
this.address = address;
this.controller = CartridgeSessionAccount.new_as_registered(

@@ -120,3 +124,3 @@ rpcUrl,

name: "@cartridge/controller",
version: "0.5.9",
version: "0.6.0",
description: "Cartridge Controller",

@@ -135,6 +139,18 @@ module: "dist/index.js",

exports: {
".": "./dist/index.js",
"./session": "./dist/session/index.js",
"./provider": "./dist/provider/index.js",
"./types": "./dist/types/index.js"
".": {
types: "./dist/index.d.ts",
default: "./dist/index.js"
},
"./session": {
types: "./dist/session/index.d.ts",
default: "./dist/session/index.js"
},
"./provider": {
types: "./dist/provider/index.d.ts",
default: "./dist/provider/index.js"
},
"./types": {
types: "./dist/types/index.d.ts",
default: "./dist/types/index.js"
}
},

@@ -152,2 +168,5 @@ tsup: {

},
peerDependencies: {
starknet: "^6.21.0"
},
dependencies: {

@@ -161,4 +180,3 @@ "@cartridge/account-wasm": "workspace:*",

"fast-deep-equal": "^3.1.3",
"query-string": "^7.1.1",
starknet: "^6.11.0"
"query-string": "^7.1.1"
},

@@ -178,123 +196,163 @@ devDependencies: {

// src/mutex.ts
function releaseStub() {
}
var Mutex = class {
m_lastPromise = Promise.resolve();
/**
* Acquire lock
* @param [bypass=false] option to skip lock acquisition
*/
async obtain(bypass = false) {
let release = releaseStub;
if (bypass) return release;
const lastPromise = this.m_lastPromise;
this.m_lastPromise = new Promise((resolve) => release = resolve);
await lastPromise;
return release;
}
};
// src/provider.ts
var mutex = new Mutex();
var BaseProvider = class {
constructor() {
this.id = "controller";
this.name = "Controller";
this.version = package_default.version;
this.icon = icon;
this.subscriptions = [];
this.request = async (call) => {
switch (call.type) {
case "wallet_getPermissions":
await this.probe();
if (this.account) {
return [Permission.ACCOUNTS];
}
return [];
case "wallet_requestAccounts": {
if (this.account) {
return [this.account.address];
}
const silentMode = call.params && call.params.silent_mode;
this.account = await this.probe();
if (!this.account && !silentMode) {
this.account = await this.connect();
}
if (this.account) {
return [this.account.address];
}
return [];
id = "controller";
name = "Controller";
version = package_default.version;
icon = icon;
account;
subscriptions = [];
_probePromise = null;
async safeProbe() {
if (this.account) {
return this.account;
}
if (this._probePromise) {
return this._probePromise;
}
const release = await mutex.obtain();
return await new Promise(async (resolve) => {
try {
this._probePromise = this.probe();
const result = await this._probePromise;
resolve(result);
} finally {
this._probePromise = null;
}
}).finally(() => {
release();
});
}
request = async (call) => {
switch (call.type) {
case "wallet_getPermissions":
await this.safeProbe();
if (this.account) {
return [Permission.ACCOUNTS];
}
case "wallet_watchAsset":
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_watchAsset not implemented"
};
case "wallet_addStarknetChain": {
let params2 = call.params;
return this.addStarknetChain(params2);
return [];
case "wallet_requestAccounts": {
if (this.account) {
return [this.account.address];
}
case "wallet_switchStarknetChain": {
let params2 = call.params;
return this.switchStarknetChain(params2.chainId);
const silentMode = call.params && call.params.silent_mode;
this.account = await this.safeProbe();
if (!this.account && !silentMode) {
this.account = await this.connect();
}
case "wallet_requestChainId":
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "Account not initialized"
};
}
return await this.account.getChainId();
case "wallet_deploymentData":
if (this.account) {
return [this.account.address];
}
return [];
}
case "wallet_watchAsset":
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_watchAsset not implemented"
};
case "wallet_addStarknetChain": {
let params2 = call.params;
return this.addStarknetChain(params2);
}
case "wallet_switchStarknetChain": {
let params2 = call.params;
return this.switchStarknetChain(params2.chainId);
}
case "wallet_requestChainId":
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_deploymentData not implemented"
data: "Account not initialized"
};
case "wallet_addInvokeTransaction":
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "Account not initialized"
};
}
let params = call.params;
return await this.account.execute(
params.calls.map((call2) => ({
contractAddress: call2.contract_address,
entrypoint: call2.entry_point,
calldata: call2.calldata
}))
);
case "wallet_addDeclareTransaction":
}
return await this.account.getChainId();
case "wallet_deploymentData":
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_deploymentData not implemented"
};
case "wallet_addInvokeTransaction":
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_addDeclareTransaction not implemented"
data: "Account not initialized"
};
case "wallet_signTypedData": {
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: "Account not initialized"
};
}
return await this.account.signMessage(call.params);
}
case "wallet_supportedSpecs":
return [];
case "wallet_supportedWalletApi":
return [];
default:
let params = call.params;
return await this.account.execute(
params.calls.map((call2) => ({
contractAddress: call2.contract_address,
entrypoint: call2.entry_point,
calldata: call2.calldata
}))
);
case "wallet_addDeclareTransaction":
throw {
code: 63,
message: "An unexpected error occurred",
data: "wallet_addDeclareTransaction not implemented"
};
case "wallet_signTypedData": {
if (!this.account) {
throw {
code: 63,
message: "An unexpected error occurred",
data: `Unknown RPC call type: ${call.type}`
data: "Account not initialized"
};
}
return await this.account.signMessage(call.params);
}
};
this.on = (event, handler) => {
if (event !== "accountsChanged" && event !== "networkChanged") {
throw new Error(`Unknown event: ${event}`);
}
this.subscriptions.push({ type: event, handler });
};
this.off = (event, handler) => {
if (event !== "accountsChanged" && event !== "networkChanged") {
throw new Error(`Unknown event: ${event}`);
}
const idx = this.subscriptions.findIndex(
(sub) => sub.type === event && sub.handler === handler
);
if (idx >= 0) {
this.subscriptions.splice(idx, 1);
}
};
}
case "wallet_supportedSpecs":
return [];
case "wallet_supportedWalletApi":
return [];
default:
throw {
code: 63,
message: "An unexpected error occurred",
data: `Unknown RPC call type: ${call.type}`
};
}
};
on = (event, handler) => {
if (event !== "accountsChanged" && event !== "networkChanged") {
throw new Error(`Unknown event: ${event}`);
}
this.subscriptions.push({ type: event, handler });
};
off = (event, handler) => {
if (event !== "accountsChanged" && event !== "networkChanged") {
throw new Error(`Unknown event: ${event}`);
}
const idx = this.subscriptions.findIndex(
(sub) => sub.type === event && sub.handler === handler
);
if (idx >= 0) {
this.subscriptions.splice(idx, 1);
}
};
emitNetworkChanged(chainId) {

@@ -312,4 +370,32 @@ this.subscriptions.filter((sub) => sub.type === "networkChanged").forEach((sub) => {

// src/policies.ts
function parsePolicies(policies) {
return {
verified: false,
contracts: policies.contracts ? Object.fromEntries(
Object.entries(policies.contracts).map(([address, contract]) => [
address,
{
...contract,
methods: contract.methods.map((method) => ({
...method,
authorized: true
}))
}
])
) : void 0,
messages: policies.messages?.map((message) => ({
...message,
authorized: true
}))
};
}
// src/telegram/provider.ts
var TelegramProvider = class extends BaseProvider {
_tmaUrl;
_chainId;
_username;
_policies;
_rpcUrl;
constructor({

@@ -325,3 +411,3 @@ rpc,

this._chainId = chainId;
this._policies = policies;
this._policies = parsePolicies(policies);
if (typeof window !== "undefined") {

@@ -328,0 +414,0 @@ window.starknet_controller = this;

@@ -6,2 +6,3 @@ import * as starknet from 'starknet';

import { ChainId } from '@starknet-io/types-js';
import { ParsedSessionPolicies } from './policies.js';

@@ -14,3 +15,3 @@ declare function normalizeCalls(calls: Call | Call[]): {

declare function toSessionPolicies(policies: Policies): SessionPolicies;
declare function toWasmPolicies(policies: SessionPolicies): wasm.Policy[];
declare function toWasmPolicies(policies: ParsedSessionPolicies): wasm.Policy[];
declare function toArray<T>(val: T | T[]): T[];

@@ -17,0 +18,0 @@ declare function humanizeString(str: string): string;

@@ -82,3 +82,4 @@ // src/utils.ts

target,
method: m.entrypoint
method: m.entrypoint,
authorized: m.authorized
}))

@@ -99,3 +100,4 @@ ),

return {
scope_hash: hash.computePoseidonHash(domainHash, typeHash)
scope_hash: hash.computePoseidonHash(domainHash, typeHash),
authorized: p.authorized
};

@@ -102,0 +104,0 @@ })

{
"name": "@cartridge/controller",
"version": "0.5.9",
"version": "0.6.0",
"description": "Cartridge Controller",

@@ -9,6 +9,18 @@ "module": "dist/index.js",

"exports": {
".": "./dist/index.js",
"./session": "./dist/session/index.js",
"./provider": "./dist/provider/index.js",
"./types": "./dist/types/index.js"
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./session": {
"types": "./dist/session/index.d.ts",
"default": "./dist/session/index.js"
},
"./provider": {
"types": "./dist/provider/index.d.ts",
"default": "./dist/provider/index.js"
},
"./types": {
"types": "./dist/types/index.d.ts",
"default": "./dist/types/index.js"
}
},

@@ -26,2 +38,5 @@ "tsup": {

},
"peerDependencies": {
"starknet": "^6.21.0"
},
"dependencies": {

@@ -35,4 +50,3 @@ "@cartridge/penpal": "^6.2.3",

"query-string": "^7.1.1",
"starknet": "^6.11.0",
"@cartridge/account-wasm": "0.5.9"
"@cartridge/account-wasm": "0.6.0"
},

@@ -45,3 +59,3 @@ "devDependencies": {

"typescript": "^5.4.5",
"@cartridge/tsconfig": "0.5.9"
"@cartridge/tsconfig": "0.6.0"
},

@@ -48,0 +62,0 @@ "scripts": {

@@ -350,2 +350,3 @@ import { AsyncMethodReturns } from "@cartridge/penpal";

}
if (!this.keychain) return;

@@ -352,0 +353,0 @@

@@ -19,3 +19,6 @@ import { WalletAccount } from "starknet";

import { icon } from "./icon";
import { Mutex } from "./mutex";
const mutex = new Mutex();
export default abstract class BaseProvider implements StarknetWindowObject {

@@ -30,6 +33,33 @@ public id = "controller";

private _probePromise: Promise<WalletAccount | undefined> | null = null;
protected async safeProbe(): Promise<WalletAccount | undefined> {
// If we already have an account, return it
if (this.account) {
return this.account;
}
// If we're already probing, wait for the existing probe
if (this._probePromise) {
return this._probePromise;
}
const release = await mutex.obtain();
return await new Promise<WalletAccount | undefined>(async (resolve) => {
try {
this._probePromise = this.probe();
const result = await this._probePromise;
resolve(result);
} finally {
this._probePromise = null;
}
}).finally(() => {
release();
});
}
request: RequestFn = async (call) => {
switch (call.type) {
case "wallet_getPermissions":
await this.probe();
await this.safeProbe();

@@ -50,3 +80,4 @@ if (this.account) {

this.account = await this.probe();
this.account = await this.safeProbe();
if (!this.account && !silentMode) {

@@ -53,0 +84,0 @@ this.account = await this.connect();

@@ -36,2 +36,3 @@ import { Policy } from "@cartridge/account-wasm";

this.address = address;
this.controller = CartridgeSessionAccount.new_as_registered(

@@ -38,0 +39,0 @@ rpcUrl,

@@ -9,2 +9,3 @@ import { ec, stark, WalletAccount } from "starknet";

import { AddStarknetChainParameters } from "@starknet-io/types-js";
import { ParsedSessionPolicies } from "../policies";

@@ -24,2 +25,3 @@ interface SessionRegistration {

redirectUrl: string;
keychainUrl?: string;
};

@@ -35,11 +37,40 @@

protected _redirectUrl: string;
protected _policies: SessionPolicies;
protected _policies: ParsedSessionPolicies;
protected _keychainUrl: string;
constructor({ rpc, chainId, policies, redirectUrl }: SessionOptions) {
constructor({
rpc,
chainId,
policies,
redirectUrl,
keychainUrl,
}: SessionOptions) {
super();
this._policies = {
verified: false,
contracts: policies.contracts
? Object.fromEntries(
Object.entries(policies.contracts).map(([address, contract]) => [
address,
{
...contract,
methods: contract.methods.map((method) => ({
...method,
authorized: true,
})),
},
]),
)
: undefined,
messages: policies.messages?.map((message) => ({
...message,
authorized: true,
})),
};
this._rpcUrl = rpc;
this._chainId = chainId;
this._redirectUrl = redirectUrl;
this._policies = policies;
this._keychainUrl = keychainUrl || KEYCHAIN_URL;

@@ -51,2 +82,38 @@ if (typeof window !== "undefined") {

private validatePoliciesSubset(
newPolicies: ParsedSessionPolicies,
existingPolicies: ParsedSessionPolicies,
): boolean {
if (newPolicies.contracts) {
if (!existingPolicies.contracts) return false;
for (const [address, contract] of Object.entries(newPolicies.contracts)) {
const existingContract = existingPolicies.contracts[address];
if (!existingContract) return false;
for (const method of contract.methods) {
const existingMethod = existingContract.methods.find(
(m) => m.entrypoint === method.entrypoint,
);
if (!existingMethod || !existingMethod.authorized) return false;
}
}
}
if (newPolicies.messages) {
if (!existingPolicies.messages) return false;
for (const message of newPolicies.messages) {
const existingMessage = existingPolicies.messages.find(
(m) =>
JSON.stringify(m.domain) === JSON.stringify(message.domain) &&
JSON.stringify(m.types) === JSON.stringify(message.types),
);
if (!existingMessage || !existingMessage.authorized) return false;
}
}
return true;
}
async username() {

@@ -58,14 +125,20 @@ await this.tryRetrieveFromQueryOrStorage();

async probe(): Promise<WalletAccount | undefined> {
await this.tryRetrieveFromQueryOrStorage();
return;
if (this.account) {
return this.account;
}
this.account = await this.tryRetrieveFromQueryOrStorage();
return this.account;
}
async connect(): Promise<WalletAccount | undefined> {
await this.tryRetrieveFromQueryOrStorage();
if (this.account) {
return this.account;
}
this.account = await this.tryRetrieveFromQueryOrStorage();
if (this.account) {
return;
return this.account;
}
// Generate a random local key pair
const pk = stark.randomAddress();

@@ -82,3 +155,7 @@ const publicKey = ec.starkCurve.getStarkKey(pk);

const url = `${KEYCHAIN_URL}/session?public_key=${publicKey}&redirect_uri=${
localStorage.setItem("sessionPolicies", JSON.stringify(this._policies));
const url = `${
this._keychainUrl
}/session?public_key=${publicKey}&redirect_uri=${
this._redirectUrl

@@ -92,3 +169,3 @@ }&redirect_query_name=startapp&policies=${JSON.stringify(

return;
return this.account;
}

@@ -107,2 +184,3 @@

localStorage.removeItem("session");
localStorage.removeItem("sessionPolicies");
this.account = undefined;

@@ -114,2 +192,6 @@ this._username = undefined;

async tryRetrieveFromQueryOrStorage() {
if (this.account) {
return this.account;
}
const signerString = localStorage.getItem("sessionSigner");

@@ -147,2 +229,43 @@ const signer = signerString ? JSON.parse(signerString) : null;

// Check expiration
const expirationTime = parseInt(sessionRegistration.expiresAt) * 1000;
console.log("Session expiration check:", {
expirationTime,
currentTime: Date.now(),
expired: Date.now() >= expirationTime,
});
if (Date.now() >= expirationTime) {
console.log("Session expired, clearing stored session");
this.clearStoredSession();
return;
}
// Check stored policies
const storedPoliciesStr = localStorage.getItem("sessionPolicies");
console.log("Checking stored policies:", {
storedPoliciesStr,
currentPolicies: this._policies,
});
if (storedPoliciesStr) {
const storedPolicies = JSON.parse(
storedPoliciesStr,
) as ParsedSessionPolicies;
const isValid = this.validatePoliciesSubset(
this._policies,
storedPolicies,
);
console.log("Policy validation result:", {
isValid,
storedPolicies,
requestedPolicies: this._policies,
});
if (!isValid) {
console.log("Policy validation failed, clearing stored session");
this.clearStoredSession();
return;
}
}
this._username = sessionRegistration.username;

@@ -161,2 +284,8 @@ this.account = new SessionAccount(this, {

}
private clearStoredSession(): void {
localStorage.removeItem("sessionSigner");
localStorage.removeItem("session");
localStorage.removeItem("sessionPolicies");
}
}

@@ -15,2 +15,3 @@ import {

import { AddStarknetChainParameters } from "@starknet-io/types-js";
import { ParsedSessionPolicies, parsePolicies } from "../policies";

@@ -29,3 +30,3 @@ interface SessionRegistration {

protected _username?: string;
protected _policies: SessionPolicies;
protected _policies: ParsedSessionPolicies;
private _rpcUrl: string;

@@ -49,3 +50,3 @@

this._chainId = chainId;
this._policies = policies;
this._policies = parsePolicies(policies);

@@ -52,0 +53,0 @@ if (typeof window !== "undefined") {

@@ -15,2 +15,3 @@ import {

import { ChainId } from "@starknet-io/types-js";
import { ParsedSessionPolicies } from "./policies";

@@ -92,3 +93,3 @@ // Whitelist of allowed property names to prevent prototype pollution

export function toWasmPolicies(policies: SessionPolicies): wasm.Policy[] {
export function toWasmPolicies(policies: ParsedSessionPolicies): wasm.Policy[] {
return [

@@ -100,2 +101,3 @@ ...Object.entries(policies.contracts ?? {}).flatMap(

method: m.entrypoint,
authorized: m.authorized,
})),

@@ -118,2 +120,3 @@ ),

scope_hash: hash.computePoseidonHash(domainHash, typeHash),
authorized: p.authorized,
};

@@ -120,0 +123,0 @@ }),

@@ -8,6 +8,5 @@ {

"composite": false,
"incremental": false,
"moduleResolution": "node"
"incremental": false
},
"include": ["src/**/*", "package.json"]
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc