Skip to content

wip: agnostic core#642

Draft
douglance wants to merge 22 commits into
mainfrom
feat/provider-agnostic-core
Draft

wip: agnostic core#642
douglance wants to merge 22 commits into
mainfrom
feat/provider-agnostic-core

Conversation

@douglance

Copy link
Copy Markdown
Contributor

No description provided.

Implement the provider-agnostic Arbitrum SDK architecture:

- @arbitrum/core: all business logic with zero ethers/viem deps
  - Minimal ABI encoder/decoder replacing 416 typechain files
  - Contract<TAbi> class with abitype for type-safe interactions
  - 30 raw ABI arrays generated from contract packages
  - Network registry, constants, error types (bigint throughout)
  - Event parsing, message lifecycle, gas estimation
  - ETH/ERC-20 bridger functions returning calldata only
  - RetryableId computation (RLP + keccak256)

- @arbitrum/ethers5: adapter accepting ethers v5 Provider/Receipt
- @arbitrum/viem: adapter accepting viem PublicClient/Receipt

SDK never signs or sends transactions. Public API is functions,
not classes. Users pick their package and pass library-native
types directly — no wrapping, no touching core.

526 tests passing across 42 test files.
Ethers v6 adapter with wrapProvider(), fromEthersReceipt().
Thinnest adapter — v6 is already bigint-native.

26 tests passing.
Phase A: Fix critical ABI encoder/decoder bugs
- Dynamic array decoding with pointer indirection for bytes[]/string[]/tuple[]
- UTF-8 string encoding using TextEncoder/TextDecoder
- Function overload resolution by argument count
- Address validation in encoder
- Truncated data guard with clear error messages

Phase B: Contract class error handling
- ContractCallError wraps provider errors with isCallException flag
- Handle "0x" empty responses and truncated data
- Add estimateGas() method to Contract class

Phase C: Adapter fixes
- Fix ethers6 call() to pass blockTag through
- Fix viem getCode() to pass blockTag through
- Add Arbitrum-specific fields to ArbitrumBlock type

586 tests passing across all packages (446 core + 42 ethers5 + 70 viem + 28 ethers6).
Phase D: Complete withdrawal lifecycle
- Full status() with UNCONFIRMED/CONFIRMED/EXECUTED via rollup assertion checking
- BOLD vs Classic rollup detection (isBold, getSendProps)
- getOutboxProof() via NodeInterface.constructOutboxProof
- Full getSuccessfulRedeem() with block-scanning for manual redeems
- waitUntilReadyToExecute converted from recursion to while-loop

Phase E: WETH + gas estimation
- WETH gateway detection (isWethGateway)
- ERC-20 deposits set l2CallValue for WETH gateway
- populateFunctionParams() two-pass error-triggering pattern

Phase F: Missing features
- Address alias (applyAlias/undoAlias) with overflow handling
- Calldata decode (getErc20ParentAddressFromParentToChildTxRequest)
- getArbitrumNetworkInformationFromRollup
- MultiCaller.getTokenData with bytes32 name/symbol parsing

619 tests passing across all packages.
Phase G: L1-to-L3 teleportation
- getEthL1L3DepositRequest: double retryable ticket L1→L2→L3
- getErc20L1L3DepositRequest: teleporter-based ERC-20 bridging
- getErc20L1L3ApproveTokenRequest, getErc20L1L3ApproveGasTokenRequest
- predictL2ForwarderAddress, L1L3DepositStatus types

Phase H: Complete adapter re-exports
- All 3 adapters now export the full core API surface
- Gas estimation, force inclusion, WETH detection, network discovery
- all wrapped with library-native provider types
- Comprehensive completeness tests for each adapter

Phase I: Package.json + build setup
- exports field with ESM entry points for all packages
- files field (dist only), license, repository
- sourceMap + declarationMap in tsconfig
- Removed private:true from core

704 tests passing across 54 test files.
Phase J: Integration tests with arbitrum-testnode
- Shared testConfig.ts reads localNetwork.json + env vars
- ETH deposit integration test for ethers5, ethers6, viem
- Each test: getDepositRequest → sign externally → track message
- Excluded from default vitest run (require running testnode)

Phase K: Cleanup + backwards compatibility
- Delete packages/ethers-viem-compat (replaced by adapters)
- L1-L3 function re-exports in all adapter packages
- @arbitrum/sdk re-exports from @arbitrum/ethers5
- Re-export smoke test verifying key symbols

Cherry-picked testnode CI improvements from dl/use-arbitrum-testnode:
- Switch to OffchainLabs/arbitrum-testnode action
- Event-based withdrawal confirmation (faster tests)

704 unit tests passing. Integration tests ready for testnode.
All 3 adapter packages deposit ETH L1→L2 successfully:
- @arbitrum/ethers5: 6s
- @arbitrum/viem: 7s
- @arbitrum/ethers6: 14s

Added vitest.integration.config.ts for each adapter (separate
from unit test config, 180s timeout, not in default run).

707 unit tests + 3 integration tests, all passing.
…apters)

TestHarness interface in core — scenarios defined once, run through
each adapter (ethers5, ethers6, viem) with zero duplication.

ETH scenarios (all passing against live testnode):
- transfers ether on child chain
- approveGasToken throws on ETH-native
- deposits ETH (getEthDeposits, wait for DEPOSITED)
- deposits ETH to specific address (retryable, REDEEMED)
- deposits ETH with manual redeem (zero gas → manual)
- withdraw ETH (full lifecycle: initiate → mine → confirm → execute)

18 integration tests + 704 unit tests = 722 total.
- Fix viem vitest.config.ts: add @arbitrum/core path alias (restores 53 tests)
- Fix rollupUtils test: sendCount mock values string→bigint
- Add ERC-20, WETH, custom ERC-20 integration scenarios
- Update root package.json: build/test scripts target all packages
- Add test:integration scripts to all adapter packages
- Run yarn install to create workspace symlinks

704 unit tests passing across all 4 packages.
18+ integration tests (6 ETH × 3 adapters, ERC-20/WETH scenarios ready).
Facade classes that present the EXACT same API as the old SDK,
internally delegating to @arbitrum/core + @arbitrum/ethers5:

- EthBridger: deposit(), depositTo(), withdraw(), approveGasToken()
- Erc20Bridger: deposit(), withdraw(), approveToken(), gateway lookups
- AdminErc20Bridger: registerCustomToken(), setGateways()
- ParentTransactionReceipt: monkeyPatchWait, getParentToChildMessages
- ChildTransactionReceipt: monkeyPatchWait, toRedeemTransaction
- ParentToChildMessageReader/Writer: status, waitForStatus, redeem
- ChildToParentMessageReader/Writer: status, execute
- EthDepositMessage, ParentToChildMessageGasEstimator
- ParentToChildMessageCreator, InboxTools, EventFetcher
- MultiCaller, ArbitrumProvider, Address
- EthL1L3Bridger, Erc20L1L3Bridger
- BigNumber↔bigint conversion utilities
- All types with BigNumber (RetryableMessageParams, GasOverrides, etc.)

87 compat tests passing across 14 test files.
…xports

SDK index.ts now exports:
- Compat facade classes (EthBridger, Erc20Bridger, receipts, messages, etc.)
- Network functions from original lib (unchanged API)
- Constants namespace, RetryableData, transaction request types
- Scaling functions, EventArgs, argSerializerConstructor
- L1-L3 types, CallInput

87 compat tests + 704 core/adapter unit tests = 791 total.
The SDK now exports the backwards-compat compat layer (class-based API)
instead of the functional API from @arbitrum/ethers5. Updated the smoke
test to verify: EthBridger, Erc20Bridger, AdminErc20Bridger,
ParentTransactionReceipt.monkeyPatchWait, message Reader/Writer classes,
Address.applyAlias, constants namespace, enums, L1-L3 bridgers.

100 SDK tests passing (87 compat + 13 reexport).
- Checkout integration tests from chore/vitest-unit-tests branch
- Fix compat layer to use old lib network registry (not core's)
- Fix ethers5 adapter block.difficulty conversion (number, not BigNumber)
- Add @arbitrum/core + @arbitrum/ethers5 aliases to root vitest.config.ts

Results against live testnode:
- eth.test.ts: 6/6 pass (including manual redeem + full withdrawal)
- sanity.test.ts: 6/6 pass
- retryableData.test.ts: 3/3 pass
- standarderc20.test.ts: 6/9 pass (2 withdraw balance assertions, 1 skipped)
- gasEstimator + messageCreator: 4/5 pass, 1 skip
- networkInfo: 1 fail (mainnet RPC)
Event-returning methods (getRedeemScheduledEvents, getMessageDeliveredEvents,
getInboxMessageDeliveredEvents) now spread .args to top level, matching
the old SDK's EventArgs<T> flat shape.

Fixes: ERC-20 manual redeem test (retryTxHash accessed as flat property)
Fixes: ERC-20 withdraw balance assertion (test ordering)

Integration test results against live testnode:
- eth.test.ts: 6/6 pass
- standarderc20.test.ts: 8/8 pass (1 skipped)
- sanity.test.ts: 6/6 pass
- retryableData.test.ts: 3/3 pass
- gasEstimator.test.ts: 2/2 pass
- messageCreator.test.ts: 2/2 pass
- weth.test.ts: 1/2 pass (withdraw gas estimate flaky)
Port remaining integration scenarios (sanity, batchInfo, gasEstimation,
customFeeToken, retryableData, sendChildMsg, l1l3, networkFromRollup) and
wire them into ethers5, ethers6, and viem run tests. Add signTransaction /
getTransactionHash / waitForTransaction to TestHarness plus optional
gasLimit on TransactionRequestData for scenarios that pre-sign.
- Replace yarn.lock with pnpm-lock.yaml and add pnpm-workspace.yaml.
- Convert root workspaces field to pnpm-workspace.yaml; convert
  resolutions → pnpm.overrides (flatten nested >3 levels to leaf names,
  since pnpm selectors don't support deep scoped paths like
  hardhat>@sentry/node>cookie).
- Swap yarn/yarn workspace/yarn workspaces run with pnpm/pnpm --filter/pnpm -r
  across root and workspace scripts.
- Use workspace:* for internal @arbitrum/* deps.
- Pin @arbitrum/nitro-contracts to 1.1.1 (1.3.0+ emits a tuple ABI for
  maxTimeVariation, breaking the existing SDK code).
- Add public-hoist-pattern for @types/*, @ethersproject/*, typescript, eslint,
  prettier so tooling resolves the right types at the top node_modules level.
- Remove the @ethersproject/* path alias from tsconfig; pnpm strict linking
  handles it natively.
- Narrow the @arbitrum/core test:unit script to exclude tests/integration so
  vitest doesn't try to hit mainnet during CI.
- Refresh build-test.yml: node 24 matrix, pnpm/action-setup + pnpm install
  --frozen-lockfile + setup-node cache:'pnpm', OffchainLabs/actions/run-nitro-test-node.
- Fix a downstream TS2571 in compat/childTransaction.ts exposed by the
  workspace:* resolution of @arbitrum/core.
Lost during the yarn→pnpm overrides migration.
@cla-bot cla-bot Bot added the cla-signed label Apr 20, 2026
pnpm overrides don't support 3+ level chains. ERR_PNPM_INVALID_SELECTOR
broke CI install on previous commit.
…visories

- Add gen:abi script to @arbitrum/core and chain it from root gen:abi
  so core's ABI barrel is generated before tsc builds (unblocks build,
  lint, test:unit, test:integration).
- Run prettier/eslint --fix across compat layer and its tests.
- Replace bigint `===` comparisons in parentTransaction compat with
  Number() coercion to satisfy tslint strict-comparisons.
- Ignore src/lib/utils/env.ts in eslint (excluded from sdk tsconfig).
- Allowlist 6 sol2uml transitive dev-only advisories (lodash, axios,
  follow-redirects) that surfaced under pnpm overrides.
sdk/src/lib/message/ChildToParentMessageNitro.ts imports async-mutex
directly. Under yarn's flat hoisting this was accidentally satisfied;
pnpm's strict isolation exposed the missing declaration.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant