ETH Price: $3,829.56 (-4.07%)

Contract

0x0db08ac3F14aC893e2cA218815F87D1CAc495550
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Supply LP171338142023-04-26 23:58:59592 days ago1682553539IN
0x0db08ac3...CAc495550
0 ETH0.0139170634.50079097
Fund171335242023-04-26 23:00:59592 days ago1682550059IN
0x0db08ac3...CAc495550
1.35356654 ETH0.0028147941.20917878
Fund171270092023-04-26 1:02:59593 days ago1682470979IN
0x0db08ac3...CAc495550
0.04274924 ETH0.0028226633.05031727
Fund171129172023-04-24 1:36:11595 days ago1682300171IN
0x0db08ac3...CAc495550
0.065 ETH0.0035685541.78388213
Fund170981912023-04-22 0:03:47597 days ago1682121827IN
0x0db08ac3...CAc495550
0.51 ETH0.0028615433.50557449
Fund170981802023-04-22 0:01:35597 days ago1682121695IN
0x0db08ac3...CAc495550
0 ETH0.002191934.95587648
Fund170926252023-04-21 5:14:11598 days ago1682054051IN
0x0db08ac3...CAc495550
0.015 ETH0.0031429836.80096683
Fund170707792023-04-18 2:59:11601 days ago1681786751IN
0x0db08ac3...CAc495550
0.11 ETH0.0027534932.24049732
Fund170707792023-04-18 2:59:11601 days ago1681786751IN
0x0db08ac3...CAc495550
0.05 ETH0.0028303633.14049732
Fund170704872023-04-18 1:59:59601 days ago1681783199IN
0x0db08ac3...CAc495550
1 ETH0.0031723637.14497596
Fund170704522023-04-18 1:52:59601 days ago1681782779IN
0x0db08ac3...CAc495550
0.2 ETH0.0034346140.21559274
Fund170704052023-04-18 1:43:35601 days ago1681782215IN
0x0db08ac3...CAc495550
0.5 ETH0.0033697839.45652514
Fund170694652023-04-17 22:31:59601 days ago1681770719IN
0x0db08ac3...CAc495550
0.15 ETH0.0027684332.41533646
Fund170690212023-04-17 21:01:59601 days ago1681765319IN
0x0db08ac3...CAc495550
0.83 ETH0.0029506934.54945396
Fund170689892023-04-17 20:55:23601 days ago1681764923IN
0x0db08ac3...CAc495550
0.48 ETH0.0031961337.42329911
Fund170689752023-04-17 20:52:35601 days ago1681764755IN
0x0db08ac3...CAc495550
0.47 ETH0.0037397943.78894423
Fund170689232023-04-17 20:42:11601 days ago1681764131IN
0x0db08ac3...CAc495550
1 ETH0.0038965938.01371921

Latest 4 internal transactions

Advanced mode:
Parent Transaction Hash Block From To
171338142023-04-26 23:58:59592 days ago1682553539
0x0db08ac3...CAc495550
0.16760509 ETH
171338142023-04-26 23:58:59592 days ago1682553539
0x0db08ac3...CAc495550
 Contract Creation0 ETH
171338142023-04-26 23:58:59592 days ago1682553539
0x0db08ac3...CAc495550
6.60871069 ETH
170664972023-04-17 12:28:47602 days ago1681734527  Contract Creation0 ETH
Loading...
Loading

Minimal Proxy Contract for 0x8b071cef7759f4b353b9ed7cd74a9e8b68b30a4e

Contract Name:
GroupLP

Compiler Version
v0.8.4+commit.c7e474f2

Optimization Enabled:
No with 200 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 6 : GroupLP.sol
pragma solidity ^0.8.4;

import '@uniswap/v2-periphery/contracts/interfaces/IERC20.sol';
import '@uniswap/v2-periphery/contracts/interfaces/IWETH.sol';
import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';
import "@openzeppelin/contracts/proxy/Clones.sol";

interface IOptiVault {
  function initialize(address _token, uint256 _lockupDate, uint256 _minimumTokenCommitment, uint256 _withdrawalsLockedUntilTimestamp, address _balanceLookup) external;
}

contract GroupLP {
  receive() external payable {}

  address private constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
  address private constant operations = 0x133A5437951EE1D312fD36a74481987Ec4Bf8A96;
  address private constant optiVaultMaster = 0xC5A00A96E6a7039daD5af5c41584469048B26038; // Address to clone OptiVault from

  IERC20 public token;                                   // Token to add LP for
  address public tokenSupplier;                          // Supplier of token
  uint256 public committedTokenAmount;                   // Amount to match
  uint256 public goalDate;                               // Date by which to complete the campaign
  uint256 public withdrawalsLockedDuration;              // Proposed duration of OptiVault lock
  uint256 public withdrawalsLockedUntilTimestamp;        // End time plus Duration
  IUniswapV2Pair private pair;                           // Pair to add to
  bool private initialized;                              // Campaign pamaters hav been set
  bool public campaignFailed;                            // Allows recovery of ETH under failure conditions

  mapping (address => uint256) public ethContributionOf; // Used as numerator for calculating users shares
  uint256 public totalFundingRaised;                     // Used as denominator for calculating users shares

  uint256 public mintedLP;                               // Amount of LP that has been minted
  address public optiVault;                              // cloned OptiVault contract that holds the minted LP

  function initialize(address _pair, address _tokenSupplier, uint256 _goalDate, uint256 _withdrawalsLockedDuration,  uint256 _commitment) external payable {
    require(!initialized, "GroupLP: Already initialized");
    committedTokenAmount = _commitment;
    pair = IUniswapV2Pair(_pair);
    token = (pair.token0() == WETH) ? IERC20(pair.token1()) : IERC20(pair.token0());
    goalDate = _goalDate;
    withdrawalsLockedDuration = _withdrawalsLockedDuration;
    tokenSupplier = _tokenSupplier;
    ethContributionOf[tokenSupplier] = msg.value;
    totalFundingRaised = msg.value; 
    initialized = true;
  }

  function supplierHasCommitedBalance() public view returns (bool valid) {
    // Campaign is valid IFF: (Commited tokens <= Supplier approval <= Supplier balance)
    uint256 approval = token.allowance(tokenSupplier, address(this));
    uint256 balance = token.balanceOf(tokenSupplier);
    valid = committedTokenAmount <= approval && approval <= balance;
  }

  function getReserves() internal view returns (uint256 ethReserves, uint256 tokenReserves) {
    (uint reserveA, uint reserveB, ) = pair.getReserves();
    (ethReserves, tokenReserves) = WETH < address(token) ? (reserveA, reserveB) : (reserveB, reserveA);
  }

  function uniswapQuote(uint amountToken) internal view returns (uint256 amountEth) {
    (uint256 ethReserves, uint256 tokenReserves) = getReserves();
    amountEth = amountToken * ethReserves / tokenReserves;
  }  

  function ethMatchEstimate() public view returns (uint256 ethGoal) {
    ethGoal = uniswapQuote(committedTokenAmount) * 1005 / 1000;
  }

  function fund() public payable {
    require(supplierHasCommitedBalance(), "GroupLP: Supplier is missing tokens");
    require(!campaignFailed, "GroupLP: Campaign failed, use recoverETH");
    require((ethMatchEstimate() * 110) / 100 >= address(this).balance, "GroupLP: Over funded!");
    ethContributionOf[msg.sender] += msg.value;
    totalFundingRaised += msg.value;
  }

  function endFailedCampaign() public {
    // Either the campaign is invalid (token supplier's balance or approval has fallen beneath the committed tokens)
    // Or the campaign goalDate is past with no LP created
    require(!campaignFailed, "GroupLP: Campaign already failed.");
    require(mintedLP == 0, "GroupLP: LP already added!");
    bool campaignHasExpired = (block.timestamp > (goalDate + 1 hours));
    if (!supplierHasCommitedBalance() || campaignHasExpired) {
      campaignFailed = true;
    }
  }

  function readyToMint() public view returns (bool ready) {
    require(address(this).balance >= ethMatchEstimate(), "GroupLP: Campaign needs more ETH to match supplier's committed tokens");
    require(supplierHasCommitedBalance(), "GroupLP: Supplier's token balance or approval has fallen below the required amount.");
    return true;
  }

  function supplyLP(uint minimumEth) public returns (uint256 amountToken, uint256 amountETH) {
    require(msg.sender == operations);
    require(readyToMint());
    address pairAddress = address(pair);
    uint256 tokenBalanceOfPairPreSupply = token.balanceOf(pairAddress);
    TransferHelper.safeTransferFrom(address(token), tokenSupplier, pairAddress, committedTokenAmount);
    uint256 tokenBalanceOfPairPostSupply = token.balanceOf(pairAddress);
    amountToken = tokenBalanceOfPairPostSupply - tokenBalanceOfPairPreSupply; 
    amountETH = uniswapQuote(amountToken); 
    require(amountETH >= minimumEth, "GroupLP: Tokens must be valued at least the minimum Eth"); 
    IWETH(WETH).deposit{value: amountETH}();
    assert(IWETH(WETH).transfer(pairAddress, amountETH));

    withdrawalsLockedUntilTimestamp = block.timestamp + withdrawalsLockedDuration;
    optiVault = Clones.clone(optiVaultMaster);
    mintedLP = IUniswapV2Pair(pairAddress).mint(optiVault);
    payable(operations).transfer(address(this).balance); //Excess ETH to operations
    IOptiVault(optiVault).initialize(pairAddress, 0, 0, withdrawalsLockedUntilTimestamp, address(this));
  }

  function recoverETH() public {
    require(campaignFailed, "GroupLP: Campaign is active. Use withdrawLP.");
    require(ethContributionOf[msg.sender] > 0, "GroupLP: You have recovered all your ETH");
    payable(msg.sender).transfer(ethContributionOf[msg.sender]);
    ethContributionOf[msg.sender] == 0;
  }

  function contributionOf(address user) external view returns (uint256 _ethBalance) {
    _ethBalance = ethContributionOf[user];
  }

  function sharesOf(address user) external view returns (uint256 _lpTokenShare) {
    _lpTokenShare = (mintedLP / 2) * ethContributionOf[user] / totalFundingRaised;
    if (user == tokenSupplier) {
      _lpTokenShare += mintedLP / 2;
    }
  }

}

File 2 of 6 : IERC20.sol
pragma solidity >=0.5.0;

interface IERC20 {
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);

    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint8);
    function totalSupply() external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function allowance(address owner, address spender) external view returns (uint);

    function approve(address spender, uint value) external returns (bool);
    function transfer(address to, uint value) external returns (bool);
    function transferFrom(address from, address to, uint value) external returns (bool);
}

File 3 of 6 : IWETH.sol
pragma solidity >=0.5.0;

interface IWETH {
    function deposit() external payable;
    function transfer(address to, uint value) external returns (bool);
    function withdraw(uint) external;
}

File 4 of 6 : IUniswapV2Pair.sol
pragma solidity >=0.5.0;

interface IUniswapV2Pair {
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);

    function name() external pure returns (string memory);
    function symbol() external pure returns (string memory);
    function decimals() external pure returns (uint8);
    function totalSupply() external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function allowance(address owner, address spender) external view returns (uint);

    function approve(address spender, uint value) external returns (bool);
    function transfer(address to, uint value) external returns (bool);
    function transferFrom(address from, address to, uint value) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);
    function PERMIT_TYPEHASH() external pure returns (bytes32);
    function nonces(address owner) external view returns (uint);

    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;

    event Mint(address indexed sender, uint amount0, uint amount1);
    event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
    event Swap(
        address indexed sender,
        uint amount0In,
        uint amount1In,
        uint amount0Out,
        uint amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint);
    function factory() external view returns (address);
    function token0() external view returns (address);
    function token1() external view returns (address);
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
    function price0CumulativeLast() external view returns (uint);
    function price1CumulativeLast() external view returns (uint);
    function kLast() external view returns (uint);

    function mint(address to) external returns (uint liquidity);
    function burn(address to) external returns (uint amount0, uint amount1);
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
    function skim(address to) external;
    function sync() external;

    function initialize(address, address) external;
}

File 5 of 6 : TransferHelper.sol
pragma solidity >=0.6.0;

// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library TransferHelper {
    function safeApprove(address token, address to, uint value) internal {
        // bytes4(keccak256(bytes('approve(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: APPROVE_FAILED');
    }

    function safeTransfer(address token, address to, uint value) internal {
        // bytes4(keccak256(bytes('transfer(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FAILED');
    }

    function safeTransferFrom(address token, address from, address to, uint value) internal {
        // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FROM_FAILED');
    }

    function safeTransferETH(address to, uint value) internal {
        (bool success,) = to.call{value:value}(new bytes(0));
        require(success, 'TransferHelper: ETH_TRANSFER_FAILED');
    }
}

File 6 of 6 : Clones.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/Clones.sol)

pragma solidity ^0.8.0;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 *
 * _Available since v3.4._
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create(0, ptr, 0x37)
        }
        require(instance != address(0), "ERC1167: create failed");
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create2(0, ptr, 0x37, salt)
        }
        require(instance != address(0), "ERC1167: create2 failed");
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
            mstore(add(ptr, 0x38), shl(0x60, deployer))
            mstore(add(ptr, 0x4c), salt)
            mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
            predicted := keccak256(add(ptr, 0x37), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(address implementation, bytes32 salt)
        internal
        view
        returns (address predicted)
    {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}

Settings
{
  "optimizer": {
    "enabled": false,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract ABI

[{"inputs":[],"name":"campaignFailed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"committedTokenAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"contributionOf","outputs":[{"internalType":"uint256","name":"_ethBalance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"endFailedCampaign","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"ethContributionOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ethMatchEstimate","outputs":[{"internalType":"uint256","name":"ethGoal","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fund","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"goalDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_pair","type":"address"},{"internalType":"address","name":"_tokenSupplier","type":"address"},{"internalType":"uint256","name":"_goalDate","type":"uint256"},{"internalType":"uint256","name":"_withdrawalsLockedDuration","type":"uint256"},{"internalType":"uint256","name":"_commitment","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"mintedLP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"optiVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"readyToMint","outputs":[{"internalType":"bool","name":"ready","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"recoverETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"sharesOf","outputs":[{"internalType":"uint256","name":"_lpTokenShare","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"supplierHasCommitedBalance","outputs":[{"internalType":"bool","name":"valid","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"minimumEth","type":"uint256"}],"name":"supplyLP","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenSupplier","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalFundingRaised","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawalsLockedDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawalsLockedUntilTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ 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.