diff --git a/packages/ethers/package.json b/packages/ethers/package.json index f88a4162..ec4aa18d 100644 --- a/packages/ethers/package.json +++ b/packages/ethers/package.json @@ -24,8 +24,7 @@ "test": "yarn prepare && hardhat test" }, "peerDependencies": { - "ethers": "^6.12.0", - "evm-maths": "^6.0.0" + "ethers": "^6.12.0" }, "devDependencies": { "@nomicfoundation/hardhat-chai-matchers": "^2.0.2", 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..de665a1b --- /dev/null +++ b/packages/viem/hardhat.config.ts @@ -0,0 +1,11 @@ +import "evm-maths"; +import "hardhat-gas-reporter"; +import "hardhat-tracer"; +import "solidity-coverage"; + +import "@nomicfoundation/hardhat-chai-matchers"; +import "@nomicfoundation/hardhat-ethers"; + +import config from "../../hardhat.config"; + +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..459e483e --- /dev/null +++ b/packages/viem/package.json @@ -0,0 +1,55 @@ +{ + "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": { + "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", + "@types/chai": "^4.3.8", + "@types/mocha": "^10.0.2", + "@types/node": "^20.8.6", + "chai": "^4.3.10", + "dotenv": "^16.3.1", + "ethers": "^6.13.2", + "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", + "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..efa4939f --- /dev/null +++ b/packages/viem/src/BundlerAction.ts @@ -0,0 +1,766 @@ +import { + aaveV2MigrationBundlerAbi, + aaveV3MigrationBundlerAbi, + aaveV3OptimizerMigrationBundlerAbi, + compoundV2MigrationBundlerAbi, + compoundV3MigrationBundlerAbi, + erc20WrapperBundlerAbi, + erc4626BundlerAbi, + ethereumPermitBundlerAbi, + morphoBundlerAbi, + permit2BundlerAbi, + permitBundlerAbi, + stEthBundlerAbi, + transferBundlerAbi, + urdBundlerAbi, + wNativeBundlerAbi, +} from "./abis"; + +import { Address, Hex, encodeAbiParameters, encodeFunctionData, parseSignature } from "viem"; + +export type BundlerCall = Hex; + +export interface MarketParams { + loanToken: Address; + collateralToken: Address; + oracle: Address; + irm: Address; + lltv: bigint; +} + +export interface Authorization { + authorizer: Address; + authorized: Address; + isAuthorized: boolean; + nonce: bigint; + deadline: bigint; +} + +export interface ReallocationWithdrawal { + marketParams: MarketParams; + amount: bigint; +} + +export interface Permit2PermitSingleDetails { + token: Address; + amount: bigint; + expiration: number; + nonce: number; +} + +export interface Permit2PermitSingle { + details: Permit2PermitSingleDetails; + spender: Address; + sigDeadline: bigint; +} + +/** + * 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: Address, amount: bigint): BundlerCall { + return encodeFunctionData({ abi: transferBundlerAbi, functionName: "nativeTransfer", args: [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: Address, recipient: Address, amount: bigint): BundlerCall { + return encodeFunctionData({ + abi: transferBundlerAbi, + functionName: "erc20Transfer", + args: [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: Address, amount: bigint): BundlerCall { + return encodeFunctionData({ abi: transferBundlerAbi, functionName: "erc20TransferFrom", args: [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: Address, + amount: bigint, + deadline: bigint, + signature: Hex, + skipRevert: boolean, + ): BundlerCall { + const { r, s, yParity } = parseSignature(signature); + + return encodeFunctionData({ + abi: permitBundlerAbi, + functionName: "permit", + args: [asset, amount, deadline, yParity, r, 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: bigint, + expiry: bigint, + allowed: boolean, + signature: Hex, + skipRevert: boolean, + ): BundlerCall { + const { r, s, yParity } = parseSignature(signature); + + return encodeFunctionData({ + abi: ethereumPermitBundlerAbi, + functionName: "permitDai", + args: [nonce, expiry, allowed, yParity, r, 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: Permit2PermitSingle, signature: Hex, skipRevert: boolean): BundlerCall { + return encodeFunctionData({ + abi: permit2BundlerAbi, + functionName: "approve2", + args: [permitSingle, signature, 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: Address, amount: bigint): BundlerCall { + return encodeFunctionData({ abi: permit2BundlerAbi, functionName: "transferFrom2", args: [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: Address, amount: bigint): BundlerCall { + return encodeFunctionData({ + abi: erc20WrapperBundlerAbi, + functionName: "erc20WrapperDepositFor", + args: [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: Address, account: Address, amount: bigint): BundlerCall { + return encodeFunctionData({ + abi: erc20WrapperBundlerAbi, + functionName: "erc20WrapperWithdrawTo", + args: [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: Address, shares: bigint, maxAssets: bigint, receiver: Address): BundlerCall { + return encodeFunctionData({ + abi: erc4626BundlerAbi, + functionName: "erc4626Mint", + args: [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: Address, assets: bigint, minShares: bigint, receiver: Address): BundlerCall { + return encodeFunctionData({ + abi: erc4626BundlerAbi, + functionName: "erc4626Deposit", + args: [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: Address, + assets: bigint, + maxShares: bigint, + receiver: Address, + owner: Address, + ): BundlerCall { + return encodeFunctionData({ + abi: erc4626BundlerAbi, + functionName: "erc4626Withdraw", + args: [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: Address, + shares: bigint, + minAssets: bigint, + receiver: Address, + owner: Address, + ): BundlerCall { + return encodeFunctionData({ + abi: erc4626BundlerAbi, + functionName: "erc4626Redeem", + args: [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: Authorization, + signature: Hex, + skipRevert: boolean, + ): BundlerCall { + const { r, s, yParity } = parseSignature(signature); + + return encodeFunctionData({ + abi: morphoBundlerAbi, + functionName: "morphoSetAuthorizationWithSig", + args: [authorization, { v: yParity, r, 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: MarketParams, + assets: bigint, + shares: bigint, + slippageAmount: bigint, + onBehalf: Address, + callbackCalls: BundlerCall[], + ): BundlerCall { + return encodeFunctionData({ + abi: morphoBundlerAbi, + functionName: "morphoSupply", + args: [ + market, + assets, + shares, + slippageAmount, + onBehalf, + encodeAbiParameters([{ type: "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: MarketParams, + assets: bigint, + onBehalf: Address, + callbackCalls: BundlerCall[], + ): BundlerCall { + return encodeFunctionData({ + abi: morphoBundlerAbi, + functionName: "morphoSupplyCollateral", + args: [market, assets, onBehalf, encodeAbiParameters([{ type: "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: MarketParams, + assets: bigint, + shares: bigint, + slippageAmount: bigint, + receiver: Address, + ): BundlerCall { + return encodeFunctionData({ + abi: morphoBundlerAbi, + functionName: "morphoBorrow", + args: [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: MarketParams, + assets: bigint, + shares: bigint, + slippageAmount: bigint, + onBehalf: Address, + callbackCalls: BundlerCall[], + ): BundlerCall { + return encodeFunctionData({ + abi: morphoBundlerAbi, + functionName: "morphoRepay", + args: [ + market, + assets, + shares, + slippageAmount, + onBehalf, + encodeAbiParameters([{ type: "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: MarketParams, + assets: bigint, + shares: bigint, + slippageAmount: bigint, + receiver: Address, + ): BundlerCall { + return encodeFunctionData({ + abi: morphoBundlerAbi, + functionName: "morphoWithdraw", + args: [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: MarketParams, assets: bigint, receiver: Address): BundlerCall { + return encodeFunctionData({ + abi: morphoBundlerAbi, + functionName: "morphoWithdrawCollateral", + args: [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: Address, amount: bigint, callbackCalls: BundlerCall[]): BundlerCall { + return encodeFunctionData({ + abi: morphoBundlerAbi, + functionName: "morphoFlashLoan", + args: [asset, amount, encodeAbiParameters([{ type: "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: Address, + vault: Address, + value: bigint, + withdrawals: ReallocationWithdrawal[], + supplyMarketParams: MarketParams, + ): BundlerCall { + return encodeFunctionData({ + abi: morphoBundlerAbi, + functionName: "reallocateTo", + args: [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: Address, + account: Address, + reward: Address, + amount: bigint, + proof: Hex[], + skipRevert: boolean, + ): BundlerCall { + return encodeFunctionData({ + abi: urdBundlerAbi, + functionName: "urdClaim", + args: [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: bigint): BundlerCall { + return encodeFunctionData({ abi: wNativeBundlerAbi, functionName: "wrapNative", args: [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: bigint): BundlerCall { + return encodeFunctionData({ abi: wNativeBundlerAbi, functionName: "unwrapNative", args: [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: bigint, minShares: bigint, referral: Address): BundlerCall { + return encodeFunctionData({ abi: stEthBundlerAbi, functionName: "stakeEth", args: [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: bigint): BundlerCall { + return encodeFunctionData({ abi: stEthBundlerAbi, functionName: "wrapStEth", args: [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: bigint): BundlerCall { + return encodeFunctionData({ abi: stEthBundlerAbi, functionName: "unwrapStEth", args: [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: Address, amount: bigint, rateMode: bigint): BundlerCall { + return encodeFunctionData({ + abi: aaveV2MigrationBundlerAbi, + functionName: "aaveV2Repay", + args: [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: Address, amount: bigint): BundlerCall { + return encodeFunctionData({ + abi: aaveV2MigrationBundlerAbi, + functionName: "aaveV2Withdraw", + args: [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: Address, amount: bigint, rateMode: bigint): BundlerCall { + return encodeFunctionData({ + abi: aaveV3MigrationBundlerAbi, + functionName: "aaveV3Repay", + args: [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: Address, amount: bigint): BundlerCall { + return encodeFunctionData({ + abi: aaveV3MigrationBundlerAbi, + functionName: "aaveV3Withdraw", + args: [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: Address, amount: bigint): BundlerCall { + return encodeFunctionData({ + abi: aaveV3OptimizerMigrationBundlerAbi, + functionName: "aaveV3OptimizerRepay", + args: [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: Address, amount: bigint, maxIterations: bigint): BundlerCall { + return encodeFunctionData({ + abi: aaveV3OptimizerMigrationBundlerAbi, + functionName: "aaveV3OptimizerWithdraw", + args: [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: Address, amount: bigint): BundlerCall { + return encodeFunctionData({ + abi: aaveV3OptimizerMigrationBundlerAbi, + functionName: "aaveV3OptimizerWithdrawCollateral", + args: [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: bigint, + deadline: bigint, + signature: Hex, + skipRevert: boolean, + ): BundlerCall { + const { r, s, yParity } = parseSignature(signature); + + return encodeFunctionData({ + abi: aaveV3OptimizerMigrationBundlerAbi, + functionName: "aaveV3OptimizerApproveManagerWithSig", + args: [isApproved, nonce, deadline, { v: yParity, r, 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: Address, amount: bigint): BundlerCall { + return encodeFunctionData({ + abi: compoundV2MigrationBundlerAbi, + functionName: "compoundV2Repay", + args: [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: Address, amount: bigint): BundlerCall { + return encodeFunctionData({ + abi: compoundV2MigrationBundlerAbi, + functionName: "compoundV2Redeem", + args: [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: Address, amount: bigint): BundlerCall { + return encodeFunctionData({ + abi: compoundV3MigrationBundlerAbi, + functionName: "compoundV3Repay", + args: [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: Address, asset: Address, amount: bigint): BundlerCall { + return encodeFunctionData({ + abi: compoundV3MigrationBundlerAbi, + functionName: "compoundV3WithdrawFrom", + args: [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: Address, + isAllowed: boolean, + nonce: bigint, + expiry: bigint, + signature: Hex, + skipRevert: boolean, + ): BundlerCall { + const { r, s, yParity } = parseSignature(signature); + + return encodeFunctionData({ + abi: compoundV3MigrationBundlerAbi, + functionName: "compoundV3AllowBySig", + args: [instance, isAllowed, nonce, expiry, yParity, r, s, skipRevert], + }); + } +} + +export default BundlerAction; diff --git a/packages/viem/src/abis.ts b/packages/viem/src/abis.ts new file mode 100644 index 00000000..e7b899f9 --- /dev/null +++ b/packages/viem/src/abis.ts @@ -0,0 +1,849 @@ +export const baseBundlerAbi = [ + { + type: "function", + name: "initiator", + inputs: [], + outputs: [{ name: "", type: "address", internalType: "address" }], + stateMutability: "view", + }, + { + type: "function", + name: "multicall", + inputs: [{ name: "data", type: "bytes[]", internalType: "bytes[]" }], + outputs: [], + stateMutability: "payable", + }, +] as const; + +export const transferBundlerAbi = [ + ...baseBundlerAbi, + { + type: "function", + name: "erc20Transfer", + inputs: [ + { name: "asset", type: "address", internalType: "address" }, + { name: "recipient", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "erc20TransferFrom", + inputs: [ + { name: "asset", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "nativeTransfer", + inputs: [ + { name: "recipient", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, +] as const; + +export const permitBundlerAbi = [ + ...baseBundlerAbi, + { + type: "function", + name: "permit", + inputs: [ + { name: "asset", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + { name: "deadline", type: "uint256", internalType: "uint256" }, + { name: "v", type: "uint8", internalType: "uint8" }, + { name: "r", type: "bytes32", internalType: "bytes32" }, + { name: "s", type: "bytes32", internalType: "bytes32" }, + { name: "skipRevert", type: "bool", internalType: "bool" }, + ], + outputs: [], + stateMutability: "payable", + }, +] as const; + +export const ethereumPermitBundlerAbi = [ + ...permitBundlerAbi, + { + type: "function", + name: "permitDai", + inputs: [ + { name: "nonce", type: "uint256", internalType: "uint256" }, + { name: "expiry", type: "uint256", internalType: "uint256" }, + { name: "allowed", type: "bool", internalType: "bool" }, + { name: "v", type: "uint8", internalType: "uint8" }, + { name: "r", type: "bytes32", internalType: "bytes32" }, + { name: "s", type: "bytes32", internalType: "bytes32" }, + { name: "skipRevert", type: "bool", internalType: "bool" }, + ], + outputs: [], + stateMutability: "payable", + }, +] as const; + +export const permit2BundlerAbi = [ + ...baseBundlerAbi, + { + type: "function", + name: "approve2", + inputs: [ + { + name: "permitSingle", + type: "tuple", + internalType: "struct IAllowanceTransfer.PermitSingle", + components: [ + { + name: "details", + type: "tuple", + internalType: "struct IAllowanceTransfer.PermitDetails", + components: [ + { name: "token", type: "address", internalType: "address" }, + { name: "amount", type: "uint160", internalType: "uint160" }, + { name: "expiration", type: "uint48", internalType: "uint48" }, + { name: "nonce", type: "uint48", internalType: "uint48" }, + ], + }, + { name: "spender", type: "address", internalType: "address" }, + { name: "sigDeadline", type: "uint256", internalType: "uint256" }, + ], + }, + { name: "signature", type: "bytes", internalType: "bytes" }, + { name: "skipRevert", type: "bool", internalType: "bool" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "transferFrom2", + inputs: [ + { name: "asset", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { type: "error", name: "UnsafeCast", inputs: [] }, +] as const; + +export const erc20WrapperBundlerAbi = [ + ...baseBundlerAbi, + { + type: "function", + name: "erc20WrapperDepositFor", + inputs: [ + { name: "wrapper", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "erc20WrapperWithdrawTo", + inputs: [ + { name: "wrapper", type: "address", internalType: "address" }, + { name: "account", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, +] as const; + +export const erc4626BundlerAbi = [ + ...baseBundlerAbi, + { + type: "function", + name: "erc4626Deposit", + inputs: [ + { name: "vault", type: "address", internalType: "address" }, + { name: "assets", type: "uint256", internalType: "uint256" }, + { name: "minShares", type: "uint256", internalType: "uint256" }, + { name: "receiver", type: "address", internalType: "address" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "erc4626Mint", + inputs: [ + { name: "vault", type: "address", internalType: "address" }, + { name: "shares", type: "uint256", internalType: "uint256" }, + { name: "maxAssets", type: "uint256", internalType: "uint256" }, + { name: "receiver", type: "address", internalType: "address" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "erc4626Redeem", + inputs: [ + { name: "vault", type: "address", internalType: "address" }, + { name: "shares", type: "uint256", internalType: "uint256" }, + { name: "minAssets", type: "uint256", internalType: "uint256" }, + { name: "receiver", type: "address", internalType: "address" }, + { name: "owner", type: "address", internalType: "address" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "erc4626Withdraw", + inputs: [ + { name: "vault", type: "address", internalType: "address" }, + { name: "assets", type: "uint256", internalType: "uint256" }, + { name: "maxShares", type: "uint256", internalType: "uint256" }, + { name: "receiver", type: "address", internalType: "address" }, + { name: "owner", type: "address", internalType: "address" }, + ], + outputs: [], + stateMutability: "payable", + }, +] as const; + +export const morphoBundlerAbi = [ + ...baseBundlerAbi, + { + type: "function", + name: "MORPHO", + inputs: [], + outputs: [{ name: "", type: "address", internalType: "contract IMorpho" }], + stateMutability: "view", + }, + { + type: "function", + name: "morphoBorrow", + inputs: [ + { + name: "marketParams", + type: "tuple", + internalType: "struct MarketParams", + components: [ + { name: "loanToken", type: "address", internalType: "address" }, + { name: "collateralToken", type: "address", internalType: "address" }, + { name: "oracle", type: "address", internalType: "address" }, + { name: "irm", type: "address", internalType: "address" }, + { name: "lltv", type: "uint256", internalType: "uint256" }, + ], + }, + { name: "assets", type: "uint256", internalType: "uint256" }, + { name: "shares", type: "uint256", internalType: "uint256" }, + { name: "slippageAmount", type: "uint256", internalType: "uint256" }, + { name: "receiver", type: "address", internalType: "address" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "morphoFlashLoan", + inputs: [ + { name: "token", type: "address", internalType: "address" }, + { name: "assets", type: "uint256", internalType: "uint256" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "morphoRepay", + inputs: [ + { + name: "marketParams", + type: "tuple", + internalType: "struct MarketParams", + components: [ + { name: "loanToken", type: "address", internalType: "address" }, + { name: "collateralToken", type: "address", internalType: "address" }, + { name: "oracle", type: "address", internalType: "address" }, + { name: "irm", type: "address", internalType: "address" }, + { name: "lltv", type: "uint256", internalType: "uint256" }, + ], + }, + { name: "assets", type: "uint256", internalType: "uint256" }, + { name: "shares", type: "uint256", internalType: "uint256" }, + { name: "slippageAmount", type: "uint256", internalType: "uint256" }, + { name: "onBehalf", type: "address", internalType: "address" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "morphoSetAuthorizationWithSig", + inputs: [ + { + name: "authorization", + type: "tuple", + internalType: "struct Authorization", + components: [ + { name: "authorizer", type: "address", internalType: "address" }, + { name: "authorized", type: "address", internalType: "address" }, + { name: "isAuthorized", type: "bool", internalType: "bool" }, + { name: "nonce", type: "uint256", internalType: "uint256" }, + { name: "deadline", type: "uint256", internalType: "uint256" }, + ], + }, + { + name: "signature", + type: "tuple", + internalType: "struct Signature", + components: [ + { name: "v", type: "uint8", internalType: "uint8" }, + { name: "r", type: "bytes32", internalType: "bytes32" }, + { name: "s", type: "bytes32", internalType: "bytes32" }, + ], + }, + { name: "skipRevert", type: "bool", internalType: "bool" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "morphoSupply", + inputs: [ + { + name: "marketParams", + type: "tuple", + internalType: "struct MarketParams", + components: [ + { name: "loanToken", type: "address", internalType: "address" }, + { name: "collateralToken", type: "address", internalType: "address" }, + { name: "oracle", type: "address", internalType: "address" }, + { name: "irm", type: "address", internalType: "address" }, + { name: "lltv", type: "uint256", internalType: "uint256" }, + ], + }, + { name: "assets", type: "uint256", internalType: "uint256" }, + { name: "shares", type: "uint256", internalType: "uint256" }, + { name: "slippageAmount", type: "uint256", internalType: "uint256" }, + { name: "onBehalf", type: "address", internalType: "address" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "morphoSupplyCollateral", + inputs: [ + { + name: "marketParams", + type: "tuple", + internalType: "struct MarketParams", + components: [ + { name: "loanToken", type: "address", internalType: "address" }, + { name: "collateralToken", type: "address", internalType: "address" }, + { name: "oracle", type: "address", internalType: "address" }, + { name: "irm", type: "address", internalType: "address" }, + { name: "lltv", type: "uint256", internalType: "uint256" }, + ], + }, + { name: "assets", type: "uint256", internalType: "uint256" }, + { name: "onBehalf", type: "address", internalType: "address" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "morphoWithdraw", + inputs: [ + { + name: "marketParams", + type: "tuple", + internalType: "struct MarketParams", + components: [ + { name: "loanToken", type: "address", internalType: "address" }, + { name: "collateralToken", type: "address", internalType: "address" }, + { name: "oracle", type: "address", internalType: "address" }, + { name: "irm", type: "address", internalType: "address" }, + { name: "lltv", type: "uint256", internalType: "uint256" }, + ], + }, + { name: "assets", type: "uint256", internalType: "uint256" }, + { name: "shares", type: "uint256", internalType: "uint256" }, + { name: "slippageAmount", type: "uint256", internalType: "uint256" }, + { name: "receiver", type: "address", internalType: "address" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "morphoWithdrawCollateral", + inputs: [ + { + name: "marketParams", + type: "tuple", + internalType: "struct MarketParams", + components: [ + { name: "loanToken", type: "address", internalType: "address" }, + { name: "collateralToken", type: "address", internalType: "address" }, + { name: "oracle", type: "address", internalType: "address" }, + { name: "irm", type: "address", internalType: "address" }, + { name: "lltv", type: "uint256", internalType: "uint256" }, + ], + }, + { name: "assets", type: "uint256", internalType: "uint256" }, + { name: "receiver", type: "address", internalType: "address" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "onMorphoFlashLoan", + inputs: [ + { name: "", type: "uint256", internalType: "uint256" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "onMorphoRepay", + inputs: [ + { name: "", type: "uint256", internalType: "uint256" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "onMorphoSupply", + inputs: [ + { name: "", type: "uint256", internalType: "uint256" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "onMorphoSupplyCollateral", + inputs: [ + { name: "", type: "uint256", internalType: "uint256" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "reallocateTo", + inputs: [ + { name: "publicAllocator", type: "address", internalType: "address" }, + { name: "vault", type: "address", internalType: "address" }, + { name: "value", type: "uint256", internalType: "uint256" }, + { + name: "withdrawals", + type: "tuple[]", + internalType: "struct Withdrawal[]", + components: [ + { + name: "marketParams", + type: "tuple", + internalType: "struct MarketParams", + components: [ + { name: "loanToken", type: "address", internalType: "address" }, + { name: "collateralToken", type: "address", internalType: "address" }, + { name: "oracle", type: "address", internalType: "address" }, + { name: "irm", type: "address", internalType: "address" }, + { name: "lltv", type: "uint256", internalType: "uint256" }, + ], + }, + { name: "amount", type: "uint128", internalType: "uint128" }, + ], + }, + { + name: "supplyMarketParams", + type: "tuple", + internalType: "struct MarketParams", + components: [ + { name: "loanToken", type: "address", internalType: "address" }, + { name: "collateralToken", type: "address", internalType: "address" }, + { name: "oracle", type: "address", internalType: "address" }, + { name: "irm", type: "address", internalType: "address" }, + { name: "lltv", type: "uint256", internalType: "uint256" }, + ], + }, + ], + outputs: [], + stateMutability: "payable", + }, +] as const; + +export const urdBundlerAbi = [ + ...baseBundlerAbi, + { + type: "function", + name: "urdClaim", + inputs: [ + { name: "distributor", type: "address", internalType: "address" }, + { name: "account", type: "address", internalType: "address" }, + { name: "reward", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + { name: "proof", type: "bytes32[]", internalType: "bytes32[]" }, + { name: "skipRevert", type: "bool", internalType: "bool" }, + ], + outputs: [], + stateMutability: "payable", + }, +] as const; + +export const wNativeBundlerAbi = [ + ...baseBundlerAbi, + { type: "receive", stateMutability: "payable" }, + { + type: "function", + name: "WRAPPED_NATIVE", + inputs: [], + outputs: [{ name: "", type: "address", internalType: "address" }], + stateMutability: "view", + }, + { + type: "function", + name: "unwrapNative", + inputs: [{ name: "amount", type: "uint256", internalType: "uint256" }], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "wrapNative", + inputs: [{ name: "amount", type: "uint256", internalType: "uint256" }], + outputs: [], + stateMutability: "payable", + }, +] as const; + +export const stEthBundlerAbi = [ + ...baseBundlerAbi, + { + type: "function", + name: "ST_ETH", + inputs: [], + outputs: [{ name: "", type: "address", internalType: "address" }], + stateMutability: "view", + }, + { + type: "function", + name: "WST_ETH", + inputs: [], + outputs: [{ name: "", type: "address", internalType: "address" }], + stateMutability: "view", + }, + { + type: "function", + name: "stakeEth", + inputs: [ + { name: "amount", type: "uint256", internalType: "uint256" }, + { name: "minShares", type: "uint256", internalType: "uint256" }, + { name: "referral", type: "address", internalType: "address" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "unwrapStEth", + inputs: [{ name: "amount", type: "uint256", internalType: "uint256" }], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "wrapStEth", + inputs: [{ name: "amount", type: "uint256", internalType: "uint256" }], + outputs: [], + stateMutability: "payable", + }, +] as const; + +export const aaveV2MigrationBundlerAbi = [ + ...transferBundlerAbi, + ...permitBundlerAbi, + ...permit2BundlerAbi, + ...stEthBundlerAbi, + ...erc4626BundlerAbi, + ...morphoBundlerAbi, + { + type: "constructor", + inputs: [ + { name: "morpho", type: "address", internalType: "address" }, + { name: "aaveV2Pool", type: "address", internalType: "address" }, + { name: "wstEth", type: "address", internalType: "address" }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "AAVE_V2_POOL", + inputs: [], + outputs: [{ name: "", type: "address", internalType: "contract IAaveV2" }], + stateMutability: "view", + }, + { + type: "function", + name: "aaveV2Repay", + inputs: [ + { name: "asset", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + { name: "interestRateMode", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "aaveV2Withdraw", + inputs: [ + { name: "asset", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { type: "error", name: "UnsafeCast", inputs: [] }, +] as const; + +export const aaveV3MigrationBundlerAbi = [ + ...transferBundlerAbi, + ...permitBundlerAbi, + ...permit2BundlerAbi, + ...stEthBundlerAbi, + ...erc4626BundlerAbi, + ...morphoBundlerAbi, + { + type: "constructor", + inputs: [ + { name: "morpho", type: "address", internalType: "address" }, + { name: "aaveV3Pool", type: "address", internalType: "address" }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "AAVE_V3_POOL", + inputs: [], + outputs: [{ name: "", type: "address", internalType: "contract IAaveV3" }], + stateMutability: "view", + }, + { + type: "function", + name: "aaveV3Repay", + inputs: [ + { name: "asset", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + { name: "interestRateMode", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "aaveV3Withdraw", + inputs: [ + { name: "asset", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { type: "error", name: "UnsafeCast", inputs: [] }, +] as const; + +export const aaveV3OptimizerMigrationBundlerAbi = [ + ...transferBundlerAbi, + ...permitBundlerAbi, + ...permit2BundlerAbi, + ...stEthBundlerAbi, + ...erc4626BundlerAbi, + ...morphoBundlerAbi, + { + type: "constructor", + inputs: [ + { name: "morpho", type: "address", internalType: "address" }, + { name: "aaveV3Optimizer", type: "address", internalType: "address" }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "AAVE_V3_OPTIMIZER", + inputs: [], + outputs: [{ name: "", type: "address", internalType: "contract IAaveV3Optimizer" }], + stateMutability: "view", + }, + { + type: "function", + name: "aaveV3OptimizerApproveManagerWithSig", + inputs: [ + { name: "isApproved", type: "bool", internalType: "bool" }, + { name: "nonce", type: "uint256", internalType: "uint256" }, + { name: "deadline", type: "uint256", internalType: "uint256" }, + { + name: "signature", + type: "tuple", + internalType: "struct Signature", + components: [ + { name: "v", type: "uint8", internalType: "uint8" }, + { name: "r", type: "bytes32", internalType: "bytes32" }, + { name: "s", type: "bytes32", internalType: "bytes32" }, + ], + }, + { name: "skipRevert", type: "bool", internalType: "bool" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "aaveV3OptimizerRepay", + inputs: [ + { name: "underlying", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "aaveV3OptimizerWithdraw", + inputs: [ + { name: "underlying", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + { name: "maxIterations", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "aaveV3OptimizerWithdrawCollateral", + inputs: [ + { name: "underlying", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { type: "error", name: "UnsafeCast", inputs: [] }, +] as const; + +export const compoundV2MigrationBundlerAbi = [ + ...transferBundlerAbi, + ...permitBundlerAbi, + ...permit2BundlerAbi, + ...stEthBundlerAbi, + ...erc4626BundlerAbi, + ...morphoBundlerAbi, + { + type: "constructor", + inputs: [ + { name: "morpho", type: "address", internalType: "address" }, + { name: "wNative", type: "address", internalType: "address" }, + { name: "cEth", type: "address", internalType: "address" }, + ], + stateMutability: "nonpayable", + }, + { type: "receive", stateMutability: "payable" }, + { + type: "function", + name: "C_ETH", + inputs: [], + outputs: [{ name: "", type: "address", internalType: "address" }], + stateMutability: "view", + }, + { + type: "function", + name: "compoundV2Redeem", + inputs: [ + { name: "cToken", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "compoundV2Repay", + inputs: [ + { name: "cToken", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { type: "error", name: "UnsafeCast", inputs: [] }, +] as const; + +export const compoundV3MigrationBundlerAbi = [ + ...transferBundlerAbi, + ...permitBundlerAbi, + ...permit2BundlerAbi, + ...stEthBundlerAbi, + ...erc4626BundlerAbi, + ...morphoBundlerAbi, + { + type: "constructor", + inputs: [{ name: "morpho", type: "address", internalType: "address" }], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "compoundV3AllowBySig", + inputs: [ + { name: "instance", type: "address", internalType: "address" }, + { name: "isAllowed", type: "bool", internalType: "bool" }, + { name: "nonce", type: "uint256", internalType: "uint256" }, + { name: "expiry", type: "uint256", internalType: "uint256" }, + { name: "v", type: "uint8", internalType: "uint8" }, + { name: "r", type: "bytes32", internalType: "bytes32" }, + { name: "s", type: "bytes32", internalType: "bytes32" }, + { name: "skipRevert", type: "bool", internalType: "bool" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "compoundV3Repay", + inputs: [ + { name: "instance", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "compoundV3WithdrawFrom", + inputs: [ + { name: "instance", type: "address", internalType: "address" }, + { name: "asset", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { type: "error", name: "UnsafeCast", inputs: [] }, +] as const; diff --git a/packages/viem/src/index.ts b/packages/viem/src/index.ts new file mode 100644 index 00000000..09bd89c6 --- /dev/null +++ b/packages/viem/src/index.ts @@ -0,0 +1,2 @@ +export { BundlerAction, BundlerCall } from "./BundlerAction"; +export * from "./abis"; diff --git a/packages/viem/test/EthereumBundler.spec.ts b/packages/viem/test/EthereumBundler.spec.ts new file mode 100644 index 00000000..eb451225 --- /dev/null +++ b/packages/viem/test/EthereumBundler.spec.ts @@ -0,0 +1,336 @@ +import { expect } from "chai"; +import { MaxUint256, Signature, TypedDataDomain, TypedDataField, toBigInt } from "ethers"; +import hre from "hardhat"; + +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 2443407f..0d5ed029 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" @@ -1380,7 +1387,35 @@ __metadata: typescript: "npm:^5.2.2" peerDependencies: ethers: ^6.12.0 - evm-maths: ^6.0.0 + 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" + "@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" + ethers: "npm:^6.13.2" + 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" + typescript: "npm:^5.2.2" + viem: "npm:^2.21.15" + peerDependencies: + viem: ^2.0.0 languageName: unknown linkType: soft @@ -1420,6 +1455,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 +1503,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 +2203,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 +2232,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 +2263,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 +2759,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 +6154,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 +9656,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 +9717,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"