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/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)`.