@coinlist-co/react 0.11.1-rc.8cfcd2a → 0.11.1-rc.e102bdb

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -240,6 +240,12 @@ var InvariantError = class extends Error {
240
240
  this.name = "InvariantError";
241
241
  }
242
242
  };
243
+ var MathError = class extends Error {
244
+ constructor(message) {
245
+ super(message);
246
+ this.name = "MathError";
247
+ }
248
+ };
243
249
 
244
250
  // src/shared/core/observability/log-cause.ts
245
251
  function classifyLogCause(error) {
@@ -252,6 +258,9 @@ function classifyLogCause(error) {
252
258
  if (error instanceof InvariantError) {
253
259
  return { type: "invariant", message: error.message };
254
260
  }
261
+ if (error instanceof MathError) {
262
+ return { type: "math", message: error.message };
263
+ }
255
264
  if (error instanceof NotAuthenticatedError) {
256
265
  return { type: "not-authenticated" };
257
266
  }
@@ -902,7 +911,9 @@ async function fetchAllPages(fetchPage, baseParams) {
902
911
  // src/shared/types/blockchain/core.ts
903
912
  var ETHEREUM_CHAINS = {
904
913
  ethereum_mainnet: true,
905
- ethereum_sepolia: true
914
+ ethereum_sepolia: true,
915
+ base_mainnet: true,
916
+ base_sepolia: true
906
917
  };
907
918
  var EthereumChain = (value) => {
908
919
  if (!Object.keys(ETHEREUM_CHAINS).includes(value)) {
@@ -951,9 +962,25 @@ var BlockchainAmount = Object.assign(
951
962
  (value) => value,
952
963
  {
953
964
  add: (a, b) => combineAmounts(a, b, (x, y) => x + y),
954
- sub: (a, b) => combineAmounts(a, b, (x, y) => x - y)
965
+ sub: (a, b) => combineAmounts(a, b, (x, y) => x - y),
966
+ mul: multiplyAmounts,
967
+ div: divideAmounts
955
968
  }
956
969
  );
970
+ function multiplyAmounts(a, b) {
971
+ const product = a.raw * b.raw;
972
+ return BlockchainAmount({
973
+ raw: product / 10n ** BigInt(b.decimals),
974
+ decimals: a.decimals
975
+ });
976
+ }
977
+ function divideAmounts(a, b) {
978
+ if (b.raw === 0n) {
979
+ throw new MathError("Cannot divide a BlockchainAmount by zero");
980
+ }
981
+ const scaled = a.raw * 10n ** BigInt(b.decimals);
982
+ return BlockchainAmount({ raw: scaled / b.raw, decimals: a.decimals });
983
+ }
957
984
  function combineAmounts(a, b, op) {
958
985
  if (a.decimals !== b.decimals) {
959
986
  throw new InvariantError(
@@ -1694,7 +1721,9 @@ var USD_FRACTION_DIGITS = AssetDecimals(2);
1694
1721
  // src/shared/core/blockchain/chain.ts
1695
1722
  var CHAIN_IDS = {
1696
1723
  ethereum_mainnet: 1,
1697
- ethereum_sepolia: 11155111
1724
+ ethereum_sepolia: 11155111,
1725
+ base_mainnet: 8453,
1726
+ base_sepolia: 84532
1698
1727
  };
1699
1728
  function chainFromId(chainId) {
1700
1729
  const chains = Object.keys(CHAIN_IDS);
@@ -1770,47 +1799,122 @@ var OndoQuote = {
1770
1799
  };
1771
1800
  }
1772
1801
  };
1773
- var OndoSwapTransaction = {
1802
+ var OndoBuyTransaction = {
1774
1803
  fromDto: (dto) => {
1775
- const inputDecimals = AssetDecimals(dto.pay_input_decimals);
1804
+ const spendDecimals = AssetDecimals(dto.spend_input_decimals);
1776
1805
  const outputDecimals = AssetDecimals(dto.receive_output_decimals);
1777
1806
  return {
1778
- tx: {
1779
- to: EvmContractAddress(dto.to),
1780
- data: HexEncodedTransactionData(dto.data)
1781
- },
1782
- expiresAt: parseExpiresAt(dto.expires_at),
1783
- payInputAmount: blockchainAmountFromRawOrThrow({
1784
- label: "pay_input_amount",
1785
- raw: dto.pay_input_amount,
1786
- decimals: inputDecimals
1787
- }),
1807
+ ...parseSwapCore(dto, spendDecimals),
1808
+ side: "buy",
1788
1809
  fee: blockchainAmountFromRawOrThrow({
1789
1810
  label: "fee",
1790
1811
  raw: dto.fee,
1791
- decimals: inputDecimals
1812
+ decimals: spendDecimals
1792
1813
  }),
1793
1814
  notionalValue: blockchainAmountFromRawOrThrow({
1794
1815
  label: "notional_value",
1795
1816
  raw: dto.notional_value,
1796
- decimals: inputDecimals
1817
+ decimals: spendDecimals
1797
1818
  }),
1798
- receiveOutputAmount: parseReceiveOutputAmount(
1799
- dto.receive_output_amount,
1800
- outputDecimals
1801
- )
1819
+ receiveOutputAmount: parsePositiveAmount({
1820
+ label: "receive_output_amount",
1821
+ raw: dto.receive_output_amount,
1822
+ decimals: outputDecimals,
1823
+ // A transaction that yields nothing is not one to sign - the user
1824
+ // would pay the deposit and receive no asset - and frontline refuses
1825
+ // to emit one. A zero here is a changed encoding, not a small order.
1826
+ // Rejecting it at the boundary is also what lets `computeOndoBuyPrice`
1827
+ // divide by it without a fallible result: the failure surfaces as the
1828
+ // data hook's ERROR state rather than as a division during render.
1829
+ reason: "a buy that yields nothing is not fillable"
1830
+ })
1802
1831
  };
1803
1832
  }
1804
1833
  };
1805
- function parseReceiveOutputAmount(raw, decimals) {
1806
- const amount = blockchainAmountFromRawOrThrow({
1807
- label: "receive_output_amount",
1834
+ var OndoSellTransaction = {
1835
+ fromDto: (dto) => {
1836
+ const spendDecimals = AssetDecimals(dto.spend_input_decimals);
1837
+ const outputDecimals = AssetDecimals(dto.receive_output_decimals);
1838
+ const expectedQuantity = parsePositiveAmount({
1839
+ label: "expected_quantity",
1840
+ raw: dto.expected_quantity,
1841
+ decimals: outputDecimals,
1842
+ reason: "a sale that yields nothing is not fillable"
1843
+ });
1844
+ return {
1845
+ ...parseSwapCore(dto, spendDecimals),
1846
+ side: "sell",
1847
+ expected: {
1848
+ quantity: expectedQuantity,
1849
+ fee: blockchainAmountFromRawOrThrow({
1850
+ label: "expected_fee",
1851
+ raw: dto.expected_fee,
1852
+ decimals: outputDecimals
1853
+ })
1854
+ },
1855
+ minimum: {
1856
+ quantity: parseMinimumQuantity(
1857
+ dto.minimum_quantity,
1858
+ outputDecimals,
1859
+ expectedQuantity
1860
+ ),
1861
+ fee: blockchainAmountFromRawOrThrow({
1862
+ label: "minimum_fee",
1863
+ raw: dto.minimum_fee,
1864
+ decimals: outputDecimals
1865
+ })
1866
+ }
1867
+ };
1868
+ }
1869
+ };
1870
+ function parseSwapCore(dto, spendDecimals) {
1871
+ return {
1872
+ tx: {
1873
+ to: EvmContractAddress(dto.to),
1874
+ data: HexEncodedTransactionData(dto.data)
1875
+ },
1876
+ expiresAt: parseExpiresAt(dto.expires_at),
1877
+ spendInputAmount: parsePositiveAmount({
1878
+ label: "spend_input_amount",
1879
+ raw: dto.spend_input_amount,
1880
+ decimals: spendDecimals,
1881
+ // A transaction that takes nothing from the wallet is not one to sign:
1882
+ // it would settle one leg of a trade and skip the other. Frontline
1883
+ // refuses an `amount` of zero whichever way the trade runs, so this is a
1884
+ // changed encoding rather than a small order.
1885
+ //
1886
+ // Guarded on both sides rather than on the sale alone, because which
1887
+ // amount becomes the divisor in the price flips with the direction: a
1888
+ // guard placed by that would be a rule about the arithmetic rather than
1889
+ // about the trade.
1890
+ reason: "a swap that spends nothing is not fillable"
1891
+ })
1892
+ };
1893
+ }
1894
+ function parseMinimumQuantity(raw, decimals, expectedQuantity) {
1895
+ const amount = parsePositiveAmount({
1896
+ label: "minimum_quantity",
1808
1897
  raw,
1809
- decimals
1898
+ decimals,
1899
+ reason: "a floor of zero guarantees nothing"
1810
1900
  });
1901
+ if (amount.raw > expectedQuantity.raw) {
1902
+ throw new ValidationError(
1903
+ `minimum_quantity: must not exceed expected_quantity ("${raw}" > "${expectedQuantity.raw}")`
1904
+ );
1905
+ }
1906
+ return amount;
1907
+ }
1908
+ function parsePositiveAmount({
1909
+ label,
1910
+ raw,
1911
+ decimals,
1912
+ reason
1913
+ }) {
1914
+ const amount = blockchainAmountFromRawOrThrow({ label, raw, decimals });
1811
1915
  if (amount.raw <= 0n) {
1812
1916
  throw new ValidationError(
1813
- `receive_output_amount: must be greater than zero ("${raw}")`
1917
+ `${label}: must be greater than zero ("${raw}") - ${reason}`
1814
1918
  );
1815
1919
  }
1816
1920
  return amount;
@@ -1847,27 +1951,57 @@ async function getOndoQuote(api, params) {
1847
1951
  });
1848
1952
  return OndoQuote.fromDto(dto);
1849
1953
  }
1850
- async function buildOndoSwapTransaction(api, params) {
1954
+ async function buildOndoBuy(api, params) {
1851
1955
  const dto = await api.send({
1852
1956
  method: "POST",
1853
- url: "/v1/ondo/swap/transaction",
1854
- body: {
1855
- symbol: params.symbol,
1856
- chain: params.chain,
1857
- wallet_address: params.walletAddress,
1858
- amount: params.amount.raw.toString()
1859
- },
1957
+ url: "/v1/ondo/swap/buy",
1958
+ body: swapBody(params),
1860
1959
  attributes: Attributes.protected()
1861
1960
  });
1862
- assertFundingScaleAgrees(dto, params);
1863
- return OndoSwapTransaction.fromDto(dto);
1961
+ assertSpendScaleAgrees({
1962
+ published: dto.spend_input_decimals,
1963
+ sized: params.amount,
1964
+ trade: "purchase"
1965
+ });
1966
+ return OndoBuyTransaction.fromDto(dto);
1864
1967
  }
1865
- function assertFundingScaleAgrees(dto, params) {
1866
- if (dto.pay_input_decimals !== params.amount.decimals) {
1867
- throw new ValidationError(
1868
- `pay_input_decimals: the swap was priced in ${dto.pay_input_decimals} decimals but the order was sized in ${params.amount.decimals}`
1869
- );
1870
- }
1968
+ async function buildOndoSell(api, params) {
1969
+ const dto = await api.send({
1970
+ method: "POST",
1971
+ url: "/v1/ondo/swap/sell",
1972
+ body: swapBody(params),
1973
+ attributes: Attributes.protected()
1974
+ });
1975
+ assertSpendScaleAgrees({
1976
+ published: dto.spend_input_decimals,
1977
+ sized: params.amount,
1978
+ trade: "sale",
1979
+ // The two answers come from two chains, so on a testnet they can disagree
1980
+ // for a reason that is neither the caller's nor a corrupt response. Say so,
1981
+ // or a QA run reads as a puzzle rather than a diagnosis.
1982
+ note: "the quote resolves the asset on Ethereum mainnet while the swap executes on the chain requested, so these disagree until frontline serves a chain-scoped quote"
1983
+ });
1984
+ return OndoSellTransaction.fromDto(dto);
1985
+ }
1986
+ function swapBody(params) {
1987
+ return {
1988
+ symbol: params.symbol,
1989
+ chain: params.chain,
1990
+ wallet_address: params.walletAddress,
1991
+ amount: params.amount.raw.toString()
1992
+ };
1993
+ }
1994
+ function assertSpendScaleAgrees({
1995
+ published,
1996
+ sized,
1997
+ trade,
1998
+ note
1999
+ }) {
2000
+ if (published === sized.decimals) return;
2001
+ const because = note === void 0 ? "" : ` - ${note}`;
2002
+ throw new ValidationError(
2003
+ `spend_input_decimals: the ${trade} was priced in ${published} decimals but the order was sized in ${sized.decimals}${because}`
2004
+ );
1871
2005
  }
1872
2006
  function sizeParam(params) {
1873
2007
  const tokenAmount = "tokenAmount" in params ? params.tokenAmount : void 0;
@@ -1906,10 +2040,16 @@ var OndoNamespaceImpl = class {
1906
2040
  return getOndoQuote(this.ctx.api, params);
1907
2041
  });
1908
2042
  }
1909
- async buildSwapTransaction(params) {
1910
- return this.log.wrap("buildSwapTransaction", params, async () => {
2043
+ async buildBuyTransaction(params) {
2044
+ return this.log.wrap("buildBuyTransaction", params, async () => {
2045
+ await this.ctx.ensureUserAuthenticated();
2046
+ return buildOndoBuy(this.ctx.api, params);
2047
+ });
2048
+ }
2049
+ async buildSellTransaction(params) {
2050
+ return this.log.wrap("buildSellTransaction", params, async () => {
1911
2051
  await this.ctx.ensureUserAuthenticated();
1912
- return buildOndoSwapTransaction(this.ctx.api, params);
2052
+ return buildOndoSell(this.ctx.api, params);
1913
2053
  });
1914
2054
  }
1915
2055
  };
@@ -1981,27 +2121,47 @@ var TokenMetadata = {
1981
2121
  logoDark: dto.logo_dark === void 0 ? null : TokenLogo.fromDto(dto.logo_dark, baseUrl)
1982
2122
  };
1983
2123
  },
2124
+ /**
2125
+ * Maps the complete registry snapshot to every token it lists across the
2126
+ * chains this SDK models, skipping native coins and chains outside
2127
+ * {@link EthereumChain} (e.g. Solana) rather than failing on them — the
2128
+ * registry may serve chains ahead of the SDK's type surface.
2129
+ */
2130
+ fromRegistryDto: (dto, baseUrl) => {
2131
+ assertSupportedSchemaVersion(dto.schema_version);
2132
+ return dto.chains.flatMap((chainDto) => {
2133
+ let chain;
2134
+ try {
2135
+ chain = EthereumChain(chainDto.chain);
2136
+ } catch {
2137
+ return [];
2138
+ }
2139
+ return tokensOfChain(chain, chainDto.assets, baseUrl);
2140
+ });
2141
+ },
1984
2142
  /**
1985
2143
  * Maps a chain snapshot to the tokens it lists, skipping the chain's native
1986
2144
  * coin (`kind: 'COIN'`, no contract address).
1987
2145
  */
1988
2146
  fromChainAssetsDto: (dto, baseUrl) => {
1989
2147
  assertSupportedSchemaVersion(dto.schema_version);
1990
- const chain = EthereumChain(dto.chain);
1991
- return dto.assets.filter((asset) => asset.kind === "TOKEN" && asset.address !== void 0).map((asset) => ({
1992
- identifier: {
1993
- chain,
1994
- // The filter above cannot narrow `address` for the type checker.
1995
- address: EvmContractAddress(asset.address)
1996
- },
1997
- name: asset.name,
1998
- symbol: AssetSymbol(asset.symbol),
1999
- decimals: AssetDecimals(asset.decimals),
2000
- logo: TokenLogo.fromDto(asset.logo, baseUrl),
2001
- logoDark: asset.logo_dark === void 0 ? null : TokenLogo.fromDto(asset.logo_dark, baseUrl)
2002
- }));
2148
+ return tokensOfChain(EthereumChain(dto.chain), dto.assets, baseUrl);
2003
2149
  }
2004
2150
  };
2151
+ function tokensOfChain(chain, assets, baseUrl) {
2152
+ return assets.filter((asset) => asset.kind === "TOKEN" && asset.address !== void 0).map((asset) => ({
2153
+ identifier: {
2154
+ chain,
2155
+ // The filter above cannot narrow `address` for the type checker.
2156
+ address: EvmContractAddress(asset.address)
2157
+ },
2158
+ name: asset.name,
2159
+ symbol: AssetSymbol(asset.symbol),
2160
+ decimals: AssetDecimals(asset.decimals),
2161
+ logo: TokenLogo.fromDto(asset.logo, baseUrl),
2162
+ logoDark: asset.logo_dark === void 0 ? null : TokenLogo.fromDto(asset.logo_dark, baseUrl)
2163
+ }));
2164
+ }
2005
2165
  function assertSupportedSchemaVersion(version) {
2006
2166
  if (version !== 1) {
2007
2167
  throw new ValidationError(
@@ -2070,6 +2230,29 @@ async function fetchTokensMetadata(client, chain) {
2070
2230
  }
2071
2231
  return TokenMetadata.fromChainAssetsDto(response.body, client.config.baseUrl);
2072
2232
  }
2233
+ async function fetchAllTokensMetadata(client) {
2234
+ let response;
2235
+ try {
2236
+ response = await client.send({
2237
+ method: "GET",
2238
+ url: `/assets.json`
2239
+ });
2240
+ } catch (error) {
2241
+ if (isRegistryHtmlFallback(error)) {
2242
+ throw new ValidationError(
2243
+ "Token registry returned non-JSON for the complete snapshot"
2244
+ );
2245
+ }
2246
+ throw error;
2247
+ }
2248
+ assertOk(response);
2249
+ if (response.body === null) {
2250
+ throw new ValidationError(
2251
+ "Token registry returned an empty complete snapshot"
2252
+ );
2253
+ }
2254
+ return TokenMetadata.fromRegistryDto(response.body, client.config.baseUrl);
2255
+ }
2073
2256
  function isRegistryHtmlFallback(error) {
2074
2257
  return error instanceof HttpError && error.response.status >= 200 && error.response.status < 300;
2075
2258
  }
@@ -2108,7 +2291,7 @@ var TokensNamespaceImpl = class {
2108
2291
  return this.log.wrap(
2109
2292
  "list",
2110
2293
  chain,
2111
- () => fetchTokensMetadata(this.api, chain)
2294
+ () => chain === void 0 ? fetchAllTokensMetadata(this.api) : fetchTokensMetadata(this.api, chain)
2112
2295
  );
2113
2296
  }
2114
2297
  };