diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz index 8198205b..1a309409 100644 Binary files a/.yarn/install-state.gz and b/.yarn/install-state.gz differ diff --git a/lerna.json b/lerna.json new file mode 100644 index 00000000..0fedcc85 --- /dev/null +++ b/lerna.json @@ -0,0 +1,6 @@ +{ + "$schema": "node_modules/@lerna-lite/cli/schemas/lerna-schema.json", + "version": "independent", + "npmClient": "yarn", + "useWorkspaces": true +} diff --git a/packages/ethers/package.json b/packages/ethers/package.json index dde35e79..f88a4162 100644 --- a/packages/ethers/package.json +++ b/packages/ethers/package.json @@ -1,8 +1,8 @@ { "name": "@morpho-org/bundlers-sdk-ethers", - "description": "Morpho Blue Bundlers SDK", + "description": "Morpho Blue Bundlers SDK (ethers-based)", "license": "GPL-2.0-or-later", - "version": "1.2.0", + "version": "1.0.0", "repository": { "type": "git", "url": "git+https://github.com/morpho-labs/morpho-blue-bundlers.git" @@ -16,11 +16,6 @@ "url": "https://github.com/morpho-labs/morpho-blue-bundlers/issues" }, "homepage": "https://github.com/morpho-labs/morpho-blue-bundlers#readme", - "private": true, - "packageManager": "yarn@4.4.0", - "workspaces": [ - "packages/*" - ], "scripts": { "prepublish": "yarn build", "prepare": "ln -sfn ../../src/ ./contracts && ln -sfn ../../lib/ ./lib", diff --git a/packages/viem/.env.example b/packages/viem/.env.example new file mode 100644 index 00000000..e4da1c67 --- /dev/null +++ b/packages/viem/.env.example @@ -0,0 +1 @@ +ALCHEMY_KEY= diff --git a/packages/viem/.gitignore b/packages/viem/.gitignore new file mode 100644 index 00000000..3e23fc20 --- /dev/null +++ b/packages/viem/.gitignore @@ -0,0 +1,7 @@ +# hardhat +contracts/ +artifacts/ +cache_hardhat/ +dist/ +lib/ +foundry.toml \ No newline at end of file diff --git a/packages/viem/contracts b/packages/viem/contracts new file mode 120000 index 00000000..a8d2a675 --- /dev/null +++ b/packages/viem/contracts @@ -0,0 +1 @@ +../../src/ \ No newline at end of file diff --git a/packages/viem/hardhat.config.ts b/packages/viem/hardhat.config.ts new file mode 100644 index 00000000..05adaefb --- /dev/null +++ b/packages/viem/hardhat.config.ts @@ -0,0 +1,16 @@ +import "evm-maths"; +import "hardhat-gas-reporter"; +import "hardhat-tracer"; +import "solidity-coverage"; + +import "@nomicfoundation/hardhat-chai-matchers"; +import "@nomicfoundation/hardhat-ethers"; +import "@typechain/hardhat"; + +import config from "../../hardhat.config"; + +config.typechain = { + outDir: "src/types/", +}; + +export default config; diff --git a/packages/viem/lib b/packages/viem/lib new file mode 120000 index 00000000..bc1a1ee0 --- /dev/null +++ b/packages/viem/lib @@ -0,0 +1 @@ +../../lib/ \ No newline at end of file diff --git a/packages/viem/package.json b/packages/viem/package.json new file mode 100644 index 00000000..e74cba85 --- /dev/null +++ b/packages/viem/package.json @@ -0,0 +1,58 @@ +{ + "name": "@morpho-org/bundlers-sdk-viem", + "description": "Morpho Blue Bundlers SDK (viem-based)", + "license": "GPL-2.0-or-later", + "version": "1.0.0", + "repository": { + "type": "git", + "url": "git+https://github.com/morpho-labs/morpho-blue-bundlers.git" + }, + "author": { + "name": "Morpho Labs", + "email": "security@morpho.org", + "url": "https://github.com/morpho-labs" + }, + "bugs": { + "url": "https://github.com/morpho-labs/morpho-blue-bundlers/issues" + }, + "homepage": "https://github.com/morpho-labs/morpho-blue-bundlers#readme", + "scripts": { + "prepublish": "yarn build", + "prepare": "ln -sfn ../../src/ ./contracts && ln -sfn ../../lib/ ./lib", + "typecheck": "tsc --noEmit", + "build": "yarn prepare && hardhat compile --force && tsc --build ./tsconfig.build.json", + "test": "yarn prepare && hardhat test" + }, + "peerDependencies": { + "evm-maths": "^6.0.0", + "viem": "^2.0.0" + }, + "devDependencies": { + "@nomicfoundation/hardhat-chai-matchers": "^2.0.2", + "@nomicfoundation/hardhat-ethers": "^3.0.4", + "@nomicfoundation/hardhat-foundry": "^1.1.1", + "@nomicfoundation/hardhat-network-helpers": "^1.0.9", + "@typechain/ethers-v6": "^0.5.1", + "@typechain/hardhat": "^9.1.0", + "@types/chai": "^4.3.8", + "@types/mocha": "^10.0.2", + "@types/node": "^20.8.6", + "chai": "^4.3.10", + "dotenv": "^16.3.1", + "evm-maths": "^7.0.0", + "hardhat": "^2.18.1", + "hardhat-gas-reporter": "^1.0.9", + "hardhat-tracer": "^2.6.0", + "solidity-coverage": "^0.8.5", + "solmate": "6.2.0", + "ts-node": "^10.9.1", + "tsconfig-paths": "^4.2.0", + "typechain": "^8.3.2", + "typescript": "^5.2.2", + "viem": "^2.21.15" + }, + "publishConfig": { + "main": "dist/index.js", + "access": "public" + } +} diff --git a/packages/viem/src/BundlerAction.ts b/packages/viem/src/BundlerAction.ts new file mode 100644 index 00000000..b506e9b9 --- /dev/null +++ b/packages/viem/src/BundlerAction.ts @@ -0,0 +1,685 @@ +import { BigNumberish, BytesLike, Signature } from "ethers"; + +import { + AaveV2MigrationBundlerV2__factory, + AaveV3MigrationBundlerV2__factory, + AaveV3OptimizerMigrationBundlerV2__factory, + CompoundV2MigrationBundlerV2__factory, + CompoundV3MigrationBundlerV2__factory, + ERC20WrapperBundler__factory, + ERC4626Bundler__factory, + EthereumPermitBundler__factory, + IAllowanceTransfer, + MorphoBundler__factory, + Permit2Bundler__factory, + PermitBundler__factory, + StEthBundler__factory, + TransferBundler__factory, + UrdBundler__factory, + WNativeBundler__factory, +} from "./types"; +import { AuthorizationStruct, MarketParamsStruct, WithdrawalStruct } from "./types/contracts/MorphoBundler"; + +export type BundlerCall = string; + +const TRANSFER_BUNDLER_IFC = TransferBundler__factory.createInterface(); +const PERMIT_BUNDLER_IFC = PermitBundler__factory.createInterface(); +const PERMIT2_BUNDLER_IFC = Permit2Bundler__factory.createInterface(); +const ERC20_WRAPPER_BUNDLER_IFC = ERC20WrapperBundler__factory.createInterface(); +const ERC4626_BUNDLER_IFC = ERC4626Bundler__factory.createInterface(); +const MORPHO_BUNDLER_IFC = MorphoBundler__factory.createInterface(); +const URD_BUNDLER_IFC = UrdBundler__factory.createInterface(); +const WNATIVE_BUNDLER_IFC = WNativeBundler__factory.createInterface(); +const ST_ETH_BUNDLER_IFC = StEthBundler__factory.createInterface(); +const ETHEREUM_PERMIT_BUNDLER_IFC = EthereumPermitBundler__factory.createInterface(); + +const AAVE_V2_BUNDLER_IFC = AaveV2MigrationBundlerV2__factory.createInterface(); +const AAVE_V3_BUNDLER_IFC = AaveV3MigrationBundlerV2__factory.createInterface(); +const AAVE_V3_OPTIMIZER_BUNDLER_IFC = AaveV3OptimizerMigrationBundlerV2__factory.createInterface(); +const COMPOUND_V2_BUNDLER_IFC = CompoundV2MigrationBundlerV2__factory.createInterface(); +const COMPOUND_V3_BUNDLER_IFC = CompoundV3MigrationBundlerV2__factory.createInterface(); + +/** + * Namespace to easily encode calls to the Bundler contract, using ethers. + */ +export namespace BundlerAction { + /* ERC20 */ + + /** + * Encodes a call to the Bundler to transfer native tokens (ETH on ethereum, MATIC on polygon, etc). + * @param recipient The address to send native tokens to. + * @param amount The amount of native tokens to send (in wei). + */ + export function nativeTransfer(recipient: string, amount: BigNumberish): BundlerCall { + return TRANSFER_BUNDLER_IFC.encodeFunctionData("nativeTransfer", [recipient, amount]); + } + + /** + * Encodes a call to the Bundler to transfer ERC20 tokens. + * @param asset The address of the ERC20 token to transfer. + * @param recipient The address to send tokens to. + * @param amount The amount of tokens to send. + */ + export function erc20Transfer(asset: string, recipient: string, amount: BigNumberish): BundlerCall { + return TRANSFER_BUNDLER_IFC.encodeFunctionData("erc20Transfer", [asset, recipient, amount]); + } + + /** + * Encodes a call to the Bundler to transfer ERC20 tokens from the sender to the Bundler. + * @param asset The address of the ERC20 token to transfer. + * @param amount The amount of tokens to send. + */ + export function erc20TransferFrom(asset: string, amount: BigNumberish): BundlerCall { + return TRANSFER_BUNDLER_IFC.encodeFunctionData("erc20TransferFrom", [asset, amount]); + } + + /* Permit */ + + /** + * Encodes a call to the Bundler to permit an ERC20 token. + * @param asset The address of the ERC20 token to permit. + * @param amount The amount of tokens to permit. + * @param deadline The timestamp until which the signature is valid. + * @param signature The Ethers signature to permit the tokens. + * @param skipRevert Whether to allow the permit to revert without making the whole multicall revert. + */ + export function permit( + asset: string, + amount: BigNumberish, + deadline: BigNumberish, + signature: Signature, + skipRevert: boolean, + ): BundlerCall { + return PERMIT_BUNDLER_IFC.encodeFunctionData("permit", [ + asset, + amount, + deadline, + signature.v, + signature.r, + signature.s, + skipRevert, + ]); + } + + /** + * Encodes a call to the Bundler to permit DAI. + * @param nonce The permit nonce used. + * @param expiry The timestamp until which the signature is valid. + * @param allowed The amount of DAI to permit. + * @param signature The Ethers signature to permit the tokens. + * @param skipRevert Whether to allow the permit to revert without making the whole multicall revert. + */ + export function permitDai( + nonce: BigNumberish, + expiry: BigNumberish, + allowed: boolean, + signature: Signature, + skipRevert: boolean, + ): BundlerCall { + return ETHEREUM_PERMIT_BUNDLER_IFC.encodeFunctionData("permitDai", [ + nonce, + expiry, + allowed, + signature.v, + signature.r, + signature.s, + skipRevert, + ]); + } + + /* Permit2 */ + + /** + * Encodes a call to the Bundler to permit ERC20 tokens via Permit2. + * @param permitSingle The permit details to submit to Permit2. + * @param signature The Ethers signature to permit the tokens. + * @param skipRevert Whether to allow the permit to revert without making the whole multicall revert. + */ + export function approve2( + permitSingle: IAllowanceTransfer.PermitSingleStruct, + signature: Signature, + skipRevert: boolean, + ): BundlerCall { + return PERMIT2_BUNDLER_IFC.encodeFunctionData("approve2", [permitSingle, signature.serialized, skipRevert]); + } + + /** + * Encodes a call to the Bundler to transfer ERC20 tokens via Permit2 from the sender to the Bundler. + * @param asset The address of the ERC20 token to transfer. + * @param amount The amount of tokens to send. + */ + export function transferFrom2(asset: string, amount: BigNumberish): BundlerCall { + return PERMIT2_BUNDLER_IFC.encodeFunctionData("transferFrom2", [asset, amount]); + } + + /* ERC20 Wrapper */ + + /** + * Encodes a call to the Bundler to wrap ERC20 tokens via the provided ERC20Wrapper. + * @param wrapper The address of the ERC20 wrapper token. + * @param amount The amount of tokens to send. + */ + export function erc20WrapperDepositFor(wrapper: string, amount: BigNumberish): BundlerCall { + return ERC20_WRAPPER_BUNDLER_IFC.encodeFunctionData("erc20WrapperDepositFor", [wrapper, amount]); + } + + /** + * Encodes a call to the Bundler to unwrap ERC20 tokens from the provided ERC20Wrapper. + * @param wrapper The address of the ERC20 wrapper token. + * @param account The address to send the underlying ERC20 tokens. + * @param amount The amount of tokens to send. + */ + export function erc20WrapperWithdrawTo(wrapper: string, account: string, amount: BigNumberish): BundlerCall { + return ERC20_WRAPPER_BUNDLER_IFC.encodeFunctionData("erc20WrapperWithdrawTo", [wrapper, account, amount]); + } + + /* ERC4626 */ + + /** + * Encodes a call to the Bundler to mint shares of the provided ERC4626 vault. + * @param erc4626 The address of the ERC4626 vault. + * @param shares The amount of shares to mint. + * @param maxAssets The maximum amount of assets to deposit (protects the sender from unexpected slippage). + * @param receiver The address to send the shares to. + */ + export function erc4626Mint( + erc4626: string, + shares: BigNumberish, + maxAssets: BigNumberish, + receiver: string, + ): BundlerCall { + return ERC4626_BUNDLER_IFC.encodeFunctionData("erc4626Mint", [erc4626, shares, maxAssets, receiver]); + } + + /** + * Encodes a call to the Bundler to deposit assets into the provided ERC4626 vault. + * @param erc4626 The address of the ERC4626 vault. + * @param assets The amount of assets to deposit. + * @param minShares The minimum amount of shares to mint (protects the sender from unexpected slippage). + * @param receiver The address to send the shares to. + */ + export function erc4626Deposit( + erc4626: string, + assets: BigNumberish, + minShares: BigNumberish, + receiver: string, + ): BundlerCall { + return ERC4626_BUNDLER_IFC.encodeFunctionData("erc4626Deposit", [erc4626, assets, minShares, receiver]); + } + + /** + * Encodes a call to the Bundler to withdraw assets from the provided ERC4626 vault. + * @param erc4626 The address of the ERC4626 vault. + * @param assets The amount of assets to withdraw. + * @param maxShares The maximum amount of shares to redeem (protects the sender from unexpected slippage). + * @param receiver The address to send the assets to. + */ + export function erc4626Withdraw( + erc4626: string, + assets: BigNumberish, + maxShares: BigNumberish, + receiver: string, + owner: string, + ): BundlerCall { + return ERC4626_BUNDLER_IFC.encodeFunctionData("erc4626Withdraw", [erc4626, assets, maxShares, receiver, owner]); + } + + /** + * Encodes a call to the Bundler to redeem shares from the provided ERC4626 vault. + * @param erc4626 The address of the ERC4626 vault. + * @param shares The amount of shares to redeem. + * @param minAssets The minimum amount of assets to withdraw (protects the sender from unexpected slippage). + * @param receiver The address to send the assets to. + */ + export function erc4626Redeem( + erc4626: string, + shares: BigNumberish, + minAssets: BigNumberish, + receiver: string, + owner: string, + ): BundlerCall { + return ERC4626_BUNDLER_IFC.encodeFunctionData("erc4626Redeem", [erc4626, shares, minAssets, receiver, owner]); + } + + /* Morpho */ + + /** + * Encodes a call to the Bundler to authorize an account on Morpho Blue. + * @param authorization The authorization details to submit to Morpho Blue. + * @param signature The Ethers signature to authorize the account. + * @param skipRevert Whether to allow the authorization call to revert without making the whole multicall revert. + */ + export function morphoSetAuthorizationWithSig( + authorization: AuthorizationStruct, + signature: Signature, + skipRevert: boolean, + ): BundlerCall { + return MORPHO_BUNDLER_IFC.encodeFunctionData("morphoSetAuthorizationWithSig", [ + authorization, + { v: signature.v, r: signature.r, s: signature.s }, + skipRevert, + ]); + } + + /** + * Encodes a call to the Bundler to supply to a Morpho Blue market. + * @param market The market params to supply to. + * @param assets The amount of assets to supply. + * @param shares The amount of supply shares to mint. + * @param slippageAmount The maximum (resp. minimum) amount of assets (resp. supply shares) to supply (resp. mint) (protects the sender from unexpected slippage). + * @param onBehalf The address to supply on behalf of. + * @param callbackCalls The array of calls to execute inside Morpho Blue's `onMorphoSupply` callback. + */ + export function morphoSupply( + market: MarketParamsStruct, + assets: BigNumberish, + shares: BigNumberish, + slippageAmount: BigNumberish, + onBehalf: string, + callbackCalls: BundlerCall[], + ): BundlerCall { + return MORPHO_BUNDLER_IFC.encodeFunctionData("morphoSupply", [ + market, + assets, + shares, + slippageAmount, + onBehalf, + MORPHO_BUNDLER_IFC.getAbiCoder().encode(["bytes[]"], [callbackCalls]), + ]); + } + + /** + * Encodes a call to the Bundler to supply collateral to a Morpho Blue market. + * @param market The market params to supply to. + * @param assets The amount of assets to supply. + * @param onBehalf The address to supply on behalf of. + * @param callbackCalls The array of calls to execute inside Morpho Blue's `onMorphoSupplyCollateral` callback. + */ + export function morphoSupplyCollateral( + market: MarketParamsStruct, + assets: BigNumberish, + onBehalf: string, + callbackCalls: BundlerCall[], + ): BundlerCall { + return MORPHO_BUNDLER_IFC.encodeFunctionData("morphoSupplyCollateral", [ + market, + assets, + onBehalf, + MORPHO_BUNDLER_IFC.getAbiCoder().encode(["bytes[]"], [callbackCalls]), + ]); + } + + /** + * Encodes a call to the Bundler to borrow from a Morpho Blue market. + * @param market The market params to borrow from. + * @param assets The amount of assets to borrow. + * @param shares The amount of borrow shares to mint. + * @param slippageAmount The minimum (resp. maximum) amount of assets (resp. borrow shares) to borrow (resp. mint) (protects the sender from unexpected slippage). + * @param receiver The address to send borrowed tokens to. + */ + export function morphoBorrow( + market: MarketParamsStruct, + assets: BigNumberish, + shares: BigNumberish, + slippageAmount: BigNumberish, + receiver: string, + ): BundlerCall { + return MORPHO_BUNDLER_IFC.encodeFunctionData("morphoBorrow", [market, assets, shares, slippageAmount, receiver]); + } + + /** + * Encodes a call to the Bundler to repay to a Morpho Blue market. + * @param market The market params to repay to. + * @param assets The amount of assets to repay. + * @param shares The amount of borrow shares to redeem. + * @param slippageAmount The maximum (resp. minimum) amount of assets (resp. borrow shares) to repay (resp. redeem) (protects the sender from unexpected slippage). + * @param onBehalf The address to repay on behalf of. + * @param callbackCalls The array of calls to execute inside Morpho Blue's `onMorphoSupply` callback. + */ + export function morphoRepay( + market: MarketParamsStruct, + assets: BigNumberish, + shares: BigNumberish, + slippageAmount: BigNumberish, + onBehalf: string, + callbackCalls: BundlerCall[], + ): BundlerCall { + return MORPHO_BUNDLER_IFC.encodeFunctionData("morphoRepay", [ + market, + assets, + shares, + slippageAmount, + onBehalf, + MORPHO_BUNDLER_IFC.getAbiCoder().encode(["bytes[]"], [callbackCalls]), + ]); + } + + /** + * Encodes a call to the Bundler to withdraw from a Morpho Blue market. + * @param market The market params to withdraw from. + * @param assets The amount of assets to withdraw. + * @param shares The amount of supply shares to redeem. + * @param slippageAmount The minimum (resp. maximum) amount of assets (resp. supply shares) to withdraw (resp. redeem) (protects the sender from unexpected slippage). + * @param receiver The address to send withdrawn tokens to. + */ + export function morphoWithdraw( + market: MarketParamsStruct, + assets: BigNumberish, + shares: BigNumberish, + slippageAmount: BigNumberish, + receiver: string, + ): BundlerCall { + return MORPHO_BUNDLER_IFC.encodeFunctionData("morphoWithdraw", [market, assets, shares, slippageAmount, receiver]); + } + + /** + * Encodes a call to the Bundler to withdraw collateral from a Morpho Blue market. + * @param market The market params to withdraw from. + * @param assets The amount of assets to withdraw. + * @param receiver The address to send withdrawn tokens to. + */ + export function morphoWithdrawCollateral( + market: MarketParamsStruct, + assets: BigNumberish, + receiver: string, + ): BundlerCall { + return MORPHO_BUNDLER_IFC.encodeFunctionData("morphoWithdrawCollateral", [market, assets, receiver]); + } + + /** + * Encodes a call to the Bundler to flash loan from Morpho Blue. + * @param asset The address of the ERC20 token to flash loan. + * @param amount The amount of tokens to flash loan. + * @param callbackCalls The array of calls to execute inside Morpho Blue's `onMorphoFlashLoan` callback. + */ + export function morphoFlashLoan(asset: string, amount: BigNumberish, callbackCalls: BundlerCall[]): BundlerCall { + return MORPHO_BUNDLER_IFC.encodeFunctionData("morphoFlashLoan", [ + asset, + amount, + MORPHO_BUNDLER_IFC.getAbiCoder().encode(["bytes[]"], [callbackCalls]), + ]); + } + + /** + * Encodes a call to the Bundler to trigger a public reallocation on the PublicAllocator. + * @param publicAllocator The address of the PublicAllocator to use. + * @param vault The vault to reallocate. + * @param value The value of the call. Can be used to pay the vault reallocation fees. + * @param withdrawals The array of withdrawals to perform, before supplying everything to the supply market. + * @param supplyMarketParams The market params to reallocate to. + */ + export function metaMorphoReallocateTo( + publicAllocator: string, + vault: string, + value: BigNumberish, + withdrawals: WithdrawalStruct[], + supplyMarketParams: MarketParamsStruct, + ): BundlerCall { + return MORPHO_BUNDLER_IFC.encodeFunctionData("reallocateTo", [ + publicAllocator, + vault, + value, + withdrawals, + supplyMarketParams, + ]); + } + + /* Universal Rewards Distributor */ + + /** + * Encodes a call to the Bundler to claim rewards from the Universal Rewards Distributor. + * @param distributor The address of the distributor to claim rewards from. + * @param account The address to claim rewards for. + * @param reward The address of the reward token to claim. + * @param amount The amount of rewards to claim. + * @param proof The Merkle proof to claim the rewards. + * @param skipRevert Whether to allow the claim to revert without making the whole multicall revert. + */ + export function urdClaim( + distributor: string, + account: string, + reward: string, + amount: BigNumberish, + proof: BytesLike[], + skipRevert: boolean, + ): BundlerCall { + return URD_BUNDLER_IFC.encodeFunctionData("urdClaim", [distributor, account, reward, amount, proof, skipRevert]); + } + + /* Wrapped Native */ + + /** + * Encodes a call to the Bundler to wrap native tokens (ETH to WETH on ethereum, MATIC to WMATIC on polygon, etc). + * @param amount The amount of native tokens to wrap (in wei). + */ + export function wrapNative(amount: BigNumberish): BundlerCall { + return WNATIVE_BUNDLER_IFC.encodeFunctionData("wrapNative", [amount]); + } + + /** + * Encodes a call to the Bundler to unwrap native tokens (WETH to ETH on ethereum, WMATIC to MATIC on polygon, etc). + * @param amount The amount of native tokens to unwrap (in wei). + */ + export function unwrapNative(amount: BigNumberish): BundlerCall { + return WNATIVE_BUNDLER_IFC.encodeFunctionData("unwrapNative", [amount]); + } + + /* stETH */ + + /** + * Encodes a call to the Bundler to stake native tokens using Lido (ETH to stETH on ethereum). + * @param amount The amount of native tokens to stake (in wei). + * @param minShares The minimum amount of shares to mint (protects the sender from unexpected slippage). + * @param referral The referral address to use. + */ + export function stakeEth(amount: BigNumberish, minShares: BigNumberish, referral: string): BundlerCall { + return ST_ETH_BUNDLER_IFC.encodeFunctionData("stakeEth", [amount, minShares, referral]); + } + + /* Wrapped stETH */ + + /** + * Encodes a call to the Bundler to wrap stETH (stETH to wstETH on ethereum). + * @param amount The amount of stETH to wrap (in wei). + */ + export function wrapStEth(amount: BigNumberish): BundlerCall { + return ST_ETH_BUNDLER_IFC.encodeFunctionData("wrapStEth", [amount]); + } + + /** + * Encodes a call to the Bundler to unwrap wstETH (wstETH to stETH on ethereum). + * @param amount The amount of wstETH to unwrap (in wei). + */ + export function unwrapStEth(amount: BigNumberish): BundlerCall { + return ST_ETH_BUNDLER_IFC.encodeFunctionData("unwrapStEth", [amount]); + } + + /* AaveV2 */ + + /** + * ! Only available on AaveV2MigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to repay a debt on AaveV2. + * @param asset The debt asset to repay. + * @param amount The amount of debt to repay. + * @param rateMode The interest rate mode used by the debt to repay. + */ + export function aaveV2Repay(asset: string, amount: BigNumberish, rateMode: BigNumberish): BundlerCall { + return AAVE_V2_BUNDLER_IFC.encodeFunctionData("aaveV2Repay", [asset, amount, rateMode]); + } + + /** + * ! Only available on AaveV2MigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to withdrawn from AaveV2. + * @param asset The asset to withdraw. + * @param amount The amount of asset to withdraw. + */ + export function aaveV2Withdraw(asset: string, amount: BigNumberish): BundlerCall { + return AAVE_V2_BUNDLER_IFC.encodeFunctionData("aaveV2Withdraw", [asset, amount]); + } + + /* AaveV3 */ + + /** + * ! Only available on AaveV3MigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to repay a debt on AaveV3. + * @param asset The debt asset to repay. + * @param amount The amount of debt to repay. + * @param rateMode The interest rate mode used by the debt to repay. + */ + export function aaveV3Repay(asset: string, amount: BigNumberish, rateMode: BigNumberish): BundlerCall { + return AAVE_V3_BUNDLER_IFC.encodeFunctionData("aaveV3Repay", [asset, amount, rateMode]); + } + + /** + * ! Only available on AaveV3MigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to withdrawn from AaveV3. + * @param asset The asset to withdraw. + * @param amount The amount of asset to withdraw. + */ + export function aaveV3Withdraw(asset: string, amount: BigNumberish): BundlerCall { + return AAVE_V3_BUNDLER_IFC.encodeFunctionData("aaveV3Withdraw", [asset, amount]); + } + + /* AaveV3 Optimizer */ + + /** + * ! Only available on AaveV3OptimizerMigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to repay a debt on Morpho's AaveV3Optimizer. + * @param underlying The underlying debt asset to repay. + * @param amount The amount of debt to repay. + * @param maxIterations The maximum amount of iterations to use for the repayment. + */ + export function aaveV3OptimizerRepay(underlying: string, amount: BigNumberish): BundlerCall { + return AAVE_V3_OPTIMIZER_BUNDLER_IFC.encodeFunctionData("aaveV3OptimizerRepay", [underlying, amount]); + } + + /** + * ! Only available on AaveV3OptimizerMigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to withdraw from Morpho's AaveV3Optimizer. + * @param underlying The underlying asset to withdraw. + * @param amount The amount to withdraw. + * @param maxIterations The maximum amount of iterations to use for the withdrawal. + */ + export function aaveV3OptimizerWithdraw( + underlying: string, + amount: BigNumberish, + maxIterations: BigNumberish, + ): BundlerCall { + return AAVE_V3_OPTIMIZER_BUNDLER_IFC.encodeFunctionData("aaveV3OptimizerWithdraw", [ + underlying, + amount, + maxIterations, + ]); + } + + /** + * ! Only available on AaveV3OptimizerMigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to withdraw collateral from Morpho's AaveV3Optimizer. + * @param underlying The underlying asset to withdraw. + * @param amount The amount to withdraw. + */ + export function aaveV3OptimizerWithdrawCollateral(underlying: string, amount: BigNumberish): BundlerCall { + return AAVE_V3_OPTIMIZER_BUNDLER_IFC.encodeFunctionData("aaveV3OptimizerWithdrawCollateral", [underlying, amount]); + } + + /** + * ! Only available on AaveV3OptimizerMigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to approve the Bundler as the sender's manager on Morpho's AaveV3Optimizer. + * @param isApproved Whether the manager is approved. + * @param nonce The nonce used to sign. + * @param deadline The timestamp until which the signature is valid. + * @param signature The Ethers signature to submit. + * @param skipRevert Whether to allow the signature to revert without making the whole multicall revert. + */ + export function aaveV3OptimizerApproveManagerWithSig( + isApproved: boolean, + nonce: BigNumberish, + deadline: BigNumberish, + signature: Signature, + skipRevert: boolean, + ): BundlerCall { + return AAVE_V3_OPTIMIZER_BUNDLER_IFC.encodeFunctionData("aaveV3OptimizerApproveManagerWithSig", [ + isApproved, + nonce, + deadline, + { v: signature.v, r: signature.r, s: signature.s }, + skipRevert, + ]); + } + + /* CompoundV2 */ + + /** + * ! Only available on CompoundV2MigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to repay a debt on CompoundV2. + * @param cToken The cToken on which to repay the debt. + * @param amount The amount of debt to repay. + */ + export function compoundV2Repay(cToken: string, amount: BigNumberish): BundlerCall { + return COMPOUND_V2_BUNDLER_IFC.encodeFunctionData("compoundV2Repay", [cToken, amount]); + } + + /** + * ! Only available on CompoundV2MigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to withdraw collateral from CompoundV2. + * @param cToken The cToken on which to withdraw. + * @param amount The amount to withdraw. + */ + export function compoundV2Redeem(cToken: string, amount: BigNumberish): BundlerCall { + return COMPOUND_V2_BUNDLER_IFC.encodeFunctionData("compoundV2Redeem", [cToken, amount]); + } + + /* CompoundV3 */ + + /** + * ! Only available on CompoundV3MigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to repay a debt on CompoundV3. + * @param instance The CompoundV3 instance on which to repay the debt. + * @param amount The amount of debt to repay. + */ + export function compoundV3Repay(instance: string, amount: BigNumberish): BundlerCall { + return COMPOUND_V3_BUNDLER_IFC.encodeFunctionData("compoundV3Repay", [instance, amount]); + } + + /** + * ! Only available on CompoundV3MigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to withdraw collateral from CompoundV3. + * @param instance The CompoundV3 instance on which to withdraw. + * @param amount The amount to withdraw. + */ + export function compoundV3WithdrawFrom(instance: string, asset: string, amount: BigNumberish): BundlerCall { + return COMPOUND_V3_BUNDLER_IFC.encodeFunctionData("compoundV3WithdrawFrom", [instance, asset, amount]); + } + + /** + * ! Only available on CompoundV3MigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to allow the Bundler to act on the sender's position on CompoundV3. + * @param instance The CompoundV3 instance on which to submit the signature. + * @param isAllowed Whether the manager is allowed. + * @param nonce The nonce used to sign. + * @param expiry The timestamp until which the signature is valid. + * @param signature The Ethers signature to submit. + * @param skipRevert Whether to allow the signature to revert without making the whole multicall revert. + */ + export function compoundV3AllowBySig( + instance: string, + isAllowed: boolean, + nonce: BigNumberish, + expiry: BigNumberish, + signature: Signature, + skipRevert: boolean, + ): BundlerCall { + return COMPOUND_V3_BUNDLER_IFC.encodeFunctionData("compoundV3AllowBySig", [ + instance, + isAllowed, + nonce, + expiry, + signature.v, + signature.r, + signature.s, + skipRevert, + ]); + } +} + +export default BundlerAction; diff --git a/packages/viem/src/index.ts b/packages/viem/src/index.ts new file mode 100644 index 00000000..c7031171 --- /dev/null +++ b/packages/viem/src/index.ts @@ -0,0 +1 @@ +export { BundlerAction, BundlerCall } from "./BundlerAction"; diff --git a/packages/viem/test/EthereumBundler.spec.ts b/packages/viem/test/EthereumBundler.spec.ts new file mode 100644 index 00000000..231679a7 --- /dev/null +++ b/packages/viem/test/EthereumBundler.spec.ts @@ -0,0 +1,346 @@ +import { expect } from "chai"; +import { MaxUint256, Signature, TypedDataDomain, TypedDataField, toBigInt } from "ethers"; +import hre from "hardhat"; +import { + AdaptiveCurveIrm, + ERC20Mock, + ERC4626Mock, + EthereumBundlerV2, + EthereumBundlerV2__factory, + MorphoMock, + OracleMock, +} from "../src/types"; +import { MarketParamsStruct } from "../src/types/lib/morpho-blue/src/Morpho"; + +import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; +import { + increaseTo, + latest, + setNextBlockTimestamp, +} from "@nomicfoundation/hardhat-network-helpers/dist/src/helpers/time"; + +import { BundlerAction } from "../src"; + +interface TypedDataConfig { + domain: TypedDataDomain; + types: Record; +} + +const permit2Address = "0x000000000022D473030F116dDEE9F6B43aC78BA3"; + +const permit2Config: TypedDataConfig = { + domain: { + name: "Permit2", + chainId: "0x1", + verifyingContract: permit2Address, + }, + types: { + PermitSingle: [ + { + name: "details", + type: "PermitDetails", + }, + { + name: "spender", + type: "address", + }, + { + name: "sigDeadline", + type: "uint256", + }, + ], + PermitDetails: [ + { + name: "token", + type: "address", + }, + { + name: "amount", + type: "uint160", + }, + { + name: "expiration", + type: "uint48", + }, + { + name: "nonce", + type: "uint48", + }, + ], + }, +}; + +const morphoAuthorizationTypes: TypedDataConfig["types"] = { + Authorization: [ + { + name: "authorizer", + type: "address", + }, + { + name: "authorized", + type: "address", + }, + { + name: "isAuthorized", + type: "bool", + }, + { + name: "nonce", + type: "uint256", + }, + { + name: "deadline", + type: "uint256", + }, + ], +}; + +// Without the division it overflows. +const initBalance = MaxUint256 / 10000000000000000n; +const oraclePriceScale = 1000000000000000000000000000000000000n; + +const MAX_UINT48 = 281474976710655n; + +let seed = 42; +const random = () => { + seed = (seed * 16807) % 2147483647; + + return (seed - 1) / 2147483646; +}; + +const logProgress = (name: string, i: number, max: number) => { + if (i % 10 == 0) console.log("[" + name + "]", Math.floor((100 * i) / max), "%"); +}; + +const forwardTimestamp = async (elapsed: number) => { + const timestamp = await latest(); + const newTimestamp = timestamp + elapsed; + + await increaseTo(newTimestamp); + await setNextBlockTimestamp(newTimestamp); +}; + +const randomForwardTimestamp = async () => { + const elapsed = random() < 1 / 2 ? 0 : (1 + Math.floor(random() * 100)) * 12; // 50% of the time, don't go forward in time. + + await forwardTimestamp(elapsed); +}; + +describe("EthereumBundler", () => { + let admin: SignerWithAddress; + let suppliers: SignerWithAddress[]; + let borrowers: SignerWithAddress[]; + + let morpho: MorphoMock; + let loan: ERC20Mock; + let collateral: ERC20Mock; + let oracle: OracleMock; + let irm: AdaptiveCurveIrm; + + let morphoAuthorizationConfig: TypedDataConfig; + + let erc4626: ERC4626Mock; + let erc4626Address: string; + + let bundler: EthereumBundlerV2; + let bundlerAddress: string; + + let marketParams: MarketParamsStruct; + + const updateMarket = (newMarket: Partial) => { + marketParams = { ...marketParams, ...newMarket }; + }; + + beforeEach(async () => { + const allSigners = await hre.ethers.getSigners(); + + const users = allSigners.slice(0, -3); + + admin = allSigners.slice(-1)[0]!; + suppliers = users.slice(0, users.length / 2); + borrowers = users.slice(users.length / 2); + + const ERC20MockFactory = await hre.ethers.getContractFactory("ERC20Mock", admin); + + loan = await ERC20MockFactory.deploy("DAI", "DAI"); + collateral = await ERC20MockFactory.deploy("Wrapped BTC", "WBTC"); + + const OracleMockFactory = await hre.ethers.getContractFactory("OracleMock", admin); + + oracle = await OracleMockFactory.deploy(); + + await oracle.setPrice(oraclePriceScale); + + const MorphoFactory = await hre.ethers.getContractFactory("MorphoMock", admin); + + morpho = await MorphoFactory.deploy(admin.address); + + const morphoAddress = await morpho.getAddress(); + + const AdaptiveCurveIrmFactory = await hre.ethers.getContractFactory("AdaptiveCurveIrm", admin); + + irm = await AdaptiveCurveIrmFactory.deploy(morphoAddress); + + morphoAuthorizationConfig = { + domain: { chainId: "0x1", verifyingContract: morphoAddress }, + types: morphoAuthorizationTypes, + }; + + const ERC4626MockFactory = await hre.ethers.getContractFactory("ERC4626Mock", admin); + + const collateralAddress = await collateral.getAddress(); + + erc4626 = await ERC4626MockFactory.deploy(collateralAddress, "MetaMorpho", "MM"); + + erc4626Address = await erc4626.getAddress(); + + const loanAddress = await loan.getAddress(); + const oracleAddress = await oracle.getAddress(); + const irmAddress = await irm.getAddress(); + + updateMarket({ + loanToken: loanAddress, + collateralToken: collateralAddress, + oracle: oracleAddress, + irm: irmAddress, + lltv: BigInt.WAD / 2n + 1n, + }); + + await morpho.enableIrm(irmAddress); + await morpho.enableLltv(marketParams.lltv); + await morpho.createMarket(marketParams); + + const EthereumBundlerV2Factory = await hre.ethers.getContractFactory("EthereumBundlerV2", admin); + + bundler = await EthereumBundlerV2Factory.deploy(morphoAddress); + + bundlerAddress = await bundler.getAddress(); + + for (const user of users) { + await loan.setBalance(user.address, initBalance); + await loan.connect(user).approve(morphoAddress, MaxUint256); + await collateral.setBalance(user.address, initBalance); + await collateral.connect(user).approve(morphoAddress, MaxUint256); + } + + await forwardTimestamp(1); + + hre.tracer.nameTags[morphoAddress] = "Morpho"; + hre.tracer.nameTags[collateralAddress] = "Collateral"; + hre.tracer.nameTags[loanAddress] = "Loan"; + hre.tracer.nameTags[oracleAddress] = "Oracle"; + hre.tracer.nameTags[irmAddress] = "AdaptiveCurveIrm"; + hre.tracer.nameTags[bundlerAddress] = "EthereumBundlerV2"; + }); + + it("should simulate gas cost [morpho-supplyCollateral+borrow]", async () => { + for (let i = 0; i < suppliers.length; ++i) { + logProgress("supplyCollateral+borrow", i, suppliers.length); + + const supplier = suppliers[i]!; + + const assets = BigInt.WAD * toBigInt(1 + Math.floor(random() * 100)); + + await morpho.connect(supplier).supply(marketParams, assets, 0, supplier.address, "0x"); + + const borrower = borrowers[i]!; + + const authorization = { + authorizer: borrower.address, + authorized: bundlerAddress, + isAuthorized: true, + nonce: 0n, + deadline: MAX_UINT48, + }; + + const collateralAddress = await collateral.getAddress(); + + const approve2 = { + details: { + token: collateralAddress, + amount: assets, + nonce: 0n, + expiration: MAX_UINT48, + }, + spender: bundlerAddress, + sigDeadline: MAX_UINT48, + }; + + await collateral.connect(borrower).approve(permit2Address, MaxUint256); + + await randomForwardTimestamp(); + + await bundler + .connect(borrower) + .multicall([ + BundlerAction.morphoSetAuthorizationWithSig( + authorization, + Signature.from( + await borrower.signTypedData( + morphoAuthorizationConfig.domain, + morphoAuthorizationConfig.types, + authorization, + ), + ), + false, + ), + BundlerAction.approve2( + approve2, + Signature.from(await borrower.signTypedData(permit2Config.domain, permit2Config.types, approve2)), + false, + ), + BundlerAction.transferFrom2(collateralAddress, assets), + BundlerAction.morphoSupplyCollateral(marketParams, assets, borrower.address, []), + BundlerAction.morphoBorrow(marketParams, assets / 2n, 0, borrower.address, borrower.address), + ]); + } + }); + + it("should simulate gas cost [erc4626-deposit]", async () => { + for (let i = 0; i < suppliers.length; ++i) { + logProgress("erc4626-deposit", i, suppliers.length); + + const supplier = suppliers[i]!; + + const assets = BigInt.WAD * toBigInt(1 + Math.floor(random() * 100)); + const collateralAddress = await collateral.getAddress(); + + const approve2 = { + details: { + token: collateralAddress, + amount: assets, + expiration: MAX_UINT48, + nonce: 0n, + }, + spender: bundlerAddress, + sigDeadline: MAX_UINT48, + }; + + await collateral.connect(supplier).approve(permit2Address, MaxUint256); + + await randomForwardTimestamp(); + + await bundler + .connect(supplier) + .multicall([ + BundlerAction.approve2( + approve2, + Signature.from(await supplier.signTypedData(permit2Config.domain, permit2Config.types, approve2)), + false, + ), + BundlerAction.transferFrom2(collateralAddress, assets), + BundlerAction.erc4626Deposit(erc4626Address, assets, 0, supplier.address), + ]); + } + }); + + it("should have all batched functions payable", async () => { + EthereumBundlerV2__factory.createInterface().forEachFunction((func) => { + if (func.stateMutability === "view" || func.stateMutability === "pure") return; + + const shouldPayable = !func.name.startsWith("onMorpho"); + + expect(func.payable).to.equal(shouldPayable); + }); + }); +}); diff --git a/packages/viem/tsconfig.build.json b/packages/viem/tsconfig.build.json new file mode 100644 index 00000000..3d448c4d --- /dev/null +++ b/packages/viem/tsconfig.build.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "target": "es2020", + "rootDir": "src" + }, + "include": ["src"], + "files": [] +} diff --git a/packages/viem/tsconfig.json b/packages/viem/tsconfig.json new file mode 100644 index 00000000..751f119e --- /dev/null +++ b/packages/viem/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "baseUrl": "." + }, + "include": ["src", "test"], + "files": ["hardhat.config.ts"] +} diff --git a/yarn.lock b/yarn.lock index 63f8e83b..3b86636b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,6 +5,13 @@ __metadata: version: 8 cacheKey: 10c0 +"@adraffy/ens-normalize@npm:1.10.0": + version: 1.10.0 + resolution: "@adraffy/ens-normalize@npm:1.10.0" + checksum: 10c0/78ae700847a2516d5a0ae12c4e23d09392a40c67e73b137eb7189f51afb1601c8d18784aeda2ed288a278997824dc924d1f398852c21d41ee2c4c564f2fb4d26 + languageName: node + linkType: hard + "@adraffy/ens-normalize@npm:1.10.1": version: 1.10.1 resolution: "@adraffy/ens-normalize@npm:1.10.1" @@ -1384,6 +1391,38 @@ __metadata: languageName: unknown linkType: soft +"@morpho-org/bundlers-sdk-viem@workspace:packages/viem": + version: 0.0.0-use.local + resolution: "@morpho-org/bundlers-sdk-viem@workspace:packages/viem" + dependencies: + "@nomicfoundation/hardhat-chai-matchers": "npm:^2.0.2" + "@nomicfoundation/hardhat-ethers": "npm:^3.0.4" + "@nomicfoundation/hardhat-foundry": "npm:^1.1.1" + "@nomicfoundation/hardhat-network-helpers": "npm:^1.0.9" + "@typechain/ethers-v6": "npm:^0.5.1" + "@typechain/hardhat": "npm:^9.1.0" + "@types/chai": "npm:^4.3.8" + "@types/mocha": "npm:^10.0.2" + "@types/node": "npm:^20.8.6" + chai: "npm:^4.3.10" + dotenv: "npm:^16.3.1" + evm-maths: "npm:^7.0.0" + hardhat: "npm:^2.18.1" + hardhat-gas-reporter: "npm:^1.0.9" + hardhat-tracer: "npm:^2.6.0" + solidity-coverage: "npm:^0.8.5" + solmate: "npm:6.2.0" + ts-node: "npm:^10.9.1" + tsconfig-paths: "npm:^4.2.0" + typechain: "npm:^8.3.2" + typescript: "npm:^5.2.2" + viem: "npm:^2.21.15" + peerDependencies: + evm-maths: ^6.0.0 + viem: ^2.0.0 + languageName: unknown + linkType: soft + "@morpho-org/morpho-blue-bundlers@workspace:.": version: 0.0.0-use.local resolution: "@morpho-org/morpho-blue-bundlers@workspace:." @@ -1420,6 +1459,33 @@ __metadata: languageName: node linkType: hard +"@noble/curves@npm:1.4.0": + version: 1.4.0 + resolution: "@noble/curves@npm:1.4.0" + dependencies: + "@noble/hashes": "npm:1.4.0" + checksum: 10c0/31fbc370df91bcc5a920ca3f2ce69c8cf26dc94775a36124ed8a5a3faf0453badafd2ee4337061ffea1b43c623a90ee8b286a5a81604aaf9563bdad7ff795d18 + languageName: node + linkType: hard + +"@noble/curves@npm:^1.4.0": + version: 1.6.0 + resolution: "@noble/curves@npm:1.6.0" + dependencies: + "@noble/hashes": "npm:1.5.0" + checksum: 10c0/f3262aa4d39148e627cd82b5ac1c93f88c5bb46dd2566b5e8e52ffac3a0fc381ad30c2111656fd2bd3b0d37d43d540543e0d93a5ff96a6cb184bc3bfe10d1cd9 + languageName: node + linkType: hard + +"@noble/curves@npm:~1.4.0": + version: 1.4.2 + resolution: "@noble/curves@npm:1.4.2" + dependencies: + "@noble/hashes": "npm:1.4.0" + checksum: 10c0/65620c895b15d46e8087939db6657b46a1a15cd4e0e4de5cd84b97a0dfe0af85f33a431bb21ac88267e3dc508618245d4cb564213959d66a84d690fe18a63419 + languageName: node + linkType: hard + "@noble/hashes@npm:1.2.0, @noble/hashes@npm:~1.2.0": version: 1.2.0 resolution: "@noble/hashes@npm:1.2.0" @@ -1441,6 +1507,20 @@ __metadata: languageName: node linkType: hard +"@noble/hashes@npm:1.4.0, @noble/hashes@npm:~1.4.0": + version: 1.4.0 + resolution: "@noble/hashes@npm:1.4.0" + checksum: 10c0/8c3f005ee72e7b8f9cff756dfae1241485187254e3f743873e22073d63906863df5d4f13d441b7530ea614b7a093f0d889309f28b59850f33b66cb26a779a4a5 + languageName: node + linkType: hard + +"@noble/hashes@npm:1.5.0, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:~1.5.0": + version: 1.5.0 + resolution: "@noble/hashes@npm:1.5.0" + checksum: 10c0/1b46539695fbfe4477c0822d90c881a04d4fa2921c08c552375b444a48cac9930cb1ee68de0a3c7859e676554d0f3771999716606dc4d8f826e414c11692cdd9 + languageName: node + linkType: hard + "@noble/secp256k1@npm:1.7.1, @noble/secp256k1@npm:~1.7.0": version: 1.7.1 resolution: "@noble/secp256k1@npm:1.7.1" @@ -2127,6 +2207,13 @@ __metadata: languageName: node linkType: hard +"@scure/base@npm:~1.1.6, @scure/base@npm:~1.1.8": + version: 1.1.9 + resolution: "@scure/base@npm:1.1.9" + checksum: 10c0/77a06b9a2db8144d22d9bf198338893d77367c51b58c72b99df990c0a11f7cadd066d4102abb15e3ca6798d1529e3765f55c4355742465e49aed7a0c01fe76e8 + languageName: node + linkType: hard + "@scure/bip32@npm:1.1.5": version: 1.1.5 resolution: "@scure/bip32@npm:1.1.5" @@ -2149,6 +2236,17 @@ __metadata: languageName: node linkType: hard +"@scure/bip32@npm:1.4.0": + version: 1.4.0 + resolution: "@scure/bip32@npm:1.4.0" + dependencies: + "@noble/curves": "npm:~1.4.0" + "@noble/hashes": "npm:~1.4.0" + "@scure/base": "npm:~1.1.6" + checksum: 10c0/6849690d49a3bf1d0ffde9452eb16ab83478c1bc0da7b914f873e2930cd5acf972ee81320e3df1963eb247cf57e76d2d975b5f97093d37c0e3f7326581bf41bd + languageName: node + linkType: hard + "@scure/bip39@npm:1.1.1": version: 1.1.1 resolution: "@scure/bip39@npm:1.1.1" @@ -2169,6 +2267,16 @@ __metadata: languageName: node linkType: hard +"@scure/bip39@npm:1.4.0": + version: 1.4.0 + resolution: "@scure/bip39@npm:1.4.0" + dependencies: + "@noble/hashes": "npm:~1.5.0" + "@scure/base": "npm:~1.1.8" + checksum: 10c0/dcdceeac348ed9c0f545c1a7ef8854ef62d6eb4e7b7aaafa4e2ef27f7e1c5744b0cd26292afd04e1ee59ae035b19abdd65174a444b8db8c238ccc662f6b90eac + languageName: node + linkType: hard + "@sec-ant/readable-stream@npm:^0.4.1": version: 0.4.1 resolution: "@sec-ant/readable-stream@npm:0.4.1" @@ -2655,6 +2763,21 @@ __metadata: languageName: node linkType: hard +"abitype@npm:1.0.5": + version: 1.0.5 + resolution: "abitype@npm:1.0.5" + peerDependencies: + typescript: ">=5.0.4" + zod: ^3 >=3.22.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + checksum: 10c0/dc954877fba19e2b7a70f1025807d69fa5aabec8bd58ce94e68d1a5ec1697fff3fe5214b4392508db7191762150f19a2396cf66ffb1d3ba8c1f37a89fd25e598 + languageName: node + linkType: hard + "abstract-level@npm:^1.0.0, abstract-level@npm:^1.0.2, abstract-level@npm:^1.0.3": version: 1.0.3 resolution: "abstract-level@npm:1.0.3" @@ -6035,6 +6158,15 @@ __metadata: languageName: node linkType: hard +"isows@npm:1.0.4": + version: 1.0.4 + resolution: "isows@npm:1.0.4" + peerDependencies: + ws: "*" + checksum: 10c0/46f43b07edcf148acba735ddfc6ed985e1e124446043ea32b71023e67671e46619c8818eda8c34a9ac91cb37c475af12a3aeeee676a88a0aceb5d67a3082313f + languageName: node + linkType: hard + "jackspeak@npm:^3.1.2": version: 3.4.3 resolution: "jackspeak@npm:3.4.3" @@ -9528,6 +9660,28 @@ __metadata: languageName: node linkType: hard +"viem@npm:^2.21.15": + version: 2.21.15 + resolution: "viem@npm:2.21.15" + dependencies: + "@adraffy/ens-normalize": "npm:1.10.0" + "@noble/curves": "npm:1.4.0" + "@noble/hashes": "npm:1.4.0" + "@scure/bip32": "npm:1.4.0" + "@scure/bip39": "npm:1.4.0" + abitype: "npm:1.0.5" + isows: "npm:1.0.4" + webauthn-p256: "npm:0.0.5" + ws: "npm:8.17.1" + peerDependencies: + typescript: ">=5.0.4" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/72549a364daf3f983637a127c5f73421e088babd76b6283f474154c565c550d1d670300ab178dda7c33fce98eac23a596ddb07d3c4cbc6f90252b8d81e649c49 + languageName: node + linkType: hard + "walk-up-path@npm:^3.0.1": version: 3.0.1 resolution: "walk-up-path@npm:3.0.1" @@ -9567,6 +9721,16 @@ __metadata: languageName: node linkType: hard +"webauthn-p256@npm:0.0.5": + version: 0.0.5 + resolution: "webauthn-p256@npm:0.0.5" + dependencies: + "@noble/curves": "npm:^1.4.0" + "@noble/hashes": "npm:^1.4.0" + checksum: 10c0/8a445dddaf0e699363a0a7bca51742f672dbbec427c1a97618465bfc418df0eff10d3f1cf5e43bcd0cd0dc5abcdaad7914916c06c84107eaf226f5a1d0690c13 + languageName: node + linkType: hard + "which@npm:^1.1.1, which@npm:^1.3.1": version: 1.3.1 resolution: "which@npm:1.3.1"