SyntheticPrepaidCardSystem

Description:

Smart contract deployed on Ethereum with Oracle features.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

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

interface IERC20 {
    function transfer(address to, uint256 value) external returns (bool);
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

interface AggregatorV3Interface {
    function latestRoundData() external view returns (
        uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound
    );
    function decimals() external view returns (uint8);
}

contract SyntheticPrepaidCardSystem {
    address public owner;

    IERC20 public constant USDT = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7);
    IERC20 public constant USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);

    AggregatorV3Interface public ethUsdFeed;
    AggregatorV3Interface public usdtUsdFeed;
    AggregatorV3Interface public usdcUsdFeed;

    struct PaymentCard {
        string number;
        string expiration;
        uint16 securityCode;
        string cardType;
        string status;
        uint256 ethBalance;
        uint256 usdtBalance;
        uint256 usdcBalance;
        uint256 syntheticBalance; // synthetic token units
        string paypalAccount;
    }

    PaymentCard[] public cards;

    /* ---------- Events ---------- */
    event CardCreated(uint256 indexed cardIndex, string cardType, string number);
    event SpendExecuted(uint256 indexed cardIndex, string merchant, string asset, uint256 amount);
    event PayPalLinkRequested(uint256 indexed cardIndex, string number, string cardType, string expiration, uint16 securityCode);
    event PayPalLinkConfirmed(uint256 indexed cardIndex, string paypalAccount);
    event Deposit(uint256 indexed cardIndex, string asset, uint256 amount);
    event Withdraw(uint256 indexed cardIndex, string asset, uint256 amount);
    event OracleUpdated(string asset, address feed);
    event SwapExecuted(uint256 indexed cardIndex, string fromAsset, string toAsset, uint256 amountFrom, uint256 amountTo);

    modifier onlyOwner() { require(msg.sender == owner, "Not authorized"); _; }
    modifier validCardIndex(uint256 index) { require(index < cards.length, "Invalid card index"); _; }

    constructor() { owner = msg.sender; }

    /* ---------- Oracles ---------- */
    function setEthUsdFeed(address feed) external onlyOwner { ethUsdFeed = AggregatorV3Interface(feed); emit OracleUpdated("ETH", feed); }
    function setUsdtUsdFeed(address feed) external onlyOwner { usdtUsdFeed = AggregatorV3Interface(feed); emit OracleUpdated("USDT", feed); }
    function setUsdcUsdFeed(address feed) external onlyOwner { usdcUsdFeed = AggregatorV3Interface(feed); emit OracleUpdated("USDC", feed); }

    function _getLatestPrice(AggregatorV3Interface feed) internal view returns (uint256) {
        require(address(feed) != address(0), "Oracle not set");
        (, int256 price,,,) = feed.latestRoundData();
        require(price > 0, "Invalid price");
        uint8 dec = feed.decimals();
        if (dec < 18) return uint256(price) * 10**(18 - dec);
        else if (dec > 18) return uint256(price) / 10**(dec - 18);
        else return uint256(price);
    }

    /* ---------- Utilities ---------- */
    function uintToString(uint256 v) internal pure returns (string memory str) {
        if (v == 0) return "0";
        uint256 j = v; uint256 length; while (j != 0) { length++; j /= 10; }
        bytes memory bstr = new bytes(length);
        uint256 k = length; j = v;
        while (j != 0) { bstr[--k] = bytes1(uint8(48 + j % 10)); j /= 10; }
        str = string(bstr);
    }

    function generatePseudoRandom(uint seed, uint offset) internal view returns (uint256) {
        return uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender, seed, offset)));
    }

    function calculateLuhnCheckDigit(string memory base) internal pure returns (uint8) {
        bytes memory digits = bytes(base);
        uint256 sum = 0;
        bool doubleDigit = true;
        for (uint256 i = digits.length; i > 0; i--) {
            uint8 d = uint8(digits[i - 1]) - 48;
            if (doubleDigit) { d *= 2; if (d > 9) d -= 9; }
            sum += d; doubleDigit = !doubleDigit;
        }
        return uint8((10 - (sum % 10)) % 10);
    }

    /* ---------- Card Generation ---------- */
    function generateSyntheticVisa(uint seed) internal view returns (PaymentCard memory) {
        string memory base = "4";
        for (uint j = 0; j < 14; j++) { base = string(abi.encodePacked(base, uintToString(generatePseudoRandom(seed, j) % 10))); }
        uint8 checkDigit = calculateLuhnCheckDigit(base);
        string memory fullNumber = string(abi.encodePacked(base, uintToString(checkDigit)));
        return PaymentCard(fullNumber,"12/2027",uint16(100 + (generatePseudoRandom(seed,99)%900)),"Visa","Active",0,0,0,0,"");
    }

    function generateSyntheticMasterCard(uint seed) internal view returns (PaymentCard memory) {
        string memory base = "5";
        base = string(abi.encodePacked(base, uintToString(1 + (generatePseudoRandom(seed,0)%5))));
        for (uint j=0;j<13;j++){ base=string(abi.encodePacked(base,uintToString(generatePseudoRandom(seed,j+1)%10))); }
        uint8 checkDigit = calculateLuhnCheckDigit(base);
        string memory fullNumber = string(abi.encodePacked(base,uintToString(checkDigit)));
        return PaymentCard(fullNumber,"12/2027",uint16(100+(generatePseudoRandom(seed,88)%900)),"MasterCard","Active",0,0,0,0,"");
    }

    function createVisaCard(uint seed) external onlyOwner { PaymentCard memory c=generateSyntheticVisa(seed); cards.push(c); emit CardCreated(cards.length-1,c.cardType,c.number); }
    function createMasterCard(uint seed) external onlyOwner { PaymentCard memory c=generateSyntheticMasterCard(seed); cards.push(c); emit CardCreated(cards.length-1,c.cardType,c.number); }

    /* ---------- Deposits ---------- */
    function depositETH(uint256 cardIndex) external payable validCardIndex(cardIndex) { cards[cardIndex].ethBalance += msg.value; emit Deposit(cardIndex,"ETH",msg.value); }
    function depositUSDT(uint256 cardIndex,uint256 amount) external validCardIndex(cardIndex) { require(USDT.transferFrom(msg.sender,address(this),amount),"USDT transfer failed"); cards[cardIndex].usdtBalance+=amount; emit Deposit(cardIndex,"USDT",amount);}
    function depositUSDC(uint256 cardIndex,uint256 amount) external validCardIndex(cardIndex) { require(USDC.transferFrom(msg.sender,address(this),amount),"USDC transfer failed"); cards[cardIndex].usdcBalance+=amount; emit Deposit(cardIndex,"USDC",amount);}

    /* ---------- Withdrawals ---------- */
    function withdrawETH(uint256 cardIndex,uint256 amount) external onlyOwner validCardIndex(cardIndex){ require(cards[cardIndex].ethBalance>=amount,"Insufficient ETH"); cards[cardIndex].ethBalance-=amount; payable(owner).transfer(amount); emit Withdraw(cardIndex,"ETH",amount);}
    function withdrawUSDT(uint256 cardIndex,uint256 amount) external onlyOwner validCardIndex(cardIndex){ require(cards[cardIndex].usdtBalance>=amount,"Insufficient USDT"); cards[cardIndex].usdtBalance-=amount; require(USDT.transfer(owner,amount),"USDT transfer failed"); emit Withdraw(cardIndex,"USDT",amount);}
    function withdrawUSDC(uint256 cardIndex,uint256 amount) external onlyOwner validCardIndex(cardIndex){ require(cards[cardIndex].usdcBalance>=amount,"Insufficient USDC"); cards[cardIndex].usdcBalance-=amount; require(USDC.transfer(owner,amount),"USDC transfer failed"); emit Withdraw(cardIndex,"USDC",amount); }

    /* ---------- Spend ---------- */
    function spendFromCard(uint256 cardIndex,string calldata merchant,string calldata asset,uint256 amount) external onlyOwner validCardIndex(cardIndex){
        PaymentCard storage c=cards[cardIndex];
        if(keccak256(bytes(asset))==keccak256("ETH")){ require(c.ethBalance>=amount,"Insufficient ETH"); c.ethBalance-=amount;}
        else if(keccak256(bytes(asset))==keccak256("USDT")){ require(c.usdtBalance>=amount,"Insufficient USDT"); c.usdtBalance-=amount;}
        else if(keccak256(bytes(asset))==keccak256("USDC")){ require(c.usdcBalance>=amount,"Insufficient USDC"); c.usdcBalance-=amount;}
        else if(keccak256(bytes(asset))==keccak256("SYNTH")){ require(c.syntheticBalance>=amount,"Insufficient SYNTH"); c.syntheticBalance-=amount;}
        else{ revert("Unknown asset"); }
        emit SpendExecuted(cardIndex,merchant,asset,amount);
    }

    /* ---------- Swaps ---------- */
    function swapETHToUSDT(uint256 cardIndex,uint256 ethAmount) external onlyOwner validCardIndex(cardIndex){
        PaymentCard storage c=cards[cardIndex]; require(c.ethBalance>=ethAmount,"Insufficient ETH");
        uint256 ethPrice=_getLatestPrice(ethUsdFeed); uint256 usdtAmount=(ethAmount*ethPrice)/1e18/1e12;
        c.ethBalance-=ethAmount; c.usdtBalance+=usdtAmount; emit SwapExecuted(cardIndex,"ETH","USDT",ethAmount,usdtAmount);
    }

    function swapETHToUSDC(uint256 cardIndex,uint256 ethAmount) external onlyOwner validCardIndex(cardIndex){
        PaymentCard storage c=cards[cardIndex]; require(c.ethBalance>=ethAmount,"Insufficient ETH");
        uint256 ethPrice=_getLatestPrice(ethUsdFeed); uint256 usdcAmount=(ethAmount*ethPrice)/1e18/1e12;
        c.ethBalance-=ethAmount; c.usdcBalance+=usdcAmount; emit SwapExecuted(cardIndex,"ETH","USDC",ethAmount,usdcAmount);
    }

    function swapETHToSynth(uint256 cardIndex,uint256 ethAmount) external onlyOwner validCardIndex(cardIndex){
        PaymentCard storage c=cards[cardIndex]; require(c.ethBalance>=ethAmount,"Insufficient ETH");
        uint256 ethPrice=_getLatestPrice(ethUsdFeed); uint256 synthAmount=(ethAmount*ethPrice)/1e18;
        c.ethBalance-=ethAmount; c.syntheticBalance+=synthAmount; emit SwapExecuted(cardIndex,"ETH","SYNTH",ethAmount,synthAmount);
    }

    function swapSynthToETH(uint256 cardIndex,uint256 synthAmount) external onlyOwner validCardIndex(cardIndex){
        PaymentCard storage c=cards[cardIndex]; require(c.syntheticBalance>=synthAmount,"Insufficient SYNTH");
        uint256 ethPrice=_getLatestPrice(ethUsdFeed); uint256 ethAmount=(synthAmount*1e18)/ethPrice;
        c.syntheticBalance-=synthAmount; c.ethBalance+=ethAmount; emit SwapExecuted(cardIndex,"SYNTH","ETH",synthAmount,ethAmount);
    }

    function swapSynthToUSDT(uint256 cardIndex,uint256 synthAmount) external onlyOwner validCardIndex(cardIndex){
        PaymentCard storage c=cards[cardIndex]; require(c.syntheticBalance>=synthAmount,"Insufficient SYNTH");
        uint256 usdtPrice=_getLatestPrice(usdtUsdFeed); uint256 usdtAmount=(synthAmount*1e18)/usdtPrice;
        c.syntheticBalance-=synthAmount; c.usdtBalance+=usdtAmount; emit SwapExecuted(cardIndex,"SYNTH","USDT",synthAmount,usdtAmount);
    }

    function swapSynthToUSDC(uint256 cardIndex,uint256 synthAmount) external onlyOwner validCardIndex(cardIndex){
        PaymentCard storage c=cards[cardIndex]; require(c.syntheticBalance>=synthAmount,"Insufficient SYNTH");
        uint256 usdcPrice=_getLatestPrice(usdcUsdFeed); uint256 usdcAmount=(synthAmount*1e18)/usdcPrice;
        c.syntheticBalance-=synthAmount; c.usdcBalance+=usdcAmount; emit SwapExecuted(cardIndex,"SYNTH","USDC",synthAmount,usdcAmount);
    }

    /* ---------- Reverse Swaps ---------- */
    function swapUSDTToSynth(uint256 cardIndex,uint256 usdtAmount) external onlyOwner validCardIndex(cardIndex){
        PaymentCard storage c=cards[cardIndex]; require(c.usdtBalance>=usdtAmount,"Insufficient USDT");
        uint256 usdtPrice=_getLatestPrice(usdtUsdFeed); uint256 synthAmount=(usdtAmount*1e18)/usdtPrice;
        c.usdtBalance-=usdtAmount; c.syntheticBalance+=synthAmount; emit SwapExecuted(cardIndex,"USDT","SYNTH",usdtAmount,synthAmount);
    }

    function swapUSDCToSynth(uint256 cardIndex,uint256 usdcAmount) external onlyOwner validCardIndex(cardIndex){
        PaymentCard storage c=cards[cardIndex]; require(c.usdcBalance>=usdcAmount,"Insufficient USDC");
        uint256 usdcPrice=_getLatestPrice(usdcUsdFeed); uint256 synthAmount=(usdcAmount*1e18)/usdcPrice;
        c.usdcBalance-=usdcAmount; c.syntheticBalance+=synthAmount; emit SwapExecuted(cardIndex,"USDC","SYNTH",usdcAmount,synthAmount);
    }

    function swapUSDTToETH(uint256 cardIndex,uint256 usdtAmount) external onlyOwner validCardIndex(cardIndex){
        PaymentCard storage c=cards[cardIndex]; require(c.usdtBalance>=usdtAmount,"Insufficient USDT");
        uint256 usdtPrice=_getLatestPrice(usdtUsdFeed); uint256 ethAmount=(usdtAmount*1e18)/usdtPrice;
        c.usdtBalance-=usdtAmount; c.ethBalance+=ethAmount; emit SwapExecuted(cardIndex,"USDT","ETH",usdtAmount,ethAmount);
    }

    function swapUSDCToETH(uint256 cardIndex,uint256 usdcAmount) external onlyOwner validCardIndex(cardIndex){
        PaymentCard storage c=cards[cardIndex]; require(c.usdcBalance>=usdcAmount,"Insufficient USDC");
        uint256 usdcPrice=_getLatestPrice(usdcUsdFeed); uint256 ethAmount=(usdcAmount*1e18)/usdcPrice;
        c.usdcBalance-=usdcAmount; c.ethBalance+=ethAmount; emit SwapExecuted(cardIndex,"USDC","ETH",usdcAmount,ethAmount);
    }

    /* ---------- PayPal Linking ---------- */
    function requestPayPalLink(uint256 cardIndex) external onlyOwner validCardIndex(cardIndex){
        PaymentCard storage c=cards[cardIndex];
        emit PayPalLinkRequested(cardIndex,c.number,c.cardType,c.expiration,c.securityCode);
    }

    function confirmPayPalLink(uint256 cardIndex,string calldata paypalAccount) external onlyOwner validCardIndex(cardIndex){
        cards[cardIndex].paypalAccount = paypalAccount;
        emit PayPalLinkConfirmed(cardIndex,paypalAccount);
    }

    /* ---------- Views ---------- */
    function getCard(uint256 cardIndex) external view validCardIndex(cardIndex) returns (PaymentCard memory){ return cards[cardIndex]; }
    function getAllCards() external view returns (PaymentCard[] memory){ return cards; }
    function getContractBalance() external view returns(uint256){ return address(this).balance; }

    receive() external payable {}
}

Tags:
Oracle|addr:0x3102d84a4e917a97371d210b6217590180e71f18|verified:true|block:23450279|tx:0x549cd6459b7ee4d8ec40056a3c17363b586ae6b69af4b45907dc7d13dd5dd97e|first_check:1758963776

Submitted on: 2025-09-27 11:02:57

Comments

Log in to comment.

No comments yet.