diff --git a/docs/contracts/v2/deployments.md b/docs/contracts/v2/deployments.md index c337940..38aa581 100644 --- a/docs/contracts/v2/deployments.md +++ b/docs/contracts/v2/deployments.md @@ -4,6 +4,8 @@ sidebar_position: 2 --- The latest Ring Swap (v2), FewFactory, router, wrapper, and hook contracts are deployed at the addresses listed below. +The `UniversalRouter` rows are the Ring Universal Router addresses used by the current Ring Interface for classic +Ring Swap flows. ## Mainnet Deployments @@ -14,7 +16,7 @@ The latest Ring Swap (v2), FewFactory, router, wrapper, and hook contracts are d | Ring Swap Factory | `0xeb2A625B704d73e82946D8d026E1F588Eed06416` | | Few Factory | `0x7D86394139bf1122E82FDF45Bb4e3b038A4464DD` | | Ring Swap Router | `0x39d1d8fcC5E6EEAf567Bce4e29B94fec956D3519` | -| UniversalRouter | `0xEff87A51f5Abd015F1AFCD5737BBab450eA15A24` | +| UniversalRouter | `0x24e743CcE93235641f2be8Ce7ffC6330903ab96f` | | Permit2 | `0x000000000022D473030F116dDEE9F6B43aC78BA3` | | FewETHWrapper | `0xAda6059b4F6244Acd8934095Ed0162C5Df6B5ebB` | | FewETHHook | `0x044301939dEB7ca53C4733dd4D9B3bc5Ea0c6888` | @@ -93,7 +95,7 @@ The latest Ring Swap (v2), FewFactory, router, wrapper, and hook contracts are d | Ring Swap Factory | `0x9BfFC3B30D6659e3D84754cc38865B3D60B4980E` | | Few Factory | `0xb3Ad7754f363af676dC1C5be40423FE538a47920` | | Ring Swap Router | `0x224749CDd5791480ECEBE452e5FFAEfEf94Cd254` | -| UniversalRouter | `0x9a8C67C02250EC50f16b6143D139CbDB131f1e07` | +| UniversalRouter | `0xcc1d0c42CC672Fcb610Db8934635Acd0F605Fd86` | | Permit2 | `0x000000000022D473030F116dDEE9F6B43aC78BA3` | | FewETHWrapper | `0x20E6B1260d12910C0Ab13c1AbEBCFe24AE9c4fe7` | @@ -109,7 +111,7 @@ The latest Ring Swap (v2), FewFactory, router, wrapper, and hook contracts are d | Ring Swap Factory | `0x1246Fa62467a9AC0892a2d2A9F9aafC2F5609442` | | Few Factory | `0x974Cc3F3468cd9C12731108148C4DABFB5eE556F` | | Ring Swap Router | `0xD69ed581480239357515e200560353AF1BbaA46A` | -| UniversalRouter | `0xf293096D3C33610461a7bd101D201683111749b8` | +| UniversalRouter | `0x4e30E64faeF7c9Aee835904a48DC27eDA40e2CA7` | | Permit2 | `0x000000000022D473030F116dDEE9F6B43aC78BA3` | | FewETHWrapper | `0xEeE400Eabfba8F60f4e6B351D8577394BeB972CD` | @@ -125,7 +127,7 @@ The latest Ring Swap (v2), FewFactory, router, wrapper, and hook contracts are d | Ring Swap Factory | `0xEeE400Eabfba8F60f4e6B351D8577394BeB972CD` | | Few Factory | `0x974Cc3F3468cd9C12731108148C4DABFB5eE556F` | | Ring Swap Router | `0xf9d7ff2f6A0c3631A807199276a493Af8097916F` | -| UniversalRouter | `0xE17e4e8A43c1A0a1844BDe72ACA86Be3B81b28aF` | +| UniversalRouter | `0xa7f0381288753e01171752483413E53f8D7faf4D` | | Permit2 | `0x000000000022D473030F116dDEE9F6B43aC78BA3` | | FewETHWrapper | `0xc43a3Dd1b16168e00297315d679840e30A89df42` | @@ -141,7 +143,7 @@ The latest Ring Swap (v2), FewFactory, router, wrapper, and hook contracts are d | Ring Swap Factory | `0xEeE400Eabfba8F60f4e6B351D8577394BeB972CD` | | Few Factory | `0x974Cc3F3468cd9C12731108148C4DABFB5eE556F` | | Ring Swap Router | `0xf9d7ff2f6A0c3631A807199276a493Af8097916F` | -| UniversalRouter | `0x3374bEa3C2A347231f93Fc366346AD6e56c7af49` | +| UniversalRouter | `0x5dF345fB4e5F4791869bFD9cbCa941D931e6E781` | | Permit2 | `0x000000000022D473030F116dDEE9F6B43aC78BA3` | | FewETHWrapper | `0xc43a3Dd1b16168e00297315d679840e30A89df42` | diff --git a/docs/contracts/v2/fewtoken/get-erc20-token-address-via-fewfactory.md b/docs/contracts/v2/fewtoken/get-erc20-token-address-via-fewfactory.md index 49b7f2b..95802dc 100644 --- a/docs/contracts/v2/fewtoken/get-erc20-token-address-via-fewfactory.md +++ b/docs/contracts/v2/fewtoken/get-erc20-token-address-via-fewfactory.md @@ -1,6 +1,6 @@ --- title: Get ERC20 Token Address from FewToken -sidebar_position: 3 +sidebar_position: 4 --- FewTokens wrap original ERC20 tokens. In UI and analytics workflows, you may need to map back to the underlying token. diff --git a/docs/contracts/v2/fewtoken/get-fewtoken-address-via-fewfactory.md b/docs/contracts/v2/fewtoken/get-fewtoken-address-via-fewfactory.md index 0875c00..092d34b 100644 --- a/docs/contracts/v2/fewtoken/get-fewtoken-address-via-fewfactory.md +++ b/docs/contracts/v2/fewtoken/get-fewtoken-address-via-fewfactory.md @@ -7,6 +7,10 @@ Ring Swap uses wrapped tokens (FewTokens) in pools and swap paths. When you quote prices or build routes, use FewToken addresses instead of original ERC20 token addresses. +:::warning FewFactory is the only source of truth +Treat an address as a supported FewToken only when it is returned by the official `FewFactory` for the underlying ERC-20. Token `symbol`, `name`, `token()`, and pool existence do not define FewToken identity. See [FewToken Integration → FewToken Address Resolution](./integrating#fewtoken-address-resolution). +::: + ## FewFactory Interface ```solidity @@ -59,3 +63,16 @@ Then pass: ```text path = [fwTokenA, fwUSDC, fwTokenB] ``` + +## Validating an External Wrapper + +If a `(underlying, wrapper)` pair reaches your code from an external source (a pool's `token0`/`token1`, a quote, a router, an API), confirm it is canonical before routing, approving, or wrapping: + +```solidity +function isCanonicalFewToken(address underlying, address wrapper) external view returns (bool) { + return wrapper != address(0) + && IFewFactory(fewFactory).getWrappedToken(underlying) == wrapper; +} +``` + +Use a wrapper only when this check passes. Approval spender addresses should be selected from official Ring deployments or from the canonical FewToken returned by `FewFactory`. diff --git a/docs/contracts/v2/fewtoken/how-to-judge-is-few-token.md b/docs/contracts/v2/fewtoken/how-to-judge-is-few-token.md new file mode 100644 index 0000000..9659b3f --- /dev/null +++ b/docs/contracts/v2/fewtoken/how-to-judge-is-few-token.md @@ -0,0 +1,108 @@ +--- +title: How to Identify a FewToken +sidebar_position: 3 +--- + +A supported FewToken is the wrapper address returned by the official `FewFactory` for an underlying ERC-20. + +The canonical check is: + +```text +FewFactory.getWrappedToken(underlying) == candidateFewToken +``` + +Token metadata is not an identity source. `symbol` and `name` are not important for determining whether an address is a supported FewToken. A `token()` method or pool can be useful for discovery, but the supported FewToken check must still resolve through `FewFactory`. + +## Interfaces + +```solidity +interface IFewFactory { + function getWrappedToken(address originalToken) external view returns (address wrappedToken); +} + +interface IFewWrappedToken { + function token() external view returns (address originalToken); +} +``` + +## If You Start from an Original ERC20 + +Resolve the FewToken directly from the official `FewFactory`: + +```solidity +address fewToken = IFewFactory(fewFactory).getWrappedToken(originalToken); +require(fewToken != address(0), "FewToken not found"); +``` + +Use the returned `fewToken` for Ring Swap pairs and paths. + +## If You Start from a Candidate FewToken + +When a FewToken address comes from an external source, first read its underlying token, then confirm that `FewFactory` maps that underlying token back to the same candidate: + +```solidity +function isSupportedFewToken(address candidateFewToken) external view returns (bool) { + // This call reverts if the candidate does not implement the FewToken interface. + address originalToken = IFewWrappedToken(candidateFewToken).token(); + address expectedFewToken = IFewFactory(fewFactory).getWrappedToken(originalToken); + + return expectedFewToken != address(0) && expectedFewToken == candidateFewToken; +} +``` + +For integrations that need a reverting check: + +```solidity +function _assertSupportedFewToken(address candidateFewToken) internal view returns (address originalToken) { + originalToken = IFewWrappedToken(candidateFewToken).token(); + + address expectedFewToken = IFewFactory(fewFactory).getWrappedToken(originalToken); + require(expectedFewToken != address(0), "FewToken not found"); + require(expectedFewToken == candidateFewToken, "unsupported FewToken"); +} +``` + +## JavaScript Example + +```typescript +import { ethers } from 'ethers' + +const fewFactoryAbi = ['function getWrappedToken(address originalToken) view returns (address)'] +const fewTokenAbi = ['function token() view returns (address)'] + +async function isSupportedFewToken({ + provider, + fewFactoryAddress, + candidateFewToken, +}: { + provider: ethers.providers.Provider + fewFactoryAddress: string + candidateFewToken: string +}): Promise { + const fewToken = new ethers.Contract(candidateFewToken, fewTokenAbi, provider) + let originalToken: string + + try { + originalToken = await fewToken.token() + } catch { + return false + } + + const fewFactory = new ethers.Contract(fewFactoryAddress, fewFactoryAbi, provider) + const expectedFewToken = await fewFactory.getWrappedToken(originalToken) + + return ( + expectedFewToken !== ethers.constants.AddressZero && + ethers.utils.getAddress(expectedFewToken) === ethers.utils.getAddress(candidateFewToken) + ) +} +``` + +## Approval Spender Selection + +After identifying the FewToken, choose the approval spender from the integration path: + +- Official router flow: approve the official Ring Router, Universal Router, or Permit2 address for the chain. +- Manual wrap flow: approve the supported FewToken returned by `FewFactory`. + +Display metadata and external route metadata should not be used as the approval spender source. diff --git a/docs/contracts/v2/fewtoken/integrating.md b/docs/contracts/v2/fewtoken/integrating.md index ac37726..aaa9124 100644 --- a/docs/contracts/v2/fewtoken/integrating.md +++ b/docs/contracts/v2/fewtoken/integrating.md @@ -27,6 +27,56 @@ When integrating: - construct swap paths with FewToken addresses - resolve wrapped token addresses via FewFactory +## FewToken Address Resolution + +:::info FewFactory is the source of truth +Supported FewToken identity must come from the official `FewFactory`. + +For Ring integrations, a FewToken is supported only when `FewFactory.getWrappedToken(underlying)` returns that wrapper address. Token `name`, `symbol`, a `token()` method, or the existence of a pool do not define whether an address is a supported FewToken. +::: + +### Integration rule + +For each chain, read the official `FewFactory` address from the [Deployments](../deployments) page. + +When your code starts from an original ERC-20, resolve the FewToken yourself: + +```solidity +address wrapper = IFewFactory(fewFactory).getWrappedToken(underlying); +require(wrapper != address(0), "no FewToken for token"); +``` + +When your code receives a wrapper from an external source, validate it before using it in routing, wrapping, or approval logic: + +```solidity +function _assertCanonicalFewToken(address underlying, address wrapper) internal view { + address expected = IFewFactory(fewFactory).getWrappedToken(underlying); + require(expected != address(0), "no FewToken for token"); + require(expected == wrapper, "non-canonical FewToken"); +} +``` + +Supported FewToken addresses are resolved through `FewFactory`. The following fields are display or discovery data only: + +- `symbol()` / `name()` +- a `token()` method +- a `fw` or `Few Wrapped` prefix +- whether a RingSwap-style pool exists +- whether a quote or route looks profitable + +### Approval spender selection + +ERC-20 approvals grant transfer permission to the spender. Select the spender from the integration path you are using, not from arbitrary route or pool metadata. + +Supported patterns: + +- If you use the official Ring Router or Universal Router, approve only the official router or Permit2 address for the chain. +- If you manually wrap, approve only the canonical FewToken returned by `FewFactory.getWrappedToken(underlying)`. +- Prefer exact-amount approvals and clear temporary allowances after use when your flow supports it. +- Use a wrapper address from a pool, quote, route, token list, or subgraph only after the `FewFactory` check above. + +If you use the official Ring SDK helpers and official deployment addresses, your integration should derive the expected FewToken addresses from Ring configuration. External FewToken-looking addresses should be treated as unsupported unless they pass the `FewFactory` check. + ## Important note Ring Swap (v2) is a native Ring product line. The `v2` label describes the compatible AMM design; it is not diff --git a/docs/contracts/v4/deployments.mdx b/docs/contracts/v4/deployments.mdx index a754616..212169e 100644 --- a/docs/contracts/v4/deployments.mdx +++ b/docs/contracts/v4/deployments.mdx @@ -8,6 +8,10 @@ This page lists contract addresses used by the current `FewToken + Uniswap v4` i The current deployment surface documented here combines `Uniswap/v4-core`, `RingProtocol/v4-periphery`, and `@uniswap/universal-router`. Integrators should **not** assume that these contracts are deployed to the same addresses across chains and should confirm the mappings below before integrating. +The `Universal Router` rows on this page are the Uniswap v4 infrastructure routers for the v4 integration stack. For +Ring Swap (v2) swaps through the current Ring Interface, use the Ring Universal Router addresses on +[Ring Swap Deployments](/contracts/v2/deployments). + ## Few Hook Contracts Few Protocol hook contracts are documented separately because they are application-specific integrations built on top of this v4 integration stack. diff --git a/docs/sdk/v2/guides/01-quick-start.md b/docs/sdk/v2/guides/01-quick-start.md index b1cdd46..e8dae65 100644 --- a/docs/sdk/v2/guides/01-quick-start.md +++ b/docs/sdk/v2/guides/01-quick-start.md @@ -36,6 +36,10 @@ console.log(`The chainId of mainnet is ${ChainId.MAINNET}.`) console.log(`The few token address is ${fewDAI.address}.`) ``` +:::warning Use FewFactory-derived addresses +`fw` symbols and `Few Wrapped` names are display metadata. For routing and approvals, derive the FewToken from the original ERC-20 with the SDK, or validate that `FewFactory.getWrappedToken(underlying)` returns the candidate FewToken address. +::: + ## CommonJS (require) ```typescript diff --git a/docs/sdk/v2/overview.md b/docs/sdk/v2/overview.md index baaaa3b..9de04f1 100644 --- a/docs/sdk/v2/overview.md +++ b/docs/sdk/v2/overview.md @@ -26,3 +26,12 @@ as a separate Ring product generation. recognizing `FewToken` addresses. Use `@ring-protocol/uniswap-v2-sdk` only when you explicitly want the lower-level v2-compatible primitives rather than the higher-level Ring integration layer. + +## FewToken Address Resolution + +For Ring Swap integrations, derive FewToken addresses from the original ERC-20 with the official SDK or resolve them from the official `FewFactory`. A FewToken address is supported when `FewFactory.getWrappedToken(underlying)` returns that address; `symbol` and `name` are display metadata. + +Approval spender selection depends on the integration path: + +- Official router flow: approve only the official Ring Router, Universal Router, or Permit2 address for the chain. +- Manual wrap flow: approve only the canonical FewToken returned by `FewFactory`. diff --git a/docs/sdk/v2/reference/05-other-exports.md b/docs/sdk/v2/reference/05-other-exports.md index c5dadc2..0586361 100644 --- a/docs/sdk/v2/reference/05-other-exports.md +++ b/docs/sdk/v2/reference/05-other-exports.md @@ -71,4 +71,6 @@ Returns a `Token` instance representing the wrapped `FewToken` for an original t import { isFewToken } from '@ring-protocol/v2-sdk' ``` -Returns `true` when the provided token instance already represents a `FewToken`. +Returns `true` when the provided token instance appears to represent a `FewToken` by SDK metadata conventions. + +Use this helper only for SDK compatibility or display logic. It does not prove that a token was returned by the official `FewFactory`. For production routing and approvals, derive FewToken addresses from the original ERC-20 or validate an external wrapper with `FewFactory.getWrappedToken(underlying)`.