diff --git a/config/global.json b/config/global.json index cc487fb86..1796c3587 100644 --- a/config/global.json +++ b/config/global.json @@ -8,6 +8,7 @@ "withdrawWallet": "0x08647cc950813966142A416D40C382e2c5DB73bB", "lifuelRebalanceWallet": "0xC71284231A726A18ac85c94D75f9fe17A185BeAF", "deployerWallet": "0x11F1022cA6AdEF6400e5677528a80d49a069C00c", + "intentExecutorWallet": "0x11F1022cA6AdEF6400e5677528a80d49a069C00c", "approvedSigsForRefundWallet": [ { "sig": "0x0d19e519", diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index 62661968f..cb8f59fc0 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -20552,5 +20552,21 @@ ] } } + }, + "IntentFactory": { + "arbitrum": { + "staging": { + "1.0.0": [ + { + "ADDRESS": "0x6e9B8579097F45E843584595541fF805101a18aA", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-07-09 16:26:46", + "CONSTRUCTOR_ARGS": "0x", + "SALT": "09072024", + "VERIFIED": "true" + } + ] + } + } } } diff --git a/deployments/arbitrum.diamond.staging.json b/deployments/arbitrum.diamond.staging.json index 0d3ca3de6..29a516f61 100644 --- a/deployments/arbitrum.diamond.staging.json +++ b/deployments/arbitrum.diamond.staging.json @@ -127,7 +127,9 @@ "Receiver": "0x59B341fF54543D66C7393FfD2A050E256c97669E", "RelayerCelerIM": "0x9d3573b1d85112446593f617f1f3eb5ec1778D27", "ServiceFeeCollector": "0x9cc3164f01ED3796Fdf7Da538484D634608D2203", - "TokenWrapper": "" + "TokenWrapper": "", + "IntentFactory": "", + "ReceiverStargateV2": "" } } } \ No newline at end of file diff --git a/deployments/arbitrum.staging.json b/deployments/arbitrum.staging.json index a6e1c552d..3d1489917 100644 --- a/deployments/arbitrum.staging.json +++ b/deployments/arbitrum.staging.json @@ -34,5 +34,6 @@ "SymbiosisFacet": "0xb590b3B312f3C73621aa1E363841c8baecc2E712", "DeBridgeDlnFacet": "0xE500dED7b9C9f1020870B7a6Db076Dbd892C0fea", "MayanFacet": "0xd596C903d78870786c5DB0E448ce7F87A65A0daD", - "StandardizedCallFacet": "0x637Ac9AddC9C38b3F52878E11620a9060DC71d8B" + "StandardizedCallFacet": "0x637Ac9AddC9C38b3F52878E11620a9060DC71d8B", + "IntentFactory": "0x6e9B8579097F45E843584595541fF805101a18aA" } \ No newline at end of file diff --git a/docs/IntentFactory.md b/docs/IntentFactory.md new file mode 100644 index 000000000..216d4190a --- /dev/null +++ b/docs/IntentFactory.md @@ -0,0 +1,85 @@ +# Intent Factory + +## Description + +The intent factory allows for triggering an action for a user +in a non-custodial way. The user/on-ramp/protocol/bridge simply sends funds to +an address we compute. It can then deploy a contract to that address and +trigger the action the user intended. This is done by encoding the user's +intent into that address/contract. Fallback handling allows the user to +withdraw their funds at any time. + +## How To Use + +1. The first step to generating an intent is to calculate the intent contract's +deterministic address. You do this by providing the following: +- A random `bytes32` id +- The receiver address +- The address of the output token +- Minimum amount of token to receive + +```solidity +// Compute the address of the intent +address intentClone = factory.getIntentAddress( + IIntent.InitData({ + intentId: RANDOM_BYTES32_ID, + receiver: RECEIVER_ADDRESS, + tokenOut: TOKEN_OUT_ADDRESS, + amountOutMin: 100 * 10**TOKEN_OUT_DECIMALS + }) +); +``` + +2. Next the tokens needed to fulfill the intent need to be sent to the +pre-calculated address. (NOTE: you can send multiple tokens if you wish). +A normal use-case would be bridging tokens from one chain to the pre-calculated +address on another chain and waiting for the bridge to complete to execute +the intent. + +3. Execute the intent by passing an array of sequential calldata that will +yield the intended output amount for the receiver. For example, the first call +would approve the deposited token to an AMM. The next call would perform the +swap. Finally transfer any positive slippage or a pre-determined fee. As long +as the minimum output amount is left, the call will succeed and the remaining +output tokens will be transferred to the receiver. + +```solidity +IIntent.Call[] memory calls = new IIntent.Call[](2); + +// get approve calldata +bytes memory approveCalldata = abi.encodeWithSignature( + "approve(address,uint256)", + AMM_ADDRESS, + 1000 +); +calls[0] = IIntent.Call({ + to: TOKEN_OUT_ADDRESS, + value: 0, + data: approveCalldata +}); + +// get swap calldata +bytes memory swapCalldata = abi.encodeWithSignature( + "swap(address,uint256,address,uint256)", + TOKEN_IN_ADDRESS, + 1000 * 10**TOKEN_IN_DECIMALS, + TOKEN_OUT_ADDRESS, + 100 * 10**TOKEN_OUT_DECIMALS +); +calls[1] = IIntent.Call({ + to: AMM_ADDRESS, + value: 0, + data: swapCalldata +}); + +// execute the intent +factory.deployAndExecuteIntent( + IIntent.InitData({ + intentId: intentId, + receiver: RECEIVER_ADDRESS, + tokenOut: TOKEN_OUT_ADDRESS, + amountOutMin: 100 * 10**TOKEN_OUT_DECIMALS + }), + calls +); +``` diff --git a/docs/README.md b/docs/README.md index 55948f4ab..e4f9d5581 100644 --- a/docs/README.md +++ b/docs/README.md @@ -26,7 +26,6 @@ - [Optimism Bridge Facet](./OptimismBridgeFacet.md) - [Periphery Registry Facet](./PeripheryRegistryFacet.md) - [Polygon Bridge Facet](./PolygonBridgeFacet.md) -- [Ronin Bridge Facet](./RoninBridgeFacet.md) - [Squid Facet](./SquidFacet.md) - [Standardized Call Facet](./StandardizedCallFacet.md) - [Stargate Facet](./StargateFacet.md) @@ -56,3 +55,4 @@ - [FeeCollector](./FeeCollector.md) - [Receiver](./Receiver.md) - [RelayerCelerIM](./RelayerCelerIM.md) +- [IntentFactory](./IntentFactory.md) diff --git a/package.json b/package.json index c33c048c4..380b88bd7 100644 --- a/package.json +++ b/package.json @@ -87,6 +87,7 @@ "@arbitrum/sdk": "^3.0.0", "@hop-protocol/sdk": "0.0.1-beta.310", "@layerzerolabs/lz-v2-utilities": "^2.3.21", + "@lifi/sdk": "^3.0.0", "@safe-global/api-kit": "^2.3.1", "@safe-global/protocol-kit": "^3.1.0", "@safe-global/safe-apps-sdk": "^9.0.0", diff --git a/script/demoScripts/demoIntentFactory.ts b/script/demoScripts/demoIntentFactory.ts new file mode 100644 index 000000000..3532adb45 --- /dev/null +++ b/script/demoScripts/demoIntentFactory.ts @@ -0,0 +1,135 @@ +import { arbitrum } from 'viem/chains' +import { defineCommand, runMain } from 'citty' +import * as Deployments from '../../deployments/arbitrum.staging.json' +import * as IntentFactory from '../../out/IntentFactory.sol/IntentFactory.json' +import { + Address, + createPublicClient, + createWalletClient, + encodeFunctionData, + http, + keccak256, + parseAbi, + toHex, +} from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import { ChainId, getQuote } from '@lifi/sdk' + +const INTENT_FACTORY_ADDRESS = Deployments.IntentFactory as Address +const ABI = IntentFactory.abi +const ERC20_ABI = parseAbi([ + 'function transfer(address,uint256) external', + 'function approve(address,uint256) external', +]) +const DAI = '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1' +const USDC = '0xaf88d065e77c8cC2239327C5EDb3A432268e5831' +const AMOUNT_TO_SWAP = '1000000000000000000' + +const main = defineCommand({ + meta: { + name: 'propose-to-safe', + description: 'Propose a transaction to a Gnosis Safe', + }, + args: { + privateKey: { + type: 'string', + description: 'Private key of the signer', + required: true, + }, + }, + async run({ args }) { + const { privateKey } = args + const account = privateKeyToAccount(`0x${privateKey}`) + // Read client + const publicClient = createPublicClient({ + chain: arbitrum, + transport: http(), + }) + + // Write client + const walletClient = createWalletClient({ + account, + chain: arbitrum, + transport: http(), + }) + + // Setup the intentfactory ABI + const intentFactory = { + address: INTENT_FACTORY_ADDRESS, + abi: ABI, + } + + // Get an initial quote from LIFI + let quote = await getQuote({ + fromAddress: account.address, + toAddress: account.address, + fromChain: ChainId.ARB, + toChain: ChainId.ARB, + fromToken: DAI, + toToken: USDC, + fromAmount: AMOUNT_TO_SWAP, + }) + console.log(quote) + + // Calculate the intent address + const intentData = { + intentId: keccak256(toHex(parseInt(Math.random().toString()))), + receiver: account.address, + tokenOut: USDC, + amountOutMin: quote.estimate.toAmountMin, + } + const predictedIntentAddress: Address = (await publicClient.readContract({ + ...intentFactory, + functionName: 'getIntentAddress', + args: [intentData], + })) as Address + console.log(predictedIntentAddress) + + // Send DAI to predictedIntentAddress + let tx = await walletClient.writeContract({ + address: DAI, + abi: ERC20_ABI, + functionName: 'transfer', + args: [predictedIntentAddress, BigInt(AMOUNT_TO_SWAP)], + }) + console.log(tx) + + // Get updated quote and use intent address + quote = await getQuote({ + fromAddress: predictedIntentAddress, + toAddress: predictedIntentAddress, + fromChain: ChainId.ARB, + toChain: ChainId.ARB, + fromToken: DAI, + toToken: USDC, + fromAmount: AMOUNT_TO_SWAP, + }) + + // Deploy intent and execute the swap + const calls = [] + const approveCallData = encodeFunctionData({ + abi: ERC20_ABI, + functionName: 'approve', + args: [quote.estimate.approvalAddress as Address, BigInt(AMOUNT_TO_SWAP)], + }) + calls.push({ + to: DAI, + data: approveCallData, + value: BigInt(0), + }) + calls.push({ + to: quote.transactionRequest?.to, + data: quote.transactionRequest?.data, + value: BigInt(0), + }) + tx = await walletClient.writeContract({ + address: intentFactory.address, + abi: ABI, + functionName: 'deployAndExecuteIntent', + args: [intentData, calls], + }) + console.log(tx) + }, +}) + +runMain(main) diff --git a/script/deploy/facets/DeployIntentFactory.s.sol b/script/deploy/facets/DeployIntentFactory.s.sol new file mode 100644 index 000000000..0094d739e --- /dev/null +++ b/script/deploy/facets/DeployIntentFactory.s.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.17; + +import { DeployScriptBase } from "./utils/DeployScriptBase.sol"; +import { IntentFactory } from "lifi/Periphery/IntentFactory.sol"; +import { stdJson } from "forge-std/Script.sol"; + +contract DeployScript is DeployScriptBase { + using stdJson for string; + + constructor() DeployScriptBase("IntentFactory") {} + + function run() + public + returns (IntentFactory deployed, bytes memory constructorArgs) + { + constructorArgs = getConstructorArgs(); + deployed = IntentFactory(deploy(type(IntentFactory).creationCode)); + } + + function getConstructorArgs() internal override returns (bytes memory) { + // get path of global config file + string memory globalConfigPath = string.concat( + root, + "/config/global.json" + ); + + // read file into json variable + string memory globalConfigJson = vm.readFile(globalConfigPath); + + // extract intentExecutorWallet address + address intentExecutorWalletAddress = globalConfigJson.readAddress( + ".intentExecutorWallet" + ); + + return abi.encode(intentExecutorWalletAddress); + } +} diff --git a/src/Helpers/SwapIntentHandler.sol b/src/Helpers/SwapIntentHandler.sol new file mode 100644 index 000000000..751ed3eaf --- /dev/null +++ b/src/Helpers/SwapIntentHandler.sol @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { LibClone } from "solady/utils/LibClone.sol"; +import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; +import { IIntent } from "../Interfaces/IIntent.sol"; + +interface IERC20 { + function balanceOf(address account) external view returns (uint256); +} + +/// @title Intent +/// @author LI.FI (https://li.fi) +/// @notice Intent contract that can execute arbitrary calls. +/// @custom:version 1.0.0 +contract SwapIntentHandler { + /// Storage /// + + struct IntentConfig { + bytes32 intentId; + bytes32 salt; + address owner; + address receiver; + address factory; + address tokenOut; + uint256 amountOutMin; + uint256 deadline; + uint8 executed; + } + + address public immutable implementation; + IntentConfig public config; + + /// Events /// + event IntentExecuted( + bytes32 indexed intentId, + address receiver, + address tokenOut, + uint256 amountOut + ); + + /// Errors /// + + error Unauthorized(); + error AlreadyExecuted(); + error InvalidParams(); + error ExecutionFailed(); + error InsufficientOutputAmount(); + + /// Constructor /// + + constructor() { + implementation = address(this); + } + + /// External Methods /// + + /// @notice Initializes the intent with the given parameters. + /// @param _initData The init data. + function init(IIntent.InitData calldata _initData) external { + config.salt = keccak256(abi.encode(_initData)); + config.factory = msg.sender; + address predictedAddress = LibClone.predictDeterministicAddress( + implementation, + config.salt, + msg.sender + ); + if (address(this) != predictedAddress) { + revert InvalidParams(); + } + + config.intentId = _initData.intentId; + config.owner = _initData.owner; + config.receiver = _initData.receiver; + config.tokenOut = _initData.tokenOut; + config.amountOutMin = _initData.amountOutMin; + } + + /// @notice Executes the intent with the given calls. + /// @param calls The calls to execute. + function execute(IIntent.Call[] calldata calls) external { + if (msg.sender != config.factory) { + revert Unauthorized(); + } + if (config.executed > 0) { + revert AlreadyExecuted(); + } + config.executed = 1; // Set as executed + + for (uint256 i = 0; i < calls.length; i++) { + (bool success, ) = calls[i].to.call{ value: calls[i].value }( + calls[i].data + ); + if (!success) { + revert ExecutionFailed(); + } + } + + bool isNative = config.tokenOut == address(0); + + if (isNative) { + // Handle native output + if (address(this).balance < config.amountOutMin) { + revert InsufficientOutputAmount(); + } + SafeTransferLib.safeTransferAllETH(config.receiver); + emit IntentExecuted( + config.intentId, + config.receiver, + config.tokenOut, + address(this).balance + ); + return; + } else { + uint256 balance = IERC20(config.tokenOut).balanceOf(address(this)); + // Handle ERC20 output + if (balance < config.amountOutMin) { + revert InsufficientOutputAmount(); + } + SafeTransferLib.safeTransferAll(config.tokenOut, config.receiver); + emit IntentExecuted( + config.intentId, + config.receiver, + config.tokenOut, + balance + ); + } + } + + /// @notice Withdraws all the tokens. + /// @param tokens The tokens to withdraw. + function withdrawAll( + address[] calldata tokens, + address payable receiver + ) external { + if (msg.sender != config.factory && msg.sender != config.owner) { + revert Unauthorized(); + } + for (uint256 i = 0; i < tokens.length; ) { + if (tokens[i] == address(0)) { + SafeTransferLib.safeTransferAllETH(receiver); + unchecked { + ++i; + } + continue; + } + SafeTransferLib.safeTransferAll(tokens[i], receiver); + unchecked { + ++i; + } + } + } + + // Recieve ETH + receive() external payable {} +} diff --git a/src/Interfaces/IIntent.sol b/src/Interfaces/IIntent.sol new file mode 100644 index 000000000..9c20f52e4 --- /dev/null +++ b/src/Interfaces/IIntent.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +interface IIntent { + struct Call { + address to; + uint256 value; + bytes data; + } + + struct InitData { + bytes32 intentId; + address owner; + address receiver; + address tokenOut; + uint256 amountOutMin; + uint256 deadline; + } +} diff --git a/src/Periphery/IntentFactory.sol b/src/Periphery/IntentFactory.sol new file mode 100644 index 000000000..7f931fea2 --- /dev/null +++ b/src/Periphery/IntentFactory.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { LibClone } from "solady/utils/LibClone.sol"; +import { IIntent } from "../Interfaces/IIntent.sol"; +import { SwapIntentHandler } from "../Helpers/SwapIntentHandler.sol"; +import { TransferrableOwnership } from "../Helpers/TransferrableOwnership.sol"; + +/// @title Intent Factory +/// @author LI.FI (https://li.fi) +/// @notice Deploys minimal proxies of "intents" that can execute arbitrary calls. +/// @custom:version 1.0.0 +contract IntentFactory is TransferrableOwnership { + /// Storage /// + + address public immutable implementation; + + /// Errors /// + + error Unauthorized(); + + /// Constructor /// + + constructor(address _owner) TransferrableOwnership(_owner) { + implementation = payable(address(new SwapIntentHandler())); + } + + /// External Functions /// + + /// @notice Deploys a new intent and executes the given calls. + /// @param _initData The init data. + /// @param _calls The calls to execute. + function deployAndExecuteIntent( + IIntent.InitData calldata _initData, + IIntent.Call[] calldata _calls + ) external { + if (msg.sender != owner) { + revert Unauthorized(); + } + + bytes32 salt = keccak256(abi.encode(_initData)); + address payable clone = payable( + LibClone.cloneDeterministic(implementation, salt) + ); + SwapIntentHandler(clone).init(_initData); + SwapIntentHandler(clone).execute(_calls); + } + + /// @notice Deploys a new intent and withdraws all the tokens. + /// @param _initData The init data. + /// @param tokens The tokens to withdraw. + function deployAndWithdrawAll( + IIntent.InitData calldata _initData, + address[] calldata tokens, + address payable receiver + ) external { + if (msg.sender != _initData.owner) { + revert Unauthorized(); + } + bytes32 salt = keccak256(abi.encode(_initData)); + address payable clone = payable( + LibClone.cloneDeterministic(implementation, salt) + ); + SwapIntentHandler(clone).init(_initData); + SwapIntentHandler(clone).withdrawAll(tokens, receiver); + } + + /// @notice Predicts the address of the intent. + /// @param _initData The init data. + function getIntentAddress( + IIntent.InitData calldata _initData + ) external view returns (address) { + bytes32 salt = keccak256(abi.encode(_initData)); + return + LibClone.predictDeterministicAddress( + implementation, + salt, + address(this) + ); + } +} diff --git a/test/solidity/Periphery/IntentFactory.t.sol b/test/solidity/Periphery/IntentFactory.t.sol new file mode 100644 index 000000000..0d3e28404 --- /dev/null +++ b/test/solidity/Periphery/IntentFactory.t.sol @@ -0,0 +1,444 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { Test, console } from "forge-std/Test.sol"; +import { SwapIntentHandler } from "lifi/Helpers/SwapIntentHandler.sol"; +import { IIntent } from "lifi/Interfaces/IIntent.sol"; +import { IntentFactory } from "lifi/Periphery/IntentFactory.sol"; +import { TestToken } from "../utils/TestToken.sol"; +import { TestAMM } from "../utils/TestAMM.sol"; + +contract IntentFactoryTest is Test { + SwapIntentHandler public implementation; + IntentFactory public factory; + TestAMM public amm; + TestToken public tokenA; + TestToken public tokenB; + address public alice; + address public receiver; + + event IntentExecuted( + bytes32 indexed intentId, + address receiver, + address tokenOut, + uint256 amountOut + ); + + function setUp() public { + (implementation, factory) = deploy(); + amm = new TestAMM(); + tokenA = new TestToken("TokenA", "TKNA", 18); + tokenB = new TestToken("TokenB", "TKNB", 18); + alice = makeAddr("alice"); + receiver = makeAddr("receiver"); + } + + function deploy() public returns (SwapIntentHandler, IntentFactory) { + IntentFactory _factory = new IntentFactory(address(this)); + address payable _implementation = payable(_factory.implementation()); + return (SwapIntentHandler(_implementation), _factory); + } + + function test_can_deposit_and_execute_swap() public { + tokenA.mint(alice, 1000); + bytes32 intentId = keccak256("intentId"); + + // Compute the address of the intent + address intentClone = factory.getIntentAddress( + IIntent.InitData({ + intentId: intentId, + owner: alice, + receiver: receiver, + tokenOut: address(tokenB), + amountOutMin: 100, + deadline: block.timestamp + }) + ); + + // Send tokens to the precomputed address + vm.prank(alice); + tokenA.transfer(intentClone, 1000); + + IIntent.Call[] memory calls = new IIntent.Call[](2); + + // get approve calldata + bytes memory approveCalldata = abi.encodeWithSignature( + "approve(address,uint256)", + address(amm), + 1000 + ); + calls[0] = IIntent.Call({ + to: address(tokenA), + value: 0, + data: approveCalldata + }); + + // get swap calldata + bytes memory swapCalldata = abi.encodeWithSignature( + "swap(address,uint256,address,uint256)", + address(tokenA), + 1000, + address(tokenB), + 100 + ); + calls[1] = IIntent.Call({ + to: address(amm), + value: 0, + data: swapCalldata + }); + + vm.expectEmit(); + emit IntentExecuted(intentId, receiver, address(tokenB), 100); + + // execute the intent + factory.deployAndExecuteIntent( + IIntent.InitData({ + intentId: intentId, + owner: alice, + receiver: receiver, + tokenOut: address(tokenB), + amountOutMin: 100, + deadline: block.timestamp + }), + calls + ); + + // assertions + assertEq(tokenB.balanceOf(receiver), 100); + assertEq(tokenA.balanceOf(alice), 0); + assertEq(tokenB.balanceOf(intentClone), 0); + assertEq(tokenA.balanceOf(intentClone), 0); + } + + function test_fails_to_execute_after_executed() public { + tokenA.mint(alice, 2000); + bytes32 intentId = keccak256("intentId"); + + // Compute the address of the intent + address intentClone = factory.getIntentAddress( + IIntent.InitData({ + intentId: intentId, + owner: alice, + receiver: receiver, + tokenOut: address(tokenB), + amountOutMin: 100, + deadline: block.timestamp + }) + ); + + // Send tokens to the precomputed address + vm.prank(alice); + tokenA.transfer(intentClone, 1000); + + IIntent.Call[] memory calls = new IIntent.Call[](2); + + // get approve calldata + bytes memory approveCalldata = abi.encodeWithSignature( + "approve(address,uint256)", + address(amm), + 1000 + ); + calls[0] = IIntent.Call({ + to: address(tokenA), + value: 0, + data: approveCalldata + }); + + // get swap calldata + bytes memory swapCalldata = abi.encodeWithSignature( + "swap(address,uint256,address,uint256)", + address(tokenA), + 1000, + address(tokenB), + 100 + ); + calls[1] = IIntent.Call({ + to: address(amm), + value: 0, + data: swapCalldata + }); + + vm.expectEmit(); + emit IntentExecuted(intentId, receiver, address(tokenB), 100); + + // execute the intent + factory.deployAndExecuteIntent( + IIntent.InitData({ + intentId: intentId, + owner: alice, + receiver: receiver, + tokenOut: address(tokenB), + amountOutMin: 100, + deadline: block.timestamp + }), + calls + ); + + vm.prank(alice); + tokenA.transfer(intentClone, 1000); + + vm.expectRevert(); + + // execute the intent + factory.deployAndExecuteIntent( + IIntent.InitData({ + intentId: intentId, + owner: alice, + receiver: receiver, + tokenOut: address(tokenB), + amountOutMin: 100, + deadline: block.timestamp + }), + calls + ); + } + + function test_fail_when_min_amount_not_received() public { + tokenA.mint(alice, 1000); + bytes32 intentId = keccak256("intentId"); + + // Compute the address of the intent + address intentClone = factory.getIntentAddress( + IIntent.InitData({ + intentId: intentId, + owner: alice, + receiver: receiver, + tokenOut: address(tokenB), + amountOutMin: 100, + deadline: block.timestamp + }) + ); + + // Send tokens to the precomputed address + vm.prank(alice); + tokenA.transfer(intentClone, 1000); + + IIntent.Call[] memory calls = new IIntent.Call[](2); + + // get approve calldata + bytes memory approveCalldata = abi.encodeWithSignature( + "approve(address,uint256)", + address(amm), + 1000 + ); + calls[0] = IIntent.Call({ + to: address(tokenA), + value: 0, + data: approveCalldata + }); + + // get swap calldata + bytes memory swapCalldata = abi.encodeWithSignature( + "swap(address,uint256,address,uint256)", + address(tokenA), + 1000, + address(tokenB), + 1 + ); + calls[1] = IIntent.Call({ + to: address(amm), + value: 0, + data: swapCalldata + }); + + vm.expectRevert(); + // execute the intent + factory.deployAndExecuteIntent( + IIntent.InitData({ + intentId: intentId, + owner: alice, + receiver: receiver, + tokenOut: address(tokenB), + amountOutMin: 100, + deadline: block.timestamp + }), + calls + ); + } + + function test_can_deposit_native_and_execute_swap() public { + bytes32 intentId = keccak256("intentId"); + + // Compute the address of the intent + address intentClone = factory.getIntentAddress( + IIntent.InitData({ + intentId: intentId, + owner: alice, + receiver: receiver, + tokenOut: address(tokenB), + amountOutMin: 100, + deadline: block.timestamp + }) + ); + + // Send tokens to the precomputed address + vm.prank(alice); + (bool ok, ) = intentClone.call{ value: 0.1 ether }(""); + ok; + + IIntent.Call[] memory calls = new IIntent.Call[](1); + + // get swap calldata + bytes memory swapCalldata = abi.encodeWithSignature( + "swap(address,uint256,address,uint256)", + address(0), + 0.1 ether, + address(tokenB), + 100 + ); + calls[0] = IIntent.Call({ + to: address(amm), + value: 0, + data: swapCalldata + }); + + vm.expectEmit(); + emit IntentExecuted(intentId, receiver, address(tokenB), 100); + + // execute the intent + factory.deployAndExecuteIntent( + IIntent.InitData({ + intentId: intentId, + owner: alice, + receiver: receiver, + tokenOut: address(tokenB), + amountOutMin: 100, + deadline: block.timestamp + }), + calls + ); + + // assertions + assertEq(tokenB.balanceOf(receiver), 100); + assertEq(tokenB.balanceOf(intentClone), 0); + assertEq(intentClone.balance, 0); + } + + function test_can_deposit_and_withdraw_all() public { + tokenA.mint(alice, 2000); + bytes32 intentId = keccak256("intentId"); + // Compute the address of the intent + address intentClone = factory.getIntentAddress( + IIntent.InitData({ + intentId: intentId, + owner: alice, + receiver: receiver, + tokenOut: address(tokenB), + amountOutMin: 100, + deadline: block.timestamp + }) + ); + // Send tokens to the precomputed address + vm.startPrank(alice); + tokenA.transfer(intentClone, 1000); + (bool ok, ) = intentClone.call{ value: 1 ether }(""); + ok; + // Deploy and withdraw all tokens + address[] memory tokens = new address[](2); + tokens[0] = address(tokenA); + tokens[1] = address(0); + factory.deployAndWithdrawAll( + IIntent.InitData({ + intentId: intentId, + owner: alice, + receiver: receiver, + tokenOut: address(tokenB), + amountOutMin: 100, + deadline: block.timestamp + }), + tokens, + payable(alice) + ); + + // Send more tokens + tokenA.transfer(intentClone, 1000); + + // Withdraw again + SwapIntentHandler(payable(intentClone)).withdrawAll( + tokens, + payable(alice) + ); + vm.stopPrank(); + + // assertions + assertEq(tokenA.balanceOf(alice), 2000); + assertEq(tokenA.balanceOf(intentClone), 0); + assertEq(intentClone.balance, 0); + } + + function test_can_withdraw_after_intent_is_executed() public { + tokenA.mint(alice, 2000); + bytes32 intentId = keccak256("intentId"); + + // Compute the address of the intent + address intentClone = factory.getIntentAddress( + IIntent.InitData({ + intentId: intentId, + owner: alice, + receiver: receiver, + tokenOut: address(tokenB), + amountOutMin: 100, + deadline: block.timestamp + }) + ); + + // Send tokens to the precomputed address + vm.prank(alice); + tokenA.transfer(intentClone, 1000); + + IIntent.Call[] memory calls = new IIntent.Call[](2); + + // get approve calldata + bytes memory approveCalldata = abi.encodeWithSignature( + "approve(address,uint256)", + address(amm), + 1000 + ); + calls[0] = IIntent.Call({ + to: address(tokenA), + value: 0, + data: approveCalldata + }); + + // get swap calldata + bytes memory swapCalldata = abi.encodeWithSignature( + "swap(address,uint256,address,uint256)", + address(tokenA), + 1000, + address(tokenB), + 100 + ); + calls[1] = IIntent.Call({ + to: address(amm), + value: 0, + data: swapCalldata + }); + + vm.expectEmit(); + emit IntentExecuted(intentId, receiver, address(tokenB), 100); + + // execute the intent + factory.deployAndExecuteIntent( + IIntent.InitData({ + intentId: intentId, + owner: alice, + receiver: receiver, + tokenOut: address(tokenB), + amountOutMin: 100, + deadline: block.timestamp + }), + calls + ); + + vm.startPrank(alice); + tokenA.transfer(intentClone, 1000); + address[] memory tokens = new address[](1); + tokens[0] = address(tokenA); + + SwapIntentHandler(payable(intentClone)).withdrawAll( + tokens, + payable(alice) + ); + vm.stopPrank(); + } +} diff --git a/yarn.lock b/yarn.lock index 3f3afbef3..2e942aed9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -43,7 +43,7 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/runtime@^7.24.6": +"@babel/runtime@^7.24.6", "@babel/runtime@^7.24.7": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.7.tgz#f4f0d5530e8dbdf59b3451b9b3e594b6ba082e12" integrity sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw== @@ -781,6 +781,22 @@ "@solana/web3.js" "^1.92.1" tiny-invariant "^1.3.1" +"@lifi/sdk@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@lifi/sdk/-/sdk-3.0.0.tgz#cfb0a885279744571ff1ec11107877fa6a929b9b" + integrity sha512-Xinus12HOlQqJm/tdl3uNt0/Mp2+x3lK5cFPo2OsfLHmWirtc+tax1ExlUcDq5+ODURwKUrbo9WisHqa8DB1uA== + dependencies: + "@lifi/types" "^13.17.0" + "@solana/wallet-adapter-base" "^0.9.23" + "@solana/web3.js" "^1.93.2" + eth-rpc-errors "^4.0.3" + viem "^2.16.2" + +"@lifi/types@^13.17.0": + version "13.17.1" + resolved "https://registry.yarnpkg.com/@lifi/types/-/types-13.17.1.tgz#4745615ac6dd7cfc15c9b864d7496409d7ee0487" + integrity sha512-w3UCUzHMCMDq4CjxlawBQsAkSv22z2Kflb/NbhnhzKrMqtFAaxgW+82hEGs+Vlbyrzi6F6M2PEkkayzJioSCBQ== + "@maticnetwork/maticjs@^2.0.38": version "2.0.51" resolved "https://registry.yarnpkg.com/@maticnetwork/maticjs/-/maticjs-2.0.51.tgz#4fe82150384d6241ed2276bba4ad7c111445593f" @@ -865,17 +881,17 @@ dependencies: "@noble/hashes" "1.3.3" -"@noble/curves@^1.4.0": +"@noble/curves@1.4.0", "@noble/curves@^1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.4.0.tgz#f05771ef64da724997f69ee1261b2417a49522d6" integrity sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg== dependencies: "@noble/hashes" "1.4.0" -"@noble/curves@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.4.0.tgz#f05771ef64da724997f69ee1261b2417a49522d6" - integrity sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg== +"@noble/curves@~1.4.0": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.4.2.tgz#40309198c76ed71bc6dbf7ba24e81ceb4d0d1fe9" + integrity sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw== dependencies: "@noble/hashes" "1.4.0" @@ -894,8 +910,7 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== -"@noble/hashes@1.4.0", "@noble/hashes@^1.3.3", "@noble/hashes@^1.4.0": -"@noble/hashes@1.4.0", "@noble/hashes@^1.3.3", "@noble/hashes@^1.4.0": +"@noble/hashes@1.4.0", "@noble/hashes@^1.3.3", "@noble/hashes@^1.4.0", "@noble/hashes@~1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== @@ -1280,7 +1295,7 @@ resolved "https://registry.yarnpkg.com/@safe-global/safe-gateway-typescript-sdk/-/safe-gateway-typescript-sdk-3.21.1.tgz#984ec2d3d4211caf6a96786ab922b39909093538" integrity sha512-7nakIjcRSs6781LkizYpIfXh1DYlkUDqyALciqz/BjFU/S97sVjZdL4cuKsG9NEarytE+f6p0Qbq2Bo1aocVUA== -"@scure/base@~1.1.0", "@scure/base@~1.1.2": +"@scure/base@~1.1.0", "@scure/base@~1.1.2", "@scure/base@~1.1.6": version "1.1.7" resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.7.tgz#fe973311a5c6267846aa131bc72e96c5d40d2b30" integrity sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g== @@ -1317,6 +1332,15 @@ "@noble/hashes" "~1.3.2" "@scure/base" "~1.1.4" +"@scure/bip32@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.4.0.tgz#4e1f1e196abedcef395b33b9674a042524e20d67" + integrity sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg== + dependencies: + "@noble/curves" "~1.4.0" + "@noble/hashes" "~1.4.0" + "@scure/base" "~1.1.6" + "@scure/bip39@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.0.tgz#92f11d095bae025f166bef3defcc5bf4945d419a" @@ -1341,6 +1365,14 @@ "@noble/hashes" "~1.3.2" "@scure/base" "~1.1.4" +"@scure/bip39@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.3.0.tgz#0f258c16823ddd00739461ac31398b4e7d6a18c3" + integrity sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ== + dependencies: + "@noble/hashes" "~1.4.0" + "@scure/base" "~1.1.6" + "@sentry/core@5.30.0": version "5.30.0" resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.30.0.tgz#6b203664f69e75106ee8b5a2fe1d717379b331f3" @@ -1419,13 +1451,52 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== -"@solana/buffer-layout@^4.0.1": +"@solana/buffer-layout@^4 || ^3", "@solana/buffer-layout@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz#b996235eaec15b1e0b5092a8ed6028df77fa6c15" integrity sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA== dependencies: buffer "~6.0.3" +"@solana/wallet-adapter-base@^0.9.23": + version "0.9.23" + resolved "https://registry.yarnpkg.com/@solana/wallet-adapter-base/-/wallet-adapter-base-0.9.23.tgz#3b17c28afd44e173f44f658bf9700fd637e12a11" + integrity sha512-apqMuYwFp1jFi55NxDfvXUX2x1T0Zh07MxhZ/nCCTGys5raSfYUh82zen2BLv8BSDj/JxZ2P/s7jrQZGrX8uAw== + dependencies: + "@solana/wallet-standard-features" "^1.1.0" + "@wallet-standard/base" "^1.0.1" + "@wallet-standard/features" "^1.0.3" + eventemitter3 "^4.0.7" + +"@solana/wallet-standard-features@^1.1.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@solana/wallet-standard-features/-/wallet-standard-features-1.2.0.tgz#be8b3824abf5ebcfeaa7298445bf53f76a27c935" + integrity sha512-tUd9srDLkRpe1BYg7we+c4UhRQkq+XQWswsr/L1xfGmoRDF47BPSXf4zE7ZU2GRBGvxtGt7lwJVAufQyQYhxTQ== + dependencies: + "@wallet-standard/base" "^1.0.1" + "@wallet-standard/features" "^1.0.3" + +"@solana/web3.js@^1.87.6", "@solana/web3.js@^1.93.2": + version "1.94.0" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.94.0.tgz#f662ce046f59cb294e8304beeb4d549c3ff05d73" + integrity sha512-wMiBebzu5I2fTSz623uj6VXpWFhl0d7qJKqPFK2I4IBLTNUdv+bOeA4H7OBM7Gworv7sOvB3xibRql6l61MeqA== + dependencies: + "@babel/runtime" "^7.24.7" + "@noble/curves" "^1.4.0" + "@noble/hashes" "^1.4.0" + "@solana/buffer-layout" "^4.0.1" + agentkeepalive "^4.5.0" + bigint-buffer "^1.1.5" + bn.js "^5.2.1" + borsh "^0.7.0" + bs58 "^4.0.1" + buffer "6.0.3" + fast-stable-stringify "^1.0.0" + jayson "^4.1.0" + node-fetch "^2.7.0" + rpc-websockets "^9.0.2" + superstruct "^1.0.4" + "@solana/web3.js@^1.92.1": version "1.92.2" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.92.2.tgz#49760e887b31e309543668e81e1fa60f53a3e34e" @@ -1572,13 +1643,6 @@ dependencies: "@types/node" "*" -"@types/connect@^3.4.33": - version "3.4.38" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" - integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== - dependencies: - "@types/node" "*" - "@types/fined@*": version "1.1.3" resolved "https://registry.yarnpkg.com/@types/fined/-/fined-1.1.3.tgz#83f03e8f0a8d3673dfcafb18fce3571f6250e1bc" @@ -1696,7 +1760,6 @@ dependencies: undici-types "~5.26.4" -"@types/node@^12.12.54", "@types/node@^12.12.6": "@types/node@^12.12.54", "@types/node@^12.12.6": version "12.20.55" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" @@ -1772,6 +1835,11 @@ dependencies: "@types/node" "*" +"@types/uuid@^8.3.4": + version "8.3.4" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" + integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== + "@types/ws@^7.4.4": version "7.4.7" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" @@ -1779,6 +1847,13 @@ dependencies: "@types/node" "*" +"@types/ws@^8.2.2": + version "8.5.10" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787" + integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== + dependencies: + "@types/node" "*" + "@typescript-eslint/eslint-plugin@^5.16.0": version "5.47.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.47.0.tgz#dadb79df3b0499699b155839fd6792f16897d910" @@ -1942,6 +2017,18 @@ resolved "https://registry.yarnpkg.com/@uniswap/v2-core/-/v2-core-1.0.1.tgz#af8f508bf183204779938969e2e54043e147d425" integrity sha512-MtybtkUPSyysqLY2U210NBDeCHX+ltHt3oADGdjqoThZaFRDKwM6k1Nb3F0A3hk5hwuQvytFWhrWHOEq6nVJ8Q== +"@wallet-standard/base@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@wallet-standard/base/-/base-1.0.1.tgz#860dd94d47c9e3c5c43b79d91c6afdbd7a36264e" + integrity sha512-1To3ekMfzhYxe0Yhkpri+Fedq0SYcfrOfJi3vbLjMwF2qiKPjTGLwZkf2C9ftdQmxES+hmxhBzTwF4KgcOwf8w== + +"@wallet-standard/features@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@wallet-standard/features/-/features-1.0.3.tgz#c992876c5e4f7a0672f8869c4146c87e0dfe48c8" + integrity sha512-m8475I6W5LTatTZuUz5JJNK42wFRgkJTB0I9tkruMwfqBF2UN2eomkYNVf9RbrsROelCRzSFmugqjKZBFaubsA== + dependencies: + "@wallet-standard/base" "^1.0.1" + "@yarnpkg/lockfile@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" @@ -1963,14 +2050,6 @@ JSONStream@^1.3.5: jsonparse "^1.2.0" through ">=2.2.7 <3" -JSONStream@^1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" - integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== - dependencies: - jsonparse "^1.2.0" - through ">=2.2.7 <3" - abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -1991,6 +2070,11 @@ abitype@1.0.0: resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.0.tgz#237176dace81d90d018bebf3a45cb42f2a2d9e97" integrity sha512-NMeMah//6bJ56H5XRj8QCV4AwuW6hB6zqz2LnhhLdcWVQOsXki6/Pn3APeqxCma62nXIcmZWdu1DlHWS74umVQ== +abitype@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.5.tgz#29d0daa3eea867ca90f7e4123144c1d1270774b6" + integrity sha512-YzDhti7cjlfaBhHutMaboYB21Ha3rXR9QTkNJFzYC4kC8YclaiwPBBBJY8ejFdu2wnJeZCVZSMlQJ7fi8S6hsw== + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -2092,13 +2176,6 @@ agentkeepalive@^4.5.0: dependencies: humanize-ms "^1.2.1" -agentkeepalive@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" - integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== - dependencies: - humanize-ms "^1.2.1" - aggregate-error@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" @@ -2465,13 +2542,6 @@ bigint-buffer@^1.1.5: dependencies: bindings "^1.3.0" -bigint-buffer@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/bigint-buffer/-/bigint-buffer-1.1.5.tgz#d038f31c8e4534c1f8d0015209bf34b4fa6dd442" - integrity sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA== - dependencies: - bindings "^1.3.0" - bigint-crypto-utils@^3.0.23: version "3.1.8" resolved "https://registry.yarnpkg.com/bigint-crypto-utils/-/bigint-crypto-utils-3.1.8.tgz#e2e0f40cf45488f9d7f0e32ff84152aa73819d5d" @@ -2506,13 +2576,6 @@ bindings@^1.3.0: dependencies: file-uri-to-path "1.0.0" -bindings@^1.3.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" - integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== - dependencies: - file-uri-to-path "1.0.0" - bl@^1.0.0: version "1.2.3" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7" @@ -2596,15 +2659,6 @@ borsh@^0.7.0: bs58 "^4.0.0" text-encoding-utf-8 "^1.0.2" -borsh@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.7.0.tgz#6e9560d719d86d90dc589bca60ffc8a6c51fec2a" - integrity sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA== - dependencies: - bn.js "^5.2.0" - bs58 "^4.0.0" - text-encoding-utf-8 "^1.0.2" - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -2701,7 +2755,6 @@ browserify-sign@^4.0.0: readable-stream "^3.6.0" safe-buffer "^5.2.0" -bs58@^4.0.0, bs58@^4.0.1: bs58@^4.0.0, bs58@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" @@ -2778,14 +2831,6 @@ buffer@6.0.3, buffer@^6.0.3, buffer@~6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" -buffer@6.0.3, buffer@^6.0.3, buffer@~6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - buffer@^5.0.5, buffer@^5.5.0, buffer@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" @@ -3234,11 +3279,6 @@ commander@^2.20.3: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^2.20.3: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -3643,11 +3683,6 @@ delay@^5.0.0: resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== -delay@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" - integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== - delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -3974,7 +4009,6 @@ es6-iterator@^2.0.3: es5-ext "^0.10.35" es6-symbol "^3.1.1" -es6-promise@^4.0.3, es6-promise@^4.2.8: es6-promise@^4.0.3, es6-promise@^4.2.8: version "4.2.8" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" @@ -3987,13 +4021,6 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" -es6-promisify@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" - integrity sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ== - dependencies: - es6-promise "^4.0.3" - es6-symbol@^3.1.1, es6-symbol@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" @@ -4346,6 +4373,13 @@ eth-lib@^0.1.26: ws "^3.0.0" xhr-request-promise "^0.1.2" +eth-rpc-errors@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eth-rpc-errors/-/eth-rpc-errors-4.0.3.tgz#6ddb6190a4bf360afda82790bb7d9d5e724f423a" + integrity sha512-Z3ymjopaoft7JDoxZcEb3pwdGh7yiYMhOwm2doUt6ASXlMavpNlK6Cre0+IMl2VSGyEU9rkiperQhp5iRxn5Pg== + dependencies: + fast-safe-stringify "^2.0.6" + eth-sig-util@^2.5.3: version "2.5.4" resolved "https://registry.yarnpkg.com/eth-sig-util/-/eth-sig-util-2.5.4.tgz#577b01fe491b6bf59b0464be09633e20c1677bc5" @@ -4693,11 +4727,6 @@ eyes@^0.1.8: resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" integrity sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ== -eyes@^0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" - integrity sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ== - fast-base64-decode@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz#b434a0dd7d92b12b43f26819300d2dafb83ee418" @@ -4744,10 +4773,10 @@ fast-redact@^3.0.0, fast-redact@^3.1.1: resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.1.2.tgz#d58e69e9084ce9fa4c1a6fa98a3e1ecf5d7839aa" integrity sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw== -fast-stable-stringify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz#5c5543462b22aeeefd36d05b34e51c78cb86d313" - integrity sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag== +fast-safe-stringify@^2.0.6: + version "2.1.1" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" + integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== fast-stable-stringify@^1.0.0: version "1.0.0" @@ -4799,11 +4828,6 @@ file-uri-to-path@1.0.0: resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== -file-uri-to-path@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== - fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -5759,13 +5783,6 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -humanize-ms@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" - integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== - dependencies: - ms "^2.0.0" - husky@^8.0.1: version "8.0.2" resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.2.tgz#5816a60db02650f1f22c8b69b928fd6bcd77a236" @@ -6264,11 +6281,6 @@ isomorphic-ws@^4.0.1: resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== -isomorphic-ws@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" - integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== - isows@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.3.tgz#93c1cf0575daf56e7120bab5c8c448b0809d0d74" @@ -6302,24 +6314,6 @@ jayson@^4.1.0: uuid "^8.3.2" ws "^7.4.5" -jayson@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/jayson/-/jayson-4.1.0.tgz#60dc946a85197317f2b1439d672a8b0a99cea2f9" - integrity sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A== - dependencies: - "@types/connect" "^3.4.33" - "@types/node" "^12.12.54" - "@types/ws" "^7.4.4" - JSONStream "^1.3.5" - commander "^2.20.3" - delay "^5.0.0" - es6-promisify "^5.0.0" - eyes "^0.1.8" - isomorphic-ws "^4.0.1" - json-stringify-safe "^5.0.1" - uuid "^8.3.2" - ws "^7.4.5" - js-cookie@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" @@ -6412,7 +6406,6 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== -json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -7126,7 +7119,6 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3, ms@^2.0.0, ms@^2.1.1: ms@2.1.3, ms@^2.0.0, ms@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" @@ -8256,11 +8248,6 @@ regenerator-runtime@^0.14.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== -regenerator-runtime@^0.14.0: - version "0.14.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" - integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== - regexp.prototype.flags@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" @@ -8483,6 +8470,22 @@ rpc-websockets@^7.11.1: bufferutil "^4.0.1" utf-8-validate "^5.0.2" +rpc-websockets@^9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-9.0.2.tgz#4c1568d00b8100f997379a363478f41f8f4b242c" + integrity sha512-YzggvfItxMY3Lwuax5rC18inhbjJv9Py7JXRHxTIi94JOLrqBsSsUUc5bbl5W6c11tXhdfpDPK0KzBhoGe8jjw== + dependencies: + "@swc/helpers" "^0.5.11" + "@types/uuid" "^8.3.4" + "@types/ws" "^8.2.2" + buffer "^6.0.3" + eventemitter3 "^5.0.1" + uuid "^8.3.2" + ws "^8.5.0" + optionalDependencies: + bufferutil "^4.0.1" + utf-8-validate "^5.0.2" + run-async@^2.2.0, run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -9324,11 +9327,6 @@ text-encoding-utf-8@^1.0.2: resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13" integrity sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg== -text-encoding-utf-8@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13" - integrity sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg== - text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -9870,6 +9868,20 @@ viem@^2.11.1: isows "1.0.4" ws "8.17.1" +viem@^2.16.2: + version "2.17.3" + resolved "https://registry.yarnpkg.com/viem/-/viem-2.17.3.tgz#f15616049d8154b83e499eb5446e6d7fe6312626" + integrity sha512-FY/1uBQWfko4Esy8mU1RamvL64TLy91LZwFyQJ20E6AI3vTTEOctWfSn0pkMKa3okq4Gxs5dJE7q1hmWOQ7xcw== + dependencies: + "@adraffy/ens-normalize" "1.10.0" + "@noble/curves" "1.4.0" + "@noble/hashes" "1.4.0" + "@scure/bip32" "1.4.0" + "@scure/bip39" "1.3.0" + abitype "1.0.5" + isows "1.0.4" + ws "8.17.1" + wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" @@ -10714,7 +10726,6 @@ ws@^3.0.0: safe-buffer "~5.1.0" ultron "~1.1.0" -ws@^7.4.5, ws@^7.4.6: ws@^7.4.5, ws@^7.4.6: version "7.5.9" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" @@ -10725,11 +10736,6 @@ ws@^8.5.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.0.tgz#d145d18eca2ed25aaf791a183903f7be5e295fea" integrity sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow== -ws@^8.5.0: - version "8.17.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.0.tgz#d145d18eca2ed25aaf791a183903f7be5e295fea" - integrity sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow== - xhr-request-promise@^0.1.2: version "0.1.3" resolved "https://registry.yarnpkg.com/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz#2d5f4b16d8c6c893be97f1a62b0ed4cf3ca5f96c"