Skip to content

refactor: rewrite & modernization - @polygonlabs/pos-sdk@1.0.0 #476

Draft
MaximusHaximus wants to merge 3 commits into
masterfrom
pos-sdk-1.0-rewrite
Draft

refactor: rewrite & modernization - @polygonlabs/pos-sdk@1.0.0 #476
MaximusHaximus wants to merge 3 commits into
masterfrom
pos-sdk-1.0-rewrite

Conversation

@MaximusHaximus
Copy link
Copy Markdown
Contributor

@MaximusHaximus MaximusHaximus commented May 1, 2026

Why

@maticnetwork/maticjs 3.9.x has accumulated a decade of abstraction debt that actively obstructs both consumers and maintainers. These are concrete failure modes, not cosmetic concerns:

  • The plugin model mutates module-level globals (utils.Web3Client = X). Constructing two POSClient instances against different networks in the same process — a routine pattern in indexers and multi-tenant API services — cross-contaminates them. We have hit this in production.
  • The lazy ITransactionWriteResult shape conflates "submitted" and "confirmed". getTransactionHash() initiates a send rather than observing one, and consumers consistently mis-use it. A caller assuming getTransactionHash() was idempotent caused a production outage by double-broadcasting a transaction.
  • The BaseToken → POSToken → ERC20 inheritance chain forces every contract wrapper — including RootChainManager and GasSwapper, which are not tokens — to extend a class named BaseToken. Adding a new contract wrapper requires inheriting bridge-specific predicate logic that does not apply to it. This has blocked otherwise-trivial feature work multiple times.
  • ABIs are loaded at runtime from a single CDN. If static.polygon.technology moves, every running consumer breaks. This is a single point of failure for the entire SDK.
  • BaseBigNumber predates native bigint by half a decade and now adds boilerplate without value. Consumers convert from their library's native type into BaseBigNumber only to have the SDK convert it back internally.
  • Type safety is poor. 27 : any types and 50 as casts on the public surface, including 5 as any casts that erase the entire client config type. Consumer code has no compile-time signal that it is misusing the SDK.

Scope: PoS only — zkEVM stays on the legacy package

@maticnetwork/maticjs shipped both the PoS bridge and the zkEVM bridge surfaces. This rewrite covers only the PoS side. The zkEVM chain is on a wind-down schedule; rewriting and shipping a @polygonlabs/zkevm-sdk would put consumers through a migration twice — once now, once when the package is sunset alongside the chain. Skipping the intermediate step is cleaner:

  • PoS-only consumers migrate to @polygonlabs/pos-sdk per MIGRATION.md.
  • zkEVM-only consumers stay on @maticnetwork/maticjs and drop the package when the chain is shut down.
  • Mixed consumers install both packages side by side during the window — they have non-overlapping public surfaces.

What we got

Correctness and determinism

  • Multi-tenancy safe. No more utils.Web3Client = X global mutation. Multiple POSClient instances against different networks coexist in one process without cross-contamination.
  • Tx-result foot-gun fixed. TxResult = { hash, confirmed() } is observe-only — no method call has hidden side effects. The hash is populated synchronously the moment the RPC accepts the broadcast; confirmed() waits for the receipt and is idempotent.
  • ABIs cannot drift from the code. Vendored at SDK build time as as const TypeScript files, so the SDK's compiled call sites and the bundled ABIs are guaranteed to match. The CDN dependency for ABIs is gone.
  • Long-running services pick up contract redeployments without restart. Addresses are still dynamic (the RootChainManager and predicate addresses change occasionally and we do not want to ship a new SDK each time), but the AddressFetcher uses stale-while-revalidate semantics on a 1h TTL — every individual call serves the cached value at near-zero cost, and the next call after the TTL window kicks off a background refresh.
  • No Boolean traps. pos.parent.erc20(addr) and pos.child.erc20(addr) namespaces eliminate the easily-inverted isParent: boolean parameter.
  • No silent ABI version pinning. The legacy version config field that let consumers pin to stale contract metadata is gone — there is one canonical ABI per release.

Type safety

  • All 27 : any types eliminated. The four load-bearing ones (provider: any, abi: any) are now typed via the Adapter interface and as const ABIs respectively.
  • All 50 as casts pruned. The 5 as any casts on the legacy Web3SideChainClient config disappeared entirely with the class.
  • TYPE_AMOUNT (legacy BaseBigNumber | string | number union) replaced with native bigint on 27+ method signatures.
  • as const ABIs give viem-typed inference at every internal call site — method names, argument shapes, and return types are compile-time-checked.
  • POSBridgeError's 28-code discriminator union enables exhaustive switch narrowing in consumer error handlers — TypeScript fails the consumer's build the moment a new failure mode is added without a corresponding case.

Capability — keeping the tools consumers actually rely on

A migration audit across repositories/ (portal, proof-generation-api, staking-ui, bridge-indexer, etc.) surfaced two real consumer use cases the initial rewrite cut too aggressively. Both are addressed in this PR:

  • Unsigned transactions for smart wallets. Every public write now has a prepareXxx sibling (prepareApprove, prepareDeposit, prepareCompleteWithdraw, …) that returns { to, data, value? } instead of broadcasting. Closes the gap for Safe / Sequence / account-abstraction bundlers, batched multicall flows, pre-flight inspection, and offline signing. Replaces the legacy option.returnTransaction foot-gun (same return type meaning two different things depending on a boolean) with two distinct, statically-typed methods.
  • Bridge helpers exposed flat on POSClient. proof-generation-api (and any other consumer building exit payloads outside the standard token flows — sync block events, custom bridge events, plasma exits) used pos.client.exitUtil.{buildPayloadForExit, isCheckPointed, getBlockProof, …} directly. The 1.0 SDK exposes those as pos.buildExitPayload, pos.isCheckpointed, pos.getBlockProof, pos.isWithdrawn, pos.isWithdrawnOnIndex, pos.getPredicateAddress, pos.buildExitPayloadOnIndex. The token classes still wrap them for the 95% case.

Errors extend VError

POSBridgeError extends VError from @polygonlabs/verror — a TypeScript-first, browser-friendly port of Joyent's canonical Node verror library. Consumers get the standard cause-chain helpers (findCauseByName, findCauseByType, info(err), fullStack(err)) matching Joyent's documented API, plus a typed code discriminator on top. info is an own enumerable property, so any logger that serializes own properties on Error instances picks up the structured payload. Zero runtime dependencies and ESM-only — bundles cleanly for both Node and browser.

Performance and resource use

  • Per-contract ABI fetches eliminated — ~70 KB across many round trips replaced by a single ~7 KB index.json fetch for addresses.
  • Web3SideChainClient singleton removed; per-instance state reduces memory pressure in multi-tenant deployments.
  • Address cache keyed by ${baseUrl}/${network}, not just network — survives POSClient instance churn and stays multi-tenant-safe.
  • p-limit (battle-tested 10 KB dep) replaces ~80 lines of custom mapPromise with cleaner concurrency semantics and proper first-failure rejection.

Observability

  • POSBridgeError exposes a stable discriminator code consumers can route on (error.code === 'BURN_TX_NOT_CHECKPOINTED'). The 28 codes match the legacy ErrorHelper.throw(code, ...) keys verbatim, so existing consumer dashboards and alert queries continue to fire after the rewrite.
  • RPC token sanitisation (regex strip of ?token=... / &token=...) applied at the SDK boundary before errors reach consumer loggers — protects services using non-sanitising loggers from leaking RPC credentials.
  • Structural Logger interface (pino-shaped) lets consumers plug in their own structured logger (pino, bunyan, custom) without the SDK taking a runtime dep on any specific implementation. The default is noopLogger — the SDK no longer writes to stdout.

Browser compatibility

A new @polygonlabs/pos-sdk-test-app workspace package (packages/test-app/) bundles the SDK through Vite and exercises the public surface in a real browser via Playwright. Catches any case where the SDK accidentally relies on Node built-ins, polyfills, or non-browser-safe APIs that Vitest's Node-mode tests would miss. The Playwright spec runs in chromium and asserts the bundle loads cleanly, every public symbol is callable, and no console.error occurs. pnpm -r run test auto-skips the spec gracefully when Playwright browsers aren't installed.

The smoke test surfaced a real long-term hazard worth tracking: pos-sdk's dist transitively imports Node's events (via @ethereumjs/util's asyncEventEmitter) and buffer (via readable-stream / safe-buffer) — 60 Buffer references in dist/, behind method-call gates so the happy path doesn't trip them.

Testability

  • ContractCaller and POSBridgeHelpers are independently mockable services because they were extracted as composition, not inheritance. Unit testing the bridge logic no longer requires instantiating the full client.
  • Pure functions — proof building, Merkle tree construction, RLP encoding — are now independently testable without spinning up a chain.
  • Adapter parity tests run the same suite three times (viem, ethers v5, ethers v6) to catch translation bugs at PR time.
  • Historical burn-tx fixtures let exit-payload byte-snapshot tests run in seconds without waiting for a fresh checkpoint cycle.

Architectural cleanup

  • BaseToken → POSToken → ERC20 chain dismantled in favour of two composed services: ContractCaller (transaction plumbing) and POSBridgeHelpers (predicates, exit payloads). Adding a new contract wrapper now means writing a class that uses a ContractCaller, not one that inherits 3 levels of bridge logic that does not apply.
  • The abstracts/, implementation/, and enums/ directories are deleted entirely.
  • 11 utility files deleted; all circular dependencies between utils/index.ts ↔ abstracts/index.ts ↔ subpaths broken.
  • Webpack 4 → tsup (single config, ESM + CJS + DTS, target: es2023).
  • Final source tree: ~3,900 lines (down from 5,674), structurally clearer.

Test plan

  • Add repo secrets POS_SDK_TEST_PARENT_RPC, POS_SDK_TEST_CHILD_RPC, POS_SDK_TEST_PRIVATE_KEY so PR-trigger CI runs the full integration suite (without them, the integration tier skips cleanly via describe.skipIf(!HAS_CREDS))
  • Verify PR CI green: lint, typecheck, unit, integration, browser smoke (Playwright)
  • Verify nightly CI green at least once (runs the gated 4h deposit-withdraw cycle test on each adapter via .github/workflows/ci-nightly.yml)
  • Verify the auto-generated changeset-release/master PR appears after merge
  • First-publish bootstrap: if CI publish 403s on the initial @polygonlabs/pos-sdk release, recover locally with pnpm exec changeset publish on the merge commit
  • Migrate proof-generation-api to use pos.buildExitPayload, pos.isCheckpointed, pos.getBlockProof in place of maticClient.exitUtil.X (full mapping in MIGRATION.md)
  • Audit portal and staking-ui for legacy pos.client.parent.X calls (signTypedData, etheriumSha3, encode, sendRPCRequest) and migrate per MIGRATION.md's replacement table
  • Confirm any consumers using the legacy ZkEvmClient understand they keep @maticnetwork/maticjs installed alongside @polygonlabs/pos-sdk (or only) until the zkEVM chain is shut down
  • Record real burn-tx triples for tests/integration/exit-payload.test.ts (one per ERC-20/721/1155); recording procedure in packages/pos-sdk/tests/README.md
  • Verify test-token contract addresses in tests/fixtures/networks.ts against currently-deployed mintable testnet tokens
  • Reduce pos-sdk's transitive Node-built-in deps (Buffer, events) so the test-app's stub layer can be removed — track as a follow-up, not a blocker for 1.0
  • After merge: deprecate @maticnetwork/maticjs on npm with a message pointing PoS users at @polygonlabs/pos-sdk and zkEVM users at the chain's wind-down notice
  • After merge: update internal services consuming @maticnetwork/maticjs per packages/pos-sdk/MIGRATION.md

# is not blown by checkpoint waits.
POS_SDK_TEST_E2E_ENABLED: 'true'
steps:
- uses: 0xPolygon/pipelines/.github/actions/ci@main
@MaximusHaximus MaximusHaximus force-pushed the pos-sdk-1.0-rewrite branch from 8a11db7 to 11801e5 Compare May 1, 2026 12:57
Foundation for the @polygonlabs/pos-sdk 1.0 monorepo. None of these
touch package source — they wire up the workspace, the CI gates, and
the planning record so the rewrite can land in a clean per-package
commit on top of this one.

Workspace tooling
- tsconfig.json: project reference for packages/pos-sdk/tsconfig.build.json
- pnpm-workspace.yaml: adds publicHoistPattern @tsconfig/* so tsup's
  load-tsconfig (a transitive dep that runs from
  node_modules/.pnpm/...) can follow extends: "@tsconfig/node20" in
  each package's strict tsconfig
- package.json (root): adds @tsconfig/node20 to devDependencies for
  the same reason
- pnpm-lock.yaml: regenerated; carries the SDK's added runtime deps
  (@polygonlabs/verror, p-limit, ethereum-cryptography, @ethereumjs/*)
  plus the test-app's vite / playwright tooling. Lockfile lives at
  workspace root; its content is consumed by all package commits in
  the series.
- eslint.config.js: drops stale ignore patterns for
  packages/maticjs/{build_helper,license.js,test} (those targets no
  longer exist after the rename) plus the file-specific overrides
  for src/utils/http_request.ts and src/index.ts that no longer
  apply

CI workflows
- .github/workflows/ci-trigger.yml: forwards integration-test secrets
  POS_SDK_TEST_{PARENT_RPC,CHILD_RPC,PRIVATE_KEY} via job.env so the
  shared 0xPolygon/pipelines composite picks them up; updates the
  build job's filter to @polygonlabs/pos-sdk and drops Node 18.x
  from the matrix (the SDK declares engines.node >=20)
- .github/workflows/ci-nightly.yml (new): daily 03:00 UTC schedule
  + workflow_dispatch, sets POS_SDK_TEST_E2E_ENABLED=true so the
  4h deposit-withdraw cycle test runs once a day without crowding
  PR-driven CI

Plan / decision record
- plans/pos-sdk-1.0-rewrite.md: the 9-stage agent-executable plan
  the rewrite was driven from. Kept on the branch as durable
  reference so the design rationale isn't lost when the PR closes.
- PLAN.md, PLAN_BACKUP.md: historical strategic-plan documents
  retained alongside the executable plan

Cleanup
- .changeset/old-dolls-prove.md: removes a stale empty changeset
  left over from a prior interactive `pnpm exec changeset add`
  session that was abandoned without a body. The pos-sdk changeset
  lands with its package commit.
Renames @maticnetwork/maticjs to @polygonlabs/pos-sdk and replaces
every load-bearing piece of the legacy SDK's design with one that
holds up under multi-tenant, type-strict, observability-aware,
browser-bundleable use.

Why this is a clean break

The 0.x SDK accumulated a decade of abstraction debt that produced
real failure modes:

- The plugin model mutated module-level globals (utils.Web3Client = X),
  making multi-tenant use unsafe — two POSClient instances against
  different networks cross-contaminated each other in the same
  process.
- The lazy ITransactionWriteResult shape conflated submitted and
  confirmed: getTransactionHash() *initiated* a send rather than
  observing one, and consumers consistently mis-used it. A caller
  assuming idempotency caused a production outage by double-
  broadcasting.
- The BaseToken → POSToken → ERC20 inheritance chain forced
  non-token contracts (RootChainManager, GasSwapper) to extend a
  class named BaseToken, blocking otherwise-trivial wrapper additions.
- ABIs were loaded at runtime from a single CDN — a single point of
  failure for every running consumer.
- BaseBigNumber predated native bigint by half a decade; consumers
  converted to it just so the SDK could convert back internally.
- 27 `: any` and 50 `as` casts on the public surface (5 of them
  `as any` on the entire client config) gave consumer code no
  compile-time signal of misuse.

What ships in 1.0

- POSClient.init({ network, parent, child }) is the only construction
  path. ParentClientConfig is a tagged union — consumers pass their
  existing viem / ethers-v5 / ethers-v6 client and tell the SDK what
  kind it is. No global state, no plugin to register.
- Three internal Adapter implementations (viem, ethers v5, ethers v6)
  abstract every read / write / estimate / receipt / keccak primitive
  the bridge code needs. Adapters take type-only imports of their
  parent client library so a static module load never crashes for
  consumers using a different library; runtime value-imports for
  ethers happen via dynamic `import('ethers')`. The Adapter is
  deliberately not exported — it's an internal abstraction.
- TxResult = { hash, confirmed() }. Hash is populated synchronously
  the moment the parent-chain RPC accepts the broadcast; confirmed()
  waits for the receipt and is idempotent (the underlying wait is
  memoised).
- Unsigned-tx surface via prepareXxx. Every public write has a
  sibling prepareXxx (prepareApprove, prepareDeposit,
  prepareCompleteWithdraw, prepareDepositEther, …) returning
  { to, data, value? } instead of broadcasting. Closes the smart-
  wallet / batched-multicall / offline-sign gap that the legacy
  option.returnTransaction flag served clumsily. Two distinct,
  statically-typed methods replace the conditional-shape foot-gun.
- Bridge helpers exposed flat on POSClient: pos.buildExitPayload,
  pos.buildExitPayloadOnIndex, pos.isCheckpointed, pos.isWithdrawn,
  pos.isWithdrawnOnIndex, pos.getBlockProof, pos.getPredicateAddress.
  The legacy SDK's pos.client.exitUtil flat-access was used by
  consumers building exit payloads outside the standard token flows
  (sync block events, custom bridge events, plasma exits); the new
  surface mirrors that ergonomics without re-exposing internals.
- BaseToken hierarchy dismantled in favour of two composed services:
  ContractCaller (transaction plumbing — adapter wiring, address
  resolution, gas estimation, EIP-1559 guards) and POSBridgeHelpers
  (predicates, exit-payload construction, isWithdrawn checks).
- Native `bigint` everywhere. Every public method on the token
  classes accepts and returns `bigint`; conversion to BigNumber for
  ethers v5 happens once at the v5 adapter boundary (.toBigInt()
  inputs, BigNumber.from outputs).
- POSBridgeError extends VError (a TypeScript-first, browser-friendly
  port of Joyent's canonical Node verror library) with a closed
  28-code POSBridgeErrorCode union. Consumers get the standard
  cause-chain helpers — findCauseByName / findCauseByType / info /
  fullStack — matching Joyent's documented API. `code` and `info`
  are own enumerable properties so any logger that walks them (pino,
  winston, custom, @polygonlabs/logger) surfaces the discriminator
  and structured payload directly. Codes match the legacy
  ErrorHelper.throw(code, ...) keys verbatim so any consumer
  dashboards or alert queries keyed off the old strings continue to
  match. Sets this.name = 'POSBridgeError' for Sentry/APM
  aggregation.
- ABIs vendored as `as const` TypeScript files in src/abi/, giving
  viem-typed inference at every internal call site. The CDN
  dependency for ABIs is gone.
- Contract addresses still fetched dynamically (the predicate /
  RootChainManager addresses change occasionally and we don't want
  to ship a new SDK each time), but cached in-process with stale-
  while-revalidate semantics on a 1h TTL — long-running services
  pick up redeployments without restart, individual calls serve the
  cached value at near-zero cost. config.addresses override available
  for staging / air-gapped, config.addressTTLMs tunes the freshness
  vs. CDN-traffic trade-off.
- Structural Logger interface (pino-shaped). Consumers plug in their
  own logger — pino, bunyan, @polygonlabs/logger, custom — without
  the SDK taking a runtime dep. Default is noopLogger.
- sanitiseError strips RPC tokens (?token=... / &token=...) from
  errors before they reach consumer logs, walking the cause chain
  and preserving subclass prototypes.
- ETH deposits hoisted to top-level POSClient.depositEther /
  depositEtherWithGas — they don't fit on parent.erc20(addr) because
  ETH has no token contract. depositEtherWithGas is mainnet-only and
  throws ONLY_ALLOWED_ON_MAINNET on Amoy.
- parent / child namespaces eliminate the easily-inverted isParent:
  boolean parameter (`pos.parent.erc20(addr)` not
  `pos.erc20(addr, true)`).
- Method renames: withdrawStart → startWithdraw, withdrawExit →
  completeWithdraw, withdrawExitFaster → completeWithdrawFast,
  etheriumSha3 → soliditySha3.
- p-limit replaces the legacy mapPromise — caps in-flight RPC fan-out
  during proof construction, propagates first failure (not the legacy
  swallow-into-result-wrapper).
- Tests: full pyramid. Unit (errors / sanitise / address-service /
  abi-types / concurrency / prepare-tx, always run), integration on
  Amoy + Sepolia with describe.skipIf(!HAS_CREDS) so the suite passes
  locally and runs for real in CI when secrets are configured.
  Adapter parity tests (same it() names across viem / ethers v5 /
  ethers v6) catch translation bugs at PR time. End-to-end
  deposit→checkpoint→withdraw cycle gated on POS_SDK_TEST_E2E_ENABLED
  for nightly CI.
- README + MIGRATION + examples (viem.ts / ethers-v5.ts /
  ethers-v6.ts) all rewritten for the new surface. MIGRATION
  includes a comprehensive replacement table for every removed API
  (signTypedData, etheriumSha3, encode, sendRPCRequest, …) with
  per-library replacement code (viem / ethers v5 / ethers v6).
  Legacy examples/{pos,utils_pos,config,package,.env}* and the
  manual/ debug scratch directory are deleted — every API they
  invoked is gone in 1.0.

Stats

Final source tree under packages/pos-sdk/: ~3,900 lines (down from
the legacy ~5,674). 11 dead-code utility files deleted. The
abstracts/, implementation/, and enums/ directories are gone
entirely. webpack 4 → tsup. Build emits ESM + CJS + DTS. The full
src/ now compiles under the strict base tsconfig (target es2023,
module nodenext, strict, erasableSyntaxOnly, verbatimModuleSyntax)
with zero `: any`, zero `as any`, and zero `console.*` calls.

Scope: PoS only

The legacy @maticnetwork/maticjs package shipped both the PoS bridge
and the zkEVM bridge surfaces. This rewrite covers only the PoS
side. The zkEVM chain is on a wind-down schedule; consumers using
ZkEvmClient stay on @maticnetwork/maticjs until the chain is shut
down, then drop both together. PoS-only and mixed consumers migrate
per packages/pos-sdk/MIGRATION.md.
…right

New private workspace package `@polygonlabs/pos-sdk-test-app` bundles
the SDK through Vite and exercises its public surface in a real browser
via Playwright. Catches any case where the SDK accidentally relies on
Node built-ins, polyfills, or non-browser-safe APIs that unit tests
under Node would miss.

Why this lives in its own package

The pos-sdk unit + integration suites run under Vitest (which is
Node), so a Buffer.from() call inside a transitive dep like
@ethereumjs/util doesn't surface as a failure there — it only breaks
when a consumer bundles for the browser. Catching that gap requires
actually loading the built dist/ output in a browser. Vitest has a
browser mode but it doesn't run integration tests against a real
chain, so the cleanest split is: the SDK package keeps its existing
Node test pyramid; this package owns the "does it bundle and run in a
browser" gate.

What the smoke test exercises

- POSClient.init({ network, parent, child, addresses }) — addresses
  override skips the CDN fetch so no real RPC is needed
- pos.parent.erc20(addr).prepareApprove(amount) — the dynamic-import
  encodeFunctionData path through the viem adapter
- new POSBridgeError(code, msg, info, { cause }) — VError construction
- sanitiseError(err) — RPC token redaction + cause-chain walk
- noopLogger calls — no-op logger isn't smuggling Node APIs
- ethereum-cryptography/keccak — the noble-hashes path the adapters use

Implementation notes

- Vite 6 (not 5) — workspace's pnpm trustPolicy: no-downgrade rejected
  vite@5.4.21's provenance attestation. Vite 6 installs cleanly and
  the API surface used here is unchanged.
- src/node-stubs/{events,buffer}.ts — throw-on-call stubs aliased via
  vite.config.ts's resolve.alias. Without these, the SDK bundle fails
  vite build because @ethereumjs/util / readable-stream named-import
  from Node's events / buffer. Stubs preserve the runtime detection
  signal — they explicitly are NOT polyfills. Every method call
  throws, so any path that actually hits Buffer.X at runtime would
  fail loudly in the browser console and the spec.
- The Playwright spec auto-skips with a clear remediation message
  (`pnpm exec playwright install --with-deps chromium`) when the
  chromium binary is absent — `pnpm -r run test` stays green on
  machines without Playwright browsers installed.

Findings the smoke test surfaced

pos-sdk's dist transitively imports Node's events (via
@ethereumjs/util's asyncEventEmitter) and buffer (via readable-stream
/ safe-buffer). 60 Buffer references are inside @ethereumjs/util's
BufferUtil static class — behind method-call gates so the happy-path
public surface (POSClient.init with addresses override + prepareApprove
with spenderAddress) does NOT currently trip them. The browser test
passes, but reducing Node-built-in dependence further is a real
long-term hazard worth tracking.

Verification

- pnpm exec tsc --noEmit (in packages/test-app): exit 0
- pnpm exec vite build: exit 0; dist/ produced (~1.3 MB unminified
  main bundle, ~270 KB gzipped)
- pnpm exec playwright test: 1 passed in chromium
- pnpm exec eslint packages/test-app/: 0 errors, 0 warnings
@MaximusHaximus MaximusHaximus force-pushed the pos-sdk-1.0-rewrite branch from e410c24 to a4870f3 Compare May 1, 2026 14:57
@MaximusHaximus MaximusHaximus changed the title refactor: 1.0 rewrite — @polygonlabs/pos-sdk + @polygonlabs/zkevm-sdk refactor: 1.0 rewrite — @polygonlabs/pos-sdk May 1, 2026
@MaximusHaximus MaximusHaximus changed the title refactor: 1.0 rewrite — @polygonlabs/pos-sdk refactor: @polygonlabs/pos-sdk / 1.0 rewrite & modernization May 1, 2026
@MaximusHaximus MaximusHaximus changed the title refactor: @polygonlabs/pos-sdk / 1.0 rewrite & modernization refactor: rewrite & modernization - @polygonlabs/pos-sdk@1.0.0 May 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants