Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
ApproveAndSwap
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 100000000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.23; import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol"; import {SafeERC20} from "openzeppelin/token/ERC20/utils/SafeERC20.sol"; import {ISwapRouter02, IV3SwapRouter} from "src/vendor/uniswap-swap-router-contracts/ISwapRouter02.sol"; import {QuarkScript} from "quark-core/src/QuarkScript.sol"; import {IComet} from "./interfaces/IComet.sol"; import {ICometRewards} from "./interfaces/ICometRewards.sol"; import {DeFiScriptErrors} from "./lib/DeFiScriptErrors.sol"; contract CometSupplyActions { using SafeERC20 for IERC20; /** * @notice Supply an asset to Comet * @param comet The Comet address * @param asset The asset address * @param amount The amount to supply */ function supply(address comet, address asset, uint256 amount) external { IERC20(asset).forceApprove(comet, amount); IComet(comet).supply(asset, amount); } /** * @notice Supply an asset to Comet to a specific address * @param comet The Comet address * @param to The recipient address * @param asset The asset address * @param amount The amount to supply */ function supplyTo(address comet, address to, address asset, uint256 amount) external { IERC20(asset).forceApprove(comet, amount); IComet(comet).supplyTo(to, asset, amount); } /** * @notice Supply an asset to Comet from one address to another address * @param comet The Comet address * @param from The from address * @param to The to address * @param asset The asset address * @param amount The amount to supply */ function supplyFrom(address comet, address from, address to, address asset, uint256 amount) external { IComet(comet).supplyFrom(from, to, asset, amount); } /** * @notice Supply multiple assets to Comet * @param comet The Comet address * @param assets The assets to supply * @param amounts The amounts of each asset to supply */ function supplyMultipleAssets(address comet, address[] calldata assets, uint256[] calldata amounts) external { if (assets.length != amounts.length) { revert DeFiScriptErrors.InvalidInput(); } for (uint256 i = 0; i < assets.length;) { IERC20(assets[i]).forceApprove(comet, amounts[i]); IComet(comet).supply(assets[i], amounts[i]); unchecked { ++i; } } } } contract CometWithdrawActions { using SafeERC20 for IERC20; /** * @notice Withdraw an asset from Comet * @param comet The Comet address * @param asset The asset address * @param amount The amount to withdraw */ function withdraw(address comet, address asset, uint256 amount) external { IComet(comet).withdraw(asset, amount); } /** * @notice Withdraw an asset from Comet to a specific address * @param comet The Comet address * @param to The recipient address * @param asset The asset address * @param amount The amount to withdraw */ function withdrawTo(address comet, address to, address asset, uint256 amount) external { IComet(comet).withdrawTo(to, asset, amount); } /** * @notice Withdraw an asset from Comet from one address to another address * @param comet The Comet address * @param from The from address * @param to The to address * @param asset The asset address * @param amount The amount to withdraw */ function withdrawFrom(address comet, address from, address to, address asset, uint256 amount) external { IComet(comet).withdrawFrom(from, to, asset, amount); } /** * @notice Withdraw multiple assets from Comet * @param comet The Comet address * @param assets The assets to withdraw * @param amounts The amounts of each asset to withdraw */ function withdrawMultipleAssets(address comet, address[] calldata assets, uint256[] calldata amounts) external { if (assets.length != amounts.length) { revert DeFiScriptErrors.InvalidInput(); } for (uint256 i = 0; i < assets.length;) { IComet(comet).withdraw(assets[i], amounts[i]); unchecked { ++i; } } } } contract UniswapSwapActions { using SafeERC20 for IERC20; struct SwapParamsExactIn { address uniswapRouter; address recipient; address tokenFrom; uint256 amount; // Minimum amount of target token to receive (revert if return amount is less than this) uint256 amountOutMinimum; // Path of the swap bytes path; } struct SwapParamsExactOut { address uniswapRouter; address recipient; address tokenFrom; uint256 amount; // Maximum amount of input token to spend (revert if input amount is greater than this) uint256 amountInMaximum; // Path of the swap bytes path; } /** * @notice Swap token on Uniswap with Exact Input (i.e. Set input amount and swap for target token) * @param params SwapParamsExactIn struct */ function swapAssetExactIn(SwapParamsExactIn calldata params) external { IERC20(params.tokenFrom).forceApprove(params.uniswapRouter, params.amount); ISwapRouter02(params.uniswapRouter).exactInput( IV3SwapRouter.ExactInputParams({ path: params.path, recipient: params.recipient, amountIn: params.amount, amountOutMinimum: params.amountOutMinimum }) ); } /** * @notice Swap token on Uniswap with Exact Output (i.e. Set output amount and swap with required amount of input token) * @param params SwapParamsExactOut struct */ function swapAssetExactOut(SwapParamsExactOut calldata params) external { IERC20(params.tokenFrom).forceApprove(params.uniswapRouter, params.amountInMaximum); uint256 amountIn = ISwapRouter02(params.uniswapRouter).exactOutput( IV3SwapRouter.ExactOutputParams({ path: params.path, recipient: params.recipient, amountOut: params.amount, amountInMaximum: params.amountInMaximum }) ); // Reset approved leftover input token back to 0, if there is any leftover approved amount if (amountIn < params.amountInMaximum) { IERC20(params.tokenFrom).forceApprove(params.uniswapRouter, 0); } } } contract TransferActions is QuarkScript { using SafeERC20 for IERC20; /** * @notice Transfer ERC20 token * @param token The token address * @param recipient The recipient address * @param amount The amount to transfer */ function transferERC20Token(address token, address recipient, uint256 amount) external onlyWallet { IERC20(token).safeTransfer(recipient, amount); } /** * @notice Transfer native token (i.e. ETH) * @param recipient The recipient address * @param amount The amount to transfer */ function transferNativeToken(address recipient, uint256 amount) external onlyWallet { (bool success, bytes memory data) = payable(recipient).call{value: amount}(""); if (!success) { revert DeFiScriptErrors.TransferFailed(data); } } } contract CometClaimRewards { /** * @notice Claim rewards * @param cometRewards The CometRewards addresses * @param comets The Comet addresses * @param recipient The recipient address, that will receive the COMP rewards */ function claim(address[] calldata cometRewards, address[] calldata comets, address recipient) external { if (cometRewards.length != comets.length) { revert DeFiScriptErrors.InvalidInput(); } for (uint256 i = 0; i < cometRewards.length;) { ICometRewards(cometRewards[i]).claim(comets[i], recipient, true); unchecked { ++i; } } } } contract CometSupplyMultipleAssetsAndBorrow { // To handle non-standard ERC20 tokens (i.e. USDT) using SafeERC20 for IERC20; function run( address comet, address[] calldata assets, uint256[] calldata amounts, address baseAsset, uint256 borrow ) external { if (assets.length != amounts.length) { revert DeFiScriptErrors.InvalidInput(); } for (uint256 i = 0; i < assets.length;) { IERC20(assets[i]).forceApprove(comet, amounts[i]); IComet(comet).supply(assets[i], amounts[i]); unchecked { ++i; } } IComet(comet).withdraw(baseAsset, borrow); } } contract CometRepayAndWithdrawMultipleAssets { // To handle non-standard ERC20 tokens (i.e. USDT) using SafeERC20 for IERC20; function run(address comet, address[] calldata assets, uint256[] calldata amounts, address baseAsset, uint256 repay) external { if (assets.length != amounts.length) { revert DeFiScriptErrors.InvalidInput(); } IERC20(baseAsset).forceApprove(comet, repay); IComet(comet).supply(baseAsset, repay); for (uint256 i = 0; i < assets.length;) { IComet(comet).withdraw(assets[i], amounts[i]); unchecked { ++i; } } } } contract ApproveAndSwap { // To handle non-standard ERC20 tokens (i.e. USDT) using SafeERC20 for IERC20; /** * Approve a specified contract for an amount of token and execute the data against it * @param to The contract address to approve execute on * @param sellToken The token address to approve * @param sellAmount The amount to approve * @param buyToken The token that is being bought * @param buyAmount The amount of the buy token to receive after the swap * @param data The data to execute */ function run( address to, address sellToken, uint256 sellAmount, address buyToken, uint256 buyAmount, bytes calldata data ) external { IERC20(sellToken).forceApprove(to, sellAmount); uint256 buyTokenBalanceBefore = IERC20(buyToken).balanceOf(address(this)); (bool success, bytes memory returnData) = to.call(data); if (!success) { revert DeFiScriptErrors.ApproveAndSwapFailed(returnData); } uint256 buyTokenBalanceAfter = IERC20(buyToken).balanceOf(address(this)); uint256 actualBuyAmount = buyTokenBalanceAfter - buyTokenBalanceBefore; if (actualBuyAmount < buyAmount) { revert DeFiScriptErrors.TooMuchSlippage(buyAmount, actualBuyAmount); } // Approvals to external contracts should always be reset to 0 IERC20(sellToken).forceApprove(to, 0); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 amount) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value)); } /** * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value)); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0)); _callOptionalReturn(token, approvalCall); } } /** * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`. * Revert on invalid signature. */ function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token)); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.7.5; pragma abicoder v2; import "@uniswap/v3-periphery/contracts/interfaces/ISelfPermit.sol"; import "@uniswap/swap-router-contracts/contracts/interfaces/IV2SwapRouter.sol"; import "@uniswap/swap-router-contracts/contracts/interfaces/IV3SwapRouter.sol"; import "@uniswap/swap-router-contracts/contracts/interfaces/IMulticallExtended.sol"; import "./IApproveAndCall.sol"; /// @title Router token swapping functionality interface ISwapRouter02 is IV2SwapRouter, IV3SwapRouter, IApproveAndCall, IMulticallExtended, ISelfPermit { }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.23; import {QuarkWallet, IHasSignerExecutor} from "quark-core/src/QuarkWallet.sol"; /** * @title Quark Script * @notice A contract that exposes helper functions for Quark scripts to inherit from * @author Compound Labs, Inc. */ abstract contract QuarkScript { error ReentrantCall(); /// @notice Storage location for the re-entrancy guard bytes32 internal constant REENTRANCY_FLAG_SLOT = bytes32(uint256(keccak256("quark.scripts.reentrancy.guard.v1")) - 1); /// @notice A safer, but gassier reentrancy guard that writes the flag to the QuarkStateManager modifier nonReentrant() { if (read(REENTRANCY_FLAG_SLOT) == bytes32(uint256(1))) { revert ReentrantCall(); } write(REENTRANCY_FLAG_SLOT, bytes32(uint256(1))); _; write(REENTRANCY_FLAG_SLOT, bytes32(uint256(0))); } /** * @notice A cheaper, but weaker reentrancy guard that does not prevent recursive reentrancy (e.g. script calling itself) * @dev Use with caution; this guard should only be used if the function being guarded cannot recursively call itself * There are currently two ways to do this from a script: * 1. The script uses `delegatecall` and the target can be itself (technically the wallet). The script * has to also enable callbacks for this reentrancy to succeed. * 2. The script defines circular codepaths that can be used to reenter the function using internal * functions. * @dev A side-effect of using this guard is that the guarded function can no longer be called as part of the Quark wallet * callback flow. This is because the fallback in Quark wallet makes a `delegatecall` instead of a `callcode`. The * guarded function would still be able to be called if a calling contract calls into the Quark wallet fallback using * a `delegatecall`, but most calling contracts are likely to make a `call` into the Quark wallet fallback instead. */ modifier onlyWallet() { if (msg.sender != address(this)) { revert ReentrantCall(); } _; } function signer() internal view returns (address) { return IHasSignerExecutor(address(this)).signer(); } function executor() internal view returns (address) { return IHasSignerExecutor(address(this)).executor(); } function allowCallback() internal { QuarkWallet self = QuarkWallet(payable(address(this))); self.stateManager().write(self.CALLBACK_KEY(), bytes32(uint256(uint160(self.stateManager().getActiveScript())))); } function clearCallback() internal { QuarkWallet self = QuarkWallet(payable(address(this))); self.stateManager().write(self.CALLBACK_KEY(), bytes32(0)); } function allowReplay() internal { return QuarkWallet(payable(address(this))).stateManager().clearNonce(); } function readU256(string memory key) internal view returns (uint256) { return uint256(read(key)); } function read(string memory key) internal view returns (bytes32) { return read(keccak256(bytes(key))); } function read(bytes32 key) internal view returns (bytes32) { return QuarkWallet(payable(address(this))).stateManager().read(key); } function writeU256(string memory key, uint256 value) internal { return write(key, bytes32(value)); } function write(string memory key, bytes32 value) internal { return write(keccak256(bytes(key)), value); } function write(bytes32 key, bytes32 value) internal { return QuarkWallet(payable(address(this))).stateManager().write(key, value); } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.23; interface IComet { function getAssetInfo(uint8 i) external view returns (AssetInfo memory); function baseToken() external view returns (address); function supply(address asset, uint256 amount) external; function supplyTo(address dst, address asset, uint256 amount) external; function supplyFrom(address from, address dst, address asset, uint256 amount) external; function withdraw(address asset, uint256 amount) external; function withdrawTo(address to, address asset, uint256 amount) external; function withdrawFrom(address src, address to, address asset, uint256 amount) external; function balanceOf(address owner) external view returns (uint256); function getPrice(address priceFeed) external view returns (uint256); function baseTokenPriceFeed() external view returns (address); function borrowBalanceOf(address account) external view returns (uint256); function collateralBalanceOf(address account, address asset) external view returns (uint128); function getAssetInfoByAddress(address asset) external view returns (AssetInfo memory); function isLiquidatable(address account) external view returns (bool); function baseScale() external view returns (uint256); function numAssets() external view returns (uint8); function allow(address manager, bool isAllowed_) external; } struct AssetInfo { uint8 offset; address asset; address priceFeed; uint64 scale; uint64 borrowCollateralFactor; uint64 liquidateCollateralFactor; uint64 liquidationFactor; uint128 supplyCap; }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.23; interface ICometRewards { function claim(address comet, address src, bool shouldAccrue) external; }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.23; /** * @title Errors library for DeFi scripts * @notice Defines the custom errors that are returned by different DeFi scripts * @author Compound Labs, Inc. */ library DeFiScriptErrors { error InvalidInput(); error TransferFailed(bytes data); error ApproveAndSwapFailed(bytes data); error TooMuchSlippage(uint256 expectedBuyAmount, uint256 actualBuyAmount); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.7.5; /// @title Self Permit /// @notice Functionality to call permit on any EIP-2612-compliant token for use in the route interface ISelfPermit { /// @notice Permits this contract to spend a given token from `msg.sender` /// @dev The `owner` is always msg.sender and the `spender` is always address(this). /// @param token The address of the token spent /// @param value The amount that can be spent of token /// @param deadline A timestamp, the current blocktime must be less than or equal to this timestamp /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s` /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s` /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v` function selfPermit( address token, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external payable; /// @notice Permits this contract to spend a given token from `msg.sender` /// @dev The `owner` is always msg.sender and the `spender` is always address(this). /// Can be used instead of #selfPermit to prevent calls from failing due to a frontrun of a call to #selfPermit /// @param token The address of the token spent /// @param value The amount that can be spent of token /// @param deadline A timestamp, the current blocktime must be less than or equal to this timestamp /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s` /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s` /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v` function selfPermitIfNecessary( address token, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external payable; /// @notice Permits this contract to spend the sender's tokens for permit signatures that have the `allowed` parameter /// @dev The `owner` is always msg.sender and the `spender` is always address(this) /// @param token The address of the token spent /// @param nonce The current nonce of the owner /// @param expiry The timestamp at which the permit is no longer valid /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s` /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s` /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v` function selfPermitAllowed( address token, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s ) external payable; /// @notice Permits this contract to spend the sender's tokens for permit signatures that have the `allowed` parameter /// @dev The `owner` is always msg.sender and the `spender` is always address(this) /// Can be used instead of #selfPermitAllowed to prevent calls from failing due to a frontrun of a call to #selfPermitAllowed. /// @param token The address of the token spent /// @param nonce The current nonce of the owner /// @param expiry The timestamp at which the permit is no longer valid /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s` /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s` /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v` function selfPermitAllowedIfNecessary( address token, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s ) external payable; }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.7.5; pragma abicoder v2; /// @title Router token swapping functionality /// @notice Functions for swapping tokens via Uniswap V2 interface IV2SwapRouter { /// @notice Swaps `amountIn` of one token for as much as possible of another token /// @dev Setting `amountIn` to 0 will cause the contract to look up its own balance, /// and swap the entire amount, enabling contracts to send tokens before calling this function. /// @param amountIn The amount of token to swap /// @param amountOutMin The minimum amount of output that must be received /// @param path The ordered list of tokens to swap through /// @param to The recipient address /// @return amountOut The amount of the received token function swapExactTokensForTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to ) external payable returns (uint256 amountOut); /// @notice Swaps as little as possible of one token for an exact amount of another token /// @param amountOut The amount of token to swap for /// @param amountInMax The maximum amount of input that the caller will pay /// @param path The ordered list of tokens to swap through /// @param to The recipient address /// @return amountIn The amount of token to pay function swapTokensForExactTokens( uint256 amountOut, uint256 amountInMax, address[] calldata path, address to ) external payable returns (uint256 amountIn); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.7.5; pragma abicoder v2; import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol'; /// @title Router token swapping functionality /// @notice Functions for swapping tokens via Uniswap V3 interface IV3SwapRouter is IUniswapV3SwapCallback { struct ExactInputSingleParams { address tokenIn; address tokenOut; uint24 fee; address recipient; uint256 amountIn; uint256 amountOutMinimum; uint160 sqrtPriceLimitX96; } /// @notice Swaps `amountIn` of one token for as much as possible of another token /// @dev Setting `amountIn` to 0 will cause the contract to look up its own balance, /// and swap the entire amount, enabling contracts to send tokens before calling this function. /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata /// @return amountOut The amount of the received token function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut); struct ExactInputParams { bytes path; address recipient; uint256 amountIn; uint256 amountOutMinimum; } /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path /// @dev Setting `amountIn` to 0 will cause the contract to look up its own balance, /// and swap the entire amount, enabling contracts to send tokens before calling this function. /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata /// @return amountOut The amount of the received token function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut); struct ExactOutputSingleParams { address tokenIn; address tokenOut; uint24 fee; address recipient; uint256 amountOut; uint256 amountInMaximum; uint160 sqrtPriceLimitX96; } /// @notice Swaps as little as possible of one token for `amountOut` of another token /// that may remain in the router after the swap. /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata /// @return amountIn The amount of the input token function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn); struct ExactOutputParams { bytes path; address recipient; uint256 amountOut; uint256 amountInMaximum; } /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed) /// that may remain in the router after the swap. /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata /// @return amountIn The amount of the input token function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.7.5; pragma abicoder v2; import '@uniswap/v3-periphery/contracts/interfaces/IMulticall.sol'; /// @title MulticallExtended interface /// @notice Enables calling multiple methods in a single call to the contract with optional validation interface IMulticallExtended is IMulticall { /// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed /// @dev The `msg.value` should not be trusted for any method callable from multicall. /// @param deadline The time by which this function must be called before failing /// @param data The encoded function data for each of the calls to make to this contract /// @return results The results from each of the calls passed in via data function multicall(uint256 deadline, bytes[] calldata data) external payable returns (bytes[] memory results); /// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed /// @dev The `msg.value` should not be trusted for any method callable from multicall. /// @param previousBlockhash The expected parent blockHash /// @param data The encoded function data for each of the calls to make to this contract /// @return results The results from each of the calls passed in via data function multicall(bytes32 previousBlockhash, bytes[] calldata data) external payable returns (bytes[] memory results); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.7.6; pragma abicoder v2; interface IApproveAndCall { enum ApprovalType {NOT_REQUIRED, MAX, MAX_MINUS_ONE, ZERO_THEN_MAX, ZERO_THEN_MAX_MINUS_ONE} /// @dev Lens to be called off-chain to determine which (if any) of the relevant approval functions should be called /// @param token The token to approve /// @param amount The amount to approve /// @return The required approval type function getApprovalType(address token, uint256 amount) external returns (ApprovalType); /// @notice Approves a token for the maximum possible amount /// @param token The token to approve function approveMax(address token) external payable; /// @notice Approves a token for the maximum possible amount minus one /// @param token The token to approve function approveMaxMinusOne(address token) external payable; /// @notice Approves a token for zero, then the maximum possible amount /// @param token The token to approve function approveZeroThenMax(address token) external payable; /// @notice Approves a token for zero, then the maximum possible amount minus one /// @param token The token to approve function approveZeroThenMaxMinusOne(address token) external payable; /// @notice Calls the position manager with arbitrary calldata /// @param data Calldata to pass along to the position manager /// @return result The result from the call function callPositionManager(bytes memory data) external payable returns (bytes memory result); struct MintParams { address token0; address token1; uint24 fee; int24 tickLower; int24 tickUpper; uint256 amount0Min; uint256 amount1Min; address recipient; } /// @notice Calls the position manager's mint function /// @param params Calldata to pass along to the position manager /// @return result The result from the call function mint(MintParams calldata params) external payable returns (bytes memory result); struct IncreaseLiquidityParams { address token0; address token1; uint256 tokenId; uint256 amount0Min; uint256 amount1Min; } /// @notice Calls the position manager's increaseLiquidity function /// @param params Calldata to pass along to the position manager /// @return result The result from the call function increaseLiquidity(IncreaseLiquidityParams calldata params) external payable returns (bytes memory result); }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.23; import {ECDSA} from "openzeppelin/utils/cryptography/ECDSA.sol"; import {IERC1271} from "openzeppelin/interfaces/IERC1271.sol"; import {CodeJar} from "codejar/src/CodeJar.sol"; import {QuarkStateManager} from "quark-core/src/QuarkStateManager.sol"; import {IHasSignerExecutor} from "quark-core/src/interfaces/IHasSignerExecutor.sol"; /** * @title Quark Wallet Metadata * @notice A library of metadata specific to this implementation of the Quark Wallet * @author Compound Labs, Inc. */ library QuarkWalletMetadata { /// @notice QuarkWallet contract name string internal constant NAME = "Quark Wallet"; /// @notice QuarkWallet contract major version string internal constant VERSION = "1"; /// @notice The EIP-712 typehash for authorizing an operation for this version of QuarkWallet bytes32 internal constant QUARK_OPERATION_TYPEHASH = keccak256( "QuarkOperation(uint96 nonce,address scriptAddress,bytes[] scriptSources,bytes scriptCalldata,uint256 expiry)" ); /// @notice The EIP-712 typehash for authorizing a MultiQuarkOperation for this version of QuarkWallet bytes32 internal constant MULTI_QUARK_OPERATION_TYPEHASH = keccak256("MultiQuarkOperation(bytes32[] opDigests)"); /// @notice The EIP-712 typehash for authorizing an EIP-1271 signature for this version of QuarkWallet bytes32 internal constant QUARK_MSG_TYPEHASH = keccak256("QuarkMessage(bytes message)"); /// @notice The EIP-712 domain typehash for this version of QuarkWallet bytes32 internal constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); /// @notice The EIP-712 domain typehash used for MultiQuarkOperations for this version of QuarkWallet bytes32 internal constant MULTI_QUARK_OPERATION_DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,string version)"); } /** * @title Quark Wallet base class * @notice A smart wallet that can run transaction scripts * @dev An implementor needs only to provide a public signer and executor: these could be constants, immutables, or address getters of any kind * @author Compound Labs, Inc. */ contract QuarkWallet is IERC1271 { error BadSignatory(); error EmptyCode(); error InvalidEIP1271Signature(); error InvalidMultiQuarkOperation(); error InvalidSignature(); error NoActiveCallback(); error SignatureExpired(); error Unauthorized(); /// @notice Enum specifying the method of execution for running a Quark script enum ExecutionType { Signature, Direct } /// @notice Event emitted when a Quark script is executed by this Quark wallet event ExecuteQuarkScript( address indexed executor, address indexed scriptAddress, uint96 indexed nonce, ExecutionType executionType ); /// @notice Address of CodeJar contract used to deploy transaction script source code CodeJar public immutable codeJar; /// @notice Address of QuarkStateManager contract that manages nonces and nonce-namespaced transaction script storage QuarkStateManager public immutable stateManager; /// @notice Name of contract string public constant NAME = QuarkWalletMetadata.NAME; /// @notice The major version of this contract string public constant VERSION = QuarkWalletMetadata.VERSION; /// @dev The EIP-712 domain typehash for this wallet bytes32 internal constant DOMAIN_TYPEHASH = QuarkWalletMetadata.DOMAIN_TYPEHASH; /// @dev The EIP-712 domain typehash used for MultiQuarkOperations for this wallet bytes32 internal constant MULTI_QUARK_OPERATION_DOMAIN_TYPEHASH = QuarkWalletMetadata.MULTI_QUARK_OPERATION_DOMAIN_TYPEHASH; /// @dev The EIP-712 typehash for authorizing an operation for this wallet bytes32 internal constant QUARK_OPERATION_TYPEHASH = QuarkWalletMetadata.QUARK_OPERATION_TYPEHASH; /// @dev The EIP-712 typehash for authorizing an operation that is part of a MultiQuarkOperation for this wallet bytes32 internal constant MULTI_QUARK_OPERATION_TYPEHASH = QuarkWalletMetadata.MULTI_QUARK_OPERATION_TYPEHASH; /// @dev The EIP-712 typehash for authorizing an EIP-1271 signature for this wallet bytes32 internal constant QUARK_MSG_TYPEHASH = QuarkWalletMetadata.QUARK_MSG_TYPEHASH; /// @dev The EIP-712 domain separator for a MultiQuarkOperation /// @dev Note: `chainId` and `verifyingContract` are left out so a single MultiQuarkOperation can be used to /// execute operations on different chains and wallets. bytes32 internal constant MULTI_QUARK_OPERATION_DOMAIN_SEPARATOR = keccak256( abi.encode( QuarkWalletMetadata.MULTI_QUARK_OPERATION_DOMAIN_TYPEHASH, keccak256(bytes(QuarkWalletMetadata.NAME)), keccak256(bytes(QuarkWalletMetadata.VERSION)) ) ); /// @notice Well-known stateManager key for the currently executing script's callback address (if any) bytes32 public constant CALLBACK_KEY = keccak256("callback.v1.quark"); /// @notice The magic value to return for valid ERC1271 signature bytes4 internal constant EIP_1271_MAGIC_VALUE = 0x1626ba7e; /// @notice The structure of a signed operation to execute in the context of this wallet struct QuarkOperation { /// @notice Nonce identifier for the operation uint96 nonce; /// @notice The address of the transaction script to run address scriptAddress; /// @notice Creation codes Quark must ensure are deployed before executing this operation bytes[] scriptSources; /// @notice Encoded function selector + arguments to invoke on the script contract bytes scriptCalldata; /// @notice Expiration time for the signature corresponding to this operation uint256 expiry; } /** * @notice Construct a new QuarkWalletImplementation * @param codeJar_ The CodeJar contract used to deploy scripts * @param stateManager_ The QuarkStateManager contract used to write/read nonces and storage for this wallet */ constructor(CodeJar codeJar_, QuarkStateManager stateManager_) { codeJar = codeJar_; stateManager = stateManager_; } /** * @notice Execute a QuarkOperation via signature * @dev Can only be called with signatures from the wallet's signer * @param op A QuarkOperation struct * @param v EIP-712 signature v value * @param r EIP-712 signature r value * @param s EIP-712 signature s value * @return Return value from the executed operation */ function executeQuarkOperation(QuarkOperation calldata op, uint8 v, bytes32 r, bytes32 s) external returns (bytes memory) { bytes32 opDigest = getDigestForQuarkOperation(op); return verifySigAndExecuteQuarkOperation(op, opDigest, v, r, s); } /** * @notice Execute a QuarkOperation that is part of a MultiQuarkOperation via signature * @dev Can only be called with signatures from the wallet's signer * @param op A QuarkOperation struct * @param opDigests A list of EIP-712 digests for the operations in a MultiQuarkOperation * @param v EIP-712 signature v value * @param r EIP-712 signature r value * @param s EIP-712 signature s value * @return Return value from the executed operation */ function executeMultiQuarkOperation( QuarkOperation calldata op, bytes32[] memory opDigests, uint8 v, bytes32 r, bytes32 s ) public returns (bytes memory) { bytes32 opDigest = getDigestForQuarkOperation(op); bool isValidOp = false; for (uint256 i = 0; i < opDigests.length; ++i) { if (opDigest == opDigests[i]) { isValidOp = true; break; } } if (!isValidOp) { revert InvalidMultiQuarkOperation(); } bytes32 multiOpDigest = getDigestForMultiQuarkOperation(opDigests); return verifySigAndExecuteQuarkOperation(op, multiOpDigest, v, r, s); } /** * @notice Verify a signature and execute a QuarkOperation * @param op A QuarkOperation struct * @param digest A EIP-712 digest for either a QuarkOperation or MultiQuarkOperation to verify the signature against * @param v EIP-712 signature v value * @param r EIP-712 signature r value * @param s EIP-712 signature s value * @return Return value from the executed operation */ function verifySigAndExecuteQuarkOperation( QuarkOperation calldata op, bytes32 digest, uint8 v, bytes32 r, bytes32 s ) internal returns (bytes memory) { if (block.timestamp >= op.expiry) { revert SignatureExpired(); } // if the signature check does not revert, the signature is valid checkValidSignatureInternal(IHasSignerExecutor(address(this)).signer(), digest, v, r, s); // guarantee every script in scriptSources is deployed for (uint256 i = 0; i < op.scriptSources.length; ++i) { codeJar.saveCode(op.scriptSources[i]); } emit ExecuteQuarkScript(msg.sender, op.scriptAddress, op.nonce, ExecutionType.Signature); return stateManager.setActiveNonceAndCallback(op.nonce, op.scriptAddress, op.scriptCalldata); } /** * @notice Execute a transaction script directly * @dev Can only be called by the wallet's executor * @param nonce Nonce for the operation; must be unused * @param scriptAddress Address for the script to execute * @param scriptCalldata Encoded call to invoke on the script * @param scriptSources Creation codes Quark must ensure are deployed before executing the script * @return Return value from the executed operation */ function executeScript( uint96 nonce, address scriptAddress, bytes calldata scriptCalldata, bytes[] calldata scriptSources ) external returns (bytes memory) { // only allow the executor for the wallet to use unsigned execution if (msg.sender != IHasSignerExecutor(address(this)).executor()) { revert Unauthorized(); } // guarantee every script in scriptSources is deployed for (uint256 i = 0; i < scriptSources.length; ++i) { codeJar.saveCode(scriptSources[i]); } emit ExecuteQuarkScript(msg.sender, scriptAddress, nonce, ExecutionType.Direct); return stateManager.setActiveNonceAndCallback(nonce, scriptAddress, scriptCalldata); } /** * @dev Returns the domain separator for this Quark wallet * @return Domain separator */ function getDomainSeparator() internal view returns (bytes32) { return keccak256( abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(NAME)), keccak256(bytes(VERSION)), block.chainid, address(this)) ); } /** * @dev Returns the EIP-712 digest for a QuarkOperation * @param op A QuarkOperation struct * @return EIP-712 digest */ function getDigestForQuarkOperation(QuarkOperation calldata op) public view returns (bytes32) { bytes memory encodedScriptSources; for (uint256 i = 0; i < op.scriptSources.length; ++i) { encodedScriptSources = abi.encodePacked(encodedScriptSources, keccak256(op.scriptSources[i])); } bytes32 structHash = keccak256( abi.encode( QUARK_OPERATION_TYPEHASH, op.nonce, op.scriptAddress, keccak256(encodedScriptSources), keccak256(op.scriptCalldata), op.expiry ) ); return keccak256(abi.encodePacked("\x19\x01", getDomainSeparator(), structHash)); } /** * @dev Returns the EIP-712 digest for a MultiQuarkOperation * @param opDigests A list of EIP-712 digests for the operations in a MultiQuarkOperation * @return EIP-712 digest */ function getDigestForMultiQuarkOperation(bytes32[] memory opDigests) public pure returns (bytes32) { bytes memory encodedOpDigests = abi.encodePacked(opDigests); bytes32 structHash = keccak256(abi.encode(MULTI_QUARK_OPERATION_TYPEHASH, keccak256(encodedOpDigests))); return keccak256(abi.encodePacked("\x19\x01", MULTI_QUARK_OPERATION_DOMAIN_SEPARATOR, structHash)); } /** * @dev Returns the EIP-712 digest of a QuarkMessage that can be signed by `signer` * @param message Message that should be hashed * @return Message hash */ function getDigestForQuarkMessage(bytes memory message) public view returns (bytes32) { bytes32 quarkMessageHash = keccak256(abi.encode(QUARK_MSG_TYPEHASH, keccak256(message))); return keccak256(abi.encodePacked("\x19\x01", getDomainSeparator(), quarkMessageHash)); } /** * @notice Checks whether an EIP-1271 signature is valid * @dev If the QuarkWallet is owned by an EOA, isValidSignature confirms * that the signature comes from the signer; if the QuarkWallet is owned by * a smart contract, isValidSignature relays the `isValidSignature` to the * smart contract * @param hash Hash of the signed data * @param signature Signature byte array associated with data * @return The ERC-1271 "magic value" that indicates the signature is valid */ function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4) { /* * Code taken directly from OpenZeppelin ECDSA.tryRecover; see: * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/HEAD/contracts/utils/cryptography/ECDSA.sol#L64-L68 * * This is effectively an optimized variant of the Reference Implementation; see: * https://eips.ethereum.org/EIPS/eip-1271#reference-implementation */ if (signature.length != 65) { revert InvalidSignature(); } bytes32 r; bytes32 s; uint8 v; assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } // Note: The following logic further encodes the provided `hash` with the wallet's domain // to prevent signature replayability for Quark wallets owned by the same `signer` bytes32 digest = getDigestForQuarkMessage(abi.encode(hash)); // If the signature check does not revert, the signature is valid checkValidSignatureInternal(IHasSignerExecutor(address(this)).signer(), digest, v, r, s); return EIP_1271_MAGIC_VALUE; } /** * @dev If the QuarkWallet is owned by an EOA, isValidSignature confirms * that the signature comes from the signer; if the QuarkWallet is owned by * a smart contract, isValidSignature relays the `isValidSignature` check * to the smart contract; if the smart contract that owns the wallet has no * code, the signature will be treated as an EIP-712 signature and revert */ function checkValidSignatureInternal(address signatory, bytes32 digest, uint8 v, bytes32 r, bytes32 s) internal view { if (signatory.code.length > 0) { bytes memory signature = abi.encodePacked(r, s, v); (bool success, bytes memory data) = signatory.staticcall(abi.encodeWithSelector(EIP_1271_MAGIC_VALUE, digest, signature)); if (!success) { revert InvalidEIP1271Signature(); } bytes4 returnValue = abi.decode(data, (bytes4)); if (returnValue != EIP_1271_MAGIC_VALUE) { revert InvalidEIP1271Signature(); } } else { (address recoveredSigner, ECDSA.RecoverError recoverError) = ECDSA.tryRecover(digest, v, r, s); if (recoverError != ECDSA.RecoverError.NoError) { revert InvalidSignature(); } if (recoveredSigner != signatory) { revert BadSignatory(); } } } /** * @notice Execute a QuarkOperation with a lock acquired on nonce-namespaced storage * @dev Can only be called by stateManager during setActiveNonceAndCallback() * @param scriptAddress Address of script to execute * @param scriptCalldata Encoded calldata for the call to execute on the scriptAddress * @return Result of executing the script, encoded as bytes */ function executeScriptWithNonceLock(address scriptAddress, bytes memory scriptCalldata) external returns (bytes memory) { require(msg.sender == address(stateManager)); if (scriptAddress.code.length == 0) { revert EmptyCode(); } bool success; uint256 returnSize; uint256 scriptCalldataLen = scriptCalldata.length; assembly { // Note: CALLCODE is used to set the QuarkWallet as the `msg.sender` success := callcode(gas(), scriptAddress, /* value */ 0, add(scriptCalldata, 0x20), scriptCalldataLen, 0x0, 0) returnSize := returndatasize() } bytes memory returnData = new bytes(returnSize); assembly { returndatacopy(add(returnData, 0x20), 0x00, returnSize) } if (!success) { assembly { revert(add(returnData, 0x20), returnSize) } } return returnData; } /** * @notice Fallback function specifically used for scripts that have enabled callbacks * @dev Reverts if callback is not enabled by the script */ fallback(bytes calldata data) external payable returns (bytes memory) { address callback = address(uint160(uint256(stateManager.read(CALLBACK_KEY)))); if (callback != address(0)) { (bool success, bytes memory result) = callback.delegatecall(data); if (!success) { assembly { let size := mload(result) revert(add(result, 0x20), size) } } return result; } else { revert NoActiveCallback(); } } /// @notice Fallback for receiving native token receive() external payable {} }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Callback for IUniswapV3PoolActions#swap /// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface interface IUniswapV3SwapCallback { /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap. /// @dev In the implementation you must pay the pool tokens owed for the swap. /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped. /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by /// the end of the swap. If positive, the callback must send that amount of token0 to the pool. /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by /// the end of the swap. If positive, the callback must send that amount of token1 to the pool. /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call function uniswapV3SwapCallback( int256 amount0Delta, int256 amount1Delta, bytes calldata data ) external; }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.7.5; pragma abicoder v2; /// @title Multicall interface /// @notice Enables calling multiple methods in a single call to the contract interface IMulticall { /// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed /// @dev The `msg.value` should not be trusted for any method callable from multicall. /// @param data The encoded function data for each of the calls to make to this contract /// @return results The results from each of the calls passed in via data function multicall(bytes[] calldata data) external payable returns (bytes[] memory results); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.0; import "../Strings.sol"; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS, InvalidSignatureV // Deprecated in v4.8 } function _throwError(RecoverError error) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert("ECDSA: invalid signature"); } else if (error == RecoverError.InvalidSignatureLength) { revert("ECDSA: invalid signature length"); } else if (error == RecoverError.InvalidSignatureS) { revert("ECDSA: invalid signature 's' value"); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature` or error string. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. /// @solidity memory-safe-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else { return (address(0), RecoverError.InvalidSignatureLength); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, signature); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); uint8 v = uint8((uint256(vs) >> 255) + 27); return tryRecover(hash, v, r, s); } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. * * _Available since v4.2._ */ function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, r, vs); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. * * _Available since v4.3._ */ function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature); } return (signer, RecoverError.NoError); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, v, r, s); _throwError(error); return recovered; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) { // 32 is the length in bytes of hash, // enforced by the type signature above /// @solidity memory-safe-assembly assembly { mstore(0x00, "\x19Ethereum Signed Message:\n32") mstore(0x1c, hash) message := keccak256(0x00, 0x3c) } } /** * @dev Returns an Ethereum Signed Message, created from `s`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s)); } /** * @dev Returns an Ethereum Signed Typed Data, created from a * `domainSeparator` and a `structHash`. This produces hash corresponding * to the one signed with the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] * JSON-RPC method as part of EIP-712. * * See {recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(ptr, "\x19\x01") mstore(add(ptr, 0x02), domainSeparator) mstore(add(ptr, 0x22), structHash) data := keccak256(ptr, 0x42) } } /** * @dev Returns an Ethereum Signed Data with intended validator, created from a * `validator` and `data` according to the version 0 of EIP-191. * * See {recover}. */ function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x00", validator, data)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC1271 standard signature validation method for * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271]. * * _Available since v4.1._ */ interface IERC1271 { /** * @dev Should return whether the signature provided is valid for the provided data * @param hash Hash of the data to be signed * @param signature Signature byte array associated with _data */ function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue); }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.23; /** * @title Code Jar * @notice Deploys contract code to deterministic addresses * @author Compound Labs, Inc. */ contract CodeJar { /** * @notice Deploys the code via Code Jar, no-op if it already exists * @dev This call is meant to be idemponent and fairly inexpensive on a second call * @param code The creation bytecode of the code to save * @return The address of the contract that matches the input code's contructor output */ function saveCode(bytes memory code) external returns (address) { address codeAddress = getCodeAddress(code); if (codeAddress.code.length > 0) { // Code is already deployed return codeAddress; } else { // The code has not been deployed here (or it was deployed and destructed). address script; assembly { script := create2(0, add(code, 0x20), mload(code), 0) } // Posit: these cannot fail and are purely defense-in-depth require(script == codeAddress); uint256 scriptSz; assembly { scriptSz := extcodesize(script) } // Disallow the empty code and self-destructing constructors // Note: Script can still self-destruct after being deployed until Dencun require(scriptSz > 0); return codeAddress; } } /** * @notice Checks if code was already deployed by CodeJar * @param code The creation bytecode of the code to check * @return True if code already exists in Code Jar */ function codeExists(bytes calldata code) external view returns (bool) { address codeAddress = getCodeAddress(code); return codeAddress.code.length > 0; } /** * @dev Returns the create2 address based on the creation code * @return The create2 address to deploy this code (via init code) */ function getCodeAddress(bytes memory code) public view returns (address) { return address( uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), uint256(0), keccak256(code))))) ); } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.23; import {IQuarkWallet} from "quark-core/src/interfaces/IQuarkWallet.sol"; /** * @title Quark State Manager * @notice Contract for managing nonces and storage for Quark wallets, guaranteeing storage isolation across wallets * and Quark operations * @author Compound Labs, Inc. */ contract QuarkStateManager { event ClearNonce(address indexed wallet, uint96 nonce); error NoActiveNonce(); error NoUnusedNonces(); error NonceAlreadySet(); error NonceScriptMismatch(); /// @notice Bit-packed structure of a nonce-script pair struct NonceScript { uint96 nonce; address scriptAddress; } /// @notice Bit-packed nonce values mapping(address wallet => mapping(uint256 bucket => uint256 bitset)) public nonces; /// @notice Per-wallet-nonce address for preventing replays with changed script address mapping(address wallet => mapping(uint96 nonce => address scriptAddress)) public nonceScriptAddress; /// @notice Per-wallet-nonce storage space that can be utilized while a nonce is active mapping(address wallet => mapping(uint96 nonce => mapping(bytes32 key => bytes32 value))) public walletStorage; /// @notice Currently active nonce-script pair for a wallet, if any, for which storage is accessible mapping(address wallet => NonceScript) internal activeNonceScript; /** * @notice Return whether a nonce has been exhausted; note that if a nonce is not set, that does not mean it has not been used before * @param wallet Address of the wallet owning the nonce * @param nonce Nonce to check * @return Whether the nonce has been exhausted */ function isNonceSet(address wallet, uint96 nonce) public view returns (bool) { (uint256 bucket, uint256 mask) = getBucket(nonce); return isNonceSetInternal(wallet, bucket, mask); } /// @dev Returns if a given nonce is set for a wallet, using the nonce's bucket and mask function isNonceSetInternal(address wallet, uint256 bucket, uint256 mask) internal view returns (bool) { return (nonces[wallet][bucket] & mask) != 0; } /** * @notice Returns the next valid unset nonce for a given wallet * @dev Any unset nonce is valid to use, but using this method * increases the likelihood that the nonce you use will be in a bucket that * has already been written to, which costs less gas * @param wallet Address of the wallet to find the next nonce for * @return The next unused nonce */ function nextNonce(address wallet) external view returns (uint96) { // Any bucket larger than `type(uint88).max` will result in unsafe undercast when converting to nonce for (uint256 bucket = 0; bucket <= type(uint88).max; ++bucket) { uint96 bucketValue = uint96(bucket << 8); uint256 bucketNonces = nonces[wallet][bucket]; // Move on to the next bucket if all bits in this bucket are already set if (bucketNonces == type(uint256).max) continue; for (uint256 maskOffset = 0; maskOffset < 256; ++maskOffset) { uint256 mask = 1 << maskOffset; if ((bucketNonces & mask) == 0) { uint96 nonce = uint96(bucketValue + maskOffset); // The next available nonce should not be reserved for a replayable transaction if (nonceScriptAddress[wallet][nonce] == address(0)) { return nonce; } } } } revert NoUnusedNonces(); } /** * @notice Return the script address associated with the currently active nonce; revert if none * @return Currently active script address */ function getActiveScript() external view returns (address) { address scriptAddress = activeNonceScript[msg.sender].scriptAddress; if (scriptAddress == address(0)) { revert NoActiveNonce(); } return scriptAddress; } /// @dev Locate a nonce at a (bucket, mask) bitset position in the nonces mapping function getBucket(uint96 nonce) internal pure returns (uint256, /* bucket */ uint256 /* mask */ ) { uint256 bucket = nonce >> 8; uint256 setMask = 1 << (nonce & 0xff); return (bucket, setMask); } /// @notice Clears (un-sets) the active nonce to allow its reuse; allows a script to be replayed function clearNonce() external { if (activeNonceScript[msg.sender].scriptAddress == address(0)) { revert NoActiveNonce(); } uint96 nonce = activeNonceScript[msg.sender].nonce; (uint256 bucket, uint256 setMask) = getBucket(nonce); nonces[msg.sender][bucket] &= ~setMask; emit ClearNonce(msg.sender, nonce); } /** * @notice Set a given nonce for the calling wallet; effectively cancels any replayable script using that nonce * @param nonce Nonce to set for the calling wallet */ function setNonce(uint96 nonce) external { // TODO: should we check whether there exists a nonceScriptAddress? (uint256 bucket, uint256 setMask) = getBucket(nonce); setNonceInternal(bucket, setMask); } /// @dev Set a nonce for the msg.sender, using the nonce's bucket and mask function setNonceInternal(uint256 bucket, uint256 setMask) internal { nonces[msg.sender][bucket] |= setMask; } /** * @notice Set a wallet nonce as the active nonce and yield control back to the wallet by calling into callback * @param nonce Nonce to activate for the transaction * @param scriptAddress Address of script to invoke with nonce lock * @param scriptCalldata Calldata for script call to invoke with nonce lock * @return Return value from the executed operation * @dev The script is expected to clearNonce() if it wishes to be replayable */ function setActiveNonceAndCallback(uint96 nonce, address scriptAddress, bytes calldata scriptCalldata) external returns (bytes memory) { // retrieve the (bucket, mask) pair that addresses the nonce in memory (uint256 bucket, uint256 setMask) = getBucket(nonce); // ensure nonce is not already set if (isNonceSetInternal(msg.sender, bucket, setMask)) { revert NonceAlreadySet(); } address cachedScriptAddress = nonceScriptAddress[msg.sender][nonce]; // if the nonce has been used before, check if the script address matches, and revert if not if ((cachedScriptAddress != address(0)) && (cachedScriptAddress != scriptAddress)) { revert NonceScriptMismatch(); } // spend the nonce; only if the callee chooses to clear it will it get un-set and become replayable setNonceInternal(bucket, setMask); // set the nonce-script pair active and yield to the wallet callback NonceScript memory previousNonceScript = activeNonceScript[msg.sender]; activeNonceScript[msg.sender] = NonceScript({nonce: nonce, scriptAddress: scriptAddress}); bytes memory result = IQuarkWallet(msg.sender).executeScriptWithNonceLock(scriptAddress, scriptCalldata); // if a nonce was cleared, set the nonceScriptAddress to lock nonce re-use to the same script address if (cachedScriptAddress == address(0) && !isNonceSetInternal(msg.sender, bucket, setMask)) { nonceScriptAddress[msg.sender][nonce] = scriptAddress; } // release the nonce when the wallet finishes executing callback activeNonceScript[msg.sender] = previousNonceScript; return result; } /// @notice Write arbitrary bytes to storage namespaced by the currently active nonce; reverts if no nonce is currently active function write(bytes32 key, bytes32 value) external { if (activeNonceScript[msg.sender].scriptAddress == address(0)) { revert NoActiveNonce(); } walletStorage[msg.sender][activeNonceScript[msg.sender].nonce][key] = value; } /** * @notice Read from storage namespaced by the currently active nonce; reverts if no nonce is currently active * @return Value at the nonce storage location, as bytes */ function read(bytes32 key) external view returns (bytes32) { if (activeNonceScript[msg.sender].scriptAddress == address(0)) { revert NoActiveNonce(); } return walletStorage[msg.sender][activeNonceScript[msg.sender].nonce][key]; } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.23; /** * @title Has Signer and Executor interface * @notice A helper interface that represents a shell for a QuarkWallet providing an executor and signer * @author Compound Labs, Inc. */ interface IHasSignerExecutor { function signer() external view returns (address); function executor() external view returns (address); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; import "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toString(int256 value) internal pure returns (string memory) { return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value)))); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.23; /** * @title Quark Wallet interface * @notice An interface for interacting with Quark Wallets * @author Compound Labs, Inc. */ interface IQuarkWallet { /// @notice The structure of a signed operation to execute in the context of this wallet struct QuarkOperation { /// @notice Nonce identifier for the operation uint96 nonce; /// @notice The address of the transaction script to run address scriptAddress; /// @notice Creation codes Quark must ensure are deployed before executing this operation bytes[] scriptSources; /// @notice Encoded function selector + arguments to invoke on the script contract bytes scriptCalldata; /// @notice Expiration time for the signature corresponding to this operation uint256 expiry; } function executeQuarkOperation(QuarkOperation calldata op, uint8 v, bytes32 r, bytes32 s) external returns (bytes memory); function executeMultiQuarkOperation( QuarkOperation calldata op, bytes32[] memory opDigests, uint8 v, bytes32 r, bytes32 s ) external returns (bytes memory); function executeScript( uint96 nonce, address scriptAddress, bytes calldata scriptCalldata, bytes[] calldata scriptSources ) external returns (bytes memory); function getDigestForQuarkOperation(QuarkOperation calldata op) external view returns (bytes32); function getDigestForMultiQuarkOperation(bytes32[] memory opDigests) external pure returns (bytes32); function getDigestForQuarkMessage(bytes memory message) external view returns (bytes32); function executeScriptWithNonceLock(address scriptAddress, bytes memory scriptCalldata) external returns (bytes memory); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.0; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
{ "remappings": [ "test/=test/", "@uniswap/=lib/", "ds-test/=lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "forge-std/=lib/forge-std/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "openzeppelin/=lib/openzeppelin-contracts/contracts/", "v3-core/=lib/v3-core/", "v3-periphery/=lib/v3-periphery/contracts/", "swap-router-contracts/=lib/swap-router-contracts/contracts/", "codejar/=lib/quark/src/codejar/", "quark-core/=lib/quark/src/quark-core/", "quark-factory/=lib/quark/src/quark-factory/", "quark-proxy/=lib/quark/src/quark-proxy/", "src/=src/", "quark-core-scripts/=lib/quark/src/quark-core-scripts/", "quark/=lib/quark/" ], "optimizer": { "enabled": true, "runs": 100000000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "none", "appendCBOR": false }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "viaIR": true, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"ApproveAndSwapFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"expectedBuyAmount","type":"uint256"},{"internalType":"uint256","name":"actualBuyAmount","type":"uint256"}],"name":"TooMuchSlippage","type":"error"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"sellToken","type":"address"},{"internalType":"uint256","name":"sellAmount","type":"uint256"},{"internalType":"address","name":"buyToken","type":"address"},{"internalType":"uint256","name":"buyAmount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"run","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60808060405234610016576107c0908161001c8239f35b600080fdfe608080604052600436101561001357600080fd5b600090813560e01c639bc2f5091461002a57600080fd5b346103c65760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103c65773ffffffffffffffffffffffffffffffffffffffff806004351690816004350361045957602435908082168092036104555760643581811681036103415767ffffffffffffffff908160a435116104515736602360a4350112156104515760a43560040135918211610451573660248360a435010111610451577f095ea7b300000000000000000000000000000000000000000000000000000000958660208201528560248201526044356044820152604481526101168161045d565b878082516020840182895af161012a6104e9565b81610422575b5080610418575b156103dd575b5060405191877f70a0823100000000000000000000000000000000000000000000000000000000918285523060048601526020856024818988165afa9485156103d257829561039a575b508190604051818192602460a43501833781018381520390826004355af16101ad6104e9565b9015610358575090602460209260405195869384928352306004840152165afa91821561034d578692610315575b5081039081116102e85760843581106102af575060405191848060208501868152836024870152816044870152604486526102158661045d565b85519082865af16102246104e9565b81610280575b5080610276575b1561023a578480f35b61026e93610269916040519160208301526024820152856044820152604481526102638161045d565b826105c2565b6105c2565b388080808480f35b50813b1515610231565b8051801592508215610295575b50503861022a565b6102a892506020809183010191016105a5565b388061028d565b604490604051907f42e0f17d00000000000000000000000000000000000000000000000000000000825260843560048301526024820152fd5b6024857f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b9091506020813d602011610345575b81610331602093836104a8565b81010312610341575190386101db565b8580fd5b3d9150610324565b6040513d88823e3d90fd5b610396906040519182917fbfa56265000000000000000000000000000000000000000000000000000000008352602060048401526024830190610547565b0390fd5b9094506020813d6020116103ca575b816103b6602093836104a8565b810103126103c657519381610187565b5080fd5b3d91506103a9565b6040513d84823e3d90fd5b6104129061040c6040518960208201528860248201528a6044820152604481526104068161045d565b876105c2565b856105c2565b3861013d565b50843b1515610137565b8051801592508215610437575b505038610130565b61044a92506020809183010191016105a5565b388061042f565b8680fd5b8480fd5b8380fd5b6080810190811067ffffffffffffffff82111761047957604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761047957604052565b3d15610542573d9067ffffffffffffffff8211610479576040519161053660207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846104a8565b82523d6000602084013e565b606090565b919082519283825260005b8481106105915750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b602081830181015184830182015201610552565b908160209103126105bd575180151581036105bd5790565b600080fd5b73ffffffffffffffffffffffffffffffffffffffff16604051604081019181831067ffffffffffffffff8411176104795761063f926040526000806020958685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656487860152868151910182855af16106396104e9565b916106f4565b8051908282159283156106dc575b505050156106585750565b608490604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b6106ec93508201810191016105a5565b38828161064d565b9192901561076f5750815115610708575090565b3b156107115790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b8251909150156107825750805190602001fd5b610396906040519182917f08c379a000000000000000000000000000000000000000000000000000000000835260206004840152602483019061054756
Deployed Bytecode
0x608080604052600436101561001357600080fd5b600090813560e01c639bc2f5091461002a57600080fd5b346103c65760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103c65773ffffffffffffffffffffffffffffffffffffffff806004351690816004350361045957602435908082168092036104555760643581811681036103415767ffffffffffffffff908160a435116104515736602360a4350112156104515760a43560040135918211610451573660248360a435010111610451577f095ea7b300000000000000000000000000000000000000000000000000000000958660208201528560248201526044356044820152604481526101168161045d565b878082516020840182895af161012a6104e9565b81610422575b5080610418575b156103dd575b5060405191877f70a0823100000000000000000000000000000000000000000000000000000000918285523060048601526020856024818988165afa9485156103d257829561039a575b508190604051818192602460a43501833781018381520390826004355af16101ad6104e9565b9015610358575090602460209260405195869384928352306004840152165afa91821561034d578692610315575b5081039081116102e85760843581106102af575060405191848060208501868152836024870152816044870152604486526102158661045d565b85519082865af16102246104e9565b81610280575b5080610276575b1561023a578480f35b61026e93610269916040519160208301526024820152856044820152604481526102638161045d565b826105c2565b6105c2565b388080808480f35b50813b1515610231565b8051801592508215610295575b50503861022a565b6102a892506020809183010191016105a5565b388061028d565b604490604051907f42e0f17d00000000000000000000000000000000000000000000000000000000825260843560048301526024820152fd5b6024857f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b9091506020813d602011610345575b81610331602093836104a8565b81010312610341575190386101db565b8580fd5b3d9150610324565b6040513d88823e3d90fd5b610396906040519182917fbfa56265000000000000000000000000000000000000000000000000000000008352602060048401526024830190610547565b0390fd5b9094506020813d6020116103ca575b816103b6602093836104a8565b810103126103c657519381610187565b5080fd5b3d91506103a9565b6040513d84823e3d90fd5b6104129061040c6040518960208201528860248201528a6044820152604481526104068161045d565b876105c2565b856105c2565b3861013d565b50843b1515610137565b8051801592508215610437575b505038610130565b61044a92506020809183010191016105a5565b388061042f565b8680fd5b8480fd5b8380fd5b6080810190811067ffffffffffffffff82111761047957604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761047957604052565b3d15610542573d9067ffffffffffffffff8211610479576040519161053660207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846104a8565b82523d6000602084013e565b606090565b919082519283825260005b8481106105915750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b602081830181015184830182015201610552565b908160209103126105bd575180151581036105bd5790565b600080fd5b73ffffffffffffffffffffffffffffffffffffffff16604051604081019181831067ffffffffffffffff8411176104795761063f926040526000806020958685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656487860152868151910182855af16106396104e9565b916106f4565b8051908282159283156106dc575b505050156106585750565b608490604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b6106ec93508201810191016105a5565b38828161064d565b9192901561076f5750815115610708575090565b3b156107115790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b8251909150156107825750805190602001fd5b610396906040519182917f08c379a000000000000000000000000000000000000000000000000000000000835260206004840152602483019061054756
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 27 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.