Skip to content

feat(sdk): integration with proving service - #480

Closed
noa-starkware wants to merge 1 commit into
m-kus/sn-integration-demofrom
noa/sdk/proving-service
Closed

noa-starkware wants to merge 1 commit into
m-kus/sn-integration-demofrom
noa/sdk/proving-service

Conversation

@noa-starkware

@noa-starkware noa-starkware commented Feb 12, 2026 •

Copy link
Copy Markdown
Collaborator

This change is Reviewable

@noa-starkware noa-starkware self-assigned this Feb 12, 2026
@noa-starkware
noa-starkware marked this pull request as draft February 12, 2026 17:13
@m-kus
m-kus force-pushed the m-kus/sn-integration-demo branch 3 times, most recently from c3ef2de to 794ccf7 Compare February 15, 2026 14:45
@noa-starkware
noa-starkware force-pushed the noa/sdk/proving-service branch from 54fd897 to 9c7226b Compare February 16, 2026 09:23
Comment thread demo/discovery-service.toml
@noa-starkware
noa-starkware force-pushed the noa/sdk/proving-service branch from 9c7226b to 7b2c490 Compare February 16, 2026 09:34
@noa-starkware
noa-starkware marked this pull request as ready for review February 16, 2026 09:47
@noa-starkware
noa-starkware force-pushed the noa/sdk/proving-service branch from 7b2c490 to 66f0ce8 Compare February 16, 2026 09:51
constructor(
private readonly params: {
account: Account; // the user account (for signing)
account: AccountSignerRaw; // the user account (for signing)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use Account::sign_message instead? Using Account interface allows seamless wallet plug in here. Any additional interface wallets need to implement increase friction and maintenance burden

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think this could work? The doc of sign_message: "Signs a JSON object for off-chain usage with the private key and returns the signature. This adds a message prefix so it can't be interchanged with transactions"


async prove(invocation: ProofInvocationWithPayload): Promise<Proof> {
const inv = invocation;
const transactionPayload = {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious why do you do this low level rather than using starknet.js helpers?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this isnt a trivial call with an array of Calls, do you know of any helper I could use for this non conventional use case?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would probably do the trick:

import { type RpcProvider, ETransactionType } from "starknet";

 const channel = (this.provider as RpcProvider).channel;
 const transactionPayload = channel.buildTransaction(
   {
     type: ETransactionType.INVOKE,
     contractAddress: invocation.contractAddress,
     calldata: invocation.calldata,
     signature: invocation.signature ?? [],
     nonce: invocation.nonce,
     resourceBounds: invocation.resourceBounds,
     tip: invocation.tip,
     paymasterData: invocation.paymasterData ?? [],
     accountDeploymentData: invocation.accountDeploymentData ?? [],
     nonceDataAvailabilityMode: invocation.nonceDataAvailabilityMode ?? "L1",
     feeDataAvailabilityMode: invocation.feeDataAvailabilityMode ?? "L1",
   },
   "transaction"
 );

* without actually generating zero-knowledge proofs.
*/
export class CallMockProofProvider implements ProofProviderInterface {
export class CallMockProofProvider extends AbstractProofProvider {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd suggest adding mocked tests to sdk as well to run in ci

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working on mock prover for ci, is that what you meant?

*/
protected async getViewingKey(): Promise<ViewingKey> {
return await this.viewingKeyProvider.getViewingKey();
protected getViewingKey(): Promise<ViewingKey> | ViewingKey {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why making it sync?

@noa-starkware noa-starkware Feb 18, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -0,0 +1,55 @@
/**
* Abstract base for proof providers with shared getDefaultDetails implementation.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this abstraction needed? Could we instead update ProofProviderInterface ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both the mock and the real proof providers use the same get_details impl

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A simpler alternative would be a plain helper function (avoids extra abstraction layer):

// Replace abstract class with:
function getDefaultProofDetails(chainId: StarknetChainId): ProofInvocationFactoryDetails {
return { nonce: 0n, resourceBounds: { ... }, chainId, ... };
}

Comment thread demo/README.md Outdated
| `VITE_ADMIN_ADDRESS` | Admin/minter account address |
| `VITE_ADMIN_KEY` | Admin account private key |
| `VITE_ACCOUNTS` | JSON array of user accounts (see `.env.example`) |
| `VITE_PROVING_SERVICE_URL` | Optional. Proving service URL (e.g. `http://136.115.124.93:3000`). If unset, the app uses the mock prover (`execute_view` only) |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest adding support to the demo in a separate PR, this one should successfully run mocked tests in CI and e2e scenario against integration env

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

type ProofInvocationFactoryDetails,
type ProofProviderInterface,
} from "starknet-sdk";
import { IndexerClient } from "../src/indexer-client.js";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use ContractDiscoveryProvider instead of indexer btw, to simplify your e2e flow (just test sdk<>proving service)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we wanted to test everything together, didnt we?

@@ -5,16 +5,12 @@
* MockServerAction[] callbacks as the proof output.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note the signature validation error in CallMockProofProvider (see devnet tests)

@m-kus
m-kus force-pushed the m-kus/sn-integration-demo branch 10 times, most recently from de39ecd to f805a3a Compare February 17, 2026 12:27
}

/** Check result: proof non-empty, proof_facts and l2_to_l1_messages are arrays. */
function isProveTransactionResult(value: unknown): value is ProveTransactionResult {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to validate proving service response?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need IMO

Comment thread sdk/src/internal/proving-service.ts Outdated
);
}

export type BlockId =

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This already exists in starknet.js

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread sdk/src/utils/convert.ts Outdated
// ============ Calldata ============

/** Ensure calldata elements are 0x-prefixed hex (starknet signer/RPC expect this). */
export function ensureHexCalldata(calldata: string[]): string[] {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's used only once, perhaps worth just inlining?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread sdk/src/interfaces.ts Outdated
export interface ProofProviderInterface {
/** Get the default factory details for creating proof invocations */
getDefaultDetails(): ProofInvocationFactoryDetails;
/** Get the default factory details for creating proof invocations (may be async e.g. to fetch nonce) */

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd make it always async to avoid mixing async/sync ret types

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the comment, not relevant

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that two mixed return types remain:

  • ViewingKeyProvider.getViewingKey(): returns Promise | ViewingKey
  • ProofProviderInterface.prove(): returns Proof | Promise

l2_to_l1_messages: MessageToL1[];
}

export interface MessageToL1 {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be in starknet.js

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the same name?

@m-kus m-kus Feb 18, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import type { RPC } from "starknet";
type MessageToL1 = RPC.RPCSPEC010.MSG_TO_L1;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although it's quite ugly :) Can probably keep the custom type

/**
* Proving service URL. If a host is given (no scheme), defaults to http and port 3000.
*/
export function normalizeProvingServiceUrl(hostOrUrl: string): string {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if it's worth introducing a special helper for host + default port, just require users to provide a url in expected format

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}

/** Proof to bytes: u32[] packed big-endian, or base64 string decoded. */
function proofToBytes(proof: number[] | string): Uint8Array {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is proof returned as base64 string from the service? afaics it's serialized as list of integers https://github.com/starkware-libs/sequencer/blob/69b4c8d378580a1aec345f23dadd4766e036fe3b/crates/starknet_os_runner/src/server/http_server.rs#L35

}

/** Map JSON-RPC error code to typed exception. */
export function mapProvingServiceError(error: {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Separate classes for proving errors seems a bit of an overkill tbh, can just throw a detailed error message?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

senderAddress: poolAddressHex,
compiledCalldata,
version: (details.version ?? ETransactionVersion3.V3) as `${typeof ETransactionVersion3.V3}`,
nonce,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest using existing methods from starknet.js for building/signing tx

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because its non conventional invoke I dont know if its possible

@m-kus
m-kus force-pushed the m-kus/sn-integration-demo branch 2 times, most recently from 629ec28 to 4a8e35a Compare February 17, 2026 13:56
@noa-starkware
noa-starkware force-pushed the noa/sdk/proving-service branch from 1abc72b to 8742b39 Compare February 17, 2026 14:08
Comment thread e2e/package-lock.json
Comment on lines 2950 to 2956
"node_modules/starknet": {
"version": "9.4.0",
"resolved": "git+ssh://git@github.com/m-kus/starknet.js.git#425082b072b705ac129d1898ea4fc9863fe367f9",
"resolved": "git+ssh://git@github.com/m-kus/starknet.js.git#19c1b5fcb227df60dc4d7e965cf1aea0699f543f",
"integrity": "sha512-26eJb/OY7nZA/bDGd8VTOwlp9NKBZsIjjYx1uqtr5ZRLiy2UsZQefWN5YfHiow1oyAzTO+dLtcfHUX410/TTSw==",
"license": "MIT",
"dependencies": {
"@noble/curves": "~1.7.0",

@semgrep-code-starkware-libs semgrep-code-starkware-libs Bot Feb 17, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Package "$KEY" is resolved from a non-standard source: "git+ssh://git@github.com/m-kus/starknet.js.git#19c1b5fcb227df60dc4d7e965cf1aea0699f543f". Expected packages to be resolved from the official npm registry (registry.npmjs.org). Unusual sources may indicate dependency confusion, supply chain attacks, or misconfigured registries.

🌟 Fixed in commit 3b8850e 🌟

@m-kus
m-kus force-pushed the noa/sdk/proving-service branch from 0bafa59 to 96f2d5b Compare February 18, 2026 21:37
@m-kus
m-kus force-pushed the graphite-base/480 branch from 20cf470 to d5d2ec1 Compare February 18, 2026 21:37
@m-kus
m-kus changed the base branch from graphite-base/480 to m-kus/sn-integration-demo February 18, 2026 21:37
@m-kus
m-kus changed the base branch from m-kus/sn-integration-demo to graphite-base/480 February 19, 2026 15:06
@m-kus
m-kus force-pushed the graphite-base/480 branch from d5d2ec1 to 277d444 Compare February 19, 2026 15:41
@m-kus
m-kus force-pushed the noa/sdk/proving-service branch from 96f2d5b to 1092be3 Compare February 19, 2026 15:41
@m-kus
m-kus changed the base branch from graphite-base/480 to m-kus/sn-integration-demo February 19, 2026 15:42
@m-kus
m-kus force-pushed the m-kus/sn-integration-demo branch from 277d444 to faad3a9 Compare February 19, 2026 16:29
@m-kus
m-kus force-pushed the noa/sdk/proving-service branch from 1092be3 to acf6e20 Compare February 19, 2026 16:29
Comment thread sdk/src/testing/devnet.ts
// Mock proof provider returns proofFacts but proof.data may be undefined; use placeholder for sequencer
const response = await this.setup.admin.executeFromOutside(outsideTransaction, {
proofFacts: callAndProof.proofFacts,
proofFacts: callAndProof.proof.proofFacts,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't the proof passed ?

fee_data_availability_mode: inv.feeDataAvailabilityMode ?? "L1",
};

const result = await this.provingService.proveTransaction("latest", transactionPayload);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we prove 'latest' only, it means now the wallet needs to wait 10 blocks before transmitting the tx. There should probably be some configuration that allows to specify 'latest' (in case we're proving a new state change) or 'latest-verifiable' or relative number from current block number (nice to have) or explicit block hash (for max reliability)

@m-kus
m-kus force-pushed the m-kus/sn-integration-demo branch from faad3a9 to 8a0efd1 Compare February 22, 2026 17:26
@m-kus
m-kus force-pushed the noa/sdk/proving-service branch 2 times, most recently from b01d143 to 39bb920 Compare February 22, 2026 18:19
@m-kus
m-kus force-pushed the m-kus/sn-integration-demo branch from 8a0efd1 to 4d1b5d9 Compare February 22, 2026 18:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants