@stablechain/sdk 1.0.8 → 1.0.9

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.
package/README.md CHANGED
@@ -129,7 +129,13 @@ const { txHash } = await stable.transfer({
129
129
  });
130
130
  ```
131
131
 
132
- **ERC-20 (USDC, EURC, …)**
132
+ **ERC-20 (PYUSD, USDC, EURC, …)**
133
+
134
+ ```ts
135
+ import { STABLE_PYUSD_ADDRESS } from "@stablechain/sdk";
136
+
137
+ await stable.transfer({ to: "0xRecipient", amount: 25, token: STABLE_PYUSD_ADDRESS, tokenDecimals: 6 });
138
+ ```
133
139
 
134
140
  ```ts
135
141
  const { txHash } = await stable.transfer({
@@ -254,8 +260,9 @@ automatically. Switches the wallet to the source chain (MetaMask will prompt) be
254
260
  sending.
255
261
 
256
262
  Only a lockbox pathway needs an allowance at all — Ethereum for USDT0 and XAUt0, and
257
- Sepolia on testnet. Every other deployment is an OFT that burns from the caller, so no
258
- approve and no allowance read is issued there. Where one is needed and the current
263
+ Sepolia on testnet. Every other deployment burns from the caller, so no approve and no
264
+ allowance read is issued there. That includes PYUSD on Ethereum, which mints and burns
265
+ through the token instead of escrowing it. Where one is needed and the current
259
266
  allowance is short but not zero, it takes **two** approvals: legacy USDT refuses to
260
267
  overwrite a non-zero allowance with another non-zero value, so the SDK sends
261
268
  `approve(0)` first.
@@ -337,7 +344,7 @@ one of them. Alongside them:
337
344
  | Field | Type | What it is |
338
345
  |-----------|-----------------|---------------------------------------------------------------------|
339
346
  | `chainId` | `number` | The source chain every step must be sent on |
340
- | `asset` | `"usdt0" \| "xaut0"` | Which OFT mesh this send moves through |
347
+ | `asset` | `"usdt0" \| "xaut0" \| "pyusd"` | Which OFT mesh this send moves through |
341
348
  | `quote` | `BridgeQuote` | The quote the steps were built from — supplied or freshly read |
342
349
  | `send` | `object` | The `send` step's arguments decoded: `dstEid`, `recipient`, `to`, `amountLD`, `minAmountLD`, `extraOptions`, `nativeFee`, `lzTokenFee`, `refundAddress` |
343
350
 
@@ -381,7 +388,7 @@ addresses name, LI.FI otherwise. Forcing `"oft"` on an unsupported pair throws
381
388
 
382
389
  #### Native OFT assets
383
390
 
384
- Two assets have their own OFT mesh, each with its own adapter and token address per
391
+ Each OFT asset has its own mesh, with its own adapter and token address per
385
392
  chain. A bridge stays within one mesh: the OFT mints its own peer on the far side, so it
386
393
  cannot hand back a different token. A cross-asset request (USDT0 in, XAUt0 out) is a
387
394
  trade, not a bridge, and `"auto"` sends it to LI.FI.
@@ -409,6 +416,27 @@ chain burns from the caller and skips the approval transaction entirely.
409
416
 
410
417
  BSC is XAUt0-only: it has no USDT0 deployment, so a BSC USDT bridge routes to LI.FI.
411
418
 
419
+ **PYUSD** (PayPal USD, 6 decimals) — Stable and Ethereum. The OFT is live between those
420
+ two chains only, so `"auto"` takes it for Ethereum ↔ Stable and sends every other PYUSD
421
+ pair to LI.FI.
422
+
423
+ ```ts
424
+ // PYUSD: Ethereum → Stable, over the native OFT mesh
425
+ await stable.bridge({
426
+ fromChain: Chain.Ethereum,
427
+ toChain: Chain.Stable,
428
+ fromToken: "0x6c3ea9036406852006290770BEdFcAbA0e23A0e8", // PYUSD on Ethereum
429
+ toToken: STABLE_PYUSD_ADDRESS,
430
+ amount: 100, // 100 PYUSD
431
+ });
432
+ ```
433
+
434
+ Neither side is a lockbox: both adapters mint and burn through PYUSD itself, so no
435
+ approval is sent, even on Ethereum. Each direction is rate-limited by the adapter
436
+ (100,000,000 PYUSD per 24 hours as of 2026-10-27), and a send past the limit reverts.
437
+ From other chains, LI.FI had no PYUSD liquidity on Stable when last checked (2026-10-22),
438
+ so those bridges fail with "no available quotes".
439
+
412
440
  Addresses per chain are in `BRIDGE_CHAIN_CONFIGS[chain].tokens`, keyed by asset:
413
441
 
414
442
  ```ts
@@ -419,7 +447,7 @@ stableChain.tokens.xaut0?.address; // XAUt0 ERC-20 on Stable
419
447
  stableChain.tokens.xaut0?.oftAdapter; // its OFT
420
448
 
421
449
  // Which asset an address refers to on a chain, if any
422
- bridgeAssetFor(stableChain, someToken); // "usdt0" | "xaut0" | undefined
450
+ bridgeAssetFor(stableChain, someToken); // "usdt0" | "xaut0" | "pyusd" | undefined
423
451
  ```
424
452
 
425
453
  ---
@@ -631,6 +659,21 @@ if (check.ok) await stable.authorization.transferWithAuthorization({ signed });
631
659
 
632
660
  `value` is a **raw `bigint`** in the token's smallest unit (USDT0 is 6 decimals) — this is the 6-dp ERC-20 path, not the 18-dp native one. `token` defaults to USDT0; `nonce` defaults to a cryptographically-random 32 bytes; the EIP-712 `domain` is read from the token (EIP-5267 `eip712Domain()`, falling back to `name()` + version `"1"`) unless overridden.
633
661
 
662
+ PYUSD on Stable implements ERC-3009 too. Pass `token: STABLE_PYUSD_ADDRESS` and the same
663
+ actions work with no domain override, because the `name()` + `"1"` fallback matches its
664
+ on-chain `DOMAIN_SEPARATOR`:
665
+
666
+ ```ts
667
+ import { STABLE_PYUSD_ADDRESS } from "@stablechain/sdk";
668
+
669
+ const signed = await stable.authorization.signTransferAuthorization({
670
+ token: STABLE_PYUSD_ADDRESS,
671
+ to: "0xVendor",
672
+ value: 1_250_000n, // 1.25 PYUSD (6 dp)
673
+ validBefore: BigInt(Math.floor(Date.now() / 1000) + 3600),
674
+ });
675
+ ```
676
+
634
677
  To relay gaslessly, pass `prepareTransferWithAuthorization`'s output to the [`@stablechain/enterprise`](../enterprise) gas-waiver rail.
635
678
 
636
679
  > **Contract signers:** canonical ERC-3009 uses `ecrecover`, so Safe / EIP-1271 accounts cannot sign an authorization. Use an EOA (optionally a 7702 session key) for the payer.
@@ -653,6 +696,23 @@ const stable = createStable({
653
696
  const { txHash } = await stable.earn.deposit({ amount: 100 }); // 100 USDT0
654
697
  ```
655
698
 
699
+ Earn works with any Morpho V2 vault, not just USDT0. The SDK reads the vault's own
700
+ `asset()` and that token's decimals, and approves that token, so a PYUSD vault needs only
701
+ its address. `deposit` and `bulkDeposit` accept `vault` per call. Withdraw, redeem and
702
+ the reader always use the vault set in config, so manage a PYUSD position with a client
703
+ configured for that vault:
704
+
705
+ ```ts
706
+ const pyusdEarn = createStable({
707
+ network: Network.Mainnet,
708
+ earn: { vault: PYUSD_VAULT },
709
+ walletClient: myWalletClient,
710
+ });
711
+
712
+ await pyusdEarn.earn.deposit({ amount: 100 }); // 100 PYUSD
713
+ await pyusdEarn.earn.withdraw({ amount: 40 }); // 40 PYUSD back
714
+ ```
715
+
656
716
  | Param | Type | Description |
657
717
  |---------------------|----------|-------------------------------------------------------------------|
658
718
  | `amount` | `number` | Amount in human-readable units |
@@ -122,6 +122,7 @@ import { signTypedData } from "viem/actions";
122
122
 
123
123
  // src/constants.ts
124
124
  var STABLE_USDT_ADDRESS = "0x779ded0c9e1022225f8e0630b35a9b54be713736";
125
+ var STABLE_PYUSD_ADDRESS = "0x28F1f38a9F7dB1f769A71aF0a13E8BACe2ab0E9f";
125
126
  var STABLE_VAULT_ADDRESS = "0xb7Df8db22A5DBBFA9ebeb94b3910aec6a4f05c08";
126
127
 
127
128
  // src/errors.ts
@@ -649,9 +650,10 @@ export {
649
650
  StableTransactionError,
650
651
  StableNetworkError,
651
652
  StableBulkError,
652
- ERC3009_ABI,
653
653
  STABLE_USDT_ADDRESS,
654
+ STABLE_PYUSD_ADDRESS,
654
655
  STABLE_VAULT_ADDRESS,
656
+ ERC3009_ABI,
655
657
  signTransferAuthorization,
656
658
  signReceiveAuthorization,
657
659
  transferWithAuthorization,
@@ -122,6 +122,7 @@ var _actions = require('viem/actions');
122
122
 
123
123
  // src/constants.ts
124
124
  var STABLE_USDT_ADDRESS = "0x779ded0c9e1022225f8e0630b35a9b54be713736";
125
+ var STABLE_PYUSD_ADDRESS = "0x28F1f38a9F7dB1f769A71aF0a13E8BACe2ab0E9f";
125
126
  var STABLE_VAULT_ADDRESS = "0xb7Df8db22A5DBBFA9ebeb94b3910aec6a4f05c08";
126
127
 
127
128
  // src/errors.ts
@@ -662,4 +663,5 @@ function erc3009Actions(client) {
662
663
 
663
664
 
664
665
 
665
- exports.StableError = StableError; exports.StableValidationError = StableValidationError; exports.StableQuoteError = StableQuoteError; exports.StableTransactionError = StableTransactionError; exports.StableNetworkError = StableNetworkError; exports.StableBulkError = StableBulkError; exports.ERC3009_ABI = ERC3009_ABI; exports.STABLE_USDT_ADDRESS = STABLE_USDT_ADDRESS; exports.STABLE_VAULT_ADDRESS = STABLE_VAULT_ADDRESS; exports.signTransferAuthorization = signTransferAuthorization; exports.signReceiveAuthorization = signReceiveAuthorization; exports.transferWithAuthorization = transferWithAuthorization; exports.receiveWithAuthorization = receiveWithAuthorization; exports.cancelAuthorization = cancelAuthorization; exports.prepareTransferWithAuthorization = prepareTransferWithAuthorization; exports.verifyAuthorization = verifyAuthorization; exports.getAuthorizationState = getAuthorizationState; exports.getAuthorizationUsage = getAuthorizationUsage; exports.erc3009Actions = erc3009Actions;
666
+
667
+ exports.StableError = StableError; exports.StableValidationError = StableValidationError; exports.StableQuoteError = StableQuoteError; exports.StableTransactionError = StableTransactionError; exports.StableNetworkError = StableNetworkError; exports.StableBulkError = StableBulkError; exports.STABLE_USDT_ADDRESS = STABLE_USDT_ADDRESS; exports.STABLE_PYUSD_ADDRESS = STABLE_PYUSD_ADDRESS; exports.STABLE_VAULT_ADDRESS = STABLE_VAULT_ADDRESS; exports.ERC3009_ABI = ERC3009_ABI; exports.signTransferAuthorization = signTransferAuthorization; exports.signReceiveAuthorization = signReceiveAuthorization; exports.transferWithAuthorization = transferWithAuthorization; exports.receiveWithAuthorization = receiveWithAuthorization; exports.cancelAuthorization = cancelAuthorization; exports.prepareTransferWithAuthorization = prepareTransferWithAuthorization; exports.verifyAuthorization = verifyAuthorization; exports.getAuthorizationState = getAuthorizationState; exports.getAuthorizationUsage = getAuthorizationUsage; exports.erc3009Actions = erc3009Actions;
package/dist/erc3009.cjs CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
 
12
12
 
13
- var _chunkQQ26UI3Fcjs = require('./chunk-QQ26UI3F.cjs');
13
+ var _chunkMMZ7HVWZcjs = require('./chunk-MMZ7HVWZ.cjs');
14
14
 
15
15
 
16
16
 
@@ -23,4 +23,4 @@ var _chunkQQ26UI3Fcjs = require('./chunk-QQ26UI3F.cjs');
23
23
 
24
24
 
25
25
 
26
- exports.ERC3009_ABI = _chunkQQ26UI3Fcjs.ERC3009_ABI; exports.cancelAuthorization = _chunkQQ26UI3Fcjs.cancelAuthorization; exports.erc3009Actions = _chunkQQ26UI3Fcjs.erc3009Actions; exports.getAuthorizationState = _chunkQQ26UI3Fcjs.getAuthorizationState; exports.getAuthorizationUsage = _chunkQQ26UI3Fcjs.getAuthorizationUsage; exports.prepareTransferWithAuthorization = _chunkQQ26UI3Fcjs.prepareTransferWithAuthorization; exports.receiveWithAuthorization = _chunkQQ26UI3Fcjs.receiveWithAuthorization; exports.signReceiveAuthorization = _chunkQQ26UI3Fcjs.signReceiveAuthorization; exports.signTransferAuthorization = _chunkQQ26UI3Fcjs.signTransferAuthorization; exports.transferWithAuthorization = _chunkQQ26UI3Fcjs.transferWithAuthorization; exports.verifyAuthorization = _chunkQQ26UI3Fcjs.verifyAuthorization;
26
+ exports.ERC3009_ABI = _chunkMMZ7HVWZcjs.ERC3009_ABI; exports.cancelAuthorization = _chunkMMZ7HVWZcjs.cancelAuthorization; exports.erc3009Actions = _chunkMMZ7HVWZcjs.erc3009Actions; exports.getAuthorizationState = _chunkMMZ7HVWZcjs.getAuthorizationState; exports.getAuthorizationUsage = _chunkMMZ7HVWZcjs.getAuthorizationUsage; exports.prepareTransferWithAuthorization = _chunkMMZ7HVWZcjs.prepareTransferWithAuthorization; exports.receiveWithAuthorization = _chunkMMZ7HVWZcjs.receiveWithAuthorization; exports.signReceiveAuthorization = _chunkMMZ7HVWZcjs.signReceiveAuthorization; exports.signTransferAuthorization = _chunkMMZ7HVWZcjs.signTransferAuthorization; exports.transferWithAuthorization = _chunkMMZ7HVWZcjs.transferWithAuthorization; exports.verifyAuthorization = _chunkMMZ7HVWZcjs.verifyAuthorization;
package/dist/erc3009.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  signTransferAuthorization,
11
11
  transferWithAuthorization,
12
12
  verifyAuthorization
13
- } from "./chunk-VS6GBRDF.js";
13
+ } from "./chunk-FN7OKBRH.js";
14
14
  export {
15
15
  ERC3009_ABI,
16
16
  cancelAuthorization,