├── .github ├── dependabot.yml └── workflows │ ├── build.yml │ ├── lint.yml │ ├── publish.yml │ ├── push.yml │ └── test.yml ├── .gitignore ├── .husky ├── pre-commit └── prepare-commit-msg ├── .npmrc ├── .nvmrc ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── biome.json ├── package.json ├── packages ├── blue-api-sdk │ ├── README.md │ ├── codegen.ts │ ├── package.json │ ├── src │ │ ├── cache.ts │ │ ├── converter.ts │ │ ├── errors.ts │ │ ├── index.ts │ │ ├── tags.ts │ │ ├── types.ts │ │ └── warnings.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── blue-sdk-ethers │ ├── README.md │ ├── package.json │ ├── src │ │ ├── augment │ │ │ ├── Holding.ts │ │ │ ├── Market.ts │ │ │ ├── MarketParams.ts │ │ │ ├── Position.ts │ │ │ ├── Token.ts │ │ │ ├── User.ts │ │ │ ├── Vault.ts │ │ │ ├── VaultConfig.ts │ │ │ ├── VaultMarketAllocation.ts │ │ │ ├── VaultMarketConfig.ts │ │ │ ├── VaultMarketPublicAllocatorConfig.ts │ │ │ ├── VaultUser.ts │ │ │ ├── ethers.ts │ │ │ └── index.ts │ │ ├── errors.ts │ │ ├── ethers │ │ │ ├── index.ts │ │ │ └── utils.ts │ │ ├── evm.ts │ │ ├── fetch │ │ │ ├── Holding.ts │ │ │ ├── Market.ts │ │ │ ├── MarketParams.ts │ │ │ ├── Position.ts │ │ │ ├── Token.ts │ │ │ ├── User.ts │ │ │ ├── Vault.ts │ │ │ ├── VaultConfig.ts │ │ │ ├── VaultMarketAllocation.ts │ │ │ ├── VaultMarketConfig.ts │ │ │ ├── VaultMarketPublicAllocatorConfig.ts │ │ │ ├── VaultUser.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── notifications.ts │ │ ├── signatures │ │ │ ├── index.ts │ │ │ ├── manager.ts │ │ │ ├── permit.ts │ │ │ ├── permit2.ts │ │ │ ├── types.ts │ │ │ └── utils.ts │ │ └── types.ts │ ├── test │ │ ├── e2e │ │ │ ├── Holding.test.ts │ │ │ ├── Market.test.ts │ │ │ ├── MarketParams.test.ts │ │ │ ├── Position.test.ts │ │ │ ├── Token.test.ts │ │ │ ├── User.test.ts │ │ │ ├── Vault.test.ts │ │ │ ├── VaultMarketConfig.test.ts │ │ │ ├── abis.ts │ │ │ └── setup.ts │ │ └── unit │ │ │ └── utils.test.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── blue-sdk-viem │ ├── README.md │ ├── contracts │ │ ├── GetHolding.sol │ │ ├── GetMarket.sol │ │ ├── GetToken.sol │ │ ├── GetVault.sol │ │ ├── GetVaultUser.sol │ │ └── interfaces │ │ │ ├── IAdaptiveCurveIrm.sol │ │ │ ├── IERC20.sol │ │ │ ├── IERC20Permissioned.sol │ │ │ ├── IERC20Permit.sol │ │ │ ├── IERC4626.sol │ │ │ ├── IIrm.sol │ │ │ ├── IMetaMorpho.sol │ │ │ ├── IMorpho.sol │ │ │ ├── IOracle.sol │ │ │ ├── IPermit2.sol │ │ │ ├── IPublicAllocator.sol │ │ │ ├── IWhitelistControllerAggregator.sol │ │ │ ├── IWrappedBackedToken.sol │ │ │ └── IWstEth.sol │ ├── hardhat.config.cjs │ ├── package.json │ ├── src │ │ ├── MetaMorphoAction.ts │ │ ├── abis.ts │ │ ├── augment │ │ │ ├── Holding.ts │ │ │ ├── Market.ts │ │ │ ├── MarketParams.ts │ │ │ ├── Position.ts │ │ │ ├── Token.ts │ │ │ ├── User.ts │ │ │ ├── Vault.ts │ │ │ ├── VaultConfig.ts │ │ │ ├── VaultMarketAllocation.ts │ │ │ ├── VaultMarketConfig.ts │ │ │ ├── VaultMarketPublicAllocatorConfig.ts │ │ │ ├── VaultUser.ts │ │ │ └── index.ts │ │ ├── fetch │ │ │ ├── Holding.ts │ │ │ ├── Market.ts │ │ │ ├── MarketParams.ts │ │ │ ├── Position.ts │ │ │ ├── Token.ts │ │ │ ├── User.ts │ │ │ ├── Vault.ts │ │ │ ├── VaultConfig.ts │ │ │ ├── VaultMarketAllocation.ts │ │ │ ├── VaultMarketConfig.ts │ │ │ ├── VaultMarketPublicAllocatorConfig.ts │ │ │ ├── VaultUser.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── queries │ │ │ ├── GetHolding.ts │ │ │ ├── GetMarket.ts │ │ │ ├── GetToken.ts │ │ │ ├── GetVault.ts │ │ │ ├── GetVaultUser.ts │ │ │ └── index.ts │ │ ├── signatures │ │ │ ├── index.ts │ │ │ ├── manager.ts │ │ │ ├── permit.ts │ │ │ └── permit2.ts │ │ ├── types.ts │ │ └── utils.ts │ ├── test │ │ ├── Holding.test.ts │ │ ├── Market.test.ts │ │ ├── MarketParams.test.ts │ │ ├── Position.test.ts │ │ ├── PreLiquidationPosition.test.ts │ │ ├── Token.test.ts │ │ ├── User.test.ts │ │ ├── Vault.test.ts │ │ ├── VaultMarketConfig.test.ts │ │ └── setup.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── blue-sdk-wagmi │ ├── README.md │ ├── package.json │ ├── src │ │ ├── hooks │ │ │ ├── index.ts │ │ │ ├── useChainId.ts │ │ │ ├── useHolding.ts │ │ │ ├── useHoldings.ts │ │ │ ├── useMarket.ts │ │ │ ├── useMarketParams.ts │ │ │ ├── useMarkets.ts │ │ │ ├── useMarketsParams.ts │ │ │ ├── usePosition.ts │ │ │ ├── usePositions.ts │ │ │ ├── useReadContracts.ts │ │ │ ├── useToken.ts │ │ │ ├── useTokens.ts │ │ │ ├── useUser.ts │ │ │ ├── useUsers.ts │ │ │ ├── useVault.ts │ │ │ ├── useVaultConfig.ts │ │ │ ├── useVaultConfigs.ts │ │ │ ├── useVaultMarketConfig.ts │ │ │ ├── useVaultMarketConfigs.ts │ │ │ ├── useVaultUser.ts │ │ │ ├── useVaultUsers.ts │ │ │ └── useVaults.ts │ │ ├── index.ts │ │ ├── queries │ │ │ ├── fetchHolding.ts │ │ │ ├── fetchMarket.ts │ │ │ ├── fetchMarketParams.ts │ │ │ ├── fetchPosition.ts │ │ │ ├── fetchToken.ts │ │ │ ├── fetchUser.ts │ │ │ ├── fetchVault.ts │ │ │ ├── fetchVaultConfig.ts │ │ │ ├── fetchVaultMarketConfig.ts │ │ │ ├── fetchVaultUser.ts │ │ │ └── index.ts │ │ ├── types │ │ │ ├── index.ts │ │ │ └── properties.ts │ │ └── utils │ │ │ ├── index.ts │ │ │ ├── replaceDeepEqual.ts │ │ │ └── uniqBy.ts │ ├── test │ │ ├── e2e │ │ │ ├── setup.ts │ │ │ ├── useChainId.test.ts │ │ │ └── useMarkets.test.ts │ │ └── unit │ │ │ └── structuralSharing.test.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── blue-sdk │ ├── README.md │ ├── package.json │ ├── src │ │ ├── addresses.ts │ │ ├── chain.ts │ │ ├── constants.ts │ │ ├── errors.ts │ │ ├── holding │ │ │ ├── AssetBalances.ts │ │ │ ├── Holding.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── market │ │ │ ├── Market.ts │ │ │ ├── MarketParams.ts │ │ │ ├── MarketUtils.ts │ │ │ └── index.ts │ │ ├── math │ │ │ ├── AdaptiveCurveIrmLib.ts │ │ │ ├── MathLib.ts │ │ │ ├── SharesMath.ts │ │ │ └── index.ts │ │ ├── position │ │ │ ├── Position.ts │ │ │ ├── PreLiquidationPosition.ts │ │ │ └── index.ts │ │ ├── preLiquidation.ts │ │ ├── token │ │ │ ├── ConstantWrappedToken.ts │ │ │ ├── Eip5267Domain.ts │ │ │ ├── ExchangeRateWrappedToken.ts │ │ │ ├── Token.ts │ │ │ ├── VaultToken.ts │ │ │ ├── WrappedToken.ts │ │ │ └── index.ts │ │ ├── types.ts │ │ ├── user │ │ │ ├── User.ts │ │ │ └── index.ts │ │ └── vault │ │ │ ├── Vault.ts │ │ │ ├── VaultConfig.ts │ │ │ ├── VaultMarketAllocation.ts │ │ │ ├── VaultMarketConfig.ts │ │ │ ├── VaultMarketPublicAllocatorConfig.ts │ │ │ ├── VaultUser.ts │ │ │ ├── VaultUtils.ts │ │ │ └── index.ts │ ├── test │ │ ├── e2e │ │ │ ├── Market.test.ts │ │ │ ├── abis.ts │ │ │ └── setup.ts │ │ └── unit │ │ │ ├── MarketUtils.test.ts │ │ │ ├── PreLiquidationPosition.test.ts │ │ │ └── chain.test.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── bundler-sdk-ethers │ ├── .env.example │ ├── .gitignore │ ├── README.md │ ├── abis │ │ ├── AaveV2MigrationBundlerV2.json │ │ ├── AaveV3MigrationBundlerV2.json │ │ ├── AaveV3OptimizerMigrationBundlerV2.json │ │ ├── CompoundV2MigrationBundlerV2.json │ │ ├── CompoundV3MigrationBundlerV2.json │ │ ├── ERC20WrapperBundler.json │ │ ├── ERC4626Bundler.json │ │ ├── EthereumPermitBundler.json │ │ ├── MorphoBundler.json │ │ ├── Permit2Bundler.json │ │ ├── PermitBundler.json │ │ ├── StEthBundler.json │ │ ├── TransferBundler.json │ │ ├── UrdBundler.json │ │ └── WNativeBundler.json │ ├── package.json │ ├── src │ │ ├── BundlerAction.ts │ │ └── index.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── bundler-sdk-viem │ ├── .env.example │ ├── README.md │ ├── package.json │ ├── src │ │ ├── ActionBundle.ts │ │ ├── BundlerAction.ts │ │ ├── abis.ts │ │ ├── actions.ts │ │ ├── bundle.ts │ │ ├── errors.ts │ │ ├── index.ts │ │ ├── operations.ts │ │ └── types │ │ │ ├── actions.ts │ │ │ ├── index.ts │ │ │ └── operations.ts │ ├── test │ │ ├── helpers.ts │ │ ├── populateBundle.test.ts │ │ ├── setup.ts │ │ └── sharedLiquidity.test.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── liquidation-sdk-viem │ ├── .env.example │ ├── README.md │ ├── codegen.ts │ ├── contracts │ │ └── SwapMock.sol │ ├── examples │ │ └── whitelistedMarkets.ts │ ├── graphql │ │ ├── GetLiquidatablePositions.query.gql │ │ ├── GetMarketsAssets.query.gql │ │ ├── GetWhitelistedMarketIds.query.gql │ │ └── MarketPosition.fragment.gql │ ├── hardhat.config.cjs │ ├── package.json │ ├── src │ │ ├── LiquidationEncoder.ts │ │ ├── abis.ts │ │ ├── addresses.ts │ │ ├── api │ │ │ ├── index.ts │ │ │ ├── sdk.ts │ │ │ └── types.ts │ │ ├── flashbots.ts │ │ ├── index.ts │ │ ├── positions │ │ │ ├── getters.ts │ │ │ └── index.ts │ │ ├── preLiquidation │ │ │ ├── helpers.ts │ │ │ ├── index.ts │ │ │ ├── positionGetters.ts │ │ │ └── types.ts │ │ ├── swap │ │ │ ├── 1inch.ts │ │ │ ├── index.ts │ │ │ ├── paraswap.ts │ │ │ └── types.ts │ │ ├── thresholds.ts │ │ └── tokens │ │ │ ├── index.ts │ │ │ ├── midas.ts │ │ │ ├── pendle.ts │ │ │ ├── sky.ts │ │ │ ├── spectra.ts │ │ │ └── usual.ts │ ├── test │ │ ├── contracts │ │ │ └── SwapMock.ts │ │ ├── examples │ │ │ ├── midas.test.ts │ │ │ ├── preLiquidation.test.ts │ │ │ ├── spectra.test.ts │ │ │ └── whitelistedMarkets.test.ts │ │ ├── mockData │ │ │ ├── pendleMarketData.json │ │ │ ├── pendleTokens.json │ │ │ └── spectraTokens.json │ │ └── setup.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── liquidity-sdk-ethers │ ├── .env.example │ ├── README.md │ ├── codegen.ts │ ├── graphql │ │ └── GetMarkets.query.gql │ ├── package.json │ ├── src │ │ ├── api │ │ │ ├── index.ts │ │ │ ├── sdk.ts │ │ │ └── types.ts │ │ ├── index.ts │ │ └── loader.ts │ ├── test │ │ ├── loader.test.ts │ │ ├── mocks │ │ │ ├── dataloader.0.json │ │ │ └── dataloader.1.json │ │ └── setup.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── liquidity-sdk-viem │ ├── .env.example │ ├── README.md │ ├── codegen.ts │ ├── graphql │ │ └── GetMarkets.query.gql │ ├── package.json │ ├── src │ │ ├── api │ │ │ ├── index.ts │ │ │ ├── sdk.ts │ │ │ └── types.ts │ │ ├── index.ts │ │ └── loader.ts │ ├── test │ │ ├── loader.test.ts │ │ ├── mocks │ │ │ ├── dataloader.0.json │ │ │ └── dataloader.1.json │ │ └── setup.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── migration-sdk-viem │ ├── .env.example │ ├── README.md │ ├── package.json │ ├── src │ │ ├── abis │ │ │ ├── aaveV2.ts │ │ │ ├── aaveV3.ts │ │ │ ├── aaveV3Optimizer.ts │ │ │ ├── compoundV2.ts │ │ │ └── compoundV3.ts │ │ ├── config.ts │ │ ├── fetchers │ │ │ ├── aaveV2 │ │ │ │ └── aaveV2.fetchers.ts │ │ │ ├── aaveV3 │ │ │ │ └── aaveV3.fetchers.ts │ │ │ ├── aaveV3Optimizer │ │ │ │ ├── AaveV3.maths.ts │ │ │ │ ├── aaveV3Optimizer.fetchers.ts │ │ │ │ └── aaveV3Optimizer.helpers.ts │ │ │ ├── compoundV2 │ │ │ │ ├── compoundV2.fetchers.ts │ │ │ │ └── compoundV2.helpers.ts │ │ │ ├── compoundV3 │ │ │ │ └── compoundV3.fetchers.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── positions │ │ │ ├── borrow │ │ │ │ ├── MigratableBorrowPosition.ts │ │ │ │ ├── aaveV2.borrow.ts │ │ │ │ ├── aaveV3.borrow.ts │ │ │ │ ├── blue.borrow.ts │ │ │ │ ├── compoundV3.borrow.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── signature │ │ │ │ ├── aaveV3Optimizer.ts │ │ │ │ └── compoundV3.ts │ │ │ └── supply │ │ │ │ ├── MigratableSupplyPosition.ts │ │ │ │ ├── aaveV2.supply.ts │ │ │ │ ├── aaveV3.supply.ts │ │ │ │ ├── aaveV3Optimizer.supply.ts │ │ │ │ ├── compoundV2.supply.ts │ │ │ │ ├── compoundV3.supply.ts │ │ │ │ └── index.ts │ │ ├── types │ │ │ ├── actions.ts │ │ │ ├── index.ts │ │ │ └── positions.ts │ │ └── utils │ │ │ └── rates.ts │ ├── test │ │ └── e2e │ │ │ ├── aaveV2 │ │ │ ├── borrow.test.ts │ │ │ └── supply.test.ts │ │ │ ├── aaveV3 │ │ │ ├── borrow.test.ts │ │ │ └── supply.test.ts │ │ │ ├── aaveV3Optimizer │ │ │ └── supply.test.ts │ │ │ ├── blue │ │ │ └── borrow.test.ts │ │ │ ├── compoundV2 │ │ │ └── supply.test.ts │ │ │ ├── compoundV3 │ │ │ ├── borrow.test.ts │ │ │ └── supply.test.ts │ │ │ └── setup.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── morpho-test │ ├── README.md │ ├── package.json │ ├── src │ │ ├── fixtures │ │ │ ├── index.ts │ │ │ ├── markets.ts │ │ │ ├── tokens.ts │ │ │ └── vaults.ts │ │ └── index.ts │ └── tsconfig.json ├── morpho-ts │ ├── README.md │ ├── package.json │ ├── src │ │ ├── format │ │ │ ├── array.ts │ │ │ ├── format │ │ │ │ ├── format.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── locale.ts │ │ │ └── string.ts │ │ ├── index.ts │ │ ├── time │ │ │ ├── index.ts │ │ │ └── time.ts │ │ ├── types.ts │ │ ├── urls.ts │ │ └── utils.ts │ ├── test │ │ ├── format.test.ts │ │ └── time.test.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── simulation-sdk-wagmi │ ├── README.md │ ├── package.json │ ├── src │ │ ├── hooks │ │ │ ├── index.ts │ │ │ └── useSimulationState.ts │ │ └── index.ts │ ├── test │ │ ├── handlers │ │ │ ├── blue │ │ │ │ └── accrueInterest.test.ts │ │ │ └── metamorpho │ │ │ │ ├── deposit.test.ts │ │ │ │ ├── publicReallocate.test.ts │ │ │ │ ├── reallocate.test.ts │ │ │ │ └── withdraw.test.ts │ │ ├── hooks │ │ │ └── useSimulationState.test.ts │ │ └── setup.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── simulation-sdk │ ├── README.md │ ├── package.json │ ├── src │ │ ├── SimulationState.ts │ │ ├── errors.ts │ │ ├── handlers │ │ │ ├── blue │ │ │ │ ├── accrueInterest.ts │ │ │ │ ├── borrow.ts │ │ │ │ ├── index.ts │ │ │ │ ├── repay.ts │ │ │ │ ├── setAuthorization.ts │ │ │ │ ├── supply.ts │ │ │ │ ├── supplyCollateral.ts │ │ │ │ ├── withdraw.ts │ │ │ │ └── withdrawCollateral.ts │ │ │ ├── dispatchers.ts │ │ │ ├── erc20 │ │ │ │ ├── approve.ts │ │ │ │ ├── index.ts │ │ │ │ ├── permit.ts │ │ │ │ ├── permit2.ts │ │ │ │ ├── transfer.ts │ │ │ │ ├── transfer2.ts │ │ │ │ ├── unwrap.ts │ │ │ │ └── wrap.ts │ │ │ ├── index.ts │ │ │ ├── metamorpho │ │ │ │ ├── accrueInterest.ts │ │ │ │ ├── deposit.ts │ │ │ │ ├── index.ts │ │ │ │ ├── publicReallocate.ts │ │ │ │ ├── reallocate.ts │ │ │ │ └── withdraw.ts │ │ │ └── types.ts │ │ ├── helpers │ │ │ ├── index.ts │ │ │ └── mutative.ts │ │ ├── index.ts │ │ └── operations.ts │ ├── test │ │ ├── SimulationState.test.ts │ │ ├── fixtures.ts │ │ └── handlers │ │ │ ├── blue │ │ │ ├── accrueInterest.test.ts │ │ │ ├── borrow.test.ts │ │ │ ├── repay.test.ts │ │ │ ├── setAuthorization.test.ts │ │ │ ├── supply.test.ts │ │ │ ├── supplyCollateral.test.ts │ │ │ ├── withdraw.test.ts │ │ │ └── withdrawCollateral.test.ts │ │ │ ├── dispatchers.test.ts │ │ │ ├── erc20 │ │ │ ├── approve.test.ts │ │ │ ├── permit.test.ts │ │ │ ├── permit2.test.ts │ │ │ ├── transfer.test.ts │ │ │ ├── transfer2.test.ts │ │ │ ├── unwrap.test.ts │ │ │ └── wrap.test.ts │ │ │ └── metamorpho │ │ │ ├── deposit.test.ts │ │ │ ├── publicReallocate.test.ts │ │ │ ├── reallocate.test.ts │ │ │ └── withdraw.test.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── test-wagmi │ ├── README.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── react.ts │ │ └── vitest.ts │ └── tsconfig.json └── test │ ├── README.md │ ├── package.json │ ├── src │ ├── anvil.ts │ ├── client.ts │ ├── fixtures │ │ ├── ethers.ts │ │ └── index.ts │ ├── index.ts │ ├── playwright.ts │ └── vitest │ │ ├── ethers.ts │ │ └── index.ts │ ├── tsconfig.build.cjs.json │ ├── tsconfig.build.esm.json │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── scripts ├── lint │ └── checksum-address.js └── release │ ├── bumper.js │ ├── release.js │ └── version.js ├── tsconfig.json ├── vitest.config.ts └── vitest.setup.ts /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | target-branch: dev 8 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | workflow_call: 5 | 6 | jobs: 7 | build: 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | package: 12 | - test 13 | - test-wagmi 14 | 15 | - morpho-ts 16 | - morpho-test 17 | 18 | - blue-api-sdk 19 | - blue-sdk 20 | - blue-sdk-ethers 21 | - blue-sdk-viem 22 | - blue-sdk-wagmi 23 | 24 | - simulation-sdk 25 | - simulation-sdk-wagmi 26 | 27 | - bundler-sdk-ethers 28 | - bundler-sdk-viem 29 | 30 | - liquidation-sdk-viem 31 | - liquidity-sdk-ethers 32 | - liquidity-sdk-viem 33 | 34 | - migration-sdk-viem 35 | 36 | name: "@morpho-org/${{ matrix.package }}" 37 | runs-on: ubuntu-latest 38 | 39 | steps: 40 | - uses: actions/checkout@v4 41 | 42 | - uses: pnpm/action-setup@v4 43 | 44 | - uses: actions/setup-node@v4 45 | with: 46 | node-version-file: .nvmrc 47 | cache: pnpm 48 | 49 | - run: pnpm install --frozen-lockfile 50 | 51 | - run: pnpm --filter @morpho-org/${{ matrix.package }} build 52 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | workflow_call: 5 | 6 | jobs: 7 | biome: 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v4 12 | 13 | - uses: pnpm/action-setup@v4 14 | 15 | - uses: actions/setup-node@v4 16 | with: 17 | node-version-file: .nvmrc 18 | cache: pnpm 19 | 20 | - run: pnpm install --frozen-lockfile 21 | 22 | - run: pnpm lint 23 | -------------------------------------------------------------------------------- /.github/workflows/push.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**' 7 | tags-ignore: 8 | - '**' 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | lint: 16 | uses: ./.github/workflows/lint.yml 17 | 18 | build: 19 | uses: ./.github/workflows/build.yml 20 | 21 | test: 22 | uses: ./.github/workflows/test.yml 23 | secrets: inherit 24 | 25 | publish: 26 | if: github.ref_name == 'main' || github.ref_name == 'next' 27 | 28 | needs: 29 | - lint 30 | - build 31 | - test 32 | 33 | uses: ./.github/workflows/publish.yml 34 | secrets: inherit -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | workflow_call: 5 | secrets: 6 | MAINNET_RPC_URL: 7 | required: true 8 | 9 | jobs: 10 | vitest: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - uses: pnpm/action-setup@v4 17 | 18 | - uses: actions/setup-node@v4 19 | with: 20 | node-version-file: .nvmrc 21 | cache: pnpm 22 | 23 | - name: Restore Foundry cache 24 | uses: actions/cache/restore@v3 25 | with: 26 | path: ~/.foundry/cache/rpc 27 | key: ${{ runner.os }}-foundry-chain-fork-${{ github.job }}-${{ github.sha }} 28 | restore-keys: ${{ runner.os }}-foundry-chain-fork-${{ github.job }}- 29 | 30 | - uses: foundry-rs/foundry-toolchain@v1 31 | with: 32 | cache: false 33 | 34 | - run: pnpm install --frozen-lockfile 35 | 36 | - run: pnpm run test --coverage 37 | env: 38 | MAINNET_RPC_URL: ${{ secrets.MAINNET_RPC_URL }} 39 | IS_LOGGING_DISABLED: true 40 | 41 | - name: Save Foundry cache 42 | if: always() 43 | uses: actions/cache/save@v3 44 | with: 45 | path: ~/.foundry/cache/rpc 46 | key: ${{ runner.os }}-foundry-chain-fork-${{ github.job }}-${{ github.sha }} 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE 2 | .idea 3 | .project 4 | .classpath 5 | .c9/ 6 | *.launch 7 | .settings/ 8 | *.sublime-workspace 9 | 10 | # dependencies 11 | node_modules 12 | 13 | # testing 14 | coverage 15 | .nyc_output 16 | .mp4 17 | 18 | # production 19 | build 20 | artifacts 21 | dist 22 | packages/**/lib 23 | sdks/**/lib 24 | services/**/lib 25 | services/**/.esbuild 26 | services/**/.serverless 27 | 28 | # misc 29 | .DS_Store 30 | *.env* 31 | !*.env.example* 32 | !*.env.test 33 | *.pem 34 | stats-*.json 35 | **/disk-store 36 | 37 | 38 | # debug 39 | cache 40 | *.log* 41 | *.tsbuildinfo 42 | 43 | # yarn 44 | .pnp.* 45 | .yarn/* 46 | !.yarn/patches 47 | !.yarn/plugins 48 | !.yarn/releases 49 | !.yarn/sdks 50 | !.yarn/versions 51 | 52 | 53 | # auto generated files 54 | manifest.json 55 | !**/public/manifest.json 56 | 57 | # Logs 58 | logs 59 | *.log 60 | npm-debug.log* 61 | pnpm-debug.log* 62 | yarn-debug.log* 63 | yarn-error.log* 64 | lerna-debug.log* 65 | 66 | # Local 67 | local_* 68 | 69 | # Cypress 70 | **/cypress/videos 71 | **/cypress/screenshots 72 | 73 | # Local Netlify folder 74 | .netlify 75 | 76 | !.vscode -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | pnpm exec lint-staged 2 | -------------------------------------------------------------------------------- /.husky/prepare-commit-msg: -------------------------------------------------------------------------------- 1 | pnpm exec commitlint --edit 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | auto-install-peers=true 2 | link-workspace-packages=deep 3 | prefer-workspace-packages=true 4 | 5 | ignore-workspace-cycles=true 6 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 22 -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "esbenp.prettier-vscode", 4 | "biomejs.biome" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "biomejs.biome", 3 | "editor.codeActionsOnSave": { 4 | "quickfix.biome": "always", 5 | "source.organizeImports.biome": "always" 6 | }, 7 | "[markdown]": { 8 | "editor.defaultFormatter": "esbenp.prettier-vscode" 9 | }, 10 | "[typescript]": { 11 | "editor.defaultFormatter": "biomejs.biome" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Morpho Association 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.9.3/schema.json", 3 | "files": { 4 | "ignore": [ 5 | "lib", 6 | ".vscode", 7 | "cache", 8 | "coverage", 9 | "artifacts", 10 | "packages/bundler-sdk-ethers/src/types", 11 | "packages/liquidation-sdk-viem/src/api/sdk.ts", 12 | "packages/liquidity-sdk-ethers/src/api/sdk.ts", 13 | "packages/liquidity-sdk-viem/src/api/sdk.ts" 14 | ] 15 | }, 16 | "formatter": { 17 | "enabled": true, 18 | "formatWithErrors": false, 19 | "indentStyle": "space", 20 | "indentWidth": 2 21 | }, 22 | "organizeImports": { 23 | "enabled": true 24 | }, 25 | "linter": { 26 | "enabled": true, 27 | "rules": { 28 | "recommended": true, 29 | "complexity": { 30 | "noForEach": "off", 31 | "noBannedTypes": "off" 32 | }, 33 | "correctness": { 34 | "noUnusedImports": "error", 35 | "noUnusedVariables": "error", 36 | "useExhaustiveDependencies": "error" 37 | }, 38 | "style": { 39 | "noParameterAssign": "off", 40 | "useBlockStatements": "off", 41 | "noUnusedTemplateLiteral": "off", 42 | "noNonNullAssertion": "off" 43 | }, 44 | "suspicious": { 45 | "useGetterReturn": "off", 46 | "noAssignInExpressions": "off", 47 | "noFallthroughSwitchClause": "off" 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/blue-api-sdk/codegen.ts: -------------------------------------------------------------------------------- 1 | import type { CodegenConfig } from "@graphql-codegen/cli"; 2 | 3 | import { BLUE_API_GRAPHQL_URL } from "@morpho-org/morpho-ts"; 4 | 5 | const config: CodegenConfig = { 6 | overwrite: true, 7 | schema: BLUE_API_GRAPHQL_URL, 8 | generates: { 9 | "./src/types.ts": { 10 | plugins: ["typescript"], 11 | config: { 12 | documentMode: "string", 13 | avoidOptionals: { 14 | field: true, 15 | inputValue: false, 16 | defaultValue: true, 17 | }, 18 | scalars: { 19 | BigInt: { 20 | input: "string | number", 21 | output: "bigint", 22 | }, 23 | HexString: { 24 | input: "string", 25 | output: "`0x${string}`", 26 | }, 27 | Address: { 28 | input: "string", 29 | output: "@morpho-org/blue-sdk#Address", 30 | }, 31 | MarketId: { 32 | input: "string", 33 | output: "@morpho-org/blue-sdk#MarketId", 34 | }, 35 | }, 36 | }, 37 | }, 38 | }, 39 | }; 40 | 41 | export default config; 42 | -------------------------------------------------------------------------------- /packages/blue-api-sdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@morpho-org/blue-api-sdk", 3 | "description": "GraphQL SDK that exports types from the [API's GraphQL schema](https://blue-api.morpho.org/graphql) and a useful Apollo cache controller.", 4 | "version": "2.0.0", 5 | "author": "Morpho Association ", 6 | "contributors": ["Rubilmax "], 7 | "repository": "github:morpho-org/sdks", 8 | "homepage": "https://github.com/morpho-org/sdks", 9 | "bugs": { 10 | "url": "https://github.com/morpho-org/sdks/issues", 11 | "email": "contact@morpho.org" 12 | }, 13 | "main": "src/index.ts", 14 | "files": ["lib"], 15 | "scripts": { 16 | "prepublish": "$npm_execpath build", 17 | "codegen": "graphql-codegen --config codegen.ts", 18 | "build": "tsc --noEmit && tsc --build tsconfig.build.json" 19 | }, 20 | "peerDependencies": { 21 | "@morpho-org/blue-sdk": "workspace:^", 22 | "@morpho-org/morpho-ts": "workspace:^" 23 | }, 24 | "devDependencies": { 25 | "@graphql-codegen/cli": "^5.0.3", 26 | "@graphql-codegen/typescript": "^4.1.2", 27 | "@morpho-org/blue-sdk": "workspace:^", 28 | "@morpho-org/morpho-ts": "workspace:^", 29 | "graphql": "^16.10.0", 30 | "typescript": "^5.7.2" 31 | }, 32 | "publishConfig": { 33 | "main": "lib/index.js", 34 | "types": "lib/index.d.ts" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/blue-api-sdk/src/errors.ts: -------------------------------------------------------------------------------- 1 | export enum BlueApiErrorCode { 2 | NOT_FOUND = "NOT_FOUND", 3 | BAD_USER_INPUT = "BAD_USER_INPUT", 4 | INTERNAL_SERVER_ERROR = "INTERNAL_SERVER_ERROR", 5 | } 6 | -------------------------------------------------------------------------------- /packages/blue-api-sdk/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./types"; 2 | export * from "./converter"; 3 | export * from "./warnings"; 4 | export * from "./cache"; 5 | export * from "./errors"; 6 | export * from "./tags"; 7 | -------------------------------------------------------------------------------- /packages/blue-api-sdk/src/tags.ts: -------------------------------------------------------------------------------- 1 | export enum AssetTags { 2 | permissioned = "permissioned", 3 | yieldBearingToken = "yield", 4 | stablecoin = "stablecoin", 5 | usd = "usd-pegged", 6 | eur = "eur-pegged", 7 | btc = "btc", 8 | eth = "eth", 9 | erc4626 = "erc4626", 10 | lst = "lst", 11 | lrt = "lrt", 12 | rwa = "rwa", 13 | hardcoded = "hardcoded", 14 | convex = "convex-wrapper", 15 | governance = "governance-token", 16 | permit = "simple-permit", 17 | permitDai = "dai-specific-permit", 18 | } 19 | -------------------------------------------------------------------------------- /packages/blue-api-sdk/src/warnings.ts: -------------------------------------------------------------------------------- 1 | export enum MarketWarningType { 2 | BadDebtRealized = "bad_debt_realized", 3 | BadDebtUnrealized = "bad_debt_unrealized", 4 | HardcodedOracle = "hardcoded_oracle", 5 | UnrecognizedCollateralAsset = "unrecognized_collateral_asset", 6 | UnrecognizedLender = "unrecognized_lender", 7 | UnrecognizedLoanAsset = "unrecognized_loan_asset", 8 | UnrecognizedOracle = "unrecognized_oracle", 9 | UnrecognizedOracleFeed = "unrecognized_oracle_feed", 10 | LowLiquidity = "low_liquidity", 11 | UnsafeVaultAsCollateralAsset = "unsafe_vault_as_collateral_asset", 12 | IncompatibleOracleFeeds = "incompatible_oracle_feeds", 13 | IncorrectCollateralExchangeRate = "incorrect_collateral_exchange_rate", 14 | IncorrectLoanExchangeRate = "incorrect_loan_exchange_rate", 15 | NonWhitelisted = "not_whitelisted", 16 | OracleNotFromFactory = "oracle_not_from_factory", 17 | MisconfiguredOracleDecimals = "misconfigured_oracle_decimals", 18 | IncorrectOracleConfiguration = "incorrect_oracle_configuration", 19 | } 20 | 21 | export enum VaultWarningType { 22 | UnrecognizedDepositAsset = "unrecognized_deposit_asset", 23 | UnrecognizedVaultCurator = "unrecognized_vault_curator", 24 | UnrecognizedMarket = "unrecognized_market", 25 | NonWhitelisted = "not_whitelisted", 26 | } 27 | -------------------------------------------------------------------------------- /packages/blue-api-sdk/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src" 5 | }, 6 | "include": ["src"], 7 | "files": [] 8 | } 9 | -------------------------------------------------------------------------------- /packages/blue-api-sdk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": ".", 6 | "baseUrl": "." 7 | }, 8 | "include": ["src"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@morpho-org/blue-sdk-ethers", 3 | "description": "Ethers-based augmentation of `@morpho-org/blue-sdk` that exports (and optionally injects) ethers-based fetch methods.", 4 | "version": "2.0.0", 5 | "author": "Morpho Association ", 6 | "license": "MIT", 7 | "main": "src/index.ts", 8 | "files": ["lib"], 9 | "scripts": { 10 | "prepublish": "$npm_execpath build", 11 | "build": "tsc --noEmit && tsc --build tsconfig.build.json" 12 | }, 13 | "dependencies": { 14 | "rxjs": "^7.8.1" 15 | }, 16 | "peerDependencies": { 17 | "@morpho-org/blue-sdk": "workspace:^", 18 | "@morpho-org/morpho-ts": "workspace:^", 19 | "ethers": "^6.0.0", 20 | "ethers-types": "^3.18.1" 21 | }, 22 | "devDependencies": { 23 | "@morpho-org/blue-sdk": "workspace:^", 24 | "@morpho-org/morpho-test": "workspace:^", 25 | "@morpho-org/morpho-ts": "workspace:^", 26 | "@morpho-org/test": "workspace:^", 27 | "ethers": "^6.13.5", 28 | "ethers-types": "^3.18.1", 29 | "typescript": "^5.7.2", 30 | "viem": "^2.23.0", 31 | "vitest": "^3.0.5" 32 | }, 33 | "publishConfig": { 34 | "main": "lib/index.js", 35 | "types": "lib/index.d.ts" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/augment/Holding.ts: -------------------------------------------------------------------------------- 1 | import { Holding } from "@morpho-org/blue-sdk"; 2 | import { fetchHolding } from "../fetch"; 3 | 4 | declare module "@morpho-org/blue-sdk" { 5 | namespace Holding { 6 | let fetch: typeof fetchHolding; 7 | } 8 | } 9 | 10 | Holding.fetch = fetchHolding; 11 | 12 | export { Holding }; 13 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/augment/Market.ts: -------------------------------------------------------------------------------- 1 | import { Market } from "@morpho-org/blue-sdk"; 2 | import { fetchMarket, fetchMarketFromConfig } from "../fetch"; 3 | 4 | declare module "@morpho-org/blue-sdk" { 5 | namespace Market { 6 | let fetch: typeof fetchMarket; 7 | let fetchFromConfig: typeof fetchMarketFromConfig; 8 | } 9 | } 10 | 11 | Market.fetch = fetchMarket; 12 | Market.fetchFromConfig = fetchMarketFromConfig; 13 | 14 | export { Market }; 15 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/augment/MarketParams.ts: -------------------------------------------------------------------------------- 1 | import { MarketParams } from "@morpho-org/blue-sdk"; 2 | import { fetchMarketParams } from "../fetch"; 3 | 4 | declare module "@morpho-org/blue-sdk" { 5 | namespace MarketParams { 6 | let fetch: typeof fetchMarketParams; 7 | } 8 | } 9 | 10 | MarketParams.fetch = fetchMarketParams; 11 | 12 | export { MarketParams }; 13 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/augment/Position.ts: -------------------------------------------------------------------------------- 1 | import { AccrualPosition, Position } from "@morpho-org/blue-sdk"; 2 | 3 | import { 4 | fetchAccrualPosition, 5 | fetchAccrualPositionFromConfig, 6 | fetchPosition, 7 | } from "../fetch"; 8 | 9 | declare module "@morpho-org/blue-sdk" { 10 | namespace Position { 11 | let fetch: typeof fetchPosition; 12 | } 13 | 14 | namespace AccrualPosition { 15 | let fetch: typeof fetchAccrualPosition; 16 | let fetchFromConfig: typeof fetchAccrualPositionFromConfig; 17 | } 18 | } 19 | 20 | Position.fetch = fetchPosition; 21 | AccrualPosition.fetch = fetchAccrualPosition; 22 | AccrualPosition.fetchFromConfig = fetchAccrualPositionFromConfig; 23 | 24 | export { Position, AccrualPosition }; 25 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/augment/Token.ts: -------------------------------------------------------------------------------- 1 | import { Token } from "@morpho-org/blue-sdk"; 2 | import { fetchToken } from "../fetch"; 3 | 4 | declare module "@morpho-org/blue-sdk" { 5 | namespace Token { 6 | let fetch: typeof fetchToken; 7 | } 8 | } 9 | 10 | Token.fetch = fetchToken; 11 | 12 | export { Token }; 13 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/augment/User.ts: -------------------------------------------------------------------------------- 1 | import { User } from "@morpho-org/blue-sdk"; 2 | import { fetchUser } from "../fetch"; 3 | 4 | declare module "@morpho-org/blue-sdk" { 5 | namespace User { 6 | let fetch: typeof fetchUser; 7 | } 8 | } 9 | 10 | User.fetch = fetchUser; 11 | 12 | export { User }; 13 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/augment/Vault.ts: -------------------------------------------------------------------------------- 1 | import { AccrualVault, Vault } from "@morpho-org/blue-sdk"; 2 | 3 | import { fetchAccrualVault, fetchVault } from "../fetch"; 4 | 5 | declare module "@morpho-org/blue-sdk" { 6 | namespace Vault { 7 | let fetch: typeof fetchVault; 8 | } 9 | 10 | namespace AccrualVault { 11 | let fetch: typeof fetchAccrualVault; 12 | } 13 | } 14 | 15 | Vault.fetch = fetchVault; 16 | AccrualVault.fetch = fetchAccrualVault; 17 | 18 | export { Vault, AccrualVault }; 19 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/augment/VaultConfig.ts: -------------------------------------------------------------------------------- 1 | import { VaultConfig } from "@morpho-org/blue-sdk"; 2 | import { fetchVaultConfig } from "../fetch"; 3 | 4 | declare module "@morpho-org/blue-sdk" { 5 | namespace VaultConfig { 6 | let fetch: typeof fetchVaultConfig; 7 | } 8 | } 9 | 10 | VaultConfig.fetch = fetchVaultConfig; 11 | 12 | export { VaultConfig }; 13 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/augment/VaultMarketAllocation.ts: -------------------------------------------------------------------------------- 1 | import { VaultMarketAllocation } from "@morpho-org/blue-sdk"; 2 | 3 | import { 4 | fetchVaultMarketAllocation, 5 | fetchVaultMarketAllocationFromConfig, 6 | } from "../fetch"; 7 | 8 | declare module "@morpho-org/blue-sdk" { 9 | namespace VaultMarketAllocation { 10 | let fetch: typeof fetchVaultMarketAllocation; 11 | let fetchFromConfig: typeof fetchVaultMarketAllocationFromConfig; 12 | } 13 | } 14 | 15 | VaultMarketAllocation.fetch = fetchVaultMarketAllocation; 16 | VaultMarketAllocation.fetchFromConfig = fetchVaultMarketAllocationFromConfig; 17 | 18 | export { VaultMarketAllocation }; 19 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/augment/VaultMarketConfig.ts: -------------------------------------------------------------------------------- 1 | import { VaultMarketConfig } from "@morpho-org/blue-sdk"; 2 | 3 | import { fetchVaultMarketConfig } from "../fetch"; 4 | 5 | declare module "@morpho-org/blue-sdk" { 6 | namespace VaultMarketConfig { 7 | let fetch: typeof fetchVaultMarketConfig; 8 | } 9 | } 10 | 11 | VaultMarketConfig.fetch = fetchVaultMarketConfig; 12 | 13 | export { VaultMarketConfig }; 14 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/augment/VaultMarketPublicAllocatorConfig.ts: -------------------------------------------------------------------------------- 1 | import { VaultMarketPublicAllocatorConfig } from "@morpho-org/blue-sdk"; 2 | import { fetchVaultMarketPublicAllocatorConfig } from "../fetch"; 3 | 4 | declare module "@morpho-org/blue-sdk" { 5 | namespace VaultMarketPublicAllocatorConfig { 6 | let fetch: typeof fetchVaultMarketPublicAllocatorConfig; 7 | } 8 | } 9 | 10 | VaultMarketPublicAllocatorConfig.fetch = fetchVaultMarketPublicAllocatorConfig; 11 | 12 | export { VaultMarketPublicAllocatorConfig }; 13 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/augment/VaultUser.ts: -------------------------------------------------------------------------------- 1 | import { VaultUser } from "@morpho-org/blue-sdk"; 2 | 3 | import { fetchVaultUser, fetchVaultUserFromConfig } from "../fetch"; 4 | 5 | declare module "@morpho-org/blue-sdk" { 6 | namespace VaultUser { 7 | let fetch: typeof fetchVaultUser; 8 | let fetchFromConfig: typeof fetchVaultUserFromConfig; 9 | } 10 | } 11 | 12 | VaultUser.fetch = fetchVaultUser; 13 | VaultUser.fetchFromConfig = fetchVaultUserFromConfig; 14 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/augment/ethers.ts: -------------------------------------------------------------------------------- 1 | import type { Address } from "@morpho-org/blue-sdk"; 2 | import type { BytesLike, SignatureLike } from "ethers"; 3 | 4 | declare module "ethers" { 5 | interface Signer { 6 | getAddress(): Promise
; 7 | } 8 | 9 | interface Wallet { 10 | getAddress(): Promise
; 11 | } 12 | 13 | namespace ethers { 14 | // @ts-ignore 15 | const ZeroAddress: Address; 16 | function getAddress(add: string): Address; 17 | function isAddress(add: string): add is Address; 18 | function recoverAddress( 19 | digest: BytesLike, 20 | signature: SignatureLike, 21 | ): Address; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/augment/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Holding"; 2 | export * from "./Position"; 3 | export * from "./MarketParams"; 4 | export * from "./Market"; 5 | export * from "./Token"; 6 | export * from "./User"; 7 | export * from "./VaultConfig"; 8 | export * from "./Vault"; 9 | export * from "./VaultUser"; 10 | export * from "./VaultMarketConfig"; 11 | export * from "./VaultMarketAllocation"; 12 | export * from "./VaultMarketPublicAllocatorConfig"; 13 | export * from "./ethers"; 14 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/errors.ts: -------------------------------------------------------------------------------- 1 | import type { Address } from "@morpho-org/blue-sdk"; 2 | 3 | export class InvalidSignatureError extends Error { 4 | constructor( 5 | public readonly hash: string, 6 | public readonly signer: Address, 7 | public readonly recovered: Address, 8 | ) { 9 | super( 10 | `invalid signature for hash ${hash}: expected ${signer}, recovered ${recovered}`, 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/ethers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./utils"; 2 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/fetch/MarketParams.ts: -------------------------------------------------------------------------------- 1 | import type { Provider } from "ethers"; 2 | import { MorphoBlue__factory } from "ethers-types"; 3 | 4 | import { 5 | type Address, 6 | type ChainId, 7 | type MarketId, 8 | MarketParams, 9 | UnknownMarketParamsError, 10 | _try, 11 | getChainAddresses, 12 | } from "@morpho-org/blue-sdk"; 13 | 14 | export async function fetchMarketParams( 15 | id: MarketId, 16 | runner: { provider: Provider }, 17 | { chainId }: { chainId?: ChainId } = {}, 18 | ) { 19 | let config = _try(() => MarketParams.get(id), UnknownMarketParamsError); 20 | 21 | if (!config) { 22 | chainId ??= Number((await runner.provider.getNetwork()).chainId); 23 | 24 | const { morpho } = getChainAddresses(chainId); 25 | 26 | const marketParams = await MorphoBlue__factory.connect( 27 | morpho, 28 | runner, 29 | ).idToMarketParams(id, { 30 | // Always fetch at latest block because config is immutable. 31 | blockTag: "latest", 32 | }); 33 | 34 | config = new MarketParams({ 35 | lltv: marketParams.lltv, 36 | loanToken: marketParams.loanToken as Address, 37 | collateralToken: marketParams.collateralToken as Address, 38 | irm: marketParams.irm as Address, 39 | oracle: marketParams.oracle as Address, 40 | }); 41 | } 42 | 43 | return config; 44 | } 45 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/fetch/User.ts: -------------------------------------------------------------------------------- 1 | import type { Provider } from "ethers"; 2 | 3 | import { type Address, User, getChainAddresses } from "@morpho-org/blue-sdk"; 4 | import { MorphoBlue__factory } from "ethers-types"; 5 | import type { FetchOptions } from "../types"; 6 | 7 | export async function fetchUser( 8 | address: Address, 9 | runner: { provider: Provider }, 10 | { chainId, overrides = {} }: FetchOptions = {}, 11 | ) { 12 | chainId ??= Number((await runner.provider.getNetwork()).chainId); 13 | 14 | const { 15 | morpho, 16 | bundler3: { generalAdapter1 }, 17 | } = getChainAddresses(chainId); 18 | const blue = MorphoBlue__factory.connect(morpho, runner); 19 | 20 | const [isBundlerAuthorized, morphoNonce] = await Promise.all([ 21 | blue.isAuthorized(address, generalAdapter1, overrides), 22 | blue.nonce(address, overrides), 23 | ]); 24 | 25 | return new User({ 26 | address, 27 | isBundlerAuthorized, 28 | morphoNonce, 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/fetch/VaultConfig.ts: -------------------------------------------------------------------------------- 1 | import type { Provider } from "ethers"; 2 | import { MetaMorpho__factory } from "ethers-types"; 3 | 4 | import { type Address, VaultConfig } from "@morpho-org/blue-sdk"; 5 | import type { FetchOptions } from "../types"; 6 | import { fetchToken } from "./Token"; 7 | 8 | export async function fetchVaultConfig( 9 | address: Address, 10 | runner: { provider: Provider }, 11 | options: FetchOptions = {}, 12 | ) { 13 | options.chainId ??= Number((await runner.provider.getNetwork()).chainId); 14 | 15 | const mm = MetaMorpho__factory.connect(address, runner); 16 | 17 | const { overrides = {} } = options; 18 | 19 | const [token, asset, decimalsOffset] = await Promise.all([ 20 | fetchToken(address, runner, options), // TODO: avoid fetching decimals 21 | mm.asset() as Promise
, 22 | mm.DECIMALS_OFFSET(overrides), 23 | ]); 24 | 25 | return new VaultConfig({ 26 | ...token, 27 | asset, 28 | decimalsOffset, 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/fetch/VaultMarketAllocation.ts: -------------------------------------------------------------------------------- 1 | import type { Provider } from "ethers"; 2 | 3 | import { 4 | type Address, 5 | type MarketId, 6 | VaultMarketAllocation, 7 | type VaultMarketConfig, 8 | } from "@morpho-org/blue-sdk"; 9 | import type { FetchOptions } from "../types"; 10 | import { fetchAccrualPosition } from "./Position"; 11 | import { fetchVaultMarketConfig } from "./VaultMarketConfig"; 12 | 13 | export async function fetchVaultMarketAllocation( 14 | vault: Address, 15 | marketId: MarketId, 16 | runner: { provider: Provider }, 17 | options: FetchOptions = {}, 18 | ) { 19 | options.chainId ??= Number((await runner.provider.getNetwork()).chainId); 20 | 21 | const config = await fetchVaultMarketConfig(vault, marketId, runner, options); 22 | 23 | return fetchVaultMarketAllocationFromConfig( 24 | config, 25 | marketId, 26 | runner, 27 | options, 28 | ); 29 | } 30 | 31 | export async function fetchVaultMarketAllocationFromConfig( 32 | config: VaultMarketConfig, 33 | marketId: MarketId, 34 | runner: { provider: Provider }, 35 | options: FetchOptions = {}, 36 | ) { 37 | options.chainId ??= Number((await runner.provider.getNetwork()).chainId); 38 | 39 | return new VaultMarketAllocation({ 40 | config, 41 | position: await fetchAccrualPosition( 42 | config.vault, 43 | marketId, 44 | runner, 45 | options, 46 | ), 47 | }); 48 | } 49 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/fetch/VaultMarketConfig.ts: -------------------------------------------------------------------------------- 1 | import type { Provider } from "ethers"; 2 | import { MetaMorpho__factory } from "ethers-types"; 3 | 4 | import { 5 | type Address, 6 | type MarketId, 7 | VaultMarketConfig, 8 | } from "@morpho-org/blue-sdk"; 9 | import type { FetchOptions } from "../types"; 10 | import { fetchVaultMarketPublicAllocatorConfig } from "./VaultMarketPublicAllocatorConfig"; 11 | 12 | export async function fetchVaultMarketConfig( 13 | vault: Address, 14 | marketId: MarketId, 15 | runner: { provider: Provider }, 16 | options: FetchOptions = {}, 17 | ) { 18 | options.chainId ??= Number((await runner.provider.getNetwork()).chainId); 19 | options.overrides ??= {}; 20 | 21 | const mm = MetaMorpho__factory.connect(vault, runner); 22 | 23 | const [{ cap, removableAt, enabled }, pendingCap, publicAllocatorConfig] = 24 | await Promise.all([ 25 | mm.config(marketId, options.overrides), 26 | mm 27 | .pendingCap(marketId, options.overrides) 28 | .then(({ value, validAt }) => ({ value, validAt })), 29 | fetchVaultMarketPublicAllocatorConfig(vault, marketId, runner, options), 30 | ]); 31 | 32 | return new VaultMarketConfig({ 33 | vault, 34 | marketId, 35 | cap, 36 | pendingCap, 37 | enabled, 38 | removableAt, 39 | publicAllocatorConfig, 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/fetch/VaultMarketPublicAllocatorConfig.ts: -------------------------------------------------------------------------------- 1 | import type { Provider } from "ethers"; 2 | import { PublicAllocator__factory } from "ethers-types"; 3 | 4 | import { 5 | type Address, 6 | type MarketId, 7 | VaultMarketPublicAllocatorConfig, 8 | getChainAddresses, 9 | } from "@morpho-org/blue-sdk"; 10 | import type { FetchOptions } from "../types"; 11 | 12 | export async function fetchVaultMarketPublicAllocatorConfig( 13 | vault: Address, 14 | marketId: MarketId, 15 | runner: { provider: Provider }, 16 | { chainId, overrides = {} }: FetchOptions = {}, 17 | ) { 18 | chainId ??= Number((await runner.provider.getNetwork()).chainId); 19 | 20 | const { publicAllocator } = getChainAddresses(chainId); 21 | if (publicAllocator == null) return; 22 | 23 | const [maxIn, maxOut] = await PublicAllocator__factory.connect( 24 | publicAllocator, 25 | runner, 26 | ).flowCaps(vault, marketId, overrides); 27 | 28 | return new VaultMarketPublicAllocatorConfig({ 29 | vault, 30 | marketId, 31 | maxIn, 32 | maxOut, 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/fetch/VaultUser.ts: -------------------------------------------------------------------------------- 1 | import type { Provider } from "ethers"; 2 | import { ERC20__factory, MetaMorpho__factory } from "ethers-types"; 3 | 4 | import { 5 | type Address, 6 | type VaultConfig, 7 | VaultUser, 8 | } from "@morpho-org/blue-sdk"; 9 | import type { FetchOptions } from "../types"; 10 | import { fetchVaultConfig } from "./VaultConfig"; 11 | 12 | export async function fetchVaultUser( 13 | vault: Address, 14 | user: Address, 15 | runner: { provider: Provider }, 16 | options: FetchOptions = {}, 17 | ) { 18 | options.chainId ??= Number((await runner.provider.getNetwork()).chainId); 19 | 20 | const config = await fetchVaultConfig(vault, runner, options); 21 | 22 | return fetchVaultUserFromConfig(config, user, runner, options); 23 | } 24 | 25 | export async function fetchVaultUserFromConfig( 26 | config: VaultConfig, 27 | user: Address, 28 | runner: { provider: Provider }, 29 | options: FetchOptions = {}, 30 | ) { 31 | options.chainId ??= Number((await runner.provider.getNetwork()).chainId); 32 | options.overrides ??= {}; 33 | 34 | const mm = MetaMorpho__factory.connect(config.address, runner); 35 | const erc20 = ERC20__factory.connect(config.asset, runner); 36 | 37 | const [allowance, isAllocator] = await Promise.all([ 38 | erc20.allowance(user, config.address, options.overrides), 39 | mm.isAllocator(user, options.overrides), 40 | ]); 41 | 42 | return new VaultUser({ 43 | vault: config.address, 44 | user, 45 | isAllocator, 46 | allowance, 47 | }); 48 | } 49 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/fetch/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Holding"; 2 | export * from "./Position"; 3 | export * from "./MarketParams"; 4 | export * from "./Market"; 5 | export * from "./Token"; 6 | export * from "./User"; 7 | export * from "./VaultConfig"; 8 | export * from "./Vault"; 9 | export * from "./VaultUser"; 10 | export * from "./VaultMarketConfig"; 11 | export * from "./VaultMarketAllocation"; 12 | export * from "./VaultMarketPublicAllocatorConfig"; 13 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./evm"; 2 | export * from "./notifications"; 3 | export * from "./signatures"; 4 | export * from "./fetch"; 5 | export * from "./ethers"; 6 | export * from "./types"; 7 | 8 | import "./augment/ethers"; 9 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/signatures/index.ts: -------------------------------------------------------------------------------- 1 | import * as _Manager from "./manager"; 2 | import * as _Permit from "./permit"; 3 | import * as _Permit2 from "./permit2"; 4 | import { 5 | getMessage as _getMessage, 6 | safeSignTypedData as _safeSignTypedData, 7 | verifySignature as _verifySignature, 8 | } from "./utils"; 9 | 10 | export * from "./types"; 11 | 12 | export namespace SignatureUtils { 13 | export const Permit = _Permit; 14 | export const Permit2 = _Permit2; 15 | export const Manager = _Manager; 16 | 17 | export const safeSignTypedData = _safeSignTypedData; 18 | export const verifySignature = _verifySignature; 19 | export const getMessage = _getMessage; 20 | } 21 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/signatures/manager.ts: -------------------------------------------------------------------------------- 1 | import { type ChainId, getChainAddresses } from "@morpho-org/blue-sdk"; 2 | 3 | import type { SignatureMessage } from "./types"; 4 | import { getMessage } from "./utils"; 5 | 6 | export interface ManagerApprovalSignatureArgs { 7 | authorizer: string; 8 | authorized: string; 9 | isAuthorized: boolean; 10 | nonce: bigint; 11 | deadline: bigint; 12 | } 13 | 14 | export const getManagerApprovalMessage = ( 15 | args: ManagerApprovalSignatureArgs, 16 | chainId: ChainId, 17 | ): SignatureMessage => { 18 | const domain = { 19 | chainId: chainId.toString(), 20 | verifyingContract: getChainAddresses(chainId).morpho, 21 | }; 22 | 23 | const types = { 24 | Authorization: [ 25 | { 26 | name: "authorizer", 27 | type: "address", 28 | }, 29 | { 30 | name: "authorized", 31 | type: "address", 32 | }, 33 | { 34 | name: "isAuthorized", 35 | type: "bool", 36 | }, 37 | { 38 | name: "nonce", 39 | type: "uint256", 40 | }, 41 | { 42 | name: "deadline", 43 | type: "uint256", 44 | }, 45 | ], 46 | }; 47 | 48 | return getMessage(domain, types, args); 49 | }; 50 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/signatures/types.ts: -------------------------------------------------------------------------------- 1 | import type { TypedDataDomain } from "ethers"; 2 | 3 | export interface SignatureData { 4 | domain: TypedDataDomain; 5 | types: Record< 6 | string, 7 | { 8 | name: string; 9 | type: string; 10 | }[] 11 | >; 12 | value: object; 13 | } 14 | 15 | export interface SignatureMessage { 16 | hash: string; 17 | data: SignatureData; 18 | } 19 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/src/types.ts: -------------------------------------------------------------------------------- 1 | import type { ViewOverrides } from "ethers-types/dist/common"; 2 | 3 | export interface FetchOptions { 4 | chainId?: number; 5 | overrides?: ViewOverrides; 6 | } 7 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/test/e2e/MarketParams.test.ts: -------------------------------------------------------------------------------- 1 | import { zeroAddress } from "viem"; 2 | 3 | import { 4 | ChainId, 5 | type MarketId, 6 | addressesRegistry, 7 | } from "@morpho-org/blue-sdk"; 8 | 9 | import { MarketParams } from "../../src/augment/MarketParams"; 10 | import { test } from "./setup"; 11 | 12 | import { markets } from "@morpho-org/morpho-test"; 13 | import { describe, expect } from "vitest"; 14 | 15 | const { usdc_wstEth } = markets[ChainId.EthMainnet]; 16 | 17 | describe("augment/MarketParams", () => { 18 | test("should fetch config from cache", async ({ wallet }) => { 19 | const market = await MarketParams.fetch(usdc_wstEth.id, wallet); 20 | 21 | expect(market).toStrictEqual(usdc_wstEth); 22 | }); 23 | 24 | test("should fetch config from chain", async ({ wallet }) => { 25 | const marketParams = { 26 | collateralToken: zeroAddress, 27 | loanToken: addressesRegistry[ChainId.EthMainnet].wNative, 28 | lltv: 0n, 29 | irm: zeroAddress, 30 | oracle: zeroAddress, 31 | id: "0x58e212060645d18eab6d9b2af3d56fbc906a92ff5667385f616f662c70372284", 32 | liquidationIncentiveFactor: 1150000000000000000n, 33 | }; 34 | 35 | const market = await MarketParams.fetch( 36 | "0x58e212060645d18eab6d9b2af3d56fbc906a92ff5667385f616f662c70372284" as MarketId, 37 | wallet, 38 | ); 39 | 40 | expect(market).toEqual(marketParams); // Not strict equal because not the same class. 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/test/e2e/User.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect } from "vitest"; 2 | import { test } from "./setup"; 3 | 4 | import { ChainId, addressesRegistry } from "@morpho-org/blue-sdk"; 5 | import { User } from "../../src/augment/User"; 6 | import { blueAbi } from "./abis"; 7 | 8 | const { 9 | morpho, 10 | bundler3: { generalAdapter1 }, 11 | } = addressesRegistry[ChainId.EthMainnet]; 12 | 13 | describe("augment/User", () => { 14 | test("should fetch user data", async ({ client, wallet }) => { 15 | await client.writeContract({ 16 | address: morpho, 17 | abi: blueAbi, 18 | functionName: "setAuthorization", 19 | args: [generalAdapter1, true], 20 | }); 21 | 22 | const expectedData = new User({ 23 | address: client.account.address, 24 | isBundlerAuthorized: true, 25 | morphoNonce: 0n, 26 | }); 27 | 28 | const value = await User.fetch(client.account.address, wallet); 29 | 30 | expect(value).toStrictEqual(expectedData); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/test/e2e/setup.ts: -------------------------------------------------------------------------------- 1 | import { createEthersTest } from "@morpho-org/test/vitest/ethers"; 2 | import { mainnet } from "viem/chains"; 3 | 4 | /** 5 | * This test will run on `mainnet` forked at block `19,530,000`. 6 | */ 7 | export const test = createEthersTest(mainnet, { 8 | forkUrl: process.env.MAINNET_RPC_URL, 9 | forkBlockNumber: 19_530_000, 10 | }); 11 | 12 | /** 13 | * This test will run on `mainnet` forked at block `21,595,000`. 14 | */ 15 | export const test2 = createEthersTest(mainnet, { 16 | forkUrl: process.env.MAINNET_RPC_URL, 17 | forkBlockNumber: 21_595_000, 18 | }); 19 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/test/unit/utils.test.ts: -------------------------------------------------------------------------------- 1 | import { safeParseNumber } from "../../src"; 2 | 3 | import { describe, expect, test } from "vitest"; 4 | 5 | describe("safeParseNumber", () => { 6 | test("should parse excessively small number", () => { 7 | expect(safeParseNumber(0.000000000000000000000000000000042, 18)).toEqual( 8 | 0n, 9 | ); 10 | }); 11 | 12 | test("should parse excessively large number", () => { 13 | expect( 14 | safeParseNumber(4200000000000000000000000000000000000, 18).toString(), 15 | ).toEqual( 16 | 4200000000000000000000000000000000000000000000000000000n.toString(), 17 | ); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src" 5 | }, 6 | "include": ["src"] 7 | } 8 | -------------------------------------------------------------------------------- /packages/blue-sdk-ethers/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": ".", 6 | "baseUrl": "." 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/contracts/GetMarket.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.0; 3 | 4 | import {IMorpho, Id, MarketParams, Market} from "./interfaces/IMorpho.sol"; 5 | import {IOracle} from "./interfaces/IOracle.sol"; 6 | import {IAdaptiveCurveIrm} from "./interfaces/IAdaptiveCurveIrm.sol"; 7 | 8 | struct MarketResponse { 9 | MarketParams marketParams; 10 | Market market; 11 | bool hasPrice; 12 | uint256 price; 13 | uint256 rateAtTarget; 14 | } 15 | 16 | contract GetMarket { 17 | function query(IMorpho morpho, Id id, IAdaptiveCurveIrm adaptiveCurveIrm) 18 | external 19 | view 20 | returns (MarketResponse memory res) 21 | { 22 | res.marketParams = morpho.idToMarketParams(id); 23 | res.market = morpho.market(id); 24 | 25 | if (res.marketParams.oracle != address(0)) { 26 | try IOracle(res.marketParams.oracle).price() returns (uint256 price) { 27 | res.hasPrice = true; 28 | res.price = price; 29 | } catch {} 30 | } 31 | 32 | if (res.marketParams.irm == address(adaptiveCurveIrm)) { 33 | res.rateAtTarget = uint256(adaptiveCurveIrm.rateAtTarget(id)); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/contracts/GetToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.0; 3 | 4 | import {IERC20Permit, Eip5267Domain} from "./interfaces/IERC20Permit.sol"; 5 | import {IERC20} from "./interfaces/IERC20.sol"; 6 | import {IWstEth} from "./interfaces/IWstEth.sol"; 7 | 8 | struct TokenResponse { 9 | uint256 decimals; 10 | bool hasSymbol; 11 | string symbol; 12 | bool hasName; 13 | string name; 14 | uint256 stEthPerWstEth; 15 | Eip5267Domain eip5267Domain; 16 | bool hasEip5267Domain; 17 | } 18 | 19 | contract GetToken { 20 | function query(IERC20 token, bool isWstEth) external view returns (TokenResponse memory res) { 21 | try token.name() returns (string memory name) { 22 | res.hasName = true; 23 | res.name = name; 24 | } catch {} 25 | 26 | try token.symbol() returns (string memory symbol) { 27 | res.hasSymbol = true; 28 | res.symbol = symbol; 29 | } catch {} 30 | 31 | try token.decimals() returns (uint8 decimals) { 32 | res.decimals = decimals; 33 | } catch {} 34 | 35 | if (isWstEth) res.stEthPerWstEth = IWstEth(address(token)).stEthPerToken(); 36 | 37 | try IERC20Permit(address(token)).eip712Domain() returns (Eip5267Domain memory eip5267Domain) { 38 | res.hasEip5267Domain = true; 39 | res.eip5267Domain = eip5267Domain; 40 | } catch {} 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/contracts/GetVaultUser.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.0; 3 | 4 | import {IERC20} from "./interfaces/IERC20.sol"; 5 | import {IMetaMorpho} from "./interfaces/IMetaMorpho.sol"; 6 | 7 | struct VaultUserResponse { 8 | bool isAllocator; 9 | uint256 allowance; 10 | } 11 | 12 | contract GetVaultUser { 13 | function query(IMetaMorpho vault, address user) external view returns (VaultUserResponse memory res) { 14 | res.isAllocator = vault.isAllocator(user); 15 | res.allowance = IERC20(vault.asset()).allowance(user, address(vault)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/contracts/interfaces/IAdaptiveCurveIrm.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0; 3 | 4 | import {IIrm} from "./IIrm.sol"; 5 | import {Id} from "./IMorpho.sol"; 6 | 7 | /// @title IAdaptiveCurveIrm 8 | /// @author Morpho Labs 9 | /// @custom:contact security@morpho.org 10 | /// @notice Interface exposed by the AdaptiveCurveIrm. 11 | interface IAdaptiveCurveIrm is IIrm { 12 | /// @notice Address of Morpho. 13 | function MORPHO() external view returns (address); 14 | 15 | /// @notice Rate at target utilization. 16 | /// @dev Tells the height of the curve. 17 | function rateAtTarget(Id id) external view returns (int256); 18 | } 19 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/contracts/interfaces/IERC20Permissioned.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.5.0; 3 | 4 | import {IERC20Permit} from "./IERC20Permit.sol"; 5 | 6 | interface IERC20Permissioned is IERC20Permit { 7 | function hasPermission(address account) external view returns (bool); 8 | } 9 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/contracts/interfaces/IIrm.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity >=0.5.0; 3 | 4 | import {MarketParams, Market} from "./IMorpho.sol"; 5 | 6 | /// @title IIrm 7 | /// @author Morpho Labs 8 | /// @custom:contact security@morpho.org 9 | /// @notice Interface that Interest Rate Models (IRMs) used by Morpho must implement. 10 | interface IIrm { 11 | /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams`. 12 | /// @dev Assumes that `market` corresponds to `marketParams`. 13 | function borrowRate(MarketParams memory marketParams, Market memory market) external returns (uint256); 14 | 15 | /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams` without modifying any 16 | /// storage. 17 | /// @dev Assumes that `market` corresponds to `marketParams`. 18 | function borrowRateView(MarketParams memory marketParams, Market memory market) external view returns (uint256); 19 | } 20 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/contracts/interfaces/IOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity >=0.5.0; 3 | 4 | /// @title IOracle 5 | /// @author Morpho Labs 6 | /// @custom:contact security@morpho.org 7 | /// @notice Interface that oracles used by Morpho must implement. 8 | /// @dev It is the user's responsibility to select markets with safe oracles. 9 | interface IOracle { 10 | /// @notice Returns the price of 1 asset of collateral token quoted in 1 asset of loan token, scaled by 1e36. 11 | /// @dev It corresponds to the price of 10**(collateral token decimals) assets of collateral token quoted in 12 | /// 10**(loan token decimals) assets of loan token with `36 + loan token decimals - collateral token decimals` 13 | /// decimals of precision. 14 | function price() external view returns (uint256); 15 | } 16 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/contracts/interfaces/IWhitelistControllerAggregator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.5.0; 3 | 4 | interface IWhitelistControllerAggregator { 5 | function isWhitelisted(address addressToCheck) external view returns (bool isWhitelisted); 6 | } 7 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/contracts/interfaces/IWrappedBackedToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.5.0; 3 | 4 | import {IERC20Permit} from "./IERC20Permit.sol"; 5 | import {IWhitelistControllerAggregator} from "./IWhitelistControllerAggregator.sol"; 6 | 7 | interface IWrappedBackedToken is IERC20Permit { 8 | function whitelistControllerAggregator() external view returns (IWhitelistControllerAggregator); 9 | } 10 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/contracts/interfaces/IWstEth.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity >=0.5.0; 3 | 4 | interface IWstEth { 5 | function DOMAIN_SEPARATOR() external view returns (bytes32); 6 | 7 | function allowance(address owner, address spender) external view returns (uint256); 8 | function approve(address spender, uint256 amount) external returns (bool); 9 | function balanceOf(address account) external view returns (uint256); 10 | function decimals() external view returns (uint8); 11 | function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool); 12 | function getStETHByWstETH(uint256 wstETHAmount) external view returns (uint256); 13 | function getWstETHByStETH(uint256 stETHAmount) external view returns (uint256); 14 | function increaseAllowance(address spender, uint256 addedValue) external returns (bool); 15 | function name() external view returns (string memory); 16 | function nonces(address owner) external view returns (uint256); 17 | function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) 18 | external; 19 | function stETH() external view returns (address); 20 | function stEthPerToken() external view returns (uint256); 21 | function symbol() external view returns (string memory); 22 | function tokensPerStEth() external view returns (uint256); 23 | function totalSupply() external view returns (uint256); 24 | function transfer(address recipient, uint256 amount) external returns (bool); 25 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 26 | function unwrap(uint256 wstETHAmount) external returns (uint256); 27 | function wrap(uint256 stETHAmount) external returns (uint256); 28 | } 29 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@morpho-org/blue-sdk-viem", 3 | "description": "Viem-based augmentation of `@morpho-org/blue-sdk` that exports (and optionally injects) viem-based fetch methods.", 4 | "version": "2.0.0", 5 | "author": "Morpho Association ", 6 | "contributors": ["Rubilmax "], 7 | "repository": "github:morpho-org/sdks", 8 | "homepage": "https://github.com/morpho-org/sdks", 9 | "bugs": { 10 | "url": "https://github.com/morpho-org/sdks/issues", 11 | "email": "contact@morpho.org" 12 | }, 13 | "license": "MIT", 14 | "main": "src/index.ts", 15 | "files": ["lib"], 16 | "scripts": { 17 | "prepublish": "$npm_execpath build", 18 | "compile": "hardhat compile", 19 | "build": "tsc --noEmit && tsc --build tsconfig.build.json" 20 | }, 21 | "peerDependencies": { 22 | "@morpho-org/blue-sdk": "workspace:^", 23 | "@morpho-org/morpho-ts": "workspace:^", 24 | "viem": "^2.0.0" 25 | }, 26 | "devDependencies": { 27 | "@morpho-org/blue-sdk": "workspace:^", 28 | "@morpho-org/morpho-test": "workspace:^", 29 | "@morpho-org/morpho-ts": "workspace:^", 30 | "@morpho-org/test": "workspace:^", 31 | "hardhat": "^2.22.18", 32 | "typescript": "^5.7.2", 33 | "viem": "^2.23.0", 34 | "vitest": "^3.0.5" 35 | }, 36 | "publishConfig": { 37 | "main": "lib/index.js", 38 | "types": "lib/index.d.ts" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/augment/Holding.ts: -------------------------------------------------------------------------------- 1 | import { Holding } from "@morpho-org/blue-sdk"; 2 | import { fetchHolding } from "../fetch"; 3 | 4 | declare module "@morpho-org/blue-sdk" { 5 | namespace Holding { 6 | let fetch: typeof fetchHolding; 7 | } 8 | } 9 | 10 | Holding.fetch = fetchHolding; 11 | 12 | export { Holding }; 13 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/augment/Market.ts: -------------------------------------------------------------------------------- 1 | import { Market } from "@morpho-org/blue-sdk"; 2 | import { fetchMarket } from "../fetch"; 3 | 4 | declare module "@morpho-org/blue-sdk" { 5 | namespace Market { 6 | let fetch: typeof fetchMarket; 7 | } 8 | } 9 | 10 | Market.fetch = fetchMarket; 11 | 12 | export { Market }; 13 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/augment/MarketParams.ts: -------------------------------------------------------------------------------- 1 | import { MarketParams } from "@morpho-org/blue-sdk"; 2 | import { fetchMarketParams } from "../fetch"; 3 | 4 | declare module "@morpho-org/blue-sdk" { 5 | namespace MarketParams { 6 | let fetch: typeof fetchMarketParams; 7 | } 8 | } 9 | 10 | MarketParams.fetch = fetchMarketParams; 11 | 12 | export { MarketParams }; 13 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/augment/Position.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AccrualPosition, 3 | Position, 4 | PreLiquidationPosition, 5 | } from "@morpho-org/blue-sdk"; 6 | 7 | import { 8 | fetchAccrualPosition, 9 | fetchPosition, 10 | fetchPreLiquidationPosition, 11 | } from "../fetch"; 12 | 13 | declare module "@morpho-org/blue-sdk" { 14 | namespace Position { 15 | let fetch: typeof fetchPosition; 16 | } 17 | 18 | namespace AccrualPosition { 19 | let fetch: typeof fetchAccrualPosition; 20 | let fetchPreLiquidation: typeof fetchPreLiquidationPosition; 21 | } 22 | } 23 | 24 | Position.fetch = fetchPosition; 25 | AccrualPosition.fetch = fetchAccrualPosition; 26 | AccrualPosition.fetchPreLiquidation = fetchPreLiquidationPosition; 27 | 28 | export { Position, AccrualPosition, PreLiquidationPosition }; 29 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/augment/Token.ts: -------------------------------------------------------------------------------- 1 | import { Token } from "@morpho-org/blue-sdk"; 2 | import { fetchToken } from "../fetch"; 3 | 4 | declare module "@morpho-org/blue-sdk" { 5 | namespace Token { 6 | let fetch: typeof fetchToken; 7 | } 8 | } 9 | 10 | Token.fetch = fetchToken; 11 | 12 | export { Token }; 13 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/augment/User.ts: -------------------------------------------------------------------------------- 1 | import { User } from "@morpho-org/blue-sdk"; 2 | import { fetchUser } from "../fetch"; 3 | 4 | declare module "@morpho-org/blue-sdk" { 5 | namespace User { 6 | let fetch: typeof fetchUser; 7 | } 8 | } 9 | 10 | User.fetch = fetchUser; 11 | 12 | export { User }; 13 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/augment/Vault.ts: -------------------------------------------------------------------------------- 1 | import { AccrualVault, Vault } from "@morpho-org/blue-sdk"; 2 | 3 | import { fetchAccrualVault, fetchVault } from "../fetch"; 4 | 5 | declare module "@morpho-org/blue-sdk" { 6 | namespace Vault { 7 | let fetch: typeof fetchVault; 8 | } 9 | 10 | namespace AccrualVault { 11 | let fetch: typeof fetchAccrualVault; 12 | } 13 | } 14 | 15 | Vault.fetch = fetchVault; 16 | AccrualVault.fetch = fetchAccrualVault; 17 | 18 | export { Vault, AccrualVault }; 19 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/augment/VaultConfig.ts: -------------------------------------------------------------------------------- 1 | import { VaultConfig } from "@morpho-org/blue-sdk"; 2 | import { fetchVaultConfig } from "../fetch"; 3 | 4 | declare module "@morpho-org/blue-sdk" { 5 | namespace VaultConfig { 6 | let fetch: typeof fetchVaultConfig; 7 | } 8 | } 9 | 10 | VaultConfig.fetch = fetchVaultConfig; 11 | 12 | export { VaultConfig }; 13 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/augment/VaultMarketAllocation.ts: -------------------------------------------------------------------------------- 1 | import { VaultMarketAllocation } from "@morpho-org/blue-sdk"; 2 | 3 | import { fetchVaultMarketAllocation } from "../fetch"; 4 | 5 | declare module "@morpho-org/blue-sdk" { 6 | namespace VaultMarketAllocation { 7 | let fetch: typeof fetchVaultMarketAllocation; 8 | } 9 | } 10 | 11 | VaultMarketAllocation.fetch = fetchVaultMarketAllocation; 12 | 13 | export { VaultMarketAllocation }; 14 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/augment/VaultMarketConfig.ts: -------------------------------------------------------------------------------- 1 | import { VaultMarketConfig } from "@morpho-org/blue-sdk"; 2 | 3 | import { fetchVaultMarketConfig } from "../fetch"; 4 | 5 | declare module "@morpho-org/blue-sdk" { 6 | namespace VaultMarketConfig { 7 | let fetch: typeof fetchVaultMarketConfig; 8 | } 9 | } 10 | 11 | VaultMarketConfig.fetch = fetchVaultMarketConfig; 12 | 13 | export { VaultMarketConfig }; 14 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/augment/VaultMarketPublicAllocatorConfig.ts: -------------------------------------------------------------------------------- 1 | import { VaultMarketPublicAllocatorConfig } from "@morpho-org/blue-sdk"; 2 | import { fetchVaultMarketPublicAllocatorConfig } from "../fetch"; 3 | 4 | declare module "@morpho-org/blue-sdk" { 5 | namespace VaultMarketPublicAllocatorConfig { 6 | let fetch: typeof fetchVaultMarketPublicAllocatorConfig; 7 | } 8 | } 9 | 10 | VaultMarketPublicAllocatorConfig.fetch = fetchVaultMarketPublicAllocatorConfig; 11 | 12 | export { VaultMarketPublicAllocatorConfig }; 13 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/augment/VaultUser.ts: -------------------------------------------------------------------------------- 1 | import { VaultUser } from "@morpho-org/blue-sdk"; 2 | 3 | import { fetchVaultUser } from "../fetch"; 4 | 5 | declare module "@morpho-org/blue-sdk" { 6 | namespace VaultUser { 7 | let fetch: typeof fetchVaultUser; 8 | } 9 | } 10 | 11 | VaultUser.fetch = fetchVaultUser; 12 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/augment/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Holding"; 2 | export * from "./Position"; 3 | export * from "./MarketParams"; 4 | export * from "./Market"; 5 | export * from "./Token"; 6 | export * from "./User"; 7 | export * from "./VaultConfig"; 8 | export * from "./Vault"; 9 | export * from "./VaultUser"; 10 | export * from "./VaultMarketConfig"; 11 | export * from "./VaultMarketAllocation"; 12 | export * from "./VaultMarketPublicAllocatorConfig"; 13 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/fetch/MarketParams.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type MarketId, 3 | MarketParams, 4 | UnknownMarketParamsError, 5 | _try, 6 | getChainAddresses, 7 | } from "@morpho-org/blue-sdk"; 8 | import type { Client } from "viem"; 9 | import { getChainId } from "viem/actions"; 10 | import { blueAbi } from "../abis"; 11 | import type { FetchParameters } from "../types"; 12 | import { readContractRestructured } from "../utils"; 13 | 14 | export async function fetchMarketParams( 15 | id: MarketId, 16 | client: Client, 17 | { chainId }: Pick = {}, 18 | ) { 19 | let config = _try(() => MarketParams.get(id), UnknownMarketParamsError); 20 | 21 | if (!config) { 22 | chainId ??= await getChainId(client); 23 | 24 | const { morpho } = getChainAddresses(chainId); 25 | 26 | config = new MarketParams( 27 | await readContractRestructured(client, { 28 | address: morpho, 29 | abi: blueAbi, 30 | functionName: "idToMarketParams", 31 | args: [id], 32 | // Always fetch at latest block because config is immutable. 33 | blockTag: "latest", 34 | }), 35 | ); 36 | } 37 | 38 | return config; 39 | } 40 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/fetch/User.ts: -------------------------------------------------------------------------------- 1 | import type { Address, Client } from "viem"; 2 | 3 | import { User, getChainAddresses } from "@morpho-org/blue-sdk"; 4 | import { getChainId, readContract } from "viem/actions"; 5 | import { blueAbi } from "../abis"; 6 | import type { FetchParameters } from "../types"; 7 | 8 | export async function fetchUser( 9 | address: Address, 10 | client: Client, 11 | parameters: FetchParameters = {}, 12 | ) { 13 | parameters.chainId ??= await getChainId(client); 14 | 15 | const { 16 | morpho, 17 | bundler3: { generalAdapter1 }, 18 | } = getChainAddresses(parameters.chainId); 19 | 20 | const [isBundlerAuthorized, morphoNonce] = await Promise.all([ 21 | readContract(client, { 22 | ...parameters, 23 | address: morpho, 24 | abi: blueAbi, 25 | functionName: "isAuthorized", 26 | args: [address, generalAdapter1], 27 | }), 28 | readContract(client, { 29 | ...parameters, 30 | address: morpho, 31 | abi: blueAbi, 32 | functionName: "nonce", 33 | args: [address], 34 | }), 35 | ]); 36 | 37 | return new User({ 38 | address, 39 | isBundlerAuthorized, 40 | morphoNonce, 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/fetch/VaultConfig.ts: -------------------------------------------------------------------------------- 1 | import type { Address, Client } from "viem"; 2 | 3 | import { VaultConfig } from "@morpho-org/blue-sdk"; 4 | import { getChainId, readContract } from "viem/actions"; 5 | import { metaMorphoAbi } from "../abis"; 6 | import type { DeploylessFetchParameters } from "../types"; 7 | import { fetchToken } from "./Token"; 8 | 9 | export async function fetchVaultConfig( 10 | address: Address, 11 | client: Client, 12 | parameters: DeploylessFetchParameters = {}, 13 | ) { 14 | parameters.chainId ??= await getChainId(client); 15 | 16 | const [token, asset, decimalsOffset] = await Promise.all([ 17 | fetchToken(address, client, parameters), // TODO: avoid fetching decimals 18 | readContract(client, { 19 | ...parameters, 20 | address, 21 | abi: metaMorphoAbi, 22 | functionName: "asset", 23 | }), 24 | readContract(client, { 25 | ...parameters, 26 | address, 27 | abi: metaMorphoAbi, 28 | functionName: "DECIMALS_OFFSET", 29 | }), 30 | ]); 31 | 32 | return new VaultConfig({ 33 | ...token, 34 | asset, 35 | decimalsOffset: BigInt(decimalsOffset), 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/fetch/VaultMarketAllocation.ts: -------------------------------------------------------------------------------- 1 | import type { Address, Client } from "viem"; 2 | 3 | import { type MarketId, VaultMarketAllocation } from "@morpho-org/blue-sdk"; 4 | 5 | import { getChainId } from "viem/actions"; 6 | import type { DeploylessFetchParameters } from "../types"; 7 | import { fetchAccrualPosition } from "./Position"; 8 | import { fetchVaultMarketConfig } from "./VaultMarketConfig"; 9 | 10 | export async function fetchVaultMarketAllocation( 11 | vault: Address, 12 | marketId: MarketId, 13 | client: Client, 14 | parameters: DeploylessFetchParameters = {}, 15 | ) { 16 | parameters.chainId ??= await getChainId(client); 17 | 18 | const [config, position] = await Promise.all([ 19 | fetchVaultMarketConfig(vault, marketId, client, parameters), 20 | fetchAccrualPosition(vault, marketId, client, parameters), 21 | ]); 22 | 23 | return new VaultMarketAllocation({ config, position }); 24 | } 25 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/fetch/VaultMarketConfig.ts: -------------------------------------------------------------------------------- 1 | import type { Address, Client } from "viem"; 2 | 3 | import { type MarketId, VaultMarketConfig } from "@morpho-org/blue-sdk"; 4 | 5 | import { getChainId, readContract } from "viem/actions"; 6 | import { metaMorphoAbi } from "../abis"; 7 | import type { FetchParameters } from "../types"; 8 | import { fetchVaultMarketPublicAllocatorConfig } from "./VaultMarketPublicAllocatorConfig"; 9 | 10 | export async function fetchVaultMarketConfig( 11 | vault: Address, 12 | marketId: MarketId, 13 | client: Client, 14 | parameters: FetchParameters = {}, 15 | ) { 16 | parameters.chainId ??= await getChainId(client); 17 | 18 | const [[cap, enabled, removableAt], pendingCap, publicAllocatorConfig] = 19 | await Promise.all([ 20 | readContract(client, { 21 | ...parameters, 22 | address: vault, 23 | abi: metaMorphoAbi, 24 | functionName: "config", 25 | args: [marketId], 26 | }), 27 | readContract(client, { 28 | ...parameters, 29 | address: vault, 30 | abi: metaMorphoAbi, 31 | functionName: "pendingCap", 32 | args: [marketId], 33 | }).then(([value, validAt]) => ({ value, validAt })), 34 | fetchVaultMarketPublicAllocatorConfig( 35 | vault, 36 | marketId, 37 | client, 38 | parameters, 39 | ), 40 | ]); 41 | 42 | return new VaultMarketConfig({ 43 | vault, 44 | marketId, 45 | cap, 46 | pendingCap, 47 | enabled, 48 | removableAt, 49 | publicAllocatorConfig, 50 | }); 51 | } 52 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/fetch/VaultMarketPublicAllocatorConfig.ts: -------------------------------------------------------------------------------- 1 | import type { Address, Client } from "viem"; 2 | 3 | import { 4 | type MarketId, 5 | VaultMarketPublicAllocatorConfig, 6 | getChainAddresses, 7 | } from "@morpho-org/blue-sdk"; 8 | import { getChainId, readContract } from "viem/actions"; 9 | import { publicAllocatorAbi } from "../abis"; 10 | import type { FetchParameters } from "../types"; 11 | 12 | export async function fetchVaultMarketPublicAllocatorConfig( 13 | vault: Address, 14 | marketId: MarketId, 15 | client: Client, 16 | parameters: FetchParameters = {}, 17 | ) { 18 | parameters.chainId ??= await getChainId(client); 19 | 20 | const { publicAllocator } = getChainAddresses(parameters.chainId); 21 | if (publicAllocator == null) return; 22 | 23 | const [maxIn, maxOut] = await readContract(client, { 24 | ...parameters, 25 | address: publicAllocator, 26 | abi: publicAllocatorAbi, 27 | functionName: "flowCaps", 28 | args: [vault, marketId], 29 | }); 30 | 31 | return new VaultMarketPublicAllocatorConfig({ 32 | vault, 33 | marketId, 34 | maxIn, 35 | maxOut, 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/fetch/VaultUser.ts: -------------------------------------------------------------------------------- 1 | import { type Address, type Client, erc20Abi } from "viem"; 2 | 3 | import { VaultUser } from "@morpho-org/blue-sdk"; 4 | 5 | import { getChainId, readContract } from "viem/actions"; 6 | import type { DeploylessFetchParameters } from "../types"; 7 | 8 | import { metaMorphoAbi } from "../abis"; 9 | import { abi, code } from "../queries/GetVaultUser"; 10 | import { fetchVaultConfig } from "./VaultConfig"; 11 | 12 | export async function fetchVaultUser( 13 | vault: Address, 14 | user: Address, 15 | client: Client, 16 | { deployless = true, ...parameters }: DeploylessFetchParameters = {}, 17 | ) { 18 | parameters.chainId ??= await getChainId(client); 19 | 20 | if (deployless) { 21 | try { 22 | const { isAllocator, allowance } = await readContract(client, { 23 | ...parameters, 24 | abi, 25 | code, 26 | functionName: "query", 27 | args: [vault, user], 28 | }); 29 | 30 | return new VaultUser({ 31 | vault, 32 | user, 33 | isAllocator, 34 | allowance, 35 | }); 36 | } catch { 37 | // Fallback to multicall if deployless call fails. 38 | } 39 | } 40 | 41 | const config = await fetchVaultConfig(vault, client, parameters); 42 | 43 | const [allowance, isAllocator] = await Promise.all([ 44 | readContract(client, { 45 | ...parameters, 46 | address: config.asset, 47 | abi: erc20Abi, 48 | functionName: "allowance", 49 | args: [user, vault], 50 | }), 51 | readContract(client, { 52 | ...parameters, 53 | address: vault, 54 | abi: metaMorphoAbi, 55 | functionName: "isAllocator", 56 | args: [user], 57 | }), 58 | ]); 59 | 60 | return new VaultUser({ 61 | vault, 62 | user, 63 | isAllocator, 64 | allowance, 65 | }); 66 | } 67 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/fetch/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Holding"; 2 | export * from "./Position"; 3 | export * from "./MarketParams"; 4 | export * from "./Market"; 5 | export * from "./Token"; 6 | export * from "./User"; 7 | export * from "./VaultConfig"; 8 | export * from "./Vault"; 9 | export * from "./VaultUser"; 10 | export * from "./VaultMarketConfig"; 11 | export * from "./VaultMarketAllocation"; 12 | export * from "./VaultMarketPublicAllocatorConfig"; 13 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./abis"; 2 | export * from "./fetch"; 3 | export * from "./signatures"; 4 | export * from "./types"; 5 | export * from "./utils"; 6 | export * from "./MetaMorphoAction"; 7 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/queries/index.ts: -------------------------------------------------------------------------------- 1 | export * as GetHolding from "./GetHolding"; 2 | export * as GetMarket from "./GetMarket"; 3 | export * as GetToken from "./GetToken"; 4 | export * as GetVault from "./GetVault"; 5 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/signatures/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./manager"; 2 | export * from "./permit"; 3 | export * from "./permit2"; 4 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/signatures/manager.ts: -------------------------------------------------------------------------------- 1 | import { type ChainId, getChainAddresses } from "@morpho-org/blue-sdk"; 2 | import type { TypedDataDefinition } from "viem"; 3 | 4 | export interface AuthorizationArgs { 5 | authorizer: string; 6 | authorized: string; 7 | isAuthorized: boolean; 8 | nonce: bigint; 9 | deadline: bigint; 10 | } 11 | 12 | const authorizationTypes = { 13 | Authorization: [ 14 | { name: "authorizer", type: "address" }, 15 | { name: "authorized", type: "address" }, 16 | { name: "isAuthorized", type: "bool" }, 17 | { name: "nonce", type: "uint256" }, 18 | { name: "deadline", type: "uint256" }, 19 | ], 20 | }; 21 | 22 | export const getAuthorizationTypedData = ( 23 | { authorizer, authorized, isAuthorized, nonce, deadline }: AuthorizationArgs, 24 | chainId: ChainId, 25 | ): TypedDataDefinition => { 26 | return { 27 | domain: { 28 | chainId: chainId, 29 | verifyingContract: getChainAddresses(chainId).morpho, 30 | }, 31 | types: authorizationTypes, 32 | message: { 33 | authorizer, 34 | authorized, 35 | isAuthorized, 36 | nonce, 37 | deadline, 38 | }, 39 | primaryType: "Authorization", 40 | }; 41 | }; 42 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/src/types.ts: -------------------------------------------------------------------------------- 1 | import type { CallParameters, UnionPick } from "viem"; 2 | 3 | export type FetchParameters = UnionPick< 4 | CallParameters, 5 | "account" | "blockNumber" | "blockTag" | "stateOverride" 6 | > & { 7 | chainId?: number; 8 | }; 9 | 10 | export type DeploylessFetchParameters = FetchParameters & { 11 | deployless?: boolean; 12 | }; 13 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/test/MarketParams.test.ts: -------------------------------------------------------------------------------- 1 | import { zeroAddress } from "viem"; 2 | 3 | import { 4 | ChainId, 5 | type MarketId, 6 | addressesRegistry, 7 | } from "@morpho-org/blue-sdk"; 8 | 9 | import { MarketParams } from "../src/augment/MarketParams"; 10 | import { test } from "./setup"; 11 | 12 | import { markets } from "@morpho-org/morpho-test"; 13 | import { describe, expect } from "vitest"; 14 | 15 | const { usdc_wstEth } = markets[ChainId.EthMainnet]; 16 | 17 | describe("augment/MarketParams", () => { 18 | test("should fetch config from cache", async ({ client }) => { 19 | const market = await MarketParams.fetch(usdc_wstEth.id, client); 20 | 21 | expect(market).toStrictEqual(usdc_wstEth); 22 | }); 23 | 24 | test("should fetch config from chain", async ({ client }) => { 25 | const marketParams = { 26 | collateralToken: zeroAddress, 27 | loanToken: addressesRegistry[ChainId.EthMainnet].wNative, 28 | lltv: 0n, 29 | irm: zeroAddress, 30 | oracle: zeroAddress, 31 | id: "0x58e212060645d18eab6d9b2af3d56fbc906a92ff5667385f616f662c70372284", 32 | liquidationIncentiveFactor: 1150000000000000000n, 33 | }; 34 | 35 | const market = await MarketParams.fetch( 36 | "0x58e212060645d18eab6d9b2af3d56fbc906a92ff5667385f616f662c70372284" as MarketId, 37 | client, 38 | ); 39 | 40 | expect(market).toEqual(marketParams); // Not strict equal because not the same class. 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/test/User.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect } from "vitest"; 2 | import { test } from "./setup"; 3 | 4 | import { ChainId, addressesRegistry } from "@morpho-org/blue-sdk"; 5 | import { blueAbi } from "../src"; 6 | import { User } from "../src/augment/User"; 7 | 8 | const { 9 | morpho, 10 | bundler3: { generalAdapter1 }, 11 | } = addressesRegistry[ChainId.EthMainnet]; 12 | 13 | describe("augment/User", () => { 14 | test("should fetch user data", async ({ client }) => { 15 | await client.writeContract({ 16 | address: morpho, 17 | abi: blueAbi, 18 | functionName: "setAuthorization", 19 | args: [generalAdapter1, true], 20 | }); 21 | 22 | const expectedData = new User({ 23 | address: client.account.address, 24 | isBundlerAuthorized: true, 25 | morphoNonce: 0n, 26 | }); 27 | 28 | const value = await User.fetch(client.account.address, client); 29 | 30 | expect(value).toStrictEqual(expectedData); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/test/setup.ts: -------------------------------------------------------------------------------- 1 | import { createViemTest } from "@morpho-org/test/vitest"; 2 | import { mainnet } from "viem/chains"; 3 | 4 | /** 5 | * This test will run on `mainnet` forked at block `19,530,000`. 6 | */ 7 | export const test = createViemTest(mainnet, { 8 | forkUrl: process.env.MAINNET_RPC_URL, 9 | forkBlockNumber: 19_530_000, 10 | }); 11 | 12 | /** 13 | * This test will run on `mainnet` forked at block `21,595,000`. 14 | */ 15 | export const test2 = createViemTest(mainnet, { 16 | forkUrl: process.env.MAINNET_RPC_URL, 17 | forkBlockNumber: 21_595_000, 18 | }); 19 | 20 | /** 21 | * This test will run on `mainnet` forked at block `21,950,00`. 22 | */ 23 | export const preLiquidationTest = createViemTest(mainnet, { 24 | forkUrl: process.env.MAINNET_RPC_URL, 25 | forkBlockNumber: 21_950_000, 26 | }); 27 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src" 5 | }, 6 | "include": ["src"] 7 | } 8 | -------------------------------------------------------------------------------- /packages/blue-sdk-viem/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": ".", 6 | "baseUrl": "." 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/blue-sdk-wagmi/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./useChainId.js"; 2 | export * from "./useMarket.js"; 3 | export * from "./useMarketParams.js"; 4 | export * from "./useToken.js"; 5 | export * from "./useUser.js"; 6 | export * from "./useVault.js"; 7 | export * from "./useVaultUser.js"; 8 | export * from "./useVaultConfig.js"; 9 | export * from "./usePosition.js"; 10 | export * from "./useHolding.js"; 11 | export * from "./useVaultMarketConfig.js"; 12 | export * from "./useMarkets.js"; 13 | export * from "./useMarketsParams.js"; 14 | export * from "./useTokens.js"; 15 | export * from "./useUsers.js"; 16 | export * from "./useVaults.js"; 17 | export * from "./useVaultUsers.js"; 18 | export * from "./useVaultConfigs.js"; 19 | export * from "./usePositions.js"; 20 | export * from "./useHoldings.js"; 21 | export * from "./useVaultMarketConfigs.js"; 22 | export * from "./useReadContracts.js"; 23 | -------------------------------------------------------------------------------- /packages/blue-sdk-wagmi/src/hooks/useChainId.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type Config, 3 | type ResolvedRegister, 4 | useChainId as wagmi_useChainId, 5 | } from "wagmi"; 6 | import type { ConfigParameter } from "../types/index.js"; 7 | 8 | export type UseChainIdParameters = 9 | ConfigParameter & { 10 | chainId?: config["chains"][number]["id"]; 11 | }; 12 | 13 | export function useChainId( 14 | parameters?: UseChainIdParameters, 15 | ) { 16 | const wagmiChainId = wagmi_useChainId(parameters); 17 | 18 | return parameters?.chainId ?? wagmiChainId; 19 | } 20 | -------------------------------------------------------------------------------- /packages/blue-sdk-wagmi/src/hooks/useMarket.ts: -------------------------------------------------------------------------------- 1 | import type { Market } from "@morpho-org/blue-sdk"; 2 | import type { ReadContractErrorType } from "viem"; 3 | import { type Config, type ResolvedRegister, useConfig } from "wagmi"; 4 | import { type UseQueryReturnType, useQuery } from "wagmi/query"; 5 | import { 6 | type FetchMarketParameters, 7 | type FetchMarketQueryKey, 8 | fetchMarketQueryOptions, 9 | } from "../queries/fetchMarket.js"; 10 | import type { ConfigParameter, QueryParameter } from "../types/index.js"; 11 | import { replaceDeepEqual } from "../utils/index.js"; 12 | import { useChainId } from "./useChainId.js"; 13 | 14 | export type UseMarketParameters< 15 | config extends Config = Config, 16 | selectData = Market, 17 | > = FetchMarketParameters & 18 | ConfigParameter & 19 | QueryParameter< 20 | Market, 21 | ReadContractErrorType, 22 | selectData, 23 | FetchMarketQueryKey 24 | >; 25 | 26 | export type UseMarketReturnType = UseQueryReturnType< 27 | selectData, 28 | ReadContractErrorType 29 | >; 30 | 31 | export function useMarket< 32 | config extends Config = ResolvedRegister["config"], 33 | selectData = Market, 34 | >({ 35 | query = {}, 36 | ...parameters 37 | }: UseMarketParameters): UseMarketReturnType { 38 | const config = useConfig(parameters); 39 | const chainId = useChainId(parameters); 40 | 41 | const options = fetchMarketQueryOptions(config, { 42 | ...parameters, 43 | chainId, 44 | }); 45 | 46 | return useQuery({ 47 | ...query, 48 | ...options, 49 | enabled: parameters.marketId != null && query.enabled, 50 | structuralSharing: query.structuralSharing ?? replaceDeepEqual, 51 | staleTime: 52 | query.staleTime ?? 53 | (parameters.blockNumber != null ? Number.POSITIVE_INFINITY : undefined), 54 | }); 55 | } 56 | -------------------------------------------------------------------------------- /packages/blue-sdk-wagmi/src/hooks/useMarketParams.ts: -------------------------------------------------------------------------------- 1 | import type { MarketParams } from "@morpho-org/blue-sdk"; 2 | import type { ReadContractErrorType } from "viem"; 3 | import { type Config, type ResolvedRegister, useConfig } from "wagmi"; 4 | import { type UseQueryReturnType, useQuery } from "wagmi/query"; 5 | import { 6 | type FetchMarketParamsParameters, 7 | type FetchMarketParamsQueryKey, 8 | fetchMarketParamsQueryOptions, 9 | } from "../queries/fetchMarketParams.js"; 10 | import type { ConfigParameter, QueryParameter } from "../types/index.js"; 11 | import { replaceDeepEqual } from "../utils/index.js"; 12 | import { useChainId } from "./useChainId.js"; 13 | 14 | export type UseMarketParamsParameters< 15 | config extends Config = Config, 16 | selectData = MarketParams, 17 | > = FetchMarketParamsParameters & 18 | ConfigParameter & 19 | QueryParameter< 20 | MarketParams, 21 | ReadContractErrorType, 22 | selectData, 23 | FetchMarketParamsQueryKey 24 | >; 25 | 26 | export type UseMarketParamsReturnType = 27 | UseQueryReturnType; 28 | 29 | export function useMarketParams< 30 | config extends Config = ResolvedRegister["config"], 31 | selectData = MarketParams, 32 | >({ 33 | query = {}, 34 | ...parameters 35 | }: UseMarketParamsParameters< 36 | config, 37 | selectData 38 | >): UseMarketParamsReturnType { 39 | const config = useConfig(parameters); 40 | const chainId = useChainId(parameters); 41 | 42 | const options = fetchMarketParamsQueryOptions(config, { 43 | ...parameters, 44 | chainId, 45 | }); 46 | 47 | return useQuery({ 48 | ...query, 49 | ...options, 50 | enabled: parameters.marketId != null && query.enabled, 51 | structuralSharing: query.structuralSharing ?? replaceDeepEqual, 52 | }); 53 | } 54 | -------------------------------------------------------------------------------- /packages/blue-sdk-wagmi/src/hooks/useToken.ts: -------------------------------------------------------------------------------- 1 | import type { Token } from "@morpho-org/blue-sdk"; 2 | import type { ReadContractErrorType } from "viem"; 3 | import { type Config, type ResolvedRegister, useConfig } from "wagmi"; 4 | import { type UseQueryReturnType, useQuery } from "wagmi/query"; 5 | import { 6 | type FetchTokenParameters, 7 | type FetchTokenQueryKey, 8 | fetchTokenQueryOptions, 9 | } from "../queries/fetchToken.js"; 10 | import type { ConfigParameter, QueryParameter } from "../types/index.js"; 11 | import { replaceDeepEqual } from "../utils/index.js"; 12 | import { useChainId } from "./useChainId.js"; 13 | 14 | export type UseTokenParameters< 15 | config extends Config = Config, 16 | selectData = Token, 17 | > = FetchTokenParameters & 18 | ConfigParameter & 19 | QueryParameter; 20 | 21 | export type UseTokenReturnType = UseQueryReturnType< 22 | selectData, 23 | ReadContractErrorType 24 | >; 25 | 26 | export function useToken< 27 | config extends Config = ResolvedRegister["config"], 28 | selectData = Token, 29 | >({ 30 | query = {}, 31 | ...parameters 32 | }: UseTokenParameters): UseTokenReturnType { 33 | const config = useConfig(parameters); 34 | const chainId = useChainId(parameters); 35 | 36 | const options = fetchTokenQueryOptions(config, { 37 | ...parameters, 38 | chainId, 39 | }); 40 | 41 | return useQuery({ 42 | ...query, 43 | ...options, 44 | enabled: parameters.token != null && query.enabled, 45 | structuralSharing: query.structuralSharing ?? replaceDeepEqual, 46 | staleTime: 47 | query.staleTime ?? 48 | (parameters.blockNumber != null ? Number.POSITIVE_INFINITY : undefined), 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /packages/blue-sdk-wagmi/src/hooks/useUser.ts: -------------------------------------------------------------------------------- 1 | import type { User } from "@morpho-org/blue-sdk"; 2 | import type { ReadContractErrorType } from "viem"; 3 | import { type Config, type ResolvedRegister, useConfig } from "wagmi"; 4 | import { type UseQueryReturnType, useQuery } from "wagmi/query"; 5 | import { 6 | type FetchUserParameters, 7 | type FetchUserQueryKey, 8 | fetchUserQueryOptions, 9 | } from "../queries/fetchUser.js"; 10 | import type { ConfigParameter, QueryParameter } from "../types/index.js"; 11 | import { replaceDeepEqual } from "../utils/index.js"; 12 | import { useChainId } from "./useChainId.js"; 13 | 14 | export type UseUserParameters< 15 | config extends Config = Config, 16 | selectData = User, 17 | > = FetchUserParameters & 18 | ConfigParameter & 19 | QueryParameter; 20 | 21 | export type UseUserReturnType = UseQueryReturnType< 22 | selectData, 23 | ReadContractErrorType 24 | >; 25 | 26 | export function useUser< 27 | config extends Config = ResolvedRegister["config"], 28 | selectData = User, 29 | >({ 30 | query = {}, 31 | ...parameters 32 | }: UseUserParameters): UseUserReturnType { 33 | const config = useConfig(parameters); 34 | const chainId = useChainId(parameters); 35 | 36 | const options = fetchUserQueryOptions(config, { 37 | ...parameters, 38 | chainId, 39 | }); 40 | 41 | return useQuery({ 42 | ...query, 43 | ...options, 44 | enabled: parameters.user != null && query.enabled, 45 | structuralSharing: query.structuralSharing ?? replaceDeepEqual, 46 | staleTime: 47 | query.staleTime ?? 48 | (parameters.blockNumber != null ? Number.POSITIVE_INFINITY : undefined), 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /packages/blue-sdk-wagmi/src/hooks/useVault.ts: -------------------------------------------------------------------------------- 1 | import type { Vault } from "@morpho-org/blue-sdk"; 2 | import type { ReadContractErrorType } from "viem"; 3 | import { type Config, type ResolvedRegister, useConfig } from "wagmi"; 4 | import { type UseQueryReturnType, useQuery } from "wagmi/query"; 5 | import { 6 | type FetchVaultParameters, 7 | type FetchVaultQueryKey, 8 | fetchVaultQueryOptions, 9 | } from "../queries/fetchVault.js"; 10 | import type { ConfigParameter, QueryParameter } from "../types/index.js"; 11 | import { replaceDeepEqual } from "../utils/index.js"; 12 | import { useChainId } from "./useChainId.js"; 13 | 14 | export type UseVaultParameters< 15 | config extends Config = Config, 16 | selectData = Vault, 17 | > = FetchVaultParameters & 18 | ConfigParameter & 19 | QueryParameter; 20 | 21 | export type UseVaultReturnType = UseQueryReturnType< 22 | selectData, 23 | ReadContractErrorType 24 | >; 25 | 26 | export function useVault< 27 | config extends Config = ResolvedRegister["config"], 28 | selectData = Vault, 29 | >({ 30 | query = {}, 31 | ...parameters 32 | }: UseVaultParameters): UseVaultReturnType { 33 | const config = useConfig(parameters); 34 | const chainId = useChainId(parameters); 35 | 36 | const options = fetchVaultQueryOptions(config, { 37 | ...parameters, 38 | chainId, 39 | }); 40 | 41 | return useQuery({ 42 | ...query, 43 | ...options, 44 | enabled: parameters.vault != null && query.enabled, 45 | structuralSharing: query.structuralSharing ?? replaceDeepEqual, 46 | staleTime: 47 | query.staleTime ?? 48 | (parameters.blockNumber != null ? Number.POSITIVE_INFINITY : undefined), 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /packages/blue-sdk-wagmi/src/hooks/useVaultConfig.ts: -------------------------------------------------------------------------------- 1 | import type { VaultConfig } from "@morpho-org/blue-sdk"; 2 | import type { ReadContractErrorType } from "viem"; 3 | import { type Config, type ResolvedRegister, useConfig } from "wagmi"; 4 | import { type UseQueryReturnType, useQuery } from "wagmi/query"; 5 | import { replaceDeepEqual } from "../utils/index.js"; 6 | 7 | import { 8 | type FetchVaultConfigParameters, 9 | type FetchVaultConfigQueryKey, 10 | fetchVaultConfigQueryOptions, 11 | } from "../queries/fetchVaultConfig.js"; 12 | import type { ConfigParameter, QueryParameter } from "../types/index.js"; 13 | import { useChainId } from "./useChainId.js"; 14 | 15 | export type UseVaultConfigParameters< 16 | config extends Config = Config, 17 | selectData = VaultConfig, 18 | > = FetchVaultConfigParameters & 19 | ConfigParameter & 20 | QueryParameter< 21 | VaultConfig, 22 | ReadContractErrorType, 23 | selectData, 24 | FetchVaultConfigQueryKey 25 | >; 26 | 27 | export type UseVaultConfigReturnType = 28 | UseQueryReturnType; 29 | 30 | export function useVaultConfig< 31 | config extends Config = ResolvedRegister["config"], 32 | selectData = VaultConfig, 33 | >({ 34 | query = {}, 35 | ...parameters 36 | }: UseVaultConfigParameters< 37 | config, 38 | selectData 39 | >): UseVaultConfigReturnType { 40 | const config = useConfig(parameters); 41 | const chainId = useChainId(parameters); 42 | 43 | const options = fetchVaultConfigQueryOptions(config, { 44 | ...parameters, 45 | chainId, 46 | }); 47 | 48 | return useQuery({ 49 | ...query, 50 | ...options, 51 | enabled: parameters.vault != null && query.enabled, 52 | structuralSharing: query.structuralSharing ?? replaceDeepEqual, 53 | }); 54 | } 55 | -------------------------------------------------------------------------------- /packages/blue-sdk-wagmi/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./hooks/index.js"; 2 | export * from "./queries/index.js"; 3 | export * from "./types/index.js"; 4 | export * from "./utils/index.js"; 5 | -------------------------------------------------------------------------------- /packages/blue-sdk-wagmi/src/queries/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./fetchMarket.js"; 2 | export * from "./fetchMarketParams.js"; 3 | export * from "./fetchToken.js"; 4 | export * from "./fetchUser.js"; 5 | export * from "./fetchVault.js"; 6 | export * from "./fetchVaultConfig.js"; 7 | export * from "./fetchVaultUser.js"; 8 | export * from "./fetchPosition.js"; 9 | export * from "./fetchHolding.js"; 10 | export * from "./fetchVaultMarketConfig.js"; 11 | -------------------------------------------------------------------------------- /packages/blue-sdk-wagmi/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./properties.js"; 2 | -------------------------------------------------------------------------------- /packages/blue-sdk-wagmi/src/types/properties.ts: -------------------------------------------------------------------------------- 1 | import type { DefaultError, QueryKey } from "@tanstack/react-query"; 2 | import type { Config } from "wagmi"; 3 | import type { UseQueryParameters, UseQueryReturnType } from "wagmi/query"; 4 | 5 | export type ConfigParameter = { 6 | config?: Config | config | undefined; 7 | }; 8 | 9 | export type QueryParameter< 10 | queryFnData = unknown, 11 | error = DefaultError, 12 | data = queryFnData, 13 | queryKey extends QueryKey = QueryKey, 14 | > = { 15 | query?: 16 | | Omit< 17 | UseQueryParameters, 18 | "queryFn" | "queryHash" | "queryKey" | "queryKeyHashFn" | "throwOnError" 19 | > 20 | | undefined; 21 | }; 22 | 23 | export type IterableElement = T extends Iterable ? U : never; 24 | 25 | export interface UseIndexedQueriesReturnType< 26 | Index extends PropertyKey, 27 | QueryReturnType extends UseQueryReturnType, 28 | > { 29 | data: Record; 30 | error: Record; 31 | isFetching: Record; 32 | isFetchingAny: boolean; 33 | } 34 | 35 | export interface UseCompositeQueriesReturnType< 36 | Index1 extends PropertyKey, 37 | Index2 extends PropertyKey, 38 | QueryReturnType extends UseQueryReturnType, 39 | > { 40 | data: Record>; 41 | error: Record>; 42 | isFetching: Record>; 43 | isFetchingAny: boolean; 44 | } 45 | -------------------------------------------------------------------------------- /packages/blue-sdk-wagmi/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./replaceDeepEqual.js"; 2 | export * from "./uniqBy.js"; 3 | -------------------------------------------------------------------------------- /packages/blue-sdk-wagmi/src/utils/uniqBy.ts: -------------------------------------------------------------------------------- 1 | export const uniqBy = ( 2 | iterable: Iterable, 3 | iteratee: (value: T) => string, 4 | ): T[] => { 5 | const uniqueKeys = new Set(); 6 | 7 | const uniques: T[] = []; 8 | 9 | for (const value of iterable) { 10 | const key = iteratee(value); 11 | if (uniqueKeys.has(key)) continue; 12 | 13 | uniqueKeys.add(key); 14 | 15 | uniques.push(value); 16 | } 17 | 18 | return uniques; 19 | }; 20 | -------------------------------------------------------------------------------- /packages/blue-sdk-wagmi/test/e2e/setup.ts: -------------------------------------------------------------------------------- 1 | import { createWagmiTest } from "@morpho-org/test-wagmi"; 2 | import { mainnet } from "viem/chains"; 3 | 4 | export const test = createWagmiTest(mainnet, { 5 | forkUrl: process.env.MAINNET_RPC_URL, 6 | forkBlockNumber: 19_530_000, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/blue-sdk-wagmi/test/e2e/useChainId.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect } from "vitest"; 2 | 3 | import { ChainId } from "@morpho-org/blue-sdk"; 4 | import { renderHook } from "@morpho-org/test-wagmi"; 5 | import { useChainId } from "../../src/index.js"; 6 | import { test } from "./setup.js"; 7 | 8 | describe("useChainId", () => { 9 | test("should render", async ({ config }) => { 10 | const { result } = await renderHook(config, () => useChainId()); 11 | 12 | expect(result.current).toEqual(ChainId.EthMainnet); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /packages/blue-sdk-wagmi/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src" 5 | }, 6 | "include": ["src"] 7 | } 8 | -------------------------------------------------------------------------------- /packages/blue-sdk-wagmi/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": ".", 6 | "baseUrl": "." 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/blue-sdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@morpho-org/blue-sdk", 3 | "description": "Framework-agnostic package that defines Morpho-related entity classes (such as `Market`, `Token`, `Vault`).", 4 | "version": "2.0.0", 5 | "author": "Morpho Association ", 6 | "contributors": ["Rubilmax "], 7 | "repository": "github:morpho-org/sdks", 8 | "homepage": "https://github.com/morpho-org/sdks", 9 | "bugs": { 10 | "url": "https://github.com/morpho-org/sdks/issues", 11 | "email": "contact@morpho.org" 12 | }, 13 | "license": "MIT", 14 | "main": "src/index.ts", 15 | "files": ["lib"], 16 | "scripts": { 17 | "prepublish": "$npm_execpath build", 18 | "build": "tsc --noEmit && tsc --build tsconfig.build.json", 19 | "test": "vitest" 20 | }, 21 | "dependencies": { 22 | "@noble/hashes": "^1.6.1" 23 | }, 24 | "peerDependencies": { 25 | "@morpho-org/morpho-ts": "workspace:^" 26 | }, 27 | "devDependencies": { 28 | "@morpho-org/morpho-ts": "workspace:^", 29 | "@morpho-org/test": "workspace:^", 30 | "typescript": "^5.7.2", 31 | "viem": "^2.23.0", 32 | "vitest": "^3.0.5" 33 | }, 34 | "publishConfig": { 35 | "main": "lib/index.js", 36 | "types": "lib/index.d.ts" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/blue-sdk/src/constants.ts: -------------------------------------------------------------------------------- 1 | import { Time } from "@morpho-org/morpho-ts"; 2 | 3 | /** 4 | * The liquidation cursor used to calculate the liquidation incentive. Hardcoded to 30%. 5 | */ 6 | export const LIQUIDATION_CURSOR = 30_0000000000000000n; 7 | 8 | /** 9 | * The maximum liquidation incentive factor. Hardcoded to 115%. 10 | */ 11 | export const MAX_LIQUIDATION_INCENTIVE_FACTOR = 1_150000000000000000n; 12 | 13 | /** 14 | * The scale of the oracle price. Hardcoded to 1e36. 15 | */ 16 | export const ORACLE_PRICE_SCALE = 1_000000000000000000000000000000000000n; 17 | 18 | /** 19 | * The default slippage tolerance used in the SDK. Hardcoded to 0.03%. 20 | */ 21 | export const DEFAULT_SLIPPAGE_TOLERANCE = 3_00000000000000n; 22 | 23 | /** 24 | * The number of seconds in a year. 25 | */ 26 | export const SECONDS_PER_YEAR = Time.s.from.y(1n); 27 | -------------------------------------------------------------------------------- /packages/blue-sdk/src/holding/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Holding.js"; 2 | export * from "./AssetBalances.js"; 3 | -------------------------------------------------------------------------------- /packages/blue-sdk/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./addresses.js"; 2 | export * from "./constants.js"; 3 | export * from "./errors.js"; 4 | export * from "./market/index.js"; 5 | export * from "./chain.js"; 6 | export * from "./token/index.js"; 7 | export * from "./types.js"; 8 | export * from "./math/index.js"; 9 | export * from "./user/index.js"; 10 | export * from "./holding/index.js"; 11 | export * from "./position/index.js"; 12 | export * from "./vault/index.js"; 13 | export * from "./preLiquidation.js"; 14 | -------------------------------------------------------------------------------- /packages/blue-sdk/src/market/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./MarketUtils.js"; 2 | export * from "./MarketParams.js"; 3 | export * from "./Market.js"; 4 | -------------------------------------------------------------------------------- /packages/blue-sdk/src/math/SharesMath.ts: -------------------------------------------------------------------------------- 1 | import type { BigIntish } from "../types.js"; 2 | 3 | import { MathLib, type RoundingDirection } from "./MathLib.js"; 4 | 5 | /** 6 | * JS implementation of {@link https://github.com/morpho-org/morpho-blue/blob/main/src/libraries/SharesMathLib.sol SharesMathLib} used by Morpho Blue 7 | * & MetaMorpho (via {@link https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/ERC4626.sol ERC4626}). 8 | */ 9 | export namespace SharesMath { 10 | export const VIRTUAL_SHARES = 1000000n; 11 | export const VIRTUAL_ASSETS = 1n; 12 | 13 | export function toAssets( 14 | shares: BigIntish, 15 | totalAssets: BigIntish, 16 | totalShares: BigIntish, 17 | rounding: RoundingDirection, 18 | ) { 19 | return MathLib.mulDiv( 20 | shares, 21 | BigInt(totalAssets) + VIRTUAL_ASSETS, 22 | BigInt(totalShares) + VIRTUAL_SHARES, 23 | rounding, 24 | ); 25 | } 26 | 27 | export function toShares( 28 | assets: BigIntish, 29 | totalAssets: BigIntish, 30 | totalShares: BigIntish, 31 | rounding: RoundingDirection, 32 | ) { 33 | return MathLib.mulDiv( 34 | assets, 35 | BigInt(totalShares) + VIRTUAL_SHARES, 36 | BigInt(totalAssets) + VIRTUAL_ASSETS, 37 | rounding, 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/blue-sdk/src/math/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./MathLib.js"; 2 | export * from "./SharesMath.js"; 3 | export * from "./AdaptiveCurveIrmLib.js"; 4 | -------------------------------------------------------------------------------- /packages/blue-sdk/src/position/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Position.js"; 2 | export * from "./PreLiquidationPosition.js"; 3 | -------------------------------------------------------------------------------- /packages/blue-sdk/src/token/ExchangeRateWrappedToken.ts: -------------------------------------------------------------------------------- 1 | import { MathLib, type RoundingDirection } from "../math/index.js"; 2 | import type { Address } from "../types.js"; 3 | 4 | import type { IToken } from "./Token.js"; 5 | import { WrappedToken } from "./WrappedToken.js"; 6 | 7 | export class ExchangeRateWrappedToken extends WrappedToken { 8 | constructor( 9 | token: IToken, 10 | readonly underlying: Address, 11 | public wrappedTokenExchangeRate: bigint, 12 | ) { 13 | super(token, underlying); 14 | } 15 | 16 | protected _wrap(amount: bigint, rounding: RoundingDirection) { 17 | return MathLib.wDiv(amount, this.wrappedTokenExchangeRate, rounding); 18 | } 19 | 20 | protected _unwrap(amount: bigint, rounding: RoundingDirection) { 21 | return MathLib.wMul(amount, this.wrappedTokenExchangeRate, rounding); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/blue-sdk/src/token/VaultToken.ts: -------------------------------------------------------------------------------- 1 | import type { RoundingDirection } from "../math/index.js"; 2 | import type { Address } from "../types.js"; 3 | import type { IVaultConfig } from "../vault/VaultConfig.js"; 4 | import { VaultUtils } from "../vault/VaultUtils.js"; 5 | import { WrappedToken } from "./WrappedToken.js"; 6 | 7 | export interface IVaultToken { 8 | totalAssets: bigint; 9 | totalSupply: bigint; 10 | } 11 | 12 | export class VaultToken extends WrappedToken implements IVaultToken { 13 | public readonly asset: Address; 14 | public readonly decimalsOffset: bigint; 15 | 16 | /** 17 | * The ERC4626 vault's total supply of shares. 18 | */ 19 | public totalSupply: bigint; 20 | 21 | /** 22 | * The ERC4626 vault's total assets. 23 | */ 24 | public totalAssets: bigint; 25 | 26 | constructor(config: IVaultConfig, { totalAssets, totalSupply }: IVaultToken) { 27 | super(config, config.asset); 28 | 29 | this.asset = config.asset; 30 | 31 | this.totalAssets = totalAssets; 32 | this.totalSupply = totalSupply; 33 | this.decimalsOffset = BigInt(config.decimalsOffset); 34 | } 35 | 36 | protected _wrap(amount: bigint, rounding?: RoundingDirection) { 37 | return VaultUtils.toShares(amount, this, rounding); 38 | } 39 | 40 | protected _unwrap(amount: bigint, rounding?: RoundingDirection) { 41 | return VaultUtils.toAssets(amount, this, rounding); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/blue-sdk/src/token/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Token.js"; 2 | export * from "./WrappedToken.js"; 3 | export * from "./ConstantWrappedToken.js"; 4 | export * from "./ExchangeRateWrappedToken.js"; 5 | export * from "./VaultToken.js"; 6 | export * from "./Eip5267Domain.js"; 7 | -------------------------------------------------------------------------------- /packages/blue-sdk/src/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The address of a Contract, or an EOA 3 | */ 4 | export type Address = `0x${string}`; 5 | 6 | /** 7 | * The id of a market used on the Blue contract 8 | */ 9 | export type MarketId = `0x${string}` & { __TYPE__: "marketId" }; 10 | 11 | export type BigIntish = bigint | string | number | boolean; 12 | 13 | /** 14 | * The possible transaction type on the Blue contract 15 | */ 16 | export enum TransactionType { 17 | Supply = "Supply", 18 | SupplyCollateral = "Supply Collateral", 19 | Withdraw = "Withdraw", 20 | WithdrawCollateral = "Withdraw Collateral", 21 | Borrow = "Borrow", 22 | Repay = "Repay", 23 | } 24 | 25 | export type Loadable = T | undefined; 26 | export type Failable = T | null; 27 | export type Fetchable = Failable>; 28 | 29 | // TODO: replace with isDefined 30 | export function isFetched(v: Fetchable): v is T { 31 | return v !== undefined && v !== null; 32 | } 33 | 34 | export const isMarketId = (value: unknown): value is MarketId => 35 | typeof value === "string" && /^0x[0-9A-Fa-f]{64}$/.test(value); 36 | -------------------------------------------------------------------------------- /packages/blue-sdk/src/user/User.ts: -------------------------------------------------------------------------------- 1 | import type { Address } from "../types.js"; 2 | 3 | export class User { 4 | /** 5 | * The user's address. 6 | */ 7 | public readonly address: Address; 8 | 9 | /** 10 | * Whether the bundler is authorized to manage the user's position on Morpho Blue. 11 | */ 12 | public isBundlerAuthorized: boolean; 13 | 14 | /** 15 | * The user's nonce on Morpho Blue. 16 | */ 17 | public morphoNonce: bigint; 18 | 19 | constructor({ 20 | address, 21 | isBundlerAuthorized, 22 | morphoNonce, 23 | }: { 24 | address: Address; 25 | isBundlerAuthorized: boolean; 26 | morphoNonce: bigint; 27 | }) { 28 | this.address = address; 29 | this.isBundlerAuthorized = isBundlerAuthorized; 30 | this.morphoNonce = morphoNonce; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/blue-sdk/src/user/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./User.js"; 2 | -------------------------------------------------------------------------------- /packages/blue-sdk/src/vault/VaultConfig.ts: -------------------------------------------------------------------------------- 1 | import { type IToken, Token } from "../token/Token.js"; 2 | import type { Address, BigIntish } from "../types.js"; 3 | 4 | export interface IVaultConfig extends Omit { 5 | decimalsOffset: BigIntish; 6 | asset: Address; 7 | } 8 | 9 | export class VaultConfig extends Token implements IVaultConfig { 10 | public readonly decimalsOffset; 11 | public readonly asset; 12 | 13 | constructor({ decimalsOffset, asset, ...config }: IVaultConfig) { 14 | super({ ...config, decimals: 18 }); 15 | 16 | this.decimalsOffset = BigInt(decimalsOffset); 17 | this.asset = asset; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/blue-sdk/src/vault/VaultMarketAllocation.ts: -------------------------------------------------------------------------------- 1 | import { MathLib } from "../math/index.js"; 2 | import type { AccrualPosition } from "../position/index.js"; 3 | 4 | import type { VaultMarketConfig } from "./VaultMarketConfig.js"; 5 | 6 | export interface IVaultMarketAllocation { 7 | config: VaultMarketConfig; 8 | position: AccrualPosition; 9 | } 10 | 11 | export class VaultMarketAllocation implements IVaultMarketAllocation { 12 | /** 13 | * The vault's configuration on the corresponding market. 14 | */ 15 | public readonly config: VaultMarketConfig; 16 | 17 | /** 18 | * The vault's position on the corresponding market. 19 | */ 20 | public readonly position: AccrualPosition; 21 | 22 | constructor({ config, position }: IVaultMarketAllocation) { 23 | this.config = config; 24 | this.position = position; 25 | } 26 | 27 | get vault() { 28 | return this.config.vault; 29 | } 30 | 31 | get marketId() { 32 | return this.config.marketId; 33 | } 34 | 35 | get utilization() { 36 | if (this.config.cap === 0n) return MathLib.MAX_UINT_256; 37 | 38 | return MathLib.wDivDown(this.position.supplyAssets, this.config.cap); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/blue-sdk/src/vault/VaultMarketPublicAllocatorConfig.ts: -------------------------------------------------------------------------------- 1 | import type { Address, MarketId } from "../types.js"; 2 | 3 | /** 4 | * The vault's configuration of a market on the PublicAllocator. 5 | */ 6 | export interface IVaultMarketPublicAllocatorConfig { 7 | vault: Address; 8 | marketId: MarketId; 9 | maxIn: bigint; 10 | maxOut: bigint; 11 | } 12 | 13 | export class VaultMarketPublicAllocatorConfig 14 | implements IVaultMarketPublicAllocatorConfig 15 | { 16 | /** 17 | * The vault's address. 18 | */ 19 | public readonly vault: Address; 20 | 21 | /** 22 | * The market's id. 23 | */ 24 | public readonly marketId: MarketId; 25 | 26 | /** 27 | * The maximum amount of tokens that can be allocated to this market by the vault via the PublicAllocator. 28 | */ 29 | public maxIn: bigint; 30 | 31 | /** 32 | * The maximum amount of tokens that can be allocated out of this market by the vault via the PublicAllocator. 33 | */ 34 | public maxOut: bigint; 35 | 36 | constructor({ 37 | vault, 38 | marketId, 39 | maxIn, 40 | maxOut, 41 | }: IVaultMarketPublicAllocatorConfig) { 42 | this.vault = vault; 43 | this.marketId = marketId; 44 | this.maxIn = maxIn; 45 | this.maxOut = maxOut; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/blue-sdk/src/vault/VaultUser.ts: -------------------------------------------------------------------------------- 1 | import type { Address } from "../types.js"; 2 | 3 | export interface IVaultUser { 4 | vault: Address; 5 | user: Address; 6 | isAllocator: boolean; 7 | allowance: bigint; 8 | } 9 | 10 | export class VaultUser implements IVaultUser { 11 | /** 12 | * The vault's address. 13 | */ 14 | public readonly vault: Address; 15 | 16 | /** 17 | * The user's address. 18 | */ 19 | public readonly user: Address; 20 | 21 | /** 22 | * Whether the user is an allocator of the vault. 23 | */ 24 | public isAllocator: boolean; 25 | 26 | /** 27 | * The allowance of the vault over the user's underlying assets. 28 | */ 29 | public allowance: bigint; 30 | 31 | constructor({ vault, user, isAllocator, allowance }: IVaultUser) { 32 | this.vault = vault; 33 | this.user = user; 34 | this.isAllocator = isAllocator; 35 | this.allowance = allowance; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/blue-sdk/src/vault/VaultUtils.ts: -------------------------------------------------------------------------------- 1 | import { MathLib, type RoundingDirection } from "../math/index.js"; 2 | import type { BigIntish } from "../types.js"; 3 | 4 | export namespace VaultUtils { 5 | export const VIRTUAL_ASSETS = 1n; 6 | 7 | export function decimalsOffset(decimals: BigIntish) { 8 | return MathLib.zeroFloorSub(18n, decimals); 9 | } 10 | 11 | export function toAssets( 12 | shares: BigIntish, 13 | { 14 | totalAssets, 15 | totalSupply, 16 | decimalsOffset, 17 | }: { 18 | totalAssets: BigIntish; 19 | totalSupply: BigIntish; 20 | decimalsOffset: BigIntish; 21 | }, 22 | rounding: RoundingDirection = "Down", 23 | ) { 24 | return MathLib.mulDiv( 25 | shares, 26 | BigInt(totalAssets) + VIRTUAL_ASSETS, 27 | BigInt(totalSupply) + 10n ** BigInt(decimalsOffset), 28 | rounding, 29 | ); 30 | } 31 | 32 | export function toShares( 33 | assets: BigIntish, 34 | { 35 | totalAssets, 36 | totalSupply, 37 | decimalsOffset, 38 | }: { 39 | totalAssets: BigIntish; 40 | totalSupply: BigIntish; 41 | decimalsOffset: BigIntish; 42 | }, 43 | rounding: RoundingDirection = "Up", 44 | ) { 45 | return MathLib.mulDiv( 46 | assets, 47 | BigInt(totalSupply) + 10n ** BigInt(decimalsOffset), 48 | BigInt(totalAssets) + VIRTUAL_ASSETS, 49 | rounding, 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/blue-sdk/src/vault/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./VaultUtils.js"; 2 | export * from "./VaultConfig.js"; 3 | export * from "./VaultMarketAllocation.js"; 4 | export * from "./VaultMarketConfig.js"; 5 | export * from "./VaultMarketPublicAllocatorConfig.js"; 6 | export * from "./VaultUser.js"; 7 | export * from "./Vault.js"; 8 | -------------------------------------------------------------------------------- /packages/blue-sdk/test/e2e/setup.ts: -------------------------------------------------------------------------------- 1 | import { createViemTest } from "@morpho-org/test/vitest"; 2 | import { mainnet } from "viem/chains"; 3 | 4 | export const test = createViemTest(mainnet, { 5 | forkUrl: process.env.MAINNET_RPC_URL, 6 | forkBlockNumber: 19_530_000, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/blue-sdk/test/unit/chain.test.ts: -------------------------------------------------------------------------------- 1 | import { entries } from "@morpho-org/morpho-ts"; 2 | 3 | import { describe, expect, test } from "vitest"; 4 | import { ChainId, ChainUtils } from "../../src/index.js"; 5 | 6 | describe("ChainUtils", () => { 7 | test("should have consistent chainIds", () => { 8 | entries(ChainUtils.CHAIN_METADATA).forEach(([chainId, { id }]) => { 9 | expect(+chainId).toEqual(id); 10 | }); 11 | }); 12 | 13 | test("should convert correctly a chainId to hexChainId", () => { 14 | expect(ChainUtils.toHexChainId(ChainId.BaseMainnet)).toEqual("0x2105"); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /packages/blue-sdk/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src" 5 | }, 6 | "include": ["src"] 7 | } 8 | -------------------------------------------------------------------------------- /packages/blue-sdk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": ".", 6 | "baseUrl": "." 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/bundler-sdk-ethers/.env.example: -------------------------------------------------------------------------------- 1 | MAINNET_RPC_URL= 2 | BASE_RPC_URL= 3 | -------------------------------------------------------------------------------- /packages/bundler-sdk-ethers/.gitignore: -------------------------------------------------------------------------------- 1 | /src/types/ -------------------------------------------------------------------------------- /packages/bundler-sdk-ethers/abis/ERC20WrapperBundler.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "erc20WrapperDepositFor", 5 | "inputs": [ 6 | { 7 | "name": "wrapper", 8 | "type": "address", 9 | "internalType": "address" 10 | }, 11 | { 12 | "name": "amount", 13 | "type": "uint256", 14 | "internalType": "uint256" 15 | } 16 | ], 17 | "outputs": [], 18 | "stateMutability": "payable" 19 | }, 20 | { 21 | "type": "function", 22 | "name": "erc20WrapperWithdrawTo", 23 | "inputs": [ 24 | { 25 | "name": "wrapper", 26 | "type": "address", 27 | "internalType": "address" 28 | }, 29 | { 30 | "name": "account", 31 | "type": "address", 32 | "internalType": "address" 33 | }, 34 | { 35 | "name": "amount", 36 | "type": "uint256", 37 | "internalType": "uint256" 38 | } 39 | ], 40 | "outputs": [], 41 | "stateMutability": "payable" 42 | }, 43 | { 44 | "type": "function", 45 | "name": "initiator", 46 | "inputs": [], 47 | "outputs": [ 48 | { 49 | "name": "", 50 | "type": "address", 51 | "internalType": "address" 52 | } 53 | ], 54 | "stateMutability": "view" 55 | }, 56 | { 57 | "type": "function", 58 | "name": "multicall", 59 | "inputs": [ 60 | { 61 | "name": "data", 62 | "type": "bytes[]", 63 | "internalType": "bytes[]" 64 | } 65 | ], 66 | "outputs": [], 67 | "stateMutability": "payable" 68 | } 69 | ] 70 | -------------------------------------------------------------------------------- /packages/bundler-sdk-ethers/abis/PermitBundler.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "initiator", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "address", 10 | "internalType": "address" 11 | } 12 | ], 13 | "stateMutability": "view" 14 | }, 15 | { 16 | "type": "function", 17 | "name": "multicall", 18 | "inputs": [ 19 | { 20 | "name": "data", 21 | "type": "bytes[]", 22 | "internalType": "bytes[]" 23 | } 24 | ], 25 | "outputs": [], 26 | "stateMutability": "payable" 27 | }, 28 | { 29 | "type": "function", 30 | "name": "permit", 31 | "inputs": [ 32 | { 33 | "name": "asset", 34 | "type": "address", 35 | "internalType": "address" 36 | }, 37 | { 38 | "name": "amount", 39 | "type": "uint256", 40 | "internalType": "uint256" 41 | }, 42 | { 43 | "name": "deadline", 44 | "type": "uint256", 45 | "internalType": "uint256" 46 | }, 47 | { 48 | "name": "v", 49 | "type": "uint8", 50 | "internalType": "uint8" 51 | }, 52 | { 53 | "name": "r", 54 | "type": "bytes32", 55 | "internalType": "bytes32" 56 | }, 57 | { 58 | "name": "s", 59 | "type": "bytes32", 60 | "internalType": "bytes32" 61 | }, 62 | { 63 | "name": "skipRevert", 64 | "type": "bool", 65 | "internalType": "bool" 66 | } 67 | ], 68 | "outputs": [], 69 | "stateMutability": "payable" 70 | } 71 | ] 72 | -------------------------------------------------------------------------------- /packages/bundler-sdk-ethers/abis/UrdBundler.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "initiator", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "address", 10 | "internalType": "address" 11 | } 12 | ], 13 | "stateMutability": "view" 14 | }, 15 | { 16 | "type": "function", 17 | "name": "multicall", 18 | "inputs": [ 19 | { 20 | "name": "data", 21 | "type": "bytes[]", 22 | "internalType": "bytes[]" 23 | } 24 | ], 25 | "outputs": [], 26 | "stateMutability": "payable" 27 | }, 28 | { 29 | "type": "function", 30 | "name": "urdClaim", 31 | "inputs": [ 32 | { 33 | "name": "distributor", 34 | "type": "address", 35 | "internalType": "address" 36 | }, 37 | { 38 | "name": "account", 39 | "type": "address", 40 | "internalType": "address" 41 | }, 42 | { 43 | "name": "reward", 44 | "type": "address", 45 | "internalType": "address" 46 | }, 47 | { 48 | "name": "amount", 49 | "type": "uint256", 50 | "internalType": "uint256" 51 | }, 52 | { 53 | "name": "proof", 54 | "type": "bytes32[]", 55 | "internalType": "bytes32[]" 56 | }, 57 | { 58 | "name": "skipRevert", 59 | "type": "bool", 60 | "internalType": "bool" 61 | } 62 | ], 63 | "outputs": [], 64 | "stateMutability": "payable" 65 | } 66 | ] 67 | -------------------------------------------------------------------------------- /packages/bundler-sdk-ethers/abis/WNativeBundler.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "receive", 4 | "stateMutability": "payable" 5 | }, 6 | { 7 | "type": "function", 8 | "name": "WRAPPED_NATIVE", 9 | "inputs": [], 10 | "outputs": [ 11 | { 12 | "name": "", 13 | "type": "address", 14 | "internalType": "address" 15 | } 16 | ], 17 | "stateMutability": "view" 18 | }, 19 | { 20 | "type": "function", 21 | "name": "initiator", 22 | "inputs": [], 23 | "outputs": [ 24 | { 25 | "name": "", 26 | "type": "address", 27 | "internalType": "address" 28 | } 29 | ], 30 | "stateMutability": "view" 31 | }, 32 | { 33 | "type": "function", 34 | "name": "multicall", 35 | "inputs": [ 36 | { 37 | "name": "data", 38 | "type": "bytes[]", 39 | "internalType": "bytes[]" 40 | } 41 | ], 42 | "outputs": [], 43 | "stateMutability": "payable" 44 | }, 45 | { 46 | "type": "function", 47 | "name": "unwrapNative", 48 | "inputs": [ 49 | { 50 | "name": "amount", 51 | "type": "uint256", 52 | "internalType": "uint256" 53 | } 54 | ], 55 | "outputs": [], 56 | "stateMutability": "payable" 57 | }, 58 | { 59 | "type": "function", 60 | "name": "wrapNative", 61 | "inputs": [ 62 | { 63 | "name": "amount", 64 | "type": "uint256", 65 | "internalType": "uint256" 66 | } 67 | ], 68 | "outputs": [], 69 | "stateMutability": "payable" 70 | } 71 | ] 72 | -------------------------------------------------------------------------------- /packages/bundler-sdk-ethers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@morpho-org/bundler-sdk-ethers", 3 | "description": "Ethers-based package that simplifies encoding bundles (ERC20 approvals, transfers, deposits, etc) to submit to the bundler onchain.", 4 | "version": "2.0.0", 5 | "author": "Morpho Association ", 6 | "contributors": ["Rubilmax "], 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/morpho-org/sdks.git", 10 | "directory": "packages/bundler-sdk-ethers" 11 | }, 12 | "homepage": "https://github.com/morpho-org/sdks", 13 | "bugs": { 14 | "url": "https://github.com/morpho-org/sdks/issues", 15 | "email": "contact@morpho.org" 16 | }, 17 | "license": "MIT", 18 | "main": "src/index.ts", 19 | "files": ["lib"], 20 | "scripts": { 21 | "prebuild": "$npm_execpath type", 22 | "prepublish": "$npm_execpath build", 23 | "build": "tsc --noEmit && tsc --build tsconfig.build.json", 24 | "type": "typechain --target ethers-v6 --out-dir src/types/ abis/*.json" 25 | }, 26 | "peerDependencies": { 27 | "ethers": "^6.12.0" 28 | }, 29 | "devDependencies": { 30 | "@typechain/ethers-v6": "^0.5.1", 31 | "ethers": "^6.13.5", 32 | "hardhat": "^2.22.18", 33 | "typechain": "^8.3.2", 34 | "typescript": "^5.7.2" 35 | }, 36 | "publishConfig": { 37 | "main": "lib/index.js", 38 | "types": "lib/index.d.ts" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/bundler-sdk-ethers/src/index.ts: -------------------------------------------------------------------------------- 1 | export { BundlerAction, BundlerCall } from "./BundlerAction"; 2 | -------------------------------------------------------------------------------- /packages/bundler-sdk-ethers/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src" 5 | }, 6 | "include": ["src"] 7 | } 8 | -------------------------------------------------------------------------------- /packages/bundler-sdk-ethers/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": ".", 6 | "baseUrl": "." 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/bundler-sdk-viem/.env.example: -------------------------------------------------------------------------------- 1 | MAINNET_RPC_URL= 2 | BASE_RPC_URL= 3 | -------------------------------------------------------------------------------- /packages/bundler-sdk-viem/README.md: -------------------------------------------------------------------------------- 1 | # @morpho-org/bundler-sdk-viem 2 | 3 | 4 | 5 | 6 | Version 7 | 8 | 9 | 10 | 11 | 12 | MIT License 13 | 14 | 15 | 16 | 17 | 18 | Downloads per month 19 | 20 | 21 |
22 |
23 | 24 | Viem-based extension of `@morpho-org/simulation-sdk` that exports utilities to transform simple interactions on Morpho (such as `Blue_Borrow`) and Morpho Vaults (such as `MetaMorpho_Deposit`) into the required bundles (with ERC20 approvals, transfers, etc) to submit to the bundler onchain. 25 | 26 | ## Installation 27 | 28 | ```bash 29 | npm install @morpho-org/bundler-sdk-viem 30 | ``` 31 | 32 | ```bash 33 | yarn add @morpho-org/bundler-sdk-viem 34 | ``` 35 | -------------------------------------------------------------------------------- /packages/bundler-sdk-viem/src/bundle.ts: -------------------------------------------------------------------------------- 1 | import type { SimulationState } from "@morpho-org/simulation-sdk"; 2 | import type { Address } from "viem"; 3 | import { encodeBundle } from "./actions.js"; 4 | import { 5 | type BundlingOptions, 6 | finalizeBundle, 7 | populateBundle, 8 | } from "./operations.js"; 9 | import type { InputBundlerOperation } from "./types/index.js"; 10 | 11 | export const setupBundle = ( 12 | inputOperations: InputBundlerOperation[], 13 | startData: SimulationState, 14 | receiver: Address, 15 | { 16 | supportsSignature, 17 | unwrapTokens, 18 | unwrapSlippage, 19 | ...options 20 | }: BundlingOptions & { 21 | supportsSignature?: boolean; 22 | unwrapTokens?: Set
; 23 | unwrapSlippage?: bigint; 24 | } = {}, 25 | ) => { 26 | let { operations } = populateBundle(inputOperations, startData, options); 27 | 28 | operations = finalizeBundle( 29 | operations, 30 | startData, 31 | receiver, 32 | unwrapTokens, 33 | unwrapSlippage, 34 | ); 35 | 36 | const bundle = encodeBundle(operations, startData, supportsSignature); 37 | 38 | return { 39 | operations, 40 | bundle, 41 | }; 42 | }; 43 | -------------------------------------------------------------------------------- /packages/bundler-sdk-viem/src/errors.ts: -------------------------------------------------------------------------------- 1 | import type { SimulationResult } from "@morpho-org/simulation-sdk"; 2 | 3 | import type { Address } from "viem"; 4 | import type { ActionType, InputBundlerOperation } from "./types/index.js"; 5 | 6 | export namespace BundlerErrors { 7 | export class Bundle extends Error { 8 | constructor( 9 | public readonly error: Error, 10 | public readonly index: number, 11 | public readonly inputOperation: InputBundlerOperation, 12 | public readonly steps: SimulationResult, 13 | ) { 14 | super(error.message); 15 | 16 | this.stack = error.stack; 17 | } 18 | } 19 | 20 | export class MissingSignature extends Error { 21 | constructor() { 22 | super(`missing signature`); 23 | } 24 | } 25 | 26 | export class UnexpectedAction extends Error { 27 | constructor(type: ActionType, chainId: number) { 28 | super(`unexpected action "${type}" on chain "${chainId}"`); 29 | } 30 | } 31 | 32 | export class UnexpectedSignature extends Error { 33 | constructor(spender: Address) { 34 | super(`unexpected signature consumer "${spender}"`); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/bundler-sdk-viem/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./actions.js"; 2 | export * from "./operations.js"; 3 | export * from "./errors.js"; 4 | export * from "./types/index.js"; 5 | export * from "./BundlerAction.js"; 6 | export * from "./bundle.js"; 7 | export * from "./ActionBundle.js"; 8 | export * from "./abis.js"; 9 | -------------------------------------------------------------------------------- /packages/bundler-sdk-viem/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./actions.js"; 2 | export * from "./operations.js"; 3 | -------------------------------------------------------------------------------- /packages/bundler-sdk-viem/test/setup.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "@morpho-org/blue-sdk"; 2 | import { createWagmiTest } from "@morpho-org/test-wagmi"; 3 | import { base, mainnet } from "viem/chains"; 4 | 5 | export const test = { 6 | [ChainId.EthMainnet]: createWagmiTest(mainnet, { 7 | forkUrl: process.env.MAINNET_RPC_URL, 8 | forkBlockNumber: 21_872_137, 9 | }), 10 | [ChainId.BaseMainnet]: createWagmiTest(base, { 11 | forkUrl: process.env.BASE_RPC_URL, 12 | forkBlockNumber: 26_539_234, 13 | }), 14 | } as const; 15 | -------------------------------------------------------------------------------- /packages/bundler-sdk-viem/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src" 5 | }, 6 | "include": ["src"] 7 | } 8 | -------------------------------------------------------------------------------- /packages/bundler-sdk-viem/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": ".", 6 | "baseUrl": "." 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/liquidation-sdk-viem/.env.example: -------------------------------------------------------------------------------- 1 | MAINNET_RPC_URL= 2 | ONE_INCH_SWAP_API_KEY= 3 | -------------------------------------------------------------------------------- /packages/liquidation-sdk-viem/codegen.ts: -------------------------------------------------------------------------------- 1 | import type { CodegenConfig } from "@graphql-codegen/cli"; 2 | 3 | import { BLUE_API_GRAPHQL_URL } from "@morpho-org/morpho-ts"; 4 | 5 | const config: CodegenConfig = { 6 | overwrite: true, 7 | schema: BLUE_API_GRAPHQL_URL, 8 | documents: ["graphql/*.{query,fragment}.gql"], 9 | generates: { 10 | "src/api/types.ts": { 11 | plugins: ["typescript-operations"], 12 | preset: "import-types", 13 | presetConfig: { 14 | typesPath: "@morpho-org/blue-api-sdk", 15 | }, 16 | config: { 17 | avoidOptionals: { 18 | field: true, 19 | inputValue: false, 20 | defaultValue: true, 21 | }, 22 | scalars: { 23 | BigInt: { 24 | input: `Types.Scalars["BigInt"]["input"]`, 25 | output: `Types.Scalars["BigInt"]["output"]`, 26 | }, 27 | HexString: { 28 | input: `Types.Scalars["HexString"]["input"]`, 29 | output: `Types.Scalars["HexString"]["output"]`, 30 | }, 31 | Address: { 32 | input: `Types.Scalars["Address"]["input"]`, 33 | output: `Types.Scalars["Address"]["output"]`, 34 | }, 35 | MarketId: { 36 | input: `Types.Scalars["MarketId"]["input"]`, 37 | output: `Types.Scalars["MarketId"]["output"]`, 38 | }, 39 | }, 40 | }, 41 | }, 42 | "src/api/sdk.ts": { 43 | plugins: ["typescript-graphql-request"], 44 | preset: "import-types", 45 | presetConfig: { 46 | typesPath: "./types.js", 47 | }, 48 | }, 49 | }, 50 | }; 51 | 52 | export default config; 53 | -------------------------------------------------------------------------------- /packages/liquidation-sdk-viem/graphql/GetLiquidatablePositions.query.gql: -------------------------------------------------------------------------------- 1 | query getLiquidatablePositions( 2 | $chainId: Int! 3 | $wNative: String! 4 | $marketIds: [String!] 5 | $first: Int = 1000 6 | ) { 7 | assetByAddress(chainId: $chainId, address: $wNative) { 8 | priceUsd 9 | } 10 | 11 | marketPositions( 12 | first: $first 13 | where: { 14 | chainId_in: [$chainId] 15 | marketUniqueKey_in: $marketIds 16 | healthFactor_lte: 1 17 | } 18 | ) { 19 | items { 20 | ...MarketPosition 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/liquidation-sdk-viem/graphql/GetMarketsAssets.query.gql: -------------------------------------------------------------------------------- 1 | query getMarketsAssets($chainId: Int!, $marketIds: [String!]!) { 2 | markets(where: { chainId_in: [$chainId], uniqueKey_in: $marketIds }) { 3 | items { 4 | uniqueKey 5 | collateralAsset { 6 | address 7 | decimals 8 | symbol 9 | priceUsd 10 | spotPriceEth 11 | } 12 | loanAsset { 13 | address 14 | decimals 15 | symbol 16 | priceUsd 17 | spotPriceEth 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/liquidation-sdk-viem/graphql/GetWhitelistedMarketIds.query.gql: -------------------------------------------------------------------------------- 1 | query getWhitelistedMarketIds($chainId: Int!) { 2 | markets(where: { chainId_in: [$chainId], whitelisted: true }) { 3 | items { 4 | uniqueKey 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/liquidation-sdk-viem/graphql/MarketPosition.fragment.gql: -------------------------------------------------------------------------------- 1 | fragment MarketPosition on MarketPosition { 2 | user { 3 | address 4 | } 5 | market { 6 | uniqueKey 7 | collateralAsset { 8 | address 9 | decimals 10 | symbol 11 | priceUsd 12 | spotPriceEth 13 | } 14 | loanAsset { 15 | address 16 | decimals 17 | symbol 18 | priceUsd 19 | spotPriceEth 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/liquidation-sdk-viem/src/api/index.ts: -------------------------------------------------------------------------------- 1 | import { GraphQLClient } from "graphql-request"; 2 | 3 | import { BLUE_API_GRAPHQL_URL } from "@morpho-org/morpho-ts"; 4 | 5 | import { getSdk } from "./sdk.js"; 6 | 7 | export * from "./sdk.js"; 8 | export * from "./types.js"; 9 | 10 | export const apiSdk: ReturnType = getSdk( 11 | new GraphQLClient(BLUE_API_GRAPHQL_URL), 12 | ); 13 | -------------------------------------------------------------------------------- /packages/liquidation-sdk-viem/src/index.ts: -------------------------------------------------------------------------------- 1 | import "evm-maths"; 2 | 3 | export * from "./api/index.js"; 4 | export * from "./addresses.js"; 5 | export * from "./flashbots.js"; 6 | export * from "./LiquidationEncoder.js"; 7 | export * from "./preLiquidation/index.js"; 8 | export * from "./swap/index.js"; 9 | export * from "./tokens/index.js"; 10 | export * from "./abis.js"; 11 | export * from "./positions/index.js"; 12 | export * from "./thresholds.js"; 13 | -------------------------------------------------------------------------------- /packages/liquidation-sdk-viem/src/positions/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./getters.js"; 2 | -------------------------------------------------------------------------------- /packages/liquidation-sdk-viem/src/preLiquidation/helpers.ts: -------------------------------------------------------------------------------- 1 | import { 2 | MathLib, 3 | ORACLE_PRICE_SCALE, 4 | type PreLiquidationPosition, 5 | } from "@morpho-org/blue-sdk"; 6 | 7 | export function parseWithBigInt(jsonText: string): T { 8 | return JSON.parse(jsonText, (_key, value) => { 9 | if (typeof value === "string" && /^-?\d+n$/.test(value)) { 10 | return BigInt(value.slice(0, -1)); 11 | } 12 | return value; 13 | }) as T; 14 | } 15 | 16 | export function getRepayDataPreLiquidation( 17 | preLiquidation: PreLiquidationPosition, 18 | ) { 19 | const lltv = preLiquidation.market.params.lltv; 20 | const preLltv = preLiquidation.preLiquidationParams.preLltv; 21 | const ltv = preLiquidation.ltv ?? 0n; 22 | const quotient = MathLib.wDivDown(ltv - preLltv, lltv - preLltv); 23 | 24 | const preLIF = 25 | preLiquidation.preLiquidationParams.getIncentiveFactor(quotient); 26 | 27 | const preSeizedAssetsQuoted = MathLib.mulDivUp( 28 | preLiquidation.seizableCollateral ?? 0n, 29 | preLiquidation.market.price!, 30 | ORACLE_PRICE_SCALE, 31 | ); 32 | 33 | const repaidAssets = MathLib.wDivUp(preSeizedAssetsQuoted, preLIF); 34 | const repaidShares = preLiquidation.market.toBorrowShares(repaidAssets, "Up"); 35 | 36 | return { repaidAssets, repaidShares }; 37 | } 38 | -------------------------------------------------------------------------------- /packages/liquidation-sdk-viem/src/preLiquidation/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./helpers.js"; 2 | export * from "./types.js"; 3 | export * from "./positionGetters.js"; 4 | -------------------------------------------------------------------------------- /packages/liquidation-sdk-viem/src/preLiquidation/types.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | Address, 3 | IPreLiquidationParams, 4 | MarketId, 5 | } from "@morpho-org/blue-sdk"; 6 | 7 | export type PreLiquidationResponse = { 8 | warnings: string[]; 9 | results: { 10 | marketId: MarketId; 11 | address: Address; 12 | preLiquidationParams: IPreLiquidationParams; 13 | enabledPositions: Address[]; 14 | price: bigint; 15 | }[]; 16 | }; 17 | -------------------------------------------------------------------------------- /packages/liquidation-sdk-viem/src/swap/1inch.ts: -------------------------------------------------------------------------------- 1 | import type { BigIntish } from "@morpho-org/blue-sdk"; 2 | import type { SwapParams, SwapResponse } from "./types.js"; 3 | 4 | export namespace OneInch { 5 | export const API_BASE_URL = "https://api.1inch.dev"; 6 | 7 | export const getSwapApiPath = (chainId: BigIntish) => 8 | `/swap/v6.0/${chainId}/swap`; 9 | export const getSwapApiUrl = (chainId: BigIntish) => 10 | new URL(getSwapApiPath(chainId), API_BASE_URL).toString(); 11 | 12 | export async function fetchSwap( 13 | swapParams: SwapParams, 14 | apiKey = process.env.ONE_INCH_SWAP_API_KEY, 15 | ) { 16 | const url = new URL(getSwapApiPath(swapParams.chainId), API_BASE_URL); 17 | Object.entries(swapParams).forEach(([key, value]) => { 18 | if (value == null) return; 19 | switch (key) { 20 | case "slippage": 21 | // 1inch expects slippage as a percentage, so we divide our value (in basis points) by 100 22 | url.searchParams.set(key, (Number(value) / 100).toString(10)); 23 | break; 24 | default: 25 | url.searchParams.set(key, value); 26 | } 27 | }); 28 | 29 | const res = await fetch(url, { 30 | headers: { 31 | accept: "application/json", 32 | Authorization: `Bearer ${apiKey}`, 33 | }, 34 | }); 35 | 36 | if (!res.ok) throw Error(res.statusText); 37 | 38 | return (await res.json()) as SwapResponse; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/liquidation-sdk-viem/src/swap/types.ts: -------------------------------------------------------------------------------- 1 | import type { BigIntish } from "@morpho-org/blue-sdk"; 2 | import type { Address, Hex } from "viem"; 3 | 4 | export interface SwapParams { 5 | chainId: BigIntish; 6 | src: string; 7 | dst: string; 8 | amount: BigIntish; 9 | from: string; 10 | origin: string; 11 | slippage: BigIntish; 12 | protocols?: string; 13 | fee?: BigIntish; 14 | gasPrice?: BigIntish; 15 | complexityLevel?: number; 16 | parts?: number; 17 | mainRouteParts?: number; 18 | gasLimit?: BigIntish; 19 | includeTokensInfo?: boolean; 20 | includeProtocols?: boolean; 21 | includeGas?: boolean; 22 | connectorTokens?: string; 23 | excludedProtocols?: string; 24 | permit?: string; 25 | receiver?: string; 26 | referrer?: string; 27 | allowPartialFill?: boolean; 28 | disableEstimate?: boolean; 29 | usePermit2?: boolean; 30 | } 31 | 32 | export interface SwapToken { 33 | symbol: string; 34 | name: string; 35 | decimals: number; 36 | address: string; 37 | logoURI: string; 38 | } 39 | 40 | export interface SwapResponse { 41 | srcToken: SwapToken; 42 | dstToken: SwapToken; 43 | dstAmount: string; 44 | protocols: {}[]; 45 | tx: { 46 | to: Address; 47 | data: Hex; 48 | value: bigint; 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /packages/liquidation-sdk-viem/src/thresholds.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "@morpho-org/blue-sdk"; 2 | import { parseEther } from "viem"; 3 | 4 | export const collateralUsdThreshold: Record = { 5 | [ChainId.EthMainnet]: parseEther("1000"), 6 | [ChainId.BaseMainnet]: parseEther("2"), 7 | }; 8 | -------------------------------------------------------------------------------- /packages/liquidation-sdk-viem/src/tokens/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./pendle.js"; 2 | export * from "./usual.js"; 3 | export * from "./sky.js"; 4 | export * from "./spectra.js"; 5 | export * from "./midas.js"; 6 | -------------------------------------------------------------------------------- /packages/liquidation-sdk-viem/src/tokens/usual.ts: -------------------------------------------------------------------------------- 1 | export namespace Usual { 2 | export const USD0_USDC_USD0_INDEX = 0n; 3 | export const USD0_USDC_USDC_INDEX = 1n; 4 | 5 | export const USD0_USD0PP_USD0_INDEX = 0n; 6 | export const USD0_USD0PP_USDPP_INDEX = 1n; 7 | } 8 | -------------------------------------------------------------------------------- /packages/liquidation-sdk-viem/test/contracts/SwapMock.ts: -------------------------------------------------------------------------------- 1 | export const abi = [ 2 | { 3 | inputs: [ 4 | { 5 | components: [ 6 | { internalType: "address", name: "token", type: "address" }, 7 | { internalType: "uint256", name: "amount", type: "uint256" }, 8 | ], 9 | internalType: "struct TokenAmount", 10 | name: "input", 11 | type: "tuple", 12 | }, 13 | { 14 | components: [ 15 | { internalType: "address", name: "token", type: "address" }, 16 | { internalType: "uint256", name: "amount", type: "uint256" }, 17 | ], 18 | internalType: "struct TokenAmount", 19 | name: "output", 20 | type: "tuple", 21 | }, 22 | ], 23 | name: "swap", 24 | outputs: [], 25 | stateMutability: "nonpayable", 26 | type: "function", 27 | }, 28 | ] as const; 29 | 30 | export const bytecode = 31 | "0x60808060405234601557610197908161001a8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c63432c1a8f14610025575f80fd5b34610121573660031901608081126101215760401361012157604036604319011261012157600435906001600160a01b03821682036101215760645f918284602095506323b872dd60e01b835233600484015230602484015260243560448401525af13d15601f3d1160015f511416171615610125576044356001600160a01b03811681036101215760405163a9059cbb60e01b815233600482015260643560248201526020915f9160449183905af13d15601f3d1160015f5114161716156100ea57005b60405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606490fd5b5f80fd5b60405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606490fdfea2646970667358221220d540fd4d1eb630bbf1be4847ebc37159e01387d164f9f9dd157e25ff60e49ec264736f6c634300081b0033"; 32 | -------------------------------------------------------------------------------- /packages/liquidation-sdk-viem/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src" 5 | }, 6 | "include": ["src"], 7 | "files": [] 8 | } 9 | -------------------------------------------------------------------------------- /packages/liquidation-sdk-viem/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": ".", 6 | "baseUrl": "." 7 | }, 8 | "include": ["src", "test", "examples"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/liquidity-sdk-ethers/.env.example: -------------------------------------------------------------------------------- 1 | MAINNET_RPC_URL= 2 | -------------------------------------------------------------------------------- /packages/liquidity-sdk-ethers/codegen.ts: -------------------------------------------------------------------------------- 1 | import type { CodegenConfig } from "@graphql-codegen/cli"; 2 | 3 | import { BLUE_API_GRAPHQL_URL } from "@morpho-org/morpho-ts"; 4 | 5 | const config: CodegenConfig = { 6 | overwrite: true, 7 | schema: BLUE_API_GRAPHQL_URL, 8 | documents: ["graphql/*.{query,fragment}.gql"], 9 | generates: { 10 | "src/api/types.ts": { 11 | plugins: ["typescript-operations"], 12 | preset: "import-types", 13 | presetConfig: { 14 | typesPath: "@morpho-org/blue-api-sdk", 15 | }, 16 | config: { 17 | avoidOptionals: { 18 | field: true, 19 | inputValue: false, 20 | defaultValue: true, 21 | }, 22 | scalars: { 23 | BigInt: { 24 | input: `Types.Scalars["BigInt"]["input"]`, 25 | output: `Types.Scalars["BigInt"]["output"]`, 26 | }, 27 | HexString: { 28 | input: `Types.Scalars["HexString"]["input"]`, 29 | output: `Types.Scalars["HexString"]["output"]`, 30 | }, 31 | Address: { 32 | input: `Types.Scalars["Address"]["input"]`, 33 | output: `Types.Scalars["Address"]["output"]`, 34 | }, 35 | MarketId: { 36 | input: `Types.Scalars["MarketId"]["input"]`, 37 | output: `Types.Scalars["MarketId"]["output"]`, 38 | }, 39 | }, 40 | }, 41 | }, 42 | "src/api/sdk.ts": { 43 | plugins: ["typescript-graphql-request"], 44 | preset: "import-types", 45 | presetConfig: { 46 | typesPath: "./types.js", 47 | }, 48 | }, 49 | }, 50 | }; 51 | 52 | export default config; 53 | -------------------------------------------------------------------------------- /packages/liquidity-sdk-ethers/graphql/GetMarkets.query.gql: -------------------------------------------------------------------------------- 1 | query getMarkets($chainId: Int!, $marketIds: [String!]) { 2 | markets(where: { chainId_in: [$chainId], uniqueKey_in: $marketIds }) { 3 | items { 4 | uniqueKey 5 | targetBorrowUtilization 6 | publicAllocatorSharedLiquidity { 7 | vault { 8 | address 9 | } 10 | allocationMarket { 11 | uniqueKey 12 | } 13 | assets 14 | } 15 | supplyingVaults { 16 | address 17 | state { 18 | allocation { 19 | market { 20 | uniqueKey 21 | loanAsset { 22 | address 23 | } 24 | targetWithdrawUtilization 25 | } 26 | } 27 | } 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/liquidity-sdk-ethers/src/api/index.ts: -------------------------------------------------------------------------------- 1 | import { GraphQLClient } from "graphql-request"; 2 | 3 | import { BLUE_API_GRAPHQL_URL } from "@morpho-org/morpho-ts"; 4 | 5 | import { getSdk } from "./sdk.js"; 6 | 7 | export * from "./sdk.js"; 8 | export * from "./types.js"; 9 | 10 | export const apiSdk = getSdk(new GraphQLClient(BLUE_API_GRAPHQL_URL)); 11 | -------------------------------------------------------------------------------- /packages/liquidity-sdk-ethers/src/api/types.ts: -------------------------------------------------------------------------------- 1 | import type * as Types from "@morpho-org/blue-api-sdk"; 2 | 3 | export type GetMarketsQueryVariables = Types.Exact<{ 4 | chainId: Types.Scalars["Int"]["input"]; 5 | marketIds?: Types.InputMaybe< 6 | Array | Types.Scalars["String"]["input"] 7 | >; 8 | }>; 9 | 10 | export type GetMarketsQuery = { 11 | __typename?: "Query"; 12 | markets: { 13 | __typename?: "PaginatedMarkets"; 14 | items: Array<{ 15 | __typename?: "Market"; 16 | uniqueKey: Types.Scalars["MarketId"]["output"]; 17 | targetBorrowUtilization: Types.Scalars["BigInt"]["output"]; 18 | publicAllocatorSharedLiquidity: Array<{ 19 | __typename?: "PublicAllocatorSharedLiquidity"; 20 | assets: Types.Scalars["BigInt"]["output"]; 21 | vault: { 22 | __typename?: "Vault"; 23 | address: Types.Scalars["Address"]["output"]; 24 | }; 25 | allocationMarket: { 26 | __typename?: "Market"; 27 | uniqueKey: Types.Scalars["MarketId"]["output"]; 28 | }; 29 | }> | null; 30 | supplyingVaults: Array<{ 31 | __typename?: "Vault"; 32 | address: Types.Scalars["Address"]["output"]; 33 | state: { 34 | __typename?: "VaultState"; 35 | allocation: Array<{ 36 | __typename?: "VaultAllocation"; 37 | market: { 38 | __typename?: "Market"; 39 | uniqueKey: Types.Scalars["MarketId"]["output"]; 40 | targetWithdrawUtilization: Types.Scalars["BigInt"]["output"]; 41 | loanAsset: { 42 | __typename?: "Asset"; 43 | address: Types.Scalars["Address"]["output"]; 44 | }; 45 | }; 46 | }> | null; 47 | } | null; 48 | }> | null; 49 | }> | null; 50 | }; 51 | }; 52 | -------------------------------------------------------------------------------- /packages/liquidity-sdk-ethers/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./loader"; 2 | -------------------------------------------------------------------------------- /packages/liquidity-sdk-ethers/test/setup.ts: -------------------------------------------------------------------------------- 1 | import { createEthersTest } from "@morpho-org/test/vitest/ethers"; 2 | import { mainnet } from "viem/chains"; 3 | 4 | export const test = createEthersTest(mainnet, { 5 | forkUrl: process.env.MAINNET_RPC_URL, 6 | forkBlockNumber: 21_365_000, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/liquidity-sdk-ethers/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src" 5 | }, 6 | "include": ["src"], 7 | "files": [] 8 | } 9 | -------------------------------------------------------------------------------- /packages/liquidity-sdk-ethers/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": ".", 6 | "baseUrl": "." 7 | }, 8 | "include": ["src", "test"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/liquidity-sdk-viem/.env.example: -------------------------------------------------------------------------------- 1 | MAINNET_RPC_URL= 2 | -------------------------------------------------------------------------------- /packages/liquidity-sdk-viem/codegen.ts: -------------------------------------------------------------------------------- 1 | import type { CodegenConfig } from "@graphql-codegen/cli"; 2 | 3 | import { BLUE_API_GRAPHQL_URL } from "@morpho-org/morpho-ts"; 4 | 5 | const config: CodegenConfig = { 6 | overwrite: true, 7 | schema: BLUE_API_GRAPHQL_URL, 8 | documents: ["graphql/*.{query,fragment}.gql"], 9 | generates: { 10 | "src/api/types.ts": { 11 | plugins: ["typescript-operations"], 12 | preset: "import-types", 13 | presetConfig: { 14 | typesPath: "@morpho-org/blue-api-sdk", 15 | }, 16 | config: { 17 | avoidOptionals: { 18 | field: true, 19 | inputValue: false, 20 | defaultValue: true, 21 | }, 22 | scalars: { 23 | BigInt: { 24 | input: `Types.Scalars["BigInt"]["input"]`, 25 | output: `Types.Scalars["BigInt"]["output"]`, 26 | }, 27 | HexString: { 28 | input: `Types.Scalars["HexString"]["input"]`, 29 | output: `Types.Scalars["HexString"]["output"]`, 30 | }, 31 | Address: { 32 | input: `Types.Scalars["Address"]["input"]`, 33 | output: `Types.Scalars["Address"]["output"]`, 34 | }, 35 | MarketId: { 36 | input: `Types.Scalars["MarketId"]["input"]`, 37 | output: `Types.Scalars["MarketId"]["output"]`, 38 | }, 39 | }, 40 | }, 41 | }, 42 | "src/api/sdk.ts": { 43 | plugins: ["typescript-graphql-request"], 44 | preset: "import-types", 45 | presetConfig: { 46 | typesPath: "./types.js", 47 | }, 48 | }, 49 | }, 50 | }; 51 | 52 | export default config; 53 | -------------------------------------------------------------------------------- /packages/liquidity-sdk-viem/graphql/GetMarkets.query.gql: -------------------------------------------------------------------------------- 1 | query getMarkets($chainId: Int!, $marketIds: [String!]) { 2 | markets(where: { chainId_in: [$chainId], uniqueKey_in: $marketIds }) { 3 | items { 4 | uniqueKey 5 | targetBorrowUtilization 6 | publicAllocatorSharedLiquidity { 7 | vault { 8 | address 9 | } 10 | allocationMarket { 11 | uniqueKey 12 | } 13 | assets 14 | } 15 | supplyingVaults { 16 | address 17 | state { 18 | allocation { 19 | market { 20 | uniqueKey 21 | loanAsset { 22 | address 23 | } 24 | targetWithdrawUtilization 25 | } 26 | } 27 | } 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/liquidity-sdk-viem/src/api/index.ts: -------------------------------------------------------------------------------- 1 | import { GraphQLClient } from "graphql-request"; 2 | 3 | import { BLUE_API_GRAPHQL_URL } from "@morpho-org/morpho-ts"; 4 | 5 | import { getSdk } from "./sdk.js"; 6 | 7 | export * from "./sdk.js"; 8 | export * from "./types.js"; 9 | 10 | export const apiSdk = getSdk(new GraphQLClient(BLUE_API_GRAPHQL_URL)); 11 | -------------------------------------------------------------------------------- /packages/liquidity-sdk-viem/src/api/types.ts: -------------------------------------------------------------------------------- 1 | import type * as Types from "@morpho-org/blue-api-sdk"; 2 | 3 | export type GetMarketsQueryVariables = Types.Exact<{ 4 | chainId: Types.Scalars["Int"]["input"]; 5 | marketIds?: Types.InputMaybe< 6 | Array | Types.Scalars["String"]["input"] 7 | >; 8 | }>; 9 | 10 | export type GetMarketsQuery = { 11 | __typename?: "Query"; 12 | markets: { 13 | __typename?: "PaginatedMarkets"; 14 | items: Array<{ 15 | __typename?: "Market"; 16 | uniqueKey: Types.Scalars["MarketId"]["output"]; 17 | targetBorrowUtilization: Types.Scalars["BigInt"]["output"]; 18 | publicAllocatorSharedLiquidity: Array<{ 19 | __typename?: "PublicAllocatorSharedLiquidity"; 20 | assets: Types.Scalars["BigInt"]["output"]; 21 | vault: { 22 | __typename?: "Vault"; 23 | address: Types.Scalars["Address"]["output"]; 24 | }; 25 | allocationMarket: { 26 | __typename?: "Market"; 27 | uniqueKey: Types.Scalars["MarketId"]["output"]; 28 | }; 29 | }> | null; 30 | supplyingVaults: Array<{ 31 | __typename?: "Vault"; 32 | address: Types.Scalars["Address"]["output"]; 33 | state: { 34 | __typename?: "VaultState"; 35 | allocation: Array<{ 36 | __typename?: "VaultAllocation"; 37 | market: { 38 | __typename?: "Market"; 39 | uniqueKey: Types.Scalars["MarketId"]["output"]; 40 | targetWithdrawUtilization: Types.Scalars["BigInt"]["output"]; 41 | loanAsset: { 42 | __typename?: "Asset"; 43 | address: Types.Scalars["Address"]["output"]; 44 | }; 45 | }; 46 | }> | null; 47 | } | null; 48 | }> | null; 49 | }> | null; 50 | }; 51 | }; 52 | -------------------------------------------------------------------------------- /packages/liquidity-sdk-viem/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./loader"; 2 | -------------------------------------------------------------------------------- /packages/liquidity-sdk-viem/test/setup.ts: -------------------------------------------------------------------------------- 1 | import { createViemTest } from "@morpho-org/test/vitest"; 2 | import { mainnet } from "viem/chains"; 3 | 4 | export const test = createViemTest(mainnet, { 5 | forkUrl: process.env.MAINNET_RPC_URL, 6 | forkBlockNumber: 21_365_000, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/liquidity-sdk-viem/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src" 5 | }, 6 | "include": ["src"], 7 | "files": [] 8 | } 9 | -------------------------------------------------------------------------------- /packages/liquidity-sdk-viem/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": ".", 6 | "baseUrl": "." 7 | }, 8 | "include": ["src", "test"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/migration-sdk-viem/.env.example: -------------------------------------------------------------------------------- 1 | MAINNET_RPC_URL= 2 | BASE_RPC_URL= 3 | -------------------------------------------------------------------------------- /packages/migration-sdk-viem/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@morpho-org/migration-sdk-viem", 3 | "description": "Viem-based extension of `@morpho-org/simulation-sdk` that exports utilities to build migration bundles to migrate lending positions (on aave, compound, morpho-aaveV3-optimizer, ...) to the morpho protocol.", 4 | "version": "1.0.0", 5 | "author": "Morpho Association ", 6 | "contributors": ["oumar-fall"], 7 | "repository": "github:morpho-org/sdks", 8 | "homepage": "https://github.com/morpho-org/sdks", 9 | "bugs": { 10 | "url": "https://github.com/morpho-org/sdks/issues", 11 | "email": "contact@morpho.org" 12 | }, 13 | "license": "MIT", 14 | "main": "src/index.ts", 15 | "files": ["lib"], 16 | "scripts": { 17 | "prepublish": "$npm_execpath build", 18 | "build": "tsc --noEmit && tsc --build tsconfig.build.json" 19 | }, 20 | "peerDependencies": { 21 | "@morpho-org/blue-sdk": "workspace:^", 22 | "@morpho-org/blue-sdk-viem": "workspace:^", 23 | "@morpho-org/bundler-sdk-viem": "workspace:^", 24 | "@morpho-org/morpho-ts": "workspace:^", 25 | "@morpho-org/simulation-sdk": "workspace:^", 26 | "viem": "^2.0.0" 27 | }, 28 | "devDependencies": { 29 | "@morpho-org/blue-sdk": "workspace:^", 30 | "@morpho-org/blue-sdk-viem": "workspace:^", 31 | "@morpho-org/morpho-test": "workspace:^", 32 | "@morpho-org/morpho-ts": "workspace:^", 33 | "@morpho-org/simulation-sdk": "workspace:^", 34 | "@morpho-org/simulation-sdk-wagmi": "workspace:^", 35 | "@morpho-org/test": "workspace:^", 36 | "@morpho-org/test-wagmi": "workspace:^", 37 | "@types/lodash": "^4.17.12", 38 | "lodash": "^4.17.21", 39 | "typescript": "^5.7.2", 40 | "viem": "^2.23.0", 41 | "vitest": "^3.0.5" 42 | }, 43 | "publishConfig": { 44 | "main": "lib/index.js", 45 | "types": "lib/index.d.ts" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/migration-sdk-viem/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./types/index.js"; 2 | export * from "./positions/index.js"; 3 | export * from "./fetchers/index.js"; 4 | export * from "./config.js"; 5 | -------------------------------------------------------------------------------- /packages/migration-sdk-viem/src/positions/borrow/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./MigratableBorrowPosition.js"; 2 | export * from "./aaveV3.borrow.js"; 3 | export * from "./blue.borrow.js"; 4 | export * from "./aaveV2.borrow.js"; 5 | export * from "./compoundV3.borrow.js"; 6 | -------------------------------------------------------------------------------- /packages/migration-sdk-viem/src/positions/index.ts: -------------------------------------------------------------------------------- 1 | import type { MigratableBorrowPosition } from "./borrow/MigratableBorrowPosition.js"; 2 | import type { MigratableSupplyPosition } from "./supply/index.js"; 3 | 4 | export { MigratableSupplyPosition } from "./supply/index.js"; 5 | export { MigratableBorrowPosition } from "./borrow/index.js"; 6 | 7 | export type MigratablePosition = 8 | | MigratableSupplyPosition 9 | | MigratableBorrowPosition; 10 | export namespace MigratablePosition { 11 | export type Args = 12 | T extends MigratableSupplyPosition 13 | ? MigratableSupplyPosition.Args 14 | : T extends MigratableBorrowPosition 15 | ? MigratableBorrowPosition.Args 16 | : never; 17 | } 18 | 19 | export { MigratableBorrowPosition_Blue } from "./borrow/blue.borrow.js"; 20 | -------------------------------------------------------------------------------- /packages/migration-sdk-viem/src/positions/signature/aaveV3Optimizer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type Address, 3 | type ChainId, 4 | UnsupportedChainIdError, 5 | } from "@morpho-org/blue-sdk"; 6 | 7 | import type { TypedDataDefinition } from "viem"; 8 | import { migrationAddresses } from "../../config.js"; 9 | import { MigratableProtocol } from "../../types/index.js"; 10 | 11 | interface MorphoAaveV3ManagerApprovalArgs { 12 | delegator: Address; 13 | manager: Address; 14 | nonce: bigint; 15 | deadline: bigint; 16 | isAllowed: boolean; 17 | } 18 | 19 | const morphoAaveV3ManagerApprovalTypes = { 20 | Authorization: [ 21 | { 22 | name: "delegator", 23 | type: "address", 24 | }, 25 | { 26 | name: "manager", 27 | type: "address", 28 | }, 29 | { 30 | name: "isAllowed", 31 | type: "bool", 32 | }, 33 | { 34 | name: "nonce", 35 | type: "uint256", 36 | }, 37 | { 38 | name: "deadline", 39 | type: "uint256", 40 | }, 41 | ], 42 | } as const; 43 | 44 | export const getMorphoAaveV3ManagerApprovalTypedData = ( 45 | args: MorphoAaveV3ManagerApprovalArgs, 46 | chainId: ChainId, 47 | ): TypedDataDefinition< 48 | typeof morphoAaveV3ManagerApprovalTypes, 49 | "Authorization" 50 | > => { 51 | const aaveV3OptimizerAddresses = 52 | migrationAddresses[chainId]?.[MigratableProtocol.aaveV3Optimizer]; 53 | 54 | if (!aaveV3OptimizerAddresses) throw new UnsupportedChainIdError(chainId); 55 | 56 | return { 57 | domain: { 58 | name: "Morpho-AaveV3", 59 | chainId, 60 | verifyingContract: aaveV3OptimizerAddresses.morpho.address, 61 | version: "0", 62 | }, 63 | types: morphoAaveV3ManagerApprovalTypes, 64 | message: args, 65 | primaryType: "Authorization", 66 | }; 67 | }; 68 | -------------------------------------------------------------------------------- /packages/migration-sdk-viem/src/positions/signature/compoundV3.ts: -------------------------------------------------------------------------------- 1 | import type { Address, ChainId } from "@morpho-org/blue-sdk"; 2 | 3 | import type { TypedDataDefinition } from "viem"; 4 | 5 | interface CompoundV3ManagerApprovalArgs { 6 | instance: Address; 7 | name: string; 8 | owner: Address; 9 | manager: Address; 10 | isAllowed: boolean; 11 | nonce: bigint; 12 | expiry: bigint; 13 | } 14 | 15 | const compoundV3ManagerApprovalTypes = { 16 | Authorization: [ 17 | { 18 | name: "owner", 19 | type: "address", 20 | }, 21 | { 22 | name: "manager", 23 | type: "address", 24 | }, 25 | { 26 | name: "isAllowed", 27 | type: "bool", 28 | }, 29 | { 30 | name: "nonce", 31 | type: "uint256", 32 | }, 33 | { 34 | name: "expiry", 35 | type: "uint256", 36 | }, 37 | ], 38 | } as const; 39 | 40 | export const getCompoundV3ManagerApprovalMessage = ( 41 | { instance, name, ...args }: CompoundV3ManagerApprovalArgs, 42 | chainId: ChainId, 43 | ): TypedDataDefinition< 44 | typeof compoundV3ManagerApprovalTypes, 45 | "Authorization" 46 | > => { 47 | return { 48 | domain: { 49 | name, 50 | chainId, 51 | verifyingContract: instance, 52 | version: "0", 53 | }, 54 | message: args, 55 | primaryType: "Authorization", 56 | types: compoundV3ManagerApprovalTypes, 57 | }; 58 | }; 59 | -------------------------------------------------------------------------------- /packages/migration-sdk-viem/src/positions/supply/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./MigratableSupplyPosition.js"; 2 | export * from "./aaveV2.supply.js"; 3 | export * from "./aaveV3.supply.js"; 4 | export * from "./aaveV3Optimizer.supply.js"; 5 | export * from "./compoundV2.supply.js"; 6 | export * from "./compoundV3.supply.js"; 7 | -------------------------------------------------------------------------------- /packages/migration-sdk-viem/src/types/actions.ts: -------------------------------------------------------------------------------- 1 | import type { Address, Hex, TransactionRequest } from "viem"; 2 | 3 | export interface MigrationTransactionRequirementArgs { 4 | /* Morpho Aave V3 */ 5 | aaveV3OptimizerApproveManager: [Address, boolean]; 6 | 7 | /* Compound V3 */ 8 | compoundV3ApproveManager: [Address, boolean]; 9 | 10 | /* ERC20 */ 11 | erc20Approve: [asset: Address, recipient: Address, amount: bigint]; 12 | 13 | /* Morpho */ 14 | morphoSetAuthorization: [authorized: Address, isAuthorized: boolean]; 15 | } 16 | 17 | export type MigrationTransactionRequirementType = 18 | keyof MigrationTransactionRequirementArgs; 19 | 20 | export type Requirements = { 21 | [T in MigrationTransactionRequirementType]: { 22 | type: T; 23 | args: MigrationTransactionRequirementArgs[T]; 24 | tx: TransactionRequest & { to: Address; data: Hex }; 25 | }; 26 | }; 27 | 28 | export type MigrationTransactionRequirement = 29 | Requirements[MigrationTransactionRequirementType]; 30 | -------------------------------------------------------------------------------- /packages/migration-sdk-viem/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./actions.js"; 2 | export * from "./positions.js"; 3 | 4 | export enum MigratableProtocol { 5 | aaveV3Optimizer = "aaveV3Optimizer", 6 | aaveV2 = "aaveV2", 7 | aaveV3 = "aaveV3", 8 | compoundV3 = "compoundV3", 9 | /** 10 | * - `Compound V2` on _Ethereum Mainnet_ 11 | * - `Moonwell` on _Base Mainnet_ 12 | */ 13 | compoundV2 = "compoundV2", // moonwell on base 14 | } 15 | -------------------------------------------------------------------------------- /packages/migration-sdk-viem/src/types/positions.ts: -------------------------------------------------------------------------------- 1 | export enum SupplyMigrationLimiter { 2 | position = "position", 3 | liquidity = "liquidity", 4 | withdrawPaused = "withdrawPaused", 5 | borrowPaused = "borrowPaused", 6 | protocolCap = "protocolCap", 7 | } 8 | 9 | export enum BorrowMigrationLimiter { 10 | position = "position", 11 | repayPaused = "repayPaused", 12 | } 13 | 14 | export enum CollateralMigrationLimiter {} 15 | -------------------------------------------------------------------------------- /packages/migration-sdk-viem/src/utils/rates.ts: -------------------------------------------------------------------------------- 1 | import type { BigIntish } from "@morpho-org/blue-sdk"; 2 | import { Time } from "@morpho-org/morpho-ts"; 3 | import { formatUnits } from "viem"; 4 | 5 | export function rateToApy( 6 | rate: BigIntish, 7 | period: Time.PeriodLike, 8 | rateDecimals = 18, 9 | isApr = false, 10 | ) { 11 | const { unit, duration } = Time.toPeriod(period); 12 | const factor = Time[unit].from.y(1) / duration; 13 | 14 | rate = BigInt(rate); 15 | 16 | if (isApr) rate /= Time[unit].from.y(1n); 17 | 18 | return (1 + Number(formatUnits(rate, rateDecimals))) ** factor - 1; 19 | } 20 | -------------------------------------------------------------------------------- /packages/migration-sdk-viem/test/e2e/setup.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "@morpho-org/blue-sdk"; 2 | import { createViemTest } from "@morpho-org/test/vitest"; 3 | import { base, mainnet } from "viem/chains"; 4 | 5 | export const test = { 6 | [ChainId.EthMainnet]: createViemTest(mainnet, { 7 | forkUrl: process.env.MAINNET_RPC_URL, 8 | forkBlockNumber: 21_872_137, 9 | }), 10 | [ChainId.BaseMainnet]: createViemTest(base, { 11 | forkUrl: process.env.BASE_RPC_URL, 12 | forkBlockNumber: 26_539_234, 13 | }), 14 | } as const; 15 | -------------------------------------------------------------------------------- /packages/migration-sdk-viem/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src" 5 | }, 6 | "include": ["src"] 7 | } 8 | -------------------------------------------------------------------------------- /packages/migration-sdk-viem/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": ".", 6 | "baseUrl": "." 7 | }, 8 | "include": ["src", "test"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/morpho-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@morpho-org/morpho-test", 3 | "description": "Framework-agnostic extension of `@morpho-org/blue-sdk` that exports test fixtures useful for E2E tests on forks.", 4 | "version": "2.0.0", 5 | "author": "Morpho Association ", 6 | "contributors": ["Rubilmax "], 7 | "repository": "github:morpho-org/sdks", 8 | "homepage": "https://github.com/morpho-org/sdks", 9 | "bugs": { 10 | "url": "https://github.com/morpho-org/sdks/issues", 11 | "email": "contact@morpho.org" 12 | }, 13 | "main": "src/index.ts", 14 | "files": ["lib"], 15 | "license": "MIT", 16 | "scripts": { 17 | "prepublish": "$npm_execpath build", 18 | "build": "tsc" 19 | }, 20 | "peerDependencies": { 21 | "@morpho-org/blue-sdk": "workspace:^", 22 | "@morpho-org/morpho-ts": "workspace:^", 23 | "@morpho-org/test": "workspace:^", 24 | "viem": "^2.21.10" 25 | }, 26 | "devDependencies": { 27 | "@morpho-org/blue-sdk": "workspace:^", 28 | "@morpho-org/morpho-ts": "workspace:^", 29 | "@morpho-org/test": "workspace:^", 30 | "@types/node": "^22.13.1", 31 | "typescript": "^5.7.2", 32 | "viem": "^2.23.0" 33 | }, 34 | "publishConfig": { 35 | "main": "lib/index.js", 36 | "types": "lib/index.d.ts" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/morpho-test/src/fixtures/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./markets"; 2 | export * from "./vaults"; 3 | export * from "./tokens"; 4 | -------------------------------------------------------------------------------- /packages/morpho-test/src/fixtures/tokens.ts: -------------------------------------------------------------------------------- 1 | import { ChainId, addressesRegistry } from "@morpho-org/blue-sdk"; 2 | import type { Address } from "viem"; 3 | 4 | export const withSimplePermit: Record> = { 5 | [ChainId.EthMainnet]: new Set([ 6 | addressesRegistry[ChainId.EthMainnet].wstEth, 7 | addressesRegistry[ChainId.EthMainnet].sDai, 8 | addressesRegistry[ChainId.EthMainnet].osEth, 9 | addressesRegistry[ChainId.EthMainnet].usdc, 10 | addressesRegistry[ChainId.EthMainnet].dai, 11 | ]), 12 | [ChainId.BaseMainnet]: new Set([ 13 | addressesRegistry[ChainId.BaseMainnet].usdc, 14 | addressesRegistry[ChainId.BaseMainnet].verUsdc, 15 | ]), 16 | }; 17 | -------------------------------------------------------------------------------- /packages/morpho-test/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./fixtures"; 2 | -------------------------------------------------------------------------------- /packages/morpho-test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src", 6 | "baseUrl": "." 7 | }, 8 | "include": ["src"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/morpho-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@morpho-org/morpho-ts", 3 | "description": "TypeScript package to handle all things time & format-related.", 4 | "version": "2.0.0", 5 | "author": "Morpho Association ", 6 | "contributors": ["Rubilmax "], 7 | "repository": "github:morpho-org/sdks", 8 | "homepage": "https://github.com/morpho-org/sdks", 9 | "bugs": { 10 | "url": "https://github.com/morpho-org/sdks/issues", 11 | "email": "contact@morpho.org" 12 | }, 13 | "license": "MIT", 14 | "main": "src/index.ts", 15 | "files": ["lib"], 16 | "scripts": { 17 | "prepublish": "$npm_execpath build", 18 | "build": "tsc --noEmit && tsc --build tsconfig.build.json" 19 | }, 20 | "devDependencies": { 21 | "typescript": "^5.7.2", 22 | "vitest": "^3.0.5" 23 | }, 24 | "publishConfig": { 25 | "main": "lib/index.js", 26 | "types": "lib/index.d.ts" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/morpho-ts/src/format/array.ts: -------------------------------------------------------------------------------- 1 | function formatItemsList(items: string[], lastSeparator: string): string { 2 | switch (items.length) { 3 | case 0: 4 | return ""; 5 | case 1: 6 | return items[0]!; 7 | case 2: 8 | return `${items[0]} ${lastSeparator} ${items[1]}`; 9 | default: 10 | return `${items.slice(0, -1).join(", ")} ${lastSeparator} ${ 11 | items[items.length - 1] 12 | }`; 13 | } 14 | } 15 | 16 | /** 17 | * Humanizes an array of strings into a comma-separated list with an "or" before the last item. 18 | * For example, ["a", "b", "c"] becomes "a, b or c". 19 | * 20 | * @param items Array of strings 21 | * @returns Humanized union 22 | */ 23 | export function formatUnion(items: string[]): string { 24 | return formatItemsList(items, "or"); 25 | } 26 | 27 | /** 28 | * Humanizes an array of strings into a comma-separated list with an "and" before the last item. 29 | * For example, ["a", "b", "c"] becomes "a, b and c". 30 | * 31 | * @param items Array of strings 32 | * @returns Humanized enumeration 33 | */ 34 | export function formatEnumeration(items: string[]): string { 35 | return formatItemsList(items, "and"); 36 | } 37 | -------------------------------------------------------------------------------- /packages/morpho-ts/src/format/format/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./format"; 2 | -------------------------------------------------------------------------------- /packages/morpho-ts/src/format/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./string"; 2 | export * from "./array"; 3 | export * from "./locale"; 4 | export * from "./format"; 5 | -------------------------------------------------------------------------------- /packages/morpho-ts/src/format/string.ts: -------------------------------------------------------------------------------- 1 | export function formatLongString(str: string, maxLength?: number) { 2 | if (maxLength == null || maxLength >= str.length) return str; 3 | if (maxLength <= 3) return "..."; 4 | 5 | const nChar = maxLength - 3; 6 | 7 | if (nChar === 1) return `${str.slice(0, 1)}...`; 8 | 9 | return `${str.slice(0, Math.round(nChar / 2))}...${str.slice(-nChar / 2)}`; 10 | } 11 | -------------------------------------------------------------------------------- /packages/morpho-ts/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./time"; 2 | export * from "./utils"; 3 | export * from "./types"; 4 | export * from "./format"; 5 | export * from "./urls"; 6 | -------------------------------------------------------------------------------- /packages/morpho-ts/src/time/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./time"; 2 | -------------------------------------------------------------------------------- /packages/morpho-ts/src/types.ts: -------------------------------------------------------------------------------- 1 | export type WithId = T & { id: string }; 2 | export type WithIndex = T & { index: number }; 3 | 4 | export type ArrayElementType = T extends (infer U)[] ? U : never; 5 | 6 | type Digit = 1 | 2 | 3 | 4 | 5 | 6 | 7; 7 | type NextDigit = [1, 2, 3, 4, 5, 6, 7, "MAX"]; 8 | type Inc = T extends Digit ? NextDigit[T] : "MAX"; 9 | 10 | export type DottedKeys = T extends object | null | undefined 11 | ? { 12 | [K in keyof T & string]: T[K] extends object | null | undefined 13 | ? `${K}.${ 14 | // biome-ignore lint/suspicious/noExplicitAny: allow any keys from max depth on 15 | Depth extends "MAX" ? any : DottedKeys> 16 | }` 17 | : K; 18 | }[keyof T & string] 19 | : ""; 20 | 21 | export type PartialDottedKeys = T extends 22 | | object 23 | | null 24 | | undefined 25 | ? { 26 | [K in keyof T & string]: T[K] extends object | null | undefined 27 | ? 28 | | `${K}.${ 29 | // biome-ignore lint/suspicious/noExplicitAny: allow any keys from max depth on 30 | Depth extends "MAX" ? any : PartialDottedKeys> 31 | }` 32 | | K 33 | : K; 34 | }[keyof T & string] 35 | : ""; 36 | 37 | export type FieldType> = T extends null 38 | ? FieldType, Path> | null 39 | : T extends undefined 40 | ? FieldType, Path> | undefined 41 | : Path extends keyof T 42 | ? T[Path] 43 | : Path extends `${infer Left}.${infer Right}` 44 | ? Left extends keyof T 45 | ? FieldType 46 | : never 47 | : never; 48 | -------------------------------------------------------------------------------- /packages/morpho-ts/src/urls.ts: -------------------------------------------------------------------------------- 1 | export const MORPHO_DOMAIN = "morpho.org"; 2 | 3 | export const getSubdomainBaseUrl = (subDomain: string) => 4 | `https://${subDomain}.${MORPHO_DOMAIN}`; 5 | 6 | export const CDN_BASE_URL = getSubdomainBaseUrl("cdn"); 7 | export const DOCS_BASE_URL = getSubdomainBaseUrl("docs"); 8 | export const BLUE_API_BASE_URL = getSubdomainBaseUrl("blue-api"); 9 | export const REWARDS_BASE_URL = getSubdomainBaseUrl("rewards"); 10 | export const OPTIMIZERS_BASE_URL = getSubdomainBaseUrl("optimizers"); 11 | export const OPTIMIZERS_API_BASE_URL = getSubdomainBaseUrl("api"); 12 | 13 | export const BLUE_API_GRAPHQL_URL = new URL( 14 | "/graphql", 15 | BLUE_API_BASE_URL, 16 | ).toString(); 17 | -------------------------------------------------------------------------------- /packages/morpho-ts/test/time.test.ts: -------------------------------------------------------------------------------- 1 | import { Time } from "../src"; 2 | 3 | import { describe, expect, test } from "vitest"; 4 | 5 | describe("time", () => { 6 | test("Should resolve after 1 second", async () => { 7 | const start = Date.now(); 8 | await Time.wait(1000); 9 | const end = Date.now(); 10 | expect(end - start).toBeGreaterThanOrEqual(999); // Faced some cases where it was waiting for 999ms 11 | }); 12 | 13 | test("should convert ms to s", () => { 14 | const ms = 123456789999n; 15 | expect(Time.s.from.ms(ms)).toBe(123456789n); 16 | }); 17 | 18 | test("should convert s to ms", () => { 19 | const s = 123456789444n; 20 | expect(Time.ms.from.s(s)).toBe(123456789444000n); 21 | }); 22 | 23 | test("should convert periods", () => { 24 | expect(Time.ms.fromPeriod("s")).toBe(1000); 25 | expect(Time.ms.fromPeriod({ unit: "s", duration: 123 })).toBe(123000); 26 | expect(Time.min.fromPeriod({ unit: "s", duration: 123 })).toBe(2); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /packages/morpho-ts/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src" 5 | }, 6 | "include": ["src"] 7 | } 8 | -------------------------------------------------------------------------------- /packages/morpho-ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": ".", 6 | "baseUrl": "." 7 | }, 8 | "include": ["src", "test"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/simulation-sdk-wagmi/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./useSimulationState.js"; 2 | -------------------------------------------------------------------------------- /packages/simulation-sdk-wagmi/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./hooks/index.js"; 2 | -------------------------------------------------------------------------------- /packages/simulation-sdk-wagmi/test/setup.ts: -------------------------------------------------------------------------------- 1 | import { createWagmiTest } from "@morpho-org/test-wagmi"; 2 | import { mainnet } from "viem/chains"; 3 | 4 | export const test = createWagmiTest(mainnet, { 5 | forkUrl: process.env.MAINNET_RPC_URL, 6 | forkBlockNumber: 19_750_000, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/simulation-sdk-wagmi/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src" 5 | }, 6 | "include": ["src"] 7 | } 8 | -------------------------------------------------------------------------------- /packages/simulation-sdk-wagmi/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": ".", 6 | "baseUrl": "." 7 | }, 8 | "include": ["src", "test"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/simulation-sdk/README.md: -------------------------------------------------------------------------------- 1 | # @morpho-org/simulation-sdk 2 | 3 | 4 | 5 | 6 | Version 7 | 8 | 9 | 10 | 11 | 12 | MIT License 13 | 14 | 15 | 16 | 17 | 18 | Downloads per month 19 | 20 | 21 |
22 |
23 | 24 | Framework-agnostic package that defines methods to simulate interactions on Morpho (such as `Supply`, `Borrow`) and Morpho Vaults (such as `Deposit`, `Withdraw`). 25 | 26 | ## Installation 27 | 28 | ```bash 29 | npm install @morpho-org/simulation-sdk 30 | ``` 31 | 32 | ```bash 33 | yarn add @morpho-org/simulation-sdk 34 | ``` 35 | -------------------------------------------------------------------------------- /packages/simulation-sdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@morpho-org/simulation-sdk", 3 | "description": "Framework-agnostic package that defines methods to simulate interactions on Morpho (such as `Supply`, `Borrow`) and Morpho Vaults (such as `Deposit`, `Withdraw`).", 4 | "version": "2.0.0", 5 | "author": "Morpho Association ", 6 | "contributors": ["Rubilmax "], 7 | "repository": "github:morpho-org/sdks", 8 | "homepage": "https://github.com/morpho-org/sdks", 9 | "bugs": { 10 | "url": "https://github.com/morpho-org/sdks/issues", 11 | "email": "contact@morpho.org" 12 | }, 13 | "license": "MIT", 14 | "main": "src/index.ts", 15 | "files": ["lib"], 16 | "scripts": { 17 | "prepublish": "$npm_execpath build", 18 | "build": "tsc --noEmit && tsc --build tsconfig.build.json" 19 | }, 20 | "dependencies": { 21 | "mutative": "^1.1.0" 22 | }, 23 | "peerDependencies": { 24 | "@morpho-org/blue-sdk": "workspace:^", 25 | "@morpho-org/morpho-ts": "workspace:^" 26 | }, 27 | "devDependencies": { 28 | "@morpho-org/blue-sdk": "workspace:^", 29 | "@morpho-org/morpho-test": "workspace:^", 30 | "@morpho-org/morpho-ts": "workspace:^", 31 | "@morpho-org/test": "workspace:^", 32 | "@types/lodash": "^4.17.12", 33 | "lodash": "^4.17.21", 34 | "typescript": "^5.7.2", 35 | "viem": "^2.23.0", 36 | "vitest": "^3.0.5" 37 | }, 38 | "publishConfig": { 39 | "main": "lib/index.js", 40 | "types": "lib/index.d.ts" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/simulation-sdk/src/handlers/blue/accrueInterest.ts: -------------------------------------------------------------------------------- 1 | import type { BlueOperations } from "../../operations.js"; 2 | import type { OperationHandler } from "../types.js"; 3 | 4 | export const handleBlueAccrueInterestOperation: OperationHandler< 5 | BlueOperations["Blue_AccrueInterest"] 6 | > = ({ args: { id } }, data) => { 7 | const marketData = data.getMarket(id); 8 | const newMarketData = marketData.accrueInterest(data.block.timestamp); 9 | 10 | data.markets[id] = newMarketData; 11 | 12 | const { feeRecipient } = data.global; 13 | if (feeRecipient != null) { 14 | const feeRecipientPosition = data.tryGetPosition(feeRecipient, id); 15 | 16 | if (feeRecipientPosition != null) 17 | feeRecipientPosition.supplyShares += 18 | newMarketData.totalSupplyShares - marketData.totalSupplyShares; 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /packages/simulation-sdk/src/handlers/blue/setAuthorization.ts: -------------------------------------------------------------------------------- 1 | import { BlueErrors, getChainAddresses } from "@morpho-org/blue-sdk"; 2 | import type { BlueOperations } from "../../operations.js"; 3 | import type { OperationHandler } from "../types.js"; 4 | 5 | export const handleBlueSetAuthorizationOperation: OperationHandler< 6 | BlueOperations["Blue_SetAuthorization"] 7 | > = ({ args: { owner, authorized, isAuthorized } }, data) => { 8 | const ownerData = data.getUser(owner); 9 | 10 | const { 11 | bundler3: { generalAdapter1 }, 12 | } = getChainAddresses(data.chainId); 13 | 14 | if (authorized === generalAdapter1) { 15 | if (ownerData.isBundlerAuthorized === isAuthorized) 16 | throw new BlueErrors.AlreadySet( 17 | "isBundlerAuthorized", 18 | isAuthorized.toString(), 19 | ); 20 | 21 | ownerData.isBundlerAuthorized = isAuthorized; 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /packages/simulation-sdk/src/handlers/blue/supplyCollateral.ts: -------------------------------------------------------------------------------- 1 | import { MathLib, getChainAddresses } from "@morpho-org/blue-sdk"; 2 | 3 | import { BlueSimulationErrors } from "../../errors.js"; 4 | import type { BlueOperations } from "../../operations.js"; 5 | import { handleOperations } from "../dispatchers.js"; 6 | import { handleErc20Operation } from "../erc20/index.js"; 7 | import type { OperationHandler } from "../types.js"; 8 | 9 | export const handleBlueSupplyCollateralOperation: OperationHandler< 10 | BlueOperations["Blue_SupplyCollateral"] 11 | > = ({ args: { id, assets, onBehalf, callback }, sender }, data) => { 12 | const { 13 | params: { collateralToken }, 14 | } = data.getMarket(id); 15 | const { 16 | morpho, 17 | bundler3: { generalAdapter1 }, 18 | } = getChainAddresses(data.chainId); 19 | 20 | // Simulate the bundler's behavior on supply. 21 | if (sender === generalAdapter1 && assets === MathLib.MAX_UINT_256) 22 | assets = data.getHolding(sender, collateralToken).balance; 23 | 24 | if (assets === 0n) throw new BlueSimulationErrors.ZeroAssets(); 25 | 26 | const position = data.getPosition(onBehalf, id); 27 | 28 | position.collateral += assets; 29 | 30 | if (callback) handleOperations(callback(data), data); 31 | 32 | // Transfer collateral. 33 | handleErc20Operation( 34 | { 35 | type: "Erc20_Transfer", 36 | sender: morpho, 37 | address: collateralToken, 38 | args: { 39 | amount: assets, 40 | from: sender, 41 | to: morpho, 42 | }, 43 | }, 44 | data, 45 | ); 46 | }; 47 | -------------------------------------------------------------------------------- /packages/simulation-sdk/src/handlers/erc20/approve.ts: -------------------------------------------------------------------------------- 1 | import { getChainAddresses } from "@morpho-org/blue-sdk"; 2 | 3 | import { UnknownAllowanceError } from "../../errors.js"; 4 | import type { Erc20Operations } from "../../operations.js"; 5 | import type { OperationHandler } from "../types.js"; 6 | 7 | export const handleErc20ApproveOperation: OperationHandler< 8 | Erc20Operations["Erc20_Approve"] 9 | > = ({ args: { spender, amount }, sender, address }, data) => { 10 | const senderTokenData = data.getHolding(sender, address); 11 | 12 | const { 13 | morpho, 14 | bundler3: { generalAdapter1 }, 15 | permit2, 16 | } = getChainAddresses(data.chainId); 17 | 18 | const contract = 19 | spender === morpho 20 | ? "morpho" 21 | : spender === generalAdapter1 22 | ? "bundler3.generalAdapter1" 23 | : spender === permit2 24 | ? "permit2" 25 | : undefined; 26 | 27 | if (contract != null) senderTokenData.erc20Allowances[contract] = amount; 28 | else { 29 | const vault = data.tryGetVault(spender); 30 | 31 | if (vault != null && vault.asset === address) { 32 | const vaultUserData = data.getVaultUser(spender, sender); 33 | 34 | vaultUserData.allowance = amount; 35 | } else throw new UnknownAllowanceError(address, sender, spender); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /packages/simulation-sdk/src/handlers/erc20/index.ts: -------------------------------------------------------------------------------- 1 | import { SimulationErrors } from "../../errors.js"; 2 | import type { Erc20Operation } from "../../operations.js"; 3 | import type { OperationHandler } from "../types.js"; 4 | 5 | import { handleErc20ApproveOperation } from "./approve.js"; 6 | import { handleErc20PermitOperation } from "./permit.js"; 7 | import { handleErc20Permit2Operation } from "./permit2.js"; 8 | import { handleErc20TransferOperation } from "./transfer.js"; 9 | import { handleErc20Transfer2Operation } from "./transfer2.js"; 10 | import { handleErc20UnwrapOperation } from "./unwrap.js"; 11 | import { handleErc20WrapOperation } from "./wrap.js"; 12 | 13 | export const handleErc20Operation: OperationHandler = ( 14 | operation, 15 | data, 16 | ) => { 17 | if ("amount" in operation.args) { 18 | const { amount } = operation.args; 19 | 20 | if (amount < 0n) throw new SimulationErrors.InvalidInput({ amount }); 21 | } 22 | 23 | switch (operation.type) { 24 | case "Erc20_Approve": 25 | return handleErc20ApproveOperation(operation, data); 26 | case "Erc20_Permit": 27 | return handleErc20PermitOperation(operation, data); 28 | case "Erc20_Permit2": 29 | return handleErc20Permit2Operation(operation, data); 30 | case "Erc20_Transfer": 31 | return handleErc20TransferOperation(operation, data); 32 | case "Erc20_Transfer2": 33 | return handleErc20Transfer2Operation(operation, data); 34 | case "Erc20_Wrap": 35 | return handleErc20WrapOperation(operation, data); 36 | case "Erc20_Unwrap": 37 | return handleErc20UnwrapOperation(operation, data); 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /packages/simulation-sdk/src/handlers/erc20/permit.ts: -------------------------------------------------------------------------------- 1 | import { Erc20Errors, UnknownEIP2612DataError } from "../../errors.js"; 2 | import type { Erc20Operations } from "../../operations.js"; 3 | import type { OperationHandler } from "../types.js"; 4 | 5 | import { handleErc20ApproveOperation } from "./approve.js"; 6 | 7 | export const handleErc20PermitOperation: OperationHandler< 8 | Erc20Operations["Erc20_Permit"] 9 | > = ({ args: { spender, amount, nonce }, sender, address }, data) => { 10 | const senderTokenData = data.getHolding(sender, address); 11 | 12 | if (senderTokenData.erc2612Nonce == null) 13 | throw new UnknownEIP2612DataError(address, sender); 14 | 15 | if (senderTokenData.erc2612Nonce !== nonce) 16 | throw new Erc20Errors.InvalidEIP2612Nonce(address, sender, nonce); 17 | 18 | senderTokenData.erc2612Nonce += 1n; 19 | 20 | handleErc20ApproveOperation( 21 | { 22 | type: "Erc20_Approve", 23 | sender, 24 | address, 25 | args: { 26 | amount, 27 | spender, 28 | }, 29 | }, 30 | data, 31 | ); 32 | }; 33 | -------------------------------------------------------------------------------- /packages/simulation-sdk/src/handlers/erc20/permit2.ts: -------------------------------------------------------------------------------- 1 | import { Erc20Errors } from "../../errors.js"; 2 | import type { Erc20Operations } from "../../operations.js"; 3 | import type { OperationHandler } from "../types.js"; 4 | 5 | export const handleErc20Permit2Operation: OperationHandler< 6 | Erc20Operations["Erc20_Permit2"] 7 | > = ({ args: { amount, expiration, nonce }, sender, address }, data) => { 8 | const { permit2BundlerAllowance } = data.getHolding(sender, address); 9 | 10 | if (permit2BundlerAllowance.nonce !== nonce) 11 | throw new Erc20Errors.InvalidPermit2Nonce(address, sender, nonce); 12 | 13 | permit2BundlerAllowance.amount = amount; 14 | permit2BundlerAllowance.expiration = expiration; 15 | permit2BundlerAllowance.nonce += 1n; 16 | }; 17 | -------------------------------------------------------------------------------- /packages/simulation-sdk/src/handlers/erc20/transfer2.ts: -------------------------------------------------------------------------------- 1 | import { getChainAddresses } from "@morpho-org/blue-sdk"; 2 | 3 | import { Erc20Errors, UnexpectedOperation } from "../../errors.js"; 4 | import type { Erc20Operations } from "../../operations.js"; 5 | import type { OperationHandler } from "../types.js"; 6 | 7 | import { handleErc20TransferOperation } from "./transfer.js"; 8 | 9 | export const handleErc20Transfer2Operation: OperationHandler< 10 | Erc20Operations["Erc20_Transfer2"] 11 | > = ({ args: { amount, from, to }, address }, data) => { 12 | const fromHolding = data.tryGetHolding(from, address); 13 | if (fromHolding == null) 14 | throw new Erc20Errors.InsufficientBalance(address, from); 15 | 16 | const { permit2 } = getChainAddresses(data.chainId); 17 | if (permit2 == null) 18 | throw new UnexpectedOperation("Erc20_Transfer2", data.chainId); 19 | 20 | const { permit2BundlerAllowance } = fromHolding; 21 | if ( 22 | permit2BundlerAllowance.expiration < data.block.timestamp || 23 | permit2BundlerAllowance.amount < amount 24 | ) 25 | throw new Erc20Errors.InsufficientPermit2Allowance(address, from); 26 | 27 | permit2BundlerAllowance.amount -= amount; 28 | 29 | handleErc20TransferOperation( 30 | { 31 | type: "Erc20_Transfer", 32 | sender: permit2, 33 | address, 34 | args: { 35 | amount, 36 | from, 37 | to, 38 | }, 39 | }, 40 | data, 41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /packages/simulation-sdk/src/handlers/erc20/unwrap.ts: -------------------------------------------------------------------------------- 1 | import { 2 | MathLib, 3 | erc20WrapperTokens, 4 | getChainAddresses, 5 | } from "@morpho-org/blue-sdk"; 6 | 7 | import type { Erc20Operations } from "../../operations.js"; 8 | import type { OperationHandler } from "../types.js"; 9 | 10 | import { ZERO_ADDRESS } from "@morpho-org/morpho-ts"; 11 | import { handleErc20TransferOperation } from "./transfer.js"; 12 | 13 | export const handleErc20UnwrapOperation: OperationHandler< 14 | Erc20Operations["Erc20_Unwrap"] 15 | > = ({ address, args: { amount, receiver, slippage = 0n }, sender }, data) => { 16 | const { 17 | bundler3: { generalAdapter1 }, 18 | } = getChainAddresses(data.chainId); 19 | 20 | // Simulate the bundler's behavior on unwraps only with MaxUint256. 21 | if (sender === generalAdapter1 && amount === MathLib.MAX_UINT_256) 22 | amount = data.getHolding(sender, address).balance; 23 | 24 | const wrappedToken = data.getWrappedToken(address); 25 | const unwrappedAmount = wrappedToken.toUnwrappedExactAmountIn( 26 | amount, 27 | slippage, 28 | ); 29 | 30 | if (!erc20WrapperTokens[data.chainId]?.has(address)) receiver = sender; 31 | 32 | // Burn wrapped assets. 33 | handleErc20TransferOperation( 34 | { 35 | type: "Erc20_Transfer", 36 | sender: address, 37 | address, 38 | args: { 39 | amount, 40 | from: sender, 41 | to: ZERO_ADDRESS, 42 | }, 43 | }, 44 | data, 45 | ); 46 | 47 | // Mint unwrapped assets. 48 | handleErc20TransferOperation( 49 | { 50 | type: "Erc20_Transfer", 51 | sender: address, 52 | address: wrappedToken.underlying, 53 | args: { 54 | amount: unwrappedAmount, 55 | from: ZERO_ADDRESS, 56 | to: receiver, 57 | }, 58 | }, 59 | data, 60 | ); 61 | }; 62 | -------------------------------------------------------------------------------- /packages/simulation-sdk/src/handlers/erc20/wrap.ts: -------------------------------------------------------------------------------- 1 | import { MathLib, getChainAddresses } from "@morpho-org/blue-sdk"; 2 | 3 | import type { Erc20Operations } from "../../operations.js"; 4 | import type { OperationHandler } from "../types.js"; 5 | 6 | import { ZERO_ADDRESS } from "@morpho-org/morpho-ts"; 7 | import { handleErc20TransferOperation } from "./transfer.js"; 8 | 9 | export const handleErc20WrapOperation: OperationHandler< 10 | Erc20Operations["Erc20_Wrap"] 11 | > = ({ address, args: { amount, owner, slippage = 0n }, sender }, data) => { 12 | const { 13 | bundler3: { generalAdapter1 }, 14 | } = getChainAddresses(data.chainId); 15 | 16 | // Simulate the bundler's behavior on wraps only with MaxUint256. 17 | if (sender === generalAdapter1 && amount === MathLib.MAX_UINT_256) 18 | amount = data.getHolding(sender, address).balance; 19 | 20 | const wrappedToken = data.getWrappedToken(address); 21 | const wrappedAmount = wrappedToken.toWrappedExactAmountIn(amount, slippage); 22 | 23 | // Burn unwrapped assets. 24 | handleErc20TransferOperation( 25 | { 26 | type: "Erc20_Transfer", 27 | sender: address, 28 | address: wrappedToken.underlying, 29 | args: { 30 | amount, 31 | from: sender, 32 | to: ZERO_ADDRESS, 33 | }, 34 | }, 35 | data, 36 | ); 37 | 38 | // Mint wrapped assets. 39 | handleErc20TransferOperation( 40 | { 41 | type: "Erc20_Transfer", 42 | sender: address, 43 | address: wrappedToken.address, 44 | args: { 45 | amount: wrappedAmount, 46 | from: ZERO_ADDRESS, 47 | to: owner, 48 | }, 49 | }, 50 | data, 51 | ); 52 | }; 53 | -------------------------------------------------------------------------------- /packages/simulation-sdk/src/handlers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./dispatchers.js"; 2 | export * from "./types.js"; 3 | -------------------------------------------------------------------------------- /packages/simulation-sdk/src/handlers/metamorpho/accrueInterest.ts: -------------------------------------------------------------------------------- 1 | import { Vault } from "@morpho-org/blue-sdk"; 2 | 3 | import { ZERO_ADDRESS } from "@morpho-org/morpho-ts"; 4 | import type { MetaMorphoOperations } from "../../operations.js"; 5 | import { handleErc20Operation } from "../erc20/index.js"; 6 | import type { OperationHandler } from "../types.js"; 7 | 8 | export const handleMetaMorphoAccrueInterestOperation: OperationHandler< 9 | MetaMorphoOperations["MetaMorpho_AccrueInterest"] 10 | > = ({ address }, data) => { 11 | const vault = data.getAccrualVault(address); 12 | const newVault = vault.accrueInterest(data.block.timestamp); 13 | 14 | data.vaults[address] = new Vault(newVault); 15 | 16 | const feeShares = newVault.totalSupply - vault.totalSupply; 17 | 18 | // Mint fee shares. 19 | if (feeShares > 0n && vault.feeRecipient !== ZERO_ADDRESS) { 20 | handleErc20Operation( 21 | { 22 | type: "Erc20_Transfer", 23 | sender: address, 24 | address, 25 | args: { 26 | amount: feeShares, 27 | from: ZERO_ADDRESS, 28 | to: vault.feeRecipient, 29 | }, 30 | }, 31 | data, 32 | ); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /packages/simulation-sdk/src/handlers/metamorpho/index.ts: -------------------------------------------------------------------------------- 1 | import { SimulationErrors } from "../../errors.js"; 2 | import type { MetaMorphoOperation } from "../../operations.js"; 3 | import type { OperationHandler } from "../types.js"; 4 | 5 | import { handleMetaMorphoAccrueInterestOperation } from "./accrueInterest.js"; 6 | import { handleMetaMorphoDepositOperation } from "./deposit.js"; 7 | import { handleMetaMorphoPublicReallocateOperation } from "./publicReallocate.js"; 8 | import { handleMetaMorphoReallocateOperation } from "./reallocate.js"; 9 | import { handleMetaMorphoWithdrawOperation } from "./withdraw.js"; 10 | 11 | export const handleMetaMorphoOperation: OperationHandler< 12 | MetaMorphoOperation 13 | > = (operation, data) => { 14 | if ("assets" in operation.args) { 15 | const { assets = 0n } = operation.args; 16 | 17 | if (assets < 0n) throw new SimulationErrors.InvalidInput({ assets }); 18 | } 19 | 20 | if ("shares" in operation.args) { 21 | const { shares = 0n } = operation.args; 22 | 23 | if (shares < 0n) throw new SimulationErrors.InvalidInput({ shares }); 24 | } 25 | 26 | switch (operation.type) { 27 | case "MetaMorpho_AccrueInterest": 28 | return handleMetaMorphoAccrueInterestOperation(operation, data); 29 | case "MetaMorpho_Deposit": 30 | return handleMetaMorphoDepositOperation(operation, data); 31 | case "MetaMorpho_Withdraw": 32 | return handleMetaMorphoWithdrawOperation(operation, data); 33 | case "MetaMorpho_Reallocate": 34 | return handleMetaMorphoReallocateOperation(operation, data); 35 | case "MetaMorpho_PublicReallocate": 36 | return handleMetaMorphoPublicReallocateOperation(operation, data); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /packages/simulation-sdk/src/handlers/types.ts: -------------------------------------------------------------------------------- 1 | import type { Draft } from "mutative"; 2 | 3 | import type { SimulationState } from "../SimulationState.js"; 4 | import type { Operation } from "../operations.js"; 5 | 6 | export type MaybeDraft = T | Draft; 7 | 8 | export type OperationHandler = ( 9 | operation: O, 10 | data: MaybeDraft, 11 | ) => void; 12 | -------------------------------------------------------------------------------- /packages/simulation-sdk/src/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./mutative.js"; 2 | -------------------------------------------------------------------------------- /packages/simulation-sdk/src/helpers/mutative.ts: -------------------------------------------------------------------------------- 1 | import { type Draft, current, isDraft } from "mutative"; 2 | import type { MaybeDraft } from "../handlers/index.js"; 3 | 4 | export const getCurrent = (draft: MaybeDraft) => 5 | isDraft(draft) ? current(draft as Draft) : (draft as T); 6 | -------------------------------------------------------------------------------- /packages/simulation-sdk/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./errors.js"; 2 | export * from "./operations.js"; 3 | export * from "./handlers/index.js"; 4 | export * from "./SimulationState.js"; 5 | export * from "./helpers/index.js"; 6 | -------------------------------------------------------------------------------- /packages/simulation-sdk/test/handlers/dispatchers.test.ts: -------------------------------------------------------------------------------- 1 | import _ from "lodash"; 2 | 3 | import { simulateOperation } from "../../src/index.js"; 4 | import { dataFixture, tokenA, userA, userB } from "../fixtures.js"; 5 | 6 | import { describe, expect, test } from "vitest"; 7 | 8 | const dataFixtureCopy = _.cloneDeep(dataFixture); 9 | 10 | describe("dispatchers", () => { 11 | test("should not mutate data", async () => { 12 | simulateOperation( 13 | { 14 | type: "Erc20_Transfer", 15 | sender: userB, 16 | address: tokenA, 17 | args: { amount: 0n, from: userB, to: userA }, 18 | }, 19 | dataFixture, 20 | ); 21 | 22 | expect(dataFixtureCopy).toEqual(dataFixture); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/simulation-sdk/test/handlers/erc20/permit2.test.ts: -------------------------------------------------------------------------------- 1 | import _ from "lodash"; 2 | 3 | import { describe, expect, test } from "vitest"; 4 | import { simulateOperation } from "../../../src/index.js"; 5 | import { dataFixture, tokenA, userA } from "../../fixtures.js"; 6 | 7 | const type = "Erc20_Permit2"; 8 | 9 | describe(type, () => { 10 | test("should permit2 bundler", () => { 11 | const result = simulateOperation( 12 | { 13 | type, 14 | sender: userA, 15 | address: tokenA, 16 | args: { 17 | amount: 2n, 18 | expiration: 5n, 19 | nonce: 1n, 20 | }, 21 | }, 22 | dataFixture, 23 | ); 24 | 25 | const expected = _.cloneDeep(dataFixture); 26 | expected.holdings[userA]![tokenA]!.permit2BundlerAllowance.amount = 2n; 27 | expected.holdings[userA]![tokenA]!.permit2BundlerAllowance.expiration = 5n; 28 | expected.holdings[userA]![tokenA]!.permit2BundlerAllowance.nonce = 2n; 29 | 30 | expect(result).toEqual(expected); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /packages/simulation-sdk/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src" 5 | }, 6 | "include": ["src"] 7 | } 8 | -------------------------------------------------------------------------------- /packages/simulation-sdk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": ".", 6 | "baseUrl": "." 7 | }, 8 | "include": ["src", "test"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/test-wagmi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@morpho-org/test-wagmi", 3 | "description": "Wagmi-based extension of `@morpho-org/test` that injects a test Wagmi config as a test fixture alongside viem's anvil client.", 4 | "version": "2.0.0", 5 | "author": "Morpho Association ", 6 | "contributors": ["Rubilmax "], 7 | "repository": "github:morpho-org/sdks", 8 | "homepage": "https://github.com/morpho-org/sdks", 9 | "bugs": { 10 | "url": "https://github.com/morpho-org/sdks/issues", 11 | "email": "contact@morpho.org" 12 | }, 13 | "type": "module", 14 | "main": "src/index.ts", 15 | "files": ["lib"], 16 | "license": "MIT", 17 | "scripts": { 18 | "prepublish": "$npm_execpath build", 19 | "build": "tsc" 20 | }, 21 | "peerDependencies": { 22 | "@morpho-org/test": "workspace:^", 23 | "@tanstack/react-query": "^5.59.0", 24 | "@testing-library/dom": "^10.4.0", 25 | "@testing-library/react": "^16.0.0", 26 | "viem": "^2.21.10", 27 | "vitest": ">=2.0.0 <4.0.0", 28 | "wagmi": "^2.12.0" 29 | }, 30 | "devDependencies": { 31 | "@morpho-org/test": "workspace:^", 32 | "@tanstack/react-query": "^5.62.11", 33 | "@testing-library/dom": "^10.4.0", 34 | "@testing-library/react": "^16.1.0", 35 | "@types/node": "^22.13.1", 36 | "@types/react": "^19.0.7", 37 | "@types/react-dom": "^19.0.2", 38 | "@wagmi/core": "^2.16.3", 39 | "react": "^19.0.0", 40 | "react-dom": "^19.0.0", 41 | "typescript": "^5.7.2", 42 | "viem": "^2.23.0", 43 | "vitest": "^3.0.5", 44 | "wagmi": "^2.14.10" 45 | }, 46 | "publishConfig": { 47 | "main": "lib/index.js", 48 | "types": "lib/index.d.ts" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/test-wagmi/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./vitest.js"; 2 | export * from "./react.js"; 3 | -------------------------------------------------------------------------------- /packages/test-wagmi/src/vitest.ts: -------------------------------------------------------------------------------- 1 | import { type AnvilArgs, testAccount } from "@morpho-org/test"; 2 | import { type ViemTestContext, createViemTest } from "@morpho-org/test/vitest"; 3 | import { mock } from "@wagmi/core"; 4 | import type { Chain, HttpTransport } from "viem"; 5 | import { type Config, createConfig } from "wagmi"; 6 | 7 | export interface WagmiConfigTestContext { 8 | config: Config>; 9 | } 10 | 11 | export interface WagmiTestContext 12 | extends ViemTestContext, 13 | WagmiConfigTestContext {} 14 | 15 | export const createWagmiTest = ( 16 | chain: chain, 17 | parameters?: AnvilArgs, 18 | ) => { 19 | return createViemTest(chain, parameters).extend< 20 | WagmiConfigTestContext 21 | >({ 22 | config: async ({ client }, use) => { 23 | await use( 24 | createConfig({ 25 | chains: [chain], 26 | connectors: [ 27 | mock({ 28 | accounts: [ 29 | testAccount().address, 30 | ...new Array(parameters?.accounts ?? 9) 31 | .fill(null) 32 | .map((_, i) => testAccount(i + 1).address), 33 | ], 34 | }), 35 | ], 36 | storage: null, 37 | client: () => client, 38 | }), 39 | ); 40 | }, 41 | }); 42 | }; 43 | -------------------------------------------------------------------------------- /packages/test-wagmi/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src", 6 | "baseUrl": "." 7 | }, 8 | "include": ["src"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/test/src/fixtures/ethers.ts: -------------------------------------------------------------------------------- 1 | import { HDNodeWallet, type Provider } from "ethers"; 2 | 3 | export function testWallet( 4 | provider: T, 5 | index?: number, 6 | ): HDNodeWallet & { provider: T }; 7 | export function testWallet( 8 | provider?: T, 9 | index?: number, 10 | ): HDNodeWallet; 11 | export function testWallet(provider?: T, index = 0) { 12 | return HDNodeWallet.fromPhrase( 13 | "test test test test test test test test test test test junk", 14 | undefined, 15 | `m/44'/60'/0'/0/${index}`, 16 | ).connect(provider ?? null); 17 | } 18 | -------------------------------------------------------------------------------- /packages/test/src/fixtures/index.ts: -------------------------------------------------------------------------------- 1 | import { checksumAddress } from "viem"; 2 | import { mnemonicToAccount } from "viem/accounts"; 3 | 4 | let seed = 1; 5 | const seededRandom = () => { 6 | const x = Math.sin(seed++) * 10000; 7 | return x - Math.floor(x); 8 | }; 9 | 10 | export const randomAddress = (chainId?: number) => 11 | checksumAddress( 12 | `0x${new Array(40) 13 | .fill(0) 14 | .map(() => Math.floor(seededRandom() * 16).toString(16)) 15 | .join("")}`, 16 | chainId, 17 | ); 18 | 19 | export const testAccount = (addressIndex?: number) => 20 | mnemonicToAccount( 21 | "test test test test test test test test test test test junk", 22 | { addressIndex }, 23 | ); 24 | -------------------------------------------------------------------------------- /packages/test/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./client"; 2 | export * from "./fixtures"; 3 | export * from "./anvil"; 4 | -------------------------------------------------------------------------------- /packages/test/src/vitest/ethers.ts: -------------------------------------------------------------------------------- 1 | import { type HDNodeWallet, JsonRpcProvider } from "ethers"; 2 | import type { Chain } from "viem"; 3 | import type { AnvilArgs } from "../anvil"; 4 | import { testWallet } from "../fixtures/ethers"; 5 | import { type ViemTestContext, createViemTest } from "./index"; 6 | 7 | export interface EthersWalletTestContext { 8 | wallet: HDNodeWallet & { provider: JsonRpcProvider }; 9 | } 10 | 11 | export interface EthersTestContext 12 | extends ViemTestContext, 13 | EthersWalletTestContext {} 14 | 15 | export const createEthersTest = ( 16 | chain: chain, 17 | parameters?: AnvilArgs, 18 | ) => { 19 | return createViemTest(chain, parameters).extend({ 20 | wallet: async ({ client }, use) => { 21 | await use( 22 | testWallet( 23 | new JsonRpcProvider(client.transport.url, undefined, { 24 | staticNetwork: true, 25 | }), 26 | ), 27 | ); 28 | }, 29 | }); 30 | }; 31 | -------------------------------------------------------------------------------- /packages/test/src/vitest/index.ts: -------------------------------------------------------------------------------- 1 | import { http, type Chain } from "viem"; 2 | import { test } from "vitest"; 3 | import { type AnvilArgs, spawnAnvil } from "../anvil"; 4 | import { type AnvilTestClient, createAnvilTestClient } from "../client"; 5 | 6 | // Vitest needs to serialize BigInts to JSON, so we need to add a toJSON method to BigInt.prototype. 7 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#use_within_json 8 | // @ts-ignore 9 | BigInt.prototype.toJSON = function () { 10 | return this.toString(); 11 | }; 12 | 13 | export interface ViemTestContext { 14 | client: AnvilTestClient; 15 | } 16 | 17 | export const createViemTest = ( 18 | chain: chain, 19 | parameters: AnvilArgs = {}, 20 | ) => { 21 | parameters.forkChainId ??= chain?.id; 22 | parameters.forkUrl ??= chain?.rpcUrls.default.http[0]; 23 | parameters.autoImpersonate ??= true; 24 | parameters.order ??= "fifo"; 25 | parameters.stepsTracing ??= true; 26 | parameters.pruneHistory ??= true; 27 | 28 | parameters.gasPrice ??= 0n; 29 | parameters.blockBaseFeePerGas ??= 0n; 30 | 31 | return test.extend>({ 32 | // biome-ignore lint/correctness/noEmptyPattern: required by vitest at runtime 33 | client: async ({}, use) => { 34 | const { rpcUrl, stop } = await spawnAnvil(parameters); 35 | 36 | const client = createAnvilTestClient( 37 | http(rpcUrl, { 38 | fetchOptions: { 39 | cache: "force-cache", 40 | }, 41 | timeout: 30_000, 42 | }), 43 | chain, 44 | ); 45 | 46 | // Make block timestamp 100% predictable. 47 | await client.setBlockTimestampInterval({ interval: 1 }); 48 | 49 | await use(client); 50 | 51 | await stop(); 52 | }, 53 | }); 54 | }; 55 | -------------------------------------------------------------------------------- /packages/test/tsconfig.build.cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "module": "CommonJS", 6 | "moduleResolution": "node10", 7 | "outDir": "./lib/cjs", 8 | "tsBuildInfoFile": "tsconfig.cjs.tsbuildinfo" 9 | }, 10 | "include": ["src"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/test/tsconfig.build.esm.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "outDir": "./lib/esm", 8 | "tsBuildInfoFile": "tsconfig.esm.tsbuildinfo" 9 | }, 10 | "include": ["src"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "baseUrl": "." 6 | }, 7 | "include": ["src"] 8 | } 9 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - packages/* -------------------------------------------------------------------------------- /scripts/lint/checksum-address.js: -------------------------------------------------------------------------------- 1 | import { lstatSync, readFileSync, readdirSync, writeFileSync } from "node:fs"; 2 | import { join } from "node:path"; 3 | import { getAddress } from "viem"; 4 | import config from "../../biome.json" with { type: "json" }; 5 | 6 | const lint = (path) => { 7 | const files = readdirSync(path, { encoding: "utf-8" }); 8 | 9 | for (const file of files) { 10 | if (file === ".git" || file === "node_modules") continue; 11 | 12 | const filePath = join(path, file); 13 | if (lstatSync(filePath).isDirectory()) { 14 | lint(filePath); 15 | continue; 16 | } 17 | 18 | if ( 19 | (!file.endsWith(".ts") && !file.endsWith(".js")) || 20 | config.files.ignore.some((ignored) => file.includes(ignored)) 21 | ) 22 | continue; 23 | 24 | const content = readFileSync(filePath, { encoding: "utf-8" }); 25 | const checksummedContent = content.replaceAll( 26 | /0x[0-9a-f]{40}(?![0-9a-f])/gi, 27 | (address) => getAddress(address), 28 | ); 29 | 30 | if (checksummedContent !== content) 31 | writeFileSync(filePath, checksummedContent); 32 | } 33 | }; 34 | 35 | lint("."); 36 | -------------------------------------------------------------------------------- /scripts/release/release.js: -------------------------------------------------------------------------------- 1 | import { writeFileSync } from "node:fs"; 2 | import { setOutput } from "@actions/core"; 3 | import { writeChangelogString } from "conventional-changelog-writer"; 4 | import { 5 | branch, 6 | channel, 7 | commits, 8 | lastTag, 9 | releaseType, 10 | tagParams, 11 | version, 12 | writer, 13 | } from "./bumper.js"; 14 | 15 | if (releaseType) { 16 | const tag = `${tagParams.prefix}v${version}`; 17 | 18 | setOutput("tag", tag); 19 | setOutput("version", version); 20 | setOutput("branch", branch); 21 | setOutput("channel", channel); 22 | 23 | writeFileSync( 24 | "CHANGELOG.md", 25 | await writeChangelogString( 26 | commits, 27 | { 28 | version: 29 | lastTag == null 30 | ? tag 31 | : `[${tag}](https://github.com/morpho-org/sdks/compare/${lastTag}...${tag})`, 32 | host: "https://github.com", 33 | owner: "morpho-org", 34 | repository: "sdks", 35 | commit: "commit", 36 | }, 37 | writer, 38 | ), 39 | ); 40 | } 41 | 42 | process.exit(0); // Sometimes hangs. 43 | -------------------------------------------------------------------------------- /scripts/release/version.js: -------------------------------------------------------------------------------- 1 | import { spawnSync } from "node:child_process"; 2 | import { version } from "./bumper.js"; 3 | 4 | spawnSync("pnpm", ["version", version, "--no-git-tag-version"], { 5 | encoding: "utf8", 6 | }); 7 | 8 | process.exit(0); // Sometimes hangs. 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "target": "ESNext", 5 | "module": "NodeNext", 6 | "moduleResolution": "NodeNext", 7 | "moduleDetection": "force", 8 | "skipLibCheck": true, 9 | "strict": true, 10 | "noImplicitAny": true, 11 | "strictNullChecks": true, 12 | "strictFunctionTypes": true, 13 | "declaration": true, 14 | "strictPropertyInitialization": true, 15 | "noImplicitThis": true, 16 | "forceConsistentCasingInFileNames": true, 17 | "noUncheckedIndexedAccess": true, 18 | "esModuleInterop": true, 19 | "resolveJsonModule": true, 20 | "experimentalDecorators": true, 21 | "jsx": "preserve" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /vitest.setup.ts: -------------------------------------------------------------------------------- 1 | import { exec, execSync } from "node:child_process"; 2 | 3 | export const setup = async () => { 4 | try { 5 | const data = await execSync("lsof -c anvil -t"); 6 | 7 | const pids = data.toString().split("\n").slice(0, -1); 8 | 9 | console.debug(`Clearing ports: ${pids.join(", ")}`); 10 | 11 | for (const pid of pids) { 12 | exec(`kill -9 ${pid}`, (error) => { 13 | if (error) console.error(`Error while killing ${pid}: ${error}`); 14 | }); 15 | } 16 | } catch {} 17 | }; 18 | --------------------------------------------------------------------------------