Address: 0x4BB8E92331BB71d26f1458B0F9bCd2ee8Eb3EDf8
Balance (XRP): 0 XRP
Bytecode: 0x608060405234801561000f575f5ffd5b506004361061009b575f3560e01c80638a7fdc15116100635780638a7fdc15146101985780638da5cb5b146101a3578063cf54f02a146101b5578063f3fef3a3146101ca578063f6847c82146101dd575f5ffd5b8063144013291461009f5780632630c12f146100de5780635909c12f1461011d57806360a85ef51461014457806389a3027114610171575b5f5ffd5b6100c96100ad3660046106ef565b6001600160a01b03165f90815260016020526040902054421090565b60405190151581526020015b60405180910390f35b6101057f000000000000000000000000b2d3a7ca7b752c9d8d19aa48e87a1a9f506c109781565b6040516001600160a01b0390911681526020016100d5565b6101057f000000000000000000000000cccccccc0000000100000000000000000000000081565b6101636101523660046106ef565b60016020525f908152604090205481565b6040519081526020016100d5565b6101057f000000000000000000000000cccccccc00000c6400000000000000000000000081565b61016363013105f081565b5f54610105906001600160a01b031681565b6101c86101c33660046106ef565b6101e8565b005b6101c86101d836600461070f565b610556565b6101636301e1338081565b7f000000000000000000000000cccccccc000000010000000000000000000000006001600160a01b0316816001600160a01b0316148061025957507f000000000000000000000000cccccccc00000c640000000000000000000000006001600160a01b0316816001600160a01b0316145b6102a25760405162461bcd60e51b815260206004820152601560248201527424b73b30b634b2103830bcb6b2b73a103a37b5b2b760591b60448201526064015b60405180910390fd5b5f7f000000000000000000000000cccccccc00000c640000000000000000000000006001600160a01b0316826001600160a01b0316036102e7575063013105f0610431565b6040516317a6948f60e21b81526001600160a01b0383811660048301525f917f000000000000000000000000b2d3a7ca7b752c9d8d19aa48e87a1a9f506c109790911690635e9a523c90602401602060405180830381865afa15801561034f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103739190610737565b9050807f000000000000000000000000cccccccc000000010000000000000000000000006001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103d2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103f6919061074e565b61040190600a610867565b61041963013105f069d3c21bcecceda1000000610875565b6104239190610875565b61042d919061088c565b9150505b6040516323b872dd60e01b8152336004820152306024820152604481018290526001600160a01b038316906323b872dd906064016020604051808303815f875af1158015610481573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104a591906108ab565b50335f908152600160205260408120544211156104d1576104ca6301e13380426108ca565b90506104f2565b335f908152600160205260409020546104ef906301e13380906108ca565b90505b335f81815260016020908152604091829020849055815192835282018390526001600160a01b0385168282015260608201849052517f3c969f868e5e122e505d15be27843dbf7f4b091c1f7c41bf709875492923f3e09181900360800190a1505050565b5f546001600160a01b031633146105af5760405162461bcd60e51b815260206004820152601760248201527f43616c6c6572206973206e6f7420746865206f776e65720000000000000000006044820152606401610299565b6040516370a0823160e01b815230600482015281906001600160a01b038416906370a0823190602401602060405180830381865afa1580156105f3573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106179190610737565b101561065c5760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b6044820152606401610299565b5f5460405163a9059cbb60e01b81526001600160a01b039182166004820152602481018390529083169063a9059cbb906044016020604051808303815f875af11580156106ab573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106cf91906108ab565b505050565b80356001600160a01b03811681146106ea575f5ffd5b919050565b5f602082840312156106ff575f5ffd5b610708826106d4565b9392505050565b5f5f60408385031215610720575f5ffd5b610729836106d4565b946020939093013593505050565b5f60208284031215610747575f5ffd5b5051919050565b5f6020828403121561075e575f5ffd5b815160ff81168114610708575f5ffd5b634e487b7160e01b5f52601160045260245ffd5b6001815b60018411156107bd578085048111156107a1576107a161076e565b60018416156107af57908102905b60019390931c928002610786565b935093915050565b5f826107d357506001610861565b816107df57505f610861565b81600181146107f557600281146107ff5761081b565b6001915050610861565b60ff8411156108105761081061076e565b50506001821b610861565b5060208310610133831016604e8410600b841016171561083e575081810a610861565b61084a5f198484610782565b805f190482111561085d5761085d61076e565b0290505b92915050565b5f61070860ff8416836107c5565b80820281158282048414176108615761086161076e565b5f826108a657634e487b7160e01b5f52601260045260245ffd5b500490565b5f602082840312156108bb575f5ffd5b81518015158114610708575f5ffd5b808201808211156108615761086161076e56fea26469706673582212205a987fa8b59be2954ca705399d212faddf08d325c097387187c415ab3c8c793564736f6c634300081c0033
IERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-20 standard as defined in the ERC. */ 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 value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` 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 value) external returns (bool); }
IERC20Metadata.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC-20 standard. */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
SubscriptionManager.sol
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {IPriceOracle} from "./interfaces/IPriceOracle.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; contract SubscriptionManager { address public immutable ROOT; address public immutable USDC; IPriceOracle public immutable priceOracle; address public owner; // Subscription price in USD (with 6 decimals) uint256 public constant SUBSCRIPTION_PRICE_USD = 19_990_000; // $19.99 uint256 public constant SUBSCRIPTION_DURATION = 365 days; mapping(address => uint256) public subscriptionExpiry; event SubscriptionPurchased( address subscriber, uint256 expiryDate, address paymentToken, uint256 paymentAmount ); constructor( address _root, address _usdc, address _priceOracle ) { require(_root != address(0), "Invalid ROOT address"); require(_usdc != address(0), "Invalid USDC address"); require(_priceOracle != address(0), "Invalid price oracle address"); ROOT = _root; USDC = _usdc; priceOracle = IPriceOracle(_priceOracle); owner = msg.sender; } modifier onlyOwner() { require(msg.sender == owner, "Caller is not the owner"); _; } function withdraw(address token, uint256 amount) external onlyOwner { require( IERC20(token).balanceOf(address(this)) >= amount, "Insufficient balance" ); IERC20(token).transfer(owner, amount); } function purchaseSubscription(address paymentToken) external { require( paymentToken == ROOT || paymentToken == USDC, "Invalid payment token" ); uint256 paymentAmount; if (paymentToken == USDC) { // USDC has 6 decimals, so no conversion needed for price paymentAmount = SUBSCRIPTION_PRICE_USD; } else { // Convert USD price to ROOT amount using price oracle // priceOracle returns ROOT price in USD with 30 decimals uint256 rootPriceUSD = priceOracle.assetPrices(paymentToken); paymentAmount = (SUBSCRIPTION_PRICE_USD * 1e24 * (10 ** IERC20Metadata(ROOT).decimals())) / rootPriceUSD; } // Transfer tokens from user IERC20(paymentToken).transferFrom( msg.sender, address(this), paymentAmount ); // Update subscription expiry uint256 newExpiry; if (subscriptionExpiry[msg.sender] < block.timestamp) { newExpiry = block.timestamp + SUBSCRIPTION_DURATION; } else { newExpiry = subscriptionExpiry[msg.sender] + SUBSCRIPTION_DURATION; } subscriptionExpiry[msg.sender] = newExpiry; emit SubscriptionPurchased( msg.sender, newExpiry, paymentToken, paymentAmount ); } function isSubscriptionActive( address subscriber ) public view returns (bool) { return subscriptionExpiry[subscriber] > block.timestamp; } }
IPriceOracle.sol
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; interface IPriceOracle { function assetPrices(address asset) external view returns (uint); }
Gas Token: