OMS
OMS Payments ships the v1 cash-in shape with a
cashIn.updated event when the barcode refreshes, drives virtual-account fiat payouts (ACH, wire, international wire and fiat-wallet book transfer) through the same step-by-step webhook sequence on every supported provider while invalidating a dead account on an ACH return and freeing money a bank returned, drives card buys and sells through the same webhooks (never reading a cashPickup sub-status on a card sell), re-exposes the payout origin, the incoming wire’s instruction id and the full cash location on partner reads, enforces per-transaction maximums and minimums on the fiat leg of a crypto-pinned withdrawal, and sweeps bank payouts the service stopped waiting on by asking the provider and settling or failing them without re-sending.New
- One v1
CashInshape withcashIn.updated(breaking): create,GET, list and everycashIn.*webhook now return the version 1 cash-in body, and refreshing a cash-in’s barcode announcescashIn.updatedso partners don’t have to poll for a new code. The npm package jumps to107.0.0and drops the fields the v1 shape no longer carries. - Virtual-account fiat payout flow sends the same webhooks on every rail (breaking): a dollars-in dollars-out virtual account payment (flow 10.11) now sends the same sequence of transaction and transfer events across ACH, wire, international wire and fiat-wallet book transfer, proved row by row by a parity suite. An ACH return of a payout now invalidates the external account that received it (matching the behavior already in place for deposit addresses and fiat-wallet withdrawals), so the next deposit is held under
awaitingAction.payoutDestinationMissinguntil the partner sets a new destination. Money a bank returned can now leave the service: ops can refund it to the sender, or re-send it re-pinned to an account other than the one that returned it. The npm package jumps to106.0.0, and themsgstring on error10240changes. - Card buy and card sell flow sends the same webhooks (flow 10.12): a card buy’s charge is a
deposittransfer on railcardthat settles when the processor reports the charge final; a card sell’s push is apayouttransfer on railcard; a card sell never reads acashPickupsub-status; and a card decline reports a dedicatedfailure.code(new sentinel values seeded for the specific processor rejection codes) instead of the genericproviderError. payoutOrigin, wire instruction id and full cash location back on partner reads:payoutOriginreturns on the transaction, deposit address and virtual account (inGETand in every v1 webhook); an incoming wire’s instruction id now sits in the transfer’strackingnext toimad,uetrandendToEndId; and a cash-in’slocationcarrieslocationProviderand a structuredlocationAddressobject instead of a single line of text.- Admin deposit address carries
payoutOrigin: operator reads of a deposit address (adminGET, admin list, force-close and deactivate) now returnpayoutOrigin {type, id, accountHolder, accountHolderName}, so the operations dashboard can keep showing whose name a payout goes out under even though the v1 partner shape no longer carries it. - Provider sweep for unconfirmed fiat payouts: every fiat payout now carries a reference the provider can be queried against (
custom_ref, the leg’s public transaction id), and a background sweeper asks the provider about every payout that readsprocessing.payoutUnconfirmed. If the provider is holding the transfer, the payout continues as that transfer and settles, fails or is returned as the provider reports; nothing is sent twice. If the provider has none, the payout fails withfailure.code: providerTimeoutand the transaction follows its flow’s failed-payout row (processing.underReviewon deposit address and virtual account payouts,failed.outboundFailedon a fiat wallet withdrawal). The ops redrive, refund and mark-failed endpoints call the same lookup first, so none of them can re-send or refund a payout the provider may still be holding. A new error10369 unconfirmedPayoutUnverifiablesurfaces when the provider cannot be reached; the npm package bumps to107.1.0.
Fixed
- Per-transaction limits apply to the fiat leg of a crypto-pinned withdrawal: a source-pinned crypto-to-fiat sell (for example ETH to USD on
ach) is now subject to its project’s per-transaction maximum, minimum and multiple, evaluated against the corridor’s fiat leg. Daily and monthly caps were already enforced; the per-transaction check previously fell open because the pinned crypto quantity was treated as having nothing meaningful to compare against. - Transfer
trackingkeeps the on-chain hash when vendor metadata cannot be decoded: a transfer whose vendor metadata failed to decode previously dropped its entiretrackingobject, including the on-chaintxHash, even though the readable references were meant to be preserved. The hash is now a column of its own, so a partner read keeps it (and every other reference the service could read) while the decode failure still logs.
AggLayer
aggkit’s L2 syncers now default to finalized blocks so they stop indexing L2 blocks that may still reorg, and the bridge tracker no longer reports a hop as
claimed for the brief window it takes to persist a multi-step tracker tick.Updated
- L2 syncer
BlockFinalitydefault isFinalizedBlock(breaking):[BridgeL2Sync]and[L2GERSync]now default toBlockFinality = "FinalizedBlock"instead of"LatestBlock", so each syncer stops indexing L2 blocks that may still be reorged.[ClaimL2Sync]inherits the new value through its existing{{BridgeL2Sync.BlockFinality}}reference. Deployments that want the old behavior (for example a CDK Besu RPC whosefinalizedtag does not advance) can still setBlockFinality = "LatestBlock"explicitly in their configuration.
Fixed
- Bridge tracker never reports
claimedfor an unclaimed bridge mid-persist: during a tracker tick that moves a bridge across several steps, a concurrentGET /tracker/v1/activity/from/{addr}?includeTracking=truecould read the latest step aspendingwhile an earlier step already readdone, sotracking.claim_statusfell back to the finalClaimedstep asclaimedeven though the bridge had not been claimed on chain. The activity refresh persisted and served that staleclaimedfrom cache for up to 30 s, hiding the Claim button in UIs that drive their state from it. Snapshot reads now ignore an in-flight persist, so an unclaimed bridge never readsclaimed.
OMS
OMS Payments ships a unified v1 shape across every top-level resource (transaction, external account, deposit address and virtual account), announces every create and change on both supported providers, replaces the
transaction.hold union with one {code, message, deadline?, match?} object alongside new sub-statuses that mean one thing each, adds an admin endpoint to close provider-reported refunds, and lands pre-stamp sweepers that fail interrupted provider sends that never left for the provider.New
transaction.statusChangedis written by the leg write (breaking): every leg write now decides and writestransaction.statusChangedin its own database transaction, from the payment’s reading before and after the write, so about twenty per-site emitters are gone and no partner-visible transition can disappear. One meaning per sub-status:processing.fundsPulledmeans only that the customer’s own money was taken; a payout that failed while OMS holds the money readsprocessing.underReview; an ops retry readsprocessing.payoutRetrying; a payout OMS stopped waiting on readsprocessing.payoutUnconfirmed; a refund in flight readsprocessing.returningToSender; a refund that reached the sender endsfailed.returnedToSender; a single-leg provider refusal readsfailed.providerDeclined.TransactionSubStatusdropsfailed.returnPending,failed.returnStarted,failed.returnComplete,failed.returnFailedandawaitingAction.depositAddressInactive, and addsprocessing.payoutRetrying,processing.payoutUnconfirmed,processing.returningToSender,failed.returnedToSenderandawaitingAction.payoutDestinationMissing. A transaction with a refund in flight or a stranded payout no longer matchesstatus=failed. The npm package jumps to102.0.0.- One
transaction.holdshape (breaking):transaction.holdis{code, message, deadline?, match?}and present only while the transaction isawaitingAction.codeissenderAttributionorpayoutDestinationMissing,messageis a fixed sentence that names no provider, anddeadlineandmatch {address, network}appear onsenderAttributiononly. The deadline follows thebpn/needs_attribution_timeout_hourssetting instead of a hardcoded 30-day constant; the union of hold arms,required,since,resolvedAtandmatchableExternalAccountCriterialeave the schema. - One
externalAccountshape and three events (breaking):GET, list, create and update return the v1 external account, andexternalAccount.created,externalAccount.statusChangedandexternalAccount.updatedcarry exactly the same body. The account is an instrument:detailsmatch what a transaction shows for it,customeris always named,owner {kind, id, name}appears only when a counterparty owns it, bank accounts carrydetails.holderName, cards carrydetails.last4anddetails.brand, and wallets list destinations by{asset, network}. Per-type objects,category,owner.customerId/owner.counterpartyIdand the create response’sresolvedTransactionsare removed;supportedDestinationsmoves to one top-level list. API creates now announceexternalAccount.createdaspendingbefore thestatusChangedtoactive, and any writer that changesdetails,failure,labelormetadatasendsexternalAccount.updatednaming the changed fields inchangedFields. A declined card now carriesfailure.code: cardVerificationDeclinedinstead of the genericproviderError. The npm package jumps to103.0.0. - One
depositAddressshape withcreatedandupdatedevents (breaking): the deposit address renders through one v1 snapshot acrossGET, list, create, update and everydepositAddress.*webhook. A bank destination’s rail and ACH options move from the destination intopayout {rail, secCode?, companyDiscretionaryData?};returnDestinationis an instrument{type, id, asset, network, owner, details};destination.payoutOrigin,expectedSourceAsset/expectedSourceNetwork,sourceToDestinationanddepositInstructions.expiresAtare removed;hold,sponsorGasandrequiresCounterpartyWalletRegistrationappear only when set, andreturnDestinationTypesonly when not empty. Every create now announcesdepositAddress.createdaspendingin the create transaction (a provider that issues the address during the create call then follows withstatusChangedtoactivein the same transaction), and aPATCHthat leaves the status alone sendsdepositAddress.updatednaming the changed fields. The npm package jumps to104.0.0. - One
virtualAccountshape withcreated,updatedand adeletingstatus (breaking): the virtual account carries one v1 snapshot acrossGET, list, create, update and everyvirtualAccount.*webhook.source,sourceToDestination,payoutOriginandfinalBalanceare removed; a bank or fiat-wallet destination’s rail and ACH options move intopayout {rail, secCode?, companyDiscretionaryData?};returnDestinationis the bank account instrument with its rail onreturnPayout.rail;BICis lowercased tobic,supportedSources[]names its rail (internationalWirewhere it readinternational_wire), empty bank fields are omitted, andsponsorGasappears only whentrue. Every create announcesvirtualAccount.createdaspending, aPATCHthat changes the label, metadata, destination, return destination or re-reported bank details sends onevirtualAccount.updatednaming the changed fields inchangedFields, and a delete now moves the account through a newdeletingstatus:statusChangedfrom the live status todeleting, thendeleting → deletedonce the close completes. A provider that cannot close right now restores the prior status; a provider refusal endsfailed. The npm package jumps to105.0.0. POST /admin/transactions/{transactionId}/complete-refund: an operations-only endpoint that completes a virtual account bank refund whose provider only reported it initiated and never a completion. Body carries a requiredreason; the refund (pending, in flight or failed) moves tocompleted, its review flag is cleared, the operator and reason are recorded, and the transaction endsfailed.returnedToSenderwith the refund transfersettled. A repeat call answersalready_completed; other payments return422 refundNotCompletable(code10368). The stuck-transaction alert gains anopen_refundarm so ops see refunds still open past the threshold.- Pre-stamp provider-send sweepers (ship dark): two new background sweepers find deposit-address payout sends and direct bank-sell sends whose transaction row stopped BEFORE the pre-call
provider_refstamp (so no provider call was ever made) and fail them, so the partner no longer seesprocessingforever. Both ship dark behind per-sweeper configuration flags (defaultfalse, read every tick) and run on a single pod. Theprovider_refstamp now refuses to write onto afailedrow (errProviderRefStampRefused), stopping a stalled send that stamps after its row was already failed from issuing a vendor call for a leg the partner was told failed.
Updated
- Transaction body is the v1 shape (breaking):
tracking,sourceToDestination,estimatedArrival,expiresAtandprojectIdleaveGETand the events; each transfer carries its owntxHash,imad,uetrandendToEndId.precursoris left out when a transaction has no recognised origin instead of serialized asnull.metadatacarries only the partner’s own keys: the server no longer injectsmemo,charge_bearerorcrypto_from_address(those values already live undersource.details.memoand the source instrument’s address). The admin body keepstracking,sourceToDestinationandprojectId. - Deposit address flows
crypto in, fiat outandcrypto in, crypto outsend the same v1 webhooks on both supported providers: every step is announced in order, every rail (ACH, wire, RTP, international wire) and both own-asset and converting payouts included. A payout transfer now readsinitiateduntil the provider reports it in flight (previouslypendingfrom acceptance on one provider), a payout the provider reports only once final now carries the earlier steps too (steps 4 and 5 announce together, in order), ops’ resend after an on-chain send failure is a new attempt on a fresh key, and a deposit whose destination external account was deleted readsawaitingAction.payoutDestinationMissingwithhold.code: payoutDestinationMissingon both providers and resumes on its own once the partner sets a new destination. A deposit address paying out on Polygon now readsprocessing.awaitingCryptoOutwhile it waits. - Virtual account flow
fiat in, crypto outsends the same v1 webhooks on both supported providers: steps 1 to 5 and failures A to E on both providers and both deposit rails (wire and ACH); a settled deposit now readsprocessing.awaitingCryptoOuton both providers until the custodial buy completes; a refund is announced as a transfer (initiated, thenpendingin flight, thensettledon completion), with partial or zero amounts respected; a payout wallet that was deleted holds the deposit underawaitingAction.payoutDestinationMissingand resumes once the partner sets a new destination; a virtual account routing to a registered external wallet with no stored address pays out with the account’s address instead of an emptyto; and the deposit’sfromcarries the sender’s bank account (bank name, routing number, holder name on ACH, last-4 on IBAN) as far as the rail reports it, with holder name stored encrypted and masked in kept event bodies. - Withdrawal flow sends the same v1 webhooks on every path: the four paths that serve a withdrawal (bank sell, same-pair send, converting send and fiat-wallet payout) now announce the same sequence of events, in the same order, with the same statuses. A withdrawal row reads its own payout rather than a stored sub-status, so
GET, the list and every event agree. A withdrawal row OMS stopped waiting on readsprocessing.payoutUnconfirmedwith its payout transferpending; a converting withdrawal whose swap converted and whose send then failed readsprocessing.payoutRetryinguntil the recovered send completes, notfailed. An ops Mark failed can now close a withdrawal’s own stuck row (endsfailed.outboundFailedwith its payoutfailed), refused with422 vendorOrderExistswhen aCOMPLETEDSEND webhook is already stored for the row.
Fixed
- Virtual account status webhooks are announced: a provider’s activation, lock, unlock or label refresh now announces
virtualAccount.statusChangedin the transaction that writes the status, instead of staying silent until a sweeper or an admin refresh happened to find it. - A stuck virtual account delete completes: a delete the provider could not answer with a close webhook is now reconciled on every sweep. The sweeper reads the account against the provider’s deposit account API and finalizes
deleting → deletedas soon as the provider reports it closed, re-sending the close on the stored idempotency key behind the balance and open-transaction gates. A provider that answers the close with HTTP 429 now reads as close-unavailable (the delete answers503withRetry-Afterand the account keeps its status) instead of surfacing as a generic 502. - A provider-reported virtual account refund is a transfer:
REFUND_INITIATEDwrites the refund legpendingkeeping the returned figure,REFUND_IN_FLIGHTmoves it topending, andREFUND_FAILEDholds the money for review withreturnFailed. The held span flushes the transaction’sstatusChangedfirst and the transfer events after, so the partner never sees the depositfailedon its own. - A refunded deposit whose buy was superseded reads its refund:
GETand the status filter agree with each other, so a refunded deposit whose buy had been superseded no longer readsfailed.inboundFailedwhile its refund is in flight. - A deposit reported failed and then completed is one transaction: a provider pair that reports a deposit
FAILEDon a false-positive compliance screen and thenCOMPLETEDis now one transaction (the completed arrival takes over the failed row’s id, payment and webhook history); only the hashless and not-active-address cases stay two transactions by design, and a lateFAILEDfor an already-credited deposit is refused as a claim duplicate. - Deposit address and virtual account country codes come from the referenced external account: a deposit address created through the API no longer renders an empty country on the SWIFT payout or the CSV export; both now read
country_codefrom the account the destination references, scoped to the deposit address’s own project. DepositAddressandVirtualAccountrenders share one SEC-code and party resolver: an ACH payout’ssecCodeis derived from the resolved party’s entity type; counterparty entity types batch into oneResolvePartiesread per page, and a failed lookup leavessecCodeout without failing the render.- A deposit address deactivation keeps the destination and return destination: the admin deactivate endpoint now closes the deposit address and releases its wallet without clearing its destination or return destination, matching force-close behavior. An ops return on a deactivated address can therefore choose
returnDestination. - Attribution timeout follows its setting: the attribution sweeper now reads
bpn/needs_attribution_timeout_hoursat every sweep and carries it to the SELECT and the timeout event, so a changed timeout moves the moment a deposit fails together with thehold.deadlinethe partner sees. An unreadable, unparseable or missing setting keeps the last value read instead of silently falling back to a shorter default. - A withdrawal is never failed early: a payout that could still complete reads
processing.payoutUnconfirmed, notfailed, and a converting withdrawal whose send failed after the swap converted readsprocessing.payoutRetryingwhile the recovery mints the next attempt. The create call for such a withdrawal now answers201with the in-flight reading instead of502.
AggLayer
aggkit adds a bridge fee estimation service and a configuration-rendering CLI.
New
- Bridge service fee estimation endpoints: two new read-only endpoints on the bridge service,
GET /bridge/v1/claim-fee-estimate(takes destination network, origin network and token) andGET /bridge/v1/bridge-fee-estimate(takes origin network and token). Both fail closed:503when no estimate is computable,400on invalid input, never a default or partial value. Estimators are wired per network at startup and exposed through the proxy vianetwork_id; no TOML configuration changes. config_renderCLI: a new tool undertools/config_renderthat composes and resolves an aggkit configuration file through the same pipeline the aggkit binary uses. Each default layer can be overridden (--default-values,--default-vars,--mandatory-vars),--cfgis repeatable (merged in order), output goes to stdout or-o <file>, env overrides use--env-prefix(defaultCDK), and--deprecated-fields <file>or--use-default-deprecated-fieldsruns a deprecated-fields check. The tool fails with exit code 1 listing every unresolved variable or any cycle.
OMS
OMS Payments reshapes every money amount in
GET, list and quote responses to its asset’s precision through one pricing shape across quotes, transactions and cash-ins (and fixes an Erebor deposit webhook that reported USDT amounts 10,000 times too small, plus a Coinme crypto payout that carried no amount until confirmation), replaces per-resource source and destination builders with one instrument shape across Transaction, Quote, DepositAddress and VirtualAccount, hides the vendor query parameter on GET /networks from the public schema, broadens the published paymentMemo character pattern so generated validators accept every character the server already accepts, and seals a two-vendor race that let a deposit address’s create-time vendor and bank-rail classification pick different vendors on the same corridor.New
- One
pricingshape on quotes, transactions and cash-ins (breaking): every amount is now a decimal string at its asset’s precision ("500298.310000"for USDT,"10.00"for USD), andpricingis one shape with{source, destination, fees, exchangeRate?, fixedAmountSide?, sponsorGas?}.pricing.{source,destination}.amountGross/amountNetcollapse topricing.{source,destination}.amount(source net and destination gross follow frompricing.fees),pricing.sponsorGasCostis removed,pricing.sponsorGasis omitted unless true, andpricing.exchangeRateappears only together with a destination amount.pricing.*.assetandpricing.fees[].assetare lowercase andPricingSide.assetis always present.CashIngainspricingand losesrates,sponsorGas,sponsorGasCost,developerFeesand the amounts onsourceanddestination. The npm package jumps to97.0.0. - One
instrumentshape on sources and destinations (breaking):Transaction,Quote,DepositAddressandVirtualAccountrender each source and destination through one builder:{type, id?, asset, network?, owner?, details?}.partybecomesowner(absent for the resource’s customer’s own account),categoryis removed,displayNameand the top-levelmemomove into typeddetails,details.blockchainAddressbecomesdetails.address, abankIbansender’saccountNumberLast4becomesibanLast4, unknown ids and rails are left out instead of rendered asnull, and asset and crypto network values are lowercase.TransactionSidegains two arms (depositAddress,virtualAccount). The npm package jumps to96.0.0.
Updated
GET /networkshides thevendorquery parameter from the public schema: the public schema no longer documents thevendorquery parameter onGET /networksor thePaymentProviderVendorenum. The parameter still exists at runtime and continues to validate (a bad value still returns400 InvalidParameter 10006); it is markedx-internaland kept in the internal schema the OMS dashboard reads. The public schema version isv26.10.14-0001; the npm package bumps to92.0.8.
Fixed
- Published
paymentMemopattern accepts every ACH-allowed character: the published schema’spaymentMemoscalar matches the union of the ISO 20022 and NACHA addenda sets, so generated validators such aszPaymentMemostop rejecting memos such asINV_42#that the server already accepts onachandachSameDay. The per-rail server rule is unchanged and still returns422on an off-rail character. - Validation error
10030carries a generic message (breaking):ValidationErrorResponse.msgon error10030is"request validation failed"instead of the memo-specific"destination.details.memo must use NACHA-allowed characters only:"sentence that was reaching the wire on every call site. The specific reason stays incause. Prefer branching on the stableerroror numericcodeand readingcause. The npm package bumps to95.0.0. - Erebor deposit webhook amounts no longer 10,000x too small: the
deposit_address.crypto_deposit.*and needs-attribution webhooks previously divided Erebor’s cents amounts by10^6, so a $500,298.31 USDT deposit rendered as"50.029831". The dedicated Erebor formatter now converts cents through the same path the transaction row uses and formats at the asset’s precision. - Coinme crypto payout carries an amount while in flight: a same-pair Coinme crypto payout leg previously left
pricing.destinationand the olddeposit_address.crypto_payout.*webhooks with no amount until the terminal SEND webhook. The amount Coinme accepts to send is now stamped at submission (for same-pair and VA forward sends), and the terminal webhook still overwrites both with the realized amount. - Deposit address create resolves one vendor on a two-vendor corridor:
POST /deposit-addressespreviously ran two independently-timed catalog loads (the create’s own vendor resolution and the bank-rail classification gate), which could observe differentroutes.prioritystates on a corridor two vendors serve and bind the create to one vendor while the gate judged the other. The catalog is now loaded once per request and the snapshot is shared.
Wallets
OMS Wallet drops Immutable zkEVM mainnet and testnet from the supported network set ahead of the matching Sequence-infra change, so sends, bridges and the yield-proxy chain allowlist no longer offer those chains.
Updated
- Immutable zkEVM networks removed:
IMMUTABLE_ZKEVM(13371) andIMMUTABLE_ZKEVM_TESTNET(13473) are dropped fromsupportedNetworkChainIds, and the yield-proxy chain id and network name allowlists lose13371andimmutablezkevm. The vendored chain data map still carries Immutable’s USDC entry (the same convention used when zkEVM networks were previously removed), and the legal terms page is unchanged.
Polygon PoS
Bor’s
develop branch confirms a contested sequencer seal gate as soon as a reconcile sees the acked journal include that seal (eliminating a four-second seal-to-broadcast delay observed on Amoy), and exposes producer stall and slow-announce counters so slow block production can be alerted on without parsing log lines.New
- Producer stall and slow-announce metrics:
worker/build_to_announce/over2sand/over4scount exact threshold crossings independent of the scrape interval,worker/producer/stalledis a gauge that names the block number this node is stuck on as the span producer (0 otherwise), andworker/producer/stallscounts stall episodes shorter than a scrape. The existingworker/build_to_announcetimer now starts when the producer was first allowed to build (the later ofcommitWorkand the parent slot boundary), so prepare work is included but the deliberate Giugliano boundary wait is not; dashboards built on it will see slightly higher values.
Fixed
- Sequencer seal gate confirmed on our own seal through a reconcile: a contested seal gate is now confirmed as soon as a reconcile sees the acked journal include that seal, using the same hash-keyed check
retire()uses. On a tail match behindackedSeqthe frontier is no longer pulled back; it is held in place and only rewound afterlaggingReadBound(8s, tworecoverGraceintervals) when the store genuinely lost acked entries. New counterssequencer/reconcile/laggingreadandsequencer/reconcile/storebehindcover the two cases. Four Amoy blocks over the last 30 days show the pre-fix four-second seal-to-broadcast delay; with the fix the gate settles on the reconcile.
OMS
OMS Payments reports
supportedBy.wallet on GET /networks for every chain where the project’s custodial-wallet pair gate admits a wallet (not only the always-served set), resolves cash-provider DA payout legs stranded by a process death between the provider_ref stamp and the outbound call by looking the order up and adopting, failing, or deferring based on the provider’s answer (never re-sending), and adds an admin endpoint for operators to fail an interrupted leg the provider cannot answer for.New
POST /admin/transactions/{transactionId}/fail-interrupted-send: an operations-only endpoint that fails an interrupted cash-provider payout leg the provider cannot answer for (same-pair crypto legs, or a leg whose project cannot be bound), after operations has checked the provider’s dashboard. Body carries a shortreasonandacknowledgeDoubleSendRisk: true; the leg is markedfailedwithvendor_order_created=unknownand a token-authored attestation, and the partner receives the usual payout-failure event. Ineligible requests return422 InterruptedSendNotEligible(code10365) with nothing written; a repeat answersalready_failed.
Fixed
GET /networksreportssupportedBy.walleton custodial-wallet pair networks: a project whose custodial wallets live on a network outside the always-served set now seeswalleton that network’s entry, derived from the same custodial-wallet pair gate the project is already subject to and narrowed by?vendor=. Networks with no custody configuration still omitwallet; fiat and cash entries never carry it. The npm client bumps a patch to92.0.7; no type, field, or enum changes.- Interrupted cash-provider DA payout sends resolved against the provider instead of hanging
pending: a new background sweeper picks up DA payout legs stuck atpendingwithprovider_refstamped but no outcome recorded. For a bank leg it asks the cash provider for the order and either adopts it atprocessingwhen the provider has it live, marks itfailedwithvendor_order_created=unknownand emits the partner’s*_payout.failedandtransaction.statusChangedevents when the provider confirms no order (after a 30-minute lookback) or reports it failed, or leaves it pending for the next tick. For same-pair crypto legs, which the provider’s order read does not index, the sweeper only acts on an explicit failed answer; use the admin endpoint above when operations has confirmed the state. The sweeper never re-sends. - Interrupted direct bank-sell creates handed to the existing reconcile: a sibling sweeper takes direct (non-DA) bank-sell rows stuck in the create-time
processingstate withprovider_refstamped but no execute outcome, stamps asubmitInterruptedreason, and lets the existing bank-sell reconcile adopt the row when the provider has the order live or hold it for operations when the provider reports it failed. The reconcile now holds a provider-reported failure (status_reason=orderFailedByVendor) for operations rather than adopting it assubmitted.
Wallets
OMS Wallet restores network fee options when a passkey wallet with pending configuration updates sends on a chain where it isn’t deployed yet, by bumping the embedded wallet SDK stack to a release that stubs pending-update passkey signatures during the fee-options simulation.
Fixed
- Passkey wallets with pending updates get fee options back: sending from a passkey wallet with pending configuration updates on the target chain no longer fails fee-option quoting with a relayer
400 InvalidPasskeySignature. The wallet SDK is bumped (wallet-core 3.0.14, wallet-wdk / relayer / dapp-client 3.1.2, wallet-primitives / guard 3.0.13) so the pending update is simulated with a stubbed passkey signature and the on-chain checker’s chained digest validates. Google and email wallets (ECDSA signers) were never affected. A regression test pins the stubbed-signature behavior against the embedded SDK.
Polygon PoS
Bor’s
develop branch keeps a configured static peer out of the drop candidate pool when it dialed the local node first, so an inbound static peer no longer flaps once the inbound slot pool approaches capacity.Fixed
- Inbound static peers protected from the dropper: the p2p dropper now checks static-set membership on every peer, not just the outbound static-dial flag, so a peer listed in
static-nodesthat happens to dial the local node first stays connected under inbound-slot pressure instead of being evicted and reconnecting. Nodes listed in bothstatic-nodesandtrusted-nodeswere already protected viaTrusted(); static-only entries now get the same guarantee.
OMS
OMS Payments accepts a raw destination address on crypto-to-crypto
walletExternal quotes for the project’s supported routes, surfaces the customer’s active Virtual Account on bank deposit addresses under payoutOrigin.id across every provider that issues them, hides USD bank rails the project’s outbound rail restrictions exclude from the GET /networks external-account destination list, and refreshes custodial balances as soon as a wallet-to-wallet quote conversion is durable.New
payoutOrigin.idon bank deposit addresses names the customer’s active Virtual Account across every supported provider: a bank-destination deposit address whose customer holds an active Virtual Account from the same provider now records that Virtual Account’s id onDepositAddressPayoutOrigin.idacross every provider that issues Virtual Accounts, matching behavior that previously only applied on one provider.payoutOrigin.accountHolderstayscustomer, and a missing Virtual Account finder now fails the deposit-address create instead of recording an incomplete snapshot.
Updated
- Raw
walletExternaladdress accepted on crypto-to-crypto quotes for supported routes:POST /quoteswith a crypto source and awalletExternaldestination now acceptsdestination.details.blockchainAddresson the project’s supported routes, matching the behavior that already worked when the same destination was supplied asdestination.details.idnaming a registered external account. Unsupported routes continue to return422 CryptoToCryptoExternalAddressUnsupported. GET /networkshides restricted USD bank rails from external-account destinations: a USD bank rail excluded by the project’s own outbound rail restrictions no longer appears undersupportedBy.externalAccount.asDestination, so a dashboard that builds its withdraw rail list from this field stops offering rails every quote and transaction gate rejects with422 bankRailNotSupported. The response shape is unchanged.achandachSameDayshare oneachcatalog entry, so a project that permits only one of them still seesachadvertised.
Fixed
GET /walletsreflects balances immediately after a wallet-to-wallet quote conversion: when a quote converts funds between two of a customer’s own custodial wallets, the create call refreshes the customer’s custodial balances as soon as the transaction row is durable, so the project-wide wallet list (GET /walletswith nocustomerId) renders the new source and destination balances without waiting on a provider webhook, a per-customerGET /transactions/{id}poll, or the 15-minute swap-strand sweeper.
Trails
Trails lets card onramps land on the intent’s destination chain when a provider supports it, adds an
onramp.cardProviders allowlist and a widget recipe for branding a host embedded wallet as the app’s own, exposes a Trails API endpoint for claim-only AggLayer transfers Trails did not route, retires v1 intent execution, and tightens the card onramp UX end to end.New
- Card onramps land on the destination chain when the provider supports it: for a USDC destination, both card onramp paths now request their quote and widget session on the destination chain’s native USDC and land there directly, with a two-step pending diagram. A chain or token the configured provider cannot deliver falls back to USDC on Base and bridges, with the three-step diagram and the real intermediary chain and token named instead of being hardcoded.
onramp.cardProviderswidget prop: a per-widget allowlist for the Credit or debit card row. Omitted, empty, or all-unknown lists keep today’s behavior (every available provider). Excluding a provider skips its client-config request, so it costs no call and its pending state cannot hold the card row in loading.RequestAggLayerClaimon the Trails API: an integrator that bridges through the AggLayer unified bridge themselves can hand Trails(originChainId, originTxHash, depositCount?, destinationAddress?)and Trails records a syntheticCLAIM_ONLYintent and runs the destination claim on the integrator’s behalf. Gated by[agglayer] claim_service_enabled, with configurable access-key, project, and destination-chain allowlists; a repeat request for the same leaf returns the existing intent, and an already-claimed leaf creates nothing.- Host-branded embedded wallets in the wagmi adapter: an existing wagmi connection now takes
walletNameandiconfrom the hostwalletsdescriptor before the catalog, so an app-branded embedded wallet shows the app’s name and logo in the connect list, the connected chip, and the funding selector. The adapter README documents the full recipe. - Labelled amounts and provider logos in the card onramp selector: each provider row shows an “Estimated amount” caption above the destination-token amount with its USD value underneath, and renders the provider’s theme-matched logo (or a lettered placeholder on absence or failure) beside its name.
Updated
- v1 intent execution removed from the Trails API (breaking):
QuoteIntent,CommitIntent, andExecuteIntentrejectv1withErrIntentProtocolDeprecated;CreateProtocolHandlerrefuses v1. Stored v1 intents stay readable and recoverable throughGetIntent, receipts, andGetIntentHistory?byIntentProtocol=v1. The default protocol is now v1.5, and a configuration or project mapping of"v1"warns and uses v1.5 instead. - Card provider screen titled “Pay by card”: fits on one line at modal width next to the back button, the card-brand marks, and the close button. The fund screen row that leads here keeps its “Credit or debit card” label.
- Card onramp quote rejections name the limit: a provider’s below-minimum or above-maximum response renders as a specific message (for example “The minimum amount is 10.00 USD”) instead of the generic “An API error occurred” fallback. The server-side error now carries a
which is <amount> <currency>fragment the widget parses, and the amount-limit branch runs ahead of the generic API-error branch so the concrete message is not swallowed. - Card onramp providers advertised only when the deployment holds their key:
GetStripeClientConfig.enabledcombines the feature flag with a secret key for the requested environment, andGetOnrampProviderAvailabilitycounts a provider as enabled only when its client is configured. A deployment without keys hides the provider instead of failing after the user commits. - Non-selected card onramp provider amounts reflect the Trails fee model: a helper recovers the fee buffer from the selected quote, treats gas and the Trails fee as flat costs, and multiplies each other provider’s remainder by the quote’s effective rate, so provider rows read as like-for-like post-fee amounts rather than the selected row’s post-swap value against the others’ pre-swap payouts.
GetIntentHistory.balances[].tokensalways an array: an intent address with no indexed tokens now serializes as"tokens":[]instead ofnull, matching the generated clients’ required-array contract. Older SDKs that callbalance.tokens.map(...)on the history screen no longer crash.- Keyless Trails API quoting fails loudly on the SDK:
trailsApiKeyis optional throughout the SDK, andquoteIntentthrows “Trails API key is not configured” before any request when the resolved client has neither an access key nor a JWT, so integrators without a key no longer see the API’s opaqueAccessKeyNotFound401.
Fixed
- Receipt polling continues after a failed wallet send: if a wallet send fails for any reason other than a user rejection, the widget hands off to intent-receipt polling for ten minutes instead of showing the deposit-not-confirmed error while still saying “Waiting for wallet”. A late deposit the backend sees now flips the widget off the waiting screen through
onOriginSend. User rejections still fail immediately. - Card onramp popup no longer falsely reported as closed: when a provider checkout navigates to a cross-origin page that severs the opener handle, the widget skips the
window.closedinterval for that provider and relies on the provider’s transaction-status polling, matching the standalone integration. - Exchange onramp list honors the resolved API URL and provider-configured key: the demo exchange-toggle section falls back to the environment Trails API URL when its override is empty, and the SDK’s “From exchange” screen resolves its API key from the widget prop, the onramp provider config, and the Trails provider (in that order), so an integrator who configures the key once on
TrailsProvidersees integrations instead of a “No supported exchanges” message.
OMS
OMS Payments surfaces the cash code on cash pickup transactions once the provider has issued it, so clients no longer see
destination.pickupCode: null on confirmed orders.Fixed
destination.pickupCodeon cash pickup transactions:GET /transactions/{transactionId}now rendersdestination.pickupCodewhenever the cash provider has issued the code. The code is written on the execute response, on the GET-time status poll (the provider can issue the code asynchronously, after the order is already confirmed), and on the ops reconcile; a code-only change is persisted without emitting a partner event. Rows previously confirmed without a stored code pick it up on the next GET or the next status poll.
Polygon PoS
Bor’s
develop branch lands the sequence store publisher and follower that make the opt-in sequence-store preconfirmation pipeline operational end to end.New
- Sequence store publisher and follower (opt-in): block producers publish each block’s lifecycle (open context, per-transaction records, sealed header) to the sequence store as it happens; RPC nodes follow the stream, re-execute it deterministically, and hold preconfirmation receipts for the block being built. The role is derived from the sealer flag (a mining node publishes, a non-mining node consumes) and everything is gated behind a new
[sequencer]configuration section. With the section unset there is no behavior change.
AggLayer
aggkit removes the in-tree autoclaim feature entirely. Operators that need autoclaim behavior should build it as an external service against the bridge service and aggkit-proxy, which expose everything required.
Updated
- Autoclaim removed (breaking): the
autoclaim/package, the/autoclaim/v1/*REST endpoints, andGET /bridge/v1/claim-candidatesare all removed. The[AutoClaim]configuration section is gone,autoclaimis dropped from every--components=list and from theAUTOCLAIMcomponent registry, and the dedicated autoclaim docs are removed.docs/bridgetracker.mdis now self-contained, and the e2e claim helpers now route through aggkit-proxy.
CDK
The legacy CDK Node core repository is being archived as part of the broader legacy zkEVM and CDK node repository cleanup. A deprecation notice has been added, and no further pull requests will be merged into that repository.
OMS
OMS Payments widens
GET /wallets list rows to carry each wallet’s variant, lets card buys name a saved external wallet by its registered id, makes counterparty address optional on supported projects (and omits a stored-less address from responses instead of rendering {}), and adds Base USDC and USDT as virtual account destinations.New
GET /walletslist rows now carrytypeanddetails: each row is the same custodial or fiat variant thatGET /wallets/{id}returns for that wallet, withdetails.asset,details.network, anddetails.addressfor custodial rows anddetails.asset: "usd"for fiat rows. No extra per-row read is required to learn a wallet’s asset or network, andbalances,routing, andcustomerare unchanged. The npm client bumps to90.0.0:WalletListItemandzWalletListItemare replaced byWalletResourceandzWalletResourceon the list response. The server must be deployed before any consumer bumps to@polygonlabs/bpn-rest-api90.- Card buys accept a registered
walletExternalid as the destination:POST /quoteswith a card source and awalletExternaldestination now acceptsdestination.details.idnaming a saved external wallet on supported routes, matching the behavior that already worked when the same destination was supplied asdestination.details.blockchainAddress. On unsupported routes422 WalletExternalIDUnsupportedstill fires. - Base USDC and USDT available as virtual account destinations:
POST /virtual-accountsaccepts USDC and USDT on Base as a destination for USD routes, andGET /networkssurfaces Base undersupportedBy.virtualAccount.asDestinationfor USDC and USDT on projects whose configured routes include them.
Updated
- Counterparty address optional on supported projects:
POST /counterpartiesmakesaddressoptional on projects whose configuration does not require one; every other project still requires an address, and a supplied address is still validated in full. A counterparty with no stored address now omitsaddressfrom the response entirely instead of rendering a zero-value{}; this response change applies to all projects. The OpenAPI marksCounterparty.addressandCounterpartyCreateRequestCommon.addressoptional and the generated TypeScript client bumps to the next major.
OMS
OMS Payments makes
walletExternal.custodian optional (required only when the project’s resolved provisioning configuration requires it) with a new CustodianRequired 422 error, derives owner-field requirements from the resolved provisioning configuration instead of a hardcoded default, and surfaces walletExternal.details.blockchainAddress on transaction reads whose destination resolves to a registered external account.New
walletExternal.custodianis now optional: onPOST /external-accountswithtype: walletExternal,custodianis optional on the request and omitted from the response when not stored. It is still required when the project’s resolved provisioning configuration requires it. When required and missing, the response is422with the newCustodianRequirederror (code10360). The npm client bumps to88.0.0to carry the response-field optionality.
Updated
- Owner-field requirements follow the project’s resolved provisioning configuration:
POST /external-accountsandGET /reference/account-type-requirementsboth derive owner-field requirements from the same resolved provisioning configuration, so the two paths cannot disagree. A project whose owner-requirements table has no entry for its resolved provider is now rejected with422 AccountTypeRequirementsNotMetinstead of silently admitted; the missing-field list is also sorted deterministically.
Fixed
walletExternal.details.blockchainAddresssurfaces on registered destinations:GET /transactions/{transactionId}now renderswalletExternal.details.blockchainAddresswhenever the destination address is known, independent of whether the destination external account is registered. Previously a destination that resolved to a registered external account rendereddetails: null, requiring a secondGET /external-accounts/{id}call to retrieve the address.
AggLayer
agglayer tightens the legacy ECDSA certificate commitment to V5 only, lifts GER finalization gating on settlement, and lets the node route proving requests through configured endpoints. aggkit-proxy accepts separately configurable L1 and L2 RPC clients, every gRPC client gains a configurable decoding ceiling, and autoclaim surfaces each API request’s global index.
New
- Configured proving endpoints: a node can route proving requests through its configured endpoints.
- Configurable L1 and L2 RPC clients in aggkit-proxy: the proxy accepts separate L1 and L2 RPC client configuration and uses
HashFromJSONfor consistent hash decoding. - Configurable gRPC decoding ceiling:
MaxDecodingMessageSizeis now configurable per gRPC client. - Autoclaim identifies API requests by global index: autoclaim surfaces each API request’s global index for correlation across callers.
Updated
- V5 signature commitment required for legacy ECDSA certificates (breaking): the RPC accepts only the V5 signature commitment for legacy ECDSA certificates; prior commitment formats are rejected.
- GER finalization no longer gates settlement: settlement proceeds without waiting on the GER finalization requirements that previously held it.
- Glamsterdam block-hash support: the go-ethereum dependency is bumped to v1.17.7 so block hashes compute correctly on Glamsterdam.
toBlockinconsistency logs downgraded: thetoBlockinconsistency message is downgraded below ERROR to reduce noise under normal RPC skew.
Wallets
OMS Wallet drops chains that the hosted node infrastructure no longer serves and tightens the Earn (yield) proxy allowlist so unsupported chains cannot slip through on a name match. Monad and Somnia are added to the proxy’s ID allowlist.
Updated
- Yield proxy allowlist tightened: both filters (the wallet-side Earn allowlist and the yield proxy) now treat a known chain ID as final; the fuzzy name match applies only to networks reported without an ID. Monad (
143) and Somnia (5031) are added to the proxy’s ID allowlist.
Fixed
- Removed chains that are no longer served: Polygon zkEVM (
1101), Blast (81457), Blast Sepolia (168587773), Xai (660279), Xai Sepolia (37714555429), B3 Sepolia (1993), Monad Testnet (10143), Incentiv (24101), Incentiv Testnet v2 (28802), Sandbox Testnet (6252), TOY Testnet (21000000), and SKALE Nebula Testnet (37084624) are removed from the wallet’s supported-chains list. The yield proxy also dropsmoonbeam,homeverse, andoasyshomeversefrom its name-based allowlist so those networks can no longer slip through.
OMS
OMS Payments corrects same-pair crypto send settlement so a
GET no longer finalizes the transaction before the outbound crypto leg confirms, and the OpenAPI schema narrows POST /external-accounts type to the account kinds the endpoint actually accepts.Updated
POST /external-accountstypenarrowed to the create-time arms: the OpenAPIExternalAccountCreateTypeenum onPOST /external-accountsis nowbankUs,bankIban,bankCanada, orwalletExternal. Server behavior is unchanged:cardregistration continues to go through the dedicated card endpoint, andtype: "card"on the generic create still returns422 CardMustUseCardEndpoint. Reads, lists, andAccountTypeRequirementcontinue to useExternalAccountType, withcardkept as a valid value for existing card external accounts. TypeScript clients regenerated from the schema can no longer passcardto the genericPOST /external-accountsrequest.- Error-code casing in operation descriptions: public operation descriptions now cite error codes in the casing they are emitted with on the wire. Codes served through the shared registry render as PascalCase (for example
WalletNotActive,WalletTypeNotSupported); direct-emit codes such asexternalAccountAlreadyRegisteredandbridgeTimeoutkeep their camelCase. Field names, enum values, and the literal-string codes themselves are unchanged.
Fixed
- Same-pair crypto send settles to a final sub-status: a transaction whose outbound leg is a crypto send stays in
processingwithsubStatus: processing.awaitingCryptoOutuntil the send confirms, then settles withsubStatus: completed.settledon success orsubStatus: failed.outboundFailedon failure.GET /transactions/{transactionId}no longer finalizes the transaction while it is awaiting the outbound send. A send that fails after submission now carriesfailed.outboundFailedon both the stored row and thetransaction.statusChangedevent, in place of the earlierprocessing.awaitingCryptoOutleak.
OMS
The OMS public API now documents
503 ServiceUnavailable on POST /counterparties so clients can tell a transient downstream outage apart from a permanent client error on counterparty create.New
503 ServiceUnavailableonPOST /counterparties: counterparty create can now return the sharedServiceUnavailableErrorResponse(error: ServiceUnavailable,code: 5003) when a downstream dependency is unavailable. The response shape matches the 503 already used onPOST /deposit-addresses,POST /external-accounts,POST /quotes,POST /virtual-accounts, and the other create and read operations. Treat it as retryable; existing retry policies for those endpoints extend to counterparty create unchanged.
Polygon PoS
Bor’s
develop branch adds an opt-in sequence-store preconfirmation pipeline with three new RPCs, WIT2 BP-signed witness announcements with pre-import serving, and pipelined state-root computation on block import, plus a batch of execution-path and p2p fixes.New
- Sequence-store preconfirmation pipeline (opt-in): a block producer can serve pre-import preconfirmations against the sequence store with new RPCs
eth_sendRawTransactionForPreconf,bor_getPreconfTransactionReceipt, andbor_getInvalidPreconfBlocks, with store audit, read-breaker gating on a frozen tail, and preserved preconf receipts across canonical import (#2373, #2388, #2393, #2423, #2427, #2439). - WIT2 BP-signed witness announcements: Bor announces BP-signed witnesses with transitive relay and pre-import serving, a size oracle for non-deterministic witnesses within a signed size band, and a gas-derived page-count ceiling (#2208, #2416, #2417).
- Pipelined state-root computation on block import (opt-in) (#2180).
Updated
consensus/bor/heimdallhonors the configured request timeout (#2411).eth_sendRawTransactionSyncreleases the execution slot while it waits (#2385).
Fixed
- BlockSTM V2 parallel balance across self-destruct boundary resolves correctly (#2403).
- Trace log counting: logs counted without being copied when tracing (#2460).
- Txpool signer under
allow-unprotected-txs: the pool signer is no longer swapped whenallow-unprotected-txsis set (#2464). - Static peers dialed across jails and dynamic slots (#2438).
- Dangling tx-fetch peers retried instead of excluded forever (#2367).
AggLayer
aggkit lands a SQLite-backed bridgetracker cache with background-refreshed
/activity, pagination and claim status on the activity endpoint, per-destination autoclaim cursors, a public bridgeservice config endpoint, and CORS on the proxy. agglayer wires minimal certificate identifiers through network-info, retries settlement on timeouts, and recovers reverted settlement jobs.New
- Bridgetracker SQLite-backed cache: tracker and activity state cached in SQLite (#1852), with background-refreshed
/activity(#1856), on-chain-anchoredstart_date/end_date(#1864), parallelized scans with configurable bridge-service timeout (#1844), RPC-based fallback for recent bridges (#1838), claim-status reporting (pending/readyToClaim/claimed/error) (#1831), and pagination on the activity endpoint (#1888). - Public bridgeservice config endpoint:
GET /bridge/v1/configis public (#1828); sync status for all syncers exposesis_halted(#1877). - aggkit-proxy CORS and proxy metrics: REST and WebSocket endpoints accept CORS (#1807); proxy cache size and alive counts are exposed as Prometheus metrics (#1871).
- Minimal certificate identifiers on
network-info(#1740, #1758).
Updated
- Per-destination autoclaim cursors with bridge-service health sync and bridgetracker emitter verification (#1854);
EthTxManagerdefaults and start-block defaulting cleaned up (#1875); L1 finality knobs made independent (#1849); error URLs redacted and bridgeservicefinder auto-registration gated (#1862). bridgeservicefinderskips dead networks via a newIgnoreNetworkIDsfilter (#1810).- Settlement retries gateway and request timeouts (#1741, #1746).
Fixed
- Autoclaim L2-to-LX block cursor: a permanently disabled source no longer pins the cursor (#1882).
- Exact settlement tx resolution for
WaitL1SettledGER(#1866). BridgeResponse.GlobalIndexencoded asBigIntString, not a bare JSON number (#1835).l2gersyncadapted to the RPCeth_getLogsblock-range cap (#1813).- Reverted settlement jobs: superseded on certificate resend (#1800); the admin API can unlink a certificate from its reverted job (#1795); out-of-range certificate heights saturated instead of failing in metrics (#1805).
Trails
Trails adds Robinhood Chain (4663) and Arc (5042) network support, a card-onramp provider chooser routed by country, pay-with-crypto settlement quoting through a widget
stripe prop, several new widget props (defaultDisplayCurrency, disablePassthrough, consentCheckbox, labels), Tron deposit mismatch protection, OMS publishable-API-key routing through the OMS API gateway, and widget UX fixes.New
- Robinhood Chain (4663) support: live across Trails SDK (#1245) and the Trails API (#1151).
- Arc (5042) support: live across Trails SDK (#1242) and the Trails API (#1146), with USDC price alias to Ethereum USDC (#1148) and CCTP support (#1124).
- Card-onramp provider chooser by country: the widget routes the card onramp to the available provider per country, with a chooser only when more than one is available (#1274); a session-scoped geo cache gates fund methods by onramp availability (#1219, #1110, #1111).
- Pay-with-crypto settlement via the widget
stripeprop: the widget accepts astripeprop and settles USD from any token (#1241, #1106); the storefront demo pays with it (#1239); settlement quotes can choose the deposit chain (#1125). defaultDisplayCurrencywidget prop (#1277).disablePassthroughwidget prop forces the intent flow (#1248, #1153).consentCheckboxwidget prop: optional consent gate above the confirm button (#1222).labelswidget prop: overrides widget copy (#1217).intentIdon widget lifecycle callback payloads (#1247).- OMS publishable API keys accepted by the OMS API gateway, backward-compatible with existing access keys (#1160).
- Earn transaction hashes exposed by the SDK (#1234); the backend accepts yield transaction hash submission (#1123).
Updated
- Flat wallet list with per-wallet Select Chain picker and EVM-scoped connect CTA (#1228); embedded-wallet connectors hidden unless the host features them (#1270); wallets connect in any order with just-in-time EVM owner (#1225).
- Chain picker skipped for single-chain wallets (#1236); widget chain filter ordered by the shared chain order (#1254); origin ChainList filter applied in Pay mode (#1243).
- Rebasing tokens rejected at quote time with
7004 RebasingToken(#1164).
Fixed
- Tron deposits: the SDK refuses to sign an RPC transaction that doesn’t match the request (#1264).
- Widget theming: widget screens honor theme CSS variables, including the WalletConnect QR container (#1262, #1271, #1272).
- WalletConnect runtime fallback: Trails falls back to its own WalletConnect runtime when host adapters have no pairing connector (#1269).
- Funding method screen shows the history and settings header buttons (#1275).
- Reconciliation: missing REFUND rows created on reconciliation (#1156); reconciled meta-txns verified by emitter with fallback refunds recorded (#1114).
Removed
Wallets
OMS Wallet adds shielded token balances to the inventory, recovers expired-session authentication loops, prunes networks that are no longer served, and ships a batch of Earn and Private Send fixes.
New
- Shielded token balances in the wallet inventory: OMS Wallet reports shielded token balances alongside the standard inventory (#371).
Updated
- Yield proxy worker performance and reliability (#372).
Fixed
- Mobile Settings menu, Earn deposit/withdraw, Earn amount parsing/labels/vault state, and Earn accessibility (WCAG 2.2 AA), plus removal of dead chains (Moonbeam, Moonbase Alpha, Homeverse, Homeverse testnet) (#372).
- Private Send progress flow aligned to the current step model (#370).
- Expired-session authentication loops recovered automatically (#367).
- Unavailable networks removed from the UI: chains no longer served by OMS Wallet infra are dropped (#378).
OMS
The OMS public API resyncs to
v26.09.30-0002. Highlights: a dedicated GET /customers/{customerId}/risk-id lookup and a browser GET /risk/config endpoint for the OMS risk SDK, a totalBalance rollup on single-customer reads, rtp added to US bank rails, Tron added as a registered-external-wallet family, a new walletDeposit precursor type, and a new awaitingAction.informationRequested sub-status with a typed HoldInformationRequested. The transaction-create body sheds its card-routing fields: the quote now carries the card, source wallet, and delivery details.New
GET /customers/{customerId}/risk-id: reads the existing opaque risk identifier without enrolling the customer or creating a session. Returnsnullwhen the customer is not enrolled. Accepts a normal bearer token, or a publishableApi-Keypaired with a customer-bound bearer token from/customers/{customerId}/token. Browser requests require a non-empty merchantOriginvalidated against the publishable key’s origin policy.GET /risk/config: returns the browser-side provider identifiers (clientId,partnerId) for the OMS risk SDK. Authenticates with a publishable key and returns only provider identifiers; no customer identity. Missing provisioning returns503 RiskConfigUnavailable.Customer.totalBalanceon single-customer reads:GET /customers/{customerId}now returns atotalBalanceobject withtotalUsd,fiatUsd, andcryptoUsd(two decimals, sign preserved). Covers fiat wallets and custodial crypto wallets; non-custodial wallets are not yet valued. Omitted when the rollup cannot be read, so treat an absent field as “unknown” rather than zero. The list, create, and update endpoints do not carry it.rtpadded to the US bank network vocabulary:rtpis now selectable on quotes and Deposit Address destinations where the receiving bank participates, and onPOST /virtual-accounts/{virtualAccountId}/simulate/inbound-transfer. It is not supported as a Virtual Account destination rail.- Tron support for registered external wallets:
networkFamily: "tron"is accepted onPOST /external-accountswithtype: walletExternalwhere the project’s provider serves it. TheblockchainAddressmust be a mainnet Base58Check address (leadingT, case-sensitive); the zero account is rejected. walletDepositprecursor type: a transaction created from a recognized custodial-wallet deposit now rendersprecursor: { type: walletDeposit, id: null }instead ofprecursor: null. Filter onprecursorType=walletDepositto match those rows.precursor: nullis now reserved for genuine out-of-band arrivals with no recognized origin at all.awaitingAction.informationRequestedsub-status andHoldInformationRequested: a transaction can now pause for a compliance or source-of-funds follow-up.hold.typeisinformationRequested, withinstructions(human-readable, free text) andrequestedFrom(today eithercustomerordeveloper). There is no API endpoint to satisfy the request; the hold clears when the information arrives through the normal operational channel.DepositAddress.returnDestinationTypes: deposit address responses carry a read-only array listing thewalletCryptoandwalletExternalreturn targets the address accepts, derived from the issuing provider andexpectedSourceNetwork. An empty array means no return destination can be configured. The field is on API responses only; it is not included in thedepositAddress.statusChangedevent payload./cash-locationsvalidates radius and limit: the search radius must be greater than 0 and at most 100 miles; the page limit must be between 1 and 100 inclusive. Typed clients regenerated from the schema pick up the bounds asminimum/maximumconstraints.
Changed
- Card routing moves from the transaction create to the quote:
POST /transactionsis now justquoteId,webSessionId,deviceFingerprint, andmetadata.settlementType,externalAccountId,paymentMethodId,externalCustodyCreditInfo,sourceWalletAddress, andtagshave been removed from the transaction-create request. The card and source wallet are taken from the referenced quote. A card buy delivers to the quote’s destination address whendestination.typeiswalletExternal, and otherwise to the customer’s custody wallet on the destination chain; a card buy without a reachable on-chain delivery wallet is rejected with422 ExternalDeliveryWalletRequired. The related error schemas (InvalidSettlementType,ExternalAccountIDInvalid,ExternalAccountRailMismatch,ExternalAccountRequired,ExternalCustodyNotAllowed,ExternalSellUnsupported,SourceWalletAddressNotAllowed) and theExternalCustodyCreditInfoobject are removed. - Virtual Account bank routing exposes
supportedSourcesper block: each routing entry underinstructions.bankUsnow lists the sender networks it accepts insupportedSources, limited to the incoming rails the partner has enabled. A single entry can accept both US domestic and international deposits; fields that apply to none of the networks in that entry’ssupportedSourcesarenull. - US external account uniqueness tightened for custodial bank accounts: when the project’s bank accounts are registered with a custodial provider, a routing-and-account-number combination is held once per customer across the customer and all its counterparties. Re-registering one the provider already holds returns
409 externalAccountAlreadyRegisteredwithdetails.matchedOn: routingNumberAccountNumberCustomeranddetails.existingAccountIdnaming the registered account. - Richer
422descriptions on client-side rejections: Virtual Account / Deposit Address / External Account create responses carry explicit descriptions of what makes a client error terminal;/cash-ins/{cashInId}/refreshnames the two refresh-limit cases (refreshLimitReachedandrefreshTooFrequent) and whenRetry-Afteris populated. These are documentation-only improvements; the error codes and shapes are unchanged.
Removed
- Transaction-create card routing fields: see “Card routing moves from the transaction create to the quote” above.
- Error schemas tied to the previous card-create path:
InvalidSettlementTypeErrorResponse(10144),ExternalSellUnsupportedErrorResponse(10160),ExternalAccountIDInvalidErrorResponse(10257),ExternalAccountRailMismatchErrorResponse(10263),ExternalAccountRequiredErrorResponse(10264),ExternalCustodyNotAllowedErrorResponse(10265), andSourceWalletAddressNotAllowedErrorResponse(10266) are no longer emitted and are removed from the schema. TheExternalCustodyCreditInfoobject is removed with them.
OMS
The OMS public API adds a
riskId on customer responses, extends the ExternalAccountInvalidReason closed enum with additional bank and card values, exposes the provider return code alongside it, and lets GET /external-accounts filter by status.New
Customer.riskId: customer responses now include a read-onlyriskId, an opaque identifier used by the OMS browser risk SDK. It is populated once risk enrollment for the customer is complete; customers without an enrollment do not carry the field.ExternalAccount.invalidReasonCode: an external account instatus: invalidnow carries the provider’s own code for the failure alongsideinvalidReason. For US bank payouts this is the NACHA return code, for exampleR15. UseinvalidReasonto decide what to do; useinvalidReasonCodewhen you need the exact provider code for support or reconciliation.GET /external-accountsstatusfilter: the list endpoint accepts a newstatusquery parameter to filter to a single lifecycle status. Omittingstatuscontinues to exclude soft-deleted rows; passstatus=deletedto list them.
Updated
ExternalAccountInvalidReasonenum extended: the closed set gainsaccountHolderDeceasedandaccountDoesNotSupportTransfersfor bank rails, andbillingAddressMismatch,cardExpired,cardClosedOrLostStolen,pushToCardUnsupported, andcardDeclinedByIssuerfor card rails. Existing values keep their meaning; regenerated typed clients pick up the new members automatically.GET /external-accountsfilters are all optional:customerId,counterpartyId, and the newstatusfilter are all optional. Omitting them lists every non-deleted external account in the project (still tenant-scoped).
OMS
The OMS public API bumps the base path from
/v0.12 to /v0.13, adds Tron to the crypto network vocabulary, expands the walletId transaction filter to fiat wallets, and surfaces per-account rail support on bankUs external accounts.Updated
- Base path bumped to
/v0.13: sandbox is nowhttps://sandbox-api.polygon.technology/v0.13and production ishttps://api.polygon.technology/v0.13. The old/v0.12base path is no longer served. Update client configuration and any hard-coded URLs. Regenerated typed clients pick up the new base path automatically; hand-written HTTP clients must be updated. No request or response shapes change with this cutover; every schema, field, and error code carries forward as-is. GET /transactionswalletIdfilter accepts fiat wallets: thewalletIdquery parameter onGET /transactionsnow filters transactions where a crypto wallet (wlt_prefix, legacyacc_also accepted) or a fiat wallet (wlt_fiat_prefix) is the source or destination instrument. Callers that already send awlt_value see no change.
New
- Tron network in the crypto network vocabulary:
network: "tron"is now a valid value on crypto request fields, andblockchainAsset.protocoladdstvm. Availability of a given (asset, network) pair on Tron is enforced at runtime per destination type; callGET /networksfor the live catalog.NetworkFamilyon a registered external wallet acceptstronalongsideevmandsolana.usdcandusdtare the supported assets on Tron. bankUsresponses carry per-accountsupportedDestinations:GET /external-accountsandGET /external-accounts/{externalAccountId}return asupportedDestinationsarray on everybankUsaccount. Each entry is an(asset, network)pair with the rails that specific account can be paid over (ach,achSameDay,wire, and, where the underlying bank participates,rtp), plus aminimumAmountandmaximumAmountwhen the source-of-truth limits are known. The list never advertises a rail that a subsequent create-time gate would refuse; an absentrtpentry can mean either the specific bank does not participate or participation could not be verified.
OMS
OMS Payments accepts counterparty-owned US bank external accounts as payout destinations end to end, and registers the beneficiary at the paying bank under the counterparty’s own structured identity.
New
- Counterparty-owned
bankUsexternal accounts supported end to end:POST /external-accountswithowner.kind: "counterparty"andtype: "bankUs"provisions a payout destination, and a subsequentPOST /quotesreferencing the returnedext_ID asdestinationis accepted rather than rejected with422 accountTypeRequirementsNotMet. The payout settles on ACH with the counterparty as recipient. A legacy counterparty that predates the structured name split (no storedentityType) is still rejected on this rail; register the counterparty with the requiredentityTypeand name fields before quoting. - Counterparty identity used as the paying-bank beneficiary: on a counterparty-owned US bank payout, the beneficiary registered with the paying bank uses the counterparty’s own
firstNameandlastName(orbusinessName) andaddressfrom the counterparty record. The customer’s identity is not used for a counterparty-owned destination.
OMS
OMS Payments splits the counterparty name into structured fields behind a required
entityType discriminator, and bankUs external accounts now default accountType to checking when omitted and always return it on reads.Changed
POST /counterpartiesrequiresentityTypeand split name fields: the counterparty create request now takes anentityTypediscriminator ofindividualorbusinessand the matching name fields (firstNameandlastNameforindividual, with optionaldateOfBirth;businessNameforbusiness). A missingentityTypereturns422withcode: entityTypeRequired, an unrecognized value returns422withcode: entityTypeInvalid, and a flatnamefield is rejected with400. Each arm rejects unknown keys (422 validationError). Responses echo the split fields and add a read-onlynamecomposed asbusinessNameorfirstName + " " + lastName.PATCH /counterparties/{counterpartyId}immutable name andentityType: bothentityTypeand the composednamefield are rejected with400. Name updates go through the split fields for the storedentityType; a name field that does not belong to the storedentityTypereturns422 validationError. The server recomposesnameon every accepted update.
New
bankUsexternal accounts defaultaccountTypetochecking: whenbankUs.accountTypeis omitted onPOST /external-accounts, the account is created withaccountType: "checking". An explicitly suppliedcheckingorsavingsvalue continues to win.bankUsresponses always carryaccountType:GET /external-accounts,GET /external-accounts/{externalAccountId}, andPATCH /external-accounts/{externalAccountId}always populateaccountTypeon abankUsresponse object. Generated clients that previously typed the field as optional see it as required after regenerating from the schema.
OMS
OMS Payments now flips an external bank account to
invalid on a confirmed ACH payout return and cascades every dependent virtual account and deposit address to inactiveActionRequired.New
ExternalAccountInvalidReasonclosed enum oninvalidexternal accounts: an external account that transitions tostatus: "invalid"now carriesinvalidReasonfrom the closed setaccountClosed,accountFrozenByBank,routingOrAccountNumberInvalid,payeeNameMismatch,walletUnreachableOnNetwork.invalidReasonwas previously typed as a free-text string; typed clients regenerated from the schema now see it as a closed enum.invalidFieldsaccompanies each transition and lists the fields that must change for a replacement external account to be accepted.- Automatic
invalidfrom ACH returns: seven NACHA return codes (R02,R03,R04,R13,R15,R20,R23) on a US bank ACH payout now transition the destination external account fromactivetoinvalidand emitexternalAccount.statusChanged. Any active virtual account or deposit address that references the invalidated external account is fanned out toinactiveActionRequiredand emits its ownstatusChanged. Other ACH return codes remain a no-op; wire and SWIFT rails are out of scope for this transition. A deposit address that was re-pointed between the payout send and the return is left as is rather than invalidating the new destination. An account that lands ininvalidrequires registering a new external account and re-pointing dependents; every reachableinvalidReasondescribes a permanent identifier condition with no automatic recovery.
Polygon PoS
Polygon PoS activates the Austin hardfork on mainnet with Bor v2.10.0, following the Valencia and Ithaca activations earlier in the window and a Bor v2.9.1 release that turns on BlockSTM v2 parallel transaction execution and devp2p peer jailing by default.
New
- Austin hardfork activated on mainnet with Bor v2.10.0: Austin rolled out on mainnet on August 13, 2026 alongside Bor v2.10.0. Node operators must be on v2.10.0 or later.
- Ithaca hardfork activated on Amoy and mainnet: Ithaca activated on Amoy at block 40,776,000 (July 20, 2026, ~14:00 UTC) and on mainnet at block 50,185,000 (July 29, 2026, ~14:00 UTC) through Heimdall v0.10.0. Ithaca closes the post-Rio liveness hole by autonomously rotating a span when the >1/3-agreed Bor head stops advancing past a stall threshold, so a stalled block producer no longer halts Bor production while a pending milestone tally sits in the 1/3–2/3 band.
- BlockSTM v2 and devp2p peer jailing default-on in Bor v2.9.1: Bor v2.9.1 enables BlockSTM v2 parallel transaction execution and devp2p peer jailing by default. Peer jailing replaces blanket drops on transient sync failures with a graded response that jails misbehaving peers before permanent drops, keeping healthy peers on the network under load.
- Valencia hardfork release: Bor v2.9.0 is the Valencia hardfork execution client. RPC providers who have not yet moved to
v3.7.1-privshould upgrade Erigon to at least v3.6.1 before running against a post-Valencia network.
Trails
Trails ships official Solana chain support end to end in the
0xtrails 0.17.0 SDK release.New
- Solana chain support in
0xtrails0.17.0:0xtrails0.17.0 is live in production with official Solana chain support across the Trails API, SDK, Explorer, and Docs. Prior SDK versions remain compatible with the latest API; no breaking changes were introduced. The Trails Demo and Trails Explorer are updated with Solana intents.
Wallets
OMS Non-Custodial Wallet is generally available under any OMS project on the OMS Dashboard, with an interactive playground, production-ready native SDKs, and finalized docs.
New
- OMS Non-Custodial Wallet available under any OMS project: OMS Non-Custodial Wallet is live under every OMS project on the OMS Dashboard, so a project can enable and configure non-custodial wallet support without an operator handoff.
- OMS Wallet Playground: an interactive playground at
wallet-playground.polygon.technologyexercises every OMS Wallet feature end to end, so an integrator can try wallet flows before wiring a client. - Production-ready client SDKs: OMS Wallet ships production-ready TypeScript, React Native, iOS, and Android SDKs, so mobile and web clients integrate against the same authenticated wallet surface.
- Non-custodial wallet docs, including Earn and Swap guides: OMS Wallet documentation is finalized at
docs.polygon.technology/wallets/non-custodial-wallets, covering setup, Earn, and Swap.
OMS
The OMS public API bumps the base path from
/v0.11 to /v0.12. No request or response shapes change with this cutover; every schema, field, and error code carries forward as-is.Updated
- Base path bumped to
/v0.12: sandbox is nowhttps://sandbox-api.polygon.technology/v0.12and production ishttps://api.polygon.technology/v0.12. The old/v0.11base path is no longer served. Update client configuration and any hard-coded URLs. Regenerated typed clients pick up the new base path automatically; hand-written HTTP clients must be updated.
AggLayer
The AggLayer admin API retires the
set-settlement-tx-hash operation on admin_forceEditCertificate. The operation could not recover a stuck settlement, because settlement resolves through the certificate-to-job link, but it still wrote the header and looked like it succeeded.Removed
set-settlement-tx-hashretired onadmin_forceEditCertificate: every request that carries aset-settlement-tx-hashoperation is rejected withINVALID_PARAMS, whether the operation sets or clears the hash. The rejection happens while parsing the operation, before the certificate header is read, so a certificate with no settlement job is not exempt. There is no replacement: pinning the L1 block that this operation had been repurposed for when reprocessing aProvencertificate is no longer supported. Drop the operation from any operator tooling that called it.
OMS
OMS Payments makes
Transaction.subStatus always present with new default members for every top-level status, extends the optional payment memo to more bank destinations, and adds an ACH-only company discretionary data field on US bank destinations.Updated
Transaction.subStatusis now required: every transaction read and everytransaction.statusChangedwebhook now carries a concretesubStatus. Each top-level status has a default member when no more specific value applies, so the field is never absent and nevernull. Handlers that guard against a missingsubStatuscan drop the check; continue to branch onstatus, and readsubStatusfor display and logging only. The enum remains open: new members may be added without an API version bump, so unknown values should fall back to thestatusprefix before the dot (an unrecognizedprocessing.someFutureThingstill meansprocessing).- New
subStatusmembers:processing.initiated(the default for a freshly created transaction),awaitingAction.held(a default hold state),completed.settled(the default terminal-success state), andfailed.unspecified,failed.expired, andfailed.outboundFailed(default terminal-failure states). The previously documented members (for exampleprocessing.fundsPulled,processing.cashPickupReady,completed.cashPickupCollected, and thefailed.return*family) are unchanged. - Optional
memoextended to SWIFT destinations: the customer-supplied payment memo now applies to abankIbandestination (SWIFT rail) and to abankCanadadestination on the USD/SWIFT leg, in addition to abankUsdestination withnetwork: "wire". The character set, 140-character maximum, and honored-surface rules are unchanged: memo is honored on a deposit-address destination and rejected with422 memoNotSupportedon any other surface or rail (abankUsACH rail, the CAD/local leg ofbankCanada, quotes, and virtual accounts). Sendnullto restore the generated memo.
New
companyDiscretionaryDataon ACHbankUsdestinations:POST /deposit-addressesand itsPATCHre-points accept an optionaldestination.details.companyDiscretionaryDataon abankUsdestination withnetwork: "ach"ornetwork: "achSameDay". OMS carries the value verbatim in the NACHA batch header on every outbound ACH payout. This is the originator’s internal metadata and never reaches the beneficiary; it is distinct frommemo, which does. Max 20 characters; uppercase NACHA character set (A-Z,0-9, space, and& - . $ * / # @ %). Rejected with422 memoNotSupportedon abankUsdestination withnetwork: "wire".
OMS
OMS Payments renames the OMS crypto wallet instrument discriminator from
walletOms to walletCrypto, promotes walletFiat to a supported destination on virtual accounts and deposit addresses, adds a displayName field on instruments, and embeds a resolved customer summary on transactions, deposit addresses, and virtual accounts.Renamed
walletOmsrenamed towalletCrypto: the type discriminator for an OMS-custodied crypto wallet is nowwalletCryptoon every instrument surface (quote source and destination, transaction source and destination, deposit-address and virtual-account destinations, deposit-addressreturnDestination). The rejection code for using it as a payout destination is nowdestinationWalletCryptoNotSupported. The related schema names change accordingly (WalletCryptoSideRequest,WalletCryptoInstrument,WalletCryptoDestination,WalletCryptoDetails,WalletCryptoSideDetails). Update any request payloads that send"type": "walletOms"to send"type": "walletCrypto"and switch any error-code handling fromdestinationWalletOmsNotSupportedtodestinationWalletCryptoNotSupported. The wallet-ID formats (wlt_on responses; legacyacc_still accepted on requests) are unchanged.
New
walletFiatis now a supported destination on virtual accounts and deposit addresses:POST /virtual-accounts,POST /deposit-addresses, and theirPATCHre-points accept awalletFiatdestination arm that holds the inbound USD as a fiat balance in the customer’s OMS fiat wallet. Credit is delivered by an internal book transfer, so the resolved destination instrument readsnetwork: "bookTransfer"and no external rail is used. The wallet must belong to the route’s customer (422 walletCustomerMismatch), beactive(422 walletNotActive), and holdusd; the project must have thebook_transferoutgoing rail enabled (403 destinationRailNotAllowed). Auto-created transactions for a virtual account’swalletFiatdestination carrysourceToDestination: "fiatAccountToFiatAccount"; deposit address auto-created transactions carrysourceToDestination: "cryptoToFiatAccount". APATCHthat switches a destination between kinds involvingwalletFiat(for example, from awalletExternalcrypto arm to awalletFiatarm, or vice versa) is rejected with422 destinationTypeSwitchNotSupported.displayNameon instrument reads: transaction, quote, deposit-address, and virtual-account reads now include adisplayNameon each side (source, destination,returnDestination) as an opaque, render-only summary suitable for list and detail rendering without a follow-up lookup. Examples include a masked account tail such as••••1234for a bank instrument, a shortened blockchain address for a wallet, or a merchant name for a cash pickup.displayNameis not contractual: its format may change without a version bump and it never carries a full account number, IBAN, or PAN. Do not parse it.- Embedded
customersummary on transactions, deposit addresses, and virtual accounts: reads now include acustomerobject withidandname, resolved from the owning customer, so list and detail rendering does not need a follow-upGET /customers/{customerId}call.customeris always the record’s owning customer (Transaction.customerId,VirtualAccount.customerId,DepositAddress.customerId), independent of which side a party sits on. ownerDisplayNameon external accounts: reads of an external account whoseowner.kindiscustomernow include anownerDisplayNamefield carrying the customer’s display name, so external-account lists render an owner label without a follow-up fetch. The field is absent for counterparty-owned accounts (no counterparty name resolver is wired for this resource).
Updated
- Virtual account sandbox inbound simulation amount cap: the
amountaccepted onPOST /virtual-accounts/{virtualAccountId}/simulate/inbound-transferis now capped at10.00for both thebankUsandbankIbanarms; values above the cap are rejected with400 amountExceedsCap. This affects sandbox only; production behavior is unchanged.
Removed
rtpremoved frombankUsnetwork: thebankUsinstrument’snetworkno longer accepts or rendersrtp. The accepted US bank rails areach,achSameDay, andwireon every surface that carries abankUsdetails object (quote source and destination, transaction source and destination, deposit-address and virtual-account destinations, external-account reads). Requests that send"network": "rtp"are rejected as an invalid enum value; typed clients regenerated from the schema drop the member from the union.
OMS
OMS Payments adds a runtime
GET /networks reference endpoint so partners can discover every supported network from the API, and retires the deposit-address sandbox simulation route.New
GET /networks: a newReference-tagged endpoint returns every network OMS supports, with its identifiers, available assets, and which OMS resources (external accounts, deposit addresses, virtual accounts, cash-ins, and OMS wallets) can use it as a source or destination. Availability is derived from live route configuration, so a network absent from the response is not routable. Fiat-rail availability reflects the calling project’s own configuration; crypto networks are global. Each entry carriescategory(crypto,fiatAccount, orcash),type(blockchain,bankRail,card, orphysical), adisplayName, and, for blockchains,chainIdandnetworkFamily.
Removed
POST /deposit-addresses/{depositAddressId}/simulate/inbound-transfer: the deposit-address sandbox inbound-transfer simulation is retired. Continue to simulate deposit-address behavior end to end by observing the auto-created transaction and webhook flow when funds arrive; sandbox simulations for virtual accounts and cash-ins are unchanged.Sandboxtag on deposit-address simulation: the OpenAPISandboxtag no longer covers deposit addresses. The remaining sandbox simulations (virtual accounts and cash-ins) continue to group under it.
OMS
OMS Payments formalizes the wire vocabulary for crypto
network fields on deposit addresses as a shared CryptoNetwork enum, so accepted values are discoverable from the schema.Updated
CryptoNetworkenum on cryptonetworkfields:DepositAddress.expectedSourceNetworkand the deposit-addressreturnDestination.networkare now typed as the sharedCryptoNetworkenum (ethereum,polygon,base,solana). The wire values, casing, and rejection codes are unchanged:expectedSourceNetworkstill accepts onlyethereum,base, orsolanaand rejects anything else withexpectedSourceNetworkUnsupported; the returnnetworkmust equal the deposit address’sexpectedSourceNetworkfor the return to be usable. Values remain lowercase on the wire.
OMS
OMS Payments adds an optional payment memo on bank destinations and narrows the accepted crypto destinations on deposit addresses, virtual accounts, and quotes to registered external wallets.
New
- Optional
memoon bank destinations: the sharedbankUsdestination now carries an optionalmemofield that OMS delivers to the beneficiary’s bank in the ISO 20022 unstructured remittance-information field. It is honored today only on a deposit-addressbankUsdestination withnetwork: "wire", where the stored value replaces the memo OMS would otherwise generate for every outbound Fedwire payout from that deposit address. Because the memo is read when each payout wire is built, an edit affects only future payouts, never one already in flight. Max 140 characters; allowed characters are letters, digits, space, and/?:().,'+-. Sendnullto restore the generated memo. A non-empty memo on any other surface or rail (quote or virtual-account create and update, or a deposit-addressbankUsdestination on theachorachSameDayrails) is rejected with422 memoNotSupported.
Updated
walletOmsno longer accepted as a destination: onPOST /deposit-addresses,POST /virtual-accounts, andPOST /quotes(and the correspondingPATCHre-points), awalletOmsdestination is rejected with422 destinationWalletOmsNotSupported. UsewalletExternalreferencing a registered external wallet (ext_wlt_ID) to deliver crypto onward.walletOmsremains valid on the quotesource. Cash-in continues to deliver directly to an OMS wallet through its owndestination.wallet.idshape and is unaffected.
OMS
The OMS public API bumps the base path from
/v0.10 to /v0.11, replaces the webhooks surface with a full lifecycle and delivery management API, adds embedded (WaaS) wallet association on customers, renames the wallet transaction ledger to a wallet-scoped path, and adds per-resource transaction ledgers on deposit addresses and virtual accounts.New
- Base path bumped to
/v0.11: sandbox is nowhttps://sandbox-api.polygon.technology/v0.11and production ishttps://api.polygon.technology/v0.11. The old/v0.10base path is no longer served. Update client configuration and any hard-coded URLs. - Associate an embedded wallet with a customer:
POST /customers/{customerId}/wallets/associatelinks an embedded (WaaS) wallet to an existing customer. The wallet is identified by a WaaS ID token in the request body; the token’s subject is the wallet ID (so the wallet ID is never passed directly) and the token’s audience must be the authenticated project. Returns the associated wallet. - List transactions per route:
GET /deposit-addresses/{depositAddressId}/transactionsandGET /virtual-accounts/{virtualAccountId}/transactionsreturn the auto-created transaction history for a specific deposit address or virtual account, with cursor pagination and the standardtransactionType,since, anduntilfilters. - Webhook lifecycle endpoints:
POST /webhooks/{webhookId}/enablereactivates a disabled or suspended webhook and resets the failure episode.POST /webhooks/{webhookId}/disablestops dispatch and skips queued deliveries.POST /webhooks/{webhookId}/rotate-keyissues a new HMAC signing key and returns it in cleartext once.POST /webhooks/{webhookId}/testsends a synthesized test delivery so partners can verify signature and reachability.WebhookStatusis now an enum ofenabled,disabled, andsuspended, replacing the previousenabledboolean;suspendedis set only by the service when the endpoint is unhealthy. - Webhook delivery inspection and retry:
GET /webhooks/{webhookId}/deliveriesreturns paginated deliveries filterable bystatus,eventId,test, and acreatedAfter/createdBeforewindow.GET /webhooks/{webhookId}/deliveries/{deliveryId}expands a single delivery with its HTTP attempts (target URL with credentials stripped, method, headers, body snapshot, response headers and body, timing, and error).POST /webhooks/{webhookId}/deliveries/retryrequeues deliveries: pass explicitdeliveryIds, or astatusplus acreatedAtrange to retry all matching deliveries. Every retry is audited. - Webhook create response schema:
POST /webhooksreturns{ webhook, signingKey }, wheresigningKeyis the cleartext HMAC signing key. It is shown once at creation and once per rotation, and never returned again. Thewhsec_secret prefix is retired. - Webhook subscriptions replace events:
POST /webhooksandPATCH /webhooks/{webhookId}take asubscriptionsarray in place of the previouseventsfield. Pass["*"]to receive every event type, or list specific event names to filter.WebhookRequestalso acceptstimeoutMs(per-webhook HTTP timeout, default 5000, cap 10000) andnotifyThresholdSecs(down-notification threshold, 600 to 86400 seconds, default 3600).Webhookresponses addmode(liveorsandbox),statusReason,subscriptions,timeoutMs,notifyThresholdSecs, and lifetimesuccessCountandfailureCount. - Webhook list envelope:
GET /webhooksnow returnsWebhooksListwith adataarray ofWebhookListItementries, each combining thewebhookwith adeliveryCount,successCount,failureCount, afailureRatein basis points (0 to 10000), and alastDeliverysummary. Pagination moves to opaquecursorandlimit(default 20, max 100), replacingstartingAfter/endingBefore. Filter bystatus.
Updated
GET /wallets/{id}/transactionsreplacesGET /accounts/{id}/transactions: the wallet transaction ledger moves under the wallet path. Theidpath parameter accepts a wallet ID with thewlt_prefix, and the response shape and filters are unchanged.- Webhook path parameter renamed:
GET,PATCH, andDELETEon a single webhook now use{webhookId}in the path in place of the previous{id}.DELETE /webhooks/{webhookId}is now a soft delete: the webhook is hidden from list and get, but delivery history is retained for the configured retention window.
Removed
GET /accounts/{id}/transactions: retired. UseGET /wallets/{id}/transactionsinstead. The endpoint had been marked deprecated in favor of the wallet-scoped ledger.enabledboolean onWebhook: replaced by theWebhookStatusenum (enabled,disabled,suspended). Toggle state withPOST /webhooks/{webhookId}/enableandPOST /webhooks/{webhookId}/disablerather than aPATCHonenabled.whsec_signing secret prefix and thesecretfield onWebhook: the signing material is now returned assigningKeyon the create and rotate-key responses only.Webhooknever carries a signing field on read.
OMS
OMS Payments reshapes the sandbox simulation endpoints around the resource they act on, opens deposit-address destinations to onward crypto delivery, adds a fiat balance wallet as a new instrument type, makes
precursor always present on transactions, and returns a structured 422 when the payment provider terminally rejects a virtual account or deposit address create.New
- Per-resource sandbox simulations for cash-ins: cash-in simulation is now scoped to a specific
cashInId, in three lifecycle steps.POST /cash-ins/{cashInId}/simulate/present-codesimulates the customer presenting the deposit code at the register,POST /cash-ins/{cashInId}/simulate/deposit-cashcompletes the authorized deposit, andPOST /cash-ins/{cashInId}/simulate/cancelcancels an authorized deposit. The endpoints replace the previous top-levelPOST /cash-ins/simulate/barcode-auth,.../auth-commit, and.../auth-voidroutes. - Per-resource sandbox simulations for deposit addresses and virtual accounts:
POST /deposit-addresses/{depositAddressId}/simulate/inbound-transferandPOST /virtual-accounts/{virtualAccountId}/simulate/inbound-transferreplace the olderPOST /deposit-addresses/{id}/simulateandPOST /virtual-accounts/{id}/simulateroutes. Both endpoints take anamountas a decimal string in whole units. The deposit-address body carries onlyamount(asset and network are resolved from the deposit address). The virtual-account body is discriminated bytype(bankUswithnetworkset toachorwire, orbankIbanwithnetworkset toswift) and settles synchronously for domestic rails and asynchronously for SWIFT. - Crypto destinations on deposit addresses:
DepositAddressCreateRequest.destinationnow acceptswalletOmsandwalletExternalin addition to the existingbankUs,bankIban, andbankCanadabank arms. Fiat destinations producecryptoToFiatAccounttransactions; crypto destinations producecryptoToCryptotransactions and are gated toethereum,base, andsolana.walletExternaldestinations must reference a registered external account by itsext_wlt_ID.PATCH /deposit-addresses/{depositAddressId}can re-pointdestinationto either a bank or crypto target. returnWalleton deposit addresses: partners can register a crypto return destination for a deposit address at create time or throughPATCH /deposit-addresses/{depositAddressId}. The registered wallet is echoed in reads when set.walletFiatinstrument type: a new instrument arm on quote sources and transaction sides represents a customer’s fiat balance wallet held at a partner bank.QuoteSourceRequestnow acceptswalletFiatin addition towalletOmsandcard.- Structured
422on virtual account and deposit address create:POST /virtual-accountsandPOST /deposit-addressesreturn422 validationFailedwith a machine-readablecodeand a partner-safedetails.reasonwhen the payment provider terminally rejects the create call. No resource is created when the provider rejects the request. PrecursorManualon transactions: transactions with no automated origin now carry aprecursorof typemanualwith nodetailsblock, soprecursoris always present on the response.
Updated
sourceToDestinationincludes cash corridors on the union: theSourceToDestinationcomposite tag now enumeratescryptoToCashandcashToCryptoalongsidecryptoToCrypto,cryptoToFiatAccount, andfiatAccountToCrypto. The cash arms are derived from a cash pickup destination or a cash-in source.TransactionSubStatus.failed.returnedretired:failed.returnedis retired and its meaning absorbed byfailed.returnComplete, which covers both operator-triggered crypto send-backs and provider-side ACH or wire bounces with clawback under one wire value. Consumers that switched onfailed.returnedshould switch onfailed.returnCompleteinstead.- Sandbox tag replaces Simulation: the OpenAPI
Simulationtag is retired. The new per-resource simulation endpoints group under theSandboxtag. - Wallet ID format: OMS wallet IDs now use the
wlt_<typeID>format on responses; the legacyacc_<uuid>format continues to be accepted on requests. - Cash-in
fixedAmountSideechoed on the response: the side the amount was fixed on when creating a cash-in is echoed onCashIn.fixedAmountSide; OMS calculates the other side.
Removed
- Top-level cash-in simulation routes:
POST /cash-ins/simulate/barcode-auth,POST /cash-ins/simulate/auth-commit, andPOST /cash-ins/simulate/auth-voidare removed. Use the per-resource replacements listed above. - Generic simulate routes on deposit addresses and virtual accounts:
POST /deposit-addresses/{depositAddressId}/simulateandPOST /virtual-accounts/{virtualAccountId}/simulateare removed. Use the/simulate/inbound-transferreplacements listed above. - Card create through the generic
POST /external-accountsroute: registering an external account oftype = cardthrough the generic create is now rejected with422 cardMustUseCardEndpoint.cardremains a valid type for reads, lists,GET, andDELETE.
OMS
OMS Payments consolidates quote and transaction economics into a single
pricing container, refactors the source and destination shape into a typed instrument model discriminated by type (walletOms, walletExternal, bankUs, bankIban, bankCanada, card, cash), replaces the legacy origin fields on transactions with a typed precursor object, and retires the GET /customers/{customerId}/transactions route in favor of a ?customerId= filter on GET /transactions.New
- Typed instruments on
sourceanddestination: quote and transaction sides are now discriminated bytype. A quote’s source is an OMS wallet (walletOms) or a debit card (card, pull-from-card funding); the destination can bewalletOms,walletExternal,bankUs,bankIban,bankCanada,card, orcash. Each type carries its owndetailsenvelope with the instrument-specific fields (bank routing coordinates, IBAN block, card identifiers, cash pickup coordinates, and so on) and apartyblock that identifies who is on that side. - Consolidated
pricingobject on quotes and transactions: per-side amounts (amountGross,amountNet,feesDeducted), the rate pair (exchangeRate,effectiveRate), the assetpair,fixedAmountSide,sponsorGas, andsponsorGasCostall move into a single top-levelpricingobject withpricing.sourceandpricing.destinationper-side entries. The core equation is nowpricing.source.amountNet × pricing.exchangeRate = pricing.destination.amountGross. precursoron transactions: a typedprecursorobject describes what created the transaction (quote,depositAddress,virtualAccount, orcashIn) and carries that origin’s deposit instructions. It supersedes the loosequoteId/depositAddressId/virtualAccountId/cashInIdand top-leveldepositInstructionsfields.holdandawaitingActionstatus: transactions can now enter a non-terminalawaitingActionstate when blocked on developer, upstream, or compliance action. Theholdobject explains the reason and carries a deadline, and the transaction returns toprocessingonce cleared.TransactionSubStatusis now a closed set of status-scoped strings namespaced by their parent (for exampleprocessing.cashPickupReady,completed.cashPickupCollected,awaitingAction.awaitingSenderAttribution,failed.attributionTimeout).sourceToDestinationcorridor tag: a new composite tag on quotes and transactions coverscryptoToCrypto,cryptoToCash,cryptoToFiatAccount,cashToCrypto, andfiatAccountToCrypto. It replaces the oldertypeandTransferTypefor these resources;TransferTypeis retained for deposit addresses, onramp/cash-in, and customer filters.Partymodel on each side: sides now carry apartyblock discriminated byrelationship(customer,otherCustomer,externalRegistered,externalUnregistered), so two-sided flows such as remittances and B2B payouts can identify each side inline.senderCustomerId,recipientCustomerId, and the per-requestrolefield are removed; usecustomerId(the quote owner and sender) plus the party blocks instead.?customerId=filter onGET /transactions: passcustomerIdas a query parameter to scope the list to a customer. Matches rows where the customer is on either side.SettlementErrorand operatorRecovery: transactions carry aSettlementError(code,message,occurredAt,recoverable, and an optionalrecoverypath orrefund) in place of the previous genericAsyncError.
Updated
- Removed the “MVP” note from the customer
typedescription: the field description now reads simplyMust be "individual". - Card
settlementTypefield on quote requests: card rail only.internalcustodies the crypto with OMS;externaldelivers to an on-chain wallet. Defaults areexternalfor card buys andinternalfor card sells.
Removed
GET /customers/{customerId}/transactions: retired. UseGET /transactions?customerId=…for the same behavior.- Legacy top-level fields on
Transaction:senderCustomerId,recipientCustomerId,role,quoteId,cashInId,depositAddressId,virtualAccountId,depositInstructions,rail,rates,sponsorGas,sponsorGasCost,fixedAmountSide,type,completedAt, andcashPickupare removed from the top level. The information they carried now lives onpricing,precursor,source/destinationparty,sourceToDestination, andsubStatus. cashPickupReadytop-level status: cash off-ramp pickup lifecycle events surface as sub-statuses (processing.cashPickupReady,completed.cashPickupCollected,completed.cashPickupExpired) rather than as a top-level status.
AggLayer
The AggLayer settlement service now resumes pending settlement jobs when it restarts, so a restart no longer strands an in-progress settlement until something else re-drives it.
Updated
- Settlement jobs resume on service restart: on startup, the settlement service scans persisted settlement job ids and restarts any whose result is non-terminal, skipping jobs that already completed. Operator restarts and crash recovery no longer leave an active settlement job sitting idle until an unrelated event picks it back up (#1585).
OMS
OMS Payments adds debit-card buy and internal sell to the existing quotes and transactions endpoints, requires
billingAddress when registering a card external account, returns structured JWT authentication errors that distinguish expired from missing or invalid tokens, and writes a visible retry reason on transient onboarding failures so partners can tell an actively-retrying job from a stalled one.New
- Debit-card buy and internal sell on
/quotesand/transactions: partners reference the card by itsexternalAccountId(ext_…) on the quote and transaction requests and the existing pricing and transaction machinery handles the rest. Card buys produce afiatToCryptotransaction and settle either internally or externally; internal card sells produce acryptoToFiattransaction. A new optionalsettlementTypefield on the quote request (internalorexternal) is persisted on the quote and re-read at execute time.externalAccountIdis surfaced on the transactionsource(buy) anddestination(sell) in GET and list responses and on the webhook snapshot. SourcewalletIdis now optional for card buys, and a wallet target on a card sell is rejected. New validation errors includeexternal_sell_unsupported,external_account_rail_mismatch,external_account_required,external_custody_not_allowed, andsource_wallet_address_not_allowed(422), plusinvalid_wallet_target(400). External card sell is not yet supported. billingAddressrequired for card external accounts: registering an external account oftype=cardnow requires abillingAddress(addressLine1,city,state,country,zipCode). Missing or whitespace-only fields surface abilling_address_invalidvalidation error.
Updated
- Structured JWT authentication errors: failed bearer-token requests now return RFC 6750-style bodies with a stable machine code on
errorand a human-readable reason onerror_description, plus a matchingWWW-Authenticate: Bearer error="…", error_description="…"challenge header. Distinct codes let clients tell expired tokens (invalid_tokenwith"token expired") from missing (invalid_requestwith"missing authorization token"), bad signature (invalid_tokenwith"invalid token signature"), missing required project claims, and generic invalid tokens, so a dashboard can trigger a silent refresh on expiry instead of treating every401the same. A keyset-unavailable failure now returns503 temporarily_unavailableinstead of401. Note the breaking response contract change: the top-levelerrorfield is now a stable machine code rather than free-form prose; clients reading.erroras a human message need to read.error_descriptioninstead. - Visible retry reason on transient onboarding failures: when an onboarding phase hits a transient error, the onboarding record now carries a non-terminal
statusReasonsuch asretrying after transient error (attempt N): <err>, refreshed on each retry and self-cleared once the phase advances or the onboarding reaches a terminal state. An actively-retrying onboarding is no longer indistinguishable from one that is silently stuck; terminal failures continue to record their final reason as before.
Polygon PoS
Bor adds graded peer response on
develop so transient sync failures no longer churn good peers, Heimdall lands the disabled-by-default Ithaca hardfork code for stalled-producer span rotation, and Erigon v3.6.1 ships with optimizations and bug fixes that RPC providers are recommended to take before the mainnet Zurich activation.New
- Bor devp2p peer jailing on
develop: the bor downloader replaces blanket peer drops with a graded response. Transient failures (timeouts, stalls, unsynced peers, empty headers) get a 30-second local backoff, four soft strikes in a 10-minute window escalate to a 5-minute jail, a whitelist (checkpoint or milestone) mismatch backs off then jails before dropping only after persistent disagreement within a 30-minute window, and a pruned-sidechain ghost-state mismatch jails on the first occurrence and drops only on a repeat inside a 30-minute window. Every terminal drop also benches the peer for 30 minutes so it cannot dodge the drop by reconnecting. Not consensus-affecting, fully backwards-compatible, and operator-visible through neweth/downloader/peer/response/{backoff,jail,drop,mismatch}Prometheus meters and areasonfield on the existingSynchronisation failed, dropping peerlog (#2283). - Heimdall Ithaca hardfork code merged, disabled by default: a new hardfork gate adds VEBLOP pending-stall recovery so a span rotates when the agreed actual bor head stops advancing past a stall threshold, closing the post-Rio liveness hole where a stalled single block producer could halt bor production while a pending milestone tally remained in the 1/3–2/3 band. The new behavior is gated behind a per-network
ithacaHeightthat ships at0(disabled) on every network; activation heights are a release-planning decision and must be>= Rio. No stored-state migration or genesis change (#611).
Updated
- Erigon v3.6.1: a non-breaking optimization and bug-fix release. RPC providers are strongly recommended to upgrade ahead of the mainnet Zurich activation; nodes already running
v3.7.1-privrequire no action.
AggLayer
AggLayer 0.6.0 development adds a settlement-result lookup by hash, persists EIP-1559 fees per settlement attempt so replacement transactions reliably clear the standard 10% pricebump on nonce retry, and stops a retry loop from hammering L1 RPCs during indexing lag.
New
- L1 result lookup by settlement hash: operators can query the L1 settlement result for a given settlement hash directly, instead of re-deriving the result from local state (#1572).
Updated
- Gas bump on settlement retry: each settlement attempt now persists its resolved EIP-1559 fees (
max_fee_per_gas,max_priority_fee_per_gas), and a replacement transaction on the same nonce bumps both fields by at least the geth pricebump floor (10%), tracking a fresh L1 estimate and respecting the configured ceiling. When the ceiling forbids a strict bump, the run loop waits on the existing ceiling-priced attempt rather than broadcasting an underpriced replacement that execution-layer clients would reject (#1580).
Fixed
- No reorg loop during L1 indexing lag: when a nonce is observed mined but the receipt has not yet propagated to a lagging RPC node, the settlement loop now re-checks the
(wallet, nonce)mapping and treats a still-matching transaction asNotSettledYet, retrying through the existing backoff path. Only a nonce that no longer maps to the same transaction is reported as a reorg, eliminating a tight retry loop that hammered L1 during indexing lag (#1581).
Trails
Trails makes Base ETH native an unconditional relay-handoff candidate on every edge rail (Solana and Tron, origin and destination), exposes per-rail edge enablement on
RuntimeStatus, fixes a multi-wallet connection regression in the widget, and lands a broad set of edge-monitor, provider-scoring, and shutdown reliability fixes.New
- Base ETH native on all edge rails: Base ETH native is now an unconditional relay-handoff candidate on both origin and destination across every edge rail. The previous flag that gated Base-native to Solana origin only is removed; Relay quotes the pair in all four corners (EVM↔Tron, exact-input and exact-output), so suppressing it on Tron origin saved no work and hid a swap-saving candidate. The strategy race still discards candidates that fail to quote (#944).
edgeSolanaEnabledandedgeTronEnabledonRuntimeStatus: theRuntimeStatusRPC and generated Go and TypeScript clients report per-rail edge enablement derived fromconfig.edge.solana.enabledandconfig.edge.tron.enabled(#948).
Updated
- Edge monitor enablement is rail-aware: the edge monitor is considered enabled when either Solana or Tron edge is enabled, centralised behind
Config.EdgeEnabled()so a Tron-only or Solana-only deployment starts the worker correctly (#951). - Provider scoring uses real gas costs: Relay now reports origin-chain gas in
Info()(200k gas units), Hyperlane CrossCollateral reports a 3 bps fee and 30-second duration, and Relay’s default duration moves from 10s to 30s.Capabilities()returnsSupported,Trustless, andOperationsfrom a single struct, so probe paths no longer callInfo()with a zero chain ID and silently drop Relay, Hyperlane, and CCTP from candidate building (#935).
Fixed
- Multi-wallet connection regression on EVM and SVM: a widget regression that broke connecting both EVM and Solana wallets in the same session is fixed (#1122).
- Edge monitor queue starvation: persistent-error rows no longer cycle through
POLLING → PENDINGevery 5 seconds (which had been resetting the 1-hour retry timeout) and starve newer edge intents behind 10 stuck rows in the dequeue. Failing origin edge quotes now park inPOLLINGuntil stale recovery or timeout, so persistent failures age out through the existing pending-edge pipeline (#949). - Origin edge fast-retry on missing fill hash: when Relay reports terminal success before the fill or handoff hash has propagated into
txHashes, the edge quote fast-retries on the next worker cycle instead of waiting for stale-recovery to time out (#953). - Solana edge address validation: Solana origin, destination, token, and refund addresses are validated by decoding through
solana-goinstead of a regex shape check, so base58-shaped strings that decode to the wrong public-key length are now rejected. Token-style public keys remain valid where appropriate, but are not accepted as a user recipient or refund address (#943). - Terminal intents clear queued child transactions:
IntentReceipts.RefreshStatusaborts queued child transactions still inON_HOLD,PENDING, orERROREDonce the parent receipt is terminal, so/info/workersno longer shows stale dependents waiting for a release that will never come. Active child transactions inRELAYING,SENT, orMININGare preserved (#940). - Graceful-shutdown dispatch stranding: the dispatch decision in
ProcessTransactionsruns on a context detached from worker shutdown, so a SIGTERM landing after the dequeue claim no longer leaves anORIGIN/DESTINATIONrow stuck inRELAYINGuntil the 10-minute stale sweep. Receipt waits remain cancellable so shutdown still aborts long polls cleanly (#923).
Wallets
OMS Wallet hardens WalletConnect session handling, normalizes wallet, relayer, and EVM revert errors into clearer in-app messages across send, sign-transaction, recovery, WalletConnect, and Earn, adds a cookie consent banner with separate analytics toggles and PII scrubbing, and tightens Earn deposit preflight so capped vaults and unaffordable relayer fees are caught before signing.
New
- Cookie consent and analytics preferences: a consent banner now appears on first visit with separate toggles for product analytics and Google Analytics, and Account Settings exposes a re-openable analytics preferences entry. Analytics initialize only after consent and stop again on revoke; GA Consent Mode is updated and
_ga*cookies are cleared on revoke. Wallet addresses, hashes, emails, bare hex values, ENS names, and sensitive URL substrings are scrubbed from analytics payloads before they leave the client, and contact and watched-wallet nicknames are no longer sent (#286). - Earn deposit preflight and vault status guards: ERC-4626
maxDeposit(receiver)andpreviewDeposit(amount)are checked before relayer execution, capped vaults surface a friendly vault-capacity error, and a fee-balance guard blocks deposits and withdrawals where amount plus relayer fee would exceed the selected token balance. Yield vault status (entry and exit availability, capacity state, entry limits, risk metadata) is now preserved through the proxy cache, so deposits and withdrawals are disabled with a clear reason when Yield reports the vault is not accepting them. Possible fees show as informational detail-page copy, not warning badges (#285).
Updated
- Wallet, relayer, and EVM revert errors normalize app-wide: a vendored
wallet-errorsmodule decodes wallet, relayer, and EVM revert errors (includingError(string)andAllCapsReached()) into structured, friendly messages and is wired through send, sign-transaction, recovery, WalletConnect, Earn deposit, and Earn withdraw. Raw error details (code,name,cause, original message) are preserved for analytics and debugging while user-facing copy is clearer. Failed relayer quotes are no longer classified as sponsored or free-gas options, and a stale selected relayer ID is cleared once relayer options refresh (#291).
Fixed
- WalletConnect session-request handling and stale modal UX: duplicate transaction modals from WalletConnect re-delivering unresponded
session_requestevents on every relay reconnect are de-duplicated by an in-memory seen-request set, closing the modal now bulk-deletes all pending transactions instead of triggering a close/reopen loop, pending transactions older than 10 minutes are cleaned up on WalletKit init and every 60 seconds, the sign-transaction modal opens reliably on tab focus through the document visibility API instead of the non-reactivedocument.hasFocus(), session-proposal unmount no longer cancels approvals that triggered a navigation, and unimplemented WC methods (eth_sign,eth_signTypedData,eth_sendRawTransaction, unsupported defaults) now reply withUNSUPPORTED_METHODSso dapps get a real error. The confirm button is also disabled until the relayer fee option is selected (#329). - Trails history button restored in the wallet’s Trails integration (#323).
OMS
The OMS public API surfaces a structured on-chain identity for assets on wallets and transactions, so integrators can read the underlying chain, protocol family, and token without parsing the human-readable
chain and asset strings.New
BlockchainAssetschema: a new component that combines the chain identifier (chainId), the wire-level protocol family (protocol:evm,svm, orsui), and the token identifier on that chain (tokenId). All three fields are required when the object is present.BlockchainProtocolenum:evmfor Ethereum-compatible chains,svmfor Solana, andsuifor the Sui Move VM.blockchainAssetfield on wallets, customer wallets, transactions, and wallet balances:Wallet,CustomerWallet,TransactionSource,TransactionDestination, and thedatapayload ofGET /wallets/{walletId}/balancenow include an optionalblockchainAssetobject. The field is additive: existingchainandassetstrings continue to be returned, so current integrations keep working unchanged.
OMS
residentialAddressandidentifyingInformationonPOST /customersandPATCH /customers/{customerId}: both fields are now accepted on the public surface and persisted through to compliance review.ResidentialAddressandIdentificationDocumentare exposed as reusable component schemas.- Required fields on
IdentificationDocument:type,issuingCountry, andnumberare now required when an identification document is supplied. - Single E.164 phone field: the public customer schema standardizes on one E.164-formatted
phoneNumberfield, replacing the prior pair of country code plus subscriber number.
Polygon PoS
Bor v2.8.3 ships stable across mainnet block producers, and the mainnet block gas limit lifts from 140 million to 160 million, completing the throughput target announced earlier this month.
New
- Mainnet block gas limit raised to 160 million: the planned increase from 140 million to 160 million gas has rolled out on mainnet, lifting the effective execution rate to around 106.66 Mgas/s at the 1.5-second block time. This is the final scheduled step on the path to roughly 5,079 TPS using POL transfers (21,000 gas) as the unit transaction.
Updated
- Bor v2.8.3 stable rolled out on mainnet block producers: bor v2.8.3 was cut from a multi-iteration
v2.8.3-candidateline on Amoy and promoted to mainnet block producers around June 16 to 17. The release rolls up the v2.8.3-beta fixes, including a milestone-mismatch rewind deadlock fix and the related revert from beta4.
Trails
Trails adds a gasless option to the React send hook, makes wagmi 3 the default widget integration, ships Hyperlane CrossCollateral routes with bridge-gas reimbursement, and lands a wide set of widget UX and worker-side reliability fixes.
New
gaslessoption onuseTrailsSendTransaction: integrators can opt into gasless execution directly from the React send hook.- Hyperlane CrossCollateral routes with bridge-gas reimbursement: Hyperlane CrossCollateral is wired in as a route option, with bridge-gas reimbursement so destinations receive the expected amount.
- Timed refund leaf on 1.5 intents: v1.5 intents now include a timed refund leaf, so refunds can be claimed deterministically once the timeout window elapses.
/info/workersqueue filters and admin intent-receipt refresh: the admin workers endpoint accepts queue filters and exposes an admin intent-receipt status refresh.
Updated
- Wagmi 3 is the default widget integration.
- EVM-first connect ordering: the connect dialog requires an EVM wallet connection before exposing Solana wallets, matching the EVM-anchored intent model. The SVM tab is also hidden when the Solana edge is not enabled by the API.
- Auto-sync of origin and destination chains: the widget auto-syncs selected origin and destination chains with the connected wallet and the entered recipient, and improves SVM and EVM address validation.
- Balance hooks: balance hooks now take an object-shaped params argument.
- Hyperlane monitor cadence: poll interval reduced from 15 seconds to 3 seconds for faster origin-to-destination dispatch, with CrossCollateral dispatched only after the origin receipt is confirmed.
/info/workerspending-intents speedup: the operator endpoint that lists pending intents is materially faster.
Fixed
- SOL pricing in Pay mode: SOL prices now resolve correctly in Pay mode.
- Chain resolution drift: chain selection no longer drifts between widget state and the underlying connector.
Add walletprop control: the fund-methods screen exposes a prop to hide “Add wallet” cleanly.- CCTP supported chains: CCTP routes honor the
chain_idsconfig to restrict supported chains. - Hyperlane decimals: collateral checks and quote amounts normalize decimals for Hyperlane routes, and route decimal conversions are normalized across providers.
- Legacy receipts: receipts with a null deposit transaction are refreshed correctly.
- Timeouts and retries: dispatch and execution timeouts are bounded, ancient executing intents abort cleanly, and
WaitIntentReceiptgetLogsbackoff is fixed so receipts do not stall behind RPC rate limits. - Intent
SUCCEEDEDgating: intentSUCCEEDEDstatus is now gated on destination edge delivery confirmation, with related fee and quote-path consolidation in dispatch.
Polygon PoS
Bor v2.8.3 stable ships with parallel-EVM (BlockSTM v2) consensus-correctness fixes, and Heimdall v0.9.0 lands the Zurich hardfork release: Amoy activates around block 37,750,000 on June 17, mainnet around block 47,880,000 on June 25 at 14:00 UTC.
New
- Zurich hardfork on Heimdall: v0.9.0 promotes the Zurich hardfork to mainnet. At the activation height,
x/clerkswitches to deterministic state-sync processing with block-height-based event visibility assigned inPreBlocker; bothPrepareProposalandProcessProposalenforce symmetric side-transaction caps (50/block) with wall-clock budgets for proposal construction (500 ms) and vote-extension generation (800 ms); checkpoint signature aggregation moves to commit-only; milestone propositions bind deterministically to the parent hash;MsgVoteProducersis restricted to the active validator set; and new ante decorators add a 16-output cap on aggregate bank transfers and 32-byte validation onMsgCheckpoint.AccountRootHash. All nodes must upgrade before their network’s activation height (Amoy37,750,000, mainnet47,880,000); pre-activation behavior is unchanged, no store migration or genesis change is required (#610). - Full Heimdall↔Bor gRPC transport in Heimdall v0.9.0: opt-in via
bor_grpc_flag, requires bor v2.8.3 or newer, with a startup hash-parity check across HTTP and gRPC before traffic switches. Batched milestone-proposition calls are around 4.4× faster than the HTTP path (#610). - Bor endpoint failover with health probing in Heimdall: non-producer nodes can fail over between configured bor endpoints when health probes mark an endpoint unhealthy; failover is explicitly refused on block-producing nodes to preserve a single source of truth (#610).
- EIP-1559 L1 transactions from Heimdall: bridge submissions and other L1 transactions now post as type-2 EIP-1559 transactions with operator-configurable gas caps (#610).
Updated
- Bor v2.8.3 stable released: the v2.8.3-candidate branch promoted to stable after five Amoy soak iterations. Required for operators running Heimdall v0.9.0 with the gRPC transport enabled (#2272).
Fixed
- Parallel-EVM (BlockSTM v2) consensus correctness: three independent BlockSTM v2 vs serial-execution divergences, each capable of producing a bad block, are fixed at the
core/statelayer. A 7702-delegatedCodePathread no longer splits across snapshot versions and poison the cross-block jumpdest cache;Existnow recognises accounts made to exist purely by a prior in-block nonce bump (sender increment or 7702 delegation clear), correctly applying the existing-account refund and avoiding the new-account CALL surcharge; and transfer-log emission now matches byBalanceOpsIdxinstead of by transfer shape, so a same-sender/same-recipient/same-amount selfdestruct payout no longer steals a later transfer’s log emission point. SerialStateDBandcore/vmare untouched; non-parallel nodes are unaffected (#2270). - Heimdall proposer-local liveness on dense blocks:
PrepareProposalpreviously sized the proposal with rawlen(tx), but CometBFT validates the returned proposal using the protobuf-encoded size ofData.Txs. Under high tx-count load the proposer could assemble a batch that passed its own check yet exceededMaxTxBytesonce CometBFT re-measured it, stalling block production on that proposer. Accounting now uses the same protobuf unit CometBFT uses (#608).
Trails
Trails adds Hyperlane CrossCollateral routes with bridge-gas reimbursement on the API side, tightens the widget connect-wallet flow to require an EVM wallet before Solana wallets surface, and ships a broad set of dispatch and quote reliability fixes.
New
- Hyperlane CrossCollateral routes: a new Hyperlane route variant covers cross-collateral cases that the standard Warp Route flow cannot, including bridge-gas reimbursement on the destination side. Routing dispatches the CrossCollateral call after the origin receipt lands so the destination side only acts once the origin commitment is confirmed (#843, #913).
- wagmi 3 as the default widget integration: the Trails widget and demo now ship wagmi 3 as the default wagmi path, with Privy and Sequence Connect using wagmi3-native provider/connector stacks and
viempinned to Privy’s peer requirement (#1102).
Updated
- Connect-wallet flow requires an EVM wallet before Solana: the widget’s Connect Wallet screen no longer lets users connect a Solana wallet before an EVM wallet is connected, both in the widget and in the standalone connect screen. The widget also enforces an EVM wallet before allowing the connect screen to be left (#1099, #1096).
- SVM wallet tab hidden when Solana edge is not enabled: when the API returns
edges: []for Solana, the widget hides the SVM wallet tab and all Solana wallet options, including both installable and already-detected Solana wallets (#1101). - Hyperlane monitor polling interval reduced: the Hyperlane monitor now polls every 3 seconds instead of every 15 seconds, shortening time-to-state-transition on Hyperlane intents (#912).
Fixed
- CCTP
chain_idsconfig honoured: CCTP route discovery now respects the configuredchain_idslist to restrict the set of supported chains, instead of advertising chains outside the operator’s configuration (#915). - Intent SUCCEEDED gated on destination edge delivery: an intent only transitions to SUCCEEDED once destination edge delivery is confirmed, removing a class of premature SUCCEEDED states on edge-backed routes (#893).
- Dispatch precheck ERRORED, worker interfaces, and admin transitions: dispatch precheck failures now route through the ERRORED path, worker interfaces are narrowed to the operations each worker actually performs, and admin-driven state transitions are centralised so they cannot diverge from worker-driven ones (#897).
- Ancient executing intents aborted: intents stuck in an executing state past their bound are now aborted instead of holding worker capacity indefinitely (#922).
- Timeout and retry bounds: dispatch timeout and retry handling are now explicitly bounded, preventing unbounded retry loops on transient errors (#920).
- Hyperlane decimal normalisation: the Hyperlane collateral check and quote amounts now normalise token decimals before comparing, fixing under-collateralisation false positives on tokens with non-default decimals (#917).
- Route decimal conversions normalised across the API quote path (#919).
- Legacy receipts with null deposit transaction: legacy receipts that recorded a null deposit transaction now refresh correctly through the receipt refresh path (#918).
- Fee and quote-path consolidation: a set of fee-collector and quote-path edge cases are consolidated into a single corrected path (#898).
/info/workerspending intents: the pending-intents path of/info/workersis now substantially faster on busy deployments (#928).
Polygon PoS
Heimdall v0.9.0 mainnet release schedules the Zurich hardfork for June 25, bor v2.8.3 ships as the matching stable client, and the mainnet block gas limit increases from 140M to 160M while holding 1.5s blocks.
New
- Mainnet block gas limit raised to 160M: with 1.5s blocks held, the upgrade lifts sustained throughput to roughly 5,000 payments per second at the same fee and reliability characteristics. Amoy continues at 200M for stress testing.
- Heimdall v0.9.0 mainnet release, Zurich hardfork: activates on mainnet at block
47,880,000(approximately June 25, 2026, 14:00 UTC), with Amoy already activated at block37,750,000on June 17, 14:00 UTC. The fork gates a set of consensus-level changes that stay inactive until the activation height: deterministic state-sync processing inx/clerkwith block-height-based event visibility assigned inPreBlocker; symmetric side-transaction caps of 50 per block inPrepareProposalandProcessProposal, with wall-clock budgets of 500 ms for proposal construction and 800 ms for vote-extension generation; commit-only checkpoint signature aggregation; deterministic milestone-proposition parent-hash binding;MsgVoteProducersrestricted to the active validator set; and new ante decorators (16-output aggregate bank-transfer cap andMsgCheckpoint.AccountRootHash32-byte validation). Validators must upgrade before their network’s activation height (#610). - Heimdall to bor gRPC transport (opt-in): heimdall ships full gRPC transport for the heimdall to bor channel, enabled with the
bor_grpc_flagconfig and requiring bor v2.8.3+. Startup hash-parity check and batched milestone-proposition calls included; internal benchmarks show roughly 45% lowerextendVotelatency on the heaviestGetBorChainBlockInfoInBatchpath versus HTTP (#610). - Bor endpoint failover with health probing: heimdall now supports multiple bor endpoints with health-probed failover. Disabled on block producers (#610).
- EIP-1559 L1 transactions in heimdall: heimdall now uses the EIP-1559 fee market for its L1 transactions, with safe-default gas caps (#610).
- Bor v2.8.3 stable release: backwards-compatible client cut from
v2.8.3-candidateafter five Amoy beta iterations. Recommended for all operators ahead of the Zurich activation. Includes the milestone-mismatch rewind deadlock fix landed inv2.8.3-beta5(#2272).
Fixed
- Parallel-EVM (BlockSTM v2) consensus divergences: three independent BlockSTM v2 vs serial divergences, each able to produce a bad block, are fixed at the
core/statelayer. (1) Per-tx CodePath read snapshot consistency for EIP-7702-delegated code, addressing theindex out of rangepanics invalidJumpdestandinvalid gas usedbad blocks observed on mainnet. (2)Existnow considers prior-tx nonce bumps, soapplyAuthorization’s existing-account refund and the new-account CALL surcharge match serial. (3) Settlement transfer-log mispairing intryEmitTransferAtnow matches onTransferRecord.BalanceOpsIdxrather than transfer shape, eliminating the receipts-root-only divergence from EIP-6780 selfdestruct payouts. SerialStateDBandcore/vmare untouched, so non-parallel nodes are unaffected (#2270). - Heimdall proposer liveness under high tx-count load:
PrepareProposalaccounting now uses the same protobuf-encoded size that CometBFT uses to validate the returned proposal. Rawlen(tx)could undercount per-tx protobuf overhead and let a proposer assemble a batch that passed its own check but exceededMaxTxByteson CometBFT’s re-measurement, stalling block production on that proposer. Proposer-local fix; not consensus-affecting (#608).
AggLayer
AggLayer 0.6.0 development gains a settlement-side wallet-and-nonce wait gate and certificate-to-settlement-job persistence so certificate tasks can resolve settlement jobs directly.
Updated
- Wait for any new expected
(wallet, nonce)to be on L1: settlement submission now waits until any newly expected(wallet, nonce)is observed on L1, closing a race that could surface during recovery and re-submission (#1571). - Certificate-to-settlement-job ID persistence: the state store persists the mapping from certificate IDs to settlement-job IDs through a new
certificate_settlement_job_cfcolumn family with insert-only semantics and a migration for existing databases. Certificate tasks can resolve settlement jobs directly without re-deriving the link (#1578).
Trails
Trails adds Hyperlane CrossCollateral stablecoin routes across Arbitrum, Base, and Ethereum, cuts CrossCollateral end-to-end latency on the order of
47s by dispatching the destination right after origin receipt and tightening the Hyperlane monitor poll, fixes a cross-decimal collateral bug that was stranding intents, and gates EVM and Solana intent completion on confirmed destination fills.New
- Hyperlane CrossCollateral routes: new
CROSS/moonpayUSDC and USDT route set across Arbitrum, Base, and Ethereum, including same-asset, cross-asset, and same-chain coverage. Uses the Hyperlane v4CrossCollateralRouterwithtransferRemoteToandquoteTransferRemoteTo. For overlapping USDC routes,CROSS/moonpayis preferred explicitly rather than by alphabetical order. Exact-input fee accounting reserves provider source-token overage and reimburses relayer-fronted native bridge gas through the origin-token collector sweep, so total fee no longer double-counts bridge gas (#843). - wagmi 3 as the default widget integration: the Trails wagmi integration is now wagmi 3 by default in the widget (#1102).
- CCTP
chain_idsconfig now honored: settingchain_idsunder[cctp]inapi.confactually restricts the supported chains for CCTP routes, aligning behavior with every other route provider. Previously the field was declared but silently ignored (#915).
Updated
- Hide SVM wallet tab when Solana edge is not enabled by the API: the widget gates the SVM tab and all Solana wallet options on
useSolanaEdgeActive()in addition to the adapter check. If the API returnsedges: []for Solana, the SVM tab and any detected or connected Solana wallets are hidden (#1101). - Require EVM wallet connection before connecting Solana: the widget’s connect screen now requires an EVM wallet before allowing a Solana wallet, and the widget itself no longer leaves the connect screen without one (#1099, #1096).
- Hyperlane CrossCollateral destination dispatch latency cut by approximately
47s: CrossCollateral now releases the destination transaction as soon as the origin receipt proves the Hyperlane dispatch metadata, instead of waiting for Hyperlane API delivery visibility. Worker-owned early release replaces the API delivery wait; destination preconditions still gate execution (#913). - Hyperlane monitor poll interval reduced from 15s to 3s: cuts average Hyperlane delivery-detection latency from roughly
7.5sto1.5s, the largest controllable overhead on CrossCollateral end-to-end time. The 10s GraphQL HTTP timeout keeps cycles from overlapping (#912). - Fee and quote-path consolidation: exact-input fee totals are computed per trade type in the fee calculator; gas-estimation RPCs are cached during quoting; provider fallback cascades are bounded; v1 and v1.5 protocol handlers share a single
FeeCalculatorinstance; zero-value fees and gas are initialized for testnet chains so testnet quotes no longer hit a nil-pointer (#898).
Fixed
- Hyperlane cross-decimal collateral check (caused two stuck user intents): collateral sufficiency now scales the origin-denominated transfer amount to destination decimals before comparing, instead of comparing destination-denominated collateral against a raw origin transfer amount. For Polygon to BSC 6 to 18 USDC routes, raw collateral
18.45e18was numerically larger than raw transfer amount644e6, so an insufficient-collateral case still passed the check. QuoteToAmountandToAmountUSDare now destination-denominated, and EXACT_OUTPUT requests are normalized back to origin decimals before quoting and packingtransferRemotecalldata (#917, #919). - EVM to Solana intent completion gated on destination edge delivery: for DESTINATION-mode edge intents,
WaitIntentReceiptno longer returnsDone=trueuntil the edge monitor confirms the Solana fill, failure, or refund. The intent holds atEXECUTINGuntil the edge worker terminates, thenRefreshStatusderives the terminal status (Completed to SUCCEEDED, Failed to ABORTED, Refunded to REFUNDED). A delivery bound on the bridge transactionTxnMinedAtexits the hold if the destination delivery stalls (#893). - Timeout, cancellation, and retry hardening: CCTP now uses an
http.Clientwith a 10s timeout instead of the no-timeout default; origin-edge success ordering callsExecuteIntentbefore marking the edgeCOMPLETEDand parks retryable failures inPOLLINGwith the fill hash preserved; deposit validation no longer marks potentially funded depositsFAILEDwhen shutdown cancels the retry wait, falling through to the existing fail-open provider fallback; deposit-dependent transaction release now uses a background context after the deposit is accepted, so shutdown cannot strandORIGINorDESTINATIONrowsON_HOLD(#920). - Dispatch precheck on guard error: when
shouldDispatchToRelayererrors, the transaction transitions toERROREDwith a status reason and a retry metric, instead of being silently skipped and re-dequeued forever. Context cancellation is excluded so shutdown does not produce spuriousERROREDwrites (#897).
Polygon PoS
Heimdall v0.9.0-beta announces the Zurich hard fork, with activation blocks scheduled on Amoy and mainnet, and Bor v2.8.3 ships as a non-breaking performance and stability release.
New
- Zurich hard fork activation scheduled: heimdall v0.9.0-beta defines the Zurich hard fork with activation at block
37,750,000on Amoy (June 17, ~14:00 UTC) and block47,880,000on mainnet (June 25, ~14:00 UTC). All validators, RPC providers, and node operators should upgrade their heimdall-v2 nodes before the activation block on each network.
Updated
- Bor v2.8.3 stable: non-breaking release focused on performance and stability improvements, validated through an extended Amoy testing period. No operator action beyond the standard upgrade cycle is required.
- Heimdall v0.8.2 stable: matching mainnet release of the v0.8.x line for operators that need to remain on v0.8.x ahead of the Zurich activation on mainnet.
AggLayer
AggLayer is now fully upgraded to the SP1 Hypercube (v6) proving system across all connected networks, including mainnet.
New
- SP1 Hypercube (v6) live on mainnet: AggLayer mainnet recorded its first L1 settlement on the SP1 Hypercube prover on June 16, completing the v5 → v6 migration across devnets, testnets, and mainnet. The upgraded prover is up to 5x faster on proving speed, more cost effective, and brings security hardening improvements. Backward-compatible reads of legacy v5 proofs and certificates remain in place over the migration window, so no caller-side changes are required.
AggLayer
AggLayer 0.6.0 development continues with operator usability and recovery improvements, and AggKit gains a new exit-certificate claimer service alongside resilient sync recovery for the legacy-syncer upgrade path.
New
- Exit-certificate claimer service: a new
exit_certificate_claimerHTTP service in AggKit serves the data needed to callclaimAsseton L1 for the bridge exits available to an address, with helper scripts (list-bridges.sh,claim-asset.sh,claim-all.sh). The service can run standalone or derive its config from an existing exit-certificate run via--exit-certificate-config. Default port7080(#1650). - Off-chain LER computation in the exit-certificate tool: Step G is split into G1 (lite L2 bridge sync) and G2 (
NewLocalExitRootcomputation). G2 now supports an off-chain mode that computes the LER directly from the lite exit tree without an Anvil shadow-fork, alongside the default shadow-fork verification mode.exitAddressis now mandatory and several option names switch to theignore*convention. Config files now accept JSON or TOML (#1633). - Settlement attempt and job persistence: settlement attempts and jobs are persisted to storage before submission to L1, so pending settlements can recover attempt metadata and job payload after a node restart (#1550, #1551).
Updated
- Docker image version reporting: agglayer Docker and CI builds now stamp a real git-derived version at compile time, so the node startup log prints the version instead of
VERGEN_IDEMPOTENT_OUTPUT(#1568).
Fixed
- Stuck settled-but-InError certificates: re-certification now queries the aggchain hash at the pre-settlement L1 block instead of
latest, so stateful aggchain contracts no longer revert reconciliation withL2BlockNumberLessThanNextBlockNumber()afternextBlockNumberadvances past the certificate’s L2 range. L1 archive access is required on the same path that already required it (#1563). l1infotreesyncupgrade loop: upgrading from a legacy syncer (for example 0.8.x) to the multidownloader implementation no longer leaves the syncer stuck withnot found in storage or blocks_reorged. When the last-processed block is at or below the finalized block, the multidownloader validates it against L1 by hash and resumes fromlastBlock+1on a match, removing the need to manually delete the DB (#1639, #1645).- Bare-filename config paths: agglayer accepts a bare filename config path (for example
agglayer.toml) again, without requiring a parent directory (#1553).
Trails
Trails ships a wide set of EVM↔Solana fixes after the EVM↔Solana edge support merge, hardens intent and transaction reliability on the API side, and adds an operator flag to disable the v1 intent protocol.
New
- Unified wallet connection with SVM: the widget’s wallet connection flow surfaces EVM and Solana wallets in the same connect dialog (#1019).
- Wallet screening for quote requests: quote requests are subject to wallet screening before a quote is returned (#853).
intents.disable_v1_intent_protocolconfig andGetSupportedIntentProtocolsAPI: deployments can disable the v1 intent protocol, and the supported-protocols RPC reflects the setting (#876, #884).- Recipient picker pulse and a same-chain WETH↔ETH calldata demo scenario in the widget (#1080, #1079).
Updated
- Source-token selector: shows all wallet tokens and supports API-based search inline (#1006).
- Solana wallet cancellations are retryable: cancellations from Solana wallets during signing are treated as retryable rather than fatal (#1072).
- Solana destination edge timeline: Solana destination edge transaction states are included in intent state, and duplicate edge-tx rows in the Solana edge timeline are removed (#866, #1076).
- Default connector set: select native wallet connectors are removed from the widget’s default connector set (#1068).
Fixed
- Mainnet CCTP routes: a testnet-mode gate that was breaking mainnet CCTP routes is removed (#861).
- Expired persisted fee quotes: dispatch now regenerates a fresh fee quote when the persisted quote is within 1 minute of its 10-minute expiry, instead of sending an expired quote and being rejected. Stuck DESTINATION transactions also time out after 1 hour rather than waiting forever and starving the dispatch queue (#868).
- Deposit starvation behind in-flight rows: the dispatch dequeue no longer includes
RELAYING/MININGrows, so stale in-flight rows from crashed or restarted workers no longer block recovery or starve deposit-dependent transactions (#858). - Same-chain calldata cases: same-chain swaps with calldata, including same-chain same-token with exact input, route correctly (#1074, #1078).
- Solana destination flows: destination token selection updates correctly, destination fills no longer double-fill, and Fund is disabled until a Solana recipient is selected when the destination is Solana (#1073, #1082, #1083).
- Edge metadata rail persistence is retained correctly on intent records (#857).
IntentTransactionGasFee.totalFeeUsdis now typed asfloat64instead ofstring, aligning the API contract with how it is serialized (#854).- Widget polish: connect-wallet close button, WETH demo icons, clipboard fallback compatibility, and inline-SVG canvas rendering (#1070, #1088, #1085, #1086).
Wallets
OMS Wallet rebuilds the Private Send experience on a REST send flow with a dedicated recovery surface, and adds a wallet screening auth gate across sign-in and protected-route entry points.
New
- Private Send REST flow and status UI: Private Send now runs on the REST send flow with fee quoting, order creation, deposit submission, order polling, and a five-step lifecycle and status view. Failures after the deposit hand-off are tracked as recoverable rather than fatal (#304).
- Private Send recovery: a Settings recovery surface lets a connected wallet look up saved recoverable orders, inspect stuck balances, and submit stuck-UTXO withdrawals (#306).
- Wallet screening auth gate: authenticated wallets and connected EOAs are checked against a screening service before sign-in, OAuth callback, route loaders, auth-token setup, and the experimental wallet and login routes proceed. Blocked wallets are cleared from local auth state, redirected to sign-in, and shown a blocked-wallet notice. Screening URL and key are configured via environment variables (#287).
Updated
- Private Send availability analytics: availability now reports
unsupported_chainfor non-supported EVM chains andunsupported_tokenfor collectibles and non-coin records before checking other eligibility (#305).
OMS
The OMS public token endpoint now publishes a
429 Too Many Requests response so clients can handle rate-limited responses correctly.New
429response onPOST /auth/token(get bearer token): the token endpoint is rate-limited per client IP. When the limit is exceeded, the endpoint returns429with anErrorResponsebody and aRetry-Afterresponse header carrying the integer number of seconds to wait. Clients should branch on429alongside200,400,401, and500and honorRetry-Afterbefore retrying.
OMS
The OMS public API consolidates the account-balance and account-transactions endpoints under the Wallet surface and exposes a synchronous failure response on transaction execution.
Removed
GET /accounts/{id}/balance: the standalone account-balance endpoint is no longer part of the public API. Read balances throughGET /wallets/{id}/balanceinstead. TheAccountBalanceschema is removed.Accounttag: theAccountgrouping is dropped from the API. Endpoints that referenced it now live underWallet.
Updated
GET /accounts/{id}/transactionsis now a Wallet operation: the endpoint is re-tagged fromAccounttoWalletand the summary becomes “List wallet transactions.” The path parameteridnow expects a wallet ID with thewlt_prefix. The path itself is unchanged, so existing integrations continue to work; update tooling, examples, and SDK groupings to file the endpoint under wallets.
New
502response onPOST /transactions(execute a transaction): synchronous provider failures during transaction execution now return502with aTransactionbody in failed status, so clients can distinguish “created but failed at the provider” from “not created.” Handle502alongside201when calling the execute endpoint.
Wallets
OMS Wallet adds an agentic wallet mode, refreshes the Earn experience with new filters, sorting, and interest disclosures, refreshes the Private Send flow, and ships a wide set of transaction and inventory fixes.
New
- Agentic wallet mode: dedicated landing experience with a terminal-style prompt copy flow for AI coding agents, plus an in-wallet inventory prompt card with docs link, reduced-motion support, and dismiss persistence.
- Earn interest disclosures and deposit terms: tooltip-based interest disclosures across the Earn list, detail, and deposit surfaces, with the APY column relabeled to Interest. A terms-and-conditions notice now appears below Review Deposit.
- Earn coin filters: switch between “Yours” (vaults for tokens in your inventory) and “All” to discover other opportunities, replacing the previous third “Vaults available for your funds” table.
- Inventory and empty-state defaults: collectibles are hidden by default until enabled in settings, the header wallet avatar stack is hidden when only one wallet exists, decorative placeholder cards are removed from empty states, and Earn defaults to all vaults when no coin tokens are held.
Updated
- Earn UX: vault data is now cached across page navigation with background refresh, sorting is available on any column with retained filters and sort order across navigation, deposit and withdraw amounts can be entered as a direct percentage, and vault selection rules now require APY under 100 percent (alongside the existing $300k minimum TVL and non-deprecated criteria) to filter out stale high-APY vaults.
- Private Send flow: refreshed UI with new step transitions, token selection, recipient selection, amount entry, and final send states.
- Terms of Use and Privacy Policy updated.
Fixed
- Transaction flow reliability: transaction relay and finality waiting now resolves only after a confirmed final status and handles cancellation and failure consistently; the sign-transaction modal scopes requested transactions correctly and keeps selected transactions visible while progressing; dapp send-wallet-transaction recovery handles wallet sync, duplicate approvals, stored transaction ids, the success idle state, and error rendering consistently.
- Inventory cache regressions after the persisted query cache migration: auth inventory prefetch now writes the canonical summary shape consumed by the inventory query hook, and recovery inventory reads are scoped to the requested recovery wallet, including the per-chain selection flow.
- Empty inventory notice no longer flickers when refreshing inventory.
- Earn snapshot initialization no longer gets permanently stuck if a background refresh is interrupted; the refresh lease now expires within 90 seconds and the next request re-triggers initialization.
- Private Send withdraw finalization behavior corrected.
- Trails success modal content fixed in the in-wallet cross-chain flow.
- Agentic mode terminal: text overflow fixed and generated CLI prompt arguments are now wrapped in double quotes for terminal-safe copy and paste.
Polygon PoS
A coordinated heimdall hard fork activated on mainnet on June 2 at block 46361000, and one day later the mainnet block time was reduced from 1.75 seconds to 1.5 seconds, lifting effective execution throughput by roughly 16 percent.
New
- Mainnet block time reduced from 1.75 s to 1.5 s: on June 3 the network moved to 1.5-second blocks with the block gas limit held at 140 million, raising the effective execution rate from around 80 Mgas/s to around 93.3 Mgas/s. No operator action is required. As a heads-up, a follow-on block-gas-limit increase from 140 million to 160 million is currently scheduled for June 16 as the final step on the path to 5,000 TPS.
- Heimdall hard fork activated on mainnet at block 46361000: the validator-set fee-withdrawal gate hard fork activated on Polygon PoS mainnet at block 46361000 on June 2 at 14:00 UTC, with heimdall v0.8.1 as the matching public release. After the fork, fee-withdrawal operations on heimdall are restricted to addresses in the active validator set; requests from non-validators are rejected at message-handling time. The fork also activates the v0.8.x features documented in the June 1 entry (planned-downtime successor nomination, deterministic state syncs gated on
visibilityTimeHeight, and the related bridge and consensus guards).
AggLayer
AggLayer v0.5.0 ships stable, with a revised admin API, mandatory gRPC prover transport, and a safer default for settlement confirmations.
New
- Networks can be marked as disabled in configuration, so operators can stop accepting certificates from a chain without removing it from the deployment (#1160)
Updated
- Breaking: the admin API replaces
forceSetCertificateStatuswithforceEditCertificate, which takes a certificate ID, aprocess-now=true|falseflag, and structured operations such asset-status,from=InError,to=Candidateorset-settlement-tx-hash,from=0x...,to=null. Update any operator tooling that called the previous endpoint (#1159) - Breaking: prover transport is now gRPC-only. The
prover_entrypointconfig key is removed and the[grpc]block is mandatory;proverreferences aProverTypefrom the provers repository (#1195) - Breaking: the
[extra-certificate-signer]configuration variable is removed; drop the matching block from deployment configs (#1206) - Default settlement confirmations raised from 1 to 12. Deployments that set this parameter explicitly are unaffected; deployments relying on the default now wait 12 blocks before processing a settlement (#1470)
Trails
Trails ships SDK v0.16.1 with Hyperlane and LayerZero Value Transfer routes, the Open Intents Framework as a route option, the OMS Wallet widget theme, and a dedicated recovery flow for stuck intents. EVM↔Solana edge support has merged to master and is in QA ahead of a follow-up release.
New
- Hyperlane routes: Trails routes can now traverse Hyperlane Warp Routes, including first-party USDC HWR support across Arbitrum, Base, Polygon, Ethereum, and Katana. Hyperlane is enabled in AUTO routing with market-rate fee estimation, supports
EXACT_INPUTtrades, and includes destination-collateral checks, worker recovery, and underfunded-deposit refund handling. - LayerZero Value Transfer (
LZ_TRANSFER) routes: a new route provider built on the LayerZero Value Transfer API replaces the deprecatedlz_stargateroute. The widget exposes it as a selectable bridge option. - Open Intents Framework (OIF) routes: OIF is wired in as a route provider, with an expired-order refund worker, explicit rejection of partial-fill quotes, and demo coverage for ETH USDC → Katana USDC.
- OMS Wallet widget theme: a new widget theme aligned with OMS Wallet, including a refreshed selected-wallet background.
- Recover funds screen: a dedicated screen in intent history for recovering stuck funds, replacing the prior inline recovery surface. Edge wallet intents are now included in history.
- Fund exact output: funding flows can now target an exact output amount.
- Recent chains in the chain selector: previously used chains surface at the top of the chain selector.
- Token selector refresh: a refresh control on the token selector reloads token lists inline.
- Widget chain and token filtering: new
supportedChainsandsupportedTokensByChainwidget props let integrators restrict the chains and tokens shown to users. GetEdgesdiscovery API: a new RPC enumerates enabled edge rails so integrators can discover available cross-chain routes at runtime.- Intent history filters: history and search now accept edge address, rail, mode, provider, and participant-address filters.
- Edge metadata on intent endpoints: intent receipts now include
IntentEdgeMetadatawithEdgeStatus, refund transaction hashes, and rail metadata. - Same-chain “Swap & Receive” label: same-chain terminal swap steps are labeled clearly in transaction summaries.
- Wagmi auto-detection: the widget detects an active wagmi provider without explicit adapter configuration.
- Composable action resolver APIs: the composable-action resolver helpers are now exported for integrator use. See the Trails docs.
- Somnia chain support: Somnia is supported as a route destination, including
USDC.e↔USDsoswaps andSOMIOFT routes for cross-chainSOMItransfers, with refreshedSOMIbranding in the widget. - EVM↔Solana edge support (in QA): end-to-end EVM↔Solana intents are merged to master and available in the dev demo, with SVM wallet auto-detection, Solana-standard wallet support, Solana funding and settings rows, Solana-side refund and failure handling, and server-side edge execution via a new edge monitor worker. Targeting a follow-up SDK release once QA completes.
Updated
- Hyperlane fee breakdown: Hyperlane now shows as a Bridge rather than a Swap in the fee breakdown.
- WETH passthrough: self-wrap and self-unwrap quote shapes are now supported through passthrough.
lz_stargateremoved: the deprecatedlz_stargateroute provider has been removed in favor ofLZ_TRANSFER.xaiandblastchains removed: both chains are no longer supported by the Trails SDK.- Widget input scope tightened:
from.currencyis restricted to fiat andfrom.tokento ERC-20 in the widget. - Multi-swap route discovery: routing now discovers multi-swap routes through intermediary tokens, broadening coverage.
GetEarnPoolschain list: refreshed list of chains exposed by the earn-pools endpoint.WaitIntentReceiptsynthesis: receipts are now synthesized from intent state when no receipt row exists, removing a class of “pending” hangs.
Fixed
- Polygon USDT permit: salt-based EIP-712 domain handling now works correctly for the Polygon USDT permit flow.
- EVM signer resolution: quote requests now resolve the EVM signer address when a wallet session is connected but
walletClientis still null (for example, MetaMask on Polygon before wagmi materializes the client). - Embedded wallet approval prompt: the “Please approve…” prompt is hidden for embedded wallets, including OMS Wallet.
- Embedded wallet intent recovery: resolves a recovery hang for embedded wallets on v1 intents.
- Quote owner resolution: the connected EVM wallet is always used as the quote owner; the recipient address is never substituted.
- Quote route provider fee labels: corrected labels for route provider fees in quote breakdowns.
- Fund method disabled state: unlisted fund methods now render as disabled rather than enabled.
- WalletConnect recovery flow: the WalletConnect recovery flow now completes without stalling.
- WalletConnect funding: WalletConnect funding now redirects to token selection and exposes a disconnect button. WalletConnect wallets are also shown when direct-transfer is hidden from fund methods.
- Native wallet reconnect on reload: native wallet sessions now reconnect on page reload.
- Recovery fee options: fee options no longer break for WaaS wallets in the recovery flow.
- v1.5
depositSignature: v1-only fields indepositSignatureare now guarded for v1.5 compatibility. - Credit-card flow placeholder: fund amount placeholder text fixed in the credit-card flow.
- SDK edge sends: the SDK no longer auto-executes intents after an edge deposit; server workers handle execution.
- Same-chain quote math: fixed
PriceImpactUSD, native token normalization, andEXACT_INPUTcalldata for same-chain quotes. - Edge quote timeout: intents now abort cleanly on edge quote timeout.
- Fee caps: exact-input fees are capped to the residual amount, and
CollectorFeeCapis scoped toEXACT_OUTPUTto fix anEXACT_INPUTbridge gas regression. - Edge worker transaction hashes: init and refund transaction hashes now persist correctly through worker updates.
- Token decimals cache: scoped by chain to prevent cross-chain collisions.
- Deposit transfer logs: matching deposit transfer logs are summed instead of taking the first match.
- Outer approvals: redundant outer approvals are skipped for SDK-managed
hydrateExecuteAndSweepmulticalls. - Multi-swap recipients: replay of multi-swap intents now preserves the original per-step recipients.
- Fee collector: corrected fee-collector validation, tokenlist fallthrough, and receipt-state edge cases.
- Send-transaction destination context: corrected propagation of destination context for send-transaction calls.
OMS
The OMS public API expands the set of blockchain networks accepted for inbound transfers.
Updated
- Inbound blockchain networks: the
BlockchainInNetworkenum used by the deposit-address simulation endpoint (POST /deposit-addresses/{depositAddressId}/simulate) now acceptsINKandSUIin addition toBASE,ETHEREUM, andSOLANA. Integrators using the deposit-address flow can now simulate inbound USDC, USDT, or USAT transfers from Ink and Sui in sandbox.
Polygon PoS
Bor v2.7.2 through v2.8.2 ship across the lookback window, alongside heimdall v0.7.1, introducing an opt-in heimdall-to-bor gRPC transport, expanded tracing for state-sync transactions, several new operator flags, and a large set of consensus, miner, and bridge fixes.
New
- Heimdall-to-bor gRPC transport (opt-in): every bor-facing JSON-RPC now has a gRPC counterpart, selectable per-node via
bor_grpc_flagin heimdall’sapp.tomlagainst bor’s new[grpc]config section. TheGetBlockInfoInBatchcall used in the consensus-criticalExtendVoteHandlerpath is around 4.4× faster on gRPC, and the wire path allocates 50 to 67 percent fewer bytes across the seven methods. Defaults are unchanged, and heimdall performs a startup hash-parity check across both transports before continuing. - State-sync transaction tracing: state-sync (bor bridge event) transactions are now first-class in the
debug_*tracing RPCs, includingdebug_traceBlockByNumber,debug_traceBlockByHash,debug_traceTransaction,debug_traceChain,debug_traceCall,debug_intermediateRoots, anddebug_standardTraceBlockToFile. A new wrapped tracing hook collects multiple bridge events under a single synthetic root call frame per state-sync transaction, socallTracer-class tracers see one root with N sub-calls instead of N independent traces. The legacydebug_traceBorBlockRPC method has been removed; switch any callers todebug_traceBlockByNumberordebug_traceBlockByHash. p2p.nosnapconfig flag: decouples the local snapshot tree from serving thesnap/1p2p sub-protocol. Block producers can keep the flat-state snapshot enabled for fast local reads while declining to serve snap sync to peers, which removes unnecessary network and CPU overhead on validator boxes.miner.disable-pending-blockflag: disables the pending-block creation loop inminer/worker. RPC queries against thependingtag returnnil. Intended for non-validator and serving nodes that do not need a pending block.- Builder-phase prefetch and new metrics: the builder pipeline now runs three additional, builder-synchronized prefetch phases (upfront plan scan, per-transaction forwarding, and freed-gas overflow), reducing cache-miss costs on contract-heavy blocks. New metrics include
worker/prefetch/builder_added_percentandworker/txApplyDuration/{prefetched,notPrefetched}. Additional block-building timing metrics cover prepare-work duration, pending-transaction fetch time, and the count of header-time extensions. - Heimdall planned downtime can nominate a successor:
MsgSetProducerDowntimenow accepts an optionaltarget_producer_idto nominate a specific replacement. If the target is invalid, inactive, or also down for the range, selection falls back to round-robin. Gated on a coordinated hard fork. - Heimdall deterministic state syncs: introduces height-pinned, one-block-delayed visibility for clerk events so that during heimdall halts pending events remain excluded and all validators derive identical query results. Activation is gated on
visibilityTimeHeight, which is0(disabled) on all networks until the next hard fork is scheduled. - Heimdall bridge self-healing expanded: the bridge self-heal loop now recovers
SignerChangeandUnstakeInitevents in addition toStakeUpdate, unified through a single combined subgraph query that returns the max L1 nonce across the three event types. Receipt validation now rejects reverted L1 transactions and logs emitted by unexpected contracts. prepareProposalBudgetin heimdall: implements an explicit time budget for proposal preparation in ABCI, bounding the work the proposer can do before block dissemination.
Updated
- Witness filestore is now the default: the filesystem-backed witness storage is now enabled by default in bor’s CLI. Existing operators on the database-backed store continue to work; new nodes pick up the filestore without additional flags.
- Witness configuration template hint: bor packaging templates now show
[witness]withenable = falseinstead of the deprecated top-levelwitnessprotocolkey, which the current parser no longer consumes. Update any custom configs that still usewitnessprotocol. rpc.batchlimitrenamed torpc.batch-request-limit: bor’s templates and example config now userpc.batch-request-limit, matching the runtime parser. The previousrpc.batchlimitkey was silently ignored, which could leave nodes on the default batch limit of 1000 unintentionally.cache.triejournaldirectoryis now wired end-to-end: operators who set this key inconfig.tomlnow get the configured directory respected. When unset, behavior is unchanged and the journal stays under<DATADIR>/triedb.- Heimdall pruning defaults retuned: the shipped
config.tomlnow defaultsindexer = "null"(cometbft transaction index disabled), with the pruningintervalreduced from3hto10mandEnforcedMinRetainBlocksreduced from 2,500,000 to 2,000,000. The effect on a mainnet RPC node is around 23 GB freed at steady state and the elimination of large pruning bursts. Operators who do not use/txor/tx_searchcan also reclaim around 43 GB by removing the existingtx_index.dbafter upgrading. - Heimdall bridge metric rename: the three self-heal Prometheus metrics moved into the
heimdallv2namespace and follow Prometheus naming conventions.self_healing_<chain>_StakeUpdateis nowheimdallv2_self_healing_stake_events_processed_total,self_healing_<chain>_StateSyncedis nowheimdallv2_self_healing_state_syncs_processed_total, andself_healing_<chain>_NewHeaderBlockis nowheimdallv2_self_healing_checkpoint_acks_processed_total. Update any dashboards or alerts that reference the old names. - Heimdall bridge performance: vote-extension processing now pre-fetches L1 receipts in a single batched RPC call, the root-chain listener uses an O(1) topic-to-event map instead of a per-log linear ABI scan, and the clerk mempool check uses the typed CometBFT RPC client.
- Amoy bootnodes updated: bor’s compiled-in Amoy static peer list now points at the current set of Amoy bootnodes. Operators relying on the embedded list no longer need a manual override.
- Heimdall mainnet seeds and persistent peers: refreshed to the current working set, so nodes regenerating their config pick up working peers without manual intervention.
Fixed
- Bor recovers from milestone-mismatch forks without manual wipe: nodes that briefly forked off the canonical chain due to a heimdall startup hiccup or span propagation gap could enter a state where rewinding required canonical-chain blocks that the downloader refused to deliver, requiring an operator to wipe chain data. The mismatch-rewind path now resyncs the canonical segment from peers while still rejecting genuine sidechain ghost-state attacks.
- Forkid wire-format change reverted: a change that included polygon-specific forks (Rio, Madhugiri, Dandeli, Lisovo, Giugliano, Chicago, others) in the eth p2p handshake forkid hash was reverted because changing the wire forkid is a breaking peer-to-peer change. Nodes running the changed code could not handshake with older bor peers. A future, coordinated rollout will reintroduce the fix gated on a fork block.
eth_estimateGasno longer fails on non-validator nodes: nodes whose configured signer is not in the active validator set previously returned “insufficient funds for transfer” against thependingblock becausePrepare()was failing on every block and the pending snapshot was never refreshed.Prepare()no longer fails for unauthorized signers;Seal()continues to reject blocks from unauthorized signers independently, so no invalid blocks can be produced.- Live tracing no longer conflicts with the miner: bor running with live tracing enabled could call live tracing hooks from the miner path once it reached live, causing problems. The miner now disables the tracer in its
vm.Configso only block synchronization invokes the tracer. - Heimdall checkpoint submission off-by-one fixed: the bridge stopped submitting checkpoints to L1 after the
indexer = "null"migration because the new tx-bytes lookup was passing the event’s block height instead of the height of the block that contained the checkpoint transaction. The lookup now usesheight - 1, matching the invariant established by the side-tx post-handler running in the block after the one that included the message. - Heimdall producer downtime span off-by-one fixed: veBlop spans created by
PostHandleSetProducerDowntimewere one block longer than intended (6,401 instead of 6,400), causing overlaps with adjacent spans. Gated behindproducerDowntimeSpanFixHeightto preserve pre-fork behavior. (*Header).GetValidatorBytesno longer panics on shortExtra: an exported helper used by indexers, light clients, and explorers panicked withslice bounds out of rangewhen passed a header whoseExtrafield was shorter than 97 bytes on pre-Cancun configurations. The pre- and post-Cancun branches now share a single length guard and returnnilfor malformed input.- Witness size recovers when filesystem metadata and DB disagree: if a crash interrupted a witness write between renaming the file on disk and writing the size key to the DB, witness pagination paths could silently fail because
ReadWitnessSizereturnednil. The lookup now falls back toos.Staton the witness file when DB metadata is missing. - Miner state-machine wedges closed: four silent stall paths in the producer state machine that could leak the
pendingWorkBlockorpendingTasksmap entry on edge cases (peer count zero during production, syncing-check early return, sealhash interrupt, fullresultCh) are now plugged, with regression tests for each. SpanStore.PurgeCacheno longer races its own poll loop: a background poller could write a freshly purged span back into the cache before the next test read, producing intermittent CI flakes. The poll goroutine is now cancelled and waited on before state is reset.- Milestone websocket subscriber shuts down cleanly: the heimdall milestone subscriber outlived
chainDb.Close()because it was launched withcontext.Background(). It now exits on the bor close channel, eliminating the burst ofpebble: closederrors during shutdown.
AggLayer
AggLayer migrates the prover stack to SP1 v6, aggkit ships substantial exit-certificate recovery tooling, and operators get more configurable RPC behavior plus a slate of reliability fixes.
New
- SP1 prover upgraded to v6 with backward-compatible reads of legacy v5 proofs and certificates, improving proof generation and verification performance during the migration window (#1525, #1545)
- gRPC ingress now rejects certificates carrying non-writable SP1 proof versions with a dedicated
UnsupportedProofVersionerror instead of failing downstream (#1522) - Settlement service gains an auto-retry helper (
retry_callback_until_success) that recovers from transient RPC errors instead of panicking (#1476) - aggkit exit-certificate tool: Step A is split into A1 (tracing) and A2 (receipt-recovery fallback), addressable individually or together via
--step a1,a2, ora, with automatic migration of legacy output files (#1630) - aggkit exit-certificate tool: new
stepAWindowSizeoption (default 5000) for independent Step A chunk sizing, and an abort-on-error mode that stops all workers on the first trace failure whenContinueOnTraceError=false. Trace errors now surface both the transaction hash and the underlying RPC message (#1629) - aggkit exit-certificate tool:
targetBlockaccepts finality keywords (LatestBlock,FinalizedBlock), decimal/hex values, and relative offsets likeLatestBlock/-10. The resolved block is persisted and reused across resumed runs (#1627) - aggkit LET operator tooling: new
craft-cert,send-cert --no-db,cert-status, andexport-cert-exitssubcommands, with stronger fallback diagnostics when aggsender bridge-exit data is unavailable (#1616, #1570) - aggkit RPC client: new
BatchBlockHeaderRetrievaloption lets operators disable batch header requests and fall back to sequential calls for nodes that do not support batching (#1601)
Updated
- Per-epoch certificate rate limiting is restored:
MAX_CERTIFICATE_PER_EPOCHand epoch-capacity gating inNetworkTaskonce again enforce one certificate per network per epoch (#1514) - aggsender startup recovery now requires a manual database wipe when local certificate state diverges from AggLayer, replacing the previous automatic reconciliation (#1570)
- aggkit docs now document that event logs must be available from genesis and provide reth configuration for disabling receipt pruning to prevent historical sync failures (#1610)
Fixed
- aggkit exit-certificate tool now patches ERC-20 balances correctly for smart-contract-locked exits in Step G, supporting both OpenZeppelin v4 (slot 0) and v5 (namespaced storage) layouts. Resolves
ERC20InsufficientBalanceerrors during SC-locked exit processing (#1622) - aggkit Docker images now embed
VERSION,GITREV, andGITBRANCHbuild metadata, sodocker run aggkit versionreports the correct values instead of empty fields (#1619, #1621) - aggkit
l2gersyncno longer emits ERROR-level logs for max-block-range responses that are already handled by automatic chunking (#1607) - aggkit
ParseMaxRangeFromErrornow recognizes thequery exceeds max block rangeRPC error format, so ClaimSyncer retries with chunked requests instead of failing (#1589) - aggkit bridge-call matching now filters debug trace frames to actual
CALLoperations only, ignoringDELEGATECALL,STATICCALL,CALLCODE, and untyped frames (#1609) - Pending certificate validation now uses the correct error type instead of
CertificateCandidateError::UnexpectedHeight(#1289)
Trails
Trails ships composable actions, a token-first widget destination flow, auto-refresh for expired quotes, and a wide set of widget and SDK fixes.
New
- Composable Actions: chain multiple destination calls into a single intent, with dynamic balance routing and support for multiple ERC-20 tokens in dynamic-amount slots. Integrators can sequence approvals, swaps, and custom calls inside one quote, with backend validation rejecting ambiguous recipient or passthrough configurations. See the Trails docs for usage.
- Token-first widget destinations: widgets now accept token-specific destination lists, letting you configure a fixed set of destination tokens (and chains) that users can pick from. Persisted token selections survive across sessions.
- Quote auto-refresh: expired quotes refresh automatically on the next user interaction before signing, so users no longer see an “Intent quote has expired” error in the pre-signing state. Refresh is suppressed once signing has begun.
- Wallet runtime adapters: Trails wallet runtimes are now decoupled behind explicit adapters, giving integrators a cleaner extension point for embedding custom wallet stacks.
- Configuration validation and offline recovery: intent configuration is now validated up front and cached locally, with pruning of stale entries. This enables offline recovery of in-flight intents.
Updated
- Widget lifecycle callbacks renamed:
onSwapComplete,onPaymentComplete,onFundingComplete, andonWithdrawCompleteare nowonSwapSuccess,onPaymentSuccess,onFundingSuccess, andonWithdrawSuccessfor consistency. Update your widget props to match the new names. - WalletConnect: improved QR rendering, deep-link handling, and loading state in the WalletConnect flow.
- Quote input: clearer error states when the input amount or token is invalid.
- Send flow: destination chain now defaults to the connected wallet’s chain when no destination is specified.
- Mesh exchange: mesh exchange queries can now run without a configured
trailsApiUrl. - Receipt step ids: receipt step ids are now stable across renders, making them safer to use as React keys and analytics identifiers.
- SDK packaging: improved compatibility with Node environments and better tree-shaking for smaller bundles. Server-side rendering with Trails actions is now safe.
Fixed
- Fixed swap funding method selection so the chosen method is preserved across re-renders.
- Fixed the receiver token picker losing account context when switching accounts.
- Fixed chain-switch detection in the widget when users change networks mid-flow.
- Fixed Privy wallet modes incorrectly rendering a ConnectKit button.
- Fixed
useQuotesoQuoteErroris available as a runtime value rather than a type-only export. - Fixed missing deposit transaction enrichment in intent history.
- Wrapped errors now propagate via
Error.cause, preserving the original stack and context for debugging. - Composable-action quotes now reject a top-level recipient (which is per-call only) and omit the quote recipient in earn mode.
- Guarded against undefined
detailsinUnknownRpcErrorchecks to prevent spurious crashes. - Removed a duplicate Pay fallback path that could double-trigger the pay flow.
Agglayer
Agglayer adds gRPC-level SP1 proof version validation; aggkit ships configurable block header batching and fixes RPC range errors and bridge call trace matching.
New
- gRPC ingress now rejects certificates carrying non-writable SP1 proof versions and returns a structured
UnsupportedProofVersionerror, giving integrators explicit feedback when submitting proofs built with unsupported versions BatchBlockHeaderRetrievalis now configurable in[L1NetworkConfig.RPC]and[Common.L2RPC]in aggkit; set tofalseto disable batch RPC requests for endpoints that do not support them (v0.10.0-rc3)
Fixed
- Node process no longer panics on corrupt certificate rows; storage decode failures now surface as
CodecError::Serializationinstead of unwinding the process - Pending certificate validation now returns the correct error type; previously reused
CertificateCandidateError::UnexpectedHeightfor a case it was not designed for - ClaimSyncer now recognizes the “query exceeds max block range” RPC error and automatically retries with smaller block ranges, preventing sync failures on restrictive RPC endpoints (aggkit v0.10.0-rc2, v0.8.3-rc2)
- Bridge and claim sync debug traces now correctly match only
CALLframes, preventing incorrect calldata extraction fromDELEGATECALL,STATICCALL, orCALLCODEframes (aggkit v0.10.0-rc3, v0.8.3-rc3)
Agglayer
aggkit pre-releases add configurable RPC block header batching and improve bridge call trace accuracy; agglayer corrects error type semantics in pending certificate validation.
New
- Batch block header retrieval is now configurable via
RPCClientConfigin aggkit, letting operators control how many block headers are fetched per RPC call (v0.10.0-rc3)
Fixed
- Pending certificate validation in agglayer now returns the correct error type; the code previously reused
CertificateCandidateError::UnexpectedHeight, which is semantically reserved for epoch-context candidate certificates - aggkit no longer emits a spurious error log when l2gersync encounters a block range that exceeds the RPC provider limit (v0.10.0-rc3)
- Bridge call matching in debug traces is corrected (aggkit v0.10.0-rc3, v0.8.3-rc3)
- aggkit now parses
query exceeds max block rangeresponses from RPC providers and retries with a reduced range instead of surfacing an unhandled error (v0.10.0-rc2, v0.8.3-rc2)
POL
Two governance proposals targeting the validator reward schedule and payments revenue distribution were merged this week.
New
-
PIP-86 recalibrates
CHECKPOINT_REWARDon the L1StakeManagerto hold annual POL emission at the 1% target (103,530,000 POL/yr) as Polygon Chain transitions to faster block times. The parameter decreases from 34,695.98 POL to 29,414.92 POL per checkpoint when block time moves to 1.75s (planned May 5), and to 25,212.79 POL per checkpoint at 1.5s (planned May 19). No contract upgrades are required; both changes execute viaupdateCheckpointReward()through the existingGovernancecontract. - PIP-87 proposes a fixed-cost payments program that gives payment companies predictable, fiat-denominated pricing for blockspace on Polygon. Revenue from the program is converted to stablecoins and distributed to validators and stakers via the existing PIP-65/82/85 framework, with a portion used to buy POL from the market for staker distribution and for the PIP-24 burn.
Polygon Chain
Bor v2.7.2 stable ships RPC fixes including re-enabling
eth_simulateV1, with v2.7.3-beta adding private transaction relay improvements and critical fixes for memory growth and security landing on the development branch.New
- Added
miner.disable-pending-blockconfig option to disable the pending block creation loop; when set, allpendingblock RPC queries return nil
Updated
-
RPC gas cap is now bypassed only for internal system transactions; external
eth_calland simulation requests are subject to the configured gas cap - Private transaction relay now purges stale transactions more aggressively using per-block-producer error tracking, preventing unbounded in-memory growth under sustained relay load
Fixed
-
eth_simulateV1is re-enabled in v2.7.2; a context propagation error in the RPC path was causing the method to return incorrect results - Fixed exponential memory growth in the pathDB BFS preload path: the node-child enumeration was enqueuing all 16 nibble children regardless of node type, causing the visited map to grow as O(16^depth) and consuming up to 14 GB per goroutine on restart, stalling block import for up to two hours on archive and full nodes
-
Go runtime updated to 1.26.2, resolving 6 standard library CVEs in
crypto/tlsandcrypto/x509including a TLS 1.3 KeyUpdate denial-of-service and several certificate validation bypass issues
Trails
Trails API adds gas fee option selection to the intent flow, persists wallet configuration at quote time, and fixes a recovery issue for intents using custom address overrides.
New
- Intents now support gas fee option selection. Specify fee options at quote time; the API validates the selected option against quoted options before execution, giving integrators explicit control over transaction costs.
- Wallet configuration is now stored at quote time and returned with the quote response, enabling reliable intent recovery and re-execution across sessions.
Fixed
- Recovery now correctly applies custom address overrides when deriving wallet context, preventing derived-address mismatches for intents configured with non-default addresses.
Trails
Trails SDK and widget ship smarter intent protocol defaults, better recipient handling, and improved error recovery.
New
- The SDK now automatically resolves the best intent protocol version for each transaction. An explicit override, global config, or SDK default is checked in order, with a fallback to the API-provided default when the preferred version isn’t supported.
Updated
- The widget highlights the recipient address when it differs from the sender, making it easier to spot send-to-other flows before confirming.
- Expired quote errors now show a warning icon and offer a clear recovery path so users can retry without restarting the flow.
Fixed
- Fixed an issue where the recipient address could go stale after switching wallets in the widget.
- Fixed the add-wallet flow in the recipient selector so users can successfully add new recipient addresses.
Polygon Chain
Bor v2.7.1 stable ships opt-in EVM execution optimizations, database performance improvements, and a goroutine leak fix.
New
- Added opt-in EVM execution optimization via
--switch-dispatchCLI flag orEnableSwitchDispatchconfig option, introducing a fixed-size opcode stack and a switch-dispatch fast path for hot opcodes ported from GEVM
Updated
- Tuned PebbleDB write path (1 MiB
BytesPerSync, adaptive compaction) and increased PathDB state buffer to 2 GB with intelligent carry-over across flushes, reducing write stalls for archive and full nodes - Removed legacy
UpdateDeps/GetDepDAG algorithm from BlockSTM, fully replaced by theDepsBuilderimplementation
Fixed
- Fixed goroutine leaks in the witness request path that caused unbounded memory growth when peers disconnected during parallel stateless import
Agglayer
aggkit fixes claim syncer startup block selection on empty databases.
Fixed
- Claim syncer now determines its starting block from the earliest settled reference block (not the latest) when the database is empty on startup, preventing sync errors on fresh starts
Polygon Chain
Bor v2.7.1-beta ships transaction propagation improvements and an SRC buffer reduction, with additional RPC and monitoring fixes merged to the development branch.
Updated
- Increased max transaction packet size from 100 KB to 1 MB, improving transaction propagation between peers
- Reduced SRC buffer from 500ms to 100ms, allowing block producers to spend more time on transaction execution
Fixed
bor_getLogsandbor_getLatestLogsnow correctly return state-sync logs from blocks produced before the Madhugiri upgrade- Prometheus
_countmetrics no longer reset to zero on each scrape, restoring correctrate(),increase(), and latency calculations for node operators
Wallets
The Polygon Wallet adds private token transfers and Yield vault support in the Earn tab.
New
- Added end-to-end private send flow, including a dedicated status screen for deposit, withdraw, success, and failure states, and refreshed gas and privacy fee display with up to 4 decimal place precision
- Added Yield vault support to the Earn tab
Agglayer
Agglayer raises the default settlement confirmation count to 12 for safer out-of-the-box deployments.
- Default confirmations for L1 settlement increased from 1 to 12. Deployments that do not set this value explicitly are now safer by default.
Trails
Trails API adds a new option to suppress passthrough quotes from intent responses.
- Added
intents.disable_passthrough_quotesoption. When set, the API omits passthrough quotes from intent results, giving integrators tighter control over which quote sources are returned.