@ninsei-labs/sdk 0.45.0 → 0.46.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/CHANGELOG.md CHANGED
@@ -1,3 +1,26 @@
1
+ ## 0.46.1 — 2026-10-21
2
+
3
+ THE FACTORY SELECTORS FOLLOW THE 20-FIELD QUOTE.
4
+
5
+ - `FACTORY_METHODS.predict` is `0x3e949279` and `createOrderAndFund` is `0x3a504547` (the 19-field values were
6
+ `0x0824d205` and `0x4703a9d3`). With the old selectors the node's calldata reverted at the factory on the first
7
+ call, so no order could be created even though the quote itself was correct.
8
+ - `tools/check-factory-selectors.mjs` derives both selectors from `QUOTE_FIELDS` and the ABI types, and a
9
+ breaking run mutates a copy of the module to prove the check reddens. The pair can no longer go stale in
10
+ silence when a field joins the signed tuple.
11
+
12
+ ## 0.46.0 — 2026-10-21
13
+
14
+ THE QUOTE CARRIES THE CLAIM TIP, AND THE CLAIM PAYS IT.
15
+
16
+ - `claimTip` is the last field of the signed order quote and of `FactoryQuote`, and it is encoded into
17
+ `createOrderAndFund`. The escrow pays it to whoever sends `claim` when that sender is not the claimer, out of the
18
+ payout and in the same transaction (`router-escrow-evm` #19). Zero means no tip.
19
+ - `ORDER_QUOTE_VERSION` is 9. The field set changed, so every quote signed by this package is a version 9 quote and
20
+ verifies only against an escrow that knows the field.
21
+ - A quote object without `claimTip` is refused by `quoteMissingFields` like any other absent condition: a missing tip
22
+ is not read as zero.
23
+
1
24
  ## 0.45.0 — 2026-10-20
2
25
 
3
26
  THE RECOVERY PATH WORKS WITHOUT THE APP, AND A PASSPHRASE IS A CHOICE.
@@ -10,15 +10,16 @@
10
10
  // feeFor() (the router was removed), so there is nothing to ask. All arguments are static, so calldata is a
11
11
  // selector plus 32-byte words, built here in one place and checked mechanically.
12
12
  //
13
- // cast sig "predict((address,address,address,address,address,address,uint256,uint64,uint64,bytes32,bytes32,bytes32,uint256,uint256,uint256,address,uint64,uint256,uint256),bytes32,bytes32,uint8,bytes32,bytes32,bytes32)" -> 0x0824d205
14
- // cast sig "createOrderAndFund(<same tuple>,...)" -> 0x4703a9d3
13
+ // cast sig "predict((address,address,address,address,address,address,uint256,uint64,uint64,bytes32,bytes32,bytes32,uint256,uint256,uint256,address,uint64,uint256,uint256,uint256),bytes32,bytes32,uint8,bytes32,bytes32,bytes32)" -> 0x3e949279
14
+ // cast sig "createOrderAndFund(<same tuple>,...)" -> 0x3a504547
15
15
 
16
16
  export const FACTORY_METHODS = {
17
- // THE QUOTE GREW THE xmrAmount FIELD: the XMR side of the deal is part of the signed set, so both selectors
18
- // changed a second time (earlier for the cashier field).
19
- predict: "0x0824d205",
17
+ // THE SELECTORS FOLLOW THE 20-FIELD QUOTE. claimTip is the last field of the tuple, so both selectors changed when
18
+ // it joined the signed set; the old 19-field values (0x0824d205 / 0x4703a9d3) address a function this factory no
19
+ // longer has.
20
+ predict: "0x3e949279",
20
21
  // create the escrow AND deposit funds in ONE transaction (a value transfer with the call).
21
- createOrderAndFund: "0x4703a9d3",
22
+ createOrderAndFund: "0x3a504547",
22
23
  };
23
24
 
24
25
  // The OrderCreated event topic. It grew the registry address, the provider and the signing key:
@@ -32,6 +33,7 @@ export const ORDER_CREATED_TOPIC =
32
33
  export const QUOTE_FIELDS = [
33
34
  "provider", "quoteKey", "registry", "cashier", "locker", "claimer", "amount", "readyBy", "t1", "salt",
34
35
  "commitHalfClaimer", "edPointClaimer", "chainId", "feeBps", "fee", "feeRecipient", "validUntil", "nonce", "xmrAmount",
36
+ "claimTip",
35
37
  ];
36
38
  // The depositor fields are NOT in the quote: they go as separate arguments (into the escrow init code).
37
39
  const LOCKER_HALF_FIELDS = ["commitHalfLocker", "edPointLocker", "edViewPointLocker"];
@@ -89,6 +91,7 @@ function quoteWords(quote) {
89
91
  chainId: word(quote.chainId, 32), feeBps: word(quote.feeBps, 32), fee: word(quote.fee, 32),
90
92
  feeRecipient: word(quote.feeRecipient, 32), validUntil: word(quote.validUntil, 32), nonce: word(quote.nonce, 32),
91
93
  xmrAmount: word(quote.xmrAmount, 32),
94
+ claimTip: word(quote.claimTip, 32),
92
95
  };
93
96
  return QUOTE_FIELDS.map((n) => map[n]).join("");
94
97
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ninsei-labs/sdk",
3
- "version": "0.45.0",
3
+ "version": "0.46.1",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "registry": "https://registry.npmjs.org/"
@@ -39,6 +39,7 @@
39
39
  "check:arrival-read": "node tools/check-arrival-read.mjs",
40
40
  "check:daemon-address": "node tools/check-daemon-address.mjs",
41
41
  "check:ci-scripts": "node tools/check-ci-scripts.mjs",
42
+ "check:factory-selectors": "node tools/check-factory-selectors.mjs",
42
43
  "check:fee-estimate": "node tools/check-fee-estimate.mjs",
43
44
  "check:deadline-scale": "node tools/check-deadline-scale.mjs",
44
45
  "check:node-probe": "node tools/check-node-probe.mjs"
@@ -47,6 +47,8 @@ export interface FactoryQuote {
47
47
  readonly validUntil: string | number | bigint;
48
48
  readonly nonce: string | number | bigint;
49
49
  readonly xmrAmount: string | number | bigint;
50
+ /** The tip `claim` pays to a third-party sender out of the payout; zero means none. */
51
+ readonly claimTip: string | number | bigint;
50
52
  /** r || s || v over the quote digest, 65 bytes, with or without the `0x` prefix. */
51
53
  readonly signature: string;
52
54
  readonly commitHalfLocker: string;
@@ -110,6 +110,8 @@ export interface OrderQuote {
110
110
  readonly validUntil: string | number | bigint;
111
111
  readonly nonce: string | number | bigint;
112
112
  readonly xmrAmount: string | number | bigint;
113
+ /** The tip `claim` pays to a third-party sender out of the payout; zero means none. */
114
+ readonly claimTip: string | number | bigint;
113
115
  /** The escrow factory the domain names as `verifyingContract`. */
114
116
  readonly factory: string;
115
117
  /** r || s || v over the digest, 65 bytes, with or without the `0x` prefix; v is normalised to 27/28. */
@@ -32,18 +32,22 @@
32
32
  // validUntil - the quote's own expiry, IN SECONDS (the order's readyBy/t1 and block.timestamp
33
33
  // scale): a quote past it is refused regardless of the price.
34
34
  // nonce - replay protection: the very same signed quote cannot be presented twice.
35
+ // claimTip - the tip the escrow pays to whoever sends `claim`, out of the payout, in the same
36
+ // transaction (#18). It is inside the signed set so a provider cannot raise it after the
37
+ // taker has seen the quote. Zero means no tip and is the only value a buy uses.
35
38
  //
36
39
  // The domain's verifyingContract is the escrow FACTORY taken from the order: the contract that will verify the
37
40
  // quote in EVM (stage 2). The type order, the domain name and the version are part of the protocol - they may
38
41
  // change only together with the quote version.
39
42
  export const ORDER_QUOTE_DOMAIN_NAME = "NinseiEscrowFactory";
40
43
  export const ORDER_QUOTE_DOMAIN_VERSION = "1";
41
- // 8: the signed EIP-712 field set grew `xmrAmount` (#110 step 3) - the XMR the level walk gives is now covered by
42
- // the provider's signature and verified ON CHAIN. This is the request-shape generation label, and it moves WITH the
44
+ // 9: the signed EIP-712 field set grew `claimTip` (#18) - the tip a third-party sender of `claim` takes from the
45
+ // payout, covered by the provider's signature like every other condition.
46
+ // 8: the field set grew `xmrAmount` (#110 step 3). This is the request-shape generation label, and it moves WITH the
43
47
  // field set (see the note above). The DOMAIN name and the domain version string are UNCHANGED by it: they name the
44
48
  // domain (the factory address and the chain), which did not change. So here the label and the domain version string
45
49
  // move apart, and that is deliberate - the label tracks the message shape, not the domain identity.
46
- export const ORDER_QUOTE_VERSION = 8;
50
+ export const ORDER_QUOTE_VERSION = 9;
47
51
 
48
52
  // The domain type string is assembled from parts ON PURPOSE. A single literal of it is a phrase of several
49
53
  // space-separated words, and the SDK check reads such literals as user-facing text. Do not "simplify" it.
@@ -72,6 +76,7 @@ export const ORDER_QUOTE_FIELDS = [
72
76
  ["validUntil", "uint64"],
73
77
  ["nonce", "uint256"],
74
78
  ["xmrAmount", "uint256"],
79
+ ["claimTip", "uint256"],
75
80
  ];
76
81
  export const ORDER_QUOTE_TYPE = "OrderQuote(" + ORDER_QUOTE_FIELDS.map((f) => f[1] + " " + f[0]).join(",") + ")";
77
82
 
package/src/version.mjs CHANGED
@@ -6,4 +6,4 @@
6
6
  // tools/sync-version.mjs. Do not edit it by hand - set the version in package.json and run `npm run sync:version`.
7
7
  // The `version` check in tools/check.mjs refuses a mismatch, and refuses a runtime read of package.json here.
8
8
 
9
- export const SDK_VERSION = "0.45.0";
9
+ export const SDK_VERSION = "0.46.1";