Skip to content

Improve API_REQUIREMENTS.md - #442

Merged
paullinator merged 1 commit into
masterfrom
jon/api-req-update
Feb 27, 2026
Merged

Improve API_REQUIREMENTS.md#442
paullinator merged 1 commit into
masterfrom
jon/api-req-update

Conversation

@j0ntz

@j0ntz j0ntz commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

CHANGELOG

Does this branch warrant an entry to the CHANGELOG?

  • Yes
  • No

Dependencies

none

Description

Rewrites API_REQUIREMENTS.md for clarity and conciseness.

Document-wide:

  • Added explicit "all requirements are mandatory" statement
  • Replaced the prose "key requirements include" intro with a numbered Table of Contents
  • Bolded must throughout for RFC-style obligation language
  • Removed informal phrasing ("I.e.", "Note that ...") and tightened wording
  • Fixed a malformed markdown link in section 2 and normalized formatting

New "General Principles" section:

  • Amount Representation guidance preferring native (smallest indivisible) units (satoshis, wei, lamports, etc.) with a reference table
  • Native units are a should, not a must. If an API does not use them, it must clearly document which unit convention applies to every amount field
  • Cross-referenced from Error Handling (limit amounts), Quoting (quoted amounts), and Reporting API (transaction amounts), each worded as "should use native units where possible"
  • Note: the native-unit principle is new in this PR. The source requirements doc ("2025 Exchange Provider API Requirements") uses display units in every example and states no unit convention. The Reporting API cross-reference is also internally inconsistent here: section 6 asks for native units while keeping the display-unit example values (118123 and 1.01). Section 6 was later corrected to display units in ff4cc3d, with StandardTx's asSafeNumber typing as the reason

Error Handling (section 3), major expansion:

  • Added required error types table (region, asset unsupported, over/under limit)
  • Added structured error format with correct and incorrect examples
  • Added limit error field definitions table with types and descriptions
  • Converted the example limit amounts to native units to match the new principle: 9.789 BTC became 978900000 satoshis, and 1000000 USDT became 1000000000000 micro-units. Both values are correct conversions of the previous display-unit example. The satoshi value carries a trailing comment reading 0.009789 BTC, which is off by 1000x from the value it annotates, corrected in EdgeApp/edge-exchange-plugins#489

Chain and Token Identification (section 1):

  • Added tokenId to the EVM example (was missing)
  • Separated into labeled non-EVM and EVM examples
  • Dropped the stated rationale for the numeric EVM chainId requirement (that it lets a new EVM work with no new mapping entry). The requirement itself stayed must

Reporting API (section 6):

  • Added field descriptions table (info was previously only in JSON comments)
  • Moved inline JSON comments to the table
  • Named the pagination parameters explicitly: startDate, endDate, limit

Requirements strengthened from "should" to "must":

  • Section 7, Account Activation
  • Section 8, Affiliate Revenue Withdrawal: supported withdrawal assets, and additional authentication on withdrawal-address changes
  • Section 9, User Authentication: authenticating via authKey, and account creation via API. Also adds a new requirement that account creation not depend on an external registration page
  • Section 10, Regional and Fiat Currency Support, which now also requires structured errors per section 3
  • Section 11, KYC Information
  • Section 13, Verification: submitting provider-generated codes, and signalling missing or outdated KYC info

Every remaining "should" in the document after this PR is a deliberate soft preference (the native-units guidance in sections 3, 4 and 6, plus "Edge should not be required to initiate withdrawals" in section 8), not leftover informal phrasing.

Other sections:

  • Affiliate Revenue Withdrawal reformatted as bullet points
  • Off-Ramp Flow reformatted as bullet points

@j0ntz
j0ntz force-pushed the jon/api-req-update branch from f4dc046 to 188759a Compare February 27, 2026 20:09
@paullinator
paullinator merged commit 49e3fdb into master Feb 27, 2026
2 checks passed
@j0ntz j0ntz mentioned this pull request Aug 20, 2026
2 tasks
j0ntz added a commit that referenced this pull request Aug 20, 2026
ff4cc3d reached master through a local branch merge with no pull
request, so its changes are the only part of this document that was
never reviewed. Most of that commit is an improvement. Six requirements
went the other way, each of them weaker than both PR #442 and Paul's
original "2025 Exchange Provider API Requirements", which is the source
this document was transcribed from. None are mentioned in its commit
message.

Section 1, EVM chainId: back to must. The source doc gives the reason
this is not a preference, and it was dropped along with the obligation:
numeric chain ids let a newly listed EVM work the day it is added, with
no new mapping entry.

Section 6, pagination: back to a required start date, end date, and
record count. ff4cc3d made date filtering one of three acceptable
approaches, so an offset-only API conformed. Without a date range the
reporting pipeline cannot bound a query to the window it has not yet
ingested.

Section 6, required fields: depositAddress/payoutAddress,
depositTxid/payoutTxid, depositTokenId/payoutTokenId and
depositEvmChainId/payoutEvmChainId are required again, and the chain
identifier is back in the table. ff4cc3d dropped it entirely, which left
tokenId as the only asset identifier and no way to tell which chain a
contract address belongs to.

Section 9: the authKey is Edge-generated and cryptographically random
again. ff4cc3d replaced it with "a unique per-user identifier" and
listed provider-created sessions as an acceptable implementation, which
inverts who issues the identifier.

Section 14: accepting a return URI is the requirement again, with deep
links and completion signals kept as the fallback for widgets that
cannot take one, rather than as equal alternatives.

Section 4's unit-convention "must" is bolded to match section 3.
j0ntz added a commit that referenced this pull request Aug 20, 2026
Only two pull requests have ever touched this document: #442, approved,
and this one. ff4cc3d reached master through a local branch merge
(752768f) with no PR, so it is the only unreviewed content in the file,
and it is the only revision that relaxed anything. None of the changes
below appear in its commit message, which describes its scope as mapping
errors to edge-core-js classes, naming the integration points, and
expanding the Reporting API section.

The source of truth is Paul's "2025 Exchange Provider API Requirements"
doc, which ef51604 transcribes almost verbatim.

Native units are now the single convention, stated once:

The document had drifted into per-section conventions. Section 3 asked
for display units and called itself "the one place the native-unit
preference does not apply", section 6 asked for display units citing
StandardTx's asSafeNumber typing, and sections 4 and 6 asked for native
units. Amount Representation now states outright that it governs every
amount field and that no section carries a different one, sections 3, 4
and 6 all read the same, and the asSafeNumber fact moves to where it
belongs: it constrains Edge's storage, not the provider's wire format,
and the reporting plugin already converts.

The source doc states no convention at all. Its only amounts example
sits inside the error-handling section, whose purpose is to show that
every applicable error returns in one response, so the display units in
it are incidental to the point rather than a decision. Reading a
convention out of an incidental example is what drifted twice.

Section 3's example also loses a 1000x error. The December example was
9.789 BTC against 1,000,000 USDT, two sides of the same $1M cap. #442
converted both correctly (978900000 satoshis, 1000000000000 micro-units)
but labelled the satoshi value "0.009789 BTC" in its comment. ff4cc3d
resolved the value/comment mismatch by trusting the comment, rewriting
the limit as the display string "0.009789", which is roughly $1,000
against a 1,000,000 USDT destination. The value is restored and the
comment corrected.

Six requirements weakened in ff4cc3d, each weaker than both #442 and the
source doc, are restored:

- Section 1, EVM chainId: back to must, with the reason the source doc
  gives and ff4cc3d dropped along with the obligation. Numeric chain ids
  let a newly listed EVM work the day it is added, with no new mapping
  entry.
- Section 6, pagination: back to a required start date, end date and
  record count. ff4cc3d made date filtering one of three acceptable
  approaches, so an offset-only API conformed.
- Section 6, required fields: depositAddress/payoutAddress,
  depositTxid/payoutTxid, depositTokenId/payoutTokenId and
  depositEvmChainId/payoutEvmChainId are required again.
- Section 6, chain identifier: ff4cc3d rewrote the table around
  StandardTx names and lost sourceNetwork/destinationNetwork with no
  replacement row, leaving tokenId as the only asset identifier and no
  way to tell which chain a contract address belongs to. StandardTx has
  the field as depositChainPluginId/payoutChainPluginId.
- Section 9: the authKey is Edge-generated and cryptographically random
  again. ff4cc3d replaced it with "a unique per-user identifier" and
  listed provider-created sessions as acceptable, inverting who issues
  it.
- Section 14: accepting a return URI is the requirement again, with deep
  links and completion signals as the fallback for widgets that cannot
  take one rather than as equal alternatives.

Every example now carries native amounts annotated with their display
equivalent, and native amounts are specified as JSON strings, since a
high-decimal asset in native units runs past the safe integer range.
j0ntz added a commit that referenced this pull request Aug 20, 2026
ff4cc3d is the only revision of this document that relaxed anything,
and none of the changes below appear in its commit message, which
describes its scope as mapping errors to edge-core-js classes, naming
the integration points, and expanding the Reporting API section.

The reference point throughout is Paul's "2025 Exchange Provider API
Requirements" doc, which ef51604 transcribes almost verbatim.

Native units are now the single convention, stated once:

The document had drifted into per-section conventions. Section 3 asked
for display units and called itself "the one place the native-unit
preference does not apply", section 6 asked for display units citing
StandardTx's asSafeNumber typing, and sections 4 and 6 asked for native
units. Amount Representation now states outright that it governs every
amount field and that no section carries a different one, sections 3, 4
and 6 all read the same, and the asSafeNumber fact moves to where it
belongs: it constrains Edge's storage, not the provider's wire format,
and the reporting plugin already converts.

The source doc states no convention at all. Its only amounts example
sits inside the error-handling section, whose purpose is to show that
every applicable error returns in one response, so the display units in
it are incidental to the point rather than a decision. Reading a
convention out of an incidental example is what drifted twice.

Section 3's example also loses a 1000x error. The December example was
9.789 BTC against 1,000,000 USDT, two sides of the same $1M cap. #442
converted both correctly (978900000 satoshis, 1000000000000 micro-units)
but labelled the satoshi value "0.009789 BTC" in its comment. ff4cc3d
resolved the value/comment mismatch by trusting the comment, rewriting
the limit as the display string "0.009789", which is roughly $1,000
against a 1,000,000 USDT destination. The value is restored and the
comment corrected.

Six requirements weakened in ff4cc3d, each weaker than both #442 and the
source doc, are restored:

- Section 1, EVM chainId: back to must, with the reason the source doc
  gives and ff4cc3d dropped along with the obligation. Numeric chain ids
  let a newly listed EVM work the day it is added, with no new mapping
  entry.
- Section 6, pagination: back to a required start date, end date and
  record count. ff4cc3d made date filtering one of three acceptable
  approaches, so an offset-only API conformed.
- Section 6, required fields: depositAddress/payoutAddress,
  depositTxid/payoutTxid, depositTokenId/payoutTokenId and
  depositEvmChainId/payoutEvmChainId are required again.
- Section 6, chain identifier: ff4cc3d rewrote the table around
  StandardTx names and lost sourceNetwork/destinationNetwork with no
  replacement row, leaving tokenId as the only asset identifier and no
  way to tell which chain a contract address belongs to. StandardTx has
  the field as depositChainPluginId/payoutChainPluginId.
- Section 9: the authKey is Edge-generated and cryptographically random
  again. ff4cc3d replaced it with "a unique per-user identifier" and
  listed provider-created sessions as acceptable, inverting who issues
  it.
- Section 14: accepting a return URI is the requirement again, with deep
  links and completion signals as the fallback for widgets that cannot
  take one rather than as equal alternatives.

Every example now carries native amounts annotated with their display
equivalent, and native amounts are specified as JSON strings, since a
high-decimal asset in native units runs past the safe integer range.
j0ntz added a commit that referenced this pull request Aug 20, 2026
ff4cc3d is the only revision of this document that relaxed anything,
and none of the changes below appear in its commit message, which
describes its scope as mapping errors to edge-core-js classes, naming
the integration points, and expanding the Reporting API section.

The reference point throughout is Paul's "2025 Exchange Provider API
Requirements" doc, which ef51604 transcribes almost verbatim.

Native units are now the single convention, stated once:

The document had drifted into per-section conventions. Section 3 asked
for display units and called itself "the one place the native-unit
preference does not apply", section 6 asked for display units citing
StandardTx's asSafeNumber typing, and sections 4 and 6 asked for native
units. Amount Representation now states outright that it governs every
amount field and that no section carries a different one, sections 3, 4
and 6 all read the same, and the asSafeNumber fact moves to where it
belongs: it constrains Edge's storage, not the provider's wire format,
and the reporting plugin already converts.

The source doc states no convention at all. Its only amounts example
sits inside the error-handling section, whose purpose is to show that
every applicable error returns in one response, so the display units in
it are incidental to the point rather than a decision. Reading a
convention out of an incidental example is what drifted twice.

Section 3's example also loses a 1000x error. The December example was
9.789 BTC against 1,000,000 USDT, two sides of the same $1M cap. #442
converted both correctly (978900000 satoshis, 1000000000000 micro-units)
but labelled the satoshi value "0.009789 BTC" in its comment. ff4cc3d
resolved the value/comment mismatch by trusting the comment, rewriting
the limit as the display string "0.009789", which is roughly $1,000
against a 1,000,000 USDT destination. The value is restored and the
comment corrected.

Six requirements weakened in ff4cc3d, each weaker than both #442 and the
source doc, are restored:

- Section 1, EVM chainId: back to must, with the reason the source doc
  gives and ff4cc3d dropped along with the obligation. Numeric chain ids
  let a newly listed EVM work the day it is added, with no new mapping
  entry.
- Section 6, pagination: back to a required start date, end date and
  record count. ff4cc3d made date filtering one of three acceptable
  approaches, so an offset-only API conformed.
- Section 6, required fields: depositAddress/payoutAddress,
  depositTxid/payoutTxid, depositTokenId/payoutTokenId and
  depositEvmChainId/payoutEvmChainId are required again.
- Section 6, chain identifier: ff4cc3d rewrote the table around
  StandardTx names and lost sourceNetwork/destinationNetwork with no
  replacement row, leaving tokenId as the only asset identifier and no
  way to tell which chain a contract address belongs to. StandardTx has
  the field as depositChainPluginId/payoutChainPluginId.
- Section 9: the authKey is Edge-generated and cryptographically random
  again. ff4cc3d replaced it with "a unique per-user identifier" and
  listed provider-created sessions as acceptable, inverting who issues
  it.
- Section 14: accepting a return URI is the requirement again, flat,
  where ff4cc3d had listed it as one of three equal options. It is not a
  choice between equivalents. Any step taking a card payment, Apple Pay
  or Google Pay has to run in the system browser, where Edge cannot
  observe the page and a registered redirect is the only route back. The
  observable signals ff4cc3d listed alongside it work only for the
  non-payment steps Edge shows in its own WebView, and the section now
  says which is which.

Every example now carries native amounts annotated with their display
equivalent, and native amounts are specified as JSON strings, since a
high-decimal asset in native units runs past the safe integer range.
j0ntz added a commit that referenced this pull request Aug 20, 2026
ff4cc3d is the only revision of this document that relaxed anything,
and none of the changes below appear in its commit message, which
describes its scope as mapping errors to edge-core-js classes, naming
the integration points, and expanding the Reporting API section.

The reference point throughout is Paul's "2025 Exchange Provider API
Requirements" doc, which ef51604 transcribes almost verbatim.

Native units are now the single convention, stated once:

The document had drifted into per-section conventions. Section 3 asked
for display units and called itself "the one place the native-unit
preference does not apply", section 6 asked for display units citing
StandardTx's asSafeNumber typing, and sections 4 and 6 asked for native
units. Amount Representation now states outright that it governs every
amount field and that no section carries a different one, sections 3, 4
and 6 all read the same, and the asSafeNumber fact moves to where it
belongs: it constrains Edge's storage, not the provider's wire format,
and the reporting plugin already converts.

The source doc states no convention at all. Its only amounts example
sits inside the error-handling section, whose purpose is to show that
every applicable error returns in one response, so the display units in
it are incidental to the point rather than a decision. Reading a
convention out of an incidental example is what drifted twice.

Section 3's example also loses a 1000x error. The December example was
9.789 BTC against 1,000,000 USDT, two sides of the same $1M cap. #442
converted both correctly (978900000 satoshis, 1000000000000 micro-units)
but labelled the satoshi value "0.009789 BTC" in its comment. ff4cc3d
resolved the value/comment mismatch by trusting the comment, rewriting
the limit as the display string "0.009789", which is roughly $1,000
against a 1,000,000 USDT destination. The value is restored and the
comment corrected.

Six requirements weakened in ff4cc3d, each weaker than both #442 and the
source doc, are restored:

- Section 1, EVM chainId: back to must, with the reason the source doc
  gives and ff4cc3d dropped along with the obligation. Numeric chain ids
  let a newly listed EVM work the day it is added, with no new mapping
  entry.
- Section 6, pagination: back to a required start date, end date and
  record count. ff4cc3d made date filtering one of three acceptable
  approaches, so an offset-only API conformed.
- Section 6, required fields: depositAddress/payoutAddress,
  depositTxid/payoutTxid, depositTokenId/payoutTokenId and
  depositEvmChainId/payoutEvmChainId are required again.
- Section 6, chain identifier: ff4cc3d rewrote the table around
  StandardTx names and lost sourceNetwork/destinationNetwork with no
  replacement row, leaving tokenId as the only asset identifier and no
  way to tell which chain a contract address belongs to. StandardTx has
  the field as depositChainPluginId/payoutChainPluginId.
- Section 9: the authKey is Edge-generated and cryptographically random
  again. ff4cc3d replaced it with "a unique per-user identifier" and
  listed provider-created sessions as acceptable, inverting who issues
  it.
- Section 14: accepting a return URI is the requirement again, flat,
  where ff4cc3d had listed it as one of three equal options. It is not a
  choice between equivalents. Any step taking a card payment, Apple Pay
  or Google Pay has to run in the system browser, where Edge cannot
  observe the page and a registered redirect is the only route back. The
  observable signals ff4cc3d listed alongside it work only for the
  non-payment steps Edge shows in its own WebView, and the section now
  says which is which.

Every example now carries native amounts annotated with their display
equivalent, and native amounts are specified as JSON strings, since a
high-decimal asset in native units runs past the safe integer range.

The whole document also gets a prose pass against the house writing
rules, which it had never had: 15 em dashes replaced with commas, colons
or sentence breaks, two "Additionally" openers, two "What matters is
that" forward references, and a few filler constructions ("Note that",
"How the provider consumes that identifier is open", "This is not a
preference between equivalent mechanisms") cut or rewritten. No
requirement changes in that pass.
j0ntz added a commit that referenced this pull request Aug 21, 2026
ff4cc3d is the only revision of this document that relaxed anything,
and none of the changes below appear in its commit message, which
describes its scope as mapping errors to edge-core-js classes, naming
the integration points, and expanding the Reporting API section.

The reference point throughout is Paul's "2025 Exchange Provider API
Requirements" doc, which ef51604 transcribes almost verbatim.

Native units are now the single convention, stated once:

The document had drifted into per-section conventions. Section 3 asked
for display units and called itself "the one place the native-unit
preference does not apply", section 6 asked for display units citing
StandardTx's asSafeNumber typing, and sections 4 and 6 asked for native
units. Amount Representation now states outright that it governs every
amount field and that no section carries a different one, sections 3, 4
and 6 all read the same, and the asSafeNumber fact moves to where it
belongs: it constrains Edge's storage, not the provider's wire format,
and the reporting plugin already converts.

The source doc states no convention at all. Its only amounts example
sits inside the error-handling section, whose purpose is to show that
every applicable error returns in one response, so the display units in
it are incidental to the point rather than a decision. Reading a
convention out of an incidental example is what drifted twice.

Section 3's example also loses a 1000x error. The December example was
9.789 BTC against 1,000,000 USDT, two sides of the same $1M cap. #442
converted both correctly (978900000 satoshis, 1000000000000 micro-units)
but labelled the satoshi value "0.009789 BTC" in its comment. ff4cc3d
resolved the value/comment mismatch by trusting the comment, rewriting
the limit as the display string "0.009789", which is roughly $1,000
against a 1,000,000 USDT destination. The value is restored and the
comment corrected.

Six requirements weakened in ff4cc3d, each weaker than both #442 and the
source doc, are restored:

- Section 1, EVM chainId: back to must, with the reason the source doc
  gives and ff4cc3d dropped along with the obligation. Numeric chain ids
  let a newly listed EVM work the day it is added, with no new mapping
  entry.
- Section 6, pagination: back to a required start date, end date and
  record count. ff4cc3d made date filtering one of three acceptable
  approaches, so an offset-only API conformed.
- Section 6, required fields: depositAddress/payoutAddress,
  depositTxid/payoutTxid, depositTokenId/payoutTokenId and
  depositEvmChainId/payoutEvmChainId are required again.
- Section 6, chain identifier: ff4cc3d rewrote the table around
  StandardTx names and lost sourceNetwork/destinationNetwork with no
  replacement row, leaving tokenId as the only asset identifier and no
  way to tell which chain a contract address belongs to. StandardTx has
  the field as depositChainPluginId/payoutChainPluginId.
- Section 9: the authKey is Edge-generated and cryptographically random
  again. ff4cc3d replaced it with "a unique per-user identifier" and
  listed provider-created sessions as acceptable, inverting who issues
  it.
- Section 14: accepting a return URI is the requirement again, flat,
  where ff4cc3d had listed it as one of three equal options. It is not a
  choice between equivalents. Any step taking a card payment, Apple Pay
  or Google Pay has to run in the system browser, where Edge cannot
  observe the page and a registered redirect is the only route back. The
  observable signals ff4cc3d listed alongside it work only for the
  non-payment steps Edge shows in its own WebView, and the section now
  says which is which.

Every example now carries native amounts annotated with their display
equivalent, and native amounts are specified as JSON strings, since a
high-decimal asset in native units runs past the safe integer range.

AGENTS.md and .cursor/BUGBOT.md pick up the consequences. AGENTS.md names
this doc as pre-implementation reading, both in the docs list and at the
top of "Starting a new provider", where the point is that a gap you are
about to work around may be the provider's to fix rather than yours.
Both files also record what the doc is not: it states what Edge asks
partners for, not what any given provider sends. Most existing
integrations predate the request and send decimals, so the doc settles
nothing in a units argument about a specific provider, and a live
response still does.

The whole document also gets a prose pass against the house writing
rules, which it had never had: 15 em dashes replaced with commas, colons
or sentence breaks, two "Additionally" openers, two "What matters is
that" forward references, and a few filler constructions ("Note that",
"How the provider consumes that identifier is open", "This is not a
preference between equivalent mechanisms") cut or rewritten. No
requirement changes in that pass.
j0ntz added a commit that referenced this pull request Aug 21, 2026
ff4cc3d is the only revision of this document that relaxed anything,
and none of the changes below appear in its commit message, which
describes its scope as mapping errors to edge-core-js classes, naming
the integration points, and expanding the Reporting API section.

The reference point throughout is Paul's "2025 Exchange Provider API
Requirements" doc, which ef51604 transcribes almost verbatim.

Native units are now the single convention, stated once:

The document had drifted into per-section conventions. Section 3 asked
for display units and called itself "the one place the native-unit
preference does not apply", section 6 asked for display units citing
StandardTx's asSafeNumber typing, and sections 4 and 6 asked for native
units. Amount Representation now states outright that it governs every
amount field and that no section carries a different one, sections 3, 4
and 6 all read the same, and the asSafeNumber fact moves to where it
belongs: it constrains Edge's storage, not the provider's wire format,
and the reporting plugin already converts.

The source doc states no convention at all. Its only amounts example
sits inside the error-handling section, whose purpose is to show that
every applicable error returns in one response, so the display units in
it are incidental to the point rather than a decision. Reading a
convention out of an incidental example is what drifted twice.

Section 3's example also loses a 1000x error. The December example was
9.789 BTC against 1,000,000 USDT, two sides of the same $1M cap. #442
converted both correctly (978900000 satoshis, 1000000000000 micro-units)
but labelled the satoshi value "0.009789 BTC" in its comment. ff4cc3d
resolved the value/comment mismatch by trusting the comment, rewriting
the limit as the display string "0.009789", which is roughly $1,000
against a 1,000,000 USDT destination. The value is restored and the
comment corrected.

Six requirements weakened in ff4cc3d, each weaker than both #442 and the
source doc, are restored:

- Section 1, EVM chainId: back to must, with the reason the source doc
  gives and ff4cc3d dropped along with the obligation. Numeric chain ids
  let a newly listed EVM work the day it is added, with no new mapping
  entry.
- Section 6, pagination: back to a required start date, end date and
  record count. ff4cc3d made date filtering one of three acceptable
  approaches, so an offset-only API conformed.
- Section 6, required fields: depositAddress/payoutAddress,
  depositTxid/payoutTxid, depositTokenId/payoutTokenId and
  depositEvmChainId/payoutEvmChainId are required again.
- Section 6, chain identifier: ff4cc3d rewrote the table around
  StandardTx names and lost sourceNetwork/destinationNetwork with no
  replacement row, leaving tokenId as the only asset identifier and no
  way to tell which chain a contract address belongs to. StandardTx has
  the field as depositChainPluginId/payoutChainPluginId.
- Section 9: the authKey is Edge-generated and cryptographically random
  again. ff4cc3d replaced it with "a unique per-user identifier" and
  listed provider-created sessions as acceptable, inverting who issues
  it.
- Section 14: accepting a return URI is the requirement again, flat,
  where ff4cc3d had listed it as one of three equal options. It is not a
  choice between equivalents. Any step taking a card payment, Apple Pay
  or Google Pay has to run in the system browser, where Edge cannot
  observe the page and a registered redirect is the only route back. The
  observable signals ff4cc3d listed alongside it work only for the
  non-payment steps Edge shows in its own WebView, and the section now
  says which is which.

Every example now carries native amounts annotated with their display
equivalent, and native amounts are specified as JSON strings, since a
high-decimal asset in native units runs past the safe integer range.

AGENTS.md and .cursor/BUGBOT.md pick up the consequences. AGENTS.md names
this doc as pre-implementation reading, both in the docs list and at the
top of "Starting a new provider", where the point is that a gap you are
about to work around may be the provider's to fix rather than yours.
Both files also record what the doc is not: it states what Edge asks
partners for, not what any given provider sends. Most existing
integrations predate the request and send decimals, so the doc settles
nothing in a units argument about a specific provider, and a live
response still does.

docs/CREATING_AN_EXCHANGE_PLUGIN.md carried the same defect, in the
block plugin authors copy. Its Step 6 limit sample called
denominationToNative unconditionally and unrounded, then derived one
nativeLimit and threw it as both SwapBelowLimitError and
SwapAboveLimitError, so no single rounding direction could have been
right for it. It now selects the side, rounds a minimum up and a maximum
down, and says in a comment that the unit convention has to be confirmed
against a live response. Step 5 gains the same caveat. That sample also
failed the guide's own pre-PR checklist, which already required every
denominationToNative result to be rounded and minimums to round up.

The two documents state different things and had drifted into each
other's job. API_REQUIREMENTS.md states what Edge asks a partner to
send, so it can name one convention. CREATING_AN_EXCHANGE_PLUGIN.md
states what plugin code does with what arrives, so it cannot: compliance
is not guaranteed, and most existing integrations predate the request.

Both documents also get a prose pass against the house writing rules,
which neither had had. 16 em dashes are replaced with commas, colons or
sentence breaks, including one inside a code comment that a fence hid
from the linter. Two "Additionally" openers, two "What matters is that"
forward references, two count-announcement openers and a few filler
constructions are cut or rewritten. No requirement changes in that pass,
and no-slop-lint.sh now returns clean on all four files.
j0ntz added a commit that referenced this pull request Aug 21, 2026
ff4cc3d is the only revision of this document that relaxed anything,
and none of the changes below appear in its commit message, which
describes its scope as mapping errors to edge-core-js classes, naming
the integration points, and expanding the Reporting API section.

The reference point throughout is Paul's "2025 Exchange Provider API
Requirements" doc, which ef51604 transcribes almost verbatim.

Native units are now the single convention, stated once:

The document had drifted into per-section conventions. Section 3 asked
for display units and called itself "the one place the native-unit
preference does not apply", section 6 asked for display units citing
StandardTx's asSafeNumber typing, and sections 4 and 6 asked for native
units. Amount Representation now states outright that it governs every
amount field and that no section carries a different one, sections 3, 4
and 6 all read the same, and the asSafeNumber fact moves to where it
belongs: it constrains Edge's storage, not the provider's wire format,
and the reporting plugin already converts.

The source doc states no convention at all. Its only amounts example
sits inside the error-handling section, whose purpose is to show that
every applicable error returns in one response, so the display units in
it are incidental to the point rather than a decision. Reading a
convention out of an incidental example is what drifted twice.

Section 3's example also loses a 1000x error. The December example was
9.789 BTC against 1,000,000 USDT, two sides of the same $1M cap. #442
converted both correctly (978900000 satoshis, 1000000000000 micro-units)
but labelled the satoshi value "0.009789 BTC" in its comment. ff4cc3d
resolved the value/comment mismatch by trusting the comment, rewriting
the limit as the display string "0.009789", which is roughly $1,000
against a 1,000,000 USDT destination. The value is restored and the
comment corrected.

Six requirements weakened in ff4cc3d, each weaker than both #442 and the
source doc, are restored:

- Section 1, EVM chainId: back to must, with the reason the source doc
  gives and ff4cc3d dropped along with the obligation. Numeric chain ids
  let a newly listed EVM work the day it is added, with no new mapping
  entry.
- Section 6, pagination: back to a required start date, end date and
  record count. ff4cc3d made date filtering one of three acceptable
  approaches, so an offset-only API conformed.
- Section 6, required fields: depositAddress/payoutAddress,
  depositTxid/payoutTxid, depositTokenId/payoutTokenId and
  depositEvmChainId/payoutEvmChainId are required again.
- Section 6, chain identifier: ff4cc3d rewrote the table around
  StandardTx names and lost sourceNetwork/destinationNetwork with no
  replacement row, leaving tokenId as the only asset identifier and no
  way to tell which chain a contract address belongs to. StandardTx has
  the field as depositChainPluginId/payoutChainPluginId.
- Section 9: the authKey is Edge-generated and cryptographically random
  again. ff4cc3d replaced it with "a unique per-user identifier" and
  listed provider-created sessions as acceptable, inverting who issues
  it.
- Section 14: accepting a return URI is the requirement again, flat,
  where ff4cc3d had listed it as one of three equal options. It is not a
  choice between equivalents. Any step taking a card payment, Apple Pay
  or Google Pay has to run in the system browser, where Edge cannot
  observe the page and a registered redirect is the only route back. The
  observable signals ff4cc3d listed alongside it work only for the
  non-payment steps Edge shows in its own WebView, and the section now
  says which is which.

Every example now carries native amounts annotated with their display
equivalent, and native amounts are specified as JSON strings, since a
high-decimal asset in native units runs past the safe integer range.

AGENTS.md and .cursor/BUGBOT.md pick up the consequences. AGENTS.md names
this doc as pre-implementation reading, both in the docs list and at the
top of "Starting a new provider", where the point is that a gap you are
about to work around may be the provider's to fix rather than yours.
Both files also record what the doc is not: it states what Edge asks
partners for, not what any given provider sends. Most existing
integrations predate the request and send decimals, so the doc settles
nothing in a units argument about a specific provider, and a live
response still does.

docs/CREATING_AN_EXCHANGE_PLUGIN.md carried the same defect, in the
block plugin authors copy. Its Step 6 limit sample called
denominationToNative unconditionally and unrounded, then derived one
nativeLimit and threw it as both SwapBelowLimitError and
SwapAboveLimitError, so no single rounding direction could have been
right for it. It now selects the side, rounds a minimum up and a maximum
down, and says in a comment that the unit convention has to be confirmed
against a live response. Step 5 gains the same caveat. That sample also
failed the guide's own pre-PR checklist, which already required every
denominationToNative result to be rounded and minimums to round up.

The two documents state different things and had drifted into each
other's job. API_REQUIREMENTS.md states what Edge asks a partner to
send, so it can name one convention. CREATING_AN_EXCHANGE_PLUGIN.md
states what plugin code does with what arrives, so it cannot: compliance
is not guaranteed, and most existing integrations predate the request.

Both documents also get a prose pass against the house writing rules,
which neither had had. 16 em dashes are replaced with commas, colons or
sentence breaks, including one inside a code comment that a fence hid
from the linter. Gone with them: two "Additionally" openers, two "What
matters is that" forward references, three count-announcement openers,
"load-bearing", and a run of seven consecutive bullets that each carried
mid-prose bold, so none of it landed. Headings move to sentence case in
both files, and the plugin guide's opening line stops restating its own
title. Its table of contents was also missing the pre-PR checklist.

No requirement changes in that pass, every in-document anchor still
resolves, and no-slop-lint.sh returns clean on all four files.
j0ntz added a commit that referenced this pull request Aug 21, 2026
ff4cc3d is the only revision of this document that relaxed anything,
and none of the changes below appear in its commit message, which
describes its scope as mapping errors to edge-core-js classes, naming
the integration points, and expanding the Reporting API section.

The reference point throughout is Paul's "2025 Exchange Provider API
Requirements" doc, which ef51604 transcribes almost verbatim.

Native units are now the single convention, stated once:

The document had drifted into per-section conventions. Section 3 asked
for display units and called itself "the one place the native-unit
preference does not apply", section 6 asked for display units citing
StandardTx's asSafeNumber typing, and sections 4 and 6 asked for native
units. Amount Representation now states outright that it governs every
amount field and that no section carries a different one, sections 3, 4
and 6 all read the same, and the asSafeNumber fact moves to where it
belongs: it constrains Edge's storage, not the provider's wire format,
and the reporting plugin already converts.

The source doc states no convention at all. Its only amounts example
sits inside the error-handling section, whose purpose is to show that
every applicable error returns in one response, so the display units in
it are incidental to the point rather than a decision. Reading a
convention out of an incidental example is what drifted twice.

Section 3's example also loses a 1000x error. The December example was
9.789 BTC against 1,000,000 USDT, two sides of the same $1M cap. #442
converted both correctly (978900000 satoshis, 1000000000000 micro-units)
but labelled the satoshi value "0.009789 BTC" in its comment. ff4cc3d
resolved the value/comment mismatch by trusting the comment, rewriting
the limit as the display string "0.009789", which is roughly $1,000
against a 1,000,000 USDT destination. The value is restored and the
comment corrected.

Six requirements weakened in ff4cc3d, each weaker than both #442 and the
source doc, are restored:

- Section 1, EVM chainId: back to must, with the reason the source doc
  gives and ff4cc3d dropped along with the obligation. Numeric chain ids
  let a newly listed EVM work the day it is added, with no new mapping
  entry.
- Section 6, pagination: back to a required start date, end date and
  record count. ff4cc3d made date filtering one of three acceptable
  approaches, so an offset-only API conformed.
- Section 6, required fields: depositAddress/payoutAddress,
  depositTxid/payoutTxid, depositTokenId/payoutTokenId and
  depositEvmChainId/payoutEvmChainId are required again.
- Section 6, chain identifier: ff4cc3d rewrote the table around
  StandardTx names and lost sourceNetwork/destinationNetwork with no
  replacement row, leaving tokenId as the only asset identifier and no
  way to tell which chain a contract address belongs to. StandardTx has
  the field as depositChainPluginId/payoutChainPluginId.
- Section 9: the authKey is Edge-generated and cryptographically random
  again. ff4cc3d replaced it with "a unique per-user identifier" and
  listed provider-created sessions as acceptable, inverting who issues
  it.
- Section 14: accepting a return URI is the requirement again, flat,
  where ff4cc3d had listed it as one of three equal options. It is not a
  choice between equivalents. Any step taking a card payment, Apple Pay
  or Google Pay has to run in the system browser, where Edge cannot
  observe the page and a registered redirect is the only route back. The
  observable signals ff4cc3d listed alongside it work only for the
  non-payment steps Edge shows in its own WebView, and the section now
  says which is which.

Every example now carries native amounts annotated with their display
equivalent, and native amounts are specified as JSON strings, since a
high-decimal asset in native units runs past the safe integer range.

AGENTS.md and .cursor/BUGBOT.md pick up the consequences. AGENTS.md names
this doc as pre-implementation reading, both in the docs list and at the
top of "Starting a new provider", where the point is that a gap you are
about to work around may be the provider's to fix rather than yours.
Both files also record what the doc is not: it states what Edge asks
partners for, not what any given provider sends. Most existing
integrations predate the request and send decimals, so the doc settles
nothing in a units argument about a specific provider, and a live
response still does.

docs/CREATING_AN_EXCHANGE_PLUGIN.md carried the same defect, in the
block plugin authors copy. Its Step 6 limit sample called
denominationToNative unconditionally and unrounded, then derived one
nativeLimit and threw it as both SwapBelowLimitError and
SwapAboveLimitError, so no single rounding direction could have been
right for it. It now selects the side, rounds a minimum up and a maximum
down, and says in a comment that the unit convention has to be confirmed
against a live response. Step 5 gains the same caveat. That sample also
failed the guide's own pre-PR checklist, which already required every
denominationToNative result to be rounded and minimums to round up.

The two documents state different things and had drifted into each
other's job. API_REQUIREMENTS.md states what Edge asks a partner to
send, so it can name one convention. CREATING_AN_EXCHANGE_PLUGIN.md
states what plugin code does with what arrives, so it cannot: compliance
is not guaranteed, and most existing integrations predate the request.

Both documents also get a prose pass against the house writing rules,
which neither had had. 16 em dashes are replaced with commas, colons or
sentence breaks, including one inside a code comment that a fence hid
from the linter. Gone with them: two "Additionally" openers, two "What
matters is that" forward references, three count-announcement openers,
"load-bearing", and a run of seven consecutive bullets that each carried
mid-prose bold, so none of it landed. Headings move to sentence case in
both files, and the plugin guide's opening line stops restating its own
title. Its table of contents was also missing the pre-PR checklist.

No requirement changes in that pass, every in-document anchor still
resolves, and no-slop-lint.sh returns clean on all four files.
j0ntz added a commit that referenced this pull request Aug 21, 2026
ff4cc3d is the only revision of this document that relaxed anything,
and none of the changes below appear in its commit message, which
describes its scope as mapping errors to edge-core-js classes, naming
the integration points, and expanding the Reporting API section.

The reference point throughout is Paul's "2025 Exchange Provider API
Requirements" doc, which ef51604 transcribes almost verbatim.

Native units are now the single convention, stated once:

The document had drifted into per-section conventions. Section 3 asked
for display units and called itself "the one place the native-unit
preference does not apply", section 6 asked for display units citing
StandardTx's asSafeNumber typing, and sections 4 and 6 asked for native
units. Amount Representation now states outright that it governs every
amount field and that no section carries a different one, sections 3, 4
and 6 all read the same, and the asSafeNumber fact moves to where it
belongs: it constrains Edge's storage, not the provider's wire format,
and the reporting plugin already converts.

The source doc states no convention at all. Its only amounts example
sits inside the error-handling section, whose purpose is to show that
every applicable error returns in one response, so the display units in
it are incidental to the point rather than a decision. Reading a
convention out of an incidental example is what drifted twice.

Section 3's example also loses a 1000x error. The December example was
9.789 BTC against 1,000,000 USDT, two sides of the same $1M cap. #442
converted both correctly (978900000 satoshis, 1000000000000 micro-units)
but labelled the satoshi value "0.009789 BTC" in its comment. ff4cc3d
resolved the value/comment mismatch by trusting the comment, rewriting
the limit as the display string "0.009789", which is roughly $1,000
against a 1,000,000 USDT destination. The value is restored and the
comment corrected.

Six requirements weakened in ff4cc3d, each weaker than both #442 and the
source doc, are restored:

- Section 1, EVM chainId: back to must, with the reason the source doc
  gives and ff4cc3d dropped along with the obligation. Numeric chain ids
  let a newly listed EVM work the day it is added, with no new mapping
  entry.
- Section 6, pagination: back to a required start date, end date and
  record count. ff4cc3d made date filtering one of three acceptable
  approaches, so an offset-only API conformed.
- Section 6, required fields: depositAddress/payoutAddress,
  depositTxid/payoutTxid, depositTokenId/payoutTokenId and
  depositEvmChainId/payoutEvmChainId are required again.
- Section 6, chain identifier: ff4cc3d rewrote the table around
  StandardTx names and lost sourceNetwork/destinationNetwork with no
  replacement row, leaving tokenId as the only asset identifier and no
  way to tell which chain a contract address belongs to. StandardTx has
  the field as depositChainPluginId/payoutChainPluginId.
- Section 9: the authKey is Edge-generated and cryptographically random
  again. ff4cc3d replaced it with "a unique per-user identifier" and
  listed provider-created sessions as acceptable, inverting who issues
  it.
- Section 14: accepting a return URI is the requirement again, flat,
  where ff4cc3d had listed it as one of three equal options. It is not a
  choice between equivalents. Any step taking a card payment, Apple Pay
  or Google Pay has to run in the system browser, where Edge cannot
  observe the page and a registered redirect is the only route back. The
  observable signals ff4cc3d listed alongside it work only for the
  non-payment steps Edge shows in its own WebView, and the section now
  says which is which.

Every example now carries native amounts annotated with their display
equivalent, and native amounts are specified as JSON strings, since a
high-decimal asset in native units runs past the safe integer range.

AGENTS.md and .cursor/BUGBOT.md pick up the consequences. AGENTS.md names
this doc as pre-implementation reading, both in the docs list and at the
top of "Starting a new provider", where the point is that a gap you are
about to work around may be the provider's to fix rather than yours.
Both files also record what the doc is not: it states what Edge asks
partners for, not what any given provider sends. Most existing
integrations predate the request and send decimals, so the doc settles
nothing in a units argument about a specific provider, and a live
response still does.

docs/CREATING_AN_EXCHANGE_PLUGIN.md carried the same defect, in the
block plugin authors copy. Its Step 6 limit sample called
denominationToNative unconditionally and unrounded, then derived one
nativeLimit and threw it as both SwapBelowLimitError and
SwapAboveLimitError, so no single rounding direction could have been
right for it. It now selects the side, rounds a minimum up and a maximum
down, and says in a comment that the unit convention has to be confirmed
against a live response. Step 5 gains the same caveat. That sample also
failed the guide's own pre-PR checklist, which already required every
denominationToNative result to be rounded and minimums to round up.

The two documents state different things and had drifted into each
other's job. API_REQUIREMENTS.md states what Edge asks a partner to
send, so it can name one convention. CREATING_AN_EXCHANGE_PLUGIN.md
states what plugin code does with what arrives, so it cannot: compliance
is not guaranteed, and most existing integrations predate the request.

Folded in from #485, which drops its documentation commits: section 4
gains a rate-types requirement, and BUGBOT.md gains per-endpoint auth,
catalog cache expiry, and the scope a fixed-then-floating fallback is
allowed to cover. The rate-types requirement is an addition rather than
a restoration. Neither the source doc nor #442 says anything about rate
types, and it creates a new must on partners: a provider publishing rate
types per asset but not per route leaves the client attempting a type
and inferring support from the failure, which is indistinguishable from
an outage.

The StealthEX plugin on that branch confirms the Step 6 fix rather than
motivating it. It already rounds its below-limit with ceil and its
above-limit with floor, which is what the corrected sample now shows and
what the old one did not.

CHANGELOG gains entries for both documents. This repo lists doc-only
work there: two of the four Unreleased entries are doc-only today.

Both documents also get a prose pass against the house writing rules,
which neither had had. 16 em dashes are replaced with commas, colons or
sentence breaks, including one inside a code comment that a fence hid
from the linter. Gone with them: two "Additionally" openers, two "What
matters is that" forward references, three count-announcement openers,
"load-bearing", and a run of seven consecutive bullets that each carried
mid-prose bold, so none of it landed. Headings move to sentence case in
both files, and the plugin guide's opening line stops restating its own
title. Its table of contents was also missing the pre-PR checklist.

No requirement changes in that pass, every in-document anchor still
resolves, and no-slop-lint.sh returns clean on all four files.
j0ntz added a commit that referenced this pull request Aug 21, 2026
ff4cc3d is the only revision of this document that relaxed anything,
and none of the changes below appear in its commit message, which
describes its scope as mapping errors to edge-core-js classes, naming
the integration points, and expanding the Reporting API section.

The reference point throughout is Paul's "2025 Exchange Provider API
Requirements" doc, which ef51604 transcribes almost verbatim.

Native units are now the single convention, stated once:

The document had drifted into per-section conventions. Section 3 asked
for display units and called itself "the one place the native-unit
preference does not apply", section 6 asked for display units citing
StandardTx's asSafeNumber typing, and sections 4 and 6 asked for native
units. Amount Representation now states outright that it governs every
amount field and that no section carries a different one, sections 3, 4
and 6 all read the same, and the asSafeNumber fact moves to where it
belongs: it constrains Edge's storage, not the provider's wire format,
and the reporting plugin already converts.

The source doc states no convention at all. Its only amounts example
sits inside the error-handling section, whose purpose is to show that
every applicable error returns in one response, so the display units in
it are incidental to the point rather than a decision. Reading a
convention out of an incidental example is what drifted twice.

Section 3's example also loses a 1000x error. The December example was
9.789 BTC against 1,000,000 USDT, two sides of the same $1M cap. #442
converted both correctly (978900000 satoshis, 1000000000000 micro-units)
but labelled the satoshi value "0.009789 BTC" in its comment. ff4cc3d
resolved the value/comment mismatch by trusting the comment, rewriting
the limit as the display string "0.009789", which is roughly $1,000
against a 1,000,000 USDT destination. The value is restored and the
comment corrected.

Six requirements weakened in ff4cc3d, each weaker than both #442 and the
source doc, are restored:

- Section 1, EVM chainId: back to must, with the reason the source doc
  gives and ff4cc3d dropped along with the obligation. Numeric chain ids
  let a newly listed EVM work the day it is added, with no new mapping
  entry.
- Section 6, pagination: back to a required start date, end date and
  record count. ff4cc3d made date filtering one of three acceptable
  approaches, so an offset-only API conformed.
- Section 6, required fields: depositAddress/payoutAddress,
  depositTxid/payoutTxid, depositTokenId/payoutTokenId and
  depositEvmChainId/payoutEvmChainId are required again.
- Section 6, chain identifier: ff4cc3d rewrote the table around
  StandardTx names and lost sourceNetwork/destinationNetwork with no
  replacement row, leaving tokenId as the only asset identifier and no
  way to tell which chain a contract address belongs to. StandardTx has
  the field as depositChainPluginId/payoutChainPluginId.
- Section 9: the authKey is Edge-generated and cryptographically random
  again. ff4cc3d replaced it with "a unique per-user identifier" and
  listed provider-created sessions as acceptable, inverting who issues
  it.
- Section 14: accepting a return URI is the requirement again, flat,
  where ff4cc3d had listed it as one of three equal options. It is not a
  choice between equivalents. Any step taking a card payment, Apple Pay
  or Google Pay has to run in the system browser, where Edge cannot
  observe the page and a registered redirect is the only route back. The
  observable signals ff4cc3d listed alongside it work only for the
  non-payment steps Edge shows in its own WebView, and the section now
  says which is which.

Every example now carries native amounts annotated with their display
equivalent, and native amounts are specified as JSON strings, since a
high-decimal asset in native units runs past the safe integer range.

AGENTS.md and .cursor/BUGBOT.md pick up the consequences. AGENTS.md names
this doc as pre-implementation reading, both in the docs list and at the
top of "Starting a new provider", where the point is that a gap you are
about to work around may be the provider's to fix rather than yours.
Both files also record what the doc is not: it states what Edge asks
partners for, not what any given provider sends. Most existing
integrations predate the request and send decimals, so the doc settles
nothing in a units argument about a specific provider, and a live
response still does.

docs/CREATING_AN_EXCHANGE_PLUGIN.md carried the same defect, in the
block plugin authors copy. Its Step 6 limit sample called
denominationToNative unconditionally and unrounded, then derived one
nativeLimit and threw it as both SwapBelowLimitError and
SwapAboveLimitError, so no single rounding direction could have been
right for it. It now selects the side, rounds a minimum up and a maximum
down, and says in a comment that the unit convention has to be confirmed
against a live response. Step 5 gains the same caveat. That sample also
failed the guide's own pre-PR checklist, which already required every
denominationToNative result to be rounded and minimums to round up.

The two documents state different things and had drifted into each
other's job. API_REQUIREMENTS.md states what Edge asks a partner to
send, so it can name one convention. CREATING_AN_EXCHANGE_PLUGIN.md
states what plugin code does with what arrives, so it cannot: compliance
is not guaranteed, and most existing integrations predate the request.

Folded in from #485, which drops its documentation commits: section 4
gains a rate-types requirement, and BUGBOT.md gains per-endpoint auth,
catalog cache expiry, and the scope a fixed-then-floating fallback is
allowed to cover. The rate-types requirement is an addition rather than
a restoration. Neither the source doc nor #442 says anything about rate
types, and it creates a new must on partners: a provider publishing rate
types per asset but not per route leaves the client attempting a type
and inferring support from the failure, which is indistinguishable from
an outage.

The StealthEX plugin on that branch confirms the Step 6 fix rather than
motivating it. It already rounds its below-limit with ceil and its
above-limit with floor, which is what the corrected sample now shows and
what the old one did not.

Both documents also get a prose pass against the house writing rules,
which neither had had. 16 em dashes are replaced with commas, colons or
sentence breaks, including one inside a code comment that a fence hid
from the linter. Gone with them: two "Additionally" openers, two "What
matters is that" forward references, three count-announcement openers,
"load-bearing", and a run of seven consecutive bullets that each carried
mid-prose bold, so none of it landed. Headings move to sentence case in
both files, and the plugin guide's opening line stops restating its own
title. Its table of contents was also missing the pre-PR checklist.

No requirement changes in that pass, every in-document anchor still
resolves, and no-slop-lint.sh returns clean on all four files.
j0ntz added a commit that referenced this pull request Aug 21, 2026
ff4cc3d is the only revision of this document that relaxed anything,
and none of the changes below appear in its commit message, which
describes its scope as mapping errors to edge-core-js classes, naming
the integration points, and expanding the Reporting API section.

The reference point throughout is Paul's "2025 Exchange Provider API
Requirements" doc, which ef51604 transcribes almost verbatim.

Native units are now the single convention, stated once:

The document had drifted into per-section conventions. Section 3 asked
for display units and called itself "the one place the native-unit
preference does not apply", section 6 asked for display units citing
StandardTx's asSafeNumber typing, and sections 4 and 6 asked for native
units. Amount Representation now states outright that it governs every
amount field and that no section carries a different one, sections 3, 4
and 6 all read the same, and the asSafeNumber fact moves to where it
belongs: it constrains Edge's storage, not the provider's wire format,
and the reporting plugin already converts.

The source doc states no convention at all. Its only amounts example
sits inside the error-handling section, whose purpose is to show that
every applicable error returns in one response, so the display units in
it are incidental to the point rather than a decision. Reading a
convention out of an incidental example is what drifted twice.

Section 3's example also loses a 1000x error. The December example was
9.789 BTC against 1,000,000 USDT, two sides of the same $1M cap. #442
converted both correctly (978900000 satoshis, 1000000000000 micro-units)
but labelled the satoshi value "0.009789 BTC" in its comment. ff4cc3d
resolved the value/comment mismatch by trusting the comment, rewriting
the limit as the display string "0.009789", which is roughly $1,000
against a 1,000,000 USDT destination. The value is restored and the
comment corrected.

Six requirements weakened in ff4cc3d, each weaker than both #442 and the
source doc, are restored:

- Section 1, EVM chainId: back to must, with the reason the source doc
  gives and ff4cc3d dropped along with the obligation. Numeric chain ids
  let a newly listed EVM work the day it is added, with no new mapping
  entry.
- Section 6, pagination: back to a required start date, end date and
  record count. ff4cc3d made date filtering one of three acceptable
  approaches, so an offset-only API conformed.
- Section 6, required fields: depositAddress/payoutAddress,
  depositTxid/payoutTxid, depositTokenId/payoutTokenId and
  depositEvmChainId/payoutEvmChainId are required again.
- Section 6, chain identifier: ff4cc3d rewrote the table around
  StandardTx names and lost sourceNetwork/destinationNetwork with no
  replacement row, leaving tokenId as the only asset identifier and no
  way to tell which chain a contract address belongs to. StandardTx has
  the field as depositChainPluginId/payoutChainPluginId.
- Section 9: the authKey is Edge-generated and cryptographically random
  again. ff4cc3d replaced it with "a unique per-user identifier" and
  listed provider-created sessions as acceptable, inverting who issues
  it.
- Section 14: accepting a return URI is the requirement again, flat,
  where ff4cc3d had listed it as one of three equal options. It is not a
  choice between equivalents. Any step taking a card payment, Apple Pay
  or Google Pay has to run in the system browser, where Edge cannot
  observe the page and a registered redirect is the only route back. The
  observable signals ff4cc3d listed alongside it work only for the
  non-payment steps Edge shows in its own WebView, and the section now
  says which is which.

Every example now carries native amounts annotated with their display
equivalent, and native amounts are specified as JSON strings, since a
high-decimal asset in native units runs past the safe integer range.

AGENTS.md and .cursor/BUGBOT.md pick up the consequences. AGENTS.md names
this doc as pre-implementation reading, both in the docs list and at the
top of "Starting a new provider", where the point is that a gap you are
about to work around may be the provider's to fix rather than yours.
Both files also record what the doc is not: it states what Edge asks
partners for, not what any given provider sends. Most existing
integrations predate the request and send decimals, so the doc settles
nothing in a units argument about a specific provider, and a live
response still does.

docs/CREATING_AN_EXCHANGE_PLUGIN.md carried the same defect, in the
block plugin authors copy. Its Step 6 limit sample called
denominationToNative unconditionally and unrounded, then derived one
nativeLimit and threw it as both SwapBelowLimitError and
SwapAboveLimitError, so no single rounding direction could have been
right for it. It now selects the side, rounds a minimum up and a maximum
down, and says in a comment that the unit convention has to be confirmed
against a live response. Step 5 gains the same caveat. That sample also
failed the guide's own pre-PR checklist, which already required every
denominationToNative result to be rounded and minimums to round up.

The two documents state different things and had drifted into each
other's job. API_REQUIREMENTS.md states what Edge asks a partner to
send, so it can name one convention. CREATING_AN_EXCHANGE_PLUGIN.md
states what plugin code does with what arrives, so it cannot: compliance
is not guaranteed, and most existing integrations predate the request.

Folded in from #485, which drops its documentation commits: section 4
gains a rate-types requirement, and BUGBOT.md gains per-endpoint auth,
catalog cache expiry, and the scope a fixed-then-floating fallback is
allowed to cover. The rate-types requirement is an addition rather than
a restoration. Neither the source doc nor #442 says anything about rate
types, and it creates a new must on partners: a provider publishing rate
types per asset but not per route leaves the client attempting a type
and inferring support from the failure, which is indistinguishable from
an outage.

The StealthEX plugin on that branch confirms the Step 6 fix rather than
motivating it. It already rounds its below-limit with ceil and its
above-limit with floor, which is what the corrected sample now shows and
what the old one did not.

Both documents also get a prose pass against the house writing rules,
which neither had had. 16 em dashes are replaced with commas, colons or
sentence breaks, including one inside a code comment that a fence hid
from the linter. Gone with them: two "Additionally" openers, two "What
matters is that" forward references, three count-announcement openers,
"load-bearing", and a run of seven consecutive bullets that each carried
mid-prose bold, so none of it landed. Headings move to sentence case in
both files, and the plugin guide's opening line stops restating its own
title. Its table of contents was also missing the pre-PR checklist.

No requirement changes in that pass, every in-document anchor still
resolves, and no-slop-lint.sh returns clean on all four files.
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.

2 participants