pXRPVaultWrapper_Pyth

Description:

Decentralized Finance (DeFi) protocol contract providing Factory functionality.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/* ---------- Minimal OpenZeppelin-style base contracts ---------- */
abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } }
contract Ownable is Context {
    address private _owner;
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    constructor(address initialOwner) { _owner = initialOwner; emit OwnershipTransferred(address(0), initialOwner); }
    modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; }
    function owner() public view returns (address) { return _owner; }
}
contract ERC20 {
    string public name; string public symbol; uint8 public constant decimals = 18;
    uint256 public totalSupply;
    mapping(address=>uint256) public balanceOf;
    mapping(address=>mapping(address=>uint256)) public allowance;
    event Transfer(address indexed from,address indexed to,uint256 value);
    event Approval(address indexed owner,address indexed spender,uint256 value);
    constructor(string memory n,string memory s){name=n;symbol=s;}
    function _mint(address to,uint256 amount) internal { totalSupply+=amount; balanceOf[to]+=amount; emit Transfer(address(0),to,amount); }
    function _burn(address from,uint256 amount) internal { require(balanceOf[from]>=amount,"ERC20: burn amount exceeds balance"); balanceOf[from]-=amount; totalSupply-=amount; emit Transfer(from,address(0),amount); }
    function approve(address spender,uint256 amount) external returns(bool){ allowance[msg.sender][spender]=amount; emit Approval(msg.sender,spender,amount); return true; }
    function transfer(address to,uint256 amount) external returns(bool){ require(balanceOf[msg.sender]>=amount,"ERC20: insufficient"); balanceOf[msg.sender]-=amount; balanceOf[to]+=amount; emit Transfer(msg.sender,to,amount); return true; }
    function transferFrom(address from,address to,uint256 amount) external returns(bool){
        uint256 a=allowance[from][msg.sender]; require(a>=amount,"ERC20: no allowance");
        if(a!=type(uint256).max){ allowance[from][msg.sender]=a-amount; }
        require(balanceOf[from]>=amount,"ERC20: insufficient"); balanceOf[from]-=amount; balanceOf[to]+=amount; emit Transfer(from,to,amount); return true;
    }
}

/* ---------- Interfaces ---------- */
interface IGrowthVault {
    function getBalance() external view returns (uint256);  // USDC (6 dec)
    function unlockTime() external view returns (uint256);
    function owner() external view returns (address);
}
library PythStructs { struct Price { int64 price; uint64 conf; int32 expo; uint publishTime; } }
interface IPyth {
    function getUpdateFee(bytes[] calldata updateData) external view returns (uint256);
    function updatePriceFeeds(bytes[] calldata updateData) external payable;
    function getPriceUnsafe(bytes32 id) external view returns (PythStructs.Price memory);
}

/* ---------- Contract ---------- */
contract pXRPVaultWrapper_Pyth is ERC20, Ownable {
    IGrowthVault public immutable vault;
    IPyth public immutable pyth;
    bytes32 public immutable XRP_PRICE_ID;   // Crypto.XRP/USD

    uint256 public collateralUSDC; // notional USDC reserved (6 dec)

    struct Position { uint256 pXRP; uint256 entryCostUSDC; }
    mapping(address => Position) public positions;

    event Minted(address indexed user, uint256 usdcIn, int64 px, int32 expo, uint256 pXRPMinted);
    event Burned(address indexed user, uint256 pXRPBurned, uint256 usdcReleased);
    event SettlementQuoted(address indexed user, uint256 pXRPBal, int64 px, int32 expo, uint256 usdcNow, int256 pnlUSDC);

    constructor(address _vault, address _pythContract, bytes32 _xrpPriceId)
        ERC20("Synthetic XRP","pXRP")
        Ownable(msg.sender)
    {
        require(_vault!=address(0)&&_pythContract!=address(0),"zero addr");
        vault = IGrowthVault(_vault);
        pyth  = IPyth(_pythContract);
        XRP_PRICE_ID = _xrpPriceId;
        require(vault.owner()==msg.sender,"deployer must own vault");
    }

    /* ---------- Price helpers ---------- */
    function _getXrpPx8(uint256 maxAgeSec) internal view returns (uint256 px8){
        PythStructs.Price memory p=pyth.getPriceUnsafe(XRP_PRICE_ID);
        require(p.price>0,"bad price");
        require(block.timestamp-p.publishTime<=maxAgeSec,"stale price");
        int32 targetExpo=-8;
        int32 delta=targetExpo-p.expo;
        if(delta>=0){ px8=uint256(int256(p.price))*(10**uint32(uint256(int256(delta)))); }
        else{ px8=uint256(int256(p.price))/(10**uint32(uint256(int256(-delta)))); }
    }

    function availableUSDC() public view returns(uint256){
        uint256 bal=vault.getBalance();
        return bal>collateralUSDC?bal-collateralUSDC:0;
    }

    function previewMint(uint256 usdcAmount,uint256 maxAgeSec) external view returns(uint256 pXRPOut){
        uint256 px8=_getXrpPx8(maxAgeSec);
        pXRPOut=(usdcAmount*1e20)/px8;
    }
    function previewBurn(uint256 pXRPAmount,uint256 maxAgeSec) external view returns(uint256 usdcOut){
        uint256 px8=_getXrpPx8(maxAgeSec);
        usdcOut=(pXRPAmount*px8)/1e20;
    }

    /* ---------- Mint / Burn ---------- */
    function mintByUSDC(uint256 usdcAmount,bytes[] calldata updateData,uint256 maxAgeSec) external payable onlyOwner{
        require(usdcAmount>0,"zero amount");
        require(usdcAmount<=availableUSDC(),"exceeds collateral");

        if(updateData.length>0){
            uint256 fee=pyth.getUpdateFee(updateData);
            require(msg.value>=fee,"insufficient fee");
            pyth.updatePriceFeeds{value:fee}(updateData);
            if(msg.value>fee)payable(msg.sender).transfer(msg.value-fee);
        }

        uint256 px8=_getXrpPx8(maxAgeSec);
        uint256 mintAmount=(usdcAmount*1e20)/px8;

        collateralUSDC+=usdcAmount;
        Position storage pos=positions[msg.sender];
        pos.pXRP+=mintAmount;
        pos.entryCostUSDC+=usdcAmount;

        _mint(msg.sender,mintAmount);
        PythStructs.Price memory p=pyth.getPriceUnsafe(XRP_PRICE_ID);
        emit Minted(msg.sender,usdcAmount,p.price,p.expo,mintAmount);
    }

    function burn(uint256 pXRPAmount,bytes[] calldata updateData,uint256 maxAgeSec) external payable{
        require(pXRPAmount>0,"zero amount");
        require(balanceOf[msg.sender]>=pXRPAmount,"insufficient pXRP");

        if(updateData.length>0){
            uint256 fee=pyth.getUpdateFee(updateData);
            require(msg.value>=fee,"insufficient fee");
            pyth.updatePriceFeeds{value:fee}(updateData);
            if(msg.value>fee)payable(msg.sender).transfer(msg.value-fee);
        }

        _burn(msg.sender,pXRPAmount);

        uint256 px8=_getXrpPx8(maxAgeSec);
        uint256 usdcReleased=(pXRPAmount*px8)/1e20;
        if(usdcReleased>collateralUSDC)usdcReleased=collateralUSDC;
        collateralUSDC-=usdcReleased;

        Position storage pos=positions[msg.sender];
        uint256 prevPXRP=pos.pXRP+pXRPAmount;
        if(prevPXRP>0){
            uint256 costToRemove=(pos.entryCostUSDC*pXRPAmount)/prevPXRP;
            if(costToRemove>pos.entryCostUSDC)costToRemove=pos.entryCostUSDC;
            pos.entryCostUSDC-=costToRemove;
        }
        pos.pXRP=balanceOf[msg.sender];

        emit Burned(msg.sender,pXRPAmount,usdcReleased);
    }

    /* ---------- Settlement (post-unlock) ---------- */
    function quoteRedeemNow(bytes[] calldata updateData,uint256 maxAgeSec)
        external payable returns(uint256 usdcNow,int256 pnlUSDC)
    {
        require(block.timestamp>=vault.unlockTime(),"vault locked");

        if(updateData.length>0){
            uint256 fee=pyth.getUpdateFee(updateData);
            require(msg.value>=fee,"insufficient fee");
            pyth.updatePriceFeeds{value:fee}(updateData);
            if(msg.value>fee)payable(msg.sender).transfer(msg.value-fee);
        }

        uint256 pBal=balanceOf[msg.sender];
        require(pBal>0,"no pXRP");

        uint256 px8=_getXrpPx8(maxAgeSec);
        usdcNow=(pBal*px8)/1e20;

        uint256 entryCost=positions[msg.sender].entryCostUSDC;
        pnlUSDC=int256(usdcNow)-int256(entryCost);

        PythStructs.Price memory p=pyth.getPriceUnsafe(XRP_PRICE_ID);
        emit SettlementQuoted(msg.sender,pBal,p.price,p.expo,usdcNow,pnlUSDC);
    }

    function averageEntryPrice8(address user) external view returns(uint256){
        Position memory pos=positions[user];
        if(pos.pXRP==0)return 0;
        return (pos.entryCostUSDC*1e20)/pos.pXRP;
    }
}

Tags:
DeFi, Factory|addr:0xa4c2878dac4b2884f411691b499e94d655475a71|verified:true|block:23524496|tx:0x7c4453f44a11db81b839f72b471f8284e7965518855800700a7908aa9c29ee6e|first_check:1759829495

Submitted on: 2025-10-07 11:31:35

Comments

Log in to comment.

No comments yet.