@atomiqlabs/chain-starknet 8.4.5 → 8.5.1

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/README.md CHANGED
@@ -30,6 +30,69 @@ Import backend-only utilities from the dedicated `node` subpath:
30
30
  import {StarknetChainEvents, StarknetPersistentSigner} from "@atomiqlabs/chain-starknet/node";
31
31
  ```
32
32
 
33
+ ## Signers
34
+
35
+ ### Browser wallets
36
+
37
+ For connecting to browser wallets (Braavos, Xverse, Argent/Ready...) from within browser environments,
38
+ use `StarknetSigner` with a get-starknet `WalletAccount` — the extension handles signing.
39
+
40
+ ```ts
41
+ import { connect } from "@starknet-io/get-starknet";
42
+ import { WalletAccount } from "starknet";
43
+ import { StarknetBrowserSigner } from "@atomiqlabs/chain-starknet";
44
+
45
+ // Connect the starknet wallet
46
+ const swo = await connect();
47
+
48
+ const provider = new RpcProvider({nodeUrl: starknetRpc});
49
+ const walletAccount = await WalletAccount.connect(provider, swo);
50
+ const wallet = new StarknetBrowserSigner(walletAccount);
51
+ ```
52
+
53
+ ### New keypair based wallets
54
+
55
+ For a programmatic integration driven by a raw private key using a simple single-key controlled OpenZeppelin
56
+ account. Use this only for newly created accounts, if you are bringing in your existing key from an already
57
+ deployed wallet account (Braavos, Xverse and Argent/Ready accounts), use the `DeployedStarkCurveWallet` described
58
+ in [Already deployed wallets](#already-deployed-wallets).
59
+
60
+ ```ts
61
+ import {StarknetSigner, StarknetKeypairWallet} from "@atomiqlabs/chain-starknet";
62
+ import {RpcProvider} from "starknet";
63
+
64
+ // Generate random private key
65
+ const starknetKey: string = StarknetKeypairWallet.generateRandomPrivateKey();
66
+
67
+ const provider = new RpcProvider({nodeUrl: starknetRpc});
68
+ const wallet = new StarknetKeypairWallet(provider, starknetKey);
69
+ const starknetSigner = new StarknetSigner(wallet);
70
+ ```
71
+
72
+ The account contract does not need to be deployed up front: the SDK automatically deploys it with the first
73
+ swap transaction (paying the deployment fee from the account's STRK balance).
74
+
75
+ ### Already deployed wallets
76
+
77
+ For a programmatic integration driven by a raw private key controlling an **already-deployed**
78
+ account (like existing Braavos, Xverse and Argent/Ready accounts), use `DeployedStarkCurveWallet`.
79
+ A Starknet address depends on the account's class hash, so the address is passed explicitly rather
80
+ than derived. Prefer `createAndVerify` over using the constructor, which confirms — via the account's
81
+ own on-chain signature validation — that the key controls the address:
82
+
83
+ ```ts
84
+ import {StarknetSigner, DeployedStarkCurveWallet} from "@atomiqlabs/chain-starknet";
85
+ import {RpcProvider} from "starknet";
86
+
87
+ const provider = new RpcProvider({nodeUrl: starknetRpc});
88
+ const wallet = await DeployedStarkCurveWallet.createAndVerify(provider, privateKey, address);
89
+ const signer = new StarknetSigner(wallet);
90
+ ```
91
+
92
+ It throws `AccountNotDeployedError` if the account isn't deployed, or `WalletVerificationError`
93
+ if the key doesn't control it (or the account needs a scheme a single Stark-curve key can't
94
+ satisfy, e.g. multisig). The bare constructor performs no such check — call `verifyWallet()` first.
95
+
33
96
  ## Supported Chains
34
97
 
35
98
  This package exports a single Starknet initializer:
package/dist/index.d.ts CHANGED
@@ -15,5 +15,6 @@ export { StarknetSuccessAction, StarknetSwapData } from "./starknet/swaps/Starkn
15
15
  export * from "./starknet/wallet/StarknetSigner";
16
16
  export * from "./starknet/wallet/StarknetBrowserSigner";
17
17
  export * from "./starknet/wallet/accounts/StarknetKeypairWallet";
18
+ export * from "./starknet/wallet/accounts/DeployedStarkCurveWallet";
18
19
  export * from "./starknet/StarknetChainType";
19
20
  export * from "./starknet/StarknetInitializer";
package/dist/index.js CHANGED
@@ -109,5 +109,6 @@ Object.defineProperty(exports, "StarknetSwapData", { enumerable: true, get: func
109
109
  __exportStar(require("./starknet/wallet/StarknetSigner"), exports);
110
110
  __exportStar(require("./starknet/wallet/StarknetBrowserSigner"), exports);
111
111
  __exportStar(require("./starknet/wallet/accounts/StarknetKeypairWallet"), exports);
112
+ __exportStar(require("./starknet/wallet/accounts/DeployedStarkCurveWallet"), exports);
112
113
  __exportStar(require("./starknet/StarknetChainType"), exports);
113
114
  __exportStar(require("./starknet/StarknetInitializer"), exports);
@@ -0,0 +1,68 @@
1
+ import { Account, DeployAccountContractPayload, Provider, TypedData } from "starknet";
2
+ /**
3
+ * Thrown when the target account is not deployed on-chain, so its signature rules can't be checked.
4
+ */
5
+ export declare class AccountNotDeployedError extends Error {
6
+ readonly address: string;
7
+ constructor(address: string);
8
+ }
9
+ /**
10
+ * Thrown when the deployed account rejects a signature from the key (wrong key, or a scheme a single Stark-curve key can't satisfy, e.g. multisig/guardian/secp256r1).
11
+ */
12
+ export declare class WalletVerificationError extends Error {
13
+ readonly address: string;
14
+ constructor(address: string);
15
+ }
16
+ /**
17
+ * Wallet for an already-deployed Starknet account controlled by a single Stark-curve private key,
18
+ * bound to an explicit `address` (Starknet addresses depend on the account class hash, so a key
19
+ * maps to a different address per wallet vendor). Never self-deploys.
20
+ *
21
+ * Use this when bringing in the key from an already deployed wallet account (Braavos, Xverse or Argent/Ready wallets).
22
+ *
23
+ * If you are creating a new wallet consider using {@link StarknetKeypairWallet}.
24
+ *
25
+ * @remarks Prefer {@link createAndVerify}, or call {@link verifyWallet} before use after using the constructor,
26
+ * these perform an actual check on whether a given key actually controls the wallet account at a given address.
27
+ *
28
+ * @category Wallets
29
+ */
30
+ export declare class DeployedStarkCurveWallet extends Account {
31
+ readonly publicKey: string;
32
+ /**
33
+ * The constructor does not validate anything. Prefer {@link createAndVerify}, or call
34
+ * {@link verifyWallet} before use — until then the key is not checked to control `address`.
35
+ *
36
+ * @param provider Starknet RPC provider
37
+ * @param privateKey Stark-curve private key of the account, in the 0x... format
38
+ * @param address Address of the **already deployed account**
39
+ */
40
+ constructor(provider: Provider, privateKey: string, address: string);
41
+ /**
42
+ * Always null — wraps an already-deployed account and never self-deploys.
43
+ */
44
+ getDeploymentData(): DeployAccountContractPayload | null;
45
+ /**
46
+ * @throws {@link AccountNotDeployedError} on CONTRACT_NOT_FOUND; other errors (rate limit, network) propagate.
47
+ */
48
+ protected assertDeployed(): Promise<void>;
49
+ /**
50
+ * A fixed SNIP-12 message — the contents are irrelevant, it only needs to be signed and validated.
51
+ */
52
+ protected getOwnershipMessage(): Promise<TypedData>;
53
+ /**
54
+ * Verifies the private key controls the bound account by signing a SNIP-12 message and validating it
55
+ * against the account's own on-chain `is_valid_signature` — the canonical check, independent of the
56
+ * account's class hash or signature scheme.
57
+ *
58
+ * @throws {@link AccountNotDeployedError} if the account is not deployed
59
+ * @throws {@link WalletVerificationError} if the account does not accept the key's signature
60
+ */
61
+ verifyWallet(): Promise<void>;
62
+ /**
63
+ * Use this to create a new wallet instance, prefer it over just using the constructor!
64
+ *
65
+ * Constructs the wallet and verifies wallet account ownership (by running {@link verifyWallet}).
66
+ */
67
+ static createAndVerify(provider: Provider, privateKey: string, address: string): Promise<DeployedStarkCurveWallet>;
68
+ }
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DeployedStarkCurveWallet = exports.WalletVerificationError = exports.AccountNotDeployedError = void 0;
4
+ const starknet_1 = require("starknet");
5
+ const Utils_1 = require("../../../utils/Utils");
6
+ /**
7
+ * Thrown when the target account is not deployed on-chain, so its signature rules can't be checked.
8
+ */
9
+ class AccountNotDeployedError extends Error {
10
+ constructor(address) {
11
+ super("Account " + address + " is not deployed on-chain");
12
+ this.address = address;
13
+ this.name = "AccountNotDeployedError";
14
+ Object.setPrototypeOf(this, AccountNotDeployedError.prototype);
15
+ }
16
+ }
17
+ exports.AccountNotDeployedError = AccountNotDeployedError;
18
+ /**
19
+ * Thrown when the deployed account rejects a signature from the key (wrong key, or a scheme a single Stark-curve key can't satisfy, e.g. multisig/guardian/secp256r1).
20
+ */
21
+ class WalletVerificationError extends Error {
22
+ constructor(address) {
23
+ super("Account " + address + " did not accept a signature from the provided private key");
24
+ this.address = address;
25
+ this.name = "WalletVerificationError";
26
+ Object.setPrototypeOf(this, WalletVerificationError.prototype);
27
+ }
28
+ }
29
+ exports.WalletVerificationError = WalletVerificationError;
30
+ /**
31
+ * Wallet for an already-deployed Starknet account controlled by a single Stark-curve private key,
32
+ * bound to an explicit `address` (Starknet addresses depend on the account class hash, so a key
33
+ * maps to a different address per wallet vendor). Never self-deploys.
34
+ *
35
+ * Use this when bringing in the key from an already deployed wallet account (Braavos, Xverse or Argent/Ready wallets).
36
+ *
37
+ * If you are creating a new wallet consider using {@link StarknetKeypairWallet}.
38
+ *
39
+ * @remarks Prefer {@link createAndVerify}, or call {@link verifyWallet} before use after using the constructor,
40
+ * these perform an actual check on whether a given key actually controls the wallet account at a given address.
41
+ *
42
+ * @category Wallets
43
+ */
44
+ class DeployedStarkCurveWallet extends starknet_1.Account {
45
+ /**
46
+ * The constructor does not validate anything. Prefer {@link createAndVerify}, or call
47
+ * {@link verifyWallet} before use — until then the key is not checked to control `address`.
48
+ *
49
+ * @param provider Starknet RPC provider
50
+ * @param privateKey Stark-curve private key of the account, in the 0x... format
51
+ * @param address Address of the **already deployed account**
52
+ */
53
+ constructor(provider, privateKey, address) {
54
+ super({ provider, address, signer: privateKey, cairoVersion: "1" });
55
+ this.publicKey = starknet_1.ec.starkCurve.getStarkKey((0, Utils_1.toHex)(privateKey));
56
+ }
57
+ /**
58
+ * Always null — wraps an already-deployed account and never self-deploys.
59
+ */
60
+ getDeploymentData() {
61
+ return null;
62
+ }
63
+ /**
64
+ * @throws {@link AccountNotDeployedError} on CONTRACT_NOT_FOUND; other errors (rate limit, network) propagate.
65
+ */
66
+ async assertDeployed() {
67
+ try {
68
+ await this.getClassHashAt(this.address);
69
+ }
70
+ catch (e) {
71
+ const message = ((e?.message ?? "") + (e?.baseError?.message ?? "")).toLowerCase();
72
+ if (message.includes("contract not found") || e?.code === 20 || e?.baseError?.code === 20)
73
+ throw new AccountNotDeployedError(this.address);
74
+ throw e;
75
+ }
76
+ }
77
+ /**
78
+ * A fixed SNIP-12 message — the contents are irrelevant, it only needs to be signed and validated.
79
+ */
80
+ async getOwnershipMessage() {
81
+ return {
82
+ types: {
83
+ StarknetDomain: [
84
+ { name: 'name', type: 'shortstring' },
85
+ { name: 'version', type: 'shortstring' },
86
+ { name: 'chainId', type: 'shortstring' },
87
+ { name: 'revision', type: 'shortstring' },
88
+ ],
89
+ Verification: [{ name: 'action', type: 'shortstring' }],
90
+ },
91
+ primaryType: 'Verification',
92
+ domain: { name: 'DeployedStarkCurveWallet', version: '1', chainId: await this.getChainId(), revision: '1' },
93
+ message: { action: 'Verify wallet ownership' },
94
+ };
95
+ }
96
+ /**
97
+ * Verifies the private key controls the bound account by signing a SNIP-12 message and validating it
98
+ * against the account's own on-chain `is_valid_signature` — the canonical check, independent of the
99
+ * account's class hash or signature scheme.
100
+ *
101
+ * @throws {@link AccountNotDeployedError} if the account is not deployed
102
+ * @throws {@link WalletVerificationError} if the account does not accept the key's signature
103
+ */
104
+ async verifyWallet() {
105
+ await this.assertDeployed();
106
+ const message = await this.getOwnershipMessage();
107
+ const signature = await this.signMessage(message);
108
+ const valid = await this.verifyMessageInStarknet(message, signature, this.address);
109
+ if (!valid)
110
+ throw new WalletVerificationError(this.address);
111
+ }
112
+ /**
113
+ * Use this to create a new wallet instance, prefer it over just using the constructor!
114
+ *
115
+ * Constructs the wallet and verifies wallet account ownership (by running {@link verifyWallet}).
116
+ */
117
+ static async createAndVerify(provider, privateKey, address) {
118
+ const wallet = new DeployedStarkCurveWallet(provider, privateKey, address);
119
+ await wallet.verifyWallet();
120
+ return wallet;
121
+ }
122
+ }
123
+ exports.DeployedStarkCurveWallet = DeployedStarkCurveWallet;
@@ -1,6 +1,13 @@
1
1
  import { Account, DeployAccountContractPayload, Provider } from "starknet";
2
2
  /**
3
- * Keypair-based wallet implementation using OpenZeppelin Account
3
+ * A simple keypair-based wallet implementation with a single key and not-upgradable, uses OpenZeppelin Account.
4
+ * Use this only for new keypair-based wallets which don't require additional features such as guardians, multisigs
5
+ * or upgradability.
6
+ *
7
+ * @remarks Don't use this for already deployed existing wallet account (Braavos, Xverse, Argent/Ready), as it will
8
+ * result in a different address even when used with the same key (this is because Starknet has native account
9
+ * abstraction capabilities and you need a correct combination of account type + key). For already deployed wallet
10
+ * accounts consider using {@link DeployedStarkCurveWallet} instead!
4
11
  *
5
12
  * @category Wallets
6
13
  */
@@ -6,7 +6,14 @@ const Utils_1 = require("../../../utils/Utils");
6
6
  const buffer_1 = require("buffer");
7
7
  const OZaccountClassHash = '0x00261c293c8084cd79086214176b33e5911677cec55104fddc8d25b0b736dcad';
8
8
  /**
9
- * Keypair-based wallet implementation using OpenZeppelin Account
9
+ * A simple keypair-based wallet implementation with a single key and not-upgradable, uses OpenZeppelin Account.
10
+ * Use this only for new keypair-based wallets which don't require additional features such as guardians, multisigs
11
+ * or upgradability.
12
+ *
13
+ * @remarks Don't use this for already deployed existing wallet account (Braavos, Xverse, Argent/Ready), as it will
14
+ * result in a different address even when used with the same key (this is because Starknet has native account
15
+ * abstraction capabilities and you need a correct combination of account type + key). For already deployed wallet
16
+ * accounts consider using {@link DeployedStarkCurveWallet} instead!
10
17
  *
11
18
  * @category Wallets
12
19
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atomiqlabs/chain-starknet",
3
- "version": "8.4.5",
3
+ "version": "8.5.1",
4
4
  "description": "Starknet specific base implementation",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
package/src/index.ts CHANGED
@@ -99,6 +99,7 @@ export {StarknetSuccessAction, StarknetSwapData} from "./starknet/swaps/Starknet
99
99
  export * from "./starknet/wallet/StarknetSigner";
100
100
  export * from "./starknet/wallet/StarknetBrowserSigner";
101
101
  export * from "./starknet/wallet/accounts/StarknetKeypairWallet";
102
+ export * from "./starknet/wallet/accounts/DeployedStarkCurveWallet";
102
103
 
103
104
  export * from "./starknet/StarknetChainType";
104
105
  export * from "./starknet/StarknetInitializer";
@@ -0,0 +1,125 @@
1
+ import {Account, DeployAccountContractPayload, ec, Provider, TypedData} from "starknet";
2
+ import {toHex} from "../../../utils/Utils";
3
+
4
+ /**
5
+ * Thrown when the target account is not deployed on-chain, so its signature rules can't be checked.
6
+ */
7
+ export class AccountNotDeployedError extends Error {
8
+ constructor(readonly address: string) {
9
+ super("Account "+address+" is not deployed on-chain");
10
+ this.name = "AccountNotDeployedError";
11
+ Object.setPrototypeOf(this, AccountNotDeployedError.prototype);
12
+ }
13
+ }
14
+
15
+ /**
16
+ * Thrown when the deployed account rejects a signature from the key (wrong key, or a scheme a single Stark-curve key can't satisfy, e.g. multisig/guardian/secp256r1).
17
+ */
18
+ export class WalletVerificationError extends Error {
19
+ constructor(readonly address: string) {
20
+ super("Account "+address+" did not accept a signature from the provided private key");
21
+ this.name = "WalletVerificationError";
22
+ Object.setPrototypeOf(this, WalletVerificationError.prototype);
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Wallet for an already-deployed Starknet account controlled by a single Stark-curve private key,
28
+ * bound to an explicit `address` (Starknet addresses depend on the account class hash, so a key
29
+ * maps to a different address per wallet vendor). Never self-deploys.
30
+ *
31
+ * Use this when bringing in the key from an already deployed wallet account (Braavos, Xverse or Argent/Ready wallets).
32
+ *
33
+ * If you are creating a new wallet consider using {@link StarknetKeypairWallet}.
34
+ *
35
+ * @remarks Prefer {@link createAndVerify}, or call {@link verifyWallet} before use after using the constructor,
36
+ * these perform an actual check on whether a given key actually controls the wallet account at a given address.
37
+ *
38
+ * @category Wallets
39
+ */
40
+ export class DeployedStarkCurveWallet extends Account {
41
+
42
+ public readonly publicKey: string;
43
+
44
+ /**
45
+ * The constructor does not validate anything. Prefer {@link createAndVerify}, or call
46
+ * {@link verifyWallet} before use — until then the key is not checked to control `address`.
47
+ *
48
+ * @param provider Starknet RPC provider
49
+ * @param privateKey Stark-curve private key of the account, in the 0x... format
50
+ * @param address Address of the **already deployed account**
51
+ */
52
+ constructor(provider: Provider, privateKey: string, address: string) {
53
+ super({provider, address, signer: privateKey, cairoVersion: "1"});
54
+ this.publicKey = ec.starkCurve.getStarkKey(toHex(privateKey));
55
+ }
56
+
57
+ /**
58
+ * Always null — wraps an already-deployed account and never self-deploys.
59
+ */
60
+ public getDeploymentData(): DeployAccountContractPayload | null {
61
+ return null;
62
+ }
63
+
64
+ /**
65
+ * @throws {@link AccountNotDeployedError} on CONTRACT_NOT_FOUND; other errors (rate limit, network) propagate.
66
+ */
67
+ protected async assertDeployed(): Promise<void> {
68
+ try {
69
+ await this.getClassHashAt(this.address);
70
+ } catch (e: any) {
71
+ const message = ((e?.message ?? "") + (e?.baseError?.message ?? "")).toLowerCase();
72
+ if (message.includes("contract not found") || e?.code === 20 || e?.baseError?.code === 20)
73
+ throw new AccountNotDeployedError(this.address);
74
+ throw e;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * A fixed SNIP-12 message — the contents are irrelevant, it only needs to be signed and validated.
80
+ */
81
+ protected async getOwnershipMessage(): Promise<TypedData> {
82
+ return {
83
+ types: {
84
+ StarknetDomain: [
85
+ {name: 'name', type: 'shortstring'},
86
+ {name: 'version', type: 'shortstring'},
87
+ {name: 'chainId', type: 'shortstring'},
88
+ {name: 'revision', type: 'shortstring'},
89
+ ],
90
+ Verification: [{name: 'action', type: 'shortstring'}],
91
+ },
92
+ primaryType: 'Verification',
93
+ domain: {name: 'DeployedStarkCurveWallet', version: '1', chainId: await this.getChainId(), revision: '1'},
94
+ message: {action: 'Verify wallet ownership'},
95
+ };
96
+ }
97
+
98
+ /**
99
+ * Verifies the private key controls the bound account by signing a SNIP-12 message and validating it
100
+ * against the account's own on-chain `is_valid_signature` — the canonical check, independent of the
101
+ * account's class hash or signature scheme.
102
+ *
103
+ * @throws {@link AccountNotDeployedError} if the account is not deployed
104
+ * @throws {@link WalletVerificationError} if the account does not accept the key's signature
105
+ */
106
+ public async verifyWallet(): Promise<void> {
107
+ await this.assertDeployed();
108
+ const message = await this.getOwnershipMessage();
109
+ const signature = await this.signMessage(message);
110
+ const valid = await this.verifyMessageInStarknet(message, signature, this.address);
111
+ if (!valid) throw new WalletVerificationError(this.address);
112
+ }
113
+
114
+ /**
115
+ * Use this to create a new wallet instance, prefer it over just using the constructor!
116
+ *
117
+ * Constructs the wallet and verifies wallet account ownership (by running {@link verifyWallet}).
118
+ */
119
+ public static async createAndVerify(provider: Provider, privateKey: string, address: string): Promise<DeployedStarkCurveWallet> {
120
+ const wallet = new DeployedStarkCurveWallet(provider, privateKey, address);
121
+ await wallet.verifyWallet();
122
+ return wallet;
123
+ }
124
+
125
+ }
@@ -5,7 +5,14 @@ import {Buffer} from "buffer";
5
5
  const OZaccountClassHash = '0x00261c293c8084cd79086214176b33e5911677cec55104fddc8d25b0b736dcad';
6
6
 
7
7
  /**
8
- * Keypair-based wallet implementation using OpenZeppelin Account
8
+ * A simple keypair-based wallet implementation with a single key and not-upgradable, uses OpenZeppelin Account.
9
+ * Use this only for new keypair-based wallets which don't require additional features such as guardians, multisigs
10
+ * or upgradability.
11
+ *
12
+ * @remarks Don't use this for already deployed existing wallet account (Braavos, Xverse, Argent/Ready), as it will
13
+ * result in a different address even when used with the same key (this is because Starknet has native account
14
+ * abstraction capabilities and you need a correct combination of account type + key). For already deployed wallet
15
+ * accounts consider using {@link DeployedStarkCurveWallet} instead!
9
16
  *
10
17
  * @category Wallets
11
18
  */