Address: 0xab6055DA23196133199fAfb5121002Ff74dfE4fE
Balance (XRP): 0 XRP
Bytecode: 0x608060405234801561000f575f5ffd5b506004361061009b575f3560e01c80638da5cb5b116100635780638da5cb5b1461015f57806391351c8214610171578063d3b498e6146101a2578063f3fef3a3146101c1578063f6847c82146101d4575f5ffd5b80632630c12f1461009f57806332e5ea5b146100e35780635909c12f146100f857806389a302711461011f5780638a7fdc1514610146575b5f5ffd5b6100c67f000000000000000000000000b2d3a7ca7b752c9d8d19aa48e87a1a9f506c109781565b6040516001600160a01b0390911681526020015b60405180910390f35b6100f66100f13660046106e6565b6101df565b005b6100c67f000000000000000000000000cccccccc0000000100000000000000000000000081565b6100c67f000000000000000000000000cccccccc00000c6400000000000000000000000081565b61015163013105f081565b6040519081526020016100da565b5f546100c6906001600160a01b031681565b61019261017f366004610710565b5f90815260016020526040902054421090565b60405190151581526020016100da565b6101516101b0366004610710565b60016020525f908152604090205481565b6100f66101cf366004610727565b61054d565b6101516301e1338081565b7f000000000000000000000000cccccccc000000010000000000000000000000006001600160a01b0316816001600160a01b0316148061025057507f000000000000000000000000cccccccc00000c640000000000000000000000006001600160a01b0316816001600160a01b0316145b6102995760405162461bcd60e51b815260206004820152601560248201527424b73b30b634b2103830bcb6b2b73a103a37b5b2b760591b60448201526064015b60405180910390fd5b5f7f000000000000000000000000cccccccc00000c640000000000000000000000006001600160a01b0316826001600160a01b0316036102de575063013105f0610428565b6040516317a6948f60e21b81526001600160a01b0383811660048301525f917f000000000000000000000000b2d3a7ca7b752c9d8d19aa48e87a1a9f506c109790911690635e9a523c90602401602060405180830381865afa158015610346573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061036a919061074f565b9050807f000000000000000000000000cccccccc000000010000000000000000000000006001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103c9573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103ed9190610766565b6103f890600a610886565b61041063013105f069d3c21bcecceda1000000610894565b61041a9190610894565b61042491906108ab565b9150505b6040516323b872dd60e01b8152336004820152306024820152604481018290526001600160a01b038316906323b872dd906064016020604051808303815f875af1158015610478573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061049c91906108ca565b505f838152600160205260408120544211156104c7576104c06301e13380426108e9565b90506104e7565b5f848152600160205260409020546104e4906301e13380906108e9565b90505b5f8481526001602090815260409182902083905581513381529081018390526001600160a01b038516818301526060810184905290517f3c969f868e5e122e505d15be27843dbf7f4b091c1f7c41bf709875492923f3e09181900360800190a150505050565b5f546001600160a01b031633146105a65760405162461bcd60e51b815260206004820152601760248201527f43616c6c6572206973206e6f7420746865206f776e65720000000000000000006044820152606401610290565b6040516370a0823160e01b815230600482015281906001600160a01b038416906370a0823190602401602060405180830381865afa1580156105ea573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061060e919061074f565b10156106535760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b6044820152606401610290565b5f5460405163a9059cbb60e01b81526001600160a01b039182166004820152602481018390529083169063a9059cbb906044016020604051808303815f875af11580156106a2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106c691906108ca565b505050565b80356001600160a01b03811681146106e1575f5ffd5b919050565b5f5f604083850312156106f7575f5ffd5b82359150610707602084016106cb565b90509250929050565b5f60208284031215610720575f5ffd5b5035919050565b5f5f60408385031215610738575f5ffd5b610741836106cb565b946020939093013593505050565b5f6020828403121561075f575f5ffd5b5051919050565b5f60208284031215610776575f5ffd5b815160ff81168114610786575f5ffd5b9392505050565b634e487b7160e01b5f52601160045260245ffd5b6001815b60018411156107dc578085048111156107c0576107c061078d565b60018416156107ce57908102905b60019390931c9280026107a5565b935093915050565b5f826107f257506001610880565b816107fe57505f610880565b8160018114610814576002811461081e5761083a565b6001915050610880565b60ff84111561082f5761082f61078d565b50506001821b610880565b5060208310610133831016604e8410600b841016171561085d575081810a610880565b6108695f1984846107a1565b805f190482111561087c5761087c61078d565b0290505b92915050565b5f61078660ff8416836107e4565b80820281158282048414176108805761088061078d565b5f826108c557634e487b7160e01b5f52601260045260245ffd5b500490565b5f602082840312156108da575f5ffd5b81518015158114610786575f5ffd5b808201808211156108805761088061078d56fea2646970667358221220ee5113c286d772e4deb93c9724aafed50f6e748d5654c3059a3c5d5c70f7f60464736f6c634300081c0033
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(bytes32 => 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(bytes32 node, 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[node] < block.timestamp) { newExpiry = block.timestamp + SUBSCRIPTION_DURATION; } else { newExpiry = subscriptionExpiry[node] + SUBSCRIPTION_DURATION; } subscriptionExpiry[node] = newExpiry; emit SubscriptionPurchased( msg.sender, newExpiry, paymentToken, paymentAmount ); } function isSubscriptionActive( bytes32 node ) public view returns (bool) { return subscriptionExpiry[node] > 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: