diff --git a/pages/_meta.json b/pages/_meta.json index b027401..7344e69 100644 --- a/pages/_meta.json +++ b/pages/_meta.json @@ -31,6 +31,7 @@ }, "core": "Core contracts", "templates": "Templates", + "networks": "Networks", "app_support": { "type": "page", "title": "App Support", diff --git a/pages/index.mdx b/pages/index.mdx index 4e099de..cb63ea3 100644 --- a/pages/index.mdx +++ b/pages/index.mdx @@ -28,7 +28,7 @@ just want to understand how you can use the protocol today, we recommend you che - Free: No protocol fees – runs exactly at gas cost - Unstoppable: Non-upgradable contracts run as long as the underlying network exists -- Multichain: Deployed on Ethereum, Optimism, Base, Zora, Polygon, Arbitrum + more +- Multichain: Deployed on Ethereum, Optimism, Base, Zora, Polygon, Arbitrum, Arc, Robinhood Chain + more - Open source: All contracts are [verified](https://etherscan.io/address/0x2ed6c4b5da6378c7897ac67ba9e43102feb694ee#code), [audited](https://github.com/0xSplits/splits-contracts/blob/main/audit/0xSplits_A-1.pdf), diff --git a/pages/networks.mdx b/pages/networks.mdx new file mode 100644 index 0000000..7e762f1 --- /dev/null +++ b/pages/networks.mdx @@ -0,0 +1,16 @@ +--- +description: "Network-specific notes for the Splits protocol, starting with Arc, where USDC is the native gas token." +--- + +# Networks + +Most of the protocol behaves the same on every network. This section covers +networks where something about the chain changes how you use Splits. + +| Network | Summary | +| :------ | :------ | +| [Arc](/networks/arc) | Splits V2.2 and the Warehouse on Arc, where USDC is the native gas token: what is deployed, SDK requirements, and how the two USDC token ids behave. | + +Contract addresses for every network are listed in the address tables on each +contract page, such as [Warehouse](/core/warehouse) and +[SplitV2](/core/split-v2). diff --git a/pages/networks/_meta.json b/pages/networks/_meta.json new file mode 100644 index 0000000..a07e3eb --- /dev/null +++ b/pages/networks/_meta.json @@ -0,0 +1,3 @@ +{ + "arc": "Arc" +} diff --git a/pages/networks/arc.mdx b/pages/networks/arc.mdx new file mode 100644 index 0000000..7d985f4 --- /dev/null +++ b/pages/networks/arc.mdx @@ -0,0 +1,154 @@ +--- +description: "How Splits V2.2 and the Warehouse work on Arc, where USDC is the native gas token: what is deployed, SDK requirements, and how USDC token ids behave." +--- + +import { Callout } from 'nextra-theme-docs' + +# Arc + +Splits V2.2 (Push and Pull splits) and the Warehouse are deployed on Arc +mainnet (chain id 5042) and Arc testnet (chain id 5042002). + +Arc-owned facts, such as how USDC works as the native token, fees, and RPC +behavior, live in [Arc's docs](https://docs.arc.io/arc/references/evm-differences). +This page only covers how Splits behaves there. + +## What is deployed + +Only the Warehouse and SplitV2 (Push and Pull splits) are deployed on Arc. Split +V1, V2.0 and V2.1 splits, Waterfall, Swapper, Oracle, Pass-Through, Vesting, +Liquid, Recoup and Diversifier are not. + +Addresses are listed in the "Arc – 5042" sections of the +[Warehouse](/core/warehouse) and [SplitV2](/core/split-v2) pages. They are the +same on testnet, listed under "Arc Testnet – 5042002". + +## SDK + +Arc requires `@0xsplits/splits-sdk` 6.7.0 or later, and +`@0xsplits/splits-sdk-react` 4.6.0 or later if you use the React hooks. + +On Arc the SDK supports only the [SplitV2 client](/sdk/splits-v2) and the +[Warehouse client](/sdk/warehouse). The data client, and the subgraph behind it, +do not cover Arc, so you do not need an API key for Arc. When `distribute` needs +a split's recipients and you have not passed them, the SDK recovers them from +onchain logs through your provider. + +## USDC on Arc: two token ids + +On Arc, USDC is the native token. Arc exposes it two ways, and both views read +the same balance (see [Arc's docs](https://docs.arc.io/arc/references/evm-differences)): + +- **Native**, with 18 decimals. +- **ERC-20**, at `0x3600000000000000000000000000000000000000`, with 6 decimals. + +Splits treats these as two different token ids, as the +[Warehouse](/core/warehouse) does for any two tokens: + +- The native id is `0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee`, the same id the + Warehouse uses for ETH on other networks. +- The ERC-20 id is the `uint256` form of `0x3600000000000000000000000000000000000000`. + +What follows from that: + +- **The funding route does not matter.** After you fund a split with USDC by + either route (an ERC-20 transfer or a native value send), the split's balance + is visible under both ids: 100 USDC reads as `100000000` under the ERC-20 id + and `100000000000000000000` under the native id. Never add the two together. +- **`distribute` credits under the id you pass.** Recipients are credited under + that id, whichever route funded the split. `withdraw` is also per id, so a + recipient credited under both ids makes two withdrawals, and withdrawing under + an id with no credit reverts. +- **The Warehouse labels both ids the same**: the name is "Splits Wrapped USDC" + and the symbol is "splitsUSDC". Only the decimals differ. + +How a distribution behaves depends on the id and on whether the split is Push or +Pull: + +| Route | Token id | Decimals | Push behavior | Pull behavior | Failure modes | +| :---- | :------- | :------- | :------------ | :------------ | :------------ | +| ERC-20 | `0x3600…0000` | 6 | `distribute` pays recipients directly. A blocklisted recipient makes the whole call revert and nobody is paid; so does a blocklisted distributor when the distributor fee is above 0. Contract recipients that revert on receive are fine. | `distribute` credits the Warehouse and succeeds. Only a blocklisted recipient's own `withdraw` reverts. A contract that reverts on receive can still withdraw. | On an immutable Push split, a blocklisted recipient blocks distribution permanently. | +| Native | `0xeeee…eeee` | 18 | A recipient that cannot take native value (a blocklisted address, or a contract that reverts on receive) does not block the others. Its share is credited in the Warehouse under the native id. | `distribute` credits the Warehouse and succeeds. Only the affected recipient's own `withdraw` reverts. | A share credited to a recipient that cannot receive stays stuck until that recipient can receive again. A distributor that reverts on receiving a reward above 0 reverts the whole Push call. A recipient contract can also block a Push distribution by re-entering `distribute`; see the notes on the [SplitV2](/core/split-v2) page. | + +Real transactions on Arc mainnet confirmed that funding by either route is +visible under both ids, that `distribute` credits only the id you pass, and that +`withdraw` pays out per id. They used four splits of 0.1 USDC each (Pull and +Push, funded by ERC-20 transfer and by native send). The failure modes in the +table come from read-only simulations (`eth_call` with state overrides, nothing +broadcast), using an address that is blocked on Arc testnet. The ERC-20 id is +served by an upgradeable contract controlled by Circle and Arc, so this behavior +can change. + +### Recommendation + +This is guidance derived from the behavior above, not a protocol rule. + +- Default to the ERC-20 id. It matches Arc's guidance of using the 6-decimal + interface for transfers and accounting, and it works for any contract + recipient. +- On the ERC-20 id, use a Pull split when recipients are many, untrusted, or + could be blocklisted. Use Push only for a few known recipients and an owner who + can remove one. Never use an immutable Push split if a recipient could be + blocklisted. +- Pick one id per split and stay with it. When you work on a split you did not + create, check both ids. +- Use the native id only if you specifically need Push distribution to keep + working when one recipient cannot receive, and you accept the stuck credit + described above. + + +**The zero address is not USDC in the SDK.** `distribute` and +`WarehouseClient.withdraw` treat a zero `tokenAddress` as the native id, but +`getSplitBalance` does not: it passes the address through as given. Pass the +ERC-20 address explicitly in all three. + + +## Distributing and withdrawing + +`distribute` needs the split's full configuration, and a mismatch reverts with +`InvalidSplit()`. The configuration is emitted in the `SplitUpdated` event, at +creation and on every update. The split's `updateBlockNumber()` returns the +block of the latest one, so you can read that single block instead of scanning +history. In the SDK, you can skip recovery by passing `splitFields` (the +`recipients` and `distributorFeePercent`) to `distribute`. + +The distribution incentive goes to the distributor address passed to +`distribute`, which the SDK defaults to the connected wallet. On a Pull split it +is credited to that address's Warehouse balance, so it needs a `withdraw` too. + +`distribute` does not pay out everything. It keeps 1 unit in the split, a Pull +split also keeps 1 unit in its own Warehouse balance, and rounding remainders +stay behind. The Warehouse's `withdraw(owner, token)` pays the balance minus 1: +it pays 0 at a balance of 1 and reverts at a balance of 0. The unit that stays in +a recipient's Warehouse balance is included in their next withdrawal. One unit +is 0.000001 USDC under the ERC-20 id, so smaller amounts can only move through +the native id, and 1 wei under the native id. + +For example, a Pull split with three recipients (50/30/20), funded with 0.1 USDC +and distributed under the ERC-20 id, credited 49,999, 29,999 and 19,999 units. +After their withdrawals the recipients had received 49,998, 29,998 and 19,998 +units, and 6 units of the 100,000 were left behind: 1 in the split, 2 in the +split's Warehouse balance, and 1 in each recipient's Warehouse balance. + +## Network notes + +| Network | RPC | +| :------ | :-- | +| Arc – 5042 | `https://rpc.mainnet.arc.io` | +| Arc Testnet – 5042002 | `https://rpc.testnet.arc.io` | + +- **`eth_getLogs` range.** On the public RPCs for both networks, a range of + 10,000 blocks works and 10,001 fails with error `-32012` ("requested range too + large"). The public RPCs also rate-limit requests (error `-32005`). Use + `updateBlockNumber()` to avoid wide log scans. +- **Fees and logs.** Arc sets a minimum fee per gas. A USDC move through the + ERC-20 interface emits two `Transfer` logs, and a native move emits one. Both + are defined by Arc, not Splits. See + [Arc's docs](https://docs.arc.io/arc/references/evm-differences) for the + current fee requirements, and + [USDC system events](https://docs.arc.io/arc/references/usdc-system-events) + before you count `Transfer` logs. +- **Testing.** A local fork does not reproduce the behavior of the upgradeable + ERC-20 contract at `0x3600…0000`, so test with `eth_call` against the Arc RPC. + +Verified against @0xsplits/splits-sdk 6.7.0 and Splits V2.2 on 2026-10-23. diff --git a/public/llms.txt b/public/llms.txt index f0476ac..d1685af 100644 --- a/public/llms.txt +++ b/public/llms.txt @@ -48,6 +48,11 @@ The protocol makes it easy to split onchain payments among multiple recipients. - [Liquid Split](https://splits.org/protocol/docs/templates/liquid.md): Mutable split governed by NFT ownership - [Diversifier](https://splits.org/protocol/docs/templates/diversifier.md): Automatically swap and diversify incoming tokens +## Networks + +- [Networks](https://splits.org/protocol/docs/networks.md): Network-specific notes for the protocol +- [Arc](https://splits.org/protocol/docs/networks/arc.md): Splits V2.2 and the Warehouse on Arc, where USDC is the native gas token: deployed contracts, SDK requirements, and USDC token id behavior + ## Optional - [GitHub](https://github.com/0xSplits): Source code for all contracts and SDKs