@ninsei-labs/sdk 0.40.0 → 0.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/README.md +11 -0
- package/engine/core/config.js +1 -1
- package/engine/evm/factory.js +0 -12
- package/engine/evm/funding.js +6 -22
- package/engine/evm/index.js +3 -7
- package/engine/evm/permit.js +3 -3
- package/engine/evm/session.js +3 -5
- package/engine/sdk/bridge.js +1 -1
- package/index.d.ts +53 -0
- package/package.json +25 -3
- package/src/index.mjs +1 -0
- package/src/public/core.d.ts +13 -0
- package/src/public/core.mjs +6 -0
- package/src/public/evm.d.ts +184 -0
- package/src/public/evm.mjs +29 -0
- package/src/public/order.d.ts +161 -0
- package/src/public/order.mjs +22 -0
- package/src/public/recovery.d.ts +243 -0
- package/src/public/recovery.mjs +19 -0
- package/src/public/xmr.d.ts +318 -0
- package/src/public/xmr.mjs +29 -0
- package/src/quoteEip712Spec.mjs +1 -1
- package/src/swap-flow.mjs +0 -7
- package/src/version.mjs +9 -0
- package/engine/evm/depositor.js +0 -123
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,40 @@
|
|
|
1
|
+
## 0.42.0 — 2026-10-12
|
|
2
|
+
|
|
3
|
+
EVERY ENTRY THIS PACKAGE PUBLISHES CARRIES ITS OWN DECLARATIONS, AND THE SWEEP PAGE'S TWO PRIMITIVES ARE ON
|
|
4
|
+
`./xmr`. A node that imported a subpath got no types at all - only the root had them - while a sweep page kept its
|
|
5
|
+
own copies of two rules the engine already carries. Both are closed here.
|
|
6
|
+
|
|
7
|
+
- `./xmr`, `./order`, `./evm`, `./core` and `./recovery` each carry a declaration file next to their barrel
|
|
8
|
+
(`src/public/<entry>.d.ts`) declaring exactly the symbols that barrel re-exports, with the signatures read off the
|
|
9
|
+
engine modules. `./recovery` opens the recovery file a sweep page reads back: `ENVELOPE_VERSION`, `FILE_MAGIC`,
|
|
10
|
+
`PAYLOAD_VERSION`, `createRecoveryFile`, `decryptFile`, `scanStartHeight` and the rest of its 11 symbols.
|
|
11
|
+
- `./xmr` gains the sweep side: `spendKeysFromRevealedHalf` (the spend and view keys a revealed half yields, with the
|
|
12
|
+
check that `(s_a + s_b)*G` equals the order's public spend key before anything is swept) and `scalarFromMoneroHex`
|
|
13
|
+
(a Monero-notation key read as a number, never through `BigInt("0x" + key)`, which reverses the byte order).
|
|
14
|
+
- A `types` check keeps the declarations and the runtime equal in both directions: every subpath with an import or
|
|
15
|
+
default target must declare `types` (a subpath left without them must be named, with its reason, in the check's own
|
|
16
|
+
table - `./adapters` is), the declared file must exist and read as declarations, and every symbol the barrel
|
|
17
|
+
re-exports must be declared, and nothing else. `tools/check-types-teeth.mjs` is its breaking run: four patches, each
|
|
18
|
+
RED with its own named reason, each file restored byte for byte.
|
|
19
|
+
- `SDK_VERSION` is baked into `src/version.mjs` from `package.json` by `npm run sync:version`: a page cannot fetch
|
|
20
|
+
`package.json`, so the version is a value in the code, and the `version` check refuses a mismatch and refuses a
|
|
21
|
+
runtime read of the file. `engines.node` moves to `>=22`.
|
|
22
|
+
- `RouteRefusalCode` declares all 33 codes the four route providers refuse with, and a `refusals` check refuses to let
|
|
23
|
+
the provider tables and the type drift apart in either direction.
|
|
24
|
+
|
|
25
|
+
Nothing that already existed changed behaviour: the synchronous swap path is untouched.
|
|
26
|
+
|
|
27
|
+
## 0.41.0 — 2026-10-11
|
|
28
|
+
|
|
29
|
+
THE DEPOSITOR PATH IS GONE. The factory no longer offers `createOrderAndFundByDepositor`, so the package must
|
|
30
|
+
stop building a call to a selector that does not exist in the deployed factory.
|
|
31
|
+
|
|
32
|
+
- `src/swap-flow.mjs` no longer carries or relays `depositorSignature`: the funding path is the one where the
|
|
33
|
+
payer and the refund recipient are always the same address.
|
|
34
|
+
- The engine mirror loses `evm/depositor.js` (47 modules instead of 48) and the EIP-712 spec comment now names
|
|
35
|
+
the order-quote form.
|
|
36
|
+
- A consumer that meant to fund someone else's quote funds through `createOrderAndFund`.
|
|
37
|
+
|
|
1
38
|
## 0.40.0 — 2026-10-11
|
|
2
39
|
|
|
3
40
|
THE ASYNCHRONOUS ROUTE PROVIDERS REACH `latest`. This release moves the tag from 0.39.3; the demo pins this exact
|
package/README.md
CHANGED
|
@@ -31,6 +31,13 @@ value that looks like data is more dangerous than a refusal.
|
|
|
31
31
|
`index.d.ts` is the single source of truth about the API shape, and this is checked: `tools/check-sdk.mjs`
|
|
32
32
|
compares the list of what the SDK returns with what is declared in the contract (both ways).
|
|
33
33
|
|
|
34
|
+
EVERY PUBLISHED SUBPATH CARRIES ITS OWN DECLARATIONS: `.` (`index.d.ts`), `./xmr`, `./order`, `./evm`,
|
|
35
|
+
`./core` and `./recovery` (`src/public/<entry>.d.ts`). Each of them declares exactly the symbols its barrel
|
|
36
|
+
re-exports, so a typed consumer sees the same surface the runtime has, and nothing else.
|
|
37
|
+
`tools/check.mjs --only=types` keeps the two sides equal (a subpath without a `types` target, a declarations
|
|
38
|
+
file that does not read as declarations, or a symbol declared on one side only is red), and
|
|
39
|
+
`tools/check-types-teeth.mjs` is its breaking run.
|
|
40
|
+
|
|
34
41
|
```js
|
|
35
42
|
import { createNinsei } from "@ninsei-labs/sdk";
|
|
36
43
|
|
|
@@ -209,3 +216,7 @@ The tag is passed to `npm publish` explicitly (`--tag <tag>`), and `tools/npm-ta
|
|
|
209
216
|
can go wrong **by name** - a prerelease that would take `latest`, and a plain version that would slide into a
|
|
210
217
|
channel someone else owns. A manual run of the release workflow may override the tag with an input; when it does,
|
|
211
218
|
the log says so. The derivation and its refusals are covered by `node tools/npm-tag.test.mjs`.
|
|
219
|
+
|
|
220
|
+
Before the tag, the version constant moves with the version: `npm run sync:version` writes `src/version.mjs` from
|
|
221
|
+
`package.json`. The number is BAKED into the code on purpose - a page cannot fetch `package.json`, and the file
|
|
222
|
+
need not be served beside a bundle - so `npm run check` refuses both a mismatch and a runtime read of it.
|
package/engine/core/config.js
CHANGED
|
@@ -46,7 +46,7 @@ export const ENDPOINTS = Object.freeze({
|
|
|
46
46
|
// with the page's actual origin, so a hard-coded host breaks the session on any other host.
|
|
47
47
|
const PAGE_ORIGIN = typeof location !== "undefined" ? location.origin : "";
|
|
48
48
|
|
|
49
|
-
// THE EIP-712 SIGNING DOMAIN (name/version) for
|
|
49
|
+
// THE EIP-712 SIGNING DOMAIN (name/version) for a signed order quote. It is NOT a brand literal in the signer: it
|
|
50
50
|
// belongs to the registry contract (it returns DOMAIN_SEPARATOR()), so it is taken from the config here. The defaults
|
|
51
51
|
// reproduce today's result byte for byte.
|
|
52
52
|
export const SIGNING_DOMAIN = Object.freeze({ name: "NinseiEscrowFactory", version: "1" });
|
package/engine/evm/factory.js
CHANGED
|
@@ -19,9 +19,6 @@ export const FACTORY_METHODS = {
|
|
|
19
19
|
predict: "0x0824d205",
|
|
20
20
|
// create the escrow AND deposit funds in ONE transaction (a value transfer with the call).
|
|
21
21
|
createOrderAndFund: "0x4703a9d3",
|
|
22
|
-
// create the escrow and deposit when the DEPOSITOR is recovered FROM THE SIGNATURE over the quote digest:
|
|
23
|
-
// anyone may send the transaction, and `locker` is set to the signer.
|
|
24
|
-
createOrderAndFundByDepositor: "0x2acd187b",
|
|
25
22
|
};
|
|
26
23
|
|
|
27
24
|
// The OrderCreated event topic. It grew the registry address, the provider and the signing key:
|
|
@@ -129,15 +126,6 @@ export function encodeCreateOrderAndFund(quote) {
|
|
|
129
126
|
return FACTORY_METHODS.createOrderAndFund + quoteCallArgs(quote);
|
|
130
127
|
}
|
|
131
128
|
|
|
132
|
-
// CREATE AND DEPOSIT when the depositor is recovered FROM ITS SIGNATURE over the quote digest. Anyone may send
|
|
133
|
-
// the transaction, so there are TWO signers: quote.signature (the provider, checked by the escrow) and
|
|
134
|
-
// depositorSignature (the depositor, checked by the factory, which requires the recovered address to equal
|
|
135
|
-
// quote.locker). Both over ONE digest.
|
|
136
|
-
export function encodeCreateOrderAndFundByDepositor(quote, depositorSignature) {
|
|
137
|
-
return FACTORY_METHODS.createOrderAndFundByDepositor + quoteWords(quote) +
|
|
138
|
-
signatureWords(quote.signature) + signatureWords(depositorSignature) + lockerHalfWords(quote);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
129
|
// predict() answer - one address in a 32-byte word.
|
|
142
130
|
export function decodeAddress(returnedHex) {
|
|
143
131
|
const hex = String(returnedHex || "").replace(/^0x/, "");
|
package/engine/evm/funding.js
CHANGED
|
@@ -14,10 +14,7 @@
|
|
|
14
14
|
// keccak256 is passed in as a dependency, as in the address module: the caller loads the key library, not this
|
|
15
15
|
// module. Monero and Ethereum share Keccak-256, so the hash computed by the bundle matches what Solidity checks.
|
|
16
16
|
|
|
17
|
-
import { encodeCreateOrderAndFund,
|
|
18
|
-
// THE DEPOSITOR BY SIGNATURE: the signature shape comes from one place (evm/depositor.js), next to the protocol
|
|
19
|
-
// shape. Checking the shape here would be a second record of the same rule.
|
|
20
|
-
import { depositorSignatureShape } from "./depositor.js";
|
|
17
|
+
import { encodeCreateOrderAndFund, encodePredict, decodeAddress, ORDER_CREATED_TOPIC } from "./factory.js";
|
|
21
18
|
import { encodeStatus, decodeStatus } from "./escrow.js";
|
|
22
19
|
// THE FEE: rate, amount and recipient are PART OF THE SIGNED QUOTE. The factory no longer has them (the router was
|
|
23
20
|
// removed), so the amount comes from the quote and is cross-checked by the formula mirror. Without a quote there
|
|
@@ -152,7 +149,7 @@ async function waitReceipt(receiptReader, hash, { tries = 12, delayMs = 1500 } =
|
|
|
152
149
|
|
|
153
150
|
// The full funding path. Steps are returned outward so the interface can show each one: predict is a free call,
|
|
154
151
|
// createOrder and lock are transactions in the user's wallet.
|
|
155
|
-
export async function fundOrder({ factory, locker, claimer, commitHalfLocker, commitHalfClaimer, edPointLocker, edPointClaimer, edViewPointLocker, salt: providedSalt, amountWei, amountLabel, claimWindowSeconds, readyWindowSeconds, readyBy, t1, quote,
|
|
152
|
+
export async function fundOrder({ factory, locker, claimer, commitHalfLocker, commitHalfClaimer, edPointLocker, edPointClaimer, edViewPointLocker, salt: providedSalt, amountWei, amountLabel, claimWindowSeconds, readyWindowSeconds, readyBy, t1, quote, call, send, receipt, onStep }) {
|
|
156
153
|
if (!factory) throw new Error("no escrow factory address set for this network");
|
|
157
154
|
if (!locker) throw new Error("no EVM wallet connected: there is nobody to deposit");
|
|
158
155
|
if (!claimer) throw new Error("the order recipient (claimer) is not set");
|
|
@@ -243,20 +240,10 @@ export async function fundOrder({ factory, locker, claimer, commitHalfLocker, co
|
|
|
243
240
|
? " (order " + amountWei + " + fee " + fee.feeWei + ")"
|
|
244
241
|
: "";
|
|
245
242
|
onStep?.("locking " + (amountLabel || "funds") + feeWords + " in one transaction - confirm in wallet");
|
|
246
|
-
// WHICH FACTORY DOOR THE DEPOSIT GOES THROUGH
|
|
247
|
-
//
|
|
248
|
-
//
|
|
249
|
-
|
|
250
|
-
// deposits and sends, and the direct swap works as before. On neither path may the sender change the order terms
|
|
251
|
-
// - both signatures cover THE SAME quote digest.
|
|
252
|
-
if (depositorSignature !== null && depositorSignature !== undefined && depositorSignature !== "") {
|
|
253
|
-
const shape = depositorSignatureShape(depositorSignature);
|
|
254
|
-
if (!shape.ok) throw new Error("the depositor signature is unusable: " + shape.reason);
|
|
255
|
-
}
|
|
256
|
-
const relayedByDepositor = Boolean(depositorSignature);
|
|
257
|
-
const createData = relayedByDepositor
|
|
258
|
-
? encodeCreateOrderAndFundByDepositor(quoteEnc, depositorSignature)
|
|
259
|
-
: encodeCreateOrderAndFund(quoteEnc);
|
|
243
|
+
// WHICH FACTORY DOOR THE DEPOSIT GOES THROUGH. There is ONE path: createOrderAndFund, where the wallet that
|
|
244
|
+
// sends the transaction becomes the locker. The sender may not change the order terms - the quote signature
|
|
245
|
+
// covers THE SAME digest the escrow checks.
|
|
246
|
+
const createData = encodeCreateOrderAndFund(quoteEnc);
|
|
260
247
|
const createHash = await send({
|
|
261
248
|
to: factory,
|
|
262
249
|
data: createData,
|
|
@@ -300,8 +287,5 @@ export async function fundOrder({ factory, locker, claimer, commitHalfLocker, co
|
|
|
300
287
|
// THE FEE GOES OUTWARD: it went into the transaction and the deal record must keep it - otherwise the record
|
|
301
288
|
// cannot tell how much the person deposited, and it would diverge from what the chain shows.
|
|
302
289
|
return { feeWei: fee.feeWei, feeTotalWei: totalValueWei, feeBps: fee.bps, feeWasLegacyFactory: Boolean(fee.legacy),
|
|
303
|
-
// WHICH FACTORY PATH RAN: visible outside, not only in the wallet's calldata. By it the checks and the
|
|
304
|
-
// deal record know whether the ETH returns to the signer and not to the sender.
|
|
305
|
-
depositorPath: relayedByDepositor,
|
|
306
290
|
escrow: escrowToUse, predictedEscrow: escrow, escrowMismatch: Boolean(actualEscrow && actualEscrow !== String(escrow).toLowerCase()), salt, birthBlock, terms, createHash, lockHash };
|
|
307
291
|
}
|
package/engine/evm/index.js
CHANGED
|
@@ -32,9 +32,8 @@ import { fundOrder } from "./funding.js";
|
|
|
32
32
|
export * from "./factory.js";
|
|
33
33
|
export * from "./escrow.js";
|
|
34
34
|
export * from "./funding.js";
|
|
35
|
-
// The
|
|
36
|
-
//
|
|
37
|
-
export * from "./depositor.js";
|
|
35
|
+
// The permit verdict (evm/permit.js) goes through the SAME engine seam: the page and the SDK share one
|
|
36
|
+
// protocol code, not two lookalikes.
|
|
38
37
|
export * from "./permit.js";
|
|
39
38
|
// THE ASYNCHRONOUS ROUTE, EXECUTED - through the same engine seam as the decision that chooses it. The page reaches
|
|
40
39
|
// it through the bridge; the SDK carries the same module in its engine mirror (tools/build-sdk-engine.mjs).
|
|
@@ -87,13 +86,10 @@ export async function simulateEscrowFunding(tx) {
|
|
|
87
86
|
// could not pass even the first step. Neither syntax nor checks saw this, because the live path is never run.
|
|
88
87
|
// FOUR FIELDS AND TWO DEADLINES EXPLICITLY. The deadlines come ready (readyBy, t1), not computed from now:
|
|
89
88
|
// they go into the context the proof binds to, so they must be fixed before it.
|
|
90
|
-
export async function fundOrderLive({ factory, locker, claimer, commitHalfLocker, commitHalfClaimer, edPointLocker, edPointClaimer, edViewPointLocker, salt, amountWei, amountLabel, claimWindowSeconds = 3600, readyWindowSeconds = 14400, readyBy, t1, quote,
|
|
89
|
+
export async function fundOrderLive({ factory, locker, claimer, commitHalfLocker, commitHalfClaimer, edPointLocker, edPointClaimer, edViewPointLocker, salt, amountWei, amountLabel, claimWindowSeconds = 3600, readyWindowSeconds = 14400, readyBy, t1, quote, onStep }) {
|
|
91
90
|
return fundOrder({
|
|
92
91
|
factory,
|
|
93
92
|
quote, // signed quote: carries the fee, registry address and provider
|
|
94
|
-
// THE DEPOSITOR SIGNATURE OVER THE QUOTE DIGEST. Present: funding goes via
|
|
95
|
-
// createOrderAndFundByDepositor and anyone may send the transaction. Absent: the old path.
|
|
96
|
-
depositorSignature,
|
|
97
93
|
locker,
|
|
98
94
|
claimer,
|
|
99
95
|
commitHalfLocker,
|
package/engine/evm/permit.js
CHANGED
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
// cannot cover the gas - the swap settles first and the escrow is funded after it. See usdcWithoutEthVerdict below
|
|
23
23
|
// and www/js/evm/asyncRoute.js - and it is a NAMED refusal only when no provider serves the network.
|
|
24
24
|
//
|
|
25
|
-
//
|
|
26
|
-
// not pull the token. Pulling USDC needs a separate TOKEN allowance (approve or permit) addressed to the puller.
|
|
25
|
+
// A RELAYED ORDER NEEDS A SEPARATE TOKEN ALLOWANCE. Letting a relayer SEND the order-creation transaction does
|
|
26
|
+
// not let it pull the token. Pulling USDC needs a separate TOKEN allowance (approve or permit) addressed to the puller.
|
|
27
27
|
|
|
28
28
|
// THE BALANCE-DRIVEN PATH CHOICE. Whether a provider serves a network ASYNCHRONOUSLY (an intent auction whose
|
|
29
29
|
// settlement arrives later - see www/js/evm/asyncRoute.js) is what decides the path for a person with no own
|
|
@@ -179,7 +179,7 @@ export function usdcWithoutEthVerdict({
|
|
|
179
179
|
if (!isAddr(orchestrator)) {
|
|
180
180
|
return {
|
|
181
181
|
ok: false, blocked: true, kind: "no-orchestrator", path: null,
|
|
182
|
-
reason: "no executor contract: only a contract could pull a token by the user signature and deposit native into the escrow in one transaction, and this build has none
|
|
182
|
+
reason: "no executor contract: only a contract could pull a token by the user signature and deposit native into the escrow in one transaction, and this build has none",
|
|
183
183
|
};
|
|
184
184
|
}
|
|
185
185
|
if (permitSupported === false) {
|
package/engine/evm/session.js
CHANGED
|
@@ -551,12 +551,10 @@ export async function signMessage(message) {
|
|
|
551
551
|
}
|
|
552
552
|
|
|
553
553
|
// SIGNING TYPED DATA with the wallet (EIP-712, eth_signTypedData_v4). This is also NOT a transaction: the
|
|
554
|
-
// signature moves no money and costs no gas, so a
|
|
555
|
-
// depositor lets the factory record his address in the order, and anyone may send the transaction for him
|
|
556
|
-
// (the createOrderAndFundByDepositor path).
|
|
554
|
+
// signature moves no money and costs no gas, so a wallet without its own ETH can give it.
|
|
557
555
|
//
|
|
558
|
-
// WHAT EXACTLY IS SIGNED IS DECIDED BY THE CALLER: ready typed data arrives here
|
|
559
|
-
//
|
|
556
|
+
// WHAT EXACTLY IS SIGNED IS DECIDED BY THE CALLER: ready typed data arrives here as an object, not a string.
|
|
557
|
+
// Assembling the shape here would be a second record of the protocol next to the real one.
|
|
560
558
|
export async function signTypedData(typedData) {
|
|
561
559
|
if (!provider || state.status !== "connected") {
|
|
562
560
|
throw new Error("wallet not connected: typed-data signing is impossible");
|
package/engine/sdk/bridge.js
CHANGED
|
@@ -68,7 +68,7 @@ import { height as nodeHeightValue } from "../monero/node.js";
|
|
|
68
68
|
|
|
69
69
|
// THE CORE ADDRESS AS A STRING - THE BUILT BUNDLE UNDER www/: the page gets the core as ONE module, not a pile
|
|
70
70
|
// of sdk/ sources; ?v=<hash> keeps the cache fresh.
|
|
71
|
-
const SDK_BUNDLE = "/assets/vendors/sdk/sdk-browser.js?v=
|
|
71
|
+
const SDK_BUNDLE = "/assets/vendors/sdk/sdk-browser.js?v=69d79c13";
|
|
72
72
|
|
|
73
73
|
// A NAMED SEAM FOR CHECKS - THE SAME TECHNIQUE AS sdkBookUse BELOW. The asynchronous path is driven against a
|
|
74
74
|
// stand-in order book (the demo network has no provider), and the ordinary escrow funding that follows a settled
|
package/index.d.ts
CHANGED
|
@@ -494,6 +494,58 @@ export interface QuoteOffer {
|
|
|
494
494
|
readonly networks: { readonly asset: string | null; readonly currency: string | null; readonly stated: boolean };
|
|
495
495
|
}
|
|
496
496
|
|
|
497
|
+
/**
|
|
498
|
+
* Every code a route provider of this package refuses with.
|
|
499
|
+
*
|
|
500
|
+
* The providers already name them in their own tables (`COW_REFUSAL_CODES` and its three siblings), and the
|
|
501
|
+
* battery proves each of those is reachable; what was missing was the TYPE, so an interface could enumerate
|
|
502
|
+
* them instead of meeting a string it cannot switch on. `broken` is shared by all four: it is the code for a
|
|
503
|
+
* case a provider could not classify.
|
|
504
|
+
*
|
|
505
|
+
* These are OUR codes, and they are what a person's message should be phrased from. A provider's OWN reason
|
|
506
|
+
* stays opaque on purpose (`QuoteRefusal.why`): that is data the provider sent, not a name of ours, so it is
|
|
507
|
+
* deliberately NOT part of this union.
|
|
508
|
+
*/
|
|
509
|
+
export type RouteRefusalCode =
|
|
510
|
+
// cow
|
|
511
|
+
| "cow-bad-order"
|
|
512
|
+
| "cow-bad-response"
|
|
513
|
+
| "cow-not-settled"
|
|
514
|
+
| "cow-order-unknown"
|
|
515
|
+
| "cow-refused"
|
|
516
|
+
| "cow-sign-failed"
|
|
517
|
+
| "cow-unknown-network"
|
|
518
|
+
| "cow-unreachable"
|
|
519
|
+
// kyber
|
|
520
|
+
| "kyber-bad-order"
|
|
521
|
+
| "kyber-bad-response"
|
|
522
|
+
| "kyber-not-settled"
|
|
523
|
+
| "kyber-order-unknown"
|
|
524
|
+
| "kyber-refused"
|
|
525
|
+
| "kyber-sign-failed"
|
|
526
|
+
| "kyber-unknown-network"
|
|
527
|
+
| "kyber-unreachable"
|
|
528
|
+
// uniswapx
|
|
529
|
+
| "uniswapx-bad-order"
|
|
530
|
+
| "uniswapx-bad-response"
|
|
531
|
+
| "uniswapx-no-submitter"
|
|
532
|
+
| "uniswapx-not-settled"
|
|
533
|
+
| "uniswapx-order-unknown"
|
|
534
|
+
| "uniswapx-refused"
|
|
535
|
+
| "uniswapx-sign-failed"
|
|
536
|
+
| "uniswapx-unknown-network"
|
|
537
|
+
| "uniswapx-unreachable"
|
|
538
|
+
// uniswap-relay
|
|
539
|
+
| "uniswap-relay-bad-order"
|
|
540
|
+
| "uniswap-relay-bad-response"
|
|
541
|
+
| "uniswap-relay-no-discovery"
|
|
542
|
+
| "uniswap-relay-not-settled"
|
|
543
|
+
| "uniswap-relay-sign-failed"
|
|
544
|
+
| "uniswap-relay-unknown-network"
|
|
545
|
+
| "uniswap-relay-unreachable"
|
|
546
|
+
// shared
|
|
547
|
+
| "broken";
|
|
548
|
+
|
|
497
549
|
export interface QuoteRefusal {
|
|
498
550
|
readonly providerId: string;
|
|
499
551
|
readonly displayName: string | null;
|
|
@@ -1373,6 +1425,7 @@ export declare class SdkError extends Error {
|
|
|
1373
1425
|
}
|
|
1374
1426
|
|
|
1375
1427
|
/** The step codes the SDK works with (`state.step`): the same list as the `StepCode` type. */
|
|
1428
|
+
export declare const SDK_VERSION: string;
|
|
1376
1429
|
export declare const STEP_CODES: readonly StepCode[];
|
|
1377
1430
|
/** The check codes a preflight verdict names (`checks[].code`): the same list as the `CheckCode` type. */
|
|
1378
1431
|
export declare const CHECK_CODES: readonly CheckCode[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ninsei-labs/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.42.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public",
|
|
6
6
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
"check:mirror": "node tools/check.mjs --only=mirror",
|
|
19
19
|
"check:pack": "node tools/check.mjs --only=pack",
|
|
20
20
|
"check:secrets": "node tools/check.mjs --only=secrets",
|
|
21
|
-
"check:
|
|
21
|
+
"check:types": "node tools/check.mjs --only=types",
|
|
22
|
+
"check:types-teeth": "node tools/check-types-teeth.mjs",
|
|
23
|
+
"check:sweep": "node tools/sweep-refusal.test.mjs", "sync:version": "node tools/sync-version.mjs"
|
|
22
24
|
},
|
|
23
25
|
"types": "./index.d.ts",
|
|
24
26
|
"exports": {
|
|
@@ -29,6 +31,26 @@
|
|
|
29
31
|
},
|
|
30
32
|
"./adapters": {
|
|
31
33
|
"default": "./src/adapters/index.mjs"
|
|
34
|
+
},
|
|
35
|
+
"./xmr": {
|
|
36
|
+
"types": "./src/public/xmr.d.ts",
|
|
37
|
+
"default": "./src/public/xmr.mjs"
|
|
38
|
+
},
|
|
39
|
+
"./order": {
|
|
40
|
+
"types": "./src/public/order.d.ts",
|
|
41
|
+
"default": "./src/public/order.mjs"
|
|
42
|
+
},
|
|
43
|
+
"./evm": {
|
|
44
|
+
"types": "./src/public/evm.d.ts",
|
|
45
|
+
"default": "./src/public/evm.mjs"
|
|
46
|
+
},
|
|
47
|
+
"./core": {
|
|
48
|
+
"types": "./src/public/core.d.ts",
|
|
49
|
+
"default": "./src/public/core.mjs"
|
|
50
|
+
},
|
|
51
|
+
"./recovery": {
|
|
52
|
+
"types": "./src/public/recovery.d.ts",
|
|
53
|
+
"default": "./src/public/recovery.mjs"
|
|
32
54
|
}
|
|
33
55
|
},
|
|
34
56
|
"files": [
|
|
@@ -40,7 +62,7 @@
|
|
|
40
62
|
"LICENSE"
|
|
41
63
|
],
|
|
42
64
|
"engines": {
|
|
43
|
-
"node": ">=
|
|
65
|
+
"node": ">=22"
|
|
44
66
|
},
|
|
45
67
|
"keywords": [
|
|
46
68
|
"xmr",
|
package/src/index.mjs
CHANGED
|
@@ -270,6 +270,7 @@ export function createNinsei(options) {
|
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
export { SdkError, ERROR_CODES, STEP_CODES, CHECK_CODES };
|
|
273
|
+
export { SDK_VERSION } from "./version.mjs";
|
|
273
274
|
|
|
274
275
|
// THE LEVEL WALK IS ON THE PACKAGE SURFACE. A maker publishes a level set (chunks of volume with their own prices)
|
|
275
276
|
// and both the node and the client compute a firm price by WALKING it - the client must reach the SAME price before
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// DECLARATIONS FOR THE `./core` ENTRY.
|
|
2
|
+
//
|
|
3
|
+
// src/public/core.mjs only NAMES what the engine already offers; these declarations NAME the same thing for a
|
|
4
|
+
// TypeScript consumer. Every signature is copied from the engine module the barrel re-exports
|
|
5
|
+
// (`../../engine/core/format.js`), and a symbol the barrel does not re-export is not declared here.
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The number a person entered, in units, or null when the text carries no number.
|
|
9
|
+
*
|
|
10
|
+
* A comma without a dot is the decimal separator ("0,05" is 0.05) and a comma beside a dot separates thousands
|
|
11
|
+
* ("1,234.5" is 1234.5), so what the field shows and what the rest of the code computes cannot diverge.
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseAmount(raw: string | number | null | undefined): number | null;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// NEUTRAL HELPERS that the entries above and their consumers share: a decimal amount typed as text, in units.
|
|
2
|
+
//
|
|
3
|
+
// One function today. It is a separate entry because it belongs to neither the XMR side nor the EVM side, and a
|
|
4
|
+
// consumer forced to import it from either would learn a false lesson about where it lives.
|
|
5
|
+
|
|
6
|
+
export { parseAmount } from "../../engine/core/format.js";
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// DECLARATIONS FOR THE `./evm` ENTRY: factory calldata and the arithmetic of a claim.
|
|
2
|
+
//
|
|
3
|
+
// src/public/evm.mjs only NAMES what the engine already offers; these declarations NAME the same thing for a
|
|
4
|
+
// TypeScript consumer. Every signature is copied from the modules the barrel re-exports
|
|
5
|
+
// (`../../engine/evm/factory.js`, `../../engine/evm/claimGas.js`), and a symbol the barrel does not re-export is
|
|
6
|
+
// not declared here.
|
|
7
|
+
|
|
8
|
+
/** The factory's calldata selectors, computed from the contract's own signatures. */
|
|
9
|
+
export declare const FACTORY_METHODS: {
|
|
10
|
+
readonly predict: string;
|
|
11
|
+
readonly createOrderAndFund: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/** The factory getters read to check the single shared escrow implementation every clone runs. */
|
|
15
|
+
export declare const FACTORY_READS: {
|
|
16
|
+
readonly implementation: string;
|
|
17
|
+
readonly implementationCodeHash: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** The OrderCreated event topic: the same value the contract declares. */
|
|
21
|
+
export declare const ORDER_CREATED_TOPIC: string;
|
|
22
|
+
|
|
23
|
+
/** The signed quote fields IN CONTRACT ORDER. The order is part of the ABI: a rearrangement is another call. */
|
|
24
|
+
export declare const QUOTE_FIELDS: readonly string[];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Everything `predict` and `createOrderAndFund` encode: the signed quote, the provider's signature, and the three
|
|
28
|
+
* depositor fields that travel as separate arguments (they go into the escrow's init code).
|
|
29
|
+
*/
|
|
30
|
+
export interface FactoryQuote {
|
|
31
|
+
readonly provider: string;
|
|
32
|
+
readonly quoteKey: string;
|
|
33
|
+
readonly registry: string;
|
|
34
|
+
readonly cashier: string;
|
|
35
|
+
readonly locker: string;
|
|
36
|
+
readonly claimer: string;
|
|
37
|
+
readonly amount: string | number | bigint;
|
|
38
|
+
readonly readyBy: string | number | bigint;
|
|
39
|
+
readonly t1: string | number | bigint;
|
|
40
|
+
readonly salt: string;
|
|
41
|
+
readonly commitHalfClaimer: string;
|
|
42
|
+
readonly edPointClaimer: string;
|
|
43
|
+
readonly chainId: string | number | bigint;
|
|
44
|
+
readonly feeBps: string | number | bigint;
|
|
45
|
+
readonly fee: string | number | bigint;
|
|
46
|
+
readonly feeRecipient: string;
|
|
47
|
+
readonly validUntil: string | number | bigint;
|
|
48
|
+
readonly nonce: string | number | bigint;
|
|
49
|
+
readonly xmrAmount: string | number | bigint;
|
|
50
|
+
/** r || s || v over the quote digest, 65 bytes, with or without the `0x` prefix. */
|
|
51
|
+
readonly signature: string;
|
|
52
|
+
readonly commitHalfLocker: string;
|
|
53
|
+
readonly edPointLocker: string;
|
|
54
|
+
readonly edViewPointLocker: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* One calldata word: the value right-aligned to `bytes` (32 unless another width is asked for). Decimal digits are
|
|
59
|
+
* turned into hex as a NUMBER, so padding cannot change the value. Refuses anything that is neither.
|
|
60
|
+
*/
|
|
61
|
+
export declare function word(value: string | number | bigint, bytes?: number): string;
|
|
62
|
+
|
|
63
|
+
/** The `predict` call: the argument set matches `encodeCreateOrderAndFund` byte for byte. */
|
|
64
|
+
export declare function encodePredict(quote: FactoryQuote): string;
|
|
65
|
+
|
|
66
|
+
/** The `createOrderAndFund` call: creation and deposit in one transaction. */
|
|
67
|
+
export declare function encodeCreateOrderAndFund(quote: FactoryQuote): string;
|
|
68
|
+
|
|
69
|
+
/** `predict()`'s answer - one address in a 32-byte word. */
|
|
70
|
+
export declare function decodeAddress(returnedHex: string): string;
|
|
71
|
+
|
|
72
|
+
/** `implementation()`'s answer. */
|
|
73
|
+
export declare function decodeImplementation(returnedHex: string): string;
|
|
74
|
+
|
|
75
|
+
/** `implementationCodeHash()`'s answer - one bytes32 word. */
|
|
76
|
+
export declare function decodeImplementationCodeHash(returnedHex: string): string;
|
|
77
|
+
|
|
78
|
+
/** The getter's calldata by name: it takes no arguments, so the selector is the whole call. */
|
|
79
|
+
export declare function encodeFactoryRead(name: "implementation" | "implementationCodeHash"): string;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The verdict on the escrow implementation, by the same three conditions the factory applies at order creation:
|
|
83
|
+
* the address is named and its code is non-empty, the recorded hash matches, and the hash is in the interface
|
|
84
|
+
* policy if one is set. Refusals are named, never silent.
|
|
85
|
+
*/
|
|
86
|
+
export type FactoryImplementationVerdict =
|
|
87
|
+
| {
|
|
88
|
+
readonly ok: false;
|
|
89
|
+
readonly why: string;
|
|
90
|
+
readonly codeHash?: string;
|
|
91
|
+
readonly got?: string;
|
|
92
|
+
readonly want?: string;
|
|
93
|
+
}
|
|
94
|
+
| { readonly ok: true; readonly codeHash: string };
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* What the verdict is read from. `code` is the implementation's on-chain code; `hashCode` recomputes it when the
|
|
98
|
+
* carrier can hash bytes. An unreadable base is not zero: the reason names what was missing.
|
|
99
|
+
*/
|
|
100
|
+
export interface FactoryImplementationRequest {
|
|
101
|
+
readonly implementation?: string;
|
|
102
|
+
readonly implementationCodeHash?: string;
|
|
103
|
+
readonly code?: string | null;
|
|
104
|
+
readonly hashCode?: ((code: string) => string) | null;
|
|
105
|
+
/** The interface's own list of known builds. An empty or absent list means "no policy". */
|
|
106
|
+
readonly policy?: readonly string[] | null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** The verdict on the implementation, from the fields alone. */
|
|
110
|
+
export declare function verifyFactoryImplementation(request?: FactoryImplementationRequest): FactoryImplementationVerdict;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The verdict read at the factory itself: `implementation()`/`implementationCodeHash()` and the code at the
|
|
114
|
+
* implementation's address, then the same three conditions. The chain is injected - `read` is
|
|
115
|
+
* `eth_call({to, data}) -> hex` and `code` is `eth_getCode(address) -> hex` - so the module performs no call of
|
|
116
|
+
* its own. An unreadable pair or code is a named refusal, because an old factory without these getters reverts.
|
|
117
|
+
*/
|
|
118
|
+
export type FactoryImplementationOnChainVerdict =
|
|
119
|
+
| {
|
|
120
|
+
readonly ok: false;
|
|
121
|
+
readonly why: string;
|
|
122
|
+
readonly factory?: string;
|
|
123
|
+
readonly implementation?: string;
|
|
124
|
+
readonly codeHash?: string;
|
|
125
|
+
readonly got?: string;
|
|
126
|
+
readonly want?: string;
|
|
127
|
+
}
|
|
128
|
+
| { readonly ok: true; readonly codeHash: string; readonly implementation: string; readonly implementationCodeHash: string };
|
|
129
|
+
|
|
130
|
+
export interface FactoryImplementationOnChainRequest {
|
|
131
|
+
readonly factory?: string;
|
|
132
|
+
readonly read?: (request: { readonly to: string; readonly data: string }) => Promise<string>;
|
|
133
|
+
readonly code?: (address: string) => Promise<string>;
|
|
134
|
+
readonly policy?: readonly string[] | null;
|
|
135
|
+
readonly hashCode?: ((code: string) => string) | null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** The pair read at the factory, with the same verdict the factory applies. */
|
|
139
|
+
export declare function verifyFactoryImplementationOnChain(
|
|
140
|
+
request?: FactoryImplementationOnChainRequest,
|
|
141
|
+
): Promise<FactoryImplementationOnChainVerdict>;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The claim gas limit, MEASURED on a live chain with a margin: an honest claim costs 120 721 gas. The same
|
|
145
|
+
* number on both sides of the reverse swap.
|
|
146
|
+
*/
|
|
147
|
+
export declare const CLAIM_GAS_LIMIT: bigint;
|
|
148
|
+
|
|
149
|
+
/** The network's tip floor. Zero where the network needs no tip, which is policy and not a constant here. */
|
|
150
|
+
export declare const CLAIM_TIP_FLOOR_WEI: bigint;
|
|
151
|
+
|
|
152
|
+
/** Margin for base-fee growth between the ticket and the claim, in basis points, as a node setting. */
|
|
153
|
+
export declare const DEFAULT_GIFT_RESERVE_BPS: bigint;
|
|
154
|
+
|
|
155
|
+
/** The fee of a claim in EIP-1559 terms: `2 x baseFee + tip`, tip not below the floor. */
|
|
156
|
+
export interface ClaimMaxFeeRequest {
|
|
157
|
+
readonly baseFeeWei?: string | number | bigint | null;
|
|
158
|
+
readonly priorityFeeWei?: string | number | bigint | null;
|
|
159
|
+
readonly minTipWei?: string | number | bigint | null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** `maxFeePerGas`, or null when the base fee is unreadable or zero (no fee fields is not a zero fee). */
|
|
163
|
+
export declare function claimMaxFeePerGasWei(request?: ClaimMaxFeeRequest): bigint | null;
|
|
164
|
+
|
|
165
|
+
/** What the balance needs for the claim. */
|
|
166
|
+
export interface ClaimRequiredRequest {
|
|
167
|
+
readonly gasLimit?: string | number | bigint | null;
|
|
168
|
+
readonly maxFeePerGasWei?: string | number | bigint | null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** `gasLimit x maxFeePerGas`, or null when either factor is unmeasured. */
|
|
172
|
+
export declare function claimRequiredWei(request?: ClaimRequiredRequest): bigint | null;
|
|
173
|
+
|
|
174
|
+
/** The gift the node sends to a claimer who holds no native coin. */
|
|
175
|
+
export interface ClaimGasGiftRequest {
|
|
176
|
+
readonly baseFeeWei?: string | number | bigint | null;
|
|
177
|
+
readonly priorityFeeWei?: string | number | bigint | null;
|
|
178
|
+
readonly minTipWei?: string | number | bigint | null;
|
|
179
|
+
readonly gasLimit?: string | number | bigint | null;
|
|
180
|
+
readonly reserveBps?: string | number | bigint | null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** The claim cost the client requires, plus the reserve margin. The ONE computation of the gift size. */
|
|
184
|
+
export declare function claimGasGiftWei(request?: ClaimGasGiftRequest): bigint | null;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// THE EVM SIDE: factory calldata and the arithmetic of a claim.
|
|
2
|
+
//
|
|
3
|
+
// A node verifies a deployed factory against the implementation it was told about, and prices the gas gift for
|
|
4
|
+
// a claim. Both used to be reachable only through the page. Names only.
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
FACTORY_METHODS,
|
|
8
|
+
FACTORY_READS,
|
|
9
|
+
ORDER_CREATED_TOPIC,
|
|
10
|
+
QUOTE_FIELDS,
|
|
11
|
+
decodeAddress,
|
|
12
|
+
decodeImplementation,
|
|
13
|
+
decodeImplementationCodeHash,
|
|
14
|
+
encodeCreateOrderAndFund,
|
|
15
|
+
encodeFactoryRead,
|
|
16
|
+
encodePredict,
|
|
17
|
+
verifyFactoryImplementation,
|
|
18
|
+
verifyFactoryImplementationOnChain,
|
|
19
|
+
word,
|
|
20
|
+
} from "../../engine/evm/factory.js";
|
|
21
|
+
|
|
22
|
+
export {
|
|
23
|
+
CLAIM_GAS_LIMIT,
|
|
24
|
+
CLAIM_TIP_FLOOR_WEI,
|
|
25
|
+
DEFAULT_GIFT_RESERVE_BPS,
|
|
26
|
+
claimGasGiftWei,
|
|
27
|
+
claimMaxFeePerGasWei,
|
|
28
|
+
claimRequiredWei,
|
|
29
|
+
} from "../../engine/evm/claimGas.js";
|