Skip to main content
PulseScanner.io

Address

0xaecc09f173eba3b3cc2f486e10d8182be0eb335a
Current Holdings
$0.00
TXs sent
not counted
First Active
2026-06-19
block 26,825,329
Last Active
88 days ago
block 26,825,329
Funded By
not identified

Net worth historyi

No net-worth snapshots recorded yet
exact matchSynergyPositionVaultFactorysolc 0.8.20+commit.a1b79de6runtime exact · creation exact
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

interface IPositionVaultERC20 {
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

interface ISynergizeVaultAccounting {
    function recordVaultDeposit(address owner, address pool, uint256 amount) external;
    function recordVaultWithdraw(address owner, address pool, uint256 amount) external;
    function recordVaultEmergencyWithdraw(address owner, address pool, uint256 amount) external;
}

interface IPositionVaultFactory {
    function computeVault(address owner, address pool, address farm) external view returns (address);
}

library PositionVaultSafeToken {
    function safeTransfer(address token, address to, uint256 amount) internal {
        (bool ok, bytes memory ret) = token.call(abi.encodeCall(IPositionVaultERC20.transfer, (to, amount)));
        require(ok && (ret.length == 0 || abi.decode(ret, (bool))), "token transfer failed");
    }

    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        (bool ok, bytes memory ret) = token.call(
            abi.encodeCall(IPositionVaultERC20.transferFrom, (from, to, amount))
        );
        require(ok && (ret.length == 0 || abi.decode(ret, (bool))), "token transferFrom failed");
    }
}

abstract contract PositionVaultReentrancyGuard {
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;
    uint256 private _status = _NOT_ENTERED;

    modifier nonReentrant() {
        require(_status != _ENTERED, "Reentrant");
        _status = _ENTERED;
        _;
        _status = _NOT_ENTERED;
    }
}

/// @notice Non-upgradeable owner+pool LP custody vault. The vault owns custody;
///         Synergize owns reward accounting. The farm has no LP withdrawal path.
contract SynergyPositionVault is PositionVaultReentrancyGuard {
    uint256 public constant BPS = 10_000;
    uint256 public constant LP_WITHDRAWAL_FEE_BPS = 200;
    uint256 public constant LP_WITHDRAWAL_LOCK_BPS = 100;
    uint256 public constant LP_WITHDRAWAL_TREASURY_BPS = 70;
    uint256 public constant LP_WITHDRAWAL_SCADA_CREATOR_BPS = 30;
    uint256 public constant MIN_WITHDRAWABLE_LP = 334;
    address public constant PULSECHAIN_LP_SINK = 0x0000000000000000000000000000000000000369;

    address public immutable OWNER;
    address public immutable POOL;
    address public immutable FARM;
    address public immutable PERMANENT_LP_SINK;
    address public immutable TREASURY_RECIPIENT;
    address public immutable SCADA_CREATOR_RECIPIENT;

    uint256 public accountedLP;

    event Deposited(uint256 amount);
    event Withdrawn(uint256 amount, uint256 returnedLP, uint256 lockedLP, uint256 treasuryLP, uint256 scadaCreatorLP);
    event EmergencyWithdrawn(uint256 amount, uint256 returnedLP, uint256 lockedLP, uint256 treasuryLP, uint256 scadaCreatorLP, bool farmNotified);
    event FarmWithdrawWithFee(
        address indexed user,
        address indexed pool,
        address indexed lpToken,
        uint256 amountRequested,
        uint256 amountToUser,
        uint256 permanentLpLocked,
        uint256 treasuryFee,
        uint256 scadaCreatorFee
    );
    event EmergencyAccountingCallbackFailed(uint256 amount);

    constructor(
        address owner_,
        address pool_,
        address farm_,
        address permanentLpSink_,
        address treasuryRecipient_,
        address scadaCreatorRecipient_
    ) {
        require(owner_ != address(0), "owner=0");
        require(pool_.code.length > 0, "pool not contract");
        require(farm_.code.length > 0, "farm not contract");
        require(permanentLpSink_ == PULSECHAIN_LP_SINK, "bad sink");
        require(treasuryRecipient_ != address(0), "treasury=0");
        require(scadaCreatorRecipient_ != address(0), "scada creator=0");
        OWNER = owner_;
        POOL = pool_;
        FARM = farm_;
        PERMANENT_LP_SINK = permanentLpSink_;
        TREASURY_RECIPIENT = treasuryRecipient_;
        SCADA_CREATOR_RECIPIENT = scadaCreatorRecipient_;
    }

    modifier onlyOwner() {
        require(msg.sender == OWNER, "owner only");
        _;
    }

    function deposit(uint256 amount) external onlyOwner nonReentrant {
        require(amount > 0, "amount=0");
        uint256 beforeBal = IPositionVaultERC20(POOL).balanceOf(address(this));
        PositionVaultSafeToken.safeTransferFrom(POOL, OWNER, address(this), amount);
        uint256 received = IPositionVaultERC20(POOL).balanceOf(address(this)) - beforeBal;
        require(received > 0, "no lp received");
        require(received >= MIN_WITHDRAWABLE_LP, "deposit too small");
        accountedLP += received;
        ISynergizeVaultAccounting(FARM).recordVaultDeposit(OWNER, POOL, received);
        emit Deposited(received);
    }

    function withdraw(uint256 amount) external onlyOwner nonReentrant {
        _validateExitAmount(amount);
        ISynergizeVaultAccounting(FARM).recordVaultWithdraw(OWNER, POOL, amount);
        accountedLP -= amount;
        (uint256 returnedLP, uint256 lockedLP, uint256 treasuryLP, uint256 scadaCreatorLP) = _payFootprint(amount);
        emit Withdrawn(amount, returnedLP, lockedLP, treasuryLP, scadaCreatorLP);
    }

    /// @notice Owner-only LP escape hatch. It attempts to notify farm accounting,
    ///         but LP return is not blocked by farm/policy/oracle failure.
    function emergencyWithdraw(uint256 amount) external onlyOwner nonReentrant {
        _validateExitAmount(amount);
        bool farmNotified;
        try ISynergizeVaultAccounting(FARM).recordVaultEmergencyWithdraw(OWNER, POOL, amount) {
            farmNotified = true;
        } catch {
            emit EmergencyAccountingCallbackFailed(amount);
        }
        accountedLP -= amount;
        (uint256 returnedLP, uint256 lockedLP, uint256 treasuryLP, uint256 scadaCreatorLP) = _payFootprint(amount);
        emit EmergencyWithdrawn(amount, returnedLP, lockedLP, treasuryLP, scadaCreatorLP, farmNotified);
    }

    function _validateExitAmount(uint256 amount) private view {
        require(amount > 0 && amount <= accountedLP, "bad amount");
        uint256 remaining = accountedLP - amount;
        require(remaining == 0 || remaining >= MIN_WITHDRAWABLE_LP, "remaining lp too small");
    }

    function _payFootprint(uint256 amount)
        private
        returns (uint256 returnedLP, uint256 lockedLP, uint256 treasuryLP, uint256 scadaCreatorLP)
    {
        lockedLP = amount * LP_WITHDRAWAL_LOCK_BPS / BPS;
        treasuryLP = amount * LP_WITHDRAWAL_TREASURY_BPS / BPS;
        scadaCreatorLP = amount * LP_WITHDRAWAL_SCADA_CREATOR_BPS / BPS;
        uint256 targetFee = amount * LP_WITHDRAWAL_FEE_BPS / BPS;
        uint256 allocatedFee = lockedLP + treasuryLP + scadaCreatorLP;
        if (targetFee > allocatedFee) lockedLP += targetFee - allocatedFee;
        returnedLP = amount - lockedLP - treasuryLP - scadaCreatorLP;
        if (lockedLP > 0) PositionVaultSafeToken.safeTransfer(POOL, PERMANENT_LP_SINK, lockedLP);
        if (treasuryLP > 0) PositionVaultSafeToken.safeTransfer(POOL, TREASURY_RECIPIENT, treasuryLP);
        if (scadaCreatorLP > 0) PositionVaultSafeToken.safeTransfer(POOL, SCADA_CREATOR_RECIPIENT, scadaCreatorLP);
        if (returnedLP > 0) PositionVaultSafeToken.safeTransfer(POOL, OWNER, returnedLP);
        emit FarmWithdrawWithFee(OWNER, POOL, POOL, amount, returnedLP, lockedLP, treasuryLP, scadaCreatorLP);
    }

    function custodyInvariantHolds() external view returns (bool) {
        return IPositionVaultERC20(POOL).balanceOf(address(this)) >= accountedLP;
    }
}

/// @notice Stateless deterministic factory. The farm address is part of the
///         salt, so the same owner+pool can have distinct vaults per farm.
contract SynergyPositionVaultFactory {
    address public constant PULSECHAIN_LP_SINK = 0x0000000000000000000000000000000000000369;

    address public immutable PERMANENT_LP_SINK;
    address public immutable TREASURY_RECIPIENT;
    address public immutable SCADA_CREATOR_RECIPIENT;

    event VaultCreated(address indexed owner, address indexed pool, address indexed farm, address vault);
    event WithdrawFeeRecipientsConfigured(address permanentLpSink, address treasuryRecipient, address scadaCreatorRecipient);

    constructor(address permanentLpSink_, address treasuryRecipient_, address scadaCreatorRecipient_) {
        require(permanentLpSink_ == PULSECHAIN_LP_SINK, "bad sink");
        require(treasuryRecipient_ != address(0), "treasury=0");
        require(scadaCreatorRecipient_ != address(0), "scada creator=0");
        PERMANENT_LP_SINK = permanentLpSink_;
        TREASURY_RECIPIENT = treasuryRecipient_;
        SCADA_CREATOR_RECIPIENT = scadaCreatorRecipient_;
        emit WithdrawFeeRecipientsConfigured(permanentLpSink_, treasuryRecipient_, scadaCreatorRecipient_);
    }

    function createVault(address owner, address pool, address farm) external returns (address vault) {
        vault = computeVault(owner, pool, farm);
        if (vault.code.length != 0) return vault;
        bytes32 salt = _salt(owner, pool, farm);
        vault = address(new SynergyPositionVault{salt: salt}(
            owner,
            pool,
            farm,
            PERMANENT_LP_SINK,
            TREASURY_RECIPIENT,
            SCADA_CREATOR_RECIPIENT
        ));
        emit VaultCreated(owner, pool, farm, vault);
    }

    function computeVault(address owner, address pool, address farm) public view returns (address) {
        bytes32 salt = vaultSalt(owner, pool, farm);
        bytes32 initCodeHash = vaultInitCodeHash(owner, pool, farm);
        return address(uint160(uint256(keccak256(abi.encodePacked(
            bytes1(0xff),
            address(this),
            salt,
            initCodeHash
        )))));
    }

    function vaultSalt(address owner, address pool, address farm) public pure returns (bytes32) {
        return _salt(owner, pool, farm);
    }

    function vaultInitCodeHash(address owner, address pool, address farm) public view returns (bytes32) {
        return keccak256(abi.encodePacked(
            type(SynergyPositionVault).creationCode,
            abi.encode(owner, pool, farm, PERMANENT_LP_SINK, TREASURY_RECIPIENT, SCADA_CREATOR_RECIPIENT)
        ));
    }

    function _salt(address owner, address pool, address farm) private pure returns (bytes32) {
        return keccak256(abi.encode(owner, pool, farm));
    }
}